GRPCWebToHTTP2ServerCodecTests.swift 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. /*
  2. * Copyright 2020, gRPC Authors All rights reserved.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. import struct Foundation.Data
  17. @testable import GRPC
  18. import NIO
  19. import NIOHPACK
  20. import NIOHTTP1
  21. import NIOHTTP2
  22. import XCTest
  23. class GRPCWebToHTTP2ServerCodecTests: GRPCTestCase {
  24. private func writeTrailers(_ trailers: HPACKHeaders, into buffer: inout ByteBuffer) {
  25. let encoded = trailers.map { "\($0.name): \($0.value)" }.joined(separator: "\r\n")
  26. buffer.writeInteger(UInt8(0x80))
  27. buffer.writeInteger(UInt32(encoded.utf8.count))
  28. buffer.writeString(encoded)
  29. }
  30. private func receiveHead(
  31. contentType: ContentType,
  32. path: String,
  33. on channel: EmbeddedChannel
  34. ) throws {
  35. let head = HTTPRequestHead(
  36. version: .init(major: 1, minor: 1),
  37. method: .POST,
  38. uri: path,
  39. headers: [GRPCHeaderName.contentType: contentType.canonicalValue]
  40. )
  41. assertThat(try channel.writeInbound(HTTPServerRequestPart.head(head)), .doesNotThrow())
  42. let headersPayload = try channel.readInbound(as: HTTP2Frame.FramePayload.self)
  43. assertThat(headersPayload, .notNil(.headers(.contains(":path", [path]))))
  44. }
  45. private func receiveBytes(
  46. _ buffer: ByteBuffer,
  47. on channel: EmbeddedChannel,
  48. expectedBytes: [UInt8]? = nil
  49. ) throws {
  50. assertThat(try channel.writeInbound(HTTPServerRequestPart.body(buffer)), .doesNotThrow())
  51. if let expectedBytes = expectedBytes {
  52. let dataPayload = try channel.readInbound(as: HTTP2Frame.FramePayload.self)
  53. assertThat(dataPayload, .notNil(.data(buffer: ByteBuffer(bytes: expectedBytes))))
  54. }
  55. }
  56. private func receiveEnd(on channel: EmbeddedChannel) throws {
  57. assertThat(try channel.writeInbound(HTTPServerRequestPart.end(nil)), .doesNotThrow())
  58. let dataEndPayload = try channel.readInbound(as: HTTP2Frame.FramePayload.self)
  59. assertThat(dataEndPayload, .notNil(.data(buffer: ByteBuffer(), endStream: true)))
  60. }
  61. private func sendResponseHeaders(on channel: EmbeddedChannel) throws {
  62. let responseHeaders: HPACKHeaders = [":status": "200"]
  63. let headerPayload: HTTP2Frame.FramePayload = .headers(.init(headers: responseHeaders))
  64. assertThat(try channel.writeOutbound(headerPayload), .doesNotThrow())
  65. let responseHead = try channel.readOutbound(as: HTTPServerResponsePart.self)
  66. assertThat(responseHead, .notNil(.head(status: .ok)))
  67. }
  68. private func sendTrailersOnlyResponse(on channel: EmbeddedChannel) throws {
  69. let headers: HPACKHeaders = [":status": "200"]
  70. let headerPayload: HTTP2Frame.FramePayload = .headers(.init(headers: headers, endStream: true))
  71. assertThat(try channel.writeOutbound(headerPayload), .doesNotThrow())
  72. let responseHead = try channel.readOutbound(as: HTTPServerResponsePart.self)
  73. assertThat(responseHead, .notNil(.head(status: .ok)))
  74. let end = try channel.readOutbound(as: HTTPServerResponsePart.self)
  75. assertThat(end, .notNil(.end()))
  76. }
  77. private func sendBytes(
  78. _ bytes: [UInt8],
  79. on channel: EmbeddedChannel,
  80. expectedBytes: [UInt8]? = nil
  81. ) throws {
  82. let responseBuffer = ByteBuffer(bytes: bytes)
  83. let dataPayload: HTTP2Frame.FramePayload = .data(.init(data: .byteBuffer(responseBuffer)))
  84. assertThat(try channel.writeOutbound(dataPayload), .doesNotThrow())
  85. if let expectedBytes = expectedBytes {
  86. let expectedBuffer = ByteBuffer(bytes: expectedBytes)
  87. assertThat(try channel.readOutbound(), .notNil(.body(.is(expectedBuffer))))
  88. } else {
  89. assertThat(try channel.readOutbound(as: HTTPServerResponsePart.self), .doesNotThrow(.nil()))
  90. }
  91. }
  92. private func sendEnd(
  93. status: GRPCStatus.Code,
  94. on channel: EmbeddedChannel,
  95. expectedBytes: ByteBuffer? = nil
  96. ) throws {
  97. let headers: HPACKHeaders = ["grpc-status": "\(status)"]
  98. let headersPayload: HTTP2Frame.FramePayload = .headers(.init(headers: headers, endStream: true))
  99. assertThat(try channel.writeOutbound(headersPayload), .doesNotThrow())
  100. if let expectedBytes = expectedBytes {
  101. assertThat(try channel.readOutbound(), .notNil(.body(.is(expectedBytes))))
  102. }
  103. assertThat(try channel.readOutbound(), .notNil(.end()))
  104. }
  105. func testWebBinaryHappyPath() throws {
  106. let channel = EmbeddedChannel(handler: GRPCWebToHTTP2ServerCodec(scheme: "http"))
  107. // Inbound
  108. try self.receiveHead(contentType: .webProtobuf, path: "foo", on: channel)
  109. try self.receiveBytes(ByteBuffer(bytes: [1, 2, 3]), on: channel, expectedBytes: [1, 2, 3])
  110. try self.receiveEnd(on: channel)
  111. // Outbound
  112. try self.sendResponseHeaders(on: channel)
  113. try self.sendBytes([1, 2, 3], on: channel, expectedBytes: [1, 2, 3])
  114. try self.sendEnd(status: .ok, on: channel)
  115. }
  116. func testWebTextHappyPath() throws {
  117. let channel = EmbeddedChannel(handler: GRPCWebToHTTP2ServerCodec(scheme: "http"))
  118. // Inbound
  119. try self.receiveHead(contentType: .webTextProtobuf, path: "foo", on: channel)
  120. try self.receiveBytes(
  121. ByteBuffer(bytes: [1, 2, 3]).base64Encoded(),
  122. on: channel,
  123. expectedBytes: [1, 2, 3]
  124. )
  125. try self.receiveEnd(on: channel)
  126. // Outbound
  127. try self.sendResponseHeaders(on: channel)
  128. try self.sendBytes([1, 2, 3], on: channel)
  129. // Build up the expected response, i.e. the response bytes and the trailers, base64 encoded.
  130. var expectedBodyBuffer = ByteBuffer(bytes: [1, 2, 3])
  131. let status = GRPCStatus.Code.ok
  132. self.writeTrailers(["grpc-status": "\(status)"], into: &expectedBodyBuffer)
  133. try self.sendEnd(status: status, on: channel, expectedBytes: expectedBodyBuffer.base64Encoded())
  134. }
  135. func testWebTextStatusOnlyResponse() throws {
  136. let channel = EmbeddedChannel(handler: GRPCWebToHTTP2ServerCodec(scheme: "http"))
  137. try self.receiveHead(contentType: .webTextProtobuf, path: "foo", on: channel)
  138. try self.sendTrailersOnlyResponse(on: channel)
  139. }
  140. func testWebTextByteByByte() throws {
  141. let channel = EmbeddedChannel(handler: GRPCWebToHTTP2ServerCodec(scheme: "http"))
  142. try self.receiveHead(contentType: .webTextProtobuf, path: "foo", on: channel)
  143. let bytes = ByteBuffer(bytes: [1, 2, 3]).base64Encoded()
  144. try self.receiveBytes(bytes.getSlice(at: 0, length: 1)!, on: channel, expectedBytes: nil)
  145. try self.receiveBytes(bytes.getSlice(at: 1, length: 1)!, on: channel, expectedBytes: nil)
  146. try self.receiveBytes(bytes.getSlice(at: 2, length: 1)!, on: channel, expectedBytes: nil)
  147. try self.receiveBytes(bytes.getSlice(at: 3, length: 1)!, on: channel, expectedBytes: [1, 2, 3])
  148. }
  149. func testSendAfterEnd() throws {
  150. let channel = EmbeddedChannel(handler: GRPCWebToHTTP2ServerCodec(scheme: "http"))
  151. // Get to a closed state.
  152. try self.receiveHead(contentType: .webTextProtobuf, path: "foo", on: channel)
  153. try self.sendTrailersOnlyResponse(on: channel)
  154. let headersPayload: HTTP2Frame.FramePayload = .headers(.init(headers: [:]))
  155. assertThat(try channel.write(headersPayload).wait(), .throws())
  156. let dataPayload: HTTP2Frame.FramePayload = .data(.init(data: .byteBuffer(.init())))
  157. assertThat(try channel.write(dataPayload).wait(), .throws())
  158. }
  159. }
  160. extension ByteBuffer {
  161. fileprivate func base64Encoded() -> ByteBuffer {
  162. let data = self.getData(at: self.readerIndex, length: self.readableBytes)!
  163. return ByteBuffer(string: data.base64EncodedString())
  164. }
  165. }