Improve documentation
This commit is contained in:
@@ -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"
|
||||||
|
@@ -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"
|
||||||
|
Reference in New Issue
Block a user