GRPCChannelBuilder.swift 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  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 NIO
  19. import NIOSSL
  20. #if canImport(Security)
  21. import Security
  22. #endif
  23. extension ClientConnection {
  24. /// Returns an insecure `ClientConnection` builder which is *not configured with TLS*.
  25. public static func insecure(group: EventLoopGroup) -> ClientConnection.Builder {
  26. return Builder(group: group)
  27. }
  28. /// Returns a `ClientConnection` builder configured with TLS.
  29. @available(
  30. *, deprecated,
  31. message: "Use one of 'usingPlatformAppropriateTLS(for:)', 'usingTLSBackedByNIOSSL(on:)' or 'usingTLSBackedByNetworkFramework(on:)' or 'usingTLS(on:with:)'"
  32. )
  33. public static func secure(group: EventLoopGroup) -> ClientConnection.Builder.Secure {
  34. return ClientConnection.usingTLSBackedByNIOSSL(on: group)
  35. }
  36. /// Returns a `ClientConnection` builder configured with a TLS backend appropriate for the
  37. /// given `EventLoopGroup`.
  38. ///
  39. /// gRPC Swift offers two TLS 'backends'. The 'NIOSSL' backend is available on Darwin and Linux
  40. /// platforms and delegates to SwiftNIO SSL. On recent Darwin platforms (macOS 10.14+, iOS 12+,
  41. /// tvOS 12+, and watchOS 5+) the 'Network.framework' backend is available. The two backends have
  42. /// a number of incompatible configuration options and users are responsible for selecting the
  43. /// appropriate APIs. The TLS configuration options on the builder document which backends they
  44. /// support.
  45. ///
  46. /// TLS backends must also be used with an appropriate `EventLoopGroup` implementation. The
  47. /// 'NIOSSL' backend may be used either a `MultiThreadedEventLoopGroup` or a
  48. /// `NIOTSEventLoopGroup`. The 'Network.framework' backend may only be used with a
  49. /// `NIOTSEventLoopGroup`.
  50. ///
  51. /// This function returns a builder using the `NIOSSL` backend if a `MultiThreadedEventLoopGroup`
  52. /// is supplied and a 'Network.framework' backend if a `NIOTSEventLoopGroup` is used.
  53. public static func usingPlatformAppropriateTLS(
  54. for group: EventLoopGroup
  55. ) -> ClientConnection.Builder.Secure {
  56. let networkPreference = NetworkPreference.userDefined(.matchingEventLoopGroup(group))
  57. return Builder.Secure(
  58. group: group,
  59. tlsConfiguration: .makeClientDefault(for: networkPreference)
  60. )
  61. }
  62. /// Returns a `ClientConnection` builder configured with the 'NIOSSL' TLS backend.
  63. ///
  64. /// This builder may use either a `MultiThreadedEventLoopGroup` or a `NIOTSEventLoopGroup` (or an
  65. /// `EventLoop` from either group).
  66. ///
  67. /// - Parameter group: The `EventLoopGroup` use for the connection.
  68. /// - Returns: A builder for a connection using the NIOSSL TLS backend.
  69. public static func usingTLSBackedByNIOSSL(
  70. on group: EventLoopGroup
  71. ) -> ClientConnection.Builder.Secure {
  72. return Builder.Secure(group: group, tlsConfiguration: .makeClientConfigurationBackedByNIOSSL())
  73. }
  74. #if canImport(Network)
  75. /// Returns a `ClientConnection` builder configured with the Network.framework TLS backend.
  76. ///
  77. /// This builder must use a `NIOTSEventLoopGroup` (or an `EventLoop` from a
  78. /// `NIOTSEventLoopGroup`).
  79. ///
  80. /// - Parameter group: The `EventLoopGroup` use for the connection.
  81. /// - Returns: A builder for a connection using the Network.framework TLS backend.
  82. @available(macOS 10.14, iOS 12.0, watchOS 5.0, tvOS 12.0, *)
  83. public static func usingTLSBackedByNetworkFramework(
  84. on group: EventLoopGroup
  85. ) -> ClientConnection.Builder.Secure {
  86. precondition(
  87. PlatformSupport.isTransportServicesEventLoopGroup(group),
  88. "'\(#function)' requires 'group' to be a 'NIOTransportServices.NIOTSEventLoopGroup' or 'NIOTransportServices.QoSEventLoop' (but was '\(type(of: group))'"
  89. )
  90. return Builder.Secure(
  91. group: group,
  92. tlsConfiguration: .makeClientConfigurationBackedByNetworkFramework()
  93. )
  94. }
  95. #endif
  96. /// Returns a `ClientConnection` builder configured with the TLS backend appropriate for the
  97. /// provided configuration and `EventLoopGroup`.
  98. ///
  99. /// - Important: The caller is responsible for ensuring the provided `configuration` may be used
  100. /// the the `group`.
  101. public static func usingTLS(
  102. with configuration: GRPCTLSConfiguration,
  103. on group: EventLoopGroup
  104. ) -> ClientConnection.Builder.Secure {
  105. return Builder.Secure(group: group, tlsConfiguration: configuration)
  106. }
  107. }
  108. extension ClientConnection {
  109. public class Builder {
  110. private var configuration: ClientConnection.Configuration
  111. private var maybeTLS: GRPCTLSConfiguration? { return nil }
  112. private var connectionBackoff = ConnectionBackoff()
  113. private var connectionBackoffIsEnabled = true
  114. fileprivate init(group: EventLoopGroup) {
  115. // This is okay: the configuration is only consumed on a call to `connect` which sets the host
  116. // and port.
  117. self.configuration = .default(target: .hostAndPort("", .max), eventLoopGroup: group)
  118. }
  119. public func connect(host: String, port: Int) -> ClientConnection {
  120. // Finish setting up the configuration.
  121. self.configuration.target = .hostAndPort(host, port)
  122. self.configuration.connectionBackoff =
  123. self.connectionBackoffIsEnabled ? self.connectionBackoff : nil
  124. self.configuration.tlsConfiguration = self.maybeTLS
  125. return ClientConnection(configuration: self.configuration)
  126. }
  127. }
  128. }
  129. extension ClientConnection.Builder {
  130. public class Secure: ClientConnection.Builder {
  131. internal var tls: GRPCTLSConfiguration
  132. override internal var maybeTLS: GRPCTLSConfiguration? {
  133. return self.tls
  134. }
  135. internal init(group: EventLoopGroup, tlsConfiguration: GRPCTLSConfiguration) {
  136. group.preconditionCompatible(with: tlsConfiguration)
  137. self.tls = tlsConfiguration
  138. super.init(group: group)
  139. }
  140. }
  141. }
  142. extension ClientConnection.Builder {
  143. /// Sets the initial connection backoff. That is, the initial time to wait before re-attempting to
  144. /// establish a connection. Jitter will *not* be applied to the initial backoff. Defaults to
  145. /// 1 second if not set.
  146. @discardableResult
  147. public func withConnectionBackoff(initial amount: TimeAmount) -> Self {
  148. self.connectionBackoff.initialBackoff = .seconds(from: amount)
  149. return self
  150. }
  151. /// Set the maximum connection backoff. That is, the maximum amount of time to wait before
  152. /// re-attempting to establish a connection. Note that this time amount represents the maximum
  153. /// backoff *before* jitter is applied. Defaults to 120 seconds if not set.
  154. @discardableResult
  155. public func withConnectionBackoff(maximum amount: TimeAmount) -> Self {
  156. self.connectionBackoff.maximumBackoff = .seconds(from: amount)
  157. return self
  158. }
  159. /// Backoff is 'jittered' to randomise the amount of time to wait before re-attempting to
  160. /// establish a connection. The jittered backoff will be no more than `jitter ⨯ unjitteredBackoff`
  161. /// from `unjitteredBackoff`. Defaults to 0.2 if not set.
  162. ///
  163. /// - Precondition: `0 <= jitter <= 1`
  164. @discardableResult
  165. public func withConnectionBackoff(jitter: Double) -> Self {
  166. self.connectionBackoff.jitter = jitter
  167. return self
  168. }
  169. /// The multiplier for scaling the unjittered backoff between attempts to establish a connection.
  170. /// Defaults to 1.6 if not set.
  171. @discardableResult
  172. public func withConnectionBackoff(multiplier: Double) -> Self {
  173. self.connectionBackoff.multiplier = multiplier
  174. return self
  175. }
  176. /// The minimum timeout to use when attempting to establishing a connection. The connection
  177. /// timeout for each attempt is the larger of the jittered backoff and the minimum connection
  178. /// timeout. Defaults to 20 seconds if not set.
  179. @discardableResult
  180. public func withConnectionTimeout(minimum amount: TimeAmount) -> Self {
  181. self.connectionBackoff.minimumConnectionTimeout = .seconds(from: amount)
  182. return self
  183. }
  184. /// Sets the initial and maximum backoff to given amount. Disables jitter and sets the backoff
  185. /// multiplier to 1.0.
  186. @discardableResult
  187. public func withConnectionBackoff(fixed amount: TimeAmount) -> Self {
  188. let seconds = Double.seconds(from: amount)
  189. self.connectionBackoff.initialBackoff = seconds
  190. self.connectionBackoff.maximumBackoff = seconds
  191. self.connectionBackoff.multiplier = 1.0
  192. self.connectionBackoff.jitter = 0.0
  193. return self
  194. }
  195. /// Sets the limit on the number of times to attempt to re-establish a connection. Defaults
  196. /// to `.unlimited` if not set.
  197. @discardableResult
  198. public func withConnectionBackoff(retries: ConnectionBackoff.Retries) -> Self {
  199. self.connectionBackoff.retries = retries
  200. return self
  201. }
  202. /// Sets whether the connection should be re-established automatically if it is dropped. Defaults
  203. /// to `true` if not set.
  204. @discardableResult
  205. public func withConnectionReestablishment(enabled: Bool) -> Self {
  206. self.connectionBackoffIsEnabled = enabled
  207. return self
  208. }
  209. /// Sets a custom configuration for keepalive
  210. /// The defaults for client and server are determined by the gRPC keepalive
  211. /// [documentation] (https://github.com/grpc/grpc/blob/master/doc/keepalive.md).
  212. @discardableResult
  213. public func withKeepalive(_ keepalive: ClientConnectionKeepalive) -> Self {
  214. self.configuration.connectionKeepalive = keepalive
  215. return self
  216. }
  217. /// The amount of time to wait before closing the connection. The idle timeout will start only
  218. /// if there are no RPCs in progress and will be cancelled as soon as any RPCs start. If a
  219. /// connection becomes idle, starting a new RPC will automatically create a new connection.
  220. /// Defaults to 30 minutes if not set.
  221. @discardableResult
  222. public func withConnectionIdleTimeout(_ timeout: TimeAmount) -> Self {
  223. self.configuration.connectionIdleTimeout = timeout
  224. return self
  225. }
  226. /// The behavior used to determine when an RPC should start. That is, whether it should wait for
  227. /// an active connection or fail quickly if no connection is currently available. Calls will
  228. /// use `.waitsForConnectivity` by default.
  229. @discardableResult
  230. public func withCallStartBehavior(_ behavior: CallStartBehavior) -> Self {
  231. self.configuration.callStartBehavior = behavior
  232. return self
  233. }
  234. }
  235. extension ClientConnection.Builder {
  236. /// Sets the client error delegate.
  237. @discardableResult
  238. public func withErrorDelegate(_ delegate: ClientErrorDelegate?) -> Self {
  239. self.configuration.errorDelegate = delegate
  240. return self
  241. }
  242. }
  243. extension ClientConnection.Builder {
  244. /// Sets the client connectivity state delegate and the `DispatchQueue` on which the delegate
  245. /// should be called. If no `queue` is provided then gRPC will create a `DispatchQueue` on which
  246. /// to run the delegate.
  247. @discardableResult
  248. public func withConnectivityStateDelegate(
  249. _ delegate: ConnectivityStateDelegate?,
  250. executingOn queue: DispatchQueue? = nil
  251. ) -> Self {
  252. self.configuration.connectivityStateDelegate = delegate
  253. self.configuration.connectivityStateDelegateQueue = queue
  254. return self
  255. }
  256. }
  257. // MARK: - Common TLS options
  258. extension ClientConnection.Builder.Secure {
  259. /// Sets a server hostname override to be used for the TLS Server Name Indication (SNI) extension.
  260. /// The hostname from `connect(host:port)` is for TLS SNI if this value is not set and hostname
  261. /// verification is enabled.
  262. ///
  263. /// - Note: May be used with the 'NIOSSL' and 'Network.framework' TLS backend.
  264. /// - Note: `serverHostnameOverride` may not be `nil` when using the 'Network.framework' backend.
  265. @discardableResult
  266. public func withTLS(serverHostnameOverride: String?) -> Self {
  267. self.tls.hostnameOverride = serverHostnameOverride
  268. return self
  269. }
  270. }
  271. // MARK: - NIOSSL TLS backend options
  272. extension ClientConnection.Builder.Secure {
  273. /// Sets the sources of certificates to offer during negotiation. No certificates are offered
  274. /// during negotiation by default.
  275. ///
  276. /// - Note: May only be used with the 'NIOSSL' TLS backend.
  277. @discardableResult
  278. public func withTLS(certificateChain: [NIOSSLCertificate]) -> Self {
  279. self.tls.updateNIOCertificateChain(to: certificateChain)
  280. return self
  281. }
  282. /// Sets the private key associated with the leaf certificate.
  283. ///
  284. /// - Note: May only be used with the 'NIOSSL' TLS backend.
  285. @discardableResult
  286. public func withTLS(privateKey: NIOSSLPrivateKey) -> Self {
  287. self.tls.updateNIOPrivateKey(to: privateKey)
  288. return self
  289. }
  290. /// Sets the trust roots to use to validate certificates. This only needs to be provided if you
  291. /// intend to validate certificates. Defaults to the system provided trust store (`.default`) if
  292. /// not set.
  293. ///
  294. /// - Note: May only be used with the 'NIOSSL' TLS backend.
  295. @discardableResult
  296. public func withTLS(trustRoots: NIOSSLTrustRoots) -> Self {
  297. self.tls.updateNIOTrustRoots(to: trustRoots)
  298. return self
  299. }
  300. /// Whether to verify remote certificates. Defaults to `.fullVerification` if not otherwise
  301. /// configured.
  302. ///
  303. /// - Note: May only be used with the 'NIOSSL' TLS backend.
  304. @discardableResult
  305. public func withTLS(certificateVerification: CertificateVerification) -> Self {
  306. self.tls.updateNIOCertificateVerification(to: certificateVerification)
  307. return self
  308. }
  309. /// A custom verification callback that allows completely overriding the certificate verification logic.
  310. ///
  311. /// - Note: May only be used with the 'NIOSSL' TLS backend.
  312. @discardableResult
  313. public func withTLSCustomVerificationCallback(
  314. _ callback: @escaping NIOSSLCustomVerificationCallback
  315. ) -> Self {
  316. self.tls.updateNIOCustomVerificationCallback(to: callback)
  317. return self
  318. }
  319. }
  320. // MARK: - Network.framework TLS backend options
  321. #if canImport(Network)
  322. extension ClientConnection.Builder.Secure {
  323. /// Update the local identity.
  324. ///
  325. /// - Note: May only be used with the 'Network.framework' TLS backend.
  326. @discardableResult
  327. @available(macOS 10.14, iOS 12.0, watchOS 5.0, tvOS 12.0, *)
  328. public func withTLS(localIdentity: SecIdentity) -> Self {
  329. self.tls.updateNetworkLocalIdentity(to: localIdentity)
  330. return self
  331. }
  332. /// Update the callback used to verify a trust object during a TLS handshake.
  333. ///
  334. /// - Note: May only be used with the 'Network.framework' TLS backend.
  335. @discardableResult
  336. @available(macOS 10.14, iOS 12.0, watchOS 5.0, tvOS 12.0, *)
  337. public func withTLSHandshakeVerificationCallback(
  338. on queue: DispatchQueue,
  339. verificationCallback callback: @escaping sec_protocol_verify_t
  340. ) -> Self {
  341. self.tls.updateNetworkVerifyCallbackWithQueue(callback: callback, queue: queue)
  342. return self
  343. }
  344. }
  345. #endif
  346. extension ClientConnection.Builder {
  347. /// Sets the HTTP/2 flow control target window size. Defaults to 65,535 if not explicitly set.
  348. @discardableResult
  349. public func withHTTPTargetWindowSize(_ httpTargetWindowSize: Int) -> Self {
  350. self.configuration.httpTargetWindowSize = httpTargetWindowSize
  351. return self
  352. }
  353. }
  354. extension ClientConnection.Builder {
  355. /// Sets the maximum message size the client is permitted to receive in bytes.
  356. ///
  357. /// - Precondition: `limit` must not be negative.
  358. @discardableResult
  359. public func withMaximumReceiveMessageLength(_ limit: Int) -> Self {
  360. self.configuration.maximumReceiveMessageLength = limit
  361. return self
  362. }
  363. }
  364. extension ClientConnection.Builder {
  365. /// Sets a logger to be used for background activity such as connection state changes. Defaults
  366. /// to a no-op logger if not explicitly set.
  367. ///
  368. /// Note that individual RPCs will use the logger from `CallOptions`, not the logger specified
  369. /// here.
  370. @discardableResult
  371. public func withBackgroundActivityLogger(_ logger: Logger) -> Self {
  372. self.configuration.backgroundActivityLogger = logger
  373. return self
  374. }
  375. }
  376. extension ClientConnection.Builder {
  377. /// A channel initializer which will be run after gRPC has initialized each channel. This may be
  378. /// used to add additional handlers to the pipeline and is intended for debugging.
  379. ///
  380. /// - Warning: The initializer closure may be invoked *multiple times*.
  381. @discardableResult
  382. public func withDebugChannelInitializer(
  383. _ debugChannelInitializer: @escaping (Channel) -> EventLoopFuture<Void>
  384. ) -> Self {
  385. self.configuration.debugChannelInitializer = debugChannelInitializer
  386. return self
  387. }
  388. }
  389. private extension Double {
  390. static func seconds(from amount: TimeAmount) -> Double {
  391. return Double(amount.nanoseconds) / 1_000_000_000
  392. }
  393. }