adafruit / Adafruit_CircuitPython_HTTPServer

Simple HTTP Server for CircuitPython
MIT License
46 stars 30 forks source link

socket.accept() is blocking by default #9

Closed karlfl closed 2 years ago

karlfl commented 2 years ago

After adding the Start/Poll method I setup some tests to see if other code could run even if there were no requests coming in. Unfortunately, that wasn't the case. Digging into it deeper I discovered that socket.accept() method is blocking by default.

Modifying the example code with a simple print will show the issue.

while True:
    try:
        print("Processing before polling...")
        server.poll()
        print("Processing after polling...")
    except OSError:
       continue

The code will stop at the server.poll() method waiting for a request to come in...

Listening on http://192.168.1.208:80
Processing before polling...

Once a request comes in the other lines will process, then it'll stop at server.poll() again...

Processing after polling...
Processing before polling...

To fix this the socket needs to set blocking to False or it's timeout to 0 in HTTPServer.Start()...

        self._sock.bind((host, port))
        self._sock.listen(5)
        self._sock.setblocking(False) #do not block

There is one caveat to this. When the accept() method doesn't find any incoming connections and blocking is turned off, it raises an exception OSError [Errno 11] EAGAIN. Therefore, server.poll() must be the last thing in the while loop otherwise, the exception will skip any logic after the exception is raised. Which means any logic after server.poll() will only run once a request has been received and processed successfully...

while True:
    try:
        print("Processing before polling...")
        server.poll()
        print("Processing after polling...") #This will never be called if exception is raised.
    except OSError:
       continue

Results in this output...

Listening on http://192.168.1.208:80
Processing before polling...
Processing before polling...
Processing before polling...
Processing before polling...
Received 485 bytes
Processing after polling...
Processing before polling...
Processing before polling...
...  and so on

A possible work around for this would be to handle the specific exception OSError: [Errno 11] EAGAIN in the server.poll() method. But I'm not sure if catching and handling exceptions in library code is an acceptable practice.