Skip to content

Commit b3de7fd

Browse files
authored
Add support for certificate reloading (#21)
1 parent f1181ce commit b3de7fd

File tree

5 files changed

+71
-40
lines changed

5 files changed

+71
-40
lines changed

Package.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ let package = Package(
5959
.product(name: "Logging", package: "swift-log"),
6060
.product(name: "NIOHTTPTypesHTTP1", package: "swift-nio-extras"),
6161
.product(name: "NIOHTTPTypesHTTP2", package: "swift-nio-extras"),
62+
.product(name: "NIOCertificateReloading", package: "swift-nio-extras")
6263
],
6364
swiftSettings: extraSettings
6465
),

Sources/Example/Example.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ struct Example {
2626
logger: logger,
2727
configuration: .init(
2828
bindTarget: .hostAndPort(host: "127.0.0.1", port: 12345),
29-
tlsConfiguration: .certificateChainAndPrivateKey(
29+
transportSecurity: .tls(
3030
certificateChain: [
3131
try Certificate(
3232
version: .v3,

Sources/HTTPServer/HTTPServer.swift

Lines changed: 49 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
public import HTTPTypes
22
public import Logging
3+
import NIOCertificateReloading
34
import NIOCore
45
import NIOHTTP1
56
import NIOHTTP2
@@ -164,21 +165,63 @@ public final class Server<RequestHandler: HTTPServerRequestHandler> {
164165
)
165166
}
166167

167-
switch configuration.tlSConfiguration.backing {
168-
case .insecure:
168+
switch configuration.transportSecurity.backing {
169+
case .plaintext:
169170
try await Self.serveInsecureHTTP1_1(
170171
bindTarget: configuration.bindTarget,
171172
handler: handler,
172173
asyncChannelConfiguration: asyncChannelConfiguration,
173174
logger: logger
174175
)
175176

176-
case .certificateChainAndPrivateKey(let certificateChain, let privateKey):
177-
let http2Config = NIOHTTP2Handler.Configuration(httpServerHTTP2Configuration: configuration.http2)
177+
case .reloadingTLS(let certificateReloader):
178+
let http2Config = NIOHTTP2Handler.Configuration(
179+
httpServerHTTP2Configuration: configuration.http2
180+
)
181+
182+
var tlsConfiguration: TLSConfiguration = try .makeServerConfiguration(
183+
certificateReloader: certificateReloader
184+
)
185+
tlsConfiguration.applicationProtocols = ["h2", "http/1.1"]
186+
178187
try await Self.serveSecureUpgrade(
179188
bindTarget: configuration.bindTarget,
189+
tlsConfiguration: tlsConfiguration,
190+
handler: handler,
191+
asyncChannelConfiguration: asyncChannelConfiguration,
192+
http2Configuration: http2Config,
193+
logger: logger
194+
)
195+
196+
case .staticTLS(let certificateChain, let privateKey):
197+
let http2Config = NIOHTTP2Handler.Configuration(
198+
httpServerHTTP2Configuration: configuration.http2
199+
)
200+
201+
let certificateChain = try certificateChain
202+
.map {
203+
try NIOSSLCertificate(
204+
bytes: $0.serializeAsPEM().derBytes,
205+
format: .der
206+
)
207+
}
208+
.map { NIOSSLCertificateSource.certificate($0) }
209+
let privateKey = NIOSSLPrivateKeySource.privateKey(
210+
try NIOSSLPrivateKey(
211+
bytes: privateKey.serializeAsPEM().derBytes,
212+
format: .der
213+
)
214+
)
215+
216+
var tlsConfiguration: TLSConfiguration = .makeServerConfiguration(
180217
certificateChain: certificateChain,
181-
privateKey: privateKey,
218+
privateKey: privateKey
219+
)
220+
tlsConfiguration.applicationProtocols = ["h2", "http/1.1"]
221+
222+
try await Self.serveSecureUpgrade(
223+
bindTarget: configuration.bindTarget,
224+
tlsConfiguration: tlsConfiguration,
182225
handler: handler,
183226
asyncChannelConfiguration: asyncChannelConfiguration,
184227
http2Configuration: http2Config,
@@ -225,8 +268,7 @@ public final class Server<RequestHandler: HTTPServerRequestHandler> {
225268

226269
private static func serveSecureUpgrade(
227270
bindTarget: HTTPServerConfiguration.BindTarget,
228-
certificateChain: [Certificate],
229-
privateKey: Certificate.PrivateKey,
271+
tlsConfiguration: TLSConfiguration,
230272
handler: RequestHandler,
231273
asyncChannelConfiguration: NIOAsyncChannel<HTTPRequestPart, HTTPResponsePart>.Configuration,
232274
http2Configuration: NIOHTTP2Handler.Configuration,
@@ -238,27 +280,6 @@ public final class Server<RequestHandler: HTTPServerRequestHandler> {
238280
.serverChannelOption(.socketOption(.so_reuseaddr), value: 1)
239281
.bind(host: host, port: port) { channel in
240282
channel.eventLoop.makeCompletedFuture {
241-
let certificateChain = try certificateChain
242-
.map {
243-
try NIOSSLCertificate(
244-
bytes: $0.serializeAsPEM().derBytes,
245-
format: .der
246-
)
247-
}
248-
.map { NIOSSLCertificateSource.certificate($0) }
249-
let privateKey = NIOSSLPrivateKeySource.privateKey(
250-
try NIOSSLPrivateKey(
251-
bytes: privateKey.serializeAsPEM().derBytes,
252-
format: .der
253-
)
254-
)
255-
256-
var tlsConfiguration: TLSConfiguration = .makeServerConfiguration(
257-
certificateChain: certificateChain,
258-
privateKey: privateKey
259-
)
260-
tlsConfiguration.applicationProtocols = ["h2", "http/1.1"]
261-
262283
try channel.pipeline.syncOperations
263284
.addHandler(
264285
NIOSSLServerHandler(

Sources/HTTPServer/HTTPServerConfiguration.swift

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
public import X509
2+
public import NIOCertificateReloading
3+
import NIOSSL
24

35
/// Configuration settings for the HTTP server.
46
///
@@ -33,34 +35,40 @@ public struct HTTPServerConfiguration: Sendable {
3335
}
3436
}
3537

36-
/// Configuration for TLS/SSL encryption settings.
38+
/// Configuration for transport security settings.
3739
///
3840
/// Provides options for running the server with or without TLS encryption.
39-
/// When using TLS, you must provide a certificate chain and private key.
40-
public struct TLS: Sendable {
41+
/// When using TLS, you must either provide a certificate chain and private key, or a `CertificateReloader`.
42+
public struct TransportSecurity: Sendable {
4143
enum Backing {
42-
case insecure
43-
case certificateChainAndPrivateKey(
44+
case plaintext
45+
case staticTLS(
4446
certificateChain: [Certificate],
4547
privateKey: Certificate.PrivateKey
4648
)
49+
case reloadingTLS(certificateReloader: any CertificateReloader)
50+
4751
}
4852

4953
let backing: Backing
5054

51-
public static let insecure: Self = Self(backing: .insecure)
55+
public static let plaintext: Self = Self(backing: .plaintext)
5256

53-
public static func certificateChainAndPrivateKey(
57+
public static func tls(
5458
certificateChain: [Certificate],
5559
privateKey: Certificate.PrivateKey
5660
) -> Self {
5761
Self(
58-
backing: .certificateChainAndPrivateKey(
62+
backing: .staticTLS(
5963
certificateChain: certificateChain,
6064
privateKey: privateKey
6165
)
6266
)
6367
}
68+
69+
public static func tls(certificateReloader: any CertificateReloader) throws -> Self {
70+
Self(backing: .reloadingTLS(certificateReloader: certificateReloader))
71+
}
6472
}
6573

6674
/// HTTP/2 specific configuration.
@@ -123,7 +131,7 @@ public struct HTTPServerConfiguration: Sendable {
123131
public var bindTarget: BindTarget
124132

125133
/// TLS configuration for the server.
126-
public var tlSConfiguration: TLS
134+
public var transportSecurity: TransportSecurity
127135

128136
/// Backpressure strategy to use in the server.
129137
public var backpressureStrategy: BackPressureStrategy
@@ -140,12 +148,12 @@ public struct HTTPServerConfiguration: Sendable {
140148
/// - http2: A ``HTTP2``. Defaults to ``HTTP2/defaults``.
141149
public init(
142150
bindTarget: BindTarget,
143-
tlsConfiguration: TLS = .insecure,
151+
transportSecurity: TransportSecurity = .plaintext,
144152
backpressureStrategy: BackPressureStrategy = .watermark(low: 2, high: 10),
145153
http2: HTTP2 = .defaults
146154
) {
147155
self.bindTarget = bindTarget
148-
self.tlSConfiguration = tlsConfiguration
156+
self.transportSecurity = transportSecurity
149157
self.backpressureStrategy = backpressureStrategy
150158
self.http2 = http2
151159
}

Tests/HTTPServerTests/HTTPServerTests.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ struct HTTPServerTests {
3838
//}
3939

4040
try await responseConcludingWriter.produceAndConclude { writer in
41+
var writer = writer
4142
try await writer.write([1,2].span)
4243
return nil
4344
}

0 commit comments

Comments
 (0)