NIOChannelPipeline+GRPC.swift 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  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. import GRPCCore
  17. import NIOCore
  18. import NIOHPACK
  19. import NIOHTTP2
  20. @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  21. extension ChannelPipeline.SynchronousOperations {
  22. @_spi(Package) public typealias HTTP2ConnectionChannel = NIOAsyncChannel<HTTP2Frame, HTTP2Frame>
  23. @_spi(Package) public typealias HTTP2StreamMultiplexer = NIOHTTP2Handler.AsyncStreamMultiplexer<
  24. (NIOAsyncChannel<RPCRequestPart, RPCResponsePart>, EventLoopFuture<MethodDescriptor>)
  25. >
  26. @_spi(Package)
  27. public func configureGRPCServerPipeline(
  28. channel: any Channel,
  29. compressionConfig: HTTP2ServerTransport.Config.Compression,
  30. keepaliveConfig: HTTP2ServerTransport.Config.Keepalive,
  31. connectionConfig: HTTP2ServerTransport.Config.Connection,
  32. http2Config: HTTP2ServerTransport.Config.HTTP2,
  33. rpcConfig: HTTP2ServerTransport.Config.RPC,
  34. useTLS: Bool
  35. ) throws -> (HTTP2ConnectionChannel, HTTP2StreamMultiplexer) {
  36. let serverConnectionHandler = ServerConnectionManagementHandler(
  37. eventLoop: self.eventLoop,
  38. maxIdleTime: connectionConfig.maxIdleTime.map { TimeAmount($0) },
  39. maxAge: connectionConfig.maxAge.map { TimeAmount($0) },
  40. maxGraceTime: connectionConfig.maxGraceTime.map { TimeAmount($0) },
  41. keepaliveTime: TimeAmount(keepaliveConfig.time),
  42. keepaliveTimeout: TimeAmount(keepaliveConfig.timeout),
  43. allowKeepaliveWithoutCalls: keepaliveConfig.permitWithoutCalls,
  44. minPingIntervalWithoutCalls: TimeAmount(keepaliveConfig.minPingIntervalWithoutCalls)
  45. )
  46. let flushNotificationHandler = GRPCServerFlushNotificationHandler(
  47. serverConnectionManagementHandler: serverConnectionHandler
  48. )
  49. try self.addHandler(flushNotificationHandler)
  50. var http2HandlerConnectionConfiguration = NIOHTTP2Handler.ConnectionConfiguration()
  51. var http2HandlerHTTP2Settings = HTTP2Settings([
  52. HTTP2Setting(parameter: .initialWindowSize, value: http2Config.targetWindowSize),
  53. HTTP2Setting(parameter: .maxFrameSize, value: http2Config.maxFrameSize),
  54. HTTP2Setting(parameter: .maxHeaderListSize, value: HPACKDecoder.defaultMaxHeaderListSize),
  55. ])
  56. if let maxConcurrentStreams = http2Config.maxConcurrentStreams {
  57. http2HandlerHTTP2Settings.append(
  58. HTTP2Setting(parameter: .maxConcurrentStreams, value: maxConcurrentStreams)
  59. )
  60. }
  61. http2HandlerConnectionConfiguration.initialSettings = http2HandlerHTTP2Settings
  62. var http2HandlerStreamConfiguration = NIOHTTP2Handler.StreamConfiguration()
  63. http2HandlerStreamConfiguration.targetWindowSize = http2Config.targetWindowSize
  64. let streamMultiplexer = try self.configureAsyncHTTP2Pipeline(
  65. mode: .server,
  66. streamDelegate: serverConnectionHandler.http2StreamDelegate,
  67. configuration: NIOHTTP2Handler.Configuration(
  68. connection: http2HandlerConnectionConfiguration,
  69. stream: http2HandlerStreamConfiguration
  70. )
  71. ) { streamChannel in
  72. return streamChannel.eventLoop.makeCompletedFuture {
  73. let methodDescriptorPromise = streamChannel.eventLoop.makePromise(of: MethodDescriptor.self)
  74. let streamHandler = GRPCServerStreamHandler(
  75. scheme: useTLS ? .https : .http,
  76. acceptedEncodings: compressionConfig.enabledAlgorithms,
  77. maximumPayloadSize: rpcConfig.maxRequestPayloadSize,
  78. methodDescriptorPromise: methodDescriptorPromise
  79. )
  80. try streamChannel.pipeline.syncOperations.addHandler(streamHandler)
  81. let asyncStreamChannel = try NIOAsyncChannel<RPCRequestPart, RPCResponsePart>(
  82. wrappingChannelSynchronously: streamChannel
  83. )
  84. return (asyncStreamChannel, methodDescriptorPromise.futureResult)
  85. }
  86. }
  87. try self.addHandler(serverConnectionHandler)
  88. let connectionChannel = try NIOAsyncChannel<HTTP2Frame, HTTP2Frame>(
  89. wrappingChannelSynchronously: channel
  90. )
  91. return (connectionChannel, streamMultiplexer)
  92. }
  93. }
  94. extension ChannelPipeline.SynchronousOperations {
  95. @_spi(Package)
  96. @available(macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0, *)
  97. public func configureGRPCClientPipeline(
  98. channel: Channel,
  99. config: GRPCChannel.Config
  100. ) throws -> (
  101. NIOAsyncChannel<ClientConnectionEvent, Void>,
  102. NIOHTTP2Handler.AsyncStreamMultiplexer<Void>
  103. ) {
  104. // Window size which mustn't exceed 2^32 - 1 (RFC 9113 § 6.1.3).
  105. let clampedTargetWindowSize = min(config.http2.targetWindowSize, (1 << 31) - 1)
  106. // Max frame size must be in the range 2^14 ..< 2^24 (RFC 9113 § 6.1.3).
  107. let clampedMaxFrameSize: Int
  108. if config.http2.maxFrameSize >= (1 << 24) {
  109. clampedMaxFrameSize = (1 << 24) - 1
  110. } else if config.http2.maxFrameSize < (1 << 14) {
  111. clampedMaxFrameSize = (1 << 14)
  112. } else {
  113. clampedMaxFrameSize = config.http2.maxFrameSize
  114. }
  115. // Use NIOs defaults as a starting point.
  116. var http2 = NIOHTTP2Handler.Configuration()
  117. http2.stream.targetWindowSize = clampedTargetWindowSize
  118. http2.connection.initialSettings = [
  119. // Disallow servers from creating push streams.
  120. HTTP2Setting(parameter: .enablePush, value: 0),
  121. // Set the initial window size and max frame size to the clamped configured values.
  122. HTTP2Setting(parameter: .initialWindowSize, value: clampedTargetWindowSize),
  123. HTTP2Setting(parameter: .maxFrameSize, value: clampedMaxFrameSize),
  124. // Use NIOs default max header list size (16kB)
  125. HTTP2Setting(parameter: .maxHeaderListSize, value: HPACKDecoder.defaultMaxHeaderListSize),
  126. ]
  127. let connectionHandler = ClientConnectionHandler(
  128. eventLoop: self.eventLoop,
  129. maxIdleTime: config.idle.map { TimeAmount($0.maxTime) },
  130. keepaliveTime: config.keepalive.map { TimeAmount($0.time) },
  131. keepaliveTimeout: config.keepalive.map { TimeAmount($0.timeout) },
  132. keepaliveWithoutCalls: config.keepalive?.permitWithoutCalls ?? false
  133. )
  134. let multiplexer = try self.configureAsyncHTTP2Pipeline(
  135. mode: .client,
  136. streamDelegate: connectionHandler.http2StreamDelegate,
  137. configuration: http2
  138. ) { stream in
  139. // Shouldn't happen, push-promises are disabled so the server shouldn't be able to
  140. // open streams.
  141. stream.close()
  142. }
  143. try self.addHandler(connectionHandler)
  144. let connection = try NIOAsyncChannel(
  145. wrappingChannelSynchronously: channel,
  146. configuration: NIOAsyncChannel.Configuration(
  147. inboundType: ClientConnectionEvent.self,
  148. outboundType: Void.self
  149. )
  150. )
  151. return (connection, multiplexer)
  152. }
  153. }