Skip to content

Commit 0b9b203

Browse files
committed
Address feedback from review
1 parent e8b6f32 commit 0b9b203

File tree

4 files changed

+51
-37
lines changed

4 files changed

+51
-37
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import NIOCore
2+
import NIOSSL
3+
public import X509
4+
5+
@available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0, *)
6+
extension NIOHTTPServer {
7+
/// Connection-specific information available during request handling.
8+
///
9+
/// Provides access to data such as the peer's validated certificate chain.
10+
public struct ConnectionContext: Sendable {
11+
var peerCertificateChainFuture: EventLoopFuture<NIOSSL.ValidatedCertificateChain?>?
12+
13+
init(_ peerCertificateChainFuture: EventLoopFuture<NIOSSL.ValidatedCertificateChain?>? = nil) {
14+
self.peerCertificateChainFuture = peerCertificateChainFuture
15+
}
16+
17+
/// The peer's validated certificate chain. This returns `nil` if a custom verification callback was not set
18+
/// when configuring mTLS in the server configuration, or if the custom verification callback did not return the
19+
/// derived validated chain.
20+
public var peerCertificateChain: X509.ValidatedCertificateChain? {
21+
get async throws {
22+
if let certs = try await self.peerCertificateChainFuture?.get() {
23+
return .init(uncheckedCertificateChain: try certs.map { try Certificate($0) })
24+
}
25+
return nil
26+
}
27+
}
28+
}
29+
}

Sources/HTTPServer/NIOHTTPServer.swift

Lines changed: 7 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import NIOPosix
2525
import NIOSSL
2626
import SwiftASN1
2727
import Synchronization
28-
public import X509
28+
import X509
2929

