HTTP2ServerTransport+TransportServices.swift 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. /*
  2. * Copyright 2024, 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. #if canImport(Network)
  17. public import GRPCCore
  18. public import NIOTransportServices // has to be public because of default argument value in init
  19. public import GRPCNIOTransportCore
  20. private import NIOCore
  21. private import NIOExtras
  22. private import NIOHTTP2
  23. private import Network
  24. private import Synchronization
  25. @available(gRPCSwiftNIOTransport 1.0, *)
  26. extension HTTP2ServerTransport {
  27. /// A NIO Transport Services-backed implementation of a server transport.
  28. public struct TransportServices: ServerTransport, ListeningServerTransport {
  29. public typealias Bytes = GRPCNIOTransportBytes
  30. private struct ListenerFactory: HTTP2ListenerFactory {
  31. let config: Config
  32. let transportSecurity: TransportSecurity
  33. func makeListeningChannel(
  34. eventLoopGroup: any EventLoopGroup,
  35. address: GRPCNIOTransportCore.SocketAddress,
  36. serverQuiescingHelper: ServerQuiescingHelper
  37. ) async throws -> NIOAsyncChannel<AcceptedChannel, Never> {
  38. let bootstrap: NIOTSListenerBootstrap
  39. let requireALPN: Bool
  40. let scheme: Scheme
  41. switch self.transportSecurity.wrapped {
  42. case .plaintext:
  43. requireALPN = false
  44. scheme = .http
  45. bootstrap = NIOTSListenerBootstrap(group: eventLoopGroup)
  46. case .tls(let tlsConfig):
  47. requireALPN = tlsConfig.requireALPN
  48. scheme = .https
  49. bootstrap = NIOTSListenerBootstrap(group: eventLoopGroup)
  50. .tlsOptions(try NWProtocolTLS.Options(tlsConfig))
  51. }
  52. let serverChannel =
  53. try await bootstrap
  54. .serverChannelOption(.socketOption(.so_reuseaddr), value: 1)
  55. .serverChannelInitializer { channel in
  56. channel.eventLoop.makeCompletedFuture {
  57. let quiescingHandler = serverQuiescingHelper.makeServerChannelHandler(
  58. channel: channel
  59. )
  60. try channel.pipeline.syncOperations.addHandler(quiescingHandler)
  61. }.runInitializerIfSet(
  62. self.config.channelDebuggingCallbacks.onBindTCPListener,
  63. on: channel
  64. )
  65. }
  66. .bind(to: address) { channel in
  67. return channel.eventLoop.makeCompletedFuture {
  68. try channel.pipeline.syncOperations.configureGRPCServerPipeline(
  69. channel: channel,
  70. compressionConfig: self.config.compression,
  71. connectionConfig: self.config.connection,
  72. http2Config: self.config.http2,
  73. rpcConfig: self.config.rpc,
  74. debugConfig: self.config.channelDebuggingCallbacks,
  75. requireALPN: requireALPN,
  76. scheme: scheme
  77. )
  78. }.runInitializerIfSet(
  79. self.config.channelDebuggingCallbacks.onAcceptTCPConnection,
  80. on: channel
  81. )
  82. }
  83. return serverChannel
  84. }
  85. }
  86. private let underlyingTransport: CommonHTTP2ServerTransport<ListenerFactory>
  87. /// The listening address for this server transport.
  88. ///
  89. /// It is an `async` property because it will only return once the address has been successfully bound.
  90. ///
  91. /// - Throws: A runtime error will be thrown if the address could not be bound or is not bound any
  92. /// longer, because the transport isn't listening anymore. It can also throw if the transport returned an
  93. /// invalid address.
  94. public var listeningAddress: GRPCNIOTransportCore.SocketAddress {
  95. get async throws {
  96. try await self.underlyingTransport.listeningAddress
  97. }
  98. }
  99. /// Create a new `TransportServices` transport.
  100. ///
  101. /// - Parameters:
  102. /// - address: The address to which the server should be bound.
  103. /// - transportSecurity: The security settings applied to the transport.
  104. /// - config: The transport configuration.
  105. /// - eventLoopGroup: The ELG from which to get ELs to run this transport.
  106. public init(
  107. address: GRPCNIOTransportCore.SocketAddress,
  108. transportSecurity: TransportSecurity,
  109. config: Config = .defaults,
  110. eventLoopGroup: NIOTSEventLoopGroup = .singletonNIOTSEventLoopGroup
  111. ) {
  112. let factory = ListenerFactory(config: config, transportSecurity: transportSecurity)
  113. let helper = ServerQuiescingHelper(group: eventLoopGroup)
  114. self.underlyingTransport = CommonHTTP2ServerTransport(
  115. address: address,
  116. eventLoopGroup: eventLoopGroup,
  117. quiescingHelper: helper,
  118. listenerFactory: factory
  119. )
  120. }
  121. public func listen(
  122. streamHandler: @escaping @Sendable (
  123. _ stream: RPCStream<Inbound, Outbound>,
  124. _ context: ServerContext
  125. ) async -> Void
  126. ) async throws {
  127. try await self.underlyingTransport.listen(streamHandler: streamHandler)
  128. }
  129. public func beginGracefulShutdown() {
  130. self.underlyingTransport.beginGracefulShutdown()
  131. }
  132. }
  133. }
  134. @available(gRPCSwiftNIOTransport 1.0, *)
  135. extension HTTP2ServerTransport.TransportServices {
  136. /// Configuration for the `TransportServices` transport.
  137. public struct Config: Sendable {
  138. /// Compression configuration.
  139. public var compression: HTTP2ServerTransport.Config.Compression
  140. /// Connection configuration.
  141. public var connection: HTTP2ServerTransport.Config.Connection
  142. /// HTTP2 configuration.
  143. public var http2: HTTP2ServerTransport.Config.HTTP2
  144. /// RPC configuration.
  145. public var rpc: HTTP2ServerTransport.Config.RPC
  146. /// Channel callbacks for debugging.
  147. public var channelDebuggingCallbacks: HTTP2ServerTransport.Config.ChannelDebuggingCallbacks
  148. /// Construct a new `Config`.
  149. /// - Parameters:
  150. /// - compression: Compression configuration.
  151. /// - connection: Connection configuration.
  152. /// - http2: HTTP2 configuration.
  153. /// - rpc: RPC configuration.
  154. /// - channelDebuggingCallbacks: Channel callbacks for debugging.
  155. ///
  156. /// - SeeAlso: ``defaults(configure:)`` and ``defaults``.
  157. public init(
  158. compression: HTTP2ServerTransport.Config.Compression,
  159. connection: HTTP2ServerTransport.Config.Connection,
  160. http2: HTTP2ServerTransport.Config.HTTP2,
  161. rpc: HTTP2ServerTransport.Config.RPC,
  162. channelDebuggingCallbacks: HTTP2ServerTransport.Config.ChannelDebuggingCallbacks
  163. ) {
  164. self.compression = compression
  165. self.connection = connection
  166. self.http2 = http2
  167. self.rpc = rpc
  168. self.channelDebuggingCallbacks = channelDebuggingCallbacks
  169. }
  170. public static var defaults: Self {
  171. Self.defaults()
  172. }
  173. /// Default values for the different configurations.
  174. ///
  175. /// - Parameters:
  176. /// - configure: A closure which allows you to modify the defaults before returning them.
  177. public static func defaults(
  178. configure: (_ config: inout Self) -> Void = { _ in }
  179. ) -> Self {
  180. var config = Self(
  181. compression: .defaults,
  182. connection: .defaults,
  183. http2: .defaults,
  184. rpc: .defaults,
  185. channelDebuggingCallbacks: .defaults
  186. )
  187. configure(&config)
  188. return config
  189. }
  190. }
  191. }
  192. @available(gRPCSwiftNIOTransport 1.0, *)
  193. extension NIOTSListenerBootstrap {
  194. fileprivate func bind<Output: Sendable>(
  195. to address: GRPCNIOTransportCore.SocketAddress,
  196. childChannelInitializer: @escaping @Sendable (any Channel) -> EventLoopFuture<Output>
  197. ) async throws -> NIOAsyncChannel<Output, Never> {
  198. if address.virtualSocket != nil {
  199. throw RuntimeError(
  200. code: .transportError,
  201. message: """
  202. Virtual sockets are not supported by 'HTTP2ServerTransport.TransportServices'. \
  203. Please use the 'HTTP2ServerTransport.Posix' transport.
  204. """
  205. )
  206. } else {
  207. return try await self.bind(
  208. to: NIOCore.SocketAddress(address),
  209. childChannelInitializer: childChannelInitializer
  210. )
  211. }
  212. }
  213. }
  214. @available(gRPCSwiftNIOTransport 1.0, *)
  215. extension ServerTransport where Self == HTTP2ServerTransport.TransportServices {
  216. /// Create a new `TransportServices` based HTTP/2 server transport.
  217. ///
  218. /// - Parameters:
  219. /// - address: The address to which the server should be bound.
  220. /// - transportSecurity: The security settings applied to the transport.
  221. /// - config: The transport configuration.
  222. /// - eventLoopGroup: The underlying NIO `EventLoopGroup` to the server on. This must
  223. /// be a `NIOTSEventLoopGroup` or an `EventLoop` from a `NIOTSEventLoopGroup`.
  224. public static func http2NIOTS(
  225. address: GRPCNIOTransportCore.SocketAddress,
  226. transportSecurity: HTTP2ServerTransport.TransportServices.TransportSecurity,
  227. config: HTTP2ServerTransport.TransportServices.Config = .defaults,
  228. eventLoopGroup: NIOTSEventLoopGroup = .singletonNIOTSEventLoopGroup
  229. ) -> Self {
  230. return HTTP2ServerTransport.TransportServices(
  231. address: address,
  232. transportSecurity: transportSecurity,
  233. config: config,
  234. eventLoopGroup: eventLoopGroup
  235. )
  236. }
  237. }
  238. @available(gRPCSwiftNIOTransport 1.0, *)
  239. extension NWProtocolTLS.Options {
  240. convenience init(_ tlsConfig: HTTP2ServerTransport.TransportServices.TLS) throws {
  241. self.init()
  242. guard let sec_identity = sec_identity_create(try tlsConfig.identityProvider()) else {
  243. throw RuntimeError(
  244. code: .transportError,
  245. message: """
  246. There was an issue creating the SecIdentity required to set up TLS. \
  247. Please check your TLS configuration.
  248. """
  249. )
  250. }
  251. sec_protocol_options_set_local_identity(
  252. self.securityProtocolOptions,
  253. sec_identity
  254. )
  255. switch tlsConfig.clientCertificateVerification.wrapped {
  256. case .doNotVerify:
  257. sec_protocol_options_set_peer_authentication_required(
  258. self.securityProtocolOptions,
  259. false
  260. )
  261. case .fullVerification, .noHostnameVerification:
  262. sec_protocol_options_set_peer_authentication_required(
  263. self.securityProtocolOptions,
  264. true
  265. )
  266. }
  267. sec_protocol_options_set_min_tls_protocol_version(
  268. self.securityProtocolOptions,
  269. .TLSv12
  270. )
  271. for `protocol` in ["grpc-exp", "h2"] {
  272. sec_protocol_options_add_tls_application_protocol(
  273. self.securityProtocolOptions,
  274. `protocol`
  275. )
  276. }
  277. self.setUpVerifyBlock(trustRootsSource: tlsConfig.trustRoots)
  278. }
  279. }
  280. #endif