Files
hse-2026/homework/03-http-server/solution/server.py
T
2026-09-24 21:30:37 +03:00

87 lines
2.5 KiB
Python

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()