import logging import pathlib from dataclasses import dataclass from socketserver import StreamRequestHandler import typing as t import click import socket logging.basicConfig(level=logging.DEBUG) logger = logging.getLogger(__name__) @dataclass class HTTPServer: server_address: t.Tuple[str, int] socket: socket.socket server_domain: str working_directory: pathlib.Path class HTTPHandler(StreamRequestHandler): server: HTTPServer # Use self.rfile and self.wfile to interact with the client # Access domain and working directory with self.server.{attr} def handle(self) -> None: first_line = self.rfile.readline() logger.info(f"Handle connection from {self.client_address}, first_line {first_line}") # TODO: Read the remaining headers, handle the request and send a response. # Read the body separately using its Content-Length, not until EOF. # Keep memory bounded when processing large files. pass @click.command() @click.option("--host", envvar="SERVER_HOST", default="0.0.0.0", type=str) @click.option("--port", envvar="SERVER_PORT", default=8080, type=int) @click.option("--server-domain", envvar="SERVER_DOMAIN", default="localhost", type=str) @click.option("--working-directory", envvar="SERVER_WORKING_DIRECTORY", type=str) def main(host, port, server_domain, working_directory): if not working_directory: raise SystemExit(1) working_directory_path = pathlib.Path(working_directory) logger.info( f"Starting server on {host}:{port}, domain {server_domain}, working directory {working_directory}" ) # Create a server socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Set SO_REUSEADDR option s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # Bind the socket object to the address and port s.bind((host, port)) # Start listening for incoming connections s.listen() logger.info(f"Listening at {s.getsockname()}") server = HTTPServer((host, port), s, server_domain, working_directory_path) while True: # Accept any new connection (request, client_address) try: conn, addr = s.accept() except OSError: break try: # Handle the request HTTPHandler(conn, addr, server) # Close the connection conn.shutdown(socket.SHUT_WR) conn.close() except Exception as e: logger.error(e) conn.close() if __name__ == "__main__": main()