From c748387b485fd07db1c8187c8834093cc2c2df56 Mon Sep 17 00:00:00 2001 From: Arthur Bols Date: Sun, 28 Mar 2021 17:57:08 +0200 Subject: [PATCH] Improve documentation --- server/command.py | 60 ++++++++++++++++++++++++++++++++++++++++ server/requesthandler.py | 25 +++++++++++++++-- 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/server/command.py b/server/command.py index 18926e8..926e96f 100644 --- a/server/command.py +++ b/server/command.py @@ -58,13 +58,28 @@ class AbstractCommand(ABC): @property @abstractmethod def _conditional_headers(self): + """ + The conditional headers specific to this command instance. + """ pass @abstractmethod def execute(self): + """ + Execute the command + """ pass def _build_message(self, status: int, content_type: str, body: bytes, extra_headers=None): + """ + Build the response message. + + @param status: The response status code + @param content_type: The response content-type header + @param body: The response body, may be empty. + @param extra_headers: Extra headers needed in the response message + @return: The encoded response message + """ if extra_headers is None: extra_headers = {} @@ -96,6 +111,13 @@ class AbstractCommand(ABC): return message def _get_path(self, check=True): + """ + Returns the absolute file system path of the resource in the request. + + @param check: If True, throws an error if the file doesn't exist + @raise NotFound: if `check` is True and the path doesn't exist + """ + norm_path = os.path.normpath(self.msg.target.path) if norm_path == "/": @@ -109,6 +131,9 @@ class AbstractCommand(ABC): return path def _process_conditional_headers(self): + """ + Processes the conditional headers for this command instance. + """ for header in self._conditional_headers: tmp = self.msg.headers.get(header) @@ -118,6 +143,13 @@ class AbstractCommand(ABC): self._conditional_headers[header]() def _if_modified_since(self): + """ + Processes the if-modified-since header. + @return: True if the header is invalid, and thus shouldn't be taken into account, throws NotModified + if the content isn't modified since the given date. + + @raise NotModified: If the date of if-modified-since greater than the modify date of the resource. + """ date_val = self.msg.headers.get("if-modified-since") if not date_val: return True @@ -133,6 +165,12 @@ class AbstractCommand(ABC): return True def get_mimetype(self, path): + """ + Guess the type of file. + @param path: the path to the file to guess the type of + @return: The mimetype based on the extension, or if that fails, returns "text/plain" if the file is text, + otherwise returns "application/octet-stream" + """ mime = mimetypes.guess_type(path)[0] if mime: @@ -148,10 +186,16 @@ class AbstractCommand(ABC): class AbstractModifyCommand(AbstractCommand, ABC): + """ + Base class for commands which modify a resource based on the request. + """ @property @abstractmethod def _file_mode(self): + """ + The mode to open the target resource with. (e.a. 'a' or 'w') + """ pass @property @@ -185,6 +229,10 @@ class AbstractModifyCommand(AbstractCommand, ABC): class HeadCommand(AbstractCommand): + """ + A Command instance which represents an HEAD request + """ + @property def command(self): return "HEAD" @@ -201,6 +249,10 @@ class HeadCommand(AbstractCommand): class GetCommand(AbstractCommand): + """ + A Command instance which represents a GET request + """ + @property def command(self): return "GET" @@ -221,6 +273,10 @@ class GetCommand(AbstractCommand): class PostCommand(AbstractModifyCommand): + """ + A Command instance which represents a POST request + """ + @property def command(self): return "POST" @@ -231,6 +287,10 @@ class PostCommand(AbstractModifyCommand): class PutCommand(AbstractModifyCommand): + """ + A Command instance which represents a PUT request + """ + @property def command(self): return "PUT" diff --git a/server/requesthandler.py b/server/requesthandler.py index 6079e39..3a92e54 100644 --- a/server/requesthandler.py +++ b/server/requesthandler.py @@ -6,7 +6,7 @@ from urllib.parse import ParseResultBytes, ParseResult from httplib import parser from httplib.exceptions import MethodNotAllowed, BadRequest, UnsupportedEncoding, NotImplemented, NotFound, \ HTTPVersionNotSupported -from httplib.httpsocket import HTTPSocket, FORMAT +from httplib.httpsocket import FORMAT from httplib.message import RequestMessage as Message from httplib.retriever import Retriever, PreambleRetriever from server import command @@ -23,12 +23,15 @@ class RequestHandler: messages, parse, verify them and send a respond. """ - conn: HTTPSocket + conn: ServerSocket def __init__(self, conn: socket, host): self.conn = ServerSocket(conn, host) def listen(self): + """ + Listen to incoming messages and process them. + """ retriever = PreambleRetriever(self.conn) @@ -42,6 +45,12 @@ class RequestHandler: self._handle_message(retriever, line) def _handle_message(self, retriever, line): + """ + Retrieves and processes the request message. + + @param retriever: the retriever instance to retrieve the lines. + @param line: the first received line. + """ lines = retriever.retrieve() # Parse the request-line and headers @@ -80,7 +89,8 @@ class RequestHandler: def _check_request_line(self, method: str, target: Union[ParseResultBytes, ParseResult], version): """ - Checks if the request-line is valid. Throws an appriopriate exception if not. + Checks if the request-line is valid. Throws an appropriate exception if not. + @param method: HTTP request method @param target: The request target @param version: The HTTP version @@ -124,6 +134,7 @@ class RequestHandler: def _has_body(self, headers): """ Check if the headers notify the existing of a message body. + @param headers: the headers to check @return: True if the message has a body. False otherwise. """ @@ -138,6 +149,14 @@ class RequestHandler: @staticmethod def send_error(client: socket, code, message): + """ + Send and HTTP error response to the client + + @param client: the client to send the response to + @param code: the HTTP status code + @param message: the status code message + """ + message = f"HTTP/1.1 {code} {message}\r\n" message += parser.get_date() + "\r\n" message += "Content-Length: 0\r\n"