BaseClientCall.swift 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  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 Foundation
  17. import NIO
  18. import NIOHTTP1
  19. import NIOHTTP2
  20. import SwiftProtobuf
  21. /// This class provides much of the boilerplate for the four types of gRPC call objects returned to framework
  22. /// users.
  23. ///
  24. /// Each call will be configured on a multiplexed channel on the given connection. The multiplexed
  25. /// channel will be configured as such:
  26. ///
  27. /// ┌──────────────────────────────┐
  28. /// │ ClientResponseChannelHandler │
  29. /// └────────────▲─────────────────┘
  30. /// │ ┌─────────────────────────────┐
  31. /// │ │ ClientRequestChannelHandler │
  32. /// │ └────────────────┬────────────┘
  33. /// GRPCClientResponsePart<T1>│ │GRPCClientRequestPart<T2>
  34. /// ┌─┴───────────────────────▼─┐
  35. /// │ GRPCClientCodec │
  36. /// └─▲───────────────────────┬─┘
  37. /// RawGRPCClientResponsePart│ │RawGRPCClientRequestPart
  38. /// ┌─┴───────────────────────▼─┐
  39. /// │ HTTP1ToRawGRPCClientCodec │
  40. /// └─▲───────────────────────┬─┘
  41. /// HTTPClientResponsePart│ │HTTPClientRequestPart
  42. /// ┌─┴───────────────────────▼─┐
  43. /// │ HTTP2ToHTTP1ClientCodec │
  44. /// └─▲───────────────────────┬─┘
  45. /// HTTP2Frame│ │HTTP2Frame
  46. /// | |
  47. ///
  48. /// Note: below the `HTTP2ToHTTP1ClientCodec` is the "main" pipeline provided by the channel in
  49. /// `ClientConnection`.
  50. ///
  51. /// Setup includes:
  52. /// - creation of an HTTP/2 stream for the call to execute on,
  53. /// - configuration of the NIO channel handlers for the stream, and
  54. /// - setting a call timeout, if one is provided.
  55. ///
  56. /// This class also provides much of the framework user facing functionality via conformance to `ClientCall`.
  57. open class BaseClientCall<RequestMessage: Message, ResponseMessage: Message> {
  58. /// The underlying `ClientConnection` providing the HTTP/2 channel and multiplexer.
  59. internal let connection: ClientConnection
  60. /// Promise for an HTTP/2 stream to execute the call on.
  61. internal let streamPromise: EventLoopPromise<Channel>
  62. /// Channel handler for responses.
  63. internal let responseHandler: ClientResponseChannelHandler<ResponseMessage>
  64. /// Channel handler for requests.
  65. internal let requestHandler: ClientRequestChannelHandler<RequestMessage>
  66. // Note: documentation is inherited from the `ClientCall` protocol.
  67. public let subchannel: EventLoopFuture<Channel>
  68. public let initialMetadata: EventLoopFuture<HTTPHeaders>
  69. public let status: EventLoopFuture<GRPCStatus>
  70. /// Sets up a gRPC call.
  71. ///
  72. /// This involves creating a new HTTP/2 stream on the multiplexer provided by `connection`. The
  73. /// channel associated with the stream is configured to use the provided request and response
  74. /// handlers. Note that the request head will be sent automatically from the request handler when
  75. /// the channel becomes active.
  76. ///
  77. /// - Parameters:
  78. /// - connection: connection containing the HTTP/2 channel and multiplexer to use for this call.
  79. /// - responseHandler: a channel handler for receiving responses.
  80. /// - requestHandler: a channel handler for sending requests.
  81. init(
  82. connection: ClientConnection,
  83. responseHandler: ClientResponseChannelHandler<ResponseMessage>,
  84. requestHandler: ClientRequestChannelHandler<RequestMessage>
  85. ) {
  86. self.connection = connection
  87. self.responseHandler = responseHandler
  88. self.requestHandler = requestHandler
  89. self.streamPromise = connection.channel.eventLoop.makePromise()
  90. self.subchannel = self.streamPromise.futureResult
  91. self.initialMetadata = self.responseHandler.initialMetadataPromise.futureResult
  92. self.status = self.responseHandler.statusPromise.futureResult
  93. self.streamPromise.futureResult.whenFailure { error in
  94. self.responseHandler.observeError(.unknown(error, origin: .client))
  95. }
  96. self.createStreamChannel()
  97. }
  98. /// Creates and configures an HTTP/2 stream channel. The `self.subchannel` future will hold the
  99. /// stream channel once it has been created.
  100. private func createStreamChannel() {
  101. self.connection.channel.eventLoop.execute {
  102. self.connection.multiplexer.createStreamChannel(promise: self.streamPromise) { (subchannel, streamID) -> EventLoopFuture<Void> in
  103. subchannel.pipeline.addHandlers(
  104. HTTP2ToHTTP1ClientCodec(streamID: streamID, httpProtocol: self.connection.configuration.httpProtocol),
  105. HTTP1ToRawGRPCClientCodec(),
  106. GRPCClientCodec<RequestMessage, ResponseMessage>(),
  107. self.requestHandler,
  108. self.responseHandler)
  109. }
  110. }
  111. }
  112. }
  113. extension BaseClientCall: ClientCall {
  114. // Workaround for: https://bugs.swift.org/browse/SR-10128
  115. // Once resolved this can become a default implementation on `ClientCall`.
  116. public var trailingMetadata: EventLoopFuture<HTTPHeaders> {
  117. return status.map { $0.trailingMetadata }
  118. }
  119. public func cancel() {
  120. self.connection.channel.eventLoop.execute {
  121. self.subchannel.whenSuccess { channel in
  122. channel.pipeline.fireUserInboundEventTriggered(GRPCClientUserEvent.cancelled)
  123. }
  124. }
  125. }
  126. }
  127. /// Makes a request head.
  128. ///
  129. /// - Parameter path: The path of the gRPC call, e.g. "/serviceName/methodName".
  130. /// - Parameter host: The host serving the call.
  131. /// - Parameter callOptions: Options used when making this call.
  132. internal func makeRequestHead(path: String, host: String?, callOptions: CallOptions) -> HTTPRequestHead {
  133. var headers: HTTPHeaders = [
  134. "content-type": "application/grpc",
  135. // Used to detect incompatible proxies, as per https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests
  136. "te": "trailers",
  137. //! FIXME: Add a more specific user-agent, see: https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#user-agents
  138. "user-agent": "grpc-swift-nio",
  139. GRPCHeaderName.acceptEncoding: CompressionMechanism.acceptEncodingHeader,
  140. ]
  141. if let host = host {
  142. // We're dealing with HTTP/1; the NIO HTTP2ToHTTP1Codec replaces "host" with ":authority".
  143. headers.add(name: "host", value: host)
  144. }
  145. if callOptions.timeout != .infinite {
  146. headers.add(name: GRPCHeaderName.timeout, value: String(describing: callOptions.timeout))
  147. }
  148. headers.add(contentsOf: callOptions.customMetadata)
  149. let method: HTTPMethod = callOptions.cacheable ? .GET : .POST
  150. return HTTPRequestHead(version: .init(major: 2, minor: 0), method: method, uri: path, headers: headers)
  151. }