TLSVerificationHandler.swift 3.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  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 NIOSSL
  19. import NIOTLS
  20. import Logging
  21. /// Application protocol identifiers for ALPN.
  22. internal enum GRPCApplicationProtocolIdentifier: String, CaseIterable {
  23. // This is not in the IANA ALPN protocol ID registry, but may be used by servers to indicate that
  24. // they serve only gRPC traffic. It is part of the gRPC core implementation.
  25. case gRPC = "grpc-exp"
  26. case h2 = "h2"
  27. }
  28. /// A helper `ChannelInboundHandler` to verify that a TLS handshake was completed successfully
  29. /// and that the negotiated application protocol is valid.
  30. ///
  31. /// The handler holds a promise which is succeeded on successful verification of the negotiated
  32. /// application protocol and failed if any error is received by this handler.
  33. ///
  34. /// Users of this handler should rely on the `verification` future held by this instance.
  35. ///
  36. /// On fulfillment of the promise this handler is removed from the channel pipeline.
  37. internal class TLSVerificationHandler: ChannelInboundHandler, RemovableChannelHandler {
  38. typealias InboundIn = Any
  39. private let logger = Logger(subsystem: .clientChannel)
  40. private var verificationPromise: EventLoopPromise<Void>!
  41. /// A future which is fulfilled when the state of the TLS handshake is known. If the handshake
  42. /// was successful then the future is succeeded.
  43. /// If an error occurred the future will have been failed.
  44. ///
  45. /// - Important: The promise associated with this future is created in `handlerAdded(context:)`,
  46. /// and as such must _not_ be accessed before the handler has be added to a pipeline.
  47. var verification: EventLoopFuture<Void>! {
  48. return verificationPromise.futureResult
  49. }
  50. init() { }
  51. func handlerAdded(context: ChannelHandlerContext) {
  52. self.verificationPromise = context.eventLoop.makePromise()
  53. // Remove ourselves from the pipeline when the promise gets fulfilled.
  54. self.verificationPromise.futureResult.recover { error in
  55. // If we have an error we should let the rest of the pipeline know.
  56. context.fireErrorCaught(error)
  57. }.whenComplete { _ in
  58. context.pipeline.removeHandler(self, promise: nil)
  59. }
  60. }
  61. func errorCaught(context: ChannelHandlerContext, error: Error) {
  62. precondition(self.verificationPromise != nil, "handler has not been added to the pipeline")
  63. self.logger.error(
  64. "error caught before TLS was verified",
  65. metadata: [MetadataKey.error: "\(error)"]
  66. )
  67. verificationPromise.fail(error)
  68. }
  69. func userInboundEventTriggered(context: ChannelHandlerContext, event: Any) {
  70. precondition(self.verificationPromise != nil, "handler has not been added to the pipeline")
  71. guard let tlsEvent = event as? TLSUserEvent,
  72. case .handshakeCompleted(negotiatedProtocol: let negotiatedProtocol) = tlsEvent else {
  73. context.fireUserInboundEventTriggered(event)
  74. return
  75. }
  76. if let proto = negotiatedProtocol {
  77. self.logger.debug("TLS handshake completed, negotiated protocol: \(proto)")
  78. } else {
  79. self.logger.debug("TLS handshake completed, no protocol negotiated")
  80. }
  81. self.verificationPromise.succeed(())
  82. }
  83. }