ue_pe_web/scripts/03_server/src/myserver/server.py
2024-03-05 09:44:45 +01:00

81 lines
2.4 KiB
Python

######################################################################
# Copyright (c) Adrien Luxey-Bitri, Boris Baldassari
#
# This program and the accompanying materials are made
# available under the terms of the Eclipse Public License 2.0
# which is available at https://www.eclipse.org/legal/epl-2.0/
#
# SPDX-License-Identifier: EPL-2.0
######################################################################
"""
A module for learning network programming in Python.
This functions manage the socket connections and multi-threading of clients.
"""
import socket
from myserver.log import log, log_reply
from myserver.http_request import parse_request
from myserver.file import get_resource
from myserver.date import now_rfc2616
_BUF_SIZE = 1024
_SERVER_ADDR = "0.0.0.0"
def serve(port: int, root: str):
"""
Serves http request connections for clients.
This function creates the network socket, listens, and calls
:func:`~myserver.handle_client()` when a request comes in.
"""
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Allows reusing a socket right after it got closed (after ctrl-c)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((_SERVER_ADDR, port))
s.listen()
log(f"Server started at {_SERVER_ADDR}:{port}.")
try: # Catch KeyboardInterrupt to close server socket
while True:
c, addr = s.accept()
try: # Catch KeyboardInterrupt to close client handling socket
handle_client(c, addr, root)
except KeyboardInterrupt as e:
c.close()
raise e
except KeyboardInterrupt:
s.close()
def handle_client(c: socket.socket, addr: tuple[str, int], root:str):
buf = c.recv(_BUF_SIZE)
req = parse_request(buf)
if req['head']['verb'] == 'GET':
code, content = get_resource(req['head']['resource'], root)
reply(c, addr, req, code, content)
c.close()
def reply(c: socket.socket, addr: tuple[str, int], req: dict, code: int, content: bytes):
return_message = "200 OK"
if code == 404:
return_message = "404 Not found"
# TODO: Change content type depending on actual content type
reply_header = f"""HTTP/1.1 {return_message}
Content-Type: text/html; charset=utf-8
Date: {now_rfc2616()}
Content-Length: {len(content)}
"""
c.send(reply_header.encode())
c.send(content)
log_reply(addr, req, code)