Repository navigation
serve: answer 400, not 500, for a request with no authority or Host header - #14432
Conversation
…ader RFC 9112 section 3.2 requires a 400 response to an HTTP/1.1 request that lacks a Host header field. `wasmtime serve` currently answers 500 for it: the request is rejected while it is being turned into an incoming request, and that failure is reported the same way as every other request-handling failure. Check for the missing Host in `serve` before dispatching, where the request is still known to be a malformed client request rather than an internal error.
pchickey
left a comment
There was a problem hiding this comment.
I think this comes down to an architectural decision for whether the guest or host code is responsible for enforcing the RFC invariant. De facto in this implementation, if hyper didn't enforce an RFC invariant, then it would be up to the guest to enforce it. That's not a principled decision, it was just the way things shook out, and other hosts very likely end up handling broken RFC invariants differently than the exact way hyper handles them.
So, we could enforce this particular RFC invariant here, but then the questions are:
- why, exactly, do we not enforce it for p3? The comment here doesn't make sense to me, because in p2 the guest is also capable of observing whether authority is none.
- what other RFC invariants is our implementation not enforcing in the host? Do we add them all here?
- What from this do we need to feed into specification of the wasi-http implementation?
Want to call this out to other maintainers here, cc @lann
|
If |
|
Fair — the comment was wrong. I tested both worlds: the proxy path already fails on a Host-less request, we just turned the 500 into a 400. The service path doesn't fail, it just never gets the host. |
|
Could you say more about your motivation for this change? |
|
RFC 9112 §3.2 says an HTTP/1.1 request without Host must get 400, and §3.3 lets the server reject an empty authority, so rejecting is fine — ours just surfaces as a 500 with "worker trapped or panicked", which reads as our bug. Only the status code changes. |
|
@xia-chao Its possible to modify your guest code to give the correct status, correct? |
No, that's the whole problem; wasmtime/crates/wasi-http/src/p2/types.rs Line 92 in 1bc343e |
|
No — the host rejects it before the guest runs: "invalid HTTP request missing authority in URI and host header". A duplicate or invalid Host does reach the guest. |
|
It's a bug, not a preference. |
|
OK, I apologize, I missed that, now I understand what both of you are saying. We don't want to duplicate the logic of detecting this 400 Bad Request. Instead, we should change the place the bad request is detected to also include what status code Wasmtime serve should respond with. So, instead of this patch, the architecture I'd like to see is:
|
|
Your three steps are merged, but they don't actually solve the problem: the p2 prepare error gets wrapped into a TrapOrPanicError before serve gets it, so ErrorResponse is only logged and we still return a 500. To get a 400, we should pass this error back through tx just like instantiation errors do. This touches 6 test cases, but leaves service completely unchanged. |
|
Done in fdf05d2. Passing the error back through tx gives us the 400 now; |
pchickey
left a comment
There was a problem hiding this comment.
Thanks very much for the changes. This round of feedback is mostly that Claude is awfully verbose with comments (you pay by the token, after all) and I think that all of the really obvious ones are best deleted.
Per review: keep the comment that explains why the error is handed back through tx, and drop the ones that restate the code. ErrorResponse::new and ::status now carry no doc text, which needs #[expect(missing_docs, reason = "self-explanatory")] because this crate is #![deny(missing_docs)]. The error is built with wasmtime::bail!.
|
Done |
|
Thanks! |
wasmtime serveanswers 500 when a request has neither a URI authority nor aHostheader. The rejection happens before the guest runs, so the error nowcarries the status the client should see, and
servereads it back, defaultingto 500. That gives the 400 RFC 9112 section 3.2 asks for, and it is not
version-specific: an HTTP/1.0 request without
Hostgets the same 400.wasi:http/serviceis untouched.