-
Notifications
You must be signed in to change notification settings - Fork 8
Add connection lifecycle handling #92
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 all commits
f3eedc4
9e80f30
413211a
f4c7dbe
74442f7
94995d8
67c7bcd
874d77c
ed2fbc4
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,97 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // This source file is part of the Swift HTTP Server open source project | ||
| // | ||
| // Copyright (c) 2026 Apple Inc. and the Swift HTTP Server project authors | ||
| // Licensed under Apache License v2.0 | ||
| // | ||
| // See LICENSE.txt for license information | ||
| // See CONTRIBUTORS.txt for the list of Swift HTTP Server project authors | ||
| // | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| import BasicContainers | ||
| import Crypto | ||
| import ExampleSupport | ||
| import Foundation | ||
| import Instrumentation | ||
| import Logging | ||
| import NIOHTTPServer | ||
| import X509 | ||
|
|
||
| @main | ||
| @available(anyAppleOS 26.0, *) | ||
| struct ConnectionHandlerExample { | ||
| static func main() async throws { | ||
| try await serve() | ||
| } | ||
|
|
||
| @concurrent | ||
| static func serve() async throws { | ||
| InstrumentationSystem.bootstrap(LogTracer()) | ||
|
|
||
| var rootLogger = Logger(label: "ConnectionHandlerExample") | ||
| rootLogger.logLevel = .trace | ||
| try await withLogger(rootLogger) { rootLogger in | ||
| let privateKey = P256.Signing.PrivateKey() | ||
| let server = NIOHTTPServer( | ||
| logger: rootLogger, | ||
| configuration: try .init( | ||
| bindTarget: .hostAndPort(host: "127.0.0.1", port: 12346), | ||
| supportedHTTPVersions: [.http1_1, .http2(config: .init())], | ||
| transportSecurity: .tls( | ||
| credentials: .inMemory( | ||
| certificateChain: [ | ||
| try Certificate( | ||
| version: .v3, | ||
| serialNumber: .init(bytes: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]), | ||
| publicKey: .init(privateKey.publicKey), | ||
| notValidBefore: Date.now.addingTimeInterval(-60), | ||
| notValidAfter: Date.now.addingTimeInterval(60 * 60), | ||
| issuer: DistinguishedName(), | ||
| subject: DistinguishedName(), | ||
| signatureAlgorithm: .ecdsaWithSHA256, | ||
| extensions: .init(), | ||
| issuerPrivateKey: Certificate.PrivateKey(privateKey) | ||
| ) | ||
| ], | ||
| privateKey: Certificate.PrivateKey(privateKey) | ||
| ) | ||
| ) | ||
| ) | ||
| ) | ||
|
|
||
| // A connection handler runs once per accepted connection and drives the | ||
| // request loop via `connection.handleRequests`. Both the connection | ||
| // handler and the request handler are inline closures. | ||
| try await server.serve { connection, context in | ||
| var connection = Optional(connection) | ||
| try await withLogger(mergingMetadata: [ | ||
| "peer": .string(context.remoteAddress.map { "\($0)" } ?? "unknown"), | ||
| "http": .string(context.httpVersion.rawValue), | ||
| ]) { connectionLogger in | ||
| connectionLogger.info("connection accepted") | ||
| defer { connectionLogger.info("connection closed") } | ||
|
|
||
| await connection.take()!.handleRequests { request, _, _, responseSender in | ||
| var responseSender = Optional(responseSender) | ||
| try await withLogger(mergingMetadata: [ | ||
| "path": .string(request.path ?? "") | ||
| ]) { requestLogger in | ||
| requestLogger.info("request received") | ||
| defer { requestLogger.info("request completed") } | ||
|
|
||
| var body = UniqueArray<UInt8>(copying: "Well, hello!".utf8) | ||
| try await responseSender.take()!.sendAndFinish( | ||
| HTTPResponse(status: .ok), | ||
| buffer: &body | ||
| ) | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } |
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // This source file is part of the Swift HTTP Server open source project | ||
| // | ||
| // Copyright (c) 2026 Apple Inc. and the Swift HTTP Server project authors | ||
| // Licensed under Apache License v2.0 | ||
| // | ||
| // See LICENSE.txt for license information | ||
| // See CONTRIBUTORS.txt for the list of Swift HTTP Server project authors | ||
| // | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| public import HTTPAPIs | ||
| public import X509 | ||
|
|
||
| @available(anyAppleOS 26.0, *) | ||
| extension HTTPServerCapability { | ||
| /// A request-context capability exposing connection-scoped peer and local addresses. | ||
| /// | ||
| /// Servers whose request context conforms to this capability surface the | ||
| /// peer's address and the local address the connection is bound to. Both | ||
| /// are reported best-effort: implementations may return `nil` when the | ||
| /// underlying transport cannot report an address. | ||
| public protocol ConnectionInfo: RequestContext { | ||
| /// The peer's address, when known. | ||
| var remoteAddress: NIOHTTPServer.SocketAddress? { get } | ||
|
|
||
| /// The local address the connection is bound to, when known. | ||
| var localAddress: NIOHTTPServer.SocketAddress? { get } | ||
| } | ||
|
|
||
| /// A request-context capability exposing the validated peer certificate chain. | ||
| /// | ||
| /// Servers whose request context conforms to this capability surface the | ||
| /// peer's mTLS-validated certificate chain (when applicable). Implementations | ||
| /// return `nil` if mTLS isn't configured, or if no validated chain was | ||
| /// derived. | ||
| public protocol PeerCertificate: RequestContext { | ||
|
FranzBusch marked this conversation as resolved.
|
||
| /// The peer's validated certificate chain, when available. | ||
| var peerCertificateChain: X509.ValidatedCertificateChain? { get async throws } | ||
|
Contributor
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. Why is this
Collaborator
Author
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 was already its signature - I'm just introducing this capability. Look at
Contributor
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 is highly unfortunate since it should be synchronously present. Let's file an issue to track this since in the
Collaborator
Author
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. Why should it be synchronously present and not throw? This is currently async because we're are calling We can't get around the
Contributor
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. Because at the point where we hand the connection to the user we already have the TLS connection established. So we should be able to get the peer cert before we hand the connection to the user
Collaborator
Author
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. Sure. We can probably get it via sync operations. I'll create a follow up issue.
Collaborator
Author
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. |
||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.