Set protocol version for custom handler based on SimpleHTTPRequestHandler

95 Views Asked by At

I've created my own script based on Python http.server module. The main target of script is handling custom path "/files" to get list of files in JSON format. Parameter --protocol was added in Python 3.11 for http.server module. I use Python 3.11.4. I'm trying to support this parameter in my script in the following way:

import json
import os
import sys
from functools import partial
from http.server import SimpleHTTPRequestHandler, test
from pathlib import Path


class HTTPRequestHandler(SimpleHTTPRequestHandler):
    def do_GET(self) -> None:
        if self.path == '/files':
            response = bytes(self._get_response(), 'utf-8')
            self.send_response(200)
            self.send_header('Content-Type', 'application/json')
            self.send_header('Content-Length', str(len(response)))
            self.end_headers()
            self.wfile.write(response)

            return

        return super().do_GET()

    def _get_response(self) -> str:
        results = []

        for item in Path(self.directory).rglob('*'):
            if item.is_file():
                if __file__ in str(item):
                    continue

                file_size = str(os.stat(item).st_size)
                relative_file_path = os.path.relpath(item, self.directory)

                if sys.platform == 'win32':
                    relative_file_path = relative_file_path.replace('\\', '/')

                results.append(dict(file=relative_file_path, size=file_size))

        return json.dumps(results)


if __name__ == '__main__':
    import argparse

    parser = argparse.ArgumentParser()
    parser.add_argument('-b', '--bind', metavar='ADDRESS',
                        help='bind to this address '
                        '(default: all interfaces)')
    parser.add_argument('-d', '--directory', default=os.getcwd(),
                        help='serve this directory '
                        '(default: current directory)')
    parser.add_argument('-p', '--protocol', metavar='VERSION',
                        default='HTTP/1.1',
                        help='conform to this HTTP version '
                             '(default: %(default)s)')
    parser.add_argument('port', default=8000, type=int, nargs='?',
                        help='bind to this port '
                        '(default: %(default)s)')
    args = parser.parse_args()

    handler_class = partial(HTTPRequestHandler, directory=args.directory)

    test(HandlerClass=handler_class, port=args.port, bind=args.bind, protocol=args.protocol)
python server.py --bind <ip> <port> -p HTTP/1.1

But by some reason response header still contains "HTTP/1.0" version. Could you please advice what I'm doing wrong?

2

There are 2 best solutions below

1
On BEST ANSWER

Create TCPServer instance and set protocol_version on the HTTPRequestHandler class to "HTTP/1.1" then it uses HTTP/1.1 when running.

from socketserver import TCPServer
...

handler_class = partial(HTTPRequestHandler, directory=args.directory)
HTTPRequestHandler.protocol_version = args.protocol
PORT = args.port
with TCPServer(("", PORT), handler_class) as httpd:
    print("Serving HTTP on port", PORT)
    httpd.serve_forever()

If call http.server.test() function then it sets protocol_version on the provided HandlerClass argument, but won't work if using a partial to wrap the class so must explicitly set HTTPRequestHandler.protocol_version.

0
On

Problem was solved by setting value for protocol_version on the custom handler:

...

if __name__ == '__main__':
    import argparse

    parser = argparse.ArgumentParser()
    parser.add_argument('-b', '--bind', metavar='ADDRESS',
                        help='bind to this address '
                        '(default: all interfaces)')
    parser.add_argument('-d', '--directory', default=os.getcwd(),
                        help='serve this directory '
                        '(default: current directory)')
    # --protocol argument was introduced in Python 3.11+
    parser.add_argument('-p', '--protocol', metavar='VERSION',
                        default='HTTP/1.1',
                        help='conform to this HTTP version '
                        '(default: %(default)s)')
    parser.add_argument('port', default=8000, type=int, nargs='?',
                        help='bind to this port '
                        '(default: %(default)s)')
    args = parser.parse_args()

    HTTPRequestHandler.protocol_version = args.protocol
    handler_class = partial(HTTPRequestHandler, directory=args.directory)

    test(HandlerClass=handler_class, port=args.port, bind=args.bind)