diff options
Diffstat (limited to 'day7/http_handler.py')
| -rw-r--r-- | day7/http_handler.py | 137 |
1 files changed, 137 insertions, 0 deletions
diff --git a/day7/http_handler.py b/day7/http_handler.py new file mode 100644 index 0000000..e5a551b --- /dev/null +++ b/day7/http_handler.py @@ -0,0 +1,137 @@ +from socket import socket, AF_INET, SOCK_STREAM, SHUT_RDWR +from time import strftime, gmtime + +from backend import run +from utils import validate_url, parse_cookies, parse_query, parse_headers, add_headers, NOT_FOUND, BAD_REQUEST +from templater import render_template +from config import * +import db + + +def log_func(func): + def wrapper(*args, **kwargs): + address = kwargs['address'] if 'address' in kwargs else args[0] + method = kwargs['method'] if 'method' in kwargs else args[1] + url = kwargs['url'] if 'url' in kwargs else args[2] + http_ver = kwargs['http_ver'] if 'http_ver' in kwargs else args[3] + + response = func(*args, **kwargs) + status = response.split('\n').pop(0).split()[1] + + log_line = f'{address[0]}: {strftime("[%d %b %Y %H:%M:%S]", gmtime())} "{method} {url} {http_ver}" Status: {status}' + print(log_line) + + return response + + return wrapper + + +@log_func +def process_request(address, method, url, http_ver, headers: dict, query): + if validate_url(url): + try: + cookies = parse_cookies(headers['Cookie']) + except (ValueError, KeyError): + cookies = {} + + response = run(method, url, cookies, query) + else: + response = add_headers(NOT_FOUND, '404 NOT FOUND') + + return response + + +def get_request(connection): + buffer = b'' + request = b'' + while not request.endswith(b'\r\n\r\n'): + data = connection.recv(1024).split(b'\r\n\r\n') + if len(data) == 1: + if not data[0]: + return None + request += data[0] + else: + request += data[0] + b'\r\n\r\n' + buffer += data[1] + + method, url, http_ver, headers = parse_headers(request.strip().decode()) + if 'Content-Type' in headers and headers['Content-Type'] == 'application/x-www-form-urlencoded': + length = int(headers['Content-Length']) - len(buffer) + while length > 0: + if length < CHUNK: + data = connection.recv(length) + length = 0 + else: + data = connection.recv(CHUNK) + length -= CHUNK + + buffer += data[0] + + query = parse_query(buffer.strip().decode()) if buffer else {} + + return method, url, http_ver, headers, query + + +def get_color(): + color = db.get_cookie('bg_color', 'white') + if color not in ['green', 'white']: + color = 'white' + return color + + +def handle_connection(connection, address): + try: + request = get_request(connection) + except ValueError as e: + response = add_headers(BAD_REQUEST, render_template( + TEXT_TEMPLATE_NAME, color=get_color(), + text=BAD_REQUEST + ('<br>' + str(e) if db.get_config_entry('show_errors') else '') + )) + connection.sendall(response.encode()) + return + + if request is None: + return + + method, url, http_ver, headers, query = request + + response = process_request(address, method, url, http_ver, headers, query) + connection.sendall(response.encode()) + + +def main(host, port): + sock = socket(AF_INET, SOCK_STREAM) + sock.bind((host, port)) + sock.listen(5) + print(f'* Server started on http://{host}:{port}/') + print('* Listening to new connections...') + + while True: + try: + connection, address = sock.accept() + + handle_connection(connection, address) + + connection.shutdown(SHUT_RDWR) + connection.close() + del connection + + except KeyboardInterrupt: + print('Stopping server...') + + try: + connection.shutdown(SHUT_RDWR) + connection.close() + except UnboundLocalError: + pass + + sock.shutdown(SHUT_RDWR) + sock.close() + + print('Server stopped') + break + + +if __name__ == '__main__': + HOST, PORT = ADDR = '0.0.0.0', 4002 + main(HOST, PORT) |