GRPCWebToHTTP2ServerCodecTests.swift 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  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 NIOCore
  17. import NIOEmbedded
  18. import NIOHPACK
  19. import NIOHTTP1
  20. import NIOHTTP2
  21. import XCTest
  22. import struct Foundation.Data
  23. @testable import GRPC
  24. class GRPCWebToHTTP2ServerCodecTests: GRPCTestCase {
  25. private func writeTrailers(_ trailers: HPACKHeaders, into buffer: inout ByteBuffer) {
  26. buffer.writeInteger(UInt8(0x80))
  27. try! buffer.writeLengthPrefixed(as: UInt32.self) {
  28. var length = 0
  29. for (name, value, _) in trailers {
  30. length += $0.writeString("\(name): \(value)\r\n")
  31. }
  32. return length
  33. }
  34. }
  35. private func receiveHead(
  36. contentType: ContentType,
  37. path: String,
  38. on channel: EmbeddedChannel
  39. ) throws {
  40. let head = HTTPRequestHead(
  41. version: .init(major: 1, minor: 1),
  42. method: .POST,
  43. uri: path,
  44. headers: [GRPCHeaderName.contentType: contentType.canonicalValue]
  45. )
  46. assertThat(try channel.writeInbound(HTTPServerRequestPart.head(head)), .doesNotThrow())
  47. let headersPayload = try channel.readInbound(as: HTTP2Frame.FramePayload.self)
  48. assertThat(headersPayload, .some(.headers(.contains(":path", [path]))))
  49. }
  50. private func receiveBytes(
  51. _ buffer: ByteBuffer,
  52. on channel: EmbeddedChannel,
  53. expectedBytes: [UInt8]? = nil
  54. ) throws {
  55. assertThat(try channel.writeInbound(HTTPServerRequestPart.body(buffer)), .doesNotThrow())
  56. if let expectedBytes = expectedBytes {
  57. let dataPayload = try channel.readInbound(as: HTTP2Frame.FramePayload.self)
  58. assertThat(dataPayload, .some(.data(buffer: ByteBuffer(bytes: expectedBytes))))
  59. }
  60. }
  61. private func receiveEnd(on channel: EmbeddedChannel) throws {
  62. assertThat(try channel.writeInbound(HTTPServerRequestPart.end(nil)), .doesNotThrow())
  63. let dataEndPayload = try channel.readInbound(as: HTTP2Frame.FramePayload.self)
  64. assertThat(dataEndPayload, .some(.data(buffer: ByteBuffer(), endStream: true)))
  65. }
  66. private func sendResponseHeaders(on channel: EmbeddedChannel) throws {
  67. let responseHeaders: HPACKHeaders = [":status": "200"]
  68. let headerPayload: HTTP2Frame.FramePayload = .headers(.init(headers: responseHeaders))
  69. assertThat(try channel.writeOutbound(headerPayload), .doesNotThrow())
  70. let responseHead = try channel.readOutbound(as: HTTPServerResponsePart.self)
  71. assertThat(responseHead, .some(.head(status: .ok)))
  72. }
  73. private func sendTrailersOnlyResponse(on channel: EmbeddedChannel) throws {
  74. let headers: HPACKHeaders = [":status": "200"]
  75. let headerPayload: HTTP2Frame.FramePayload = .headers(.init(headers: headers, endStream: true))
  76. assertThat(try channel.writeOutbound(headerPayload), .doesNotThrow())
  77. let responseHead = try channel.readOutbound(as: HTTPServerResponsePart.self)
  78. assertThat(responseHead, .some(.head(status: .ok)))
  79. let end = try channel.readOutbound(as: HTTPServerResponsePart.self)
  80. assertThat(end, .some(.end()))
  81. }
  82. private func sendBytes(
  83. _ bytes: [UInt8],
  84. on channel: EmbeddedChannel,
  85. expectedBytes: [UInt8]? = nil
  86. ) throws {
  87. let responseBuffer = ByteBuffer(bytes: bytes)
  88. let dataPayload: HTTP2Frame.FramePayload = .data(.init(data: .byteBuffer(responseBuffer)))
  89. assertThat(try channel.writeOutbound(dataPayload), .doesNotThrow())
  90. if let expectedBytes = expectedBytes {
  91. let expectedBuffer = ByteBuffer(bytes: expectedBytes)
  92. assertThat(try channel.readOutbound(), .some(.body(.is(expectedBuffer))))
  93. } else {
  94. assertThat(try channel.readOutbound(as: HTTPServerResponsePart.self), .doesNotThrow(.none()))
  95. }
  96. }
  97. private func sendEnd(
  98. status: GRPCStatus.Code,
  99. on channel: EmbeddedChannel,
  100. expectedBytes: ByteBuffer? = nil
  101. ) throws {
  102. let headers: HPACKHeaders = ["grpc-status": "\(status.rawValue)"]
  103. let headersPayload: HTTP2Frame.FramePayload = .headers(.init(headers: headers, endStream: true))
  104. assertThat(try channel.writeOutbound(headersPayload), .doesNotThrow())
  105. if let expectedBytes = expectedBytes {
  106. assertThat(try channel.readOutbound(), .some(.body(.is(expectedBytes))))
  107. }
  108. assertThat(try channel.readOutbound(), .some(.end()))
  109. }
  110. func testWebBinaryHappyPath() throws {
  111. let channel = EmbeddedChannel(handler: GRPCWebToHTTP2ServerCodec(scheme: "http"))
  112. // Inbound
  113. try self.receiveHead(contentType: .webProtobuf, path: "foo", on: channel)
  114. try self.receiveBytes(ByteBuffer(bytes: [1, 2, 3]), on: channel, expectedBytes: [1, 2, 3])
  115. try self.receiveEnd(on: channel)
  116. // Outbound
  117. try self.sendResponseHeaders(on: channel)
  118. try self.sendBytes([1, 2, 3], on: channel, expectedBytes: [1, 2, 3])
  119. var buffer = ByteBuffer()
  120. self.writeTrailers(["grpc-status": "0"], into: &buffer)
  121. try self.sendEnd(status: .ok, on: channel, expectedBytes: buffer)
  122. }
  123. func testWebTextHappyPath() throws {
  124. let channel = EmbeddedChannel(handler: GRPCWebToHTTP2ServerCodec(scheme: "http"))
  125. // Inbound
  126. try self.receiveHead(contentType: .webTextProtobuf, path: "foo", on: channel)
  127. try self.receiveBytes(
  128. ByteBuffer(bytes: [1, 2, 3]).base64Encoded(),
  129. on: channel,
  130. expectedBytes: [1, 2, 3]
  131. )
  132. try self.receiveEnd(on: channel)
  133. // Outbound
  134. try self.sendResponseHeaders(on: channel)
  135. try self.sendBytes([1, 2, 3], on: channel)
  136. // Build up the expected response, i.e. the response bytes and the trailers, base64 encoded.
  137. var expectedBodyBuffer = ByteBuffer(bytes: [1, 2, 3])
  138. let status = GRPCStatus.Code.ok
  139. self.writeTrailers(["grpc-status": "\(status.rawValue)"], into: &expectedBodyBuffer)
  140. try self.sendEnd(status: status, on: channel, expectedBytes: expectedBodyBuffer.base64Encoded())
  141. }
  142. func testWebTextStatusOnlyResponse() throws {
  143. let channel = EmbeddedChannel(handler: GRPCWebToHTTP2ServerCodec(scheme: "http"))
  144. try self.receiveHead(contentType: .webTextProtobuf, path: "foo", on: channel)
  145. try self.sendTrailersOnlyResponse(on: channel)
  146. }
  147. func testWebTextByteByByte() throws {
  148. let channel = EmbeddedChannel(handler: GRPCWebToHTTP2ServerCodec(scheme: "http"))
  149. try self.receiveHead(contentType: .webTextProtobuf, path: "foo", on: channel)
  150. let bytes = ByteBuffer(bytes: [1, 2, 3]).base64Encoded()
  151. try self.receiveBytes(bytes.getSlice(at: 0, length: 1)!, on: channel, expectedBytes: nil)
  152. try self.receiveBytes(bytes.getSlice(at: 1, length: 1)!, on: channel, expectedBytes: nil)
  153. try self.receiveBytes(bytes.getSlice(at: 2, length: 1)!, on: channel, expectedBytes: nil)
  154. try self.receiveBytes(bytes.getSlice(at: 3, length: 1)!, on: channel, expectedBytes: [1, 2, 3])
  155. }
  156. func testSendAfterEnd() throws {
  157. let channel = EmbeddedChannel(handler: GRPCWebToHTTP2ServerCodec(scheme: "http"))
  158. // Get to a closed state.
  159. try self.receiveHead(contentType: .webTextProtobuf, path: "foo", on: channel)
  160. try self.sendTrailersOnlyResponse(on: channel)
  161. let headersPayload: HTTP2Frame.FramePayload = .headers(.init(headers: [:]))
  162. assertThat(try channel.write(headersPayload).wait(), .throws())
  163. let dataPayload: HTTP2Frame.FramePayload = .data(.init(data: .byteBuffer(.init())))
  164. assertThat(try channel.write(dataPayload).wait(), .throws())
  165. }
  166. }
  167. extension ByteBuffer {
  168. fileprivate func base64Encoded() -> ByteBuffer {
  169. let data = self.getData(at: self.readerIndex, length: self.readableBytes)!
  170. return ByteBuffer(string: data.base64EncodedString())
  171. }
  172. }