summaryrefslogtreecommitdiff
path: root/day7/http_handler.py
diff options
context:
space:
mode:
Diffstat (limited to 'day7/http_handler.py')
-rw-r--r--day7/http_handler.py137
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)