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 + ('
' + 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)