CallOptions.swift 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  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 struct Foundation.UUID
  17. #if swift(>=5.6)
  18. @preconcurrency import Logging
  19. @preconcurrency import NIOCore
  20. #else
  21. import Logging
  22. import NIOCore
  23. #endif // swift(>=5.6)
  24. import NIOHPACK
  25. import NIOHTTP1
  26. import NIOHTTP2
  27. /// Options to use for GRPC calls.
  28. public struct CallOptions: GRPCSendable {
  29. /// Additional metadata to send to the service.
  30. public var customMetadata: HPACKHeaders
  31. /// The time limit for the RPC.
  32. ///
  33. /// - Note: timeouts are treated as deadlines as soon as an RPC has been invoked.
  34. public var timeLimit: TimeLimit
  35. /// The compression used for requests, and the compression algorithms to advertise as acceptable
  36. /// for the remote peer to use for encoding responses.
  37. ///
  38. /// Compression may also be disabled at the message-level for streaming requests (i.e. client
  39. /// streaming and bidirectional streaming RPCs) by setting `compression` to ``Compression/disabled`` in
  40. /// ``StreamingRequestClientCall/sendMessage(_:compression:)-uvtc``,
  41. /// ``StreamingRequestClientCall/sendMessage(_:compression:promise:)`` ,
  42. /// ``StreamingRequestClientCall/sendMessages(_:compression:)-55vb3`` or
  43. /// ``StreamingRequestClientCall/sendMessage(_:compression:promise:)`.
  44. ///
  45. /// Note that enabling `compression` via the `sendMessage` or `sendMessages` methods only applies
  46. /// if encoding has been specified in these options.
  47. public var messageEncoding: ClientMessageEncoding
  48. /// Whether the call is cacheable.
  49. public var cacheable: Bool
  50. /// How IDs should be provided for requests. Defaults to ``RequestIDProvider-swift.struct/autogenerated``.
  51. ///
  52. /// The request ID is used for logging and will be added to the headers of a call if
  53. /// `requestIDHeader` is specified.
  54. ///
  55. /// - Important: When setting ``CallOptions`` at the client level, ``RequestIDProvider-swift.struct/userDefined(_:)`` should __not__ be
  56. /// used otherwise each request will have the same ID.
  57. public var requestIDProvider: RequestIDProvider
  58. /// The name of the header to use when adding a request ID to a call, e.g. "x-request-id". If the
  59. /// value is `nil` (the default) then no additional header will be added.
  60. ///
  61. /// Setting this value will add a request ID to the headers of the call these options are used
  62. /// with. The request ID will be provided by ``requestIDProvider-swift.property`` and will also be used in log
  63. /// messages associated with the call.
  64. public var requestIDHeader: String?
  65. /// A preference for the `EventLoop` that the call is executed on.
  66. ///
  67. /// The `EventLoop` resulting from the preference will be used to create any `EventLoopFuture`s
  68. /// associated with the call, such as the `response` for calls with a single response (i.e. unary
  69. /// and client streaming). For calls which stream responses (server streaming and bidirectional
  70. /// streaming) the response handler is executed on this event loop.
  71. ///
  72. /// Note that the underlying connection is not guaranteed to run on the same event loop.
  73. public var eventLoopPreference: EventLoopPreference
  74. /// A logger used for the call. Defaults to a no-op logger.
  75. ///
  76. /// If a ``requestIDProvider-swift.property`` exists then a request ID will automatically attached to the logger's
  77. /// metadata using the 'grpc-request-id' key.
  78. public var logger: Logger
  79. public init(
  80. customMetadata: HPACKHeaders = HPACKHeaders(),
  81. timeLimit: TimeLimit = .none,
  82. messageEncoding: ClientMessageEncoding = .disabled,
  83. requestIDProvider: RequestIDProvider = .autogenerated,
  84. requestIDHeader: String? = nil,
  85. cacheable: Bool = false,
  86. logger: Logger = Logger(label: "io.grpc", factory: { _ in SwiftLogNoOpLogHandler() })
  87. ) {
  88. self.init(
  89. customMetadata: customMetadata,
  90. timeLimit: timeLimit,
  91. messageEncoding: messageEncoding,
  92. requestIDProvider: requestIDProvider,
  93. requestIDHeader: requestIDHeader,
  94. eventLoopPreference: .indifferent,
  95. cacheable: cacheable,
  96. logger: logger
  97. )
  98. }
  99. public init(
  100. customMetadata: HPACKHeaders = HPACKHeaders(),
  101. timeLimit: TimeLimit = .none,
  102. messageEncoding: ClientMessageEncoding = .disabled,
  103. requestIDProvider: RequestIDProvider = .autogenerated,
  104. requestIDHeader: String? = nil,
  105. eventLoopPreference: EventLoopPreference,
  106. cacheable: Bool = false,
  107. logger: Logger = Logger(label: "io.grpc", factory: { _ in SwiftLogNoOpLogHandler() })
  108. ) {
  109. self.customMetadata = customMetadata
  110. self.messageEncoding = messageEncoding
  111. self.requestIDProvider = requestIDProvider
  112. self.requestIDHeader = requestIDHeader
  113. self.cacheable = cacheable
  114. self.timeLimit = timeLimit
  115. self.logger = logger
  116. self.eventLoopPreference = eventLoopPreference
  117. }
  118. }
  119. extension CallOptions {
  120. public struct RequestIDProvider: GRPCSendable {
  121. #if swift(>=5.6)
  122. public typealias RequestIDGenerator = @Sendable () -> String
  123. #else
  124. public typealias RequestIDGenerator = () -> String
  125. #endif // swift(>=5.6)
  126. private enum RequestIDSource: GRPCSendable {
  127. case none
  128. case `static`(String)
  129. case generated(RequestIDGenerator)
  130. }
  131. private var source: RequestIDSource
  132. private init(_ source: RequestIDSource) {
  133. self.source = source
  134. }
  135. @usableFromInline
  136. internal func requestID() -> String? {
  137. switch self.source {
  138. case .none:
  139. return nil
  140. case let .static(requestID):
  141. return requestID
  142. case let .generated(generator):
  143. return generator()
  144. }
  145. }
  146. /// No request IDs are generated.
  147. public static let none = RequestIDProvider(.none)
  148. /// Generate a new request ID for each RPC.
  149. public static let autogenerated = RequestIDProvider(.generated({ UUID().uuidString }))
  150. /// Specify an ID to be used.
  151. ///
  152. /// - Important: this should only be used when ``CallOptions`` are passed directly to the call.
  153. /// If it is used for the default options on a client then all calls with have the same ID.
  154. public static func userDefined(_ requestID: String) -> RequestIDProvider {
  155. return RequestIDProvider(.static(requestID))
  156. }
  157. /// Provide a factory to generate request IDs.
  158. public static func generated(
  159. _ requestIDFactory: @escaping RequestIDGenerator
  160. ) -> RequestIDProvider {
  161. return RequestIDProvider(.generated(requestIDFactory))
  162. }
  163. }
  164. }
  165. extension CallOptions {
  166. public struct EventLoopPreference: GRPCSendable {
  167. /// No preference. The framework will assign an `EventLoop`.
  168. public static let indifferent = EventLoopPreference(.indifferent)
  169. /// Use the provided `EventLoop` for the call.
  170. public static func exact(_ eventLoop: EventLoop) -> EventLoopPreference {
  171. return EventLoopPreference(.exact(eventLoop))
  172. }
  173. @usableFromInline
  174. internal enum Preference: GRPCSendable {
  175. case indifferent
  176. case exact(EventLoop)
  177. }
  178. @usableFromInline
  179. internal var _preference: Preference
  180. @inlinable
  181. internal init(_ preference: Preference) {
  182. self._preference = preference
  183. }
  184. }
  185. }
  186. extension CallOptions.EventLoopPreference {
  187. @inlinable
  188. internal var exact: EventLoop? {
  189. switch self._preference {
  190. case let .exact(eventLoop):
  191. return eventLoop
  192. case .indifferent:
  193. return nil
  194. }
  195. }
  196. }