Repository navigation
std/net: resolve send with the response for an HTTP error status - #604
Open
notactuallytreyanastasio wants to merge 1 commit into
Open
notactuallytreyanastasio wants to merge 1 commit into
notactuallytreyanastasio wants to merge 1 commit into
Conversation
`NetResponse.status` is documented as the HTTP status code, and js and
java already resolve `send()` with the response for any status. py and
rust treated a 404 as a failure, in two different ways, and java dropped
the body of one.
For a GET of a path the server does not have, then a GET of port 1,
where nothing listens, the new functional test printed:
js status 404 / body not here / no response from port 1
java status 404 / broken promise
rust broken promise / no response from port 1
py (nothing; the test timed out after 60 seconds)
and now prints the js lines on all four.
py: `urlopen` raises HTTPError for 4xx and 5xx and URLError when it
cannot connect. Neither was caught, so the exception ended the worker
generator and `send`'s promise was never settled; `await_safe_to_exit`
then waited for it forever. HTTPError is itself a response with
`.status`, `.headers` and `.read()`, so it now resolves the promise as
the response. OSError (which URLError is) and http.client.HTTPException
break both the response promise and the body promise. The body promise
has to be settled too, or `temper run` still hangs at exit with the
response promise broken, since `await_safe_to_exit` counts it.
Reading the body had the same hang in another form. Only IOError was
caught there, so a body that is not UTF-8 raised UnicodeDecodeError, the
worker ended, and `temper run` never returned, now more likely since
error pages reach that read. Decode errors and http.client errors there
now break the body promise; the response promise still resolves. The
backends still disagree about such a body: js and java put U+FFFD for
each bad byte, rust puts "?", and py now gives a broken body promise.
The functional test does not cover this; it was checked by running a
404 with a 0xFF 0xFE body, which hung py before and now prints the
status.
rust: ureq 3 returns `Err(StatusCode(404))` by default, and `send`
breaks the promise on any `Err`. The request now runs on an agent with
`http_status_as_error(false)`.
java: the status was right, but `getInputStream` throws for a 4xx or
5xx, so `bodyContent` was broken. The body of an error response comes
from `getErrorStream`, which is null when there is no body; that reads
as an empty body, as an empty 2xx body does.
Breaking the promise for every non-2xx status everywhere would be the
smaller change on py and rust, but it would leave `status` unable to
say anything other than success, and it would change js and java.
The test server gains a `/missing` route that answers 404 with a short
body. The new functional test types/netresponse-not-found fails on py,
rust and java 17 before this change and passes on js, py, java 17 and
rust after it. It carries the same temperlang#214 marks as types/netresponse for
cpp, the interpreter and lua. csharp was not run (no dotnet here); by
reading, HttpClient already returns the response for any status.
Fixes temperlang#553.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Bobby Grayson <bobbbygrayson+github@gmail.com>
mikesamuel
approved these changes
Oct 12, 2026
| } | ||
| // A path the server does not have, for checking that an HTTP error | ||
| // status reaches Temper code as a response with that status. | ||
| server.createContext("/missing") { exchange -> |
| 214.let { issue(Ft.TypesNetresponse, cpp(it), interp(it), lua(it)) } | ||
| 214.let { | ||
| issue(Ft.TypesNetresponse, cpp(it), interp(it), lua(it)) | ||
| issue(Ft.TypesNetresponseNotFound, cpp(it), interp(it), lua(it)) |
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.
NetResponse.statusis documented as the HTTP status code, and js andjava already resolve
send()with the response for any status. py andrust treated a 404 as a failure, in two different ways, and java dropped
the body of one.
For a GET of a path the server does not have, then a GET of port 1,
where nothing listens, the new functional test printed:
and now prints the js lines on all four.
py:
urlopenraises HTTPError for 4xx and 5xx and URLError when itcannot connect. Neither was caught, so the exception ended the worker
generator and
send's promise was never settled;await_safe_to_exitthen waited for it forever. HTTPError is itself a response with
.status,.headersand.read(), so it now resolves the promise asthe response. OSError (which URLError is) and http.client.HTTPException
break both the response promise and the body promise. The body promise
has to be settled too, or
temper runstill hangs at exit with theresponse promise broken, since
await_safe_to_exitcounts it.Reading the body had the same hang in another form. Only IOError was
caught there, so a body that is not UTF-8 raised UnicodeDecodeError, the
worker ended, and
temper runnever returned, now more likely sinceerror pages reach that read. Decode errors and http.client errors there
now break the body promise; the response promise still resolves. The
backends still disagree about such a body: js and java put U+FFFD for
each bad byte, rust puts "?", and py now gives a broken body promise.
The functional test does not cover this; it was checked by running a
404 with a 0xFF 0xFE body, which hung py before and now prints the
status.
rust: ureq 3 returns
Err(StatusCode(404))by default, andsendbreaks the promise on any
Err. The request now runs on an agent withhttp_status_as_error(false).java: the status was right, but
getInputStreamthrows for a 4xx or5xx, so
bodyContentwas broken. The body of an error response comesfrom
getErrorStream, which is null when there is no body; that readsas an empty body, as an empty 2xx body does.
Breaking the promise for every non-2xx status everywhere would be the
smaller change on py and rust, but it would leave
statusunable tosay anything other than success, and it would change js and java.
The test server gains a
/missingroute that answers 404 with a shortbody. The new functional test types/netresponse-not-found fails on py,
rust and java 17 before this change and passes on js, py, java 17 and
rust after it. It carries the same #214 marks as types/netresponse for
cpp, the interpreter and lua. csharp was not run (no dotnet here); by
reading, HttpClient already returns the response for any status.
Fixes #553.
Repro case: https://github.com/notactuallytreyanastasio/temper-issue-repros/tree/main/31-net-404-per-backend
🤖 Generated with Claude Code