3030
/// A generic HTTP server that can handle incoming HTTP requests.
3131
///
@@ -87,32 +87,9 @@ public struct NIOHTTPServer: HTTPServerProtocol {
8787

8888
/// Task-local storage for connection-specific information accessible from request handlers.
8989
///
90-
/// Use this to access data such as the peer's validated certificate chain.
90+
/// - SeeAlso: ``ConnectionContext``.
9191
@TaskLocal public static var connectionContext = ConnectionContext()
9292

93-
/// Connection-specific information available during request handling.
94-
///
95-
/// Provides access to data such as the peer's validated certificate chain.
96-
public struct ConnectionContext: Sendable {
97-
var peerCertificateChainFuture: EventLoopFuture<NIOSSL.ValidatedCertificateChain?>?
98-
99-
init(_ peerCertificateChainFuture: EventLoopFuture<NIOSSL.ValidatedCertificateChain?>? = nil) {
100-
self.peerCertificateChainFuture = peerCertificateChainFuture
101-
}
102-
103-
/// The peer's validated certificate chain. This returns `nil` if a custom verification callback was not set
104-
/// when configuring mTLS in the server configuration, or if the custom verification callback did not return the
105-
/// derived validated chain.
106-
public var peerCertificateChain: X509.ValidatedCertificateChain? {
107-
get async throws {
108-
if let certs = try await self.peerCertificateChainFuture?.get() {
109-
return .init(uncheckedCertificateChain: try certs.map { try Certificate($0) })
110-
}
111-
return nil
112-
}
113-
}
114-
}
115-
11693
/// Create a new ``HTTPServer`` implemented over `SwiftNIO`.
11794
/// - Parameters:
11895
/// - logger: A logger instance for recording server events and debugging information.
@@ -387,8 +364,8 @@ public struct NIOHTTPServer: HTTPServerProtocol {
387364
case .http2((let http2Connection, let http2Multiplexer)):
388365
do {
389366
let chainFuture = http2Connection.nioSSL_peerValidatedCertificateChain()
390-
for try await http2StreamChannel in http2Multiplexer.inbound {
391-
Self.$connectionContext.withValue(ConnectionContext(chainFuture)) {
367+
try await Self.$connectionContext.withValue(ConnectionContext(chainFuture)) {
368+
for try await http2StreamChannel in http2Multiplexer.inbound {
392369
connectionGroup.addTask {
393370
try await self.handleRequestChannel(
394371
channel: http2StreamChannel,
@@ -520,7 +497,9 @@ extension NIOHTTPServer {
520497
return .certificateVerified(.init(.init(nioSSLCerts)))
521498

522499
case .failed(let error):
523-
self.logger.error("Custom certificate verification failed: \(error)")
500+
self.logger.error("Custom certificate verification failed", metadata: [
501+
"failure-reason": .string(error.reason)
502+
])
524503
return .failed
525504
}
526505
}

Sources/HTTPServer/NIOHTTPServerConfiguration.swift

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,15 @@ public struct NIOHTTPServerConfiguration: Sendable {
153153
/// - certificateVerification: Configures the client certificate validation behaviour. Defaults to
154154
/// ``CertificateVerification/noHostnameVerification``.
155155
/// - customCertificateVerificationCallback: If specified, this callback *overrides* the default NIOSSL client
156-
/// certificate verification logic. Refer to the documentation for this argument in
157-
/// ``mTLS(certificateChain:privateKey:trustRoots:certificateVerification:customCertificateVerificationCallback:)``
158-
/// for more details.
156+
/// certificate verification logic. The callback receives the certificates presented by the peer. Within the
157+
/// callback, you must validate these certificates against your trust roots and derive a validated chain of
158+
/// trust per [RFC 4158](https://datatracker.ietf.org/doc/html/rfc4158). Return
159+
/// ``CertificateVerificationResult/certificateVerified(_:)`` from the callback if verification succeeds,
160+
/// optionally including the validated certificate chain you derived. Returning the validated certificate
161+
/// chain allows ``NIOHTTPServer`` to provide access to it in the request handler through
162+
/// ``NIOHTTPServer/ConnectionContext/peerCertificateChain``, accessed via the task-local
163+
/// ``NIOHTTPServer/connectionContext`` property. Otherwise, return
164+
/// ``CertificateVerificationResult/failed(_:)`` if verification fails.
159165
///
160166
/// - Warning: If `customCertificateVerificationCallback` is set, it will **override** NIOSSL's default
161167
/// certificate verification logic.

Sources/HTTPServer/NIOSSL+X509.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import X509
1919

2020
// MARK: X509 to NIOSSL
2121

22-
@available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0, *)
22+
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, visionOS 1.0, *)
2323
extension NIOSSLCertificate {
2424
convenience init(_ certificate: Certificate) throws {
2525
var serializer = DER.Serializer()
@@ -28,28 +28,28 @@ extension NIOSSLCertificate {
2828
}
2929
}
3030

31-
@available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0, *)
31+
@available(macOS 11.0, iOS 14, tvOS 14, watchOS 7, macCatalyst 14, visionOS 1.0, *)
3232
extension NIOSSLPrivateKey {
3333
convenience init(_ privateKey: Certificate.PrivateKey) throws {
3434
try self.init(bytes: try privateKey.serializeAsPEM().derBytes, format: .der)
3535
}
3636
}
3737

38-
@available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0, *)
38+
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, visionOS 1.0, *)
3939
extension NIOSSLCertificateSource {
4040
init(_ certificate: Certificate) throws {
4141
self = .certificate(try NIOSSLCertificate(certificate))
4242
}
4343
}
4444

45-
@available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0, *)
45+
@available(macOS 11.0, iOS 14, tvOS 14, watchOS 7, macCatalyst 14, visionOS 1.0, *)
4646
extension NIOSSLPrivateKeySource {
4747
init(_ privateKey: Certificate.PrivateKey) throws {
4848
self = .privateKey(try NIOSSLPrivateKey(privateKey))
4949
}
5050
}
5151

52-
@available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0, *)
52+
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, visionOS 1.0, *)
5353
extension NIOSSLTrustRoots {
5454
init(treatingNilAsSystemTrustRoots certificates: [Certificate]?) throws {
5555
if let certificates {
@@ -62,7 +62,7 @@ extension NIOSSLTrustRoots {
6262

6363
// MARK: NIOSSL to X509
6464

65-
@available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0, *)
65+
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, visionOS 1.0, *)
6666
extension Certificate {
6767
init(_ certificate: NIOSSLCertificate) throws {
6868
try self.init(derEncoded: certificate.toDERBytes())

0 commit comments

Comments
 (0)