GRPCServerPipelineConfiguratorTests.swift 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  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 NIOHTTP2
  19. import NIOTLS
  20. import XCTest
  21. @testable import GRPC
  22. class GRPCServerPipelineConfiguratorTests: GRPCTestCase {
  23. private var channel: EmbeddedChannel!
  24. private func assertConfigurator(isPresent: Bool) {
  25. assertThat(
  26. try self.channel.pipeline.handler(type: GRPCServerPipelineConfigurator.self).wait(),
  27. isPresent ? .doesNotThrow() : .throws()
  28. )
  29. }
  30. private func assertHTTP2Handler(isPresent: Bool) {
  31. assertThat(
  32. try self.channel.pipeline.handler(type: NIOHTTP2Handler.self).wait(),
  33. isPresent ? .doesNotThrow() : .throws()
  34. )
  35. }
  36. private func assertGRPCWebToHTTP2Handler(isPresent: Bool) {
  37. assertThat(
  38. try self.channel.pipeline.handler(type: GRPCWebToHTTP2ServerCodec.self).wait(),
  39. isPresent ? .doesNotThrow() : .throws()
  40. )
  41. }
  42. private func setUp(tls: Bool, requireALPN: Bool = true) {
  43. self.channel = EmbeddedChannel()
  44. var configuration = Server.Configuration.default(
  45. target: .unixDomainSocket("/ignored"),
  46. eventLoopGroup: self.channel.eventLoop,
  47. serviceProviders: []
  48. )
  49. configuration.logger = self.serverLogger
  50. if tls {
  51. #if canImport(NIOSSL)
  52. configuration.tlsConfiguration = .makeServerConfigurationBackedByNIOSSL(
  53. certificateChain: [],
  54. privateKey: .file("not used"),
  55. requireALPN: requireALPN
  56. )
  57. #else
  58. fatalError("TLS enabled for a test when NIOSSL is not available")
  59. #endif
  60. }
  61. let handler = GRPCServerPipelineConfigurator(configuration: configuration)
  62. assertThat(try self.channel.pipeline.addHandler(handler).wait(), .doesNotThrow())
  63. }
  64. #if canImport(NIOSSL)
  65. func testHTTP2SetupViaALPN() {
  66. self.setUp(tls: true, requireALPN: true)
  67. let event = TLSUserEvent.handshakeCompleted(negotiatedProtocol: "h2")
  68. self.channel.pipeline.fireUserInboundEventTriggered(event)
  69. self.assertConfigurator(isPresent: false)
  70. self.assertHTTP2Handler(isPresent: true)
  71. }
  72. func testGRPCExpSetupViaALPN() {
  73. self.setUp(tls: true, requireALPN: true)
  74. let event = TLSUserEvent.handshakeCompleted(negotiatedProtocol: "grpc-exp")
  75. self.channel.pipeline.fireUserInboundEventTriggered(event)
  76. self.assertConfigurator(isPresent: false)
  77. self.assertHTTP2Handler(isPresent: true)
  78. }
  79. func testHTTP1Dot1SetupViaALPN() {
  80. self.setUp(tls: true, requireALPN: true)
  81. let event = TLSUserEvent.handshakeCompleted(negotiatedProtocol: "http/1.1")
  82. self.channel.pipeline.fireUserInboundEventTriggered(event)
  83. self.assertConfigurator(isPresent: false)
  84. self.assertGRPCWebToHTTP2Handler(isPresent: true)
  85. }
  86. func testUnrecognisedALPNCloses() {
  87. self.setUp(tls: true, requireALPN: true)
  88. let event = TLSUserEvent.handshakeCompleted(negotiatedProtocol: "unsupported")
  89. self.channel.pipeline.fireUserInboundEventTriggered(event)
  90. self.channel.embeddedEventLoop.run()
  91. assertThat(try self.channel.closeFuture.wait(), .doesNotThrow())
  92. }
  93. func testNoNegotiatedProtocolCloses() {
  94. self.setUp(tls: true, requireALPN: true)
  95. let event = TLSUserEvent.handshakeCompleted(negotiatedProtocol: nil)
  96. self.channel.pipeline.fireUserInboundEventTriggered(event)
  97. self.channel.embeddedEventLoop.run()
  98. assertThat(try self.channel.closeFuture.wait(), .doesNotThrow())
  99. }
  100. func testNoNegotiatedProtocolFallbackToBytesWhenALPNNotRequired() throws {
  101. self.setUp(tls: true, requireALPN: false)
  102. // Require ALPN is disabled, so this is a no-op.
  103. let event = TLSUserEvent.handshakeCompleted(negotiatedProtocol: nil)
  104. self.channel.pipeline.fireUserInboundEventTriggered(event)
  105. // Configure via bytes.
  106. let bytes = ByteBuffer(staticString: "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")
  107. assertThat(try self.channel.writeInbound(bytes), .doesNotThrow())
  108. self.assertConfigurator(isPresent: false)
  109. self.assertHTTP2Handler(isPresent: true)
  110. }
  111. #endif // canImport(NIOSSL)
  112. func testHTTP2SetupViaBytes() {
  113. self.setUp(tls: false)
  114. let bytes = ByteBuffer(staticString: "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")
  115. assertThat(try self.channel.writeInbound(bytes), .doesNotThrow())
  116. self.assertConfigurator(isPresent: false)
  117. self.assertHTTP2Handler(isPresent: true)
  118. }
  119. func testHTTP2SetupViaBytesDripFed() {
  120. self.setUp(tls: false)
  121. var bytes = ByteBuffer(staticString: "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")
  122. var head = bytes.readSlice(length: bytes.readableBytes - 1)!
  123. let tail = bytes.readSlice(length: 1)!
  124. while let slice = head.readSlice(length: 1) {
  125. assertThat(try self.channel.writeInbound(slice), .doesNotThrow())
  126. self.assertConfigurator(isPresent: true)
  127. self.assertHTTP2Handler(isPresent: false)
  128. }
  129. // Final byte.
  130. assertThat(try self.channel.writeInbound(tail), .doesNotThrow())
  131. self.assertConfigurator(isPresent: false)
  132. self.assertHTTP2Handler(isPresent: true)
  133. }
  134. func testHTTP1Dot1SetupViaBytes() {
  135. self.setUp(tls: false)
  136. let bytes = ByteBuffer(staticString: "GET http://www.foo.bar HTTP/1.1\r\n")
  137. assertThat(try self.channel.writeInbound(bytes), .doesNotThrow())
  138. self.assertConfigurator(isPresent: false)
  139. self.assertGRPCWebToHTTP2Handler(isPresent: true)
  140. }
  141. func testHTTP1Dot1SetupViaBytesDripFed() {
  142. self.setUp(tls: false)
  143. var bytes = ByteBuffer(staticString: "GET http://www.foo.bar HTTP/1.1\r\n")
  144. var head = bytes.readSlice(length: bytes.readableBytes - 1)!
  145. let tail = bytes.readSlice(length: 1)!
  146. while let slice = head.readSlice(length: 1) {
  147. assertThat(try self.channel.writeInbound(slice), .doesNotThrow())
  148. self.assertConfigurator(isPresent: true)
  149. self.assertGRPCWebToHTTP2Handler(isPresent: false)
  150. }
  151. // Final byte.
  152. assertThat(try self.channel.writeInbound(tail), .doesNotThrow())
  153. self.assertConfigurator(isPresent: false)
  154. self.assertGRPCWebToHTTP2Handler(isPresent: true)
  155. }
  156. func testUnexpectedInputClosesEventuallyWhenDripFed() {
  157. self.setUp(tls: false)
  158. var bytes = ByteBuffer(repeating: UInt8(ascii: "a"), count: 2048)
  159. while let slice = bytes.readSlice(length: 1) {
  160. assertThat(try self.channel.writeInbound(slice), .doesNotThrow())
  161. self.assertConfigurator(isPresent: true)
  162. self.assertHTTP2Handler(isPresent: false)
  163. self.assertGRPCWebToHTTP2Handler(isPresent: false)
  164. }
  165. self.channel.embeddedEventLoop.run()
  166. assertThat(try self.channel.closeFuture.wait(), .doesNotThrow())
  167. }
  168. func testReadsAreUnbufferedAfterConfiguration() throws {
  169. self.setUp(tls: false)
  170. var bytes = ByteBuffer(staticString: "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")
  171. // A SETTINGS frame MUST follow the connection preface. Append one so that the HTTP/2 handler
  172. // responds with its initial settings (and we validate that we forward frames once configuring).
  173. let emptySettingsFrameBytes: [UInt8] = [
  174. 0x00, 0x00, 0x00, // 3-byte payload length (0 bytes)
  175. 0x04, // 1-byte frame type (SETTINGS)
  176. 0x00, // 1-byte flags (none)
  177. 0x00, 0x00, 0x00, 0x00, // 4-byte stream identifier
  178. ]
  179. bytes.writeBytes(emptySettingsFrameBytes)
  180. // Do the setup.
  181. assertThat(try self.channel.writeInbound(bytes), .doesNotThrow())
  182. self.assertConfigurator(isPresent: false)
  183. self.assertHTTP2Handler(isPresent: true)
  184. // We expect the server to respond with a SETTINGS frame now.
  185. let ioData = try channel.readOutbound(as: IOData.self)
  186. switch ioData {
  187. case var .some(.byteBuffer(buffer)):
  188. if let frame = buffer.readBytes(length: 9) {
  189. // Just check it's a SETTINGS frame.
  190. assertThat(frame[3], .is(0x04))
  191. } else {
  192. XCTFail("Expected more bytes")
  193. }
  194. default:
  195. XCTFail("Expected ByteBuffer but got \(String(describing: ioData))")
  196. }
  197. }
  198. #if canImport(NIOSSL)
  199. func testALPNIsPreferredOverBytes() throws {
  200. self.setUp(tls: true, requireALPN: true)
  201. // Write in an HTTP/1 request line. This should just be buffered.
  202. let bytes = ByteBuffer(staticString: "GET http://www.foo.bar HTTP/1.1\r\n")
  203. assertThat(try self.channel.writeInbound(bytes), .doesNotThrow())
  204. self.assertConfigurator(isPresent: true)
  205. self.assertHTTP2Handler(isPresent: false)
  206. self.assertGRPCWebToHTTP2Handler(isPresent: false)
  207. // Now configure HTTP/2 with ALPN. This should be used to configure the pipeline.
  208. let event = TLSUserEvent.handshakeCompleted(negotiatedProtocol: "h2")
  209. self.channel.pipeline.fireUserInboundEventTriggered(event)
  210. self.assertConfigurator(isPresent: false)
  211. self.assertGRPCWebToHTTP2Handler(isPresent: false)
  212. self.assertHTTP2Handler(isPresent: true)
  213. }
  214. func testALPNFallbackToAlreadyBufferedBytes() throws {
  215. self.setUp(tls: true, requireALPN: false)
  216. // Write in an HTTP/2 connection preface. This should just be buffered.
  217. let bytes = ByteBuffer(staticString: "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")
  218. assertThat(try self.channel.writeInbound(bytes), .doesNotThrow())
  219. self.assertConfigurator(isPresent: true)
  220. self.assertHTTP2Handler(isPresent: false)
  221. // Complete the handshake with no protocol negotiated, we should fallback to the buffered bytes.
  222. let event = TLSUserEvent.handshakeCompleted(negotiatedProtocol: nil)
  223. self.channel.pipeline.fireUserInboundEventTriggered(event)
  224. self.assertConfigurator(isPresent: false)
  225. self.assertHTTP2Handler(isPresent: true)
  226. }
  227. #endif // canImport(NIOSSL)
  228. }