Custom implementation of Python http.server doesn't work properly when HTTPS is enabled - Stack Overflow

admin2025-05-01  8

I am aware that Python http.server module has been conceived mainly for testing purposes, since there are some well known security issues in its implementation.

That said, for a very humble personal project, I implemented my own class, derived from http.server.BaseHTTPRequestHandler, where I override the do_GET method. Everything was working properly until I updated Python version to 3.12: it seems this version produced some changes to the ssl module too and so I had to refactor a bit the code.

With this regard, this is the condensed content of the main.py file:

# ...here are import statements and other functions...

class RequestHandler(BaseHTTPRequestHandler):

    # ...here the other methods are defined...

    def do_GET(self):
        resobj = self._manage_get_request()
        self.send_response(resobj.status_code)
        self._send_headers(resobj.headers)
        self._send_body(resobj.body)

def main():
    print('Running http server...')
    address = (_HTTP_SERV_ADDR, _HTTP_SERV_PORT)
    httpd = HTTPServer(address, RequestHandler)
    if not _DEBUG_ENABLED:
        print('Enabling TLS protocol...')
        context = _ssl.SSLContext(_ssl.PROTOCOL_TLS_SERVER)
        context.load_cert_chain(certfile=_CERTIFICATE_FILE_PATH,
                                keyfile=_KEY_FILE_PATH)
        httpd.socket = context.wrap_socket(httpd.socket, server_side=True)
    print(f'Server is listening ({httpd.server_address[0]}:{httpd.server_port})...')
    try:
        httpd.serve_forever()
    except KeyboardInterrupt:
        pass
    print('Shutting down http server...')
    httpd.server_close()

If the debug is enabled, and so the code portion related to HTTPS is skipped, the server works properly. Instead, when the debug is disabled, the server seems to work fine as well but after some hours it becomes unresponsive: all the connection requests time out.

Would any of you kindly provide me some hints on what to check to better understand what it is going on and why?

Further information on the implementation

While send_response is already defined in BaseHTTPRequestHandler, the other methods called in do_GET are defined in my implementation. In particular:

  • _manage_get_request is a kind of if statement to properly manage the request, taking into account the parameters provided via the query; it returns a custom Response object with code, headers and body
  • _send_headers is basically a wrapper of the send_header method of parent class
  • _send_body simply writes the response body to the socket buffer

Jan 6th 2025 Update

Since I submitted this question, I tried several changes to code but actually none of them was resolving the issue.

So, I decided to implement a humble decorator to trace the function call stack. With this little tool, I noticed that the code was hanging inside the get_request method of the TCPServer class, where a new TCP connection is accepted:

[...]
----- Calling BaseServer.service_actions on 2025-01-05 15:59:15.092364 -----
      BaseServer.service_actions execution ended after 0:00:00.000015
----- Calling BaseServer._handle_request_noblock on 2025-01-05 15:59:15.282698 -----
----- Calling TCPServer.get_request on 2025-01-05 15:59:15.282869 -----

Honestly, I don't know the reason of this behaviour (I see only that this happens when the server receive more than a request at almost the same time, just a matter of tenths of second), but in the meantime I override the method in question by adding a simple timeout mechanism:

class TimeoutException(BaseException):
    pass


def alarm_handler(signum, frame):
    print('Timeout reached!')
    raise TimeoutException


def get_request(self):
    # Overwrite method of TCPServer class...
    _signal.signal(_signal.SIGALRM, alarm_handler)
    _signal.alarm(30)
    try:
        result = self.socket.accept()
        _signal.alarm(0)
        return result
    except TimeoutException as e:
        _signal.alarm(0)
        raise OSError from e  # OSError is already managed by caller...

I know it is not a valuable solution to the issue but it lets the server keep working somehow, so it might be considered as a temporary workaround.

That said, could any of you explain what are the possible causes of this issue and how to solve it? The application in question is running inside a Docker image derived from ubuntu:latest.

转载请注明原文地址:http://www.anycun.com/QandA/1746106878a91765.html