2
0

GRPCWebToHTTP2ServerCodecTests.swift 7.7 KB

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