2
0

GRPCNIO.swift 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. /*
  2. * Copyright 2019, 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 NIO
  17. import NIOTransportServices
  18. /// How a network implementation should be chosen.
  19. public enum NetworkPreference {
  20. /// Use the best available, that is, Network.framework (and NIOTransportServices) when it is
  21. /// available on Darwin platforms (macOS 10.14+, iOS 12.0+, tvOS 12.0+, watchOS 6.0+), and
  22. /// falling back to the POSIX network model otherwise.
  23. case best
  24. /// Use the given implementation. Doing so may require additional availability checks depending
  25. /// on the implementation.
  26. case userDefined(NetworkImplementation)
  27. }
  28. /// The network implementation to use: POSIX sockets or Network.framework. This also determines
  29. /// which variant of NIO to use; NIO or NIOTransportServices, respectively.
  30. public enum NetworkImplementation {
  31. #if canImport(Network)
  32. /// Network.framework (NIOTransportServices).
  33. @available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
  34. case networkFramework
  35. #endif
  36. /// POSIX (NIO).
  37. case posix
  38. }
  39. extension NetworkPreference {
  40. /// The network implementation, and by extension the NIO variant which will be used.
  41. ///
  42. /// Network.framework is available on macOS 10.14+, iOS 12.0+, tvOS 12.0+ and watchOS 6.0+.
  43. ///
  44. /// This isn't directly useful when implementing code which branches on the network preference
  45. /// since that code will still need the appropriate availability check:
  46. ///
  47. /// - `@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)`, or
  48. /// - `#available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)`.
  49. public var implementation: NetworkImplementation {
  50. switch self {
  51. case .best:
  52. #if canImport(Network)
  53. guard #available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *) else {
  54. // This is gated by the availability of `.networkFramework` so should never happen.
  55. fatalError(".networkFramework is being used on an unsupported platform")
  56. }
  57. return .networkFramework
  58. #else
  59. return .posix
  60. #endif
  61. case .userDefined(let implementation):
  62. return implementation
  63. }
  64. }
  65. }
  66. // MARK: - Generic Bootstraps
  67. // TODO: Revisit the handling of NIO/NIOTS once https://github.com/apple/swift-nio/issues/796
  68. // is addressed.
  69. /// This protocol is intended as a layer of abstraction over `ClientBootstrap` and
  70. /// `NIOTSConnectionBootstrap`.
  71. public protocol ClientBootstrapProtocol {
  72. func connect(to: SocketAddress) -> EventLoopFuture<Channel>
  73. func connect(host: String, port: Int) -> EventLoopFuture<Channel>
  74. func connect(unixDomainSocketPath: String) -> EventLoopFuture<Channel>
  75. func connectTimeout(_ timeout: TimeAmount) -> Self
  76. func channelOption<T>(_ option: T, value: T.Value) -> Self where T: ChannelOption
  77. func channelInitializer(_ handler: @escaping (Channel) -> EventLoopFuture<Void>) -> Self
  78. }
  79. extension ClientBootstrap: ClientBootstrapProtocol {}
  80. #if canImport(Network)
  81. @available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
  82. extension NIOTSConnectionBootstrap: ClientBootstrapProtocol {}
  83. #endif
  84. /// This protocol is intended as a layer of abstraction over `ServerBootstrap` and
  85. /// `NIOTSListenerBootstrap`.
  86. public protocol ServerBootstrapProtocol {
  87. func bind(to: SocketAddress) -> EventLoopFuture<Channel>
  88. func bind(host: String, port: Int) -> EventLoopFuture<Channel>
  89. func bind(unixDomainSocketPath: String) -> EventLoopFuture<Channel>
  90. func serverChannelInitializer(_ initializer: @escaping (Channel) -> EventLoopFuture<Void>) -> Self
  91. func serverChannelOption<T>(_ option: T, value: T.Value) -> Self where T: ChannelOption
  92. func childChannelInitializer(_ initializer: @escaping (Channel) -> EventLoopFuture<Void>) -> Self
  93. func childChannelOption<T>(_ option: T, value: T.Value) -> Self where T: ChannelOption
  94. }
  95. extension ServerBootstrap: ServerBootstrapProtocol {}
  96. #if canImport(Network)
  97. @available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
  98. extension NIOTSListenerBootstrap: ServerBootstrapProtocol {}
  99. #endif
  100. // MARK: - Bootstrap / EventLoopGroup helpers
  101. public enum GRPCNIO {
  102. /// Makes a new event loop group based on the network preference.
  103. ///
  104. /// If `.best` is chosen and `Network.framework` is available then `NIOTSEventLoopGroup` will
  105. /// be returned. A `MultiThreadedEventLoopGroup` will be returned otherwise.
  106. ///
  107. /// - Parameter loopCount: The number of event loops to create in the event loop group.
  108. /// - Parameter networkPreference: Network prefernce; defaulting to `.best`.
  109. public static func makeEventLoopGroup(
  110. loopCount: Int,
  111. networkPreference: NetworkPreference = .best
  112. ) -> EventLoopGroup {
  113. switch networkPreference.implementation {
  114. #if canImport(Network)
  115. case .networkFramework:
  116. guard #available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *) else {
  117. // This is gated by the availability of `.networkFramework` so should never happen.
  118. fatalError(".networkFramework is being used on an unsupported platform")
  119. }
  120. return NIOTSEventLoopGroup(loopCount: loopCount)
  121. #endif
  122. case .posix:
  123. return MultiThreadedEventLoopGroup(numberOfThreads: loopCount)
  124. }
  125. }
  126. /// Makes a new client bootstrap using the given `EventLoopGroup`.
  127. ///
  128. /// If the `EventLoopGroup` is a `NIOTSEventLoopGroup` then the returned bootstrap will be a
  129. /// `NIOTSConnectionBootstrap`, otherwise it will be a `ClientBootstrap`.
  130. ///
  131. /// - Parameter group: The `EventLoopGroup` to use.
  132. public static func makeClientBootstrap(group: EventLoopGroup) -> ClientBootstrapProtocol {
  133. #if canImport(Network)
  134. if #available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *) {
  135. if let tsGroup = group as? NIOTSEventLoopGroup {
  136. return NIOTSConnectionBootstrap(group: tsGroup)
  137. }
  138. }
  139. #endif
  140. return ClientBootstrap(group: group)
  141. }
  142. /// Makes a new server bootstrap using the given `EventLoopGroup`.
  143. ///
  144. /// If the `EventLoopGroup` is a `NIOTSEventLoopGroup` then the returned bootstrap will be a
  145. /// `NIOTSListenerBootstrap`, otherwise it will be a `ServerBootstrap`.
  146. ///
  147. /// - Parameter group: The `EventLoopGroup` to use.
  148. public static func makeServerBootstrap(group: EventLoopGroup) -> ServerBootstrapProtocol {
  149. #if canImport(Network)
  150. if #available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *) {
  151. if let tsGroup = group as? NIOTSEventLoopGroup {
  152. return NIOTSListenerBootstrap(group: tsGroup)
  153. }
  154. }
  155. #endif
  156. return ServerBootstrap(group: group)
  157. }
  158. }