ishahak
ishahak

Reputation: 6795

Handling socket errors in Python HTTPServer when a client closes SSE gracefully

My code is using SSE technique (Server-Sent Events) to send Jpeg images every time a camera-viewer object is updated with a new frame. I'm using Python 3.9.13.

Inside `do_GET I have this code:

self.send_response(200)
self.send_header("Content-Type", "text/event-stream")
self.send_header("Cache-Control", "no-cache")
self.send_header("Connection", "keep-alive")
self.end_headers()

And then a loop is used to wait for a new frame and send it over when available:

counter = g_cam_viewer.counter - 1
while True:
    if counter < (g_cam_viewer.counter):
        counter = g_cam_viewer.counter
        _, image_data = cv2.imencode('.jpg', g_cam_viewer.last_frame)
        image_data_bytes = image_data.tobytes()

        # Send the JPEG image data as SSE data
        image_base64 = base64.b64encode(image_data_bytes)
        image_frame = b'data: ' + image_base64 + b'\n\n'

        try:
            self.wfile.write(image_frame)
            self.wfile.flush()
        except (OSError, socket.error) as e:
            print('stream3 got exception: ' + str(e))
            return  # client disconnected, release thread
        except Exception as e:
            print('stream2 got exception: ' + str(e))
            return  # client disconnected, release thread
    time.sleep(0.05)  # allow new frame to be ready

Now everything works well. The client is using this code to read the frame one by one:

app.sse = new EventSource(app.stream_url);
app.sse.onmessage = (e) => {
    app.frame_count += 1;
    console.log('frame count: ', app.frame_count);
    var data = `data:image/jpeg;base64,${e.data}`;
    app.ui.imgStream.attr('src', data);
};
app.sse.onerror = (e) => {
    console.warn('error: ', e);
};

When the client wants to abort the stream, it calls app.sse.close(). At that time the server doesn't show any log, but after about 1.5 seconds, I'm getting two errors. the first error comes from my exception handler and is really ok:

stream3 got exception: [WinError 10053] An established connection was aborted by the software in your host machine

and another very annoying one from the socket server library:

Exception occurred during processing of request from ('127.0.0.1', 17079)
Traceback (most recent call last):
  File "C:\Users\LabUser\...\lib\socketserver.py", line 316, in _handle_request_noblock
    self.process_request(request, client_address)
  File "C:\Users\LabUser\...\lib\socketserver.py", line 347, in process_request
    self.finish_request(request, client_address)
  File "C:\Users\LabUser\...\lib\socketserver.py", line 360, in finish_request
    self.RequestHandlerClass(request, client_address, self)
  File "C:\Users\LabUser\...\lib\socketserver.py", line 747, in __init__
    self.handle()
  File "C:\Users\LabUser\...\lib\http\server.py", line 427, in handle
    self.handle_one_request()
  File "C:\Users\LabUser\...\lib\http\server.py", line 393, in handle_one_request
    self.raw_requestline = self.rfile.readline(65537)
  File "C:\Users\LabUser\...\lib\socket.py", line 704, in readinto
    return self._sock.recv_into(b)
ConnectionAbortedError: [WinError 10053] An established connection was aborted by the software in your host machine

Now my question is: how can I eliminate the error printed from the socket server library? I would really appreciate your help with that!

Upvotes: 0

Views: 131

Answers (1)

ishahak
ishahak

Reputation: 6795

I finally found a solution.

Instead of creating an instance of HTTPServer within my code, I change it to be an instance of MyHTTPServer which is declared like this:

class MyHTTPServer(HTTPServer):
    def handle_error(self, request, client_address):
        print(f'HTTP Server got error from {client_address}: {sys.exc_info()[0]}')

This way I can control what is printed for this error at the server level, in addition to handling the exception at the do_GET() level .

A typical result from my implementation would be:

HTTP Server got error from ('127.0.0.1', 14420): <class 'ConnectionAbortedError'>

Upvotes: 0

Related Questions