-
Notifications
You must be signed in to change notification settings - Fork 14
Add request context capability mechanism to HTTPServer #166
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
base: main
Are you sure you want to change the base?
Changes from all commits
9d24d23
cdbb0c6
8d83a60
73c0e07
f5744a1
6952568
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,66 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // This source file is part of the Swift HTTP API Proposal open source project | ||
| // | ||
| // Copyright (c) 2026 Apple Inc. and the Swift HTTP API Proposal project authors | ||
| // Licensed under Apache License v2.0 | ||
| // | ||
| // See LICENSE.txt for license information | ||
| // | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| /// The namespace for all protocols defining HTTP server capabilities. | ||
| /// | ||
| /// `HTTPServerCapability` groups protocols that represent optional features a server can provide. | ||
| /// Each capability protocol defines a set of properties or methods that a server's request context | ||
| /// must implement. Libraries and middleware can constrain their generic parameters to require | ||
| /// specific capabilities, enabling compile-time verification that a server supports the needed features. | ||
| /// | ||
| /// ## Defining a capability | ||
| /// | ||
| /// ```swift | ||
| /// extension HTTPServerCapability { | ||
| /// protocol ConnectionInfo: RequestContext { | ||
| /// var remoteAddress: String? { get } | ||
| /// var localAddress: String? { get } | ||
| /// } | ||
| /// } | ||
| /// ``` | ||
| /// | ||
| /// ## Using a capability in middleware or handlers | ||
| /// | ||
| /// ```swift | ||
| /// func logConnection<S: HTTPServer>(server: S) async throws | ||
| /// where S.RequestContext: HTTPServerCapability.ConnectionInfo { | ||
| /// try await server.serve { request, context, body, sender in | ||
| /// print("Request from: \(context.remoteAddress ?? "unknown")") | ||
| /// // ... | ||
| /// } | ||
| /// } | ||
| /// ``` | ||
| @available(anyAppleOS 26.0, *) | ||
| public enum HTTPServerCapability { | ||
| /// A protocol that all server request contexts must conform to. | ||
| /// | ||
| /// `RequestContext` is the base protocol for request contexts provided by HTTP servers. | ||
| /// Servers create a context for each incoming request and pass it to the request handler. | ||
| /// The context carries metadata about the request that isn't part of the HTTP message itself, | ||
| /// such as connection information, routing state, or server-specific data. | ||
| /// | ||
| /// Child protocols (capabilities) extend `RequestContext` with additional properties that | ||
| /// a subset of servers provide, allowing libraries to depend on specific capabilities | ||
| /// without coupling to a concrete server implementation. | ||
| /// | ||
| /// ## Implementing a custom context | ||
| /// | ||
| /// ```swift | ||
| /// struct MyServerContext: HTTPServerCapability.ConnectionInfo { | ||
| /// var remoteAddress: String? | ||
| /// var localAddress: String? | ||
| /// } | ||
| /// ``` | ||
| public protocol RequestContext: ~Copyable, ~Escapable { | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -26,14 +26,15 @@ | |
| /// | ||
| /// ```swift | ||
| /// struct EchoHandler< | ||
| /// Context: HTTPServerCapability.RequestContext, | ||
| /// ConcludingRequestReader: ConcludingAsyncReader<RequestReader, HTTPFields?> & ~Copyable, | ||
| /// RequestReader: AsyncReader<UInt8, any Error> & ~Copyable, | ||
| /// ConcludingResponseWriter: ConcludingAsyncWriter<ResponseWriter, HTTPFields?> & ~Copyable, | ||
| /// ResponseWriter: AsyncWriter<UInt8, any Error> & ~Copyable | ||
| /// >: HTTPServerRequestHandler { | ||
| /// func handle( | ||
| /// request: HTTPRequest, | ||
| /// requestContext: HTTPRequestContext, | ||
| /// requestContext: Context, | ||
| /// requestBodyAndTrailers: consuming sending ConcludingRequestReader, | ||
| /// responseSender: consuming sending HTTPResponseSender<ConcludingResponseWriter> | ||
| /// ) async throws { | ||
|
|
@@ -55,7 +56,10 @@ | |
| /// } | ||
| /// ``` | ||
| @available(anyAppleOS 26.0, *) | ||
| public protocol HTTPServerRequestHandler<RequestReader, ResponseWriter>: Sendable { | ||
| public protocol HTTPServerRequestHandler<RequestContext, RequestReader, ResponseWriter>: Sendable { | ||
| /// The type of the request context provided by the server. | ||
| associatedtype RequestContext: HTTPServerCapability.RequestContext, ~Copyable | ||
|
|
||
| /// The type used to read request body data and trailers. | ||
| associatedtype RequestReader: ConcludingAsyncReader, ~Copyable | ||
| where RequestReader.Underlying: ~Copyable, RequestReader.Underlying.ReadElement == UInt8, RequestReader.FinalElement == HTTPFields? | ||
|
|
@@ -76,15 +80,15 @@ public protocol HTTPServerRequestHandler<RequestReader, ResponseWriter>: Sendabl | |
| /// | ||
| /// - Parameters: | ||
| /// - request: The HTTP request headers and metadata. | ||
| /// - requestContext: A ``HTTPRequestContext`` carrying additional request information. | ||
| /// - requestContext: A context carrying additional request information provided by the server. | ||
| /// - requestBodyAndTrailers: A reader for accessing the request body data and trailing headers. | ||
| /// - responseSender: An ``HTTPResponseSender`` that accepts an HTTP response and returns a writer for the | ||
| /// response body. The returned writer allows for incremental writing of the response body and supports trailers. | ||
| /// | ||
| /// - Throws: Any error encountered during request processing or response generation. | ||
| func handle( | ||
| request: HTTPRequest, | ||
| requestContext: HTTPRequestContext, | ||
| requestContext: consuming RequestContext, | ||
|
Collaborator
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. Is consuming the right ownership model? Could the same
Contributor
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. If it's non copyable, how would it be passed multiple times?
Collaborator
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. Ah I guess |
||
| requestBodyAndTrailers: consuming sending RequestReader, | ||
| responseSender: consuming sending HTTPResponseSender<ResponseWriter> | ||
| ) async throws | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,9 +11,9 @@ | |
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| /// A context object that carries additional information about an HTTP request. | ||
| /// | ||
| /// `HTTPRequestContext` provides a way to pass metadata through the HTTP request pipeline. | ||
| public struct HTTPRequestContext: Sendable { | ||
| public import HTTPAPIs | ||
|
|
||
| @available(macOS 26.2, iOS 26.2, watchOS 26.2, tvOS 26.2, visionOS 26.2, *) | ||
| public struct HTTPRequestContext: HTTPServerCapability.RequestContext { | ||
|
Collaborator
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 type should be moved to swift-http-server?
Contributor
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. Moved to HTTPServerForTesting
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. There's a PR for depending on the NIOHTTPServer as a submodule.
Collaborator
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. Yeah probably will land that PR when everything settles to reduce the pain of interlocked revisions |
||
| public init() {} | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add more docs explaining what a capability is?