2
0

ConnectionManagerChannelProvider.swift 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  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 tlsConfiguration: Optional<TLSConfiguration>
  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. tlsConfiguration: TLSConfiguration?,
  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.tlsConfiguration = tlsConfiguration
  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. self.init(
  67. connectionTarget: configuration.target,
  68. connectionKeepalive: configuration.connectionKeepalive,
  69. connectionIdleTimeout: configuration.connectionIdleTimeout,
  70. tlsConfiguration: configuration.tls?.configuration,
  71. tlsHostnameOverride: configuration.tls?.hostnameOverride,
  72. tlsCustomVerificationCallback: configuration.tls?.customVerificationCallback,
  73. httpTargetWindowSize: configuration.httpTargetWindowSize,
  74. errorDelegate: configuration.errorDelegate,
  75. debugChannelInitializer: configuration.debugChannelInitializer
  76. )
  77. }
  78. private var serverHostname: String? {
  79. let hostname = self.tlsHostnameOverride ?? self.connectionTarget.host
  80. return hostname.isIPAddress ? nil : hostname
  81. }
  82. private var hasTLS: Bool {
  83. return self.tlsConfiguration != nil
  84. }
  85. private func requiresZeroLengthWorkaround(eventLoop: EventLoop) -> Bool {
  86. return PlatformSupport.requiresZeroLengthWriteWorkaround(group: eventLoop, hasTLS: self.hasTLS)
  87. }
  88. internal func makeChannel(
  89. managedBy connectionManager: ConnectionManager,
  90. onEventLoop eventLoop: EventLoop,
  91. connectTimeout: TimeAmount?,
  92. logger: Logger
  93. ) -> EventLoopFuture<Channel> {
  94. let hostname = self.serverHostname
  95. let needsZeroLengthWriteWorkaround = self.requiresZeroLengthWorkaround(eventLoop: eventLoop)
  96. let bootstrap = PlatformSupport.makeClientBootstrap(group: eventLoop, logger: logger)
  97. .channelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1)
  98. .channelOption(ChannelOptions.socket(IPPROTO_TCP, TCP_NODELAY), value: 1)
  99. .channelInitializer { channel in
  100. let sync = channel.pipeline.syncOperations
  101. do {
  102. try sync.configureGRPCClient(
  103. channel: channel,
  104. httpTargetWindowSize: self.httpTargetWindowSize,
  105. tlsConfiguration: self.tlsConfiguration,
  106. tlsServerHostname: hostname,
  107. connectionManager: connectionManager,
  108. connectionKeepalive: self.connectionKeepalive,
  109. connectionIdleTimeout: self.connectionIdleTimeout,
  110. errorDelegate: self.errorDelegate,
  111. requiresZeroLengthWriteWorkaround: needsZeroLengthWriteWorkaround,
  112. logger: logger,
  113. customVerificationCallback: self.tlsCustomVerificationCallback
  114. )
  115. } catch {
  116. return channel.eventLoop.makeFailedFuture(error)
  117. }
  118. // Run the debug initializer, if there is one.
  119. if let debugInitializer = self.debugChannelInitializer {
  120. return debugInitializer(channel)
  121. } else {
  122. return channel.eventLoop.makeSucceededVoidFuture()
  123. }
  124. }
  125. if let connectTimeout = connectTimeout {
  126. _ = bootstrap.connectTimeout(connectTimeout)
  127. }
  128. return bootstrap.connect(to: self.connectionTarget)
  129. }
  130. }