Commits
With EPOLLONESHOT, re-arming the fd while still holding processing loses a read
that arrives in the window between the re-arm and the release: the event loop
sees the connection as busy and the one-shot arming is consumed, so the read is
dropped and the connection hangs / leaks (CLOSE_WAIT). Release processing before
re-arming instead; no extra state needed.
https://github.com/karlseguin/http.zig/issues/207
posix:通过SetHandleInformation在Windows上实现CLOEXEC
zig17: fix changes in builtin and std.meta
Replace two TODOs in setSockFlags:
- CLOEXEC: Windows socket handles created by accept() did not have
close-on-exec semantics. Fixed by calling SetHandleInformation with
HANDLE_FLAG_INHERIT=0 — the Windows equivalent of FD_CLOEXEC.
(socket() already handled this via WSA_FLAG_NO_HANDLE_INHERIT.)
- NONBLOCK: replace the generic WSA error catch-all with specific
handling for WSAEINVAL (returns error), WSAEFAULT / WSAEOPNOTSUPP /
WSAEINPROGRESS (all unreachable for FIONBIO).
Also add constants HANDLE_FLAG_INHERIT, HANDLE_FLAG_PROTECT_FROM_CLOSE
and a setNoInherit() helper to windows.zig.
zig17: fix build.zig & SafeAllocator.init()
Refactor indexOfIgnoreCase() to findIgnoreCase()
Following ziglang changes in https://codeberg.org/ziglang/zig/commit/4590712804a0392d838a839572c722cf8640960c
https://github.com/karlseguin/http.zig/pull/197
https://github.com/karlseguin/http.zig/pull/197
tsan: add buffer pool race test reproduction
use @splat() in place of already removed ** operator; std.meta.Int() -> @Int()
Reject ambiguous Content-Length and Host headers
https://github.com/karlseguin/http.zig/issues/170
Fixes: https://github.com/karlseguin/http.zig/issues/193
Zig 0.16's networking is incomplete. Io.Threaded treats AGAIN as a "programmer
bug". Fair because using only std.Io.net you can't put a socket in nonblocking
mode, but unfair because there's nothing inherently wrong about mixing
nonblocking sockets with threads - zig's Io.Threaded just doesn't support it
[and they decide to (a) panic and (b) blame the user).
As with most things with Zig 0.16, the solution is to avoid std.Io.net as much
as possible. Most of httpz was already using posix.zig, but I left this one
case in
Cannot resize from fixed allocator to arena/fallback. Fixes
https://github.com/karlseguin/http.zig/issues/192
(Also, add format to Config.Address)
Websocket is currently disabled. It'll come very soon.
The heading `Configuration` was used twice, the TOC misleadingly linked
to the first one which was the subheading for Configuration of the
Router.
https://github.com/karlseguin/http.zig/pull/188
https://github.com/karlseguin/http.zig/issues/187
https://github.com/karlseguin/http.zig/issues/186
Also add .any as an alternative to the first example in the readme
See: https://github.com/karlseguin/http.zig/pull/185
he reason I didn't think it was possible is that, once we upgrade to websockets, we switch the to ONESHOT / EV_DISPATCH. And I _thought_ this would be enough to ensure that we only ever process 1 message at a time. The re-arming only happens as the last step the worker thread does..so while 2 threads could technically be running on the same connection, one of those threads would be in the process of shutting down and 100% NOT be doing anything with the connection. And that all works.
EXCEPT for a tiny window during our initial switch to EV_DISPATCH. Say a connection is upgraded and also sends 2 websocket messages. While a single poll (`kevent`) won't return 2 distinct events for that 1 connection, they could get split into 2 separate polls. We end up with:
```
wait#1
[signal, fd#1.recv#1]
wait#2
[fd#1.recv#2]
```
and this happens because as we're processing the wait#1 and before we switch to EV_DISPATCH, `fd#1.recv#2` has already been received and queued. Even if you delete and re-add, it seems like that only deals with future events, it doesn't clear our any already/pending events. So we need to mimic what we alreayd do for HTTP connections, which is as a guard clause for a connection that's already being processed. But we still keep the ONESHOT / DISPATCH because it does work beyond the initial window, and that just helps prevent wakeups that we'll just have to reject via the guard.
Fix shutdown on 32-bit Linux: eventfd write must be 8 bytes
* Introduce Config.AddressConfig.
* Let callers provide a pre-parsed Address for the listener
* Express the default listener address in a simpler way
* update readme to show the new way of setting listener address
Add errdefer to unlock mutex when listen() fails before reaching
the success paths. Without this, errors during socket setup, binding,
or worker initialization leave the mutex locked, causing subsequent
calls to listen() or listenInNewThread() to deadlock.
This bug is particularly severe with listenInNewThread() because the
main thread waits on a condition variable, and when the spawned thread
exits with the mutex locked, the condition wakes the main thread which
then hangs forever trying to reacquire the mutex.
Adds test that verifies mutex is properly released by calling listen()
twice when bind fails. Without the fix, the second call deadlocks.
https://github.com/karlseguin/http.zig/issues/178
https://github.com/karlseguin/http.zig/pull/173
* Enhance CORS to include credentials handling and allow multi cors origin values
Added support for credentials in CORS configuration.
Allow multi cors origin values
* Enhance CORS middleware: support wildcard & multiple origins, credentials; add tests
- Add Origin union and parseOrigin() to middleware/Cors.zig; init now takes MiddlewareConfig and uses arena for origin parsing
- Support wildcard ("*") and comma-separated origin lists; set Access-Control-Allow-Origin to "*" for wildcard, or echo allowed origin for lists
- Preserve and emit Access-Control-Allow-Credentials, Allow-Methods, Allow-Headers, and Max-Age for preflight requests
- Update tests: increase test thread array size, spawn dedicated CORS servers (wildcard, single, multiple), add comprehensive tests for GET and OPTIONS preflight flows and negative cases
- Ensure Origin header is sent in relevant test requests
* Fix parsing of multiple CORS origins by initializing count to 0
* tests: stop and deinit CORS test servers in tests:afterAll
* docs(readme): cleanup formatting, fix typos, and expand CORS documentation
- Normalize whitespace, punctuation and list formatting across README
- Fix typos and improve wording for clarity
- Expand CORS middleware section with examples and behavior:
- document wildcard, single and multiple origins
- add credentials option and note about wildcard+credentials restriction
- show preflight headers and 204 behavior
- Update middleware path reference to httpz.middleware.Cors
- Minor formatting tweaks in many sections (headers, code blocks, comments)
https://github.com/karlseguin/http.zig/issues/156
With EPOLLONESHOT, re-arming the fd while still holding processing loses a read
that arrives in the window between the re-arm and the release: the event loop
sees the connection as busy and the one-shot arming is consumed, so the read is
dropped and the connection hangs / leaks (CLOSE_WAIT). Release processing before
re-arming instead; no extra state needed.
Replace two TODOs in setSockFlags:
- CLOEXEC: Windows socket handles created by accept() did not have
close-on-exec semantics. Fixed by calling SetHandleInformation with
HANDLE_FLAG_INHERIT=0 — the Windows equivalent of FD_CLOEXEC.
(socket() already handled this via WSA_FLAG_NO_HANDLE_INHERIT.)
- NONBLOCK: replace the generic WSA error catch-all with specific
handling for WSAEINVAL (returns error), WSAEFAULT / WSAEOPNOTSUPP /
WSAEINPROGRESS (all unreachable for FIONBIO).
Also add constants HANDLE_FLAG_INHERIT, HANDLE_FLAG_PROTECT_FROM_CLOSE
and a setNoInherit() helper to windows.zig.
Fixes: https://github.com/karlseguin/http.zig/issues/193
Zig 0.16's networking is incomplete. Io.Threaded treats AGAIN as a "programmer
bug". Fair because using only std.Io.net you can't put a socket in nonblocking
mode, but unfair because there's nothing inherently wrong about mixing
nonblocking sockets with threads - zig's Io.Threaded just doesn't support it
[and they decide to (a) panic and (b) blame the user).
As with most things with Zig 0.16, the solution is to avoid std.Io.net as much
as possible. Most of httpz was already using posix.zig, but I left this one
case in
See: https://github.com/karlseguin/http.zig/pull/185
he reason I didn't think it was possible is that, once we upgrade to websockets, we switch the to ONESHOT / EV_DISPATCH. And I _thought_ this would be enough to ensure that we only ever process 1 message at a time. The re-arming only happens as the last step the worker thread does..so while 2 threads could technically be running on the same connection, one of those threads would be in the process of shutting down and 100% NOT be doing anything with the connection. And that all works.
EXCEPT for a tiny window during our initial switch to EV_DISPATCH. Say a connection is upgraded and also sends 2 websocket messages. While a single poll (`kevent`) won't return 2 distinct events for that 1 connection, they could get split into 2 separate polls. We end up with:
```
wait#1
[signal, fd#1.recv#1]
wait#2
[fd#1.recv#2]
```
and this happens because as we're processing the wait#1 and before we switch to EV_DISPATCH, `fd#1.recv#2` has already been received and queued. Even if you delete and re-add, it seems like that only deals with future events, it doesn't clear our any already/pending events. So we need to mimic what we alreayd do for HTTP connections, which is as a guard clause for a connection that's already being processed. But we still keep the ONESHOT / DISPATCH because it does work beyond the initial window, and that just helps prevent wakeups that we'll just have to reject via the guard.
Add errdefer to unlock mutex when listen() fails before reaching
the success paths. Without this, errors during socket setup, binding,
or worker initialization leave the mutex locked, causing subsequent
calls to listen() or listenInNewThread() to deadlock.
This bug is particularly severe with listenInNewThread() because the
main thread waits on a condition variable, and when the spawned thread
exits with the mutex locked, the condition wakes the main thread which
then hangs forever trying to reacquire the mutex.
Adds test that verifies mutex is properly released by calling listen()
twice when bind fails. Without the fix, the second call deadlocks.
* Enhance CORS to include credentials handling and allow multi cors origin values
Added support for credentials in CORS configuration.
Allow multi cors origin values
* Enhance CORS middleware: support wildcard & multiple origins, credentials; add tests
- Add Origin union and parseOrigin() to middleware/Cors.zig; init now takes MiddlewareConfig and uses arena for origin parsing
- Support wildcard ("*") and comma-separated origin lists; set Access-Control-Allow-Origin to "*" for wildcard, or echo allowed origin for lists
- Preserve and emit Access-Control-Allow-Credentials, Allow-Methods, Allow-Headers, and Max-Age for preflight requests
- Update tests: increase test thread array size, spawn dedicated CORS servers (wildcard, single, multiple), add comprehensive tests for GET and OPTIONS preflight flows and negative cases
- Ensure Origin header is sent in relevant test requests
* Fix parsing of multiple CORS origins by initializing count to 0
* tests: stop and deinit CORS test servers in tests:afterAll
* docs(readme): cleanup formatting, fix typos, and expand CORS documentation
- Normalize whitespace, punctuation and list formatting across README
- Fix typos and improve wording for clarity
- Expand CORS middleware section with examples and behavior:
- document wildcard, single and multiple origins
- add credentials option and note about wildcard+credentials restriction
- show preflight headers and 204 behavior
- Update middleware path reference to httpz.middleware.Cors
- Minor formatting tweaks in many sections (headers, code blocks, comments)