mirror of
https://github.com/apple/swift-nio-extras.git
synced 2025-05-14 17:02:43 +08:00
PR changes
This commit is contained in:
parent
f3cce7f10c
commit
33fe7400b5
@ -254,50 +254,14 @@ public struct TimedCertificateReloader: CertificateReloader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize a new ``TimedCertificateReloader``.
|
/// Initialize a new ``TimedCertificateReloader``.
|
||||||
|
/// - Important: ``TimedCertificateReloader/sslContextConfigurationOverride`` will not contain any
|
||||||
|
/// certificate or private key overrides until either ``TimedCertificateReloader/run()`` or
|
||||||
|
/// ``TimedCertificateReloader/reload()`` are called.
|
||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
/// - refreshInterval: The interval at which attempts to update the certificate and private key should be made.
|
/// - refreshInterval: The interval at which attempts to update the certificate and private key should be made.
|
||||||
/// - certificateSource: A ``TimedCertificateReloader/CertificateSource``.
|
/// - certificateSource: A ``TimedCertificateReloader/CertificateSource``.
|
||||||
/// - privateKeySource: A ``TimedCertificateReloader/PrivateKeySource``.
|
/// - privateKeySource: A ``TimedCertificateReloader/PrivateKeySource``.
|
||||||
public init(
|
/// - logger: An optional logger.
|
||||||
refreshInterval: TimeAmount,
|
|
||||||
certificateSource: CertificateSource,
|
|
||||||
privateKeySource: PrivateKeySource,
|
|
||||||
logger: Logger? = nil
|
|
||||||
) {
|
|
||||||
self.init(
|
|
||||||
refreshInterval: Duration(refreshInterval),
|
|
||||||
certificateSource: certificateSource,
|
|
||||||
privateKeySource: privateKeySource,
|
|
||||||
logger: logger
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Attempt to initialize a new ``TimedCertificateReloader``, but throw if the given certificate and private keys cannot be
|
|
||||||
/// loaded.
|
|
||||||
/// - Parameters:
|
|
||||||
/// - refreshInterval: The interval at which attempts to update the certificate and private key should be made.
|
|
||||||
/// - validatingCertificateSource: A ``TimedCertificateReloader/CertificateSource``.
|
|
||||||
/// - validatingPrivateKeySource: A ``TimedCertificateReloader/PrivateKeySource``.
|
|
||||||
/// - Throws: If the certificate or private key cannot be loaded.
|
|
||||||
public init(
|
|
||||||
refreshInterval: TimeAmount,
|
|
||||||
validatingCertificateSource: CertificateSource,
|
|
||||||
validatingPrivateKeySource: PrivateKeySource,
|
|
||||||
logger: Logger? = nil
|
|
||||||
) throws {
|
|
||||||
try self.init(
|
|
||||||
refreshInterval: Duration(refreshInterval),
|
|
||||||
validatingCertificateSource: validatingCertificateSource,
|
|
||||||
validatingPrivateKeySource: validatingPrivateKeySource,
|
|
||||||
logger: logger
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Initialize a new ``TimedCertificateReloader``.
|
|
||||||
/// - Parameters:
|
|
||||||
/// - refreshInterval: The interval at which attempts to update the certificate and private key should be made.
|
|
||||||
/// - certificateSource: A ``TimedCertificateReloader/CertificateSource``.
|
|
||||||
/// - privateKeySource: A ``TimedCertificateReloader/PrivateKeySource``.
|
|
||||||
public init(
|
public init(
|
||||||
refreshInterval: Duration,
|
refreshInterval: Duration,
|
||||||
certificateSource: CertificateSource,
|
certificateSource: CertificateSource,
|
||||||
@ -309,36 +273,34 @@ public struct TimedCertificateReloader: CertificateReloader {
|
|||||||
self.privateKeySource = privateKeySource
|
self.privateKeySource = privateKeySource
|
||||||
self.state = NIOLockedValueBox(nil)
|
self.state = NIOLockedValueBox(nil)
|
||||||
self.logger = logger
|
self.logger = logger
|
||||||
|
|
||||||
// Immediately try to load the configured cert and key to avoid having to wait for the first
|
|
||||||
// reload loop to run.
|
|
||||||
// We ignore errors because this initializer tolerates not finding the certificate and/or
|
|
||||||
// private key on first load.
|
|
||||||
try? self.reloadPair()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempt to initialize a new ``TimedCertificateReloader``, but throw if the given certificate and private keys cannot be
|
/// Initialize a new ``TimedCertificateReloader``, and attempt to reload the certificate and private key pair from the given
|
||||||
/// loaded.
|
/// sources. If the reload fails (because e.g. the paths aren't valid), this method will throw.
|
||||||
|
/// - Important: If this method does not throw, it is guaranteed that
|
||||||
|
/// ``TimedCertificateReloader/sslContextConfigurationOverride`` will contain the configured certificate and
|
||||||
|
/// private key pair, even before the first reload is triggered or ``TimedCertificateReloader/run()`` is called.
|
||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
/// - refreshInterval: The interval at which attempts to update the certificate and private key should be made.
|
/// - refreshInterval: The interval at which attempts to update the certificate and private key should be made.
|
||||||
/// - validatingCertificateSource: A ``TimedCertificateReloader/CertificateSource``.
|
/// - certificateSource: A ``TimedCertificateReloader/CertificateSource``.
|
||||||
/// - validatingPrivateKeySource: A ``TimedCertificateReloader/PrivateKeySource``.
|
/// - privateKeySource: A ``TimedCertificateReloader/PrivateKeySource``.
|
||||||
/// - Throws: If the certificate or private key cannot be loaded.
|
/// - logger: An optional logger.
|
||||||
public init(
|
/// - Returns: The newly created ``TimedCertificateReloader``.
|
||||||
|
/// - Throws: If either the certificate or private key sources cannot be loaded, an error will be thrown.
|
||||||
|
static public func makeReloaderValidatingSources(
|
||||||
refreshInterval: Duration,
|
refreshInterval: Duration,
|
||||||
validatingCertificateSource: CertificateSource,
|
certificateSource: CertificateSource,
|
||||||
validatingPrivateKeySource: PrivateKeySource,
|
privateKeySource: PrivateKeySource,
|
||||||
logger: Logger? = nil
|
logger: Logger? = nil
|
||||||
) throws {
|
) throws -> Self {
|
||||||
self.refreshInterval = refreshInterval
|
let reloader = Self.init(
|
||||||
self.certificateSource = validatingCertificateSource
|
refreshInterval: refreshInterval,
|
||||||
self.privateKeySource = validatingPrivateKeySource
|
certificateSource: certificateSource,
|
||||||
self.state = NIOLockedValueBox(nil)
|
privateKeySource: privateKeySource,
|
||||||
self.logger = logger
|
logger: logger
|
||||||
|
)
|
||||||
// Immediately try to load the configured cert and key to avoid having to wait for the first
|
try reloader.reload()
|
||||||
// reload loop to run.
|
return reloader
|
||||||
try self.reloadPair()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A long-running method to run the ``TimedCertificateReloader`` and start observing updates for the certificate and
|
/// A long-running method to run the ``TimedCertificateReloader`` and start observing updates for the certificate and
|
||||||
@ -347,7 +309,7 @@ public struct TimedCertificateReloader: CertificateReloader {
|
|||||||
public func run() async throws {
|
public func run() async throws {
|
||||||
for try await _ in AsyncTimerSequence.repeating(every: self.refreshInterval).cancelOnGracefulShutdown() {
|
for try await _ in AsyncTimerSequence.repeating(every: self.refreshInterval).cancelOnGracefulShutdown() {
|
||||||
do {
|
do {
|
||||||
try self.reloadPair()
|
try self.reload()
|
||||||
} catch {
|
} catch {
|
||||||
self.logger?.debug(
|
self.logger?.debug(
|
||||||
"Failed to reload certificate and private key.",
|
"Failed to reload certificate and private key.",
|
||||||
@ -361,7 +323,8 @@ public struct TimedCertificateReloader: CertificateReloader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func reloadPair() throws {
|
/// Manually attempt a certificate and private key pair update.
|
||||||
|
public func reload() throws {
|
||||||
if let certificateBytes = try self.loadCertificate(),
|
if let certificateBytes = try self.loadCertificate(),
|
||||||
let keyBytes = try self.loadPrivateKey(),
|
let keyBytes = try self.loadPrivateKey(),
|
||||||
let certificate = try self.parseCertificate(from: certificateBytes),
|
let certificate = try self.parseCertificate(from: certificateBytes),
|
||||||
|
@ -27,7 +27,8 @@ final class TimedCertificateReloaderTests: XCTestCase {
|
|||||||
privateKey: .init(
|
privateKey: .init(
|
||||||
location: .memory(provider: { Array(Self.samplePrivateKey.derRepresentation) }),
|
location: .memory(provider: { Array(Self.samplePrivateKey.derRepresentation) }),
|
||||||
format: .der
|
format: .der
|
||||||
)
|
),
|
||||||
|
validateSources: false
|
||||||
) { reloader in
|
) { reloader in
|
||||||
let override = reloader.sslContextConfigurationOverride
|
let override = reloader.sslContextConfigurationOverride
|
||||||
XCTAssertNil(override.certificateChain)
|
XCTAssertNil(override.certificateChain)
|
||||||
@ -35,6 +36,25 @@ final class TimedCertificateReloaderTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testCertificatePathDoesNotExist_ValidatingSource() async throws {
|
||||||
|
do {
|
||||||
|
try await runTimedCertificateReloaderTest(
|
||||||
|
certificate: .init(location: .file(path: "doesnotexist"), format: .der),
|
||||||
|
privateKey: .init(
|
||||||
|
location: .memory(provider: { Array(Self.samplePrivateKey.derRepresentation) }),
|
||||||
|
format: .der
|
||||||
|
)
|
||||||
|
) { _ in
|
||||||
|
XCTFail("Test should have failed before reaching this point.")
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
XCTAssertEqual(
|
||||||
|
error as? TimedCertificateReloader.Error,
|
||||||
|
TimedCertificateReloader.Error.certificatePathNotFound("doesnotexist")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testKeyPathDoesNotExist() async throws {
|
func testKeyPathDoesNotExist() async throws {
|
||||||
try await runTimedCertificateReloaderTest(
|
try await runTimedCertificateReloaderTest(
|
||||||
certificate: .init(
|
certificate: .init(
|
||||||
@ -44,7 +64,8 @@ final class TimedCertificateReloaderTests: XCTestCase {
|
|||||||
privateKey: .init(
|
privateKey: .init(
|
||||||
location: .file(path: "doesnotexist"),
|
location: .file(path: "doesnotexist"),
|
||||||
format: .der
|
format: .der
|
||||||
)
|
),
|
||||||
|
validateSources: false
|
||||||
) { reloader in
|
) { reloader in
|
||||||
let override = reloader.sslContextConfigurationOverride
|
let override = reloader.sslContextConfigurationOverride
|
||||||
XCTAssertNil(override.certificateChain)
|
XCTAssertNil(override.certificateChain)
|
||||||
@ -52,6 +73,28 @@ final class TimedCertificateReloaderTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testKeyPathDoesNotExist_ValidatingSource() async throws {
|
||||||
|
do {
|
||||||
|
try await runTimedCertificateReloaderTest(
|
||||||
|
certificate: .init(
|
||||||
|
location: .memory(provider: { try? Self.sampleCert.serializeAsPEM().derBytes }),
|
||||||
|
format: .der
|
||||||
|
),
|
||||||
|
privateKey: .init(
|
||||||
|
location: .file(path: "doesnotexist"),
|
||||||
|
format: .der
|
||||||
|
)
|
||||||
|
) { _ in
|
||||||
|
XCTFail("Test should have failed before reaching this point.")
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
XCTAssertEqual(
|
||||||
|
error as? TimedCertificateReloader.Error,
|
||||||
|
TimedCertificateReloader.Error.privateKeyPathNotFound("doesnotexist")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testCertificateIsInUnexpectedFormat() async throws {
|
func testCertificateIsInUnexpectedFormat() async throws {
|
||||||
try await runTimedCertificateReloaderTest(
|
try await runTimedCertificateReloaderTest(
|
||||||
certificate: .init(
|
certificate: .init(
|
||||||
@ -323,6 +366,7 @@ final class TimedCertificateReloaderTests: XCTestCase {
|
|||||||
private func runTimedCertificateReloaderTest(
|
private func runTimedCertificateReloaderTest(
|
||||||
certificate: TimedCertificateReloader.CertificateSource,
|
certificate: TimedCertificateReloader.CertificateSource,
|
||||||
privateKey: TimedCertificateReloader.PrivateKeySource,
|
privateKey: TimedCertificateReloader.PrivateKeySource,
|
||||||
|
validateSources: Bool = true,
|
||||||
_ body: @escaping @Sendable (TimedCertificateReloader) async throws -> Void
|
_ body: @escaping @Sendable (TimedCertificateReloader) async throws -> Void
|
||||||
) async throws {
|
) async throws {
|
||||||
let reloader = TimedCertificateReloader(
|
let reloader = TimedCertificateReloader(
|
||||||
@ -333,6 +377,11 @@ final class TimedCertificateReloaderTests: XCTestCase {
|
|||||||
),
|
),
|
||||||
privateKeySource: .init(location: privateKey.location, format: privateKey.format)
|
privateKeySource: .init(location: privateKey.location, format: privateKey.format)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if validateSources {
|
||||||
|
try reloader.reload()
|
||||||
|
}
|
||||||
|
|
||||||
try await withThrowingTaskGroup(of: Void.self) { group in
|
try await withThrowingTaskGroup(of: Void.self) { group in
|
||||||
group.addTask {
|
group.addTask {
|
||||||
try await reloader.run()
|
try await reloader.run()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user