-
Notifications
You must be signed in to change notification settings - Fork 125
Support NIO Transport Services - part 2 #184
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 29 commits
a138c04
33b6de0
eb81523
0b468f2
b06210f
f40a084
6451389
a7815d1
8e18b0b
61c735e
eeea672
4a1ff5d
014cef3
d500a0c
76b3618
8574edd
9a41fc3
639d0d5
8d43633
299e14c
69b0e38
0eef5f0
f7a3a67
131f96a
72c384b
9d4e9ef
05c0fe1
262a6fe
038ef0b
8d92958
30d943c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This source file is part of the AsyncHTTPClient open source project | ||
// | ||
// Copyright (c) 2020 Apple Inc. and the AsyncHTTPClient project authors | ||
// Licensed under Apache License v2.0 | ||
// | ||
// See LICENSE.txt for license information | ||
// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#if canImport(Network) | ||
|
||
import Network | ||
import NIO | ||
import NIOHTTP1 | ||
import NIOTransportServices | ||
|
||
extension HTTPClient { | ||
public struct NWPOSIXError: Error, CustomStringConvertible { | ||
/// POSIX error code (enum) | ||
public let errorCode: POSIXErrorCode | ||
|
||
/// actual reason, in human readable form | ||
private let reason: String | ||
|
||
/// Initialise a NWPOSIXError | ||
/// - Parameters: | ||
/// - errorType: posix error type | ||
/// - reason: String describing reason for error | ||
public init(_ errorCode: POSIXErrorCode, reason: String) { | ||
self.errorCode = errorCode | ||
self.reason = reason | ||
} | ||
|
||
public var description: String { return self.reason } | ||
} | ||
|
||
public struct NWTLSError: Error, CustomStringConvertible { | ||
/// TLS error status. List of TLS errors can be found in <Security/SecureTransport.h> | ||
public let status: OSStatus | ||
|
||
/// actual reason, in human readable form | ||
private let reason: String | ||
|
||
/// initialise a NWTLSError | ||
/// - Parameters: | ||
/// - status: TLS status | ||
/// - reason: String describing reason for error | ||
public init(_ status: OSStatus, reason: String) { | ||
self.status = status | ||
self.reason = reason | ||
} | ||
|
||
public var description: String { return self.reason } | ||
} | ||
|
||
@available(macOS 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *) | ||
class NWErrorHandler: ChannelInboundHandler { | ||
typealias InboundIn = HTTPClientResponsePart | ||
|
||
func errorCaught(context: ChannelHandlerContext, error: Error) { | ||
context.fireErrorCaught(NWErrorHandler.translateError(error)) | ||
} | ||
|
||
static func translateError(_ error: Error) -> Error { | ||
if let error = error as? NWError { | ||
switch error { | ||
case .tls(let status): | ||
return NWTLSError(status, reason: error.localizedDescription) | ||
case .posix(let errorCode): | ||
return NWPOSIXError(errorCode, reason: error.localizedDescription) | ||
default: | ||
return error | ||
} | ||
} | ||
return error | ||
} | ||
} | ||
} | ||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This source file is part of the AsyncHTTPClient open source project | ||
// | ||
// Copyright (c) 2020 Apple Inc. and the AsyncHTTPClient project authors | ||
// Licensed under Apache License v2.0 | ||
// | ||
// See LICENSE.txt for license information | ||
// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#if canImport(Network) | ||
|
||
import Foundation | ||
import Network | ||
import NIOSSL | ||
import NIOTransportServices | ||
|
||
extension TLSVersion { | ||
/// return Network framework TLS protocol version | ||
var nwTLSProtocolVersion: tls_protocol_version_t { | ||
switch self { | ||
case .tlsv1: | ||
return .TLSv10 | ||
case .tlsv11: | ||
return .TLSv11 | ||
case .tlsv12: | ||
return .TLSv12 | ||
case .tlsv13: | ||
return .TLSv13 | ||
} | ||
} | ||
} | ||
|
||
extension TLSVersion { | ||
/// return as SSL protocol | ||
var sslProtocol: SSLProtocol { | ||
switch self { | ||
case .tlsv1: | ||
return .tlsProtocol1 | ||
case .tlsv11: | ||
return .tlsProtocol11 | ||
case .tlsv12: | ||
return .tlsProtocol12 | ||
case .tlsv13: | ||
return .tlsProtocol13 | ||
} | ||
} | ||
} | ||
|
||
@available(macOS 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *) | ||
extension TLSConfiguration { | ||
/// Dispatch queue used by Network framework TLS to control certificate verification | ||
static var tlsDispatchQueue = DispatchQueue(label: "TLSDispatch") | ||
|
||
/// create NWProtocolTLS.Options for use with NIOTransportServices from the NIOSSL TLSConfiguration | ||
/// | ||
/// - Parameter queue: Dispatch queue to run `sec_protocol_options_set_verify_block` on. | ||
/// - Returns: Equivalent NWProtocolTLS Options | ||
func getNWProtocolTLSOptions() -> NWProtocolTLS.Options { | ||
let options = NWProtocolTLS.Options() | ||
|
||
// minimum TLS protocol | ||
if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) { | ||
sec_protocol_options_set_min_tls_protocol_version(options.securityProtocolOptions, self.minimumTLSVersion.nwTLSProtocolVersion) | ||
} else { | ||
sec_protocol_options_set_tls_min_version(options.securityProtocolOptions, self.minimumTLSVersion.sslProtocol) | ||
} | ||
|
||
// maximum TLS protocol | ||
if let maximumTLSVersion = self.maximumTLSVersion { | ||
if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) { | ||
sec_protocol_options_set_max_tls_protocol_version(options.securityProtocolOptions, maximumTLSVersion.nwTLSProtocolVersion) | ||
} else { | ||
sec_protocol_options_set_tls_max_version(options.securityProtocolOptions, maximumTLSVersion.sslProtocol) | ||
} | ||
} | ||
|
||
// application protocols | ||
for applicationProtocol in self.applicationProtocols { | ||
applicationProtocol.withCString { buffer in | ||
sec_protocol_options_add_tls_application_protocol(options.securityProtocolOptions, buffer) | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This one is also reasonably do-able, if we configure a custom trust challenge. For now, though, let's leave it as-is. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I assume this would be done with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's correct. You start out with a trust that's appropriately configured (it's passed in, I believe) and you can modify it as needed. |
||
|
||
// the certificate chain | ||
if self.certificateChain.count > 0 { | ||
preconditionFailure("TLSConfiguration.certificateChain is not supported") | ||
} | ||
|
||
// cipher suites | ||
if self.cipherSuites.count > 0 { | ||
// TODO: Requires NIOSSL to provide list of cipher values before we can continue | ||
// https://github.com/apple/swift-nio-ssl/issues/207 | ||
} | ||
|
||
// key log callback | ||
if self.keyLogCallback != nil { | ||
preconditionFailure("TLSConfiguration.keyLogCallback is not supported") | ||
} | ||
|
||
// private key | ||
if self.privateKey != nil { | ||
preconditionFailure("TLSConfiguration.privateKey is not supported") | ||
} | ||
|
||
// renegotiation support key is unsupported | ||
|
||
// trust roots | ||
if let trustRoots = self.trustRoots { | ||
guard case .default = trustRoots else { | ||
preconditionFailure("TLSConfiguration.trustRoots != .default is not supported") | ||
} | ||
} | ||
|
||
switch self.certificateVerification { | ||
case .none: | ||
// add verify block to control certificate verification | ||
sec_protocol_options_set_verify_block( | ||
options.securityProtocolOptions, | ||
{ _, _, sec_protocol_verify_complete in | ||
sec_protocol_verify_complete(true) | ||
}, TLSConfiguration.tlsDispatchQueue | ||
) | ||
|
||
case .noHostnameVerification: | ||
precondition(self.certificateVerification != .noHostnameVerification, "TLSConfiguration.certificateVerification = .noHostnameVerification is not supported") | ||
|
||
case .fullVerification: | ||
break | ||
} | ||
|
||
return options | ||
} | ||
} | ||
|
||
#endif |
Uh oh!
There was an error while loading. Please reload this page.