TLSVerificationHandler.swift 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  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
  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(logger: Logger) {
  51. self.logger = logger
  52. }
  53. func handlerAdded(context: ChannelHandlerContext) {
  54. self.verificationPromise = context.eventLoop.makePromise()
  55. // Remove ourselves from the pipeline when the promise gets fulfilled.
  56. self.verificationPromise.futureResult.recover { error in
  57. // If we have an error we should let the rest of the pipeline know.
  58. context.fireErrorCaught(error)
  59. }.whenComplete { _ in
  60. context.pipeline.removeHandler(self, promise: nil)
  61. }
  62. }
  63. func errorCaught(context: ChannelHandlerContext, error: Error) {
  64. precondition(self.verificationPromise != nil, "handler has not been added to the pipeline")
  65. self.logger.error(
  66. "error caught before TLS was verified",
  67. metadata: [MetadataKey.error: "\(error)"]
  68. )
  69. verificationPromise.fail(error)
  70. }
  71. func userInboundEventTriggered(context: ChannelHandlerContext, event: Any) {
  72. precondition(self.verificationPromise != nil, "handler has not been added to the pipeline")
  73. guard let tlsEvent = event as? TLSUserEvent,
  74. case .handshakeCompleted(negotiatedProtocol: let negotiatedProtocol) = tlsEvent else {
  75. context.fireUserInboundEventTriggered(event)
  76. return
  77. }
  78. if let proto = negotiatedProtocol {
  79. self.logger.debug("TLS handshake completed, negotiated protocol: \(proto)")
  80. } else {
  81. self.logger.debug("TLS handshake completed, no protocol negotiated")
  82. }
  83. self.verificationPromise.succeed(())
  84. }
  85. }