GRPCChannelBuilder.swift 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  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. import Dispatch
  17. import Logging
  18. import NIOCore
  19. extension ClientConnection {
  20. /// Returns an insecure `ClientConnection` builder which is *not configured with TLS*.
  21. public static func insecure(group: EventLoopGroup) -> ClientConnection.Builder {
  22. return Builder(group: group)
  23. }
  24. /// Returns a `ClientConnection` builder configured with a TLS backend appropriate for the
  25. /// given `EventLoopGroup`.
  26. ///
  27. /// gRPC Swift offers two TLS 'backends'. The 'NIOSSL' backend is available on Darwin and Linux
  28. /// platforms and delegates to SwiftNIO SSL. On recent Darwin platforms (macOS 10.14+, iOS 12+,
  29. /// tvOS 12+, and watchOS 6+) the 'Network.framework' backend is available. The two backends have
  30. /// a number of incompatible configuration options and users are responsible for selecting the
  31. /// appropriate APIs. The TLS configuration options on the builder document which backends they
  32. /// support.
  33. ///
  34. /// TLS backends must also be used with an appropriate `EventLoopGroup` implementation. The
  35. /// 'NIOSSL' backend may be used either a `MultiThreadedEventLoopGroup` or a
  36. /// `NIOTSEventLoopGroup`. The 'Network.framework' backend may only be used with a
  37. /// `NIOTSEventLoopGroup`.
  38. ///
  39. /// This function returns a builder using the `NIOSSL` backend if a `MultiThreadedEventLoopGroup`
  40. /// is supplied and a 'Network.framework' backend if a `NIOTSEventLoopGroup` is used.
  41. public static func usingPlatformAppropriateTLS(
  42. for group: EventLoopGroup
  43. ) -> ClientConnection.Builder.Secure {
  44. let networkPreference = NetworkPreference.userDefined(.matchingEventLoopGroup(group))
  45. return Builder.Secure(
  46. group: group,
  47. tlsConfiguration: .makeClientDefault(for: networkPreference)
  48. )
  49. }
  50. /// Returns a `ClientConnection` builder configured with the TLS backend appropriate for the
  51. /// provided configuration and `EventLoopGroup`.
  52. ///
  53. /// - Important: The caller is responsible for ensuring the provided `configuration` may be used
  54. /// the the `group`.
  55. public static func usingTLS(
  56. with configuration: GRPCTLSConfiguration,
  57. on group: EventLoopGroup
  58. ) -> ClientConnection.Builder.Secure {
  59. return Builder.Secure(group: group, tlsConfiguration: configuration)
  60. }
  61. }
  62. extension ClientConnection {
  63. public class Builder {
  64. private var configuration: ClientConnection.Configuration
  65. private var maybeTLS: GRPCTLSConfiguration? { return nil }
  66. private var connectionBackoff = ConnectionBackoff()
  67. private var connectionBackoffIsEnabled = true
  68. fileprivate init(group: EventLoopGroup) {
  69. // This is okay: the configuration is only consumed on a call to `connect` which sets the host
  70. // and port.
  71. self.configuration = .default(target: .hostAndPort("", .max), eventLoopGroup: group)
  72. }
  73. public func connect(host: String, port: Int) -> ClientConnection {
  74. // Finish setting up the configuration.
  75. self.configuration.target = .hostAndPort(host, port)
  76. self.configuration.connectionBackoff =
  77. self.connectionBackoffIsEnabled ? self.connectionBackoff : nil
  78. self.configuration.tlsConfiguration = self.maybeTLS
  79. return ClientConnection(configuration: self.configuration)
  80. }
  81. }
  82. }
  83. extension ClientConnection.Builder {
  84. public class Secure: ClientConnection.Builder {
  85. internal var tls: GRPCTLSConfiguration
  86. override internal var maybeTLS: GRPCTLSConfiguration? {
  87. return self.tls
  88. }
  89. internal init(group: EventLoopGroup, tlsConfiguration: GRPCTLSConfiguration) {
  90. group.preconditionCompatible(with: tlsConfiguration)
  91. self.tls = tlsConfiguration
  92. super.init(group: group)
  93. }
  94. /// Connect to `host` on port 443.
  95. public func connect(host: String) -> ClientConnection {
  96. return self.connect(host: host, port: 443)
  97. }
  98. }
  99. }
  100. extension ClientConnection.Builder {
  101. /// Sets the initial connection backoff. That is, the initial time to wait before re-attempting to
  102. /// establish a connection. Jitter will *not* be applied to the initial backoff. Defaults to
  103. /// 1 second if not set.
  104. @discardableResult
  105. public func withConnectionBackoff(initial amount: TimeAmount) -> Self {
  106. self.connectionBackoff.initialBackoff = .seconds(from: amount)
  107. return self
  108. }
  109. /// Set the maximum connection backoff. That is, the maximum amount of time to wait before
  110. /// re-attempting to establish a connection. Note that this time amount represents the maximum
  111. /// backoff *before* jitter is applied. Defaults to 120 seconds if not set.
  112. @discardableResult
  113. public func withConnectionBackoff(maximum amount: TimeAmount) -> Self {
  114. self.connectionBackoff.maximumBackoff = .seconds(from: amount)
  115. return self
  116. }
  117. /// Backoff is 'jittered' to randomise the amount of time to wait before re-attempting to
  118. /// establish a connection. The jittered backoff will be no more than `jitter ⨯ unjitteredBackoff`
  119. /// from `unjitteredBackoff`. Defaults to 0.2 if not set.
  120. ///
  121. /// - Precondition: `0 <= jitter <= 1`
  122. @discardableResult
  123. public func withConnectionBackoff(jitter: Double) -> Self {
  124. self.connectionBackoff.jitter = jitter
  125. return self
  126. }
  127. /// The multiplier for scaling the unjittered backoff between attempts to establish a connection.
  128. /// Defaults to 1.6 if not set.
  129. @discardableResult
  130. public func withConnectionBackoff(multiplier: Double) -> Self {
  131. self.connectionBackoff.multiplier = multiplier
  132. return self
  133. }
  134. /// The minimum timeout to use when attempting to establishing a connection. The connection
  135. /// timeout for each attempt is the larger of the jittered backoff and the minimum connection
  136. /// timeout. Defaults to 20 seconds if not set.
  137. @discardableResult
  138. public func withConnectionTimeout(minimum amount: TimeAmount) -> Self {
  139. self.connectionBackoff.minimumConnectionTimeout = .seconds(from: amount)
  140. return self
  141. }
  142. /// Sets the initial and maximum backoff to given amount. Disables jitter and sets the backoff
  143. /// multiplier to 1.0.
  144. @discardableResult
  145. public func withConnectionBackoff(fixed amount: TimeAmount) -> Self {
  146. let seconds = Double.seconds(from: amount)
  147. self.connectionBackoff.initialBackoff = seconds
  148. self.connectionBackoff.maximumBackoff = seconds
  149. self.connectionBackoff.multiplier = 1.0
  150. self.connectionBackoff.jitter = 0.0
  151. return self
  152. }
  153. /// Sets the limit on the number of times to attempt to re-establish a connection. Defaults
  154. /// to `.unlimited` if not set.
  155. @discardableResult
  156. public func withConnectionBackoff(retries: ConnectionBackoff.Retries) -> Self {
  157. self.connectionBackoff.retries = retries
  158. return self
  159. }
  160. /// Sets whether the connection should be re-established automatically if it is dropped. Defaults
  161. /// to `true` if not set.
  162. @discardableResult
  163. public func withConnectionReestablishment(enabled: Bool) -> Self {
  164. self.connectionBackoffIsEnabled = enabled
  165. return self
  166. }
  167. /// Sets a custom configuration for keepalive
  168. /// The defaults for client and server are determined by the gRPC keepalive
  169. /// [documentation] (https://github.com/grpc/grpc/blob/master/doc/keepalive.md).
  170. @discardableResult
  171. public func withKeepalive(_ keepalive: ClientConnectionKeepalive) -> Self {
  172. self.configuration.connectionKeepalive = keepalive
  173. return self
  174. }
  175. /// The amount of time to wait before closing the connection. The idle timeout will start only
  176. /// if there are no RPCs in progress and will be cancelled as soon as any RPCs start. If a
  177. /// connection becomes idle, starting a new RPC will automatically create a new connection.
  178. /// Defaults to 30 minutes if not set.
  179. @discardableResult
  180. public func withConnectionIdleTimeout(_ timeout: TimeAmount) -> Self {
  181. self.configuration.connectionIdleTimeout = timeout
  182. return self
  183. }
  184. /// The behavior used to determine when an RPC should start. That is, whether it should wait for
  185. /// an active connection or fail quickly if no connection is currently available. Calls will
  186. /// use `.waitsForConnectivity` by default.
  187. @discardableResult
  188. public func withCallStartBehavior(_ behavior: CallStartBehavior) -> Self {
  189. self.configuration.callStartBehavior = behavior
  190. return self
  191. }
  192. }
  193. extension ClientConnection.Builder {
  194. /// Sets the client error delegate.
  195. @discardableResult
  196. public func withErrorDelegate(_ delegate: ClientErrorDelegate?) -> Self {
  197. self.configuration.errorDelegate = delegate
  198. return self
  199. }
  200. }
  201. extension ClientConnection.Builder {
  202. /// Sets the client connectivity state delegate and the `DispatchQueue` on which the delegate
  203. /// should be called. If no `queue` is provided then gRPC will create a `DispatchQueue` on which
  204. /// to run the delegate.
  205. @discardableResult
  206. public func withConnectivityStateDelegate(
  207. _ delegate: ConnectivityStateDelegate?,
  208. executingOn queue: DispatchQueue? = nil
  209. ) -> Self {
  210. self.configuration.connectivityStateDelegate = delegate
  211. self.configuration.connectivityStateDelegateQueue = queue
  212. return self
  213. }
  214. }
  215. // MARK: - Common TLS options
  216. extension ClientConnection.Builder.Secure {
  217. /// Sets a server hostname override to be used for the TLS Server Name Indication (SNI) extension.
  218. /// The hostname from `connect(host:port)` is for TLS SNI if this value is not set and hostname
  219. /// verification is enabled.
  220. ///
  221. /// - Note: May be used with the 'NIOSSL' and 'Network.framework' TLS backend.
  222. /// - Note: `serverHostnameOverride` may not be `nil` when using the 'Network.framework' backend.
  223. @discardableResult
  224. public func withTLS(serverHostnameOverride: String?) -> Self {
  225. self.tls.hostnameOverride = serverHostnameOverride
  226. return self
  227. }
  228. }
  229. extension ClientConnection.Builder {
  230. /// Sets the HTTP/2 flow control target window size. Defaults to 8MB if not explicitly set.
  231. /// Values are clamped between 1 and 2^31-1 inclusive.
  232. @discardableResult
  233. public func withHTTPTargetWindowSize(_ httpTargetWindowSize: Int) -> Self {
  234. self.configuration.httpTargetWindowSize = httpTargetWindowSize
  235. return self
  236. }
  237. /// Sets the maximum size of an HTTP/2 frame in bytes which the client is willing to receive from
  238. /// the server. Defaults to 16384. Value are clamped between 2^14 and 2^24-1 octets inclusive
  239. /// (the minimum and maximum permitted values per RFC 7540 § 4.2).
  240. ///
  241. /// Raising this value may lower CPU usage for large message at the cost of increasing head of
  242. /// line blocking for small messages.
  243. @discardableResult
  244. public func withHTTPMaxFrameSize(_ httpMaxFrameSize: Int) -> Self {
  245. self.configuration.httpMaxFrameSize = httpMaxFrameSize
  246. return self
  247. }
  248. }
  249. extension ClientConnection.Builder {
  250. /// Sets the maximum message size the client is permitted to receive in bytes.
  251. ///
  252. /// - Precondition: `limit` must not be negative.
  253. @discardableResult
  254. public func withMaximumReceiveMessageLength(_ limit: Int) -> Self {
  255. self.configuration.maximumReceiveMessageLength = limit
  256. return self
  257. }
  258. }
  259. extension ClientConnection.Builder {
  260. /// Sets a logger to be used for background activity such as connection state changes. Defaults
  261. /// to a no-op logger if not explicitly set.
  262. ///
  263. /// Note that individual RPCs will use the logger from `CallOptions`, not the logger specified
  264. /// here.
  265. @discardableResult
  266. public func withBackgroundActivityLogger(_ logger: Logger) -> Self {
  267. self.configuration.backgroundActivityLogger = logger
  268. return self
  269. }
  270. }
  271. extension ClientConnection.Builder {
  272. /// A channel initializer which will be run after gRPC has initialized each channel. This may be
  273. /// used to add additional handlers to the pipeline and is intended for debugging.
  274. ///
  275. /// - Warning: The initializer closure may be invoked *multiple times*.
  276. @discardableResult
  277. public func withDebugChannelInitializer(
  278. _ debugChannelInitializer: @escaping (Channel) -> EventLoopFuture<Void>
  279. ) -> Self {
  280. self.configuration.debugChannelInitializer = debugChannelInitializer
  281. return self
  282. }
  283. }
  284. private extension Double {
  285. static func seconds(from amount: TimeAmount) -> Double {
  286. return Double(amount.nanoseconds) / 1_000_000_000
  287. }
  288. }