GRPCServerPipelineConfiguratorTests.swift 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  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. @testable import GRPC
  17. import NIOCore
  18. import NIOEmbedded
  19. import NIOHTTP2
  20. import NIOTLS
  21. import XCTest
  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. configuration.tlsConfiguration = .makeServerConfigurationBackedByNIOSSL(
  52. certificateChain: [],
  53. privateKey: .file("not used"),
  54. requireALPN: requireALPN
  55. )
  56. }
  57. let handler = GRPCServerPipelineConfigurator(configuration: configuration)
  58. assertThat(try self.channel.pipeline.addHandler(handler).wait(), .doesNotThrow())
  59. }
  60. func testHTTP2SetupViaALPN() {
  61. self.setUp(tls: true, requireALPN: true)
  62. let event = TLSUserEvent.handshakeCompleted(negotiatedProtocol: "h2")
  63. self.channel.pipeline.fireUserInboundEventTriggered(event)
  64. self.assertConfigurator(isPresent: false)
  65. self.assertHTTP2Handler(isPresent: true)
  66. }
  67. func testGRPCExpSetupViaALPN() {
  68. self.setUp(tls: true, requireALPN: true)
  69. let event = TLSUserEvent.handshakeCompleted(negotiatedProtocol: "grpc-exp")
  70. self.channel.pipeline.fireUserInboundEventTriggered(event)
  71. self.assertConfigurator(isPresent: false)
  72. self.assertHTTP2Handler(isPresent: true)
  73. }
  74. func testHTTP1Dot1SetupViaALPN() {
  75. self.setUp(tls: true, requireALPN: true)
  76. let event = TLSUserEvent.handshakeCompleted(negotiatedProtocol: "http/1.1")
  77. self.channel.pipeline.fireUserInboundEventTriggered(event)
  78. self.assertConfigurator(isPresent: false)
  79. self.assertGRPCWebToHTTP2Handler(isPresent: true)
  80. }
  81. func testUnrecognisedALPNCloses() {
  82. self.setUp(tls: true, requireALPN: true)
  83. let event = TLSUserEvent.handshakeCompleted(negotiatedProtocol: "unsupported")
  84. self.channel.pipeline.fireUserInboundEventTriggered(event)
  85. self.channel.embeddedEventLoop.run()
  86. assertThat(try self.channel.closeFuture.wait(), .doesNotThrow())
  87. }
  88. func testNoNegotiatedProtocolCloses() {
  89. self.setUp(tls: true, requireALPN: true)
  90. let event = TLSUserEvent.handshakeCompleted(negotiatedProtocol: nil)
  91. self.channel.pipeline.fireUserInboundEventTriggered(event)
  92. self.channel.embeddedEventLoop.run()
  93. assertThat(try self.channel.closeFuture.wait(), .doesNotThrow())
  94. }
  95. func testNoNegotiatedProtocolFallbackToBytesWhenALPNNotRequired() throws {
  96. self.setUp(tls: true, requireALPN: false)
  97. // Require ALPN is disabled, so this is a no-op.
  98. let event = TLSUserEvent.handshakeCompleted(negotiatedProtocol: nil)
  99. self.channel.pipeline.fireUserInboundEventTriggered(event)
  100. // Configure via bytes.
  101. let bytes = ByteBuffer(staticString: "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")
  102. assertThat(try self.channel.writeInbound(bytes), .doesNotThrow())
  103. self.assertConfigurator(isPresent: false)
  104. self.assertHTTP2Handler(isPresent: true)
  105. }
  106. func testHTTP2SetupViaBytes() {
  107. self.setUp(tls: false)
  108. let bytes = ByteBuffer(staticString: "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")
  109. assertThat(try self.channel.writeInbound(bytes), .doesNotThrow())
  110. self.assertConfigurator(isPresent: false)
  111. self.assertHTTP2Handler(isPresent: true)
  112. }
  113. func testHTTP1Dot1SetupViaBytes() {
  114. self.setUp(tls: false)
  115. let bytes = ByteBuffer(staticString: "GET http://www.foo.bar HTTP/1.1\r\n")
  116. assertThat(try self.channel.writeInbound(bytes), .doesNotThrow())
  117. self.assertConfigurator(isPresent: false)
  118. self.assertGRPCWebToHTTP2Handler(isPresent: true)
  119. }
  120. func testReadsAreUnbufferedAfterConfiguration() throws {
  121. self.setUp(tls: false)
  122. var bytes = ByteBuffer(staticString: "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")
  123. // A SETTINGS frame MUST follow the connection preface. Append one so that the HTTP/2 handler
  124. // responds with its initial settings (and we validate that we forward frames once configuring).
  125. let emptySettingsFrameBytes: [UInt8] = [
  126. 0x00, 0x00, 0x00, // 3-byte payload length (0 bytes)
  127. 0x04, // 1-byte frame type (SETTINGS)
  128. 0x00, // 1-byte flags (none)
  129. 0x00, 0x00, 0x00, 0x00, // 4-byte stream identifier
  130. ]
  131. bytes.writeBytes(emptySettingsFrameBytes)
  132. // Do the setup.
  133. assertThat(try self.channel.writeInbound(bytes), .doesNotThrow())
  134. self.assertConfigurator(isPresent: false)
  135. self.assertHTTP2Handler(isPresent: true)
  136. // We expect the server to respond with a SETTINGS frame now.
  137. let ioData = try channel.readOutbound(as: IOData.self)
  138. switch ioData {
  139. case var .some(.byteBuffer(buffer)):
  140. if let frame = buffer.readBytes(length: 9) {
  141. // Just check it's a SETTINGS frame.
  142. assertThat(frame[3], .is(0x04))
  143. } else {
  144. XCTFail("Expected more bytes")
  145. }
  146. default:
  147. XCTFail("Expected ByteBuffer but got \(String(describing: ioData))")
  148. }
  149. }
  150. func testALPNIsPreferredOverBytes() throws {
  151. self.setUp(tls: true, requireALPN: true)
  152. // Write in an HTTP/1 request line. This should just be buffered.
  153. let bytes = ByteBuffer(staticString: "GET http://www.foo.bar HTTP/1.1\r\n")
  154. assertThat(try self.channel.writeInbound(bytes), .doesNotThrow())
  155. self.assertConfigurator(isPresent: true)
  156. self.assertHTTP2Handler(isPresent: false)
  157. self.assertGRPCWebToHTTP2Handler(isPresent: false)
  158. // Now configure HTTP/2 with ALPN. This should be used to configure the pipeline.
  159. let event = TLSUserEvent.handshakeCompleted(negotiatedProtocol: "h2")
  160. self.channel.pipeline.fireUserInboundEventTriggered(event)
  161. self.assertConfigurator(isPresent: false)
  162. self.assertGRPCWebToHTTP2Handler(isPresent: false)
  163. self.assertHTTP2Handler(isPresent: true)
  164. }
  165. func testALPNFallbackToAlreadyBufferedBytes() throws {
  166. self.setUp(tls: true, requireALPN: false)
  167. // Write in an HTTP/2 connection preface. This should just be buffered.
  168. let bytes = ByteBuffer(staticString: "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")
  169. assertThat(try self.channel.writeInbound(bytes), .doesNotThrow())
  170. self.assertConfigurator(isPresent: true)
  171. self.assertHTTP2Handler(isPresent: false)
  172. // Complete the handshake with no protocol negotiated, we should fallback to the buffered bytes.
  173. let event = TLSUserEvent.handshakeCompleted(negotiatedProtocol: nil)
  174. self.channel.pipeline.fireUserInboundEventTriggered(event)
  175. self.assertConfigurator(isPresent: false)
  176. self.assertHTTP2Handler(isPresent: true)
  177. }
  178. }