ConnectionManagerChannelProvider.swift 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. /*
  2. * Copyright 2021, 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 Logging
  17. import NIO
  18. import NIOSSL
  19. internal protocol ConnectionManagerChannelProvider {
  20. /// Make an `EventLoopFuture<Channel>`.
  21. ///
  22. /// - Parameters:
  23. /// - connectionManager: The `ConnectionManager` requesting the `Channel`.
  24. /// - eventLoop: The `EventLoop` to use for the`Channel`.
  25. /// - connectTimeout: Optional connection timeout when starting the connection.
  26. /// - logger: A logger.
  27. func makeChannel(
  28. managedBy connectionManager: ConnectionManager,
  29. onEventLoop eventLoop: EventLoop,
  30. connectTimeout: TimeAmount?,
  31. logger: Logger
  32. ) -> EventLoopFuture<Channel>
  33. }
  34. internal struct DefaultChannelProvider: ConnectionManagerChannelProvider {
  35. internal var connectionTarget: ConnectionTarget
  36. internal var connectionKeepalive: ClientConnectionKeepalive
  37. internal var connectionIdleTimeout: TimeAmount
  38. internal var sslContext: Result<NIOSSLContext, Error>?
  39. internal var tlsHostnameOverride: Optional<String>
  40. internal var tlsCustomVerificationCallback: Optional<NIOSSLCustomVerificationCallback>
  41. internal var httpTargetWindowSize: Int
  42. internal var errorDelegate: Optional<ClientErrorDelegate>
  43. internal var debugChannelInitializer: Optional<(Channel) -> EventLoopFuture<Void>>
  44. internal init(
  45. connectionTarget: ConnectionTarget,
  46. connectionKeepalive: ClientConnectionKeepalive,
  47. connectionIdleTimeout: TimeAmount,
  48. sslContext: Result<NIOSSLContext, Error>?,
  49. tlsHostnameOverride: String?,
  50. tlsCustomVerificationCallback: NIOSSLCustomVerificationCallback?,
  51. httpTargetWindowSize: Int,
  52. errorDelegate: ClientErrorDelegate?,
  53. debugChannelInitializer: ((Channel) -> EventLoopFuture<Void>)?
  54. ) {
  55. self.connectionTarget = connectionTarget
  56. self.connectionKeepalive = connectionKeepalive
  57. self.connectionIdleTimeout = connectionIdleTimeout
  58. self.sslContext = sslContext
  59. self.tlsHostnameOverride = tlsHostnameOverride
  60. self.tlsCustomVerificationCallback = tlsCustomVerificationCallback
  61. self.httpTargetWindowSize = httpTargetWindowSize
  62. self.errorDelegate = errorDelegate
  63. self.debugChannelInitializer = debugChannelInitializer
  64. }
  65. internal init(configuration: ClientConnection.Configuration) {
  66. // Making a `NIOSSLContext` is expensive and we should only do it (at most) once per TLS
  67. // configuration. We do it now and surface any error during channel creation (we're limited by
  68. // our API in when we can throw any error).
  69. let sslContext: Result<NIOSSLContext, Error>? = configuration.tls.map { tls in
  70. return Result {
  71. try NIOSSLContext(configuration: tls.configuration)
  72. }
  73. }
  74. self.init(
  75. connectionTarget: configuration.target,
  76. connectionKeepalive: configuration.connectionKeepalive,
  77. connectionIdleTimeout: configuration.connectionIdleTimeout,
  78. sslContext: sslContext,
  79. tlsHostnameOverride: configuration.tls?.hostnameOverride,
  80. tlsCustomVerificationCallback: configuration.tls?.customVerificationCallback,
  81. httpTargetWindowSize: configuration.httpTargetWindowSize,
  82. errorDelegate: configuration.errorDelegate,
  83. debugChannelInitializer: configuration.debugChannelInitializer
  84. )
  85. }
  86. private var serverHostname: String? {
  87. let hostname = self.tlsHostnameOverride ?? self.connectionTarget.host
  88. return hostname.isIPAddress ? nil : hostname
  89. }
  90. private var hasTLS: Bool {
  91. return self.sslContext != nil
  92. }
  93. private func requiresZeroLengthWorkaround(eventLoop: EventLoop) -> Bool {
  94. return PlatformSupport.requiresZeroLengthWriteWorkaround(group: eventLoop, hasTLS: self.hasTLS)
  95. }
  96. internal func makeChannel(
  97. managedBy connectionManager: ConnectionManager,
  98. onEventLoop eventLoop: EventLoop,
  99. connectTimeout: TimeAmount?,
  100. logger: Logger
  101. ) -> EventLoopFuture<Channel> {
  102. let hostname = self.serverHostname
  103. let needsZeroLengthWriteWorkaround = self.requiresZeroLengthWorkaround(eventLoop: eventLoop)
  104. let bootstrap = PlatformSupport.makeClientBootstrap(group: eventLoop, logger: logger)
  105. .channelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1)
  106. .channelOption(ChannelOptions.socket(IPPROTO_TCP, TCP_NODELAY), value: 1)
  107. .channelInitializer { channel in
  108. let sync = channel.pipeline.syncOperations
  109. do {
  110. try sync.configureGRPCClient(
  111. channel: channel,
  112. httpTargetWindowSize: self.httpTargetWindowSize,
  113. sslContext: self.sslContext,
  114. tlsServerHostname: hostname,
  115. connectionManager: connectionManager,
  116. connectionKeepalive: self.connectionKeepalive,
  117. connectionIdleTimeout: self.connectionIdleTimeout,
  118. errorDelegate: self.errorDelegate,
  119. requiresZeroLengthWriteWorkaround: needsZeroLengthWriteWorkaround,
  120. logger: logger,
  121. customVerificationCallback: self.tlsCustomVerificationCallback
  122. )
  123. } catch {
  124. return channel.eventLoop.makeFailedFuture(error)
  125. }
  126. // Run the debug initializer, if there is one.
  127. if let debugInitializer = self.debugChannelInitializer {
  128. return debugInitializer(channel)
  129. } else {
  130. return channel.eventLoop.makeSucceededVoidFuture()
  131. }
  132. }
  133. if let connectTimeout = connectTimeout {
  134. _ = bootstrap.connectTimeout(connectTimeout)
  135. }
  136. return bootstrap.connect(to: self.connectionTarget)
  137. }
  138. }