Repository navigation
First release since 1.0.0 (2023-07-01). It adds the request/response APIs the README had been promising, and fixes a long list of correctness and lifecycle bugs that made the server unreliable under restarts and load.
Added
- iOS background support:
automaticallySuspendInBackgroundstops the server in the background and restarts it in the foreground, with a background task held while a connection is served from the background - Example iOS app under
Example/ - Path parameters:
server.get("/users/:id")with values onreq.params; exact routes win over parameterized ones (#31) - Query helpers
req.queryValue(_:)/req.queryValues(_:), duplicates preserved (#27) res.send(_:contentType:)for binary bodies andres.sendJSON(_:)forEncodablevalues (#28)- SSE:
id:field,dispatchRetry(_:), multilinedata:(#34) HTTPServer.readTimeout— opt-in per-connection read timeout (#26)HTTPServer.maxRequestBodySize— body cap, default 10 MiB (#15)- CI: macOS build + tests, and iOS/tvOS cross-builds that also compile the test target (#10, #17)
Fixed
- macOS build broken by an unguarded
UIApplicationreference (#1) - Chunked responses used
Charactercounts instead of UTF-8 bytes, corrupting multi-byte payloads (#14) - Status line always said
OK; the reason phrase now follows the status code (#11) - Malformed/oversized requests were dropped silently; now
400,413,431,501(#15) - Chunked request bodies were ignored and misparsed as a new request; now rejected explicitly (#15)
- Percent-encoded query values were double-encoded (#27)
- A restart could close the new listener's descriptor (duplicate
closeon a recycled fd) (#2) - Writing to a closed socket could write into an unrelated connection; closed sockets now reject writes (#19)
_stop()iterated the connection set outside its queue — random bind failures, hung requests, and a memory-corruption crash (#30)- A stale accept loop could steal connections from a newer listener;
stop()now waits for the loop to exit (#33) - Blocking
accept()on the Swift concurrency pool starved unrelated work; accept now has its own thread (#16) - The built-in router was re-registered on every
start(), running handlers twice after a restart (#16) - iOS background tasks never started, ended the wrong identifier, and leaked observers (#17)
HTTPMiddlewarewas not actually subclassable by consumers:handleis nowopen,initispublic(#36)
Behaviour changes when upgrading
res.send(_:)setsContent-Type: text/plain; charset=utf-8when you have not set one, and always sendsContent-Length— responses are no longer close-delimited with a sniffed type- Requests outside the parser limits are rejected rather than tolerated
- The reason phrase follows
statusCodeunless you assignres.reasonPhrase - Each server owns a dedicated accept thread; connection I/O is still blocking
Testing
- 8 → 42 tests, including a POSIX raw HTTP client that asserts status lines, headers, and exact chunked/SSE framing, plus a consumer-style public-API suite
- The intermittent CI failure that hid the descriptor-reuse bug is gone (#13, #40, #41)
- Full CHANGELOG: https://github.com/isaced/Aoxiang/blob/main/CHANGELOG.md