Skip to content

Dont try to read body of 204 no_content resp #211

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

FoamyGuy
Copy link
Contributor

Resolves: #210

With this change the behavior now matches between CircuitPython and CPython

circuitpython:

POSTing data to https://httpbin.org/status/204: 31F
----------------------------------------
Data received from server: b''
Response code 204

CPYthon:

POSTing data to https://httpbin.org/status/204: 31F
----------------------------------------
Data received from server: b''
Response code 204

Without this change the code goes into iter_content() which gets to readinto and stalls until timeout as there is no data to read.

I am not certain if this is the correct / best way to handle 204 responses, but it does seem to match the behavior of CPython. I tried poking around briefly in the CPYthon requests code to look for any special handling of 204 status like this and couldn't find any so I'm not sure what drives the behavior on CPython.

All of my testing was on ESP32S3 TFT with 9.2.7

@justmobilize
Copy link
Collaborator

@FoamyGuy I was going to tackle this tomorrow. The Python requests library doesn't do this based on status code, and think we should try to match their logic, thoughts?

@anecdata
Copy link
Member

anecdata commented May 11, 2025

200 without content-length and not chunked I thought was fixed by: #123

My understanding (which is shallow) is that CPython can detect when the server closes the socket, using socket.fileno() (or .select() or similar. In ESP32SPI I think we have .available or can access he specific states of socket setup and shutdown. But nothing I know of that's comparable in native wifi sockets. Not sure about WIZnet.

204 by definition has no response body:
https://www.rfc-editor.org/rfc/rfc7231.html#section-6.3.5
Also, 204 is not supposed to have content-length, and since there is no body it can't be chunked.

I may be missing a third way, but it seems we either need to condition on status code, or add capability to sense that the server has closed the socket (supporting ESP32SPI, WIZnet, and two native implementations). Otherwise, that leaves timeout.

In an environement where I can control the server to respond with a 204, the headers are:

HTTP/1.1 204 No Content
Date: Sun, 11 May 2025 18:14:13 GMT
Server: Apache
Upgrade: h2
Connection: Upgrade
Cache-Control: max-age=172800
Expires: Tue, 13 May 2025 18:14:13 GMT
Vary: User-Agent

No response body (and not chunked), no content-length header, (and also no keep-alive), but the client (curl, which didn't send Connection: close, and verbose mode reports: "* Connection #0 to host {redacted} left intact") exits immediately. keep-alive is default behavior in HTTP/1.1 unless there's a Connection: close in the request or response. That doesn't seem the case here, so not sure what options there are besides checking status code or timeout.

@justmobilize
Copy link
Collaborator

A quick look, and that PR uses _remaining, which is based on content length, which for a 204 is specifically not returned.

So the correct fix would see that _remaining is None and probably something else to make sure there wasn't any data...

@anecdata
Copy link
Member

I'm just wondering if that something needs to be a timeout, or if there's some other mechanism in the absence of content-length, chunked, or socket close. It doesn't seem like curl or CPython Requests have any discernable timeout.

If not conditioning on status code, a 200 is like a 204 except it has no body (a 200 could also have no body, but then it should have a content-length header of 0), but the client may not know there are no body bytes incoming without waiting to see?

@justmobilize
Copy link
Collaborator

My plan was to step through a normal 204 in CPython requests and see what it gets. And then see if I can reproduce it in a 3 in circuitpython...

@anecdata
Copy link
Member

I'm glad you all are on it. I'm baffled how it works given all of the above :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Responses with no content cause timeout error and crash
3 participants