Skip to content

Add APIs for reading/writing HTTP Datagrams - #106

Open
aryan-25 wants to merge 2 commits into
swift-server:mainfrom
aryan-25:unreliable-datagrams
Open

Add APIs for reading/writing HTTP Datagrams#106
aryan-25 wants to merge 2 commits into
swift-server:mainfrom
aryan-25:unreliable-datagrams

Conversation

@aryan-25

Copy link
Copy Markdown
Collaborator

Motivation

Users may wish to send/receive HTTP Datagrams (RFC 9297) using NIOHTTPServer. This PR introduces reader and writer APIs for unreliable datagrams.

Note that the underlying support is yet to be provided by swift-nio-http3. The read/write source is not wired up, and we currently throw an error when attempting to read or write to/from the unreliable datagram stream.

Modifications

  • Added an UnstableHTTPDatagrams trait, disabled by default. All new APIs are behind that trait (note that the HTTP3 trait must also be enabled as unreliable datagrams are currently only supported over QUIC).

  • Added a withDatagramReader method on NIOHTTPServer.Reader and a withDatagramWriter method on NIOHTTPServer.ResponseSender.Writer. Both methods are consuming and take a closure that receive the reliable request/response body reader/writer and optionally the unreliable datagram reader/writer as arguments.

    • In both methods, the readers/writers are passed as consuming sending, so callers can run the readers/writers separately in concurrent tasks.
  • Added ConnectUDPExample.swift to show the proposed shape end to end.

Result

APIs for sending/receiving unreliable HTTP Datagrams added.

### Motivation

Users may wish to send/receive HTTP Datagrams ([RFC 9297](https://datatracker.ietf.org/doc/html/rfc9297)) using NIOHTTPServer. This PR introduces reader and writer APIs for unreliable datagrams.

Note that the underlying support is yet to be provided by `swift-nio-http3`. The read/write source is not wired up, and we currently throw an error when attempting to read or write to/from the unreliable datagram stream.

### Modifications

- Added an `UnstableHTTPDatagrams` trait, disabled by default. All new APIs are behind that trait (note that the `HTTP3` trait must also be enabled as unreliable datagrams are currently only supported over QUIC).

- Added a `withDatagramReader` method on `NIOHTTPServer.Reader` and a `withDatagramWriter` method on `NIOHTTPServer.ResponseSender.Writer`. Both methods are `consuming` and take a closure that receive the reliable request/response body reader/writer and optionally the unreliable datagram reader/writer as arguments.
  - In both methods, the readers/writers are passed as `consuming sending`, so callers can run the readers/writers separately in concurrent tasks.

- Added `ConnectUDPExample.swift` to show the proposed shape end to end.

### Result

APIs for sending/receiving unreliable HTTP Datagrams added.
@aryan-25
aryan-25 requested review from ehaydenr and gjcairo July 31, 2026 09:25
@aryan-25 aryan-25 added the 🆕 semver/minor Adds new public API. label Jul 31, 2026
#if HTTP3 && UnstableHTTPDatagrams
/// The unreliable datagram reader, present when the underlying transport is capable of reading/writing
/// unreliable datagrams.
private var datagramReader: Disconnected<NIOHTTPServer.DatagramReader>?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need Disconnected?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, without this, the datagramReader can't be vended out as sending in withDatagramReader.

public consuming func withDatagramWriter(
_ body: (consuming sending Self, consuming sending NIOHTTPServer.DatagramWriter?) async throws -> Void
) async throws {
let streamFinish = NIOHTTPServer.StreamFinish(writer: self.writer, state: self.writerState)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this type is a bit unnecessary and adds some indirection which makes things harder to follow, IMO. I'd just finish both streams inline in this func, like we do elsewhere.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We unfortunately need this indirection because we pass the stream writer (and the datagram writer) as consuming sending to the body closure, which means we can't call finish on either after the body closure.

Comment on lines +244 to +245
readerState: .init(iterator: stream.makeAsyncIterator()),
datagramReader: .init(value: .init())

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All these .inits are a bit confusing to understand - can we spell out the names?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in commit 5986cd4.

@aryan-25
aryan-25 requested a review from gjcairo July 31, 2026 12:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🆕 semver/minor Adds new public API.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants