Fix small issues, improve error handling and documentation
This commit is contained in:
@@ -2,34 +2,58 @@ class HTTPException(Exception):
|
||||
""" Base class for HTTP exceptions """
|
||||
|
||||
|
||||
class InvalidResponse(HTTPException):
|
||||
""" Response message cannot be parsed """
|
||||
class UnhandledHTTPCode(Exception):
|
||||
status_code: str
|
||||
headers: str
|
||||
cause: str
|
||||
|
||||
def __init(self, message):
|
||||
def __init__(self, status, headers, cause):
|
||||
self.status_code = status
|
||||
self.headers = headers
|
||||
self.cause = cause
|
||||
|
||||
|
||||
class InvalidResponse(HTTPException):
|
||||
"""
|
||||
Response message cannot be parsed
|
||||
"""
|
||||
|
||||
def __init__(self, message):
|
||||
self.message = message
|
||||
|
||||
|
||||
class InvalidStatusLine(HTTPException):
|
||||
""" Response status line is invalid """
|
||||
"""
|
||||
Response status line is invalid
|
||||
"""
|
||||
|
||||
def __init(self, line):
|
||||
def __init__(self, line):
|
||||
self.line = line
|
||||
|
||||
|
||||
class UnsupportedEncoding(HTTPException):
|
||||
""" Encoding not supported """
|
||||
"""
|
||||
Encoding not supported
|
||||
"""
|
||||
|
||||
def __init(self, enc_type, encoding):
|
||||
def __init__(self, enc_type, encoding):
|
||||
self.enc_type = enc_type
|
||||
self.encoding = encoding
|
||||
|
||||
class UnsupportedProtocol(HTTPException):
|
||||
"""
|
||||
Protocol is not supported
|
||||
"""
|
||||
def __init__(self, protocol):
|
||||
self.protocol = protocol
|
||||
|
||||
|
||||
class IncompleteResponse(HTTPException):
|
||||
def __init(self, cause):
|
||||
def __init__(self, cause):
|
||||
self.cause = cause
|
||||
|
||||
|
||||
class HTTPServerException(Exception):
|
||||
class HTTPServerException(HTTPException):
|
||||
""" Base class for HTTP Server exceptions """
|
||||
status_code: str
|
||||
message: str
|
||||
@@ -68,7 +92,7 @@ class MethodNotAllowed(HTTPServerException):
|
||||
status_code = 405
|
||||
message = "Method Not Allowed"
|
||||
|
||||
def __init(self, allowed_methods):
|
||||
def __init__(self, allowed_methods):
|
||||
self.allowed_methods = allowed_methods
|
||||
|
||||
|
||||
|
@@ -6,7 +6,7 @@ from urllib.parse import SplitResult
|
||||
class Message(ABC):
|
||||
version: str
|
||||
headers: Dict[str, str]
|
||||
raw: str
|
||||
raw: [str]
|
||||
body: bytes
|
||||
|
||||
def __init__(self, version: str, headers: Dict[str, str], raw=None, body: bytes = None):
|
||||
|
@@ -1,4 +1,6 @@
|
||||
import logging
|
||||
import os
|
||||
import pathlib
|
||||
import re
|
||||
import urllib
|
||||
from typing import Dict
|
||||
@@ -85,6 +87,11 @@ def parse_request_line(line: str):
|
||||
|
||||
|
||||
def parse_headers(lines):
|
||||
"""
|
||||
Parses the lines from the `lines` iterator as headers.
|
||||
@param lines: iterator to retrieve the lines from.
|
||||
@return: A dictionary with header as key and value as value.
|
||||
"""
|
||||
headers = []
|
||||
|
||||
try:
|
||||
@@ -127,17 +134,21 @@ def parse_headers(lines):
|
||||
def check_next_header(headers, next_header: str, next_value: str):
|
||||
if next_header == "content-length":
|
||||
if "content-length" in headers:
|
||||
logging.error("Multiple content-length headers specified")
|
||||
raise InvalidResponse()
|
||||
raise InvalidResponse("Multiple content-length headers specified")
|
||||
if not next_value.isnumeric() or int(next_value) <= 0:
|
||||
logging.error("Invalid content-length value: %r", next_value)
|
||||
raise InvalidResponse()
|
||||
raise InvalidResponse(f"Invalid content-length value: {next_value}")
|
||||
|
||||
|
||||
def parse_uri(uri: str):
|
||||
"""
|
||||
Parse the specified URI into the host, port and path.
|
||||
If the URI is invalid, this method will try to create one.
|
||||
@param uri: the URI to be parsed
|
||||
@return: A tuple with the host, port and path
|
||||
"""
|
||||
parsed = urlsplit(uri)
|
||||
|
||||
# If there is no netloc, the given string is not a valid URI, so split on /
|
||||
# If there is no hostname, the given string is not a valid URI, so split on /
|
||||
if parsed.hostname:
|
||||
host = parsed.hostname
|
||||
path = parsed.path
|
||||
@@ -180,6 +191,12 @@ def urljoin(base, url):
|
||||
|
||||
|
||||
def get_charset(headers: Dict[str, str]):
|
||||
"""
|
||||
Returns the charset of the content from the headers if found. Otherwise returns `FORMAT`
|
||||
|
||||
@param headers: the headers to retrieve the charset from
|
||||
@return: A charset
|
||||
"""
|
||||
if "content-type" in headers:
|
||||
content_type = headers["content-type"]
|
||||
match = re.search(r"charset\s*=\s*([a-z\-0-9]*)", content_type, re.I)
|
||||
@@ -187,3 +204,17 @@ def get_charset(headers: Dict[str, str]):
|
||||
return match.group(1)
|
||||
|
||||
return FORMAT
|
||||
|
||||
|
||||
def get_relative_save_path(path: str):
|
||||
"""
|
||||
Returns the specified path relative to the working directory.
|
||||
|
||||
@param path: the path to compute
|
||||
@return: the relative path
|
||||
"""
|
||||
|
||||
path_obj = pathlib.PurePath(path)
|
||||
root = pathlib.PurePath(os.getcwd())
|
||||
rel = path_obj.relative_to(root)
|
||||
return str(rel)
|
||||
|
Reference in New Issue
Block a user