HTTP2ToRawGRPCStateMachine.swift 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267
  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 Logging
  17. import NIOCore
  18. import NIOHPACK
  19. import NIOHTTP2
  20. struct HTTP2ToRawGRPCStateMachine {
  21. /// The current state.
  22. private var state: State = .requestIdleResponseIdle
  23. }
  24. extension HTTP2ToRawGRPCStateMachine {
  25. enum State {
  26. // Both peers are idle. Nothing has happened to the stream.
  27. case requestIdleResponseIdle
  28. // Received valid headers. Nothing has been sent in response.
  29. case requestOpenResponseIdle(RequestOpenResponseIdleState)
  30. // Received valid headers and request(s). Response headers have been sent.
  31. case requestOpenResponseOpen(RequestOpenResponseOpenState)
  32. // Received valid headers and request(s) but not end of the request stream. Response stream has
  33. // been closed.
  34. case requestOpenResponseClosed
  35. // The request stream is closed. Nothing has been sent in response.
  36. case requestClosedResponseIdle(RequestClosedResponseIdleState)
  37. // The request stream is closed. Response headers have been sent.
  38. case requestClosedResponseOpen(RequestClosedResponseOpenState)
  39. // Both streams are closed. This state is terminal.
  40. case requestClosedResponseClosed
  41. }
  42. struct RequestOpenResponseIdleState {
  43. /// A length prefixed message reader for request messages.
  44. var reader: LengthPrefixedMessageReader
  45. /// A length prefixed message writer for response messages.
  46. var writer: CoalescingLengthPrefixedMessageWriter
  47. /// The content type of the RPC.
  48. var contentType: ContentType
  49. /// An accept encoding header to send in the response headers indicating the message encoding
  50. /// that the server supports.
  51. var acceptEncoding: String?
  52. /// A message encoding header to send in the response headers indicating the encoding which will
  53. /// be used for responses.
  54. var responseEncoding: String?
  55. /// Whether to normalize user-provided metadata.
  56. var normalizeHeaders: Bool
  57. /// The pipeline configuration state.
  58. var configurationState: ConfigurationState
  59. }
  60. struct RequestClosedResponseIdleState {
  61. /// A length prefixed message reader for request messages.
  62. var reader: LengthPrefixedMessageReader
  63. /// A length prefixed message writer for response messages.
  64. var writer: CoalescingLengthPrefixedMessageWriter
  65. /// The content type of the RPC.
  66. var contentType: ContentType
  67. /// An accept encoding header to send in the response headers indicating the message encoding
  68. /// that the server supports.
  69. var acceptEncoding: String?
  70. /// A message encoding header to send in the response headers indicating the encoding which will
  71. /// be used for responses.
  72. var responseEncoding: String?
  73. /// Whether to normalize user-provided metadata.
  74. var normalizeHeaders: Bool
  75. /// The pipeline configuration state.
  76. var configurationState: ConfigurationState
  77. init(from state: RequestOpenResponseIdleState) {
  78. self.reader = state.reader
  79. self.writer = state.writer
  80. self.contentType = state.contentType
  81. self.acceptEncoding = state.acceptEncoding
  82. self.responseEncoding = state.responseEncoding
  83. self.normalizeHeaders = state.normalizeHeaders
  84. self.configurationState = state.configurationState
  85. }
  86. }
  87. struct RequestOpenResponseOpenState {
  88. /// A length prefixed message reader for request messages.
  89. var reader: LengthPrefixedMessageReader
  90. /// A length prefixed message writer for response messages.
  91. var writer: CoalescingLengthPrefixedMessageWriter
  92. /// Whether to normalize user-provided metadata.
  93. var normalizeHeaders: Bool
  94. init(from state: RequestOpenResponseIdleState) {
  95. self.reader = state.reader
  96. self.writer = state.writer
  97. self.normalizeHeaders = state.normalizeHeaders
  98. }
  99. }
  100. struct RequestClosedResponseOpenState {
  101. /// A length prefixed message reader for request messages.
  102. var reader: LengthPrefixedMessageReader
  103. /// A length prefixed message writer for response messages.
  104. var writer: CoalescingLengthPrefixedMessageWriter
  105. /// Whether to normalize user-provided metadata.
  106. var normalizeHeaders: Bool
  107. init(from state: RequestOpenResponseOpenState) {
  108. self.reader = state.reader
  109. self.writer = state.writer
  110. self.normalizeHeaders = state.normalizeHeaders
  111. }
  112. init(from state: RequestClosedResponseIdleState) {
  113. self.reader = state.reader
  114. self.writer = state.writer
  115. self.normalizeHeaders = state.normalizeHeaders
  116. }
  117. }
  118. /// The pipeline configuration state.
  119. enum ConfigurationState {
  120. /// The pipeline is being configured. Any message data will be buffered into an appropriate
  121. /// message reader.
  122. case configuring(HPACKHeaders)
  123. /// The pipeline is configured.
  124. case configured
  125. /// Returns true if the configuration is in the `.configured` state.
  126. var isConfigured: Bool {
  127. switch self {
  128. case .configuring:
  129. return false
  130. case .configured:
  131. return true
  132. }
  133. }
  134. /// Configuration has completed.
  135. mutating func configured() -> HPACKHeaders {
  136. switch self {
  137. case .configured:
  138. preconditionFailure("Invalid state: already configured")
  139. case let .configuring(headers):
  140. self = .configured
  141. return headers
  142. }
  143. }
  144. }
  145. }
  146. extension HTTP2ToRawGRPCStateMachine {
  147. enum PipelineConfiguredAction {
  148. /// Forward the given headers.
  149. case forwardHeaders(HPACKHeaders)
  150. /// Forward the given headers and try reading the next message.
  151. case forwardHeadersAndRead(HPACKHeaders)
  152. }
  153. enum ReceiveHeadersAction {
  154. /// Configure the RPC to use the given server handler.
  155. case configure(GRPCServerHandlerProtocol)
  156. /// Reject the RPC by writing out the given headers and setting end-stream.
  157. case rejectRPC(HPACKHeaders)
  158. }
  159. enum ReadNextMessageAction {
  160. /// Do nothing.
  161. case none
  162. /// Forward the buffer.
  163. case forwardMessage(ByteBuffer)
  164. /// Forward the buffer and try reading the next message.
  165. case forwardMessageThenReadNextMessage(ByteBuffer)
  166. /// Forward the 'end' of stream request part.
  167. case forwardEnd
  168. /// Throw an error down the pipeline.
  169. case errorCaught(Error)
  170. }
  171. struct StateAndReceiveHeadersAction {
  172. /// The next state.
  173. var state: State
  174. /// The action to take.
  175. var action: ReceiveHeadersAction
  176. }
  177. struct StateAndReceiveDataAction {
  178. /// The next state.
  179. var state: State
  180. /// The action to take
  181. var action: ReceiveDataAction
  182. }
  183. enum ReceiveDataAction: Hashable {
  184. /// Try to read the next message from the state machine.
  185. case tryReading
  186. /// Invoke 'finish' on the RPC handler.
  187. case finishHandler
  188. /// Do nothing.
  189. case nothing
  190. }
  191. enum SendEndAction {
  192. /// Send trailers to the client.
  193. case sendTrailers(HPACKHeaders)
  194. /// Send trailers to the client and invoke 'finish' on the RPC handler.
  195. case sendTrailersAndFinish(HPACKHeaders)
  196. /// Fail any promise associated with this send.
  197. case failure(Error)
  198. }
  199. }
  200. // MARK: Receive Headers
  201. // This is the only state in which we can receive headers.
  202. extension HTTP2ToRawGRPCStateMachine.State {
  203. private func _receive(
  204. headers: HPACKHeaders,
  205. eventLoop: EventLoop,
  206. errorDelegate: ServerErrorDelegate?,
  207. remoteAddress: SocketAddress?,
  208. logger: Logger,
  209. allocator: ByteBufferAllocator,
  210. responseWriter: GRPCServerResponseWriter,
  211. closeFuture: EventLoopFuture<Void>,
  212. services: [Substring: CallHandlerProvider],
  213. encoding: ServerMessageEncoding,
  214. normalizeHeaders: Bool
  215. ) -> HTTP2ToRawGRPCStateMachine.StateAndReceiveHeadersAction {
  216. // Extract and validate the content type. If it's nil we need to close.
  217. guard let contentType = self.extractContentType(from: headers) else {
  218. return self.unsupportedContentType()
  219. }
  220. // Now extract the request message encoding and setup an appropriate message reader.
  221. // We may send back a list of acceptable request message encodings as well.
  222. let reader: LengthPrefixedMessageReader
  223. let acceptableRequestEncoding: String?
  224. switch self.extractRequestEncoding(from: headers, encoding: encoding) {
  225. case let .valid(messageReader, acceptEncodingHeader):
  226. reader = messageReader
  227. acceptableRequestEncoding = acceptEncodingHeader
  228. case let .invalid(status, acceptableRequestEncoding):
  229. return self.invalidRequestEncoding(
  230. status: status,
  231. acceptableRequestEncoding: acceptableRequestEncoding,
  232. contentType: contentType
  233. )
  234. }
  235. // Figure out which encoding we should use for responses.
  236. let (writer, responseEncoding) = self.extractResponseEncoding(
  237. from: headers,
  238. encoding: encoding,
  239. allocator: allocator
  240. )
  241. // Parse the path, and create a call handler.
  242. guard let path = headers.first(name: ":path") else {
  243. return self.methodNotImplemented("", contentType: contentType)
  244. }
  245. guard let callPath = CallPath(requestURI: path),
  246. let service = services[Substring(callPath.service)] else {
  247. return self.methodNotImplemented(path, contentType: contentType)
  248. }
  249. // Create a call handler context, i.e. a bunch of 'stuff' we need to create the handler with,
  250. // some of which is exposed to service providers.
  251. let context = CallHandlerContext(
  252. errorDelegate: errorDelegate,
  253. logger: logger,
  254. encoding: encoding,
  255. eventLoop: eventLoop,
  256. path: path,
  257. remoteAddress: remoteAddress,
  258. responseWriter: responseWriter,
  259. allocator: allocator,
  260. closeFuture: closeFuture
  261. )
  262. // We have a matching service, hopefully we have a provider for the method too.
  263. let method = Substring(callPath.method)
  264. if let handler = service.handle(method: method, context: context) {
  265. let nextState = HTTP2ToRawGRPCStateMachine.RequestOpenResponseIdleState(
  266. reader: reader,
  267. writer: writer,
  268. contentType: contentType,
  269. acceptEncoding: acceptableRequestEncoding,
  270. responseEncoding: responseEncoding,
  271. normalizeHeaders: normalizeHeaders,
  272. configurationState: .configuring(headers)
  273. )
  274. return .init(
  275. state: .requestOpenResponseIdle(nextState),
  276. action: .configure(handler)
  277. )
  278. } else {
  279. return self.methodNotImplemented(path, contentType: contentType)
  280. }
  281. }
  282. /// The 'content-type' is not supported; close with status code 415.
  283. private func unsupportedContentType() -> HTTP2ToRawGRPCStateMachine.StateAndReceiveHeadersAction {
  284. // From: https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md
  285. //
  286. // If 'content-type' does not begin with "application/grpc", gRPC servers SHOULD respond
  287. // with HTTP status of 415 (Unsupported Media Type). This will prevent other HTTP/2
  288. // clients from interpreting a gRPC error response, which uses status 200 (OK), as
  289. // successful.
  290. let trailers = HPACKHeaders([(":status", "415")])
  291. return .init(
  292. state: .requestClosedResponseClosed,
  293. action: .rejectRPC(trailers)
  294. )
  295. }
  296. /// The RPC method is not implemented. Close with an appropriate status.
  297. private func methodNotImplemented(
  298. _ path: String,
  299. contentType: ContentType
  300. ) -> HTTP2ToRawGRPCStateMachine.StateAndReceiveHeadersAction {
  301. let trailers = HTTP2ToRawGRPCStateMachine.makeResponseTrailersOnly(
  302. for: GRPCStatus(code: .unimplemented, message: "'\(path)' is not implemented"),
  303. contentType: contentType,
  304. acceptableRequestEncoding: nil,
  305. userProvidedHeaders: nil,
  306. normalizeUserProvidedHeaders: false
  307. )
  308. return .init(
  309. state: .requestClosedResponseClosed,
  310. action: .rejectRPC(trailers)
  311. )
  312. }
  313. /// The request encoding specified by the client is not supported. Close with an appropriate
  314. /// status.
  315. private func invalidRequestEncoding(
  316. status: GRPCStatus,
  317. acceptableRequestEncoding: String?,
  318. contentType: ContentType
  319. ) -> HTTP2ToRawGRPCStateMachine.StateAndReceiveHeadersAction {
  320. let trailers = HTTP2ToRawGRPCStateMachine.makeResponseTrailersOnly(
  321. for: status,
  322. contentType: contentType,
  323. acceptableRequestEncoding: acceptableRequestEncoding,
  324. userProvidedHeaders: nil,
  325. normalizeUserProvidedHeaders: false
  326. )
  327. return .init(
  328. state: .requestClosedResponseClosed,
  329. action: .rejectRPC(trailers)
  330. )
  331. }
  332. /// Makes a 'GRPCStatus' and response trailers suitable for returning to the client when the
  333. /// request message encoding is not supported.
  334. ///
  335. /// - Parameters:
  336. /// - encoding: The unsupported request message encoding sent by the client.
  337. /// - acceptable: The list if acceptable request message encoding the client may use.
  338. /// - Returns: The status and trailers to return to the client.
  339. private func makeStatusAndTrailersForUnsupportedEncoding(
  340. _ encoding: String,
  341. advertisedEncoding: [String]
  342. ) -> (GRPCStatus, acceptEncoding: String?) {
  343. let status: GRPCStatus
  344. let acceptEncoding: String?
  345. if advertisedEncoding.isEmpty {
  346. // No compression is supported; there's nothing to tell the client about.
  347. status = GRPCStatus(code: .unimplemented, message: "compression is not supported")
  348. acceptEncoding = nil
  349. } else {
  350. // Return a list of supported encodings which we advertise. (The list we advertise may be a
  351. // subset of the encodings we support.)
  352. acceptEncoding = advertisedEncoding.joined(separator: ",")
  353. status = GRPCStatus(
  354. code: .unimplemented,
  355. message: "\(encoding) compression is not supported, supported algorithms are " +
  356. "listed in '\(GRPCHeaderName.acceptEncoding)'"
  357. )
  358. }
  359. return (status, acceptEncoding)
  360. }
  361. /// Extract and validate the 'content-type' sent by the client.
  362. /// - Parameter headers: The headers to extract the 'content-type' from
  363. private func extractContentType(from headers: HPACKHeaders) -> ContentType? {
  364. return headers.first(name: GRPCHeaderName.contentType).flatMap(ContentType.init)
  365. }
  366. /// The result of validating the request encoding header.
  367. private enum RequestEncodingValidation {
  368. /// The encoding was valid.
  369. case valid(messageReader: LengthPrefixedMessageReader, acceptEncoding: String?)
  370. /// The encoding was invalid, the RPC should be terminated with this status.
  371. case invalid(status: GRPCStatus, acceptEncoding: String?)
  372. }
  373. /// Extract and validate the request message encoding header.
  374. /// - Parameters:
  375. /// - headers: The headers to extract the message encoding header from.
  376. /// - Returns: `RequestEncodingValidation`, either a message reader suitable for decoding requests
  377. /// and an accept encoding response header if the request encoding was valid, or a pair of
  378. /// `GRPCStatus` and trailers to close the RPC with.
  379. private func extractRequestEncoding(
  380. from headers: HPACKHeaders,
  381. encoding: ServerMessageEncoding
  382. ) -> RequestEncodingValidation {
  383. let encodingValues = headers.values(forHeader: GRPCHeaderName.encoding, canonicalForm: true)
  384. var encodingIterator = encodingValues.makeIterator()
  385. let encodingHeader = encodingIterator.next()
  386. // Fail if there's more than one encoding header.
  387. if let first = encodingHeader, let second = encodingIterator.next() {
  388. var encodings: [Substring] = []
  389. encodings.reserveCapacity(8)
  390. encodings.append(first)
  391. encodings.append(second)
  392. while let next = encodingIterator.next() {
  393. encodings.append(next)
  394. }
  395. let status = GRPCStatus(
  396. code: .invalidArgument,
  397. message: "'\(GRPCHeaderName.encoding)' must contain no more than one value but was '\(encodings.joined(separator: ", "))'"
  398. )
  399. return .invalid(status: status, acceptEncoding: nil)
  400. }
  401. let result: RequestEncodingValidation
  402. let validator = MessageEncodingHeaderValidator(encoding: encoding)
  403. switch validator.validate(requestEncoding: encodingHeader.map { String($0) }) {
  404. case let .supported(algorithm, decompressionLimit, acceptEncoding):
  405. // Request message encoding is valid and supported.
  406. result = .valid(
  407. messageReader: LengthPrefixedMessageReader(
  408. compression: algorithm,
  409. decompressionLimit: decompressionLimit
  410. ),
  411. acceptEncoding: acceptEncoding.isEmpty ? nil : acceptEncoding.joined(separator: ",")
  412. )
  413. case .noCompression:
  414. // No message encoding header was present. This means no compression is being used.
  415. result = .valid(
  416. messageReader: LengthPrefixedMessageReader(),
  417. acceptEncoding: nil
  418. )
  419. case let .unsupported(encoding, acceptable):
  420. // Request encoding is not supported.
  421. let (status, acceptEncoding) = self.makeStatusAndTrailersForUnsupportedEncoding(
  422. encoding,
  423. advertisedEncoding: acceptable
  424. )
  425. result = .invalid(status: status, acceptEncoding: acceptEncoding)
  426. }
  427. return result
  428. }
  429. /// Extract a suitable message encoding for responses.
  430. /// - Parameters:
  431. /// - headers: The headers to extract the acceptable response message encoding from.
  432. /// - configuration: The encoding configuration for the server.
  433. /// - Returns: A message writer and the response encoding header to send back to the client.
  434. private func extractResponseEncoding(
  435. from headers: HPACKHeaders,
  436. encoding: ServerMessageEncoding,
  437. allocator: ByteBufferAllocator
  438. ) -> (CoalescingLengthPrefixedMessageWriter, String?) {
  439. let writer: CoalescingLengthPrefixedMessageWriter
  440. let responseEncoding: String?
  441. switch encoding {
  442. case let .enabled(configuration):
  443. // Extract the encodings acceptable to the client for response messages.
  444. let acceptableResponseEncoding = headers[canonicalForm: GRPCHeaderName.acceptEncoding]
  445. // Select the first algorithm that we support and have enabled. If we don't find one then we
  446. // won't compress response messages.
  447. let algorithm = acceptableResponseEncoding.lazy.compactMap { value in
  448. CompressionAlgorithm(rawValue: value)
  449. }.first {
  450. configuration.enabledAlgorithms.contains($0)
  451. }
  452. writer = .init(compression: algorithm, allocator: allocator)
  453. responseEncoding = algorithm?.name
  454. case .disabled:
  455. // The server doesn't have compression enabled.
  456. writer = .init(compression: .none, allocator: allocator)
  457. responseEncoding = nil
  458. }
  459. return (writer, responseEncoding)
  460. }
  461. }
  462. // MARK: - Receive Data
  463. extension HTTP2ToRawGRPCStateMachine.RequestOpenResponseIdleState {
  464. mutating func receive(
  465. buffer: inout ByteBuffer,
  466. endStream: Bool
  467. ) -> HTTP2ToRawGRPCStateMachine.StateAndReceiveDataAction {
  468. // Append the bytes to the reader.
  469. self.reader.append(buffer: &buffer)
  470. let state: HTTP2ToRawGRPCStateMachine.State
  471. let action: HTTP2ToRawGRPCStateMachine.ReceiveDataAction
  472. switch (self.configurationState.isConfigured, endStream) {
  473. case (true, true):
  474. /// Configured and end stream: read from the buffer, end will be sent as a result of draining
  475. /// the reader in the next state.
  476. state = .requestClosedResponseIdle(.init(from: self))
  477. action = .tryReading
  478. case (true, false):
  479. /// Configured but not end stream, just read from the buffer.
  480. state = .requestOpenResponseIdle(self)
  481. action = .tryReading
  482. case (false, true):
  483. // Not configured yet, but end of stream. Request stream is now closed but there's no point
  484. // reading yet.
  485. state = .requestClosedResponseIdle(.init(from: self))
  486. action = .nothing
  487. case (false, false):
  488. // Not configured yet, not end stream. No point reading a message yet since we don't have
  489. // anywhere to deliver it.
  490. state = .requestOpenResponseIdle(self)
  491. action = .nothing
  492. }
  493. return .init(state: state, action: action)
  494. }
  495. }
  496. extension HTTP2ToRawGRPCStateMachine.RequestOpenResponseOpenState {
  497. mutating func receive(
  498. buffer: inout ByteBuffer,
  499. endStream: Bool
  500. ) -> HTTP2ToRawGRPCStateMachine.StateAndReceiveDataAction {
  501. self.reader.append(buffer: &buffer)
  502. let state: HTTP2ToRawGRPCStateMachine.State
  503. if endStream {
  504. // End stream, so move to the closed state. Any end of request stream events events will
  505. // happen as a result of reading from the closed state.
  506. state = .requestClosedResponseOpen(.init(from: self))
  507. } else {
  508. state = .requestOpenResponseOpen(self)
  509. }
  510. return .init(state: state, action: .tryReading)
  511. }
  512. }
  513. // MARK: - Send Headers
  514. extension HTTP2ToRawGRPCStateMachine.RequestOpenResponseIdleState {
  515. func send(headers userProvidedHeaders: HPACKHeaders) -> HPACKHeaders {
  516. return HTTP2ToRawGRPCStateMachine.makeResponseHeaders(
  517. contentType: self.contentType,
  518. responseEncoding: self.responseEncoding,
  519. acceptableRequestEncoding: self.acceptEncoding,
  520. userProvidedHeaders: userProvidedHeaders,
  521. normalizeUserProvidedHeaders: self.normalizeHeaders
  522. )
  523. }
  524. }
  525. extension HTTP2ToRawGRPCStateMachine.RequestClosedResponseIdleState {
  526. func send(headers userProvidedHeaders: HPACKHeaders) -> HPACKHeaders {
  527. return HTTP2ToRawGRPCStateMachine.makeResponseHeaders(
  528. contentType: self.contentType,
  529. responseEncoding: self.responseEncoding,
  530. acceptableRequestEncoding: self.acceptEncoding,
  531. userProvidedHeaders: userProvidedHeaders,
  532. normalizeUserProvidedHeaders: self.normalizeHeaders
  533. )
  534. }
  535. }
  536. // MARK: - Send Data
  537. extension HTTP2ToRawGRPCStateMachine.RequestOpenResponseOpenState {
  538. mutating func send(
  539. buffer: ByteBuffer,
  540. compress: Bool,
  541. promise: EventLoopPromise<Void>?
  542. ) {
  543. self.writer.append(buffer: buffer, compress: compress, promise: promise)
  544. }
  545. }
  546. extension HTTP2ToRawGRPCStateMachine.RequestClosedResponseOpenState {
  547. mutating func send(
  548. buffer: ByteBuffer,
  549. compress: Bool,
  550. promise: EventLoopPromise<Void>?
  551. ) {
  552. self.writer.append(buffer: buffer, compress: compress, promise: promise)
  553. }
  554. }
  555. // MARK: - Send End
  556. extension HTTP2ToRawGRPCStateMachine.RequestOpenResponseIdleState {
  557. func send(
  558. status: GRPCStatus,
  559. trailers userProvidedTrailers: HPACKHeaders
  560. ) -> HPACKHeaders {
  561. return HTTP2ToRawGRPCStateMachine.makeResponseTrailersOnly(
  562. for: status,
  563. contentType: self.contentType,
  564. acceptableRequestEncoding: self.acceptEncoding,
  565. userProvidedHeaders: userProvidedTrailers,
  566. normalizeUserProvidedHeaders: self.normalizeHeaders
  567. )
  568. }
  569. }
  570. extension HTTP2ToRawGRPCStateMachine.RequestClosedResponseIdleState {
  571. func send(
  572. status: GRPCStatus,
  573. trailers userProvidedTrailers: HPACKHeaders
  574. ) -> HPACKHeaders {
  575. return HTTP2ToRawGRPCStateMachine.makeResponseTrailersOnly(
  576. for: status,
  577. contentType: self.contentType,
  578. acceptableRequestEncoding: self.acceptEncoding,
  579. userProvidedHeaders: userProvidedTrailers,
  580. normalizeUserProvidedHeaders: self.normalizeHeaders
  581. )
  582. }
  583. }
  584. extension HTTP2ToRawGRPCStateMachine.RequestClosedResponseOpenState {
  585. func send(
  586. status: GRPCStatus,
  587. trailers userProvidedTrailers: HPACKHeaders
  588. ) -> HPACKHeaders {
  589. return HTTP2ToRawGRPCStateMachine.makeResponseTrailers(
  590. for: status,
  591. userProvidedHeaders: userProvidedTrailers,
  592. normalizeUserProvidedHeaders: true
  593. )
  594. }
  595. }
  596. extension HTTP2ToRawGRPCStateMachine.RequestOpenResponseOpenState {
  597. func send(
  598. status: GRPCStatus,
  599. trailers userProvidedTrailers: HPACKHeaders
  600. ) -> HPACKHeaders {
  601. return HTTP2ToRawGRPCStateMachine.makeResponseTrailers(
  602. for: status,
  603. userProvidedHeaders: userProvidedTrailers,
  604. normalizeUserProvidedHeaders: true
  605. )
  606. }
  607. }
  608. // MARK: - Pipeline Configured
  609. extension HTTP2ToRawGRPCStateMachine.RequestOpenResponseIdleState {
  610. mutating func pipelineConfigured() -> HTTP2ToRawGRPCStateMachine.PipelineConfiguredAction {
  611. let headers = self.configurationState.configured()
  612. let action: HTTP2ToRawGRPCStateMachine.PipelineConfiguredAction
  613. // If there are unprocessed bytes then we need to read messages as well.
  614. let hasUnprocessedBytes = self.reader.unprocessedBytes != 0
  615. if hasUnprocessedBytes {
  616. // If there are unprocessed bytes, we need to try to read after sending the metadata.
  617. action = .forwardHeadersAndRead(headers)
  618. } else {
  619. // No unprocessed bytes; the reader is empty. Just send the metadata.
  620. action = .forwardHeaders(headers)
  621. }
  622. return action
  623. }
  624. }
  625. extension HTTP2ToRawGRPCStateMachine.RequestClosedResponseIdleState {
  626. mutating func pipelineConfigured() -> HTTP2ToRawGRPCStateMachine.PipelineConfiguredAction {
  627. let headers = self.configurationState.configured()
  628. // Since we're already closed, we need to forward the headers and start reading.
  629. return .forwardHeadersAndRead(headers)
  630. }
  631. }
  632. // MARK: - Read Next Request
  633. extension HTTP2ToRawGRPCStateMachine {
  634. static func read(
  635. from reader: inout LengthPrefixedMessageReader,
  636. requestStreamClosed: Bool,
  637. maxLength: Int
  638. ) -> HTTP2ToRawGRPCStateMachine.ReadNextMessageAction {
  639. do {
  640. if let buffer = try reader.nextMessage(maxLength: maxLength) {
  641. if reader.unprocessedBytes > 0 || requestStreamClosed {
  642. // Either there are unprocessed bytes or the request stream is now closed: deliver the
  643. // message and then try to read. The subsequent read may be another message or it may
  644. // be end stream.
  645. return .forwardMessageThenReadNextMessage(buffer)
  646. } else {
  647. // Nothing left to process and the stream isn't closed yet, just forward the message.
  648. return .forwardMessage(buffer)
  649. }
  650. } else if requestStreamClosed {
  651. return .forwardEnd
  652. } else {
  653. return .none
  654. }
  655. } catch {
  656. return .errorCaught(error)
  657. }
  658. }
  659. }
  660. extension HTTP2ToRawGRPCStateMachine.RequestOpenResponseIdleState {
  661. mutating func readNextRequest(
  662. maxLength: Int
  663. ) -> HTTP2ToRawGRPCStateMachine.ReadNextMessageAction {
  664. return HTTP2ToRawGRPCStateMachine.read(
  665. from: &self.reader,
  666. requestStreamClosed: false,
  667. maxLength: maxLength
  668. )
  669. }
  670. }
  671. extension HTTP2ToRawGRPCStateMachine.RequestOpenResponseOpenState {
  672. mutating func readNextRequest(
  673. maxLength: Int
  674. ) -> HTTP2ToRawGRPCStateMachine.ReadNextMessageAction {
  675. return HTTP2ToRawGRPCStateMachine.read(
  676. from: &self.reader,
  677. requestStreamClosed: false,
  678. maxLength: maxLength
  679. )
  680. }
  681. }
  682. extension HTTP2ToRawGRPCStateMachine.RequestClosedResponseIdleState {
  683. mutating func readNextRequest(
  684. maxLength: Int
  685. ) -> HTTP2ToRawGRPCStateMachine.ReadNextMessageAction {
  686. return HTTP2ToRawGRPCStateMachine.read(
  687. from: &self.reader,
  688. requestStreamClosed: true,
  689. maxLength: maxLength
  690. )
  691. }
  692. }
  693. extension HTTP2ToRawGRPCStateMachine.RequestClosedResponseOpenState {
  694. mutating func readNextRequest(
  695. maxLength: Int
  696. ) -> HTTP2ToRawGRPCStateMachine.ReadNextMessageAction {
  697. return HTTP2ToRawGRPCStateMachine.read(
  698. from: &self.reader,
  699. requestStreamClosed: true,
  700. maxLength: maxLength
  701. )
  702. }
  703. }
  704. // MARK: - Top Level State Changes
  705. extension HTTP2ToRawGRPCStateMachine {
  706. /// Receive request headers.
  707. mutating func receive(
  708. headers: HPACKHeaders,
  709. eventLoop: EventLoop,
  710. errorDelegate: ServerErrorDelegate?,
  711. remoteAddress: SocketAddress?,
  712. logger: Logger,
  713. allocator: ByteBufferAllocator,
  714. responseWriter: GRPCServerResponseWriter,
  715. closeFuture: EventLoopFuture<Void>,
  716. services: [Substring: CallHandlerProvider],
  717. encoding: ServerMessageEncoding,
  718. normalizeHeaders: Bool
  719. ) -> ReceiveHeadersAction {
  720. return self.state.receive(
  721. headers: headers,
  722. eventLoop: eventLoop,
  723. errorDelegate: errorDelegate,
  724. remoteAddress: remoteAddress,
  725. logger: logger,
  726. allocator: allocator,
  727. responseWriter: responseWriter,
  728. closeFuture: closeFuture,
  729. services: services,
  730. encoding: encoding,
  731. normalizeHeaders: normalizeHeaders
  732. )
  733. }
  734. /// Receive request buffer.
  735. /// - Parameters:
  736. /// - buffer: The received buffer.
  737. /// - endStream: Whether end stream was set.
  738. /// - Returns: Returns whether the caller should try to read a message from the buffer.
  739. mutating func receive(buffer: inout ByteBuffer, endStream: Bool) -> ReceiveDataAction {
  740. self.state.receive(buffer: &buffer, endStream: endStream)
  741. }
  742. /// Send response headers.
  743. mutating func send(headers: HPACKHeaders) -> Result<HPACKHeaders, Error> {
  744. self.state.send(headers: headers)
  745. }
  746. /// Send a response buffer.
  747. mutating func send(
  748. buffer: ByteBuffer,
  749. compress: Bool,
  750. promise: EventLoopPromise<Void>?
  751. ) -> Result<Void, Error> {
  752. self.state.send(buffer: buffer, compress: compress, promise: promise)
  753. }
  754. mutating func nextResponse() -> (Result<ByteBuffer, Error>, EventLoopPromise<Void>?)? {
  755. self.state.nextResponse()
  756. }
  757. /// Send status and trailers.
  758. mutating func send(
  759. status: GRPCStatus,
  760. trailers: HPACKHeaders
  761. ) -> HTTP2ToRawGRPCStateMachine.SendEndAction {
  762. self.state.send(status: status, trailers: trailers)
  763. }
  764. /// The pipeline has been configured with a service provider.
  765. mutating func pipelineConfigured() -> PipelineConfiguredAction {
  766. self.state.pipelineConfigured()
  767. }
  768. /// Try to read a request message.
  769. mutating func readNextRequest(maxLength: Int) -> ReadNextMessageAction {
  770. self.state.readNextRequest(maxLength: maxLength)
  771. }
  772. }
  773. extension HTTP2ToRawGRPCStateMachine.State {
  774. mutating func pipelineConfigured() -> HTTP2ToRawGRPCStateMachine.PipelineConfiguredAction {
  775. switch self {
  776. case .requestIdleResponseIdle:
  777. preconditionFailure("Invalid state: pipeline configured before receiving request headers")
  778. case var .requestOpenResponseIdle(state):
  779. let action = state.pipelineConfigured()
  780. self = .requestOpenResponseIdle(state)
  781. return action
  782. case var .requestClosedResponseIdle(state):
  783. let action = state.pipelineConfigured()
  784. self = .requestClosedResponseIdle(state)
  785. return action
  786. case .requestOpenResponseOpen,
  787. .requestOpenResponseClosed,
  788. .requestClosedResponseOpen,
  789. .requestClosedResponseClosed:
  790. preconditionFailure("Invalid state: response stream opened before pipeline was configured")
  791. }
  792. }
  793. mutating func receive(
  794. headers: HPACKHeaders,
  795. eventLoop: EventLoop,
  796. errorDelegate: ServerErrorDelegate?,
  797. remoteAddress: SocketAddress?,
  798. logger: Logger,
  799. allocator: ByteBufferAllocator,
  800. responseWriter: GRPCServerResponseWriter,
  801. closeFuture: EventLoopFuture<Void>,
  802. services: [Substring: CallHandlerProvider],
  803. encoding: ServerMessageEncoding,
  804. normalizeHeaders: Bool
  805. ) -> HTTP2ToRawGRPCStateMachine.ReceiveHeadersAction {
  806. switch self {
  807. // These are the only states in which we can receive headers. Everything else is invalid.
  808. case .requestIdleResponseIdle,
  809. .requestClosedResponseClosed:
  810. let stateAndAction = self._receive(
  811. headers: headers,
  812. eventLoop: eventLoop,
  813. errorDelegate: errorDelegate,
  814. remoteAddress: remoteAddress,
  815. logger: logger,
  816. allocator: allocator,
  817. responseWriter: responseWriter,
  818. closeFuture: closeFuture,
  819. services: services,
  820. encoding: encoding,
  821. normalizeHeaders: normalizeHeaders
  822. )
  823. self = stateAndAction.state
  824. return stateAndAction.action
  825. // We can't receive headers in any of these states.
  826. case .requestOpenResponseIdle,
  827. .requestOpenResponseOpen,
  828. .requestOpenResponseClosed,
  829. .requestClosedResponseIdle,
  830. .requestClosedResponseOpen:
  831. preconditionFailure("Invalid state: \(self)")
  832. }
  833. }
  834. /// Receive a buffer from the client.
  835. mutating func receive(
  836. buffer: inout ByteBuffer,
  837. endStream: Bool
  838. ) -> HTTP2ToRawGRPCStateMachine.ReceiveDataAction {
  839. switch self {
  840. case .requestIdleResponseIdle:
  841. /// This isn't allowed: we must receive the request headers first.
  842. preconditionFailure("Invalid state")
  843. case var .requestOpenResponseIdle(state):
  844. let stateAndAction = state.receive(buffer: &buffer, endStream: endStream)
  845. self = stateAndAction.state
  846. return stateAndAction.action
  847. case var .requestOpenResponseOpen(state):
  848. let stateAndAction = state.receive(buffer: &buffer, endStream: endStream)
  849. self = stateAndAction.state
  850. return stateAndAction.action
  851. case .requestClosedResponseIdle,
  852. .requestClosedResponseOpen:
  853. preconditionFailure("Invalid state: the request stream is already closed")
  854. case .requestOpenResponseClosed:
  855. if endStream {
  856. // Server has finish responding and this is the end of the request stream; we're done for
  857. // this RPC now, finish the handler.
  858. self = .requestClosedResponseClosed
  859. return .finishHandler
  860. } else {
  861. // Server has finished responding but this isn't the end of the request stream; ignore the
  862. // input, we need to wait for end stream before tearing down the handler.
  863. return .nothing
  864. }
  865. case .requestClosedResponseClosed:
  866. return .nothing
  867. }
  868. }
  869. mutating func readNextRequest(
  870. maxLength: Int
  871. ) -> HTTP2ToRawGRPCStateMachine.ReadNextMessageAction {
  872. switch self {
  873. case .requestIdleResponseIdle:
  874. preconditionFailure("Invalid state")
  875. case var .requestOpenResponseIdle(state):
  876. let action = state.readNextRequest(maxLength: maxLength)
  877. self = .requestOpenResponseIdle(state)
  878. return action
  879. case var .requestOpenResponseOpen(state):
  880. let action = state.readNextRequest(maxLength: maxLength)
  881. self = .requestOpenResponseOpen(state)
  882. return action
  883. case var .requestClosedResponseIdle(state):
  884. let action = state.readNextRequest(maxLength: maxLength)
  885. self = .requestClosedResponseIdle(state)
  886. return action
  887. case var .requestClosedResponseOpen(state):
  888. let action = state.readNextRequest(maxLength: maxLength)
  889. self = .requestClosedResponseOpen(state)
  890. return action
  891. case .requestOpenResponseClosed,
  892. .requestClosedResponseClosed:
  893. return .none
  894. }
  895. }
  896. mutating func send(headers: HPACKHeaders) -> Result<HPACKHeaders, Error> {
  897. switch self {
  898. case .requestIdleResponseIdle:
  899. preconditionFailure("Invalid state: the request stream isn't open")
  900. case let .requestOpenResponseIdle(state):
  901. let headers = state.send(headers: headers)
  902. self = .requestOpenResponseOpen(.init(from: state))
  903. return .success(headers)
  904. case let .requestClosedResponseIdle(state):
  905. let headers = state.send(headers: headers)
  906. self = .requestClosedResponseOpen(.init(from: state))
  907. return .success(headers)
  908. case .requestOpenResponseOpen,
  909. .requestOpenResponseClosed,
  910. .requestClosedResponseOpen,
  911. .requestClosedResponseClosed:
  912. return .failure(GRPCError.AlreadyComplete())
  913. }
  914. }
  915. mutating func send(
  916. buffer: ByteBuffer,
  917. compress: Bool,
  918. promise: EventLoopPromise<Void>?
  919. ) -> Result<Void, Error> {
  920. switch self {
  921. case .requestIdleResponseIdle:
  922. preconditionFailure("Invalid state: the request stream is still closed")
  923. case .requestOpenResponseIdle,
  924. .requestClosedResponseIdle:
  925. let error = GRPCError.InvalidState("Response headers must be sent before response message")
  926. return .failure(error)
  927. case var .requestOpenResponseOpen(state):
  928. self = .requestClosedResponseClosed
  929. state.send(buffer: buffer, compress: compress, promise: promise)
  930. self = .requestOpenResponseOpen(state)
  931. return .success(())
  932. case var .requestClosedResponseOpen(state):
  933. self = .requestClosedResponseClosed
  934. state.send(buffer: buffer, compress: compress, promise: promise)
  935. self = .requestClosedResponseOpen(state)
  936. return .success(())
  937. case .requestOpenResponseClosed,
  938. .requestClosedResponseClosed:
  939. return .failure(GRPCError.AlreadyComplete())
  940. }
  941. }
  942. mutating func nextResponse() -> (Result<ByteBuffer, Error>, EventLoopPromise<Void>?)? {
  943. switch self {
  944. case .requestIdleResponseIdle:
  945. preconditionFailure("Invalid state: the request stream is still closed")
  946. case .requestOpenResponseIdle,
  947. .requestClosedResponseIdle:
  948. return nil
  949. case var .requestOpenResponseOpen(state):
  950. self = .requestClosedResponseClosed
  951. let result = state.writer.next()
  952. self = .requestOpenResponseOpen(state)
  953. return result
  954. case var .requestClosedResponseOpen(state):
  955. self = .requestClosedResponseClosed
  956. let result = state.writer.next()
  957. self = .requestClosedResponseOpen(state)
  958. return result
  959. case .requestOpenResponseClosed,
  960. .requestClosedResponseClosed:
  961. return nil
  962. }
  963. }
  964. mutating func send(
  965. status: GRPCStatus,
  966. trailers: HPACKHeaders
  967. ) -> HTTP2ToRawGRPCStateMachine.SendEndAction {
  968. switch self {
  969. case .requestIdleResponseIdle:
  970. preconditionFailure("Invalid state: the request stream is still closed")
  971. case let .requestOpenResponseIdle(state):
  972. self = .requestOpenResponseClosed
  973. return .sendTrailers(state.send(status: status, trailers: trailers))
  974. case let .requestClosedResponseIdle(state):
  975. self = .requestClosedResponseClosed
  976. return .sendTrailersAndFinish(state.send(status: status, trailers: trailers))
  977. case let .requestOpenResponseOpen(state):
  978. self = .requestOpenResponseClosed
  979. return .sendTrailers(state.send(status: status, trailers: trailers))
  980. case let .requestClosedResponseOpen(state):
  981. self = .requestClosedResponseClosed
  982. return .sendTrailersAndFinish(state.send(status: status, trailers: trailers))
  983. case .requestOpenResponseClosed,
  984. .requestClosedResponseClosed:
  985. return .failure(GRPCError.AlreadyComplete())
  986. }
  987. }
  988. }
  989. // MARK: - Helpers
  990. extension HTTP2ToRawGRPCStateMachine {
  991. static func makeResponseHeaders(
  992. contentType: ContentType,
  993. responseEncoding: String?,
  994. acceptableRequestEncoding: String?,
  995. userProvidedHeaders: HPACKHeaders,
  996. normalizeUserProvidedHeaders: Bool
  997. ) -> HPACKHeaders {
  998. // 4 because ':status' and 'content-type' are required. We may send back 'grpc-encoding' and
  999. // 'grpc-accept-encoding' as well.
  1000. let capacity = 4 + userProvidedHeaders.count
  1001. var headers = HPACKHeaders()
  1002. headers.reserveCapacity(capacity)
  1003. headers.add(name: ":status", value: "200")
  1004. headers.add(name: GRPCHeaderName.contentType, value: contentType.canonicalValue)
  1005. if let responseEncoding = responseEncoding {
  1006. headers.add(name: GRPCHeaderName.encoding, value: responseEncoding)
  1007. }
  1008. if let acceptEncoding = acceptableRequestEncoding {
  1009. headers.add(name: GRPCHeaderName.acceptEncoding, value: acceptEncoding)
  1010. }
  1011. // Add user provided headers, normalizing if required.
  1012. headers.add(contentsOf: userProvidedHeaders, normalize: normalizeUserProvidedHeaders)
  1013. return headers
  1014. }
  1015. static func makeResponseTrailersOnly(
  1016. for status: GRPCStatus,
  1017. contentType: ContentType,
  1018. acceptableRequestEncoding: String?,
  1019. userProvidedHeaders: HPACKHeaders?,
  1020. normalizeUserProvidedHeaders: Bool
  1021. ) -> HPACKHeaders {
  1022. // 5 because ':status', 'content-type', 'grpc-status' are required. We may also send back
  1023. // 'grpc-message' and 'grpc-accept-encoding'.
  1024. let capacity = 5 + (userProvidedHeaders.map { $0.count } ?? 0)
  1025. var headers = HPACKHeaders()
  1026. headers.reserveCapacity(capacity)
  1027. // Add the required trailers.
  1028. headers.add(name: ":status", value: "200")
  1029. headers.add(name: GRPCHeaderName.contentType, value: contentType.canonicalValue)
  1030. headers.add(name: GRPCHeaderName.statusCode, value: String(describing: status.code.rawValue))
  1031. if let message = status.message.flatMap(GRPCStatusMessageMarshaller.marshall) {
  1032. headers.add(name: GRPCHeaderName.statusMessage, value: message)
  1033. }
  1034. // We may include this if the requested encoding was not valid.
  1035. if let acceptEncoding = acceptableRequestEncoding {
  1036. headers.add(name: GRPCHeaderName.acceptEncoding, value: acceptEncoding)
  1037. }
  1038. if let userProvided = userProvidedHeaders {
  1039. headers.add(contentsOf: userProvided, normalize: normalizeUserProvidedHeaders)
  1040. }
  1041. return headers
  1042. }
  1043. static func makeResponseTrailers(
  1044. for status: GRPCStatus,
  1045. userProvidedHeaders: HPACKHeaders,
  1046. normalizeUserProvidedHeaders: Bool
  1047. ) -> HPACKHeaders {
  1048. // Most RPCs should end with status code 'ok' (hopefully!), and if the user didn't provide any
  1049. // additional trailers, then we can use a pre-canned set of headers to avoid an extra
  1050. // allocation.
  1051. if status == .ok, userProvidedHeaders.isEmpty {
  1052. return Self.gRPCStatusOkTrailers
  1053. }
  1054. // 2 because 'grpc-status' is required, we may also send back 'grpc-message'.
  1055. let capacity = 2 + userProvidedHeaders.count
  1056. var trailers = HPACKHeaders()
  1057. trailers.reserveCapacity(capacity)
  1058. // status code.
  1059. trailers.add(name: GRPCHeaderName.statusCode, value: String(describing: status.code.rawValue))
  1060. // status message, if present.
  1061. if let message = status.message.flatMap(GRPCStatusMessageMarshaller.marshall) {
  1062. trailers.add(name: GRPCHeaderName.statusMessage, value: message)
  1063. }
  1064. // user provided trailers.
  1065. trailers.add(contentsOf: userProvidedHeaders, normalize: normalizeUserProvidedHeaders)
  1066. return trailers
  1067. }
  1068. private static let gRPCStatusOkTrailers: HPACKHeaders = [
  1069. GRPCHeaderName.statusCode: String(describing: GRPCStatus.Code.ok.rawValue),
  1070. ]
  1071. }
  1072. extension HPACKHeaders {
  1073. fileprivate mutating func add(contentsOf other: HPACKHeaders, normalize: Bool) {
  1074. if normalize {
  1075. self.add(contentsOf: other.lazy.map { name, value, indexable in
  1076. (name: name.lowercased(), value: value, indexable: indexable)
  1077. })
  1078. } else {
  1079. self.add(contentsOf: other)
  1080. }
  1081. }
  1082. }