adafruit / Adafruit_CircuitPython_Requests

Requests-like interface for web interfacing
MIT License
51 stars 36 forks source link

Intermittent ValueError on first GET response with ESP32-S2 #55

Closed anecdata closed 3 years ago

anecdata commented 3 years ago

This has been observed for some months over successive versions of circuitpython and Requests (first noted in a comment in #34 ), current example with:

FeatherS2 with ESP32S2
6.1.0-beta.2-5-gacbc5fc7a on 2020-12-06
adafruit-circuitpython-bundle-6.x-mpy-20201205

The first GET in code.py will sometimes result in this exception:

Traceback (most recent call last):
  File "code.py", line 522, in http_get
  File "adafruit_requests.py", line 343, in text
  File "adafruit_requests.py", line 332, in content
  File "adafruit_requests.py", line 378, in iter_content
  File "adafruit_requests.py", line 237, in _readinto
ValueError: invalid syntax for integer with base 16

Subsequent tries at GET always yield:

Traceback (most recent call last):
  File "code.py", line 512, in http_get
  File "adafruit_requests.py", line 612, in get
  File "adafruit_requests.py", line 568, in request
  File "adafruit_requests.py", line 277, in close
  File "adafruit_requests.py", line 199, in _readto
  File "adafruit_requests.py", line 143, in _recv_into
OSError: [Errno 9] EBADF

and reload is the only thing that seems to fix it.

I think it happens when the response is empty or times out or some similar situation. The response status_code, reason, headers, and text are all empty at the time the exception is raised. The server side sees 200 status code and non-zero content length sent to client.

I see this on chunked responses, but I'm not sure if that's relevant, it's just mostly all I ever do.

This has no effect on correcting the issue:

    wifi.radio.enabled = False
    wifi.radio.enabled = True

Once Requests reloads normally and works on the first GET, it will continue to run fine for a time, then the OSError: [Errno 9] EBADF may crop up again later, requiring a reload.

anecdata commented 3 years ago

The OSError: [Errno 9] EBADF aspect is better handled with https://github.com/adafruit/circuitpython/issues/3836, and it's possible the initial ValueError may be recoverable if that socket behavior is changed.

I don't know if there's better handling for an empty chunk_header in line 236-237, or if user code should just catch and act accordingly:

>>> chunk_header = _buffer_split0(b"", b";")
>>> http_chunk_size = int(bytes(chunk_header), 16)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: invalid syntax for integer with base 16

For now, I added code to retry a couple of times, then supervisor.reload(), and it eventually gets sync'd up and works fine again.

tannewt commented 3 years ago

@anecdata Seems like we should check for empty chunk header and fail the connection. (Raising an exception or similar that closes the underlying socket.)

anecdata commented 3 years ago

Similar scenario (no headers or content returned, despite 200 status code on the server side) using today's artifacts from #4049 commits (6.2.0-beta.1-29-gaf92db28b on 2021-02-01) on multiple FeatherS2 with ESP32S2 gives:

Traceback (most recent call last):
  File "code.py", line 662, in http_get
  File "adafruit_requests.py", line 326, in text
  File "adafruit_requests.py", line 315, in content
  File "adafruit_requests.py", line 361, in iter_content
  File "adafruit_requests.py", line 219, in _readinto
  File "adafruit_requests.py", line 182, in _readto
  File "adafruit_requests.py", line 126, in _recv_into
OSError: [Errno 116] ETIMEDOUT

which is non-fatal and recoverable, so I'm closing this issue.