Improve documentation

This commit is contained in:
2021-03-28 17:57:08 +02:00
parent 0f2b039e71
commit c748387b48
2 changed files with 82 additions and 3 deletions

View File

@@ -58,13 +58,28 @@ class AbstractCommand(ABC):
@property @property
@abstractmethod @abstractmethod
def _conditional_headers(self): def _conditional_headers(self):
"""
The conditional headers specific to this command instance.
"""
pass pass
@abstractmethod @abstractmethod
def execute(self): def execute(self):
"""
Execute the command
"""
pass pass
def _build_message(self, status: int, content_type: str, body: bytes, extra_headers=None): 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: if extra_headers is None:
extra_headers = {} extra_headers = {}
@@ -96,6 +111,13 @@ class AbstractCommand(ABC):
return message return message
def _get_path(self, check=True): 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) norm_path = os.path.normpath(self.msg.target.path)
if norm_path == "/": if norm_path == "/":
@@ -109,6 +131,9 @@ class AbstractCommand(ABC):
return path return path
def _process_conditional_headers(self): def _process_conditional_headers(self):
"""
Processes the conditional headers for this command instance.
"""
for header in self._conditional_headers: for header in self._conditional_headers:
tmp = self.msg.headers.get(header) tmp = self.msg.headers.get(header)
@@ -118,6 +143,13 @@ class AbstractCommand(ABC):
self._conditional_headers[header]() self._conditional_headers[header]()
def _if_modified_since(self): 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") date_val = self.msg.headers.get("if-modified-since")
if not date_val: if not date_val:
return True return True
@@ -133,6 +165,12 @@ class AbstractCommand(ABC):
return True return True
def get_mimetype(self, path): 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] mime = mimetypes.guess_type(path)[0]
if mime: if mime:
@@ -148,10 +186,16 @@ class AbstractCommand(ABC):
class AbstractModifyCommand(AbstractCommand, ABC): class AbstractModifyCommand(AbstractCommand, ABC):
"""
Base class for commands which modify a resource based on the request.
"""
@property @property
@abstractmethod @abstractmethod
def _file_mode(self): def _file_mode(self):
"""
The mode to open the target resource with. (e.a. 'a' or 'w')
"""
pass pass
@property @property
@@ -185,6 +229,10 @@ class AbstractModifyCommand(AbstractCommand, ABC):
class HeadCommand(AbstractCommand): class HeadCommand(AbstractCommand):
"""
A Command instance which represents an HEAD request
"""
@property @property
def command(self): def command(self):
return "HEAD" return "HEAD"
@@ -201,6 +249,10 @@ class HeadCommand(AbstractCommand):
class GetCommand(AbstractCommand): class GetCommand(AbstractCommand):
"""
A Command instance which represents a GET request
"""
@property @property
def command(self): def command(self):
return "GET" return "GET"
@@ -221,6 +273,10 @@ class GetCommand(AbstractCommand):
class PostCommand(AbstractModifyCommand): class PostCommand(AbstractModifyCommand):
"""
A Command instance which represents a POST request
"""
@property @property
def command(self): def command(self):
return "POST" return "POST"
@@ -231,6 +287,10 @@ class PostCommand(AbstractModifyCommand):
class PutCommand(AbstractModifyCommand): class PutCommand(AbstractModifyCommand):
"""
A Command instance which represents a PUT request
"""
@property @property
def command(self): def command(self):
return "PUT" return "PUT"

View File

@@ -6,7 +6,7 @@ from urllib.parse import ParseResultBytes, ParseResult
from httplib import parser from httplib import parser
from httplib.exceptions import MethodNotAllowed, BadRequest, UnsupportedEncoding, NotImplemented, NotFound, \ from httplib.exceptions import MethodNotAllowed, BadRequest, UnsupportedEncoding, NotImplemented, NotFound, \
HTTPVersionNotSupported HTTPVersionNotSupported
from httplib.httpsocket import HTTPSocket, FORMAT from httplib.httpsocket import FORMAT
from httplib.message import RequestMessage as Message from httplib.message import RequestMessage as Message
from httplib.retriever import Retriever, PreambleRetriever from httplib.retriever import Retriever, PreambleRetriever
from server import command from server import command
@@ -23,12 +23,15 @@ class RequestHandler:
messages, parse, verify them and send a respond. messages, parse, verify them and send a respond.
""" """
conn: HTTPSocket conn: ServerSocket
def __init__(self, conn: socket, host): def __init__(self, conn: socket, host):
self.conn = ServerSocket(conn, host) self.conn = ServerSocket(conn, host)
def listen(self): def listen(self):
"""
Listen to incoming messages and process them.
"""
retriever = PreambleRetriever(self.conn) retriever = PreambleRetriever(self.conn)
@@ -42,6 +45,12 @@ class RequestHandler:
self._handle_message(retriever, line) self._handle_message(retriever, line)
def _handle_message(self, 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() lines = retriever.retrieve()
# Parse the request-line and headers # Parse the request-line and headers
@@ -80,7 +89,8 @@ class RequestHandler:
def _check_request_line(self, method: str, target: Union[ParseResultBytes, ParseResult], version): 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 method: HTTP request method
@param target: The request target @param target: The request target
@param version: The HTTP version @param version: The HTTP version
@@ -124,6 +134,7 @@ class RequestHandler:
def _has_body(self, headers): def _has_body(self, headers):
""" """
Check if the headers notify the existing of a message body. Check if the headers notify the existing of a message body.
@param headers: the headers to check @param headers: the headers to check
@return: True if the message has a body. False otherwise. @return: True if the message has a body. False otherwise.
""" """
@@ -138,6 +149,14 @@ class RequestHandler:
@staticmethod @staticmethod
def send_error(client: socket, code, message): 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 = f"HTTP/1.1 {code} {message}\r\n"
message += parser.get_date() + "\r\n" message += parser.get_date() + "\r\n"
message += "Content-Length: 0\r\n" message += "Content-Length: 0\r\n"