fix(browser-relay): unhang zig build test (#29)#30
Merged
Conversation
dsn_auth_test and cors_test pointed forwarder upstreams at http://localhost:5010/5012. On dev boxes where ::1 is in /etc/hosts but IPv6 SYNs are dropped (instead of refused), std.http.Client stalls indefinitely on the IPv6 attempt, causing zig build test to hang. forward_test already uses 127.0.0.1 literals and works fine. Switch the two affected files to 127.0.0.1 with high unused ports so the connect fails fast with ECONNREFUSED, matching forward_test. Refs #29 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Previously TestServer.waitAndDeinit unconditionally joined the acceptLoop thread. If a test sent fewer requests than expected (or any future bug left the server waiting on a missing client), accept() would block forever and the entire `zig build test` run would hang with no error. Add a stop atomic flag and have waitAndDeinit signal it, then self-connect once to unblock any pending accept(). The accept loop checks the flag before and after accept() and exits cleanly. The happy path (all expected requests received) is unaffected since the loop has already exited by the time waitAndDeinit runs; the self-connect just lands in the listen backlog and is discarded when the server is deinitialized. Refs #29 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Real root cause of #29: the dsn_auth_test/cors_test sendRequest helpers build POST/PUT/PATCH requests with no Content-Length header. In Zig 0.15's std.http.Server, when a body-bearing method has neither Content-Length nor Transfer-Encoding, bodyReader falls back to the raw connection stream (http.zig bodyReader, .none branch). The first test that successfully passes auth then calls reader.readSliceShort which blocks indefinitely waiting for client bytes that never come — the client is meanwhile blocked reading the response. Classic deadlock. Tests 1-4 in each file did not hit this because they reject at auth before reading the body. Test 5 onward (the first valid-auth POST) was the first to deadlock, which is exactly where local + CI runs hung. forward_test was unaffected because it uses sendRequestWithBody which already sets Content-Length. Add Content-Length: 0 to the request format strings in both helpers. All 14 browser-relay test binaries now pass under `zig build test` (123 + 118 + 113 + 11 others tests, total ~30s). Refs #29 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Real root cause of #29 was not the local IPv6/
localhostresolution I initially suspected — it was thatdsn_auth_test/cors_test'ssendRequesthelpers built POST requests with noContent-Lengthheader. In Zig 0.15std.http.Server,bodyReaderfor a body-bearing method withoutContent-LengthorTransfer-Encodingfalls back to the raw connection reader (seestd/http.zigbodyReader,.nonebranch). The first test in each file that passes auth then callsreadSliceShorton the body, which blocks indefinitely waiting for client bytes that never arrive — meanwhile the test client is blocked reading the response. Classic deadlock.Tests 1–4 in each file all reject at auth before touching the body, so they passed. Test 5 onward (the first valid-auth POST) deadlocked, which is exactly where local and CI runs hung.
forward_testwas unaffected because it usessendRequestWithBody, which already setsContent-Length.CI was hanging on
maintoo — recent runs were all cancelled with multi-hour durations, the issue's "CI is green" claim referred to a clean local container only.Changes
browser-relay/src/dsn_auth_test.zig+browser-relay/src/cors_test.zig: addContent-Length: 0to thesendRequestrequest format strings. This is the actual fix.browser-relay/src/{forward,dsn_auth,cors}_test.zig(TestServer hardening): defensive shutdown forTestServer—stopatomic + self-connect inwaitAndDeinitso any future request-count mismatch fails fast instead of hanging onaccept(). Not strictly required to fix browser-relay: zig build test hangs locally (pre-existing) #29, but prevents the next foot-gun in the same harness.browser-relay/src/{dsn_auth,cors}_test.zig(URL change): switch upstream URLs fromhttp://localhost:5010/5012tohttp://127.0.0.1:19999/19998. Removes a real-but-secondary fragility (real services running on 5010/5012 on a dev box would have changed the test outcome) and matchesforward_test.Test plan
cd browser-relay && API_KEY=x ADMIN_API_KEY=y zig build test— exit 0 in ~30sCloses #29
🤖 Generated with Claude Code