From ec5d5d02798ee43aa0af1a26a170deaeb722e34f Mon Sep 17 00:00:00 2001 From: UltraQbik Date: Fri, 23 Aug 2024 02:21:41 +0300 Subject: Add status codes --- src/status_code.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/status_code.py (limited to 'src') diff --git a/src/status_code.py b/src/status_code.py new file mode 100644 index 0000000..e4b826b --- /dev/null +++ b/src/status_code.py @@ -0,0 +1,23 @@ +class StatusCode: + """ + HTML status code + """ + + def __init__(self, code: int, message: str): + self.code: int = code + self.message: str = message + + def __bytes__(self): + return f"{self.code} {self.message}".encode("ascii") + + +# Status codes! +STATUS_CODE_OK = StatusCode(200, "OK") +STATUS_CODE_BAD_REQUEST = StatusCode(400, "Bad Request") +STATUS_CODE_UNAUTHORIZED = StatusCode(401, "Unauthorized") +STATUS_CODE_FORBIDDEN = StatusCode(403, "Forbidden") +STATUS_CODE_NOT_FOUND = StatusCode(404, "Not Found") +STATUS_CODE_PAYLOAD_TOO_LARGE = StatusCode(413, "Payload Too Large") +STATUS_CODE_URI_TOO_LONG = StatusCode(414, "URI Too Long") +STATUS_CODE_IM_A_TEAPOT = StatusCode(418, "I'm a teapot") # I followed mozilla's dev page, it was there +STATUS_CODE_FUNNY_NUMBER = StatusCode(6969, "UwU") -- cgit 1.4.1 From 82178d2facb95ee953ab1e97f5ee15f94b1d3472 Mon Sep 17 00:00:00 2001 From: UltraQbik Date: Fri, 23 Aug 2024 17:39:20 +0300 Subject: Basic responses working --- main.py | 91 ++-------------------------------------------------------- src/request.py | 40 ++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 88 deletions(-) (limited to 'src') diff --git a/main.py b/main.py index 18772f3..735e427 100644 --- a/main.py +++ b/main.py @@ -5,14 +5,12 @@ The mighty silly webserver written in python for no good reason import ssl import time -import gzip import socket -import brotli import signal import threading from src import APIv1 +from src.request import * from src.status_code import * -from src.request import Request from src.minimizer import minimize_html @@ -115,7 +113,8 @@ class HTTPServer: if request is None: break - print(request, end="\n\n") + with open("www/index.html", "rb") as file: + send_response(client, file.read(), STATUS_CODE_OK) except TimeoutError: print("Client timeout") break @@ -264,90 +263,6 @@ class HTTPServer: # # # send response to the client # HTTPServer._send(client, status_code, data.encode("ascii")) -# -# @staticmethod -# def _send(client: ssl.SSLSocket, response: int, data: bytes = None, headers: dict[str, str] = None): -# """ -# Sends client response code + headers + data -# :param client: client -# :param response: response code -# :param data: data -# :param headers: headers to include -# """ -# -# # if data was not given -# if data is None: -# data = bytes() -# -# # if headers were not given -# if headers is None: -# headers = dict() -# -# # check for 'content-encoding' header -# if headers.get("Content-Encoding") == "br": -# data = brotli.compress(data) -# -# elif headers.get("Content-Encoding") == "gzip": -# data = gzip.compress(data) -# -# # add 'Content-Length' header if not present -# if headers.get("Content-Length") is None: -# headers["Content-Length"] = len(data) -# -# # format headers -# byte_header = bytearray() -# for key, value in headers.items(): -# byte_header += f"{key}: {value}\r\n".encode("ascii") -# -# # send response to the client -# client.sendall( -# b'HTTP/1.1 ' + -# get_response_code(response) + -# b'\r\n' + -# byte_header + # if empty, we'll just get b'\r\n\r\n' -# b'\r\n' + -# data -# ) -# -# def _recvall(self, client: ssl.SSLSocket) -> bytes: -# """ -# Receive All (just receives the whole message, instead of 1 packet at a time) -# """ -# -# # create message buffer -# buffer: bytearray = bytearray() -# -# # start fetching the message -# while True: -# try: -# # fetch packet -# message = ssl_sock_recv(client, self.packet_size) -# except OSError: -# break -# -# # that happens when user stops loading the page -# if message == b'': -# break -# -# # append fetched message to the buffer -# buffer += message -# -# # check for EoF -# if buffer[-4:] == b'\r\n\r\n': -# # return the received message -# return buffer -# -# # return empty buffer on error -# return b'' -# -# def _close_client(self, client: socket.socket): -# """ -# Closes a client -# """ -# -# client.close() -# if client in self.clients: -# self.clients.remove(client) def main(): diff --git a/src/request.py b/src/request.py index 0437dfd..d9e8a86 100644 --- a/src/request.py +++ b/src/request.py @@ -1,3 +1,10 @@ +import gzip +import brotli +from typing import Any +from ssl import SSLSocket +from src.status_code import StatusCode + + class Request: """ Just a request @@ -54,3 +61,36 @@ class Request: def __str__(self): return '\n'.join([f"{key}: {val}" for key, val in self.__dict__.items()]) + + +def send_response(sock: SSLSocket, data: bytes, status: StatusCode, headers: dict[str, Any] = None): + """ + Sends response to client. + Probably ran inside a daemonic thread + :param sock: client's socket + :param data: raw data to send + :param status: status code + :param headers: headers to include + """ + + # process header data + if headers is None: + headers = dict() + if headers.get("Content-Encoding") is not None: + if headers["Content-Encoding"] == "br": + data = brotli.compress(data) + elif headers["Content-Encoding"] == "gzip": + data = gzip.compress(data) + if headers.get("Content-Length") is None: + headers["Content-Length"] = len(data) + if headers.get("Connection") is None: + headers["Connection"] = "close" + + # generate basic message + message = b'HTTP/1.1 ' + status.__bytes__() + b'\r\n' + for key, value in headers.items(): + message += f"{key}: {value}\r\n".encode("ascii") + message += b'\r\n' + data + + # send message + sock.sendall(message) -- cgit 1.4.1 From c6a546b8677c512dc9ec4f48cea1d05dca266427 Mon Sep 17 00:00:00 2001 From: UltraQbik Date: Fri, 23 Aug 2024 17:42:42 +0300 Subject: Make restricted members Make status codes immutable (I think that's how you do it?) --- src/status_code.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/status_code.py b/src/status_code.py index e4b826b..8fd165a 100644 --- a/src/status_code.py +++ b/src/status_code.py @@ -4,11 +4,19 @@ class StatusCode: """ def __init__(self, code: int, message: str): - self.code: int = code - self.message: str = message + self._code: int = code + self._message: str = message def __bytes__(self): - return f"{self.code} {self.message}".encode("ascii") + return f"{self._code} {self._message}".encode("ascii") + + @property + def code(self): + return self._code + + @property + def message(self): + return self._message # Status codes! -- cgit 1.4.1 From 0169a1834d9cd228fb2ee2810eee9599748b3b67 Mon Sep 17 00:00:00 2001 From: UltraQbik Date: Fri, 23 Aug 2024 20:17:43 +0300 Subject: Add response --- src/request.py | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'src') diff --git a/src/request.py b/src/request.py index d9e8a86..b0fb13f 100644 --- a/src/request.py +++ b/src/request.py @@ -63,6 +63,17 @@ class Request: return '\n'.join([f"{key}: {val}" for key, val in self.__dict__.items()]) +class Response: + """ + Server response + """ + + def __init__(self, data: bytes, status: StatusCode, headers: dict[str, Any] = None): + self.data: bytes = data + self.status: StatusCode = status + self.headers: dict[str, Any] = headers if headers is not None else dict() + + def send_response(sock: SSLSocket, data: bytes, status: StatusCode, headers: dict[str, Any] = None): """ Sends response to client. -- cgit 1.4.1 From 9a3c6ea6802f26a479bb91cc6a9ded9d7cb193c7 Mon Sep 17 00:00:00 2001 From: UltraQbik Date: Fri, 23 Aug 2024 20:36:49 +0300 Subject: Add thing to status code --- src/status_code.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/status_code.py b/src/status_code.py index 8fd165a..a63712c 100644 --- a/src/status_code.py +++ b/src/status_code.py @@ -10,6 +10,9 @@ class StatusCode: def __bytes__(self): return f"{self._code} {self._message}".encode("ascii") + def __str__(self): + return f"{self._code} {self._message}" + @property def code(self): return self._code -- cgit 1.4.1 From 700985f43078d4f9c79da0446a93e6c90d409a49 Mon Sep 17 00:00:00 2001 From: UltraQbik Date: Fri, 23 Aug 2024 20:43:20 +0300 Subject: Update API to give response --- src/APIv1.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/APIv1.py b/src/APIv1.py index db8c4f3..882a13d 100644 --- a/src/APIv1.py +++ b/src/APIv1.py @@ -1,6 +1,6 @@ import random -from ssl import SSLSocket -from src.request import Request +from src.request import * +from src.status_code import * API_FILE_RANDOM_MIN_SIZE_LIMIT = 1 @@ -59,7 +59,7 @@ def decode_size(size: str) -> int: return size -def respond(client: SSLSocket, request: Request) -> tuple[int, bytes, dict]: +def api_call(client: SSLSocket, request: Request) -> Response: """ Respond to clients API request """ @@ -78,10 +78,10 @@ def respond(client: SSLSocket, request: Request) -> tuple[int, bytes, dict]: # check size if size < API_FILE_RANDOM_MIN_SIZE_LIMIT or size > API_FILE_RANDOM_MAX_SIZE_LIMIT: - return 400, b'', {} + return Response(b'', STATUS_CODE_BAD_REQUEST) - return 200, random_data_gen(size), {} + return Response(random_data_gen(size), STATUS_CODE_OK) else: - return 400, b'', {} + return Response(b'', STATUS_CODE_BAD_REQUEST) else: - return 400, b'', {} + return Response(b'', STATUS_CODE_BAD_REQUEST) -- cgit 1.4.1 From fb24bdfccedcc95e9cdfb97db375120933bb451c Mon Sep 17 00:00:00 2001 From: UltraQbik Date: Fri, 23 Aug 2024 21:05:56 +0300 Subject: remove useless code --- src/request.py | 35 +---------------------------------- 1 file changed, 1 insertion(+), 34 deletions(-) (limited to 'src') diff --git a/src/request.py b/src/request.py index b0fb13f..889b894 100644 --- a/src/request.py +++ b/src/request.py @@ -71,37 +71,4 @@ class Response: def __init__(self, data: bytes, status: StatusCode, headers: dict[str, Any] = None): self.data: bytes = data self.status: StatusCode = status - self.headers: dict[str, Any] = headers if headers is not None else dict() - - -def send_response(sock: SSLSocket, data: bytes, status: StatusCode, headers: dict[str, Any] = None): - """ - Sends response to client. - Probably ran inside a daemonic thread - :param sock: client's socket - :param data: raw data to send - :param status: status code - :param headers: headers to include - """ - - # process header data - if headers is None: - headers = dict() - if headers.get("Content-Encoding") is not None: - if headers["Content-Encoding"] == "br": - data = brotli.compress(data) - elif headers["Content-Encoding"] == "gzip": - data = gzip.compress(data) - if headers.get("Content-Length") is None: - headers["Content-Length"] = len(data) - if headers.get("Connection") is None: - headers["Connection"] = "close" - - # generate basic message - message = b'HTTP/1.1 ' + status.__bytes__() + b'\r\n' - for key, value in headers.items(): - message += f"{key}: {value}\r\n".encode("ascii") - message += b'\r\n' + data - - # send message - sock.sendall(message) + self.headers: dict[str, Any] = headers if headers is not None else dict() \ No newline at end of file -- cgit 1.4.1 From ac08c5e84153160f8221b30854a9b7029b8c3fe2 Mon Sep 17 00:00:00 2001 From: UltraQbik Date: Fri, 23 Aug 2024 21:09:31 +0300 Subject: Update API --- src/APIv1.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/APIv1.py b/src/APIv1.py index 882a13d..c9ba2da 100644 --- a/src/APIv1.py +++ b/src/APIv1.py @@ -65,13 +65,11 @@ def api_call(client: SSLSocket, request: Request) -> Response: """ # decode API request - split_path = request.path.split("/") - api_level1 = split_path[2] - api_request = split_path[3] + split_path = request.path.split("/", maxsplit=16)[1:] # do something with it (oh god) - if api_level1 == "file": - if api_request == "random": + if len(split_path) > 1 and split_path[1] == "file": + if len(split_path) > 2 and split_path[2] == "random": # get size size_str = request.path_args.get("size", "16mib") size = decode_size(size_str) -- cgit 1.4.1 From a37413d9077a6ae6c711a4583b269251a42d9257 Mon Sep 17 00:00:00 2001 From: UltraQbik Date: Fri, 23 Aug 2024 21:10:51 +0300 Subject: Update response class --- src/request.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/request.py b/src/request.py index 889b894..28d7c14 100644 --- a/src/request.py +++ b/src/request.py @@ -68,7 +68,8 @@ class Response: Server response """ - def __init__(self, data: bytes, status: StatusCode, headers: dict[str, Any] = None): + def __init__(self, data: bytes, status: StatusCode, headers: dict[str, Any] = None, **kwargs): self.data: bytes = data self.status: StatusCode = status - self.headers: dict[str, Any] = headers if headers is not None else dict() \ No newline at end of file + self.headers: dict[str, Any] = headers if headers is not None else dict() + self.compress: bool = kwargs.get("compress", False) -- cgit 1.4.1 From e9f7ae54d64be673117e4cabe32badd01048bb69 Mon Sep 17 00:00:00 2001 From: UltraQbik Date: Fri, 23 Aug 2024 21:11:20 +0300 Subject: Update API and response class --- src/APIv1.py | 2 +- src/request.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/APIv1.py b/src/APIv1.py index c9ba2da..f163c5b 100644 --- a/src/APIv1.py +++ b/src/APIv1.py @@ -78,7 +78,7 @@ def api_call(client: SSLSocket, request: Request) -> Response: if size < API_FILE_RANDOM_MIN_SIZE_LIMIT or size > API_FILE_RANDOM_MAX_SIZE_LIMIT: return Response(b'', STATUS_CODE_BAD_REQUEST) - return Response(random_data_gen(size), STATUS_CODE_OK) + return Response(random_data_gen(size), STATUS_CODE_OK, compress=False) else: return Response(b'', STATUS_CODE_BAD_REQUEST) else: diff --git a/src/request.py b/src/request.py index 28d7c14..df0d433 100644 --- a/src/request.py +++ b/src/request.py @@ -72,4 +72,4 @@ class Response: self.data: bytes = data self.status: StatusCode = status self.headers: dict[str, Any] = headers if headers is not None else dict() - self.compress: bool = kwargs.get("compress", False) + self.compress: bool = kwargs.get("compress", True) -- cgit 1.4.1 From 9b5c0ee8f79b24a0a269be2a9984dc09244fd278 Mon Sep 17 00:00:00 2001 From: UltraQbik Date: Fri, 23 Aug 2024 21:12:25 +0300 Subject: Update docs --- src/request.py | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src') diff --git a/src/request.py b/src/request.py index df0d433..79c545a 100644 --- a/src/request.py +++ b/src/request.py @@ -69,6 +69,14 @@ class Response: """ def __init__(self, data: bytes, status: StatusCode, headers: dict[str, Any] = None, **kwargs): + """ + + :param data: response data + :param status: response status code + :param headers: headers to include + :param kwarg: compress - whether to compress data or not + """ + self.data: bytes = data self.status: StatusCode = status self.headers: dict[str, Any] = headers if headers is not None else dict() -- cgit 1.4.1 From fd8c46cac1c914851613cac425d2afe68d360d9d Mon Sep 17 00:00:00 2001 From: UltraQbik Date: Fri, 23 Aug 2024 21:27:04 +0300 Subject: Remove some things --- src/request.py | 2 -- 1 file changed, 2 deletions(-) (limited to 'src') diff --git a/src/request.py b/src/request.py index 79c545a..003783a 100644 --- a/src/request.py +++ b/src/request.py @@ -1,5 +1,3 @@ -import gzip -import brotli from typing import Any from ssl import SSLSocket from src.status_code import StatusCode -- cgit 1.4.1