87 lines
2.5 KiB
Python
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()
|