mirror of
https://github.com/apple/swift-nio-extras.git
synced 2025-05-15 01:18:58 +08:00
PR changes
This commit is contained in:
parent
8fbba2dd52
commit
2f52c356af
@ -303,7 +303,7 @@ let package = Package(
|
||||
.package(url: "https://github.com/apple/swift-http-structured-headers.git", from: "1.2.0"),
|
||||
.package(url: "https://github.com/apple/swift-atomics.git", from: "1.2.0"),
|
||||
.package(url: "https://github.com/apple/swift-algorithms.git", from: "1.2.0"),
|
||||
.package(url: "https://github.com/apple/swift-certificates.git", branch: "1.10.0"),
|
||||
.package(url: "https://github.com/apple/swift-certificates.git", from: "1.10.0"),
|
||||
.package(url: "https://github.com/apple/swift-nio-ssl.git", from: "2.29.3"),
|
||||
.package(url: "https://github.com/apple/swift-asn1.git", from: "1.3.1"),
|
||||
.package(url: "https://github.com/swift-server/swift-service-lifecycle.git", from: "2.8.0"),
|
||||
|
@ -30,23 +30,36 @@ public protocol CertificateReloader: Sendable {
|
||||
|
||||
extension TLSConfiguration {
|
||||
/// Errors thrown when creating a ``NIOSSL/TLSConfiguration`` with a ``CertificateReloader``.
|
||||
public struct CertificateReloaderError: Error {
|
||||
private enum _Backing {
|
||||
public struct CertificateReloaderError: Error, Hashable, CustomStringConvertible {
|
||||
private enum _Backing: CustomStringConvertible {
|
||||
case missingCertificateChain
|
||||
case missingPrivateKey
|
||||
|
||||
var description: String {
|
||||
switch self {
|
||||
case .missingCertificateChain:
|
||||
return "Missing certificate chain"
|
||||
case .missingPrivateKey:
|
||||
return "Missing private key"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private let backing: _Backing
|
||||
private let _backing: _Backing
|
||||
|
||||
private init(backing: _Backing) {
|
||||
self.backing = backing
|
||||
self._backing = backing
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
self._backing.description
|
||||
}
|
||||
|
||||
/// The given ``CertificateReloader`` could not provide a certificate chain with which to create this config.
|
||||
public static let missingCertificateChain: Self = .init(backing: .missingCertificateChain)
|
||||
public static var missingCertificateChain: Self { .init(backing: .missingCertificateChain) }
|
||||
|
||||
/// The given ``CertificateReloader`` could not provide a private key with which to create this config.
|
||||
public static let missingPrivateKey: Self = .init(backing: .missingPrivateKey)
|
||||
public static var missingPrivateKey: Self { .init(backing: .missingPrivateKey) }
|
||||
}
|
||||
|
||||
/// Create a ``NIOSSL/TLSConfiguration`` for use with server-side contexts, with certificate reloading enabled.
|
||||
@ -70,17 +83,15 @@ extension TLSConfiguration {
|
||||
certificateChain: certificateChain,
|
||||
privateKey: privateKey
|
||||
)
|
||||
return configuration.setCertificateReloader(certificateReloader)
|
||||
configuration.setCertificateReloader(certificateReloader)
|
||||
return configuration
|
||||
}
|
||||
|
||||
/// Configure a ``CertificateReloader`` to observe updates for the certificate and key pair used.
|
||||
/// - Parameter reloader: A ``CertificateReloader`` to watch for certificate and key pair updates.
|
||||
/// - Returns: A ``NIOSSL/TLSConfiguration`` that reloads the certificate and key used in its SSL handshake.
|
||||
@discardableResult
|
||||
mutating public func setCertificateReloader(_ reloader: some CertificateReloader) -> Self {
|
||||
mutating public func setCertificateReloader(_ reloader: some CertificateReloader) {
|
||||
self.sslContextCallback = { _, promise in
|
||||
promise.succeed(reloader.sslContextConfigurationOverride)
|
||||
}
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
@ -81,10 +81,14 @@ import Foundation
|
||||
/// not being recognized or not matching the configured one; not being able to verify a certificate's signature against the given
|
||||
/// private key; etc), then that attempt will be aborted but the service will keep on trying at the configured interval.
|
||||
/// The last-valid certificate-key pair (if any) will be returned as the ``sslContextConfigurationOverride``.
|
||||
#if compiler(>=6.0)
|
||||
@available(macOS 13, iOS 16, watchOS 9, tvOS 16, macCatalyst 16, visionOS 1, *)
|
||||
#else
|
||||
@available(macOS 13, iOS 16, watchOS 9, tvOS 16, macCatalyst 16, *)
|
||||
#endif
|
||||
public struct TimedCertificateReloader: CertificateReloader {
|
||||
/// The encoding for the certificate or the key.
|
||||
public struct Encoding: Sendable, Equatable {
|
||||
public struct Encoding: Sendable, Hashable {
|
||||
fileprivate enum _Backing {
|
||||
case der
|
||||
case pem
|
||||
@ -96,17 +100,26 @@ public struct TimedCertificateReloader: CertificateReloader {
|
||||
}
|
||||
|
||||
/// The encoding of this certificate/key is DER bytes.
|
||||
public static let der = Encoding(.der)
|
||||
public static var der: Self { .init(.der) }
|
||||
|
||||
/// The encoding of this certificate/key is PEM.
|
||||
public static let pem = Encoding(.pem)
|
||||
public static var pem: Self { .init(.pem) }
|
||||
}
|
||||
|
||||
/// A location specification for a certificate or key.
|
||||
public struct Location: Sendable {
|
||||
fileprivate enum _Backing {
|
||||
public struct Location: Sendable, CustomStringConvertible {
|
||||
fileprivate enum _Backing: CustomStringConvertible {
|
||||
case file(path: String)
|
||||
case memory(provider: @Sendable () -> [UInt8]?)
|
||||
|
||||
var description: String {
|
||||
switch self {
|
||||
case .file(let path):
|
||||
return "Filepath: \(path)"
|
||||
case .memory:
|
||||
return "<in-memory location>"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate let _backing: _Backing
|
||||
@ -115,6 +128,10 @@ public struct TimedCertificateReloader: CertificateReloader {
|
||||
self._backing = backing
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
self._backing.description
|
||||
}
|
||||
|
||||
/// This certificate/key can be found at the given filepath.
|
||||
/// - Parameter path: The filepath where the certificate/key can be found.
|
||||
/// - Returns: A `Location`.
|
||||
@ -170,10 +187,19 @@ public struct TimedCertificateReloader: CertificateReloader {
|
||||
}
|
||||
|
||||
/// Errors specific to the ``TimedCertificateReloader``.
|
||||
public struct Error: Swift.Error {
|
||||
private enum _Backing {
|
||||
public struct Error: Swift.Error, Hashable, CustomStringConvertible {
|
||||
private enum _Backing: Hashable, CustomStringConvertible {
|
||||
case certificatePathNotFound(String)
|
||||
case privateKeyPathNotFound(String)
|
||||
|
||||
var description: String {
|
||||
switch self {
|
||||
case .certificatePathNotFound(let path):
|
||||
return "Certificate path not found: \(path)"
|
||||
case .privateKeyPathNotFound(let path):
|
||||
return "Private key path not found: \(path)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private let _backing: _Backing
|
||||
@ -195,6 +221,10 @@ public struct TimedCertificateReloader: CertificateReloader {
|
||||
public static func privateKeyPathNotFound(_ path: String) -> Self {
|
||||
Self(.privateKeyPathNotFound(path))
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
self._backing.description
|
||||
}
|
||||
}
|
||||
|
||||
private struct CertificateKeyPair {
|
||||
@ -259,7 +289,7 @@ public struct TimedCertificateReloader: CertificateReloader {
|
||||
refreshInterval: Duration(refreshInterval),
|
||||
validatingCertificateDescription: validatingCertificateDescription,
|
||||
validatingPrivateKeyDescription: validatingPrivateKeyDescription,
|
||||
logger: nil
|
||||
logger: logger
|
||||
)
|
||||
}
|
||||
|
||||
@ -319,13 +349,12 @@ public struct TimedCertificateReloader: CertificateReloader {
|
||||
do {
|
||||
try self.reloadPair()
|
||||
} catch {
|
||||
self.logger?.error(
|
||||
"""
|
||||
An unexpected error was encountered while trying to reload the certificate and \
|
||||
private key pair.
|
||||
""",
|
||||
self.logger?.debug(
|
||||
"Failed to reload certificate and private key.",
|
||||
metadata: [
|
||||
"error": error
|
||||
"error": "\(error)",
|
||||
"certificatePath": "\(self.certificateDescription.location)",
|
||||
"privateKeyPath": "\(self.privateKeyDescription.location)",
|
||||
]
|
||||
)
|
||||
}
|
||||
@ -417,5 +446,9 @@ public struct TimedCertificateReloader: CertificateReloader {
|
||||
}
|
||||
}
|
||||
|
||||
#if compiler(>=6.0)
|
||||
@available(macOS 13, iOS 16, watchOS 9, tvOS 16, macCatalyst 16, visionOS 1, *)
|
||||
#else
|
||||
@available(macOS 13, iOS 16, watchOS 9, tvOS 16, macCatalyst 16, *)
|
||||
#endif
|
||||
extension TimedCertificateReloader: Service {}
|
||||
|
@ -272,6 +272,28 @@ final class TimedCertificateReloaderTests: XCTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
func testCertificateReloaderErrorDescription() {
|
||||
XCTAssertEqual(
|
||||
"\(TLSConfiguration.CertificateReloaderError.missingCertificateChain)",
|
||||
"Missing certificate chain"
|
||||
)
|
||||
XCTAssertEqual(
|
||||
"\(TLSConfiguration.CertificateReloaderError.missingPrivateKey)",
|
||||
"Missing private key"
|
||||
)
|
||||
}
|
||||
|
||||
func testTimedCertificateReloaderErrorDescription() {
|
||||
XCTAssertEqual(
|
||||
"\(TimedCertificateReloader.Error.certificatePathNotFound("some/path"))",
|
||||
"Certificate path not found: some/path"
|
||||
)
|
||||
XCTAssertEqual(
|
||||
"\(TimedCertificateReloader.Error.privateKeyPathNotFound("some/path"))",
|
||||
"Private key path not found: some/path"
|
||||
)
|
||||
}
|
||||
|
||||
static let startDate = Date()
|
||||
static let samplePrivateKey = P384.Signing.PrivateKey()
|
||||
static let sampleCertName = try! DistinguishedName {
|
||||
|
Loading…
x
Reference in New Issue
Block a user