Skip to content

Commit e3c41e2

Browse files
authored
Improve generic spelling of handler and server (#30)
1 parent b5cacb9 commit e3c41e2

File tree

6 files changed

+56
-68
lines changed

6 files changed

+56
-68
lines changed

Sources/Example/Example.swift

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,7 @@ struct Example {
2222

2323
// Using the new extension method that doesn't require type hints
2424
let privateKey = P256.Signing.PrivateKey()
25-
let server = NIOHTTPServer<HTTPServerClosureRequestHandler<
26-
HTTPRequestConcludingAsyncReader,
27-
HTTPRequestConcludingAsyncReader.Underlying,
28-
HTTPResponseConcludingAsyncWriter,
29-
HTTPResponseConcludingAsyncWriter.Underlying
30-
>>(
25+
let server = NIOHTTPServer(
3126
logger: logger,
3227
configuration: .init(
3328
bindTarget: .hostAndPort(host: "127.0.0.1", port: 12345),
@@ -50,6 +45,7 @@ struct Example {
5045
)
5146
)
5247
)
48+
5349
try await server.serve { request, requestContext, requestBodyAndTrailers, responseSender in
5450
let writer = try await responseSender.send(HTTPResponse(status: .ok))
5551
try await writer.writeAndConclude(element: "Well, hello!".utf8.span, finalElement: nil)
@@ -60,13 +56,7 @@ struct Example {
6056
// MARK: - Server Extensions
6157

6258
@available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0, *)
63-
extension NIOHTTPServer
64-
where RequestHandler == HTTPServerClosureRequestHandler<
65-
HTTPRequestConcludingAsyncReader,
66-
HTTPRequestConcludingAsyncReader.Underlying,
67-
HTTPResponseConcludingAsyncWriter,
68-
HTTPResponseConcludingAsyncWriter.Underlying
69-
> {
59+
extension NIOHTTPServer {
7060
/// Serve HTTP requests using a middleware chain built with the provided builder
7161
/// This method handles the type inference for HTTP middleware components
7262
func serve(

Sources/HTTPServer/HTTPServerClosureRequestHandler.swift

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,18 @@ public import HTTPTypes
2525
/// ```
2626
@available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0, *)
2727
public struct HTTPServerClosureRequestHandler<
28-
ConcludingRequestReader: ConcludingAsyncReader<RequestReader, HTTPFields?> & ~Copyable,
28+
ConcludingRequestReader: ConcludingAsyncReader<RequestReader, HTTPFields?> & ~Copyable & SendableMetatype,
2929
RequestReader: AsyncReader<Span<UInt8>, any Error> & ~Copyable,
30-
ConcludingResponseWriter: ConcludingAsyncWriter<RequestWriter, HTTPFields?> & ~Copyable,
30+
ConcludingResponseWriter: ConcludingAsyncWriter<RequestWriter, HTTPFields?> & ~Copyable & SendableMetatype,
3131
RequestWriter: AsyncWriter<Span<UInt8>, any Error> & ~Copyable
3232
>: HTTPServerRequestHandler {
3333
/// The underlying closure that handles HTTP requests
3434
private let _handler:
3535
nonisolated(nonsending) @Sendable (
3636
HTTPRequest,
3737
HTTPRequestContext,
38-
consuming sending HTTPRequestConcludingAsyncReader,
39-
consuming sending HTTPResponseSender<HTTPResponseConcludingAsyncWriter>
38+
consuming sending ConcludingRequestReader,
39+
consuming sending HTTPResponseSender<ConcludingResponseWriter>
4040
) async throws -> Void
4141

4242
/// Creates a new closure-based HTTP request handler.
@@ -47,8 +47,8 @@ public struct HTTPServerClosureRequestHandler<
4747
handler: nonisolated(nonsending) @Sendable @escaping (
4848
HTTPRequest,
4949
HTTPRequestContext,
50-
consuming sending HTTPRequestConcludingAsyncReader,
51-
consuming sending HTTPResponseSender<HTTPResponseConcludingAsyncWriter>
50+
consuming sending ConcludingRequestReader,
51+
consuming sending HTTPResponseSender<ConcludingResponseWriter>
5252
) async throws -> Void
5353
) {
5454
self._handler = handler
@@ -66,20 +66,15 @@ public struct HTTPServerClosureRequestHandler<
6666
public func handle(
6767
request: HTTPRequest,
6868
requestContext: HTTPRequestContext,
69-
requestBodyAndTrailers: consuming sending HTTPRequestConcludingAsyncReader,
70-
responseSender: consuming sending HTTPResponseSender<HTTPResponseConcludingAsyncWriter>
69+
requestBodyAndTrailers: consuming sending ConcludingRequestReader,
70+
responseSender: consuming sending HTTPResponseSender<ConcludingResponseWriter>
7171
) async throws {
7272
try await self._handler(request, requestContext, requestBodyAndTrailers, responseSender)
7373
}
7474
}
7575

7676
@available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0, *)
77-
extension HTTPServerProtocol where RequestHandler == HTTPServerClosureRequestHandler<
78-
HTTPRequestConcludingAsyncReader,
79-
HTTPRequestConcludingAsyncReader.Underlying,
80-
HTTPResponseConcludingAsyncWriter,
81-
HTTPResponseConcludingAsyncWriter.Underlying
82-
> {
77+
extension HTTPServerProtocol {
8378
/// Starts an HTTP server with a closure-based request handler.
8479
///
8580
/// This method provides a convenient way to start an HTTP server using a closure to handle incoming requests.
@@ -108,8 +103,8 @@ extension HTTPServerProtocol where RequestHandler == HTTPServerClosureRequestHan
108103
handler: @Sendable @escaping (
109104
_ request: HTTPRequest,
110105
_ requestContext: HTTPRequestContext,
111-
_ requestBodyAndTrailers: consuming sending HTTPRequestConcludingAsyncReader,
112-
_ responseSender: consuming sending HTTPResponseSender<HTTPResponseConcludingAsyncWriter>
106+
_ requestBodyAndTrailers: consuming sending RequestReader,
107+
_ responseSender: consuming sending HTTPResponseSender<ResponseWriter>
113108
) async throws -> Void
114109
) async throws {
115110
try await self.serve(handler: HTTPServerClosureRequestHandler(handler: handler))

Sources/HTTPServer/HTTPServerProtocol.swift

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,21 @@ public import HTTPTypes
33
@available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0, *)
44
/// A generic HTTP server protocol that can handle incoming HTTP requests.
55
public protocol HTTPServerProtocol: Sendable, ~Copyable, ~Escapable {
6-
// TODO: write down in the proposal we can't make the serve method generic over the handler
7-
// because otherwise, closure-based APIs can't be implemented.
6+
/// The ``ConcludingAsyncReader`` to use when reading requests. ``ConcludingAsyncReader/FinalElement``
7+
/// must be an optional `HTTPFields`, and ``ConcludingAsyncReader/Underlying`` must use `Span<UInt8>` as its
8+
/// `ReadElement`.
9+
associatedtype RequestReader: ConcludingAsyncReader & ~Copyable & SendableMetatype
10+
where RequestReader.Underlying.ReadElement == Span<UInt8>,
11+
RequestReader.Underlying.ReadFailure == any Error,
12+
RequestReader.FinalElement == HTTPFields?
813

9-
/// The ``HTTPServerRequestHandler`` to use when handling requests.
10-
associatedtype RequestHandler: HTTPServerRequestHandler
14+
/// The ``ConcludingAsyncWriter`` to use when writing responses. ``ConcludingAsyncWriter/FinalElement``
15+
/// must be an optional `HTTPFields`, and ``ConcludingAsyncWriter/Underlying`` must use `Span<UInt8>` as its
16+
/// `WriteElement`.
17+
associatedtype ResponseWriter: ConcludingAsyncWriter & ~Copyable & SendableMetatype
18+
where ResponseWriter.Underlying.WriteElement == Span<UInt8>,
19+
ResponseWriter.Underlying.WriteFailure == any Error,
20+
ResponseWriter.FinalElement == HTTPFields?
1121

1222
/// Starts an HTTP server with the specified request handler.
1323
///
@@ -18,16 +28,17 @@ public protocol HTTPServerProtocol: Sendable, ~Copyable, ~Escapable {
1828
///
1929
/// - Parameters:
2030
/// - handler: A ``HTTPServerRequestHandler`` implementation that processes incoming HTTP requests. The handler
21-
/// receives each request along with a body reader and ``HTTPResponseSender``.
31+
/// receives each request along with its context, a body and trailers reader, and an ``HTTPResponseSender``.
2232
///
2333
/// ## Example
2434
///
2535
/// ```swift
2636
/// struct EchoHandler: HTTPServerRequestHandler {
2737
/// func handle(
2838
/// request: HTTPRequest,
29-
/// requestBodyAndTrailers: consuming HTTPRequestConcludingAsyncReader,
30-
/// responseSender: consuming HTTPResponseSender<HTTPResponseConcludingAsyncWriter>
39+
/// requestContext: HTTPRequestContext,
40+
/// requestBodyAndTrailers: consuming sending HTTPRequestConcludingAsyncReader,
41+
/// responseSender: consuming sending HTTPResponseSender<HTTPResponseConcludingAsyncWriter>
3142
/// ) async throws {
3243
/// let response = HTTPResponse(status: .ok)
3344
/// let writer = try await responseSender.send(response)
@@ -39,5 +50,5 @@ public protocol HTTPServerProtocol: Sendable, ~Copyable, ~Escapable {
3950
///
4051
/// try await server.serve(handler: EchoHandler())
4152
/// ```
42-
func serve(handler: RequestHandler) async throws
53+
func serve(handler: some HTTPServerRequestHandler<RequestReader, ResponseWriter>) async throws
4354
}

Sources/HTTPServer/HTTPServerRequestHandler.swift

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ public import HTTPTypes
33
/// A protocol that defines the contract for handling HTTP server requests.
44
///
55
/// ``HTTPServerRequestHandler`` provides a structured way to process incoming HTTP requests and generate appropriate responses.
6-
/// Conforming types implement the ``handle(request:requestBodyAndTrailers:responseSender:)`` method,
6+
/// Conforming types implement the ``handle(request:requestContext:requestBodyAndTrailers:responseSender:)`` method,
77
/// which is called by the HTTP server for each incoming request. The handler is responsible for:
88
///
99
/// - Processing the request headers.
10-
/// - Reading the request body data using the provided ``HTTPRequestConcludingAsyncReader``
10+
/// - Reading the request body data using the provided `RequestReader`
1111
/// - Generating and sending an appropriate response using the response callback
1212
///
1313
/// This protocol fully supports bi-directional streaming HTTP request handling including the optional request and response trailers.
@@ -57,34 +57,30 @@ public import HTTPTypes
5757
/// }
5858
/// ```
5959
@available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0, *)
60-
public protocol HTTPServerRequestHandler: Sendable {
60+
public protocol HTTPServerRequestHandler<RequestReader, ResponseWriter>: Sendable {
6161
/// The ``ConcludingAsyncReader`` to use when reading requests. ``ConcludingAsyncReader/FinalElement``
6262
/// must be an optional `HTTPFields`, and ``ConcludingAsyncReader/Underlying`` must use `Span<UInt8>` as its
6363
/// `ReadElement`.
64-
associatedtype ConcludingRequestReader: ConcludingAsyncReader<RequestReader, HTTPFields?> & ~Copyable
64+
associatedtype RequestReader: ConcludingAsyncReader & ~Copyable & SendableMetatype
65+
where RequestReader.Underlying.ReadElement == Span<UInt8>,
66+
RequestReader.FinalElement == HTTPFields?
6567

66-
/// The underlying ``AsyncReader`` for ``ConcludingRequestReader``. Its ``AsyncReader/ReadElement`` must
67-
/// be `Span<UInt8>`.
68-
associatedtype RequestReader: AsyncReader<Span<UInt8>, any Error> & ~Copyable
69-
70-
/// The ``ConcludingAsyncWriter`` to use when reading requests. ``ConcludingAsyncWriter/FinalElement``
68+
/// The ``ConcludingAsyncWriter`` to use when writing responses. ``ConcludingAsyncWriter/FinalElement``
7169
/// must be an optional `HTTPFields`, and ``ConcludingAsyncWriter/Underlying`` must use `Span<UInt8>` as its
7270
/// `WriteElement`.
73-
associatedtype ConcludingResponseWriter: ConcludingAsyncWriter<RequestWriter, HTTPFields?> & ~Copyable
74-
75-
/// The underlying ``AsyncWriter`` for ``ConcludingResponseWriter``. Its ``AsyncWriter/WriteElement`` must
76-
/// be `Span<UInt8>`.
77-
associatedtype RequestWriter: AsyncWriter<Span<UInt8>, any Error> & ~Copyable
71+
associatedtype ResponseWriter: ConcludingAsyncWriter & ~Copyable & SendableMetatype
72+
where ResponseWriter.Underlying.WriteElement == Span<UInt8>,
73+
ResponseWriter.FinalElement == HTTPFields?
7874

7975
/// Handles an incoming HTTP request and generates a response.
8076
///
8177
/// This method is called by the HTTP server for each incoming client request. Implementations should:
8278
/// 1. Examine the request headers in the `request` parameter
83-
/// 2. Read the request body data from the ``RequestConcludingAsyncReader`` as needed
79+
/// 2. Read the request body data from the `RequestReader` as needed
8480
/// 3. Process the request and prepare a response
8581
/// 4. Optionally call ``HTTPResponseSender/sendInformational(_:)`` as needed
8682
/// 4. Call the ``HTTPResponseSender/send(_:)`` with an appropriate HTTP response
87-
/// 5. Write the response body data to the returned ``HTTPResponseConcludingAsyncWriter``
83+
/// 5. Write the response body data to the returned `ResponseWriter`
8884
///
8985
/// - Parameters:
9086
/// - request: The HTTP request headers and metadata.
@@ -99,7 +95,7 @@ public protocol HTTPServerRequestHandler: Sendable {
9995
func handle(
10096
request: HTTPRequest,
10197
requestContext: HTTPRequestContext,
102-
requestBodyAndTrailers: consuming sending ConcludingRequestReader,
103-
responseSender: consuming sending HTTPResponseSender<ConcludingResponseWriter>
98+
requestBodyAndTrailers: consuming sending RequestReader,
99+
responseSender: consuming sending HTTPResponseSender<ResponseWriter>
104100
) async throws
105101
}

Sources/HTTPServer/NIOHTTPServer.swift

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,10 @@ import Synchronization
6262
/// }
6363
/// ```
6464
@available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0, *)
65-
public struct NIOHTTPServer<RequestHandler: HTTPServerRequestHandler>: HTTPServerProtocol
66-
where RequestHandler.ConcludingRequestReader == HTTPRequestConcludingAsyncReader,
67-
RequestHandler.ConcludingResponseWriter == HTTPResponseConcludingAsyncWriter {
65+
public struct NIOHTTPServer: HTTPServerProtocol {
66+
public typealias RequestReader = HTTPRequestConcludingAsyncReader
67+
public typealias ResponseWriter = HTTPResponseConcludingAsyncWriter
68+
6869
private let logger: Logger
6970
private let configuration: HTTPServerConfiguration
7071

@@ -118,7 +119,7 @@ where RequestHandler.ConcludingRequestReader == HTTPRequestConcludingAsyncReader
118119
/// handler: EchoHandler()
119120
/// )
120121
/// ```
121-
public func serve(handler: RequestHandler) async throws {
122+
public func serve(handler: some HTTPServerRequestHandler<RequestReader, ResponseWriter>) async throws {
122123
let asyncChannelConfiguration: NIOAsyncChannel<HTTPRequestPart, HTTPResponsePart>.Configuration
123124
switch self.configuration.backpressureStrategy.backing {
124125
case .watermark(let low, let high):
@@ -274,7 +275,7 @@ where RequestHandler.ConcludingRequestReader == HTTPRequestConcludingAsyncReader
274275

275276
private func serveInsecureHTTP1_1(
276277
bindTarget: HTTPServerConfiguration.BindTarget,
277-
handler: RequestHandler,
278+
handler: some HTTPServerRequestHandler<RequestReader, ResponseWriter>,
278279
asyncChannelConfiguration: NIOAsyncChannel<HTTPRequestPart, HTTPResponsePart>.Configuration
279280
) async throws {
280281
switch bindTarget.backing {
@@ -309,7 +310,7 @@ where RequestHandler.ConcludingRequestReader == HTTPRequestConcludingAsyncReader
309310
private func serveSecureUpgrade(
310311
bindTarget: HTTPServerConfiguration.BindTarget,
311312
tlsConfiguration: TLSConfiguration,
312-
handler: RequestHandler,
313+
handler: some HTTPServerRequestHandler<RequestReader, ResponseWriter>,
313314
asyncChannelConfiguration: NIOAsyncChannel<HTTPRequestPart, HTTPResponsePart>.Configuration,
314315
http2Configuration: NIOHTTP2Handler.Configuration
315316
) async throws {
@@ -394,7 +395,7 @@ where RequestHandler.ConcludingRequestReader == HTTPRequestConcludingAsyncReader
394395

395396
private func handleRequestChannel(
396397
channel: NIOAsyncChannel<HTTPRequestPart, HTTPResponsePart>,
397-
handler: RequestHandler
398+
handler: some HTTPServerRequestHandler<RequestReader, ResponseWriter>
398399
) async throws {
399400
do {
400401
try await channel

Tests/HTTPServerTests/HTTPServerTests.swift

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,7 @@ struct HTTPServerTests {
88
@Test
99
@available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0, *)
1010
func testConsumingServe() async throws {
11-
let server = NIOHTTPServer<HTTPServerClosureRequestHandler<
12-
HTTPRequestConcludingAsyncReader,
13-
HTTPRequestConcludingAsyncReader.RequestBodyAsyncReader,
14-
HTTPResponseConcludingAsyncWriter,
15-
HTTPResponseConcludingAsyncWriter.ResponseBodyAsyncWriter
16-
>>(
11+
let server = NIOHTTPServer(
1712
logger: Logger(label: "Test"),
1813
configuration: .init(bindTarget: .hostAndPort(host: "127.0.0.1", port: 0))
1914
)

0 commit comments

Comments
 (0)