Opened 5 weeks ago
Closed 4 weeks ago
#36081 closed New feature (wontfix)
HttpResponse 204 (No Content) causes client code to hang if data is present.
Reported by: | Cleophus Robinson | Owned by: | Aditya Upadhye |
---|---|---|---|
Component: | HTTP handling | Version: | dev |
Severity: | Normal | Keywords: | 204, HTTP, API, Timeout |
Cc: | Triage Stage: | Unreviewed | |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | yes | UI/UX: | no |
Description
What Happened
I discovered this when returning "{}" as the body for a 204 status response.
Explanation
When returning an HttpResponse to the client with a status of 204 (no content), their system will hang if data is actually sent back in the body.
It does this because it's an invalid response and it breaks the HTTP spec. Clients will hang until a valid response is returned from the server.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/204#compatibility_notes
"Although this status code is intended for responses with no body, servers may erroneously include data after the headers. This issue is observable in persistent connections, where the invalid body may include a distinct response to a subsequent request. The HTTP protocol allows browsers to handle such responses differently (there is an ongoing discussion regarding the specification text in the HTTPWG http-core GitHub repository).
Apple Safari rejects any such data. Google Chrome and Microsoft Edge discard up to four invalid bytes preceding a valid response. Firefox tolerates over a kilobyte of invalid data preceding a valid response."
Examples
- This is observed in Postman and other systems, the response is already returned but it's still "Receiving Response". In this case, it will timeout after a minute. Browsers may handle it differently.
- My webhook made the client code timeout and forced the webhook into a retry sequence.
Solution
I think Django should raise an exception in the HttpResponse class if this happens. Browsers take their own approach on what to do in this scenario and an opinionated stance may be warranted. It will also save developers from bizarre behavior.
Attachments (1)
Change History (11)
by , 5 weeks ago
Attachment: | ReceivingResponse.png added |
---|
comment:1 by , 5 weeks ago
Version: | 4.2 |
---|
comment:2 by , 5 weeks ago
Owner: | set to |
---|---|
Status: | new → assigned |
comment:3 by , 5 weeks ago
Owner: | removed |
---|---|
Status: | assigned → new |
comment:4 by , 5 weeks ago
Triage Stage: | Unreviewed → Accepted |
---|
comment:5 by , 5 weeks ago
Triage Stage: | Accepted → Unreviewed |
---|---|
Type: | Uncategorized → Bug |
Version: | → 5.1 |
comment:6 by , 5 weeks ago
Type: | Bug → New feature |
---|
Marking this as a New Feature — Reading the report, it's the client that needs to handle this, and anything in Django would just be a convenience.
Best approach is for users to not send data when using 204 status codes. A project-level response class enforcing this would be my suggestion.
I'm sceptical about adding something for this in Django itself. I'd argue for wontfix probably.
comment:7 by , 5 weeks ago
Hi Cleophus Robinson, this looks like additional handling for HTTP 204. I would like to work on this.
comment:8 by , 5 weeks ago
Owner: | set to |
---|---|
Status: | new → assigned |
comment:9 by , 5 weeks ago
Whoops, I was confused by the "next steps" message above - I'll know for next time.
If I'm understanding correctly the original suggestion was for us to handle it at the project level? Or by project level do you mean another, more strict class within the django.http module? My team and I won't make this mistake again so the fix isn't so much for us. I'm more worried about the bizarreness of it as it will certainly trip up other developers. For a little more context, we were using it in our JSON API and returning "{}" for a 200 response. We changed this to a 204 later on and thought nothing of it - to our surprise.
I do think this looks like additional handling / validation for 204. The lowest hanging fruit would be a warning in the documentation but it wouldn't be as effective. The issue is too innocuous for someone to suspect a 2xx status code as the root cause.
In any case, thank you both for taking a look! Let me know if I can assist in any way.
comment:10 by , 4 weeks ago
Resolution: | → wontfix |
---|---|
Status: | assigned → closed |
Version: | 5.1 → dev |
Thank you all for the discussion and the context surrounding the report. I've reviewed the relevant sections of the RFC and done some additional research. Following that, I think that ensuring a proper 204 response is the responsibility of the project itself. For reference, Flask doesn't implement any special handling for 204 responses, and Django doesn't have a custom HttpResponse class for 20x status codes. Additionally, I believe clients should ideally be more resilient and not hang on 204 responses that include content.
Hi Cleophus — please don't "Accept" your own tickets. It needs to be triaged by someone other than the reporter. Thanks.