81 lines
2.4 KiB
Python
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)
|
|
|