diff --git a/.gitignore b/.gitignore index 7e783a8..609ea03 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +config.yaml +log + # ---> Python # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/README.md b/README.md index 97e5d14..0ba6d83 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,63 @@ -# Sludge: webthing for natalieee.net +# sludge: webthing for natalieee.net it rhymes with kludge. +## config +```yaml +ssl-key: ./key.pem +ssl-cert: ./cert.pem +http-port: 5000 +https-port: # 5001 +file-dir: 'site' +``` +ssl-{cert,key} are self explanatory. +if the value of https? port is commented or not present, it will not run the https? server on that port. +file-dir is where the files for the site can be found. +the above config is the config for the local version of my website, which is used for testing. +it runs an http server on 5000, but no https server. + +## serving files +- src/lib/router.py has the routing config for my website in it. it is easy to modify to your usecase. +- src/lib/patchers.py has the patching config for my website in it. it is also easy to modify. + +## dynamic content +### embedded bash scripting +sludge has support for inline bash inside of html documents.
+ex: +```html +

lorem ipsum $[echo foo]

+``` +would resolve to +```html +

lorem ipsum foo

+``` + +### variables in html +in addition to the above, you can have variables embedded in html. +```html +

lorem ipsum {aoeu}

+``` +would normally resolve to +```html +

lorem ipsum {aoeu}

+``` +however, if we assume that this text is in a file `foo.html`, then we can serve that file such that +```py +parse_file('./foo.html', dict(aoeu='foo')) +``` +in which case it would resolve to +```html +

lorem ipsum foo

+``` +
+in practice, this may be seen here: +```py +Route( + lambda path: os.path.isdir('.' + path.path), + [Method.GET], + lambda request, *_: Response( + ResponseCode.OK, + {'Content-Type': 'text/html'}, + parse_file('./dir_index.html', dict(path='.' + request.path.path)).encode('utf-8') + ) +) +``` diff --git a/src/lib/content.py b/src/lib/content.py index e088750..8dda7cb 100644 --- a/src/lib/content.py +++ b/src/lib/content.py @@ -45,8 +45,7 @@ def remove_html_tags(input_string: str) -> str: def error_page(code: int) -> Response: type = ResponseCode(code) - print('error page called') - aoeu= Response( + return Response( type, {'Content-Type': 'text/html'}, parse(f''' @@ -61,8 +60,6 @@ def error_page(code: int) -> Response: ''').encode('utf-8') ) - print(aoeu) - return aoeu def page(title, body): return parse(""" diff --git a/src/lib/logger.py b/src/lib/logger.py new file mode 100644 index 0000000..3b77d67 --- /dev/null +++ b/src/lib/logger.py @@ -0,0 +1,22 @@ +import logging + +log = logging.getLogger() + +log.setLevel(logging.DEBUG) + +formatter = logging.Formatter('%(asctime)s | %(name)s | %(threadName)s | %(levelname)s: %(message)s') + +file_logger = logging.FileHandler('log') +stream_logger = logging.StreamHandler() + +file_logger.setLevel(logging.DEBUG) +stream_logger.setLevel(logging.INFO) + +file_logger.setFormatter(formatter) +stream_logger.setFormatter(formatter) + +log.addHandler(file_logger) +log.addHandler(stream_logger) + +log.info('log initialized') + diff --git a/src/lib/patchers.py b/src/lib/patchers.py index babb8be..de5e1a9 100644 --- a/src/lib/patchers.py +++ b/src/lib/patchers.py @@ -86,5 +86,10 @@ patchers: List[Patcher] = [ response.code, response.headers, uwuify(response.body).encode('utf-8') - ) if 'text/html' in response.headers.values() and is_subdict({'uwu': 'true'}, request.path.params) else response + ) if 'text/html' in response.headers.values() and is_subdict({'uwu': 'true'}, request.path.params) else response, + lambda response, request: Response( + response.code, + response.headers, + re.sub(r'sludge', lambda match: 'sludge' + ' (/slʌd͡ʒ/)' if random.randint(0, 5) < 1 else 'sludge', response.body.decode('utf-8')).encode('utf-8') + ) if 'text/html' in response.headers.values() else response ] diff --git a/src/lib/request.py b/src/lib/request.py index 24d3847..68728d0 100644 --- a/src/lib/request.py +++ b/src/lib/request.py @@ -5,6 +5,7 @@ from .path import Path from .headers import Headers from .body import Body from .router import routes +from .logger import log @dataclass class Request: @@ -43,12 +44,13 @@ class Request: body_start = request_str.find('\r\n\r\n') + 4 body = Body(request_bytes[body_start:], headers.get('Content-Type') or 'text/plain') + log.info(f'received request for {path.path} from {headers.get('X-Real-IP')}') return cls(method, path, version, headers, body) def match(self): for route in routes: - if route.matches(self): - print(route) + if route.matches(self): + log.debug(f'matched {self} with {route}') return route def __repr__(self): diff --git a/src/lib/response.py b/src/lib/response.py index bab79b5..bbd271b 100644 --- a/src/lib/response.py +++ b/src/lib/response.py @@ -1,6 +1,7 @@ from socket import socket from typing import Dict from .responsecodes import ResponseCode +from .logger import log class Response: def __init__(self, code: ResponseCode, headers: Dict[str, str], body: bytes): @@ -16,6 +17,9 @@ class Response: ) def send(self, client: socket) -> None: - print(self) + log.debug(f'sending {self} to {client}') client.sendall(self.build_response()) client.close() + + def __repr__(self): + return f'Response(code={self.code}, headers={self.headers}, body={self.body[:256]})' diff --git a/src/lib/router.py b/src/lib/router.py index 51c62ff..5a9cbcb 100644 --- a/src/lib/router.py +++ b/src/lib/router.py @@ -8,7 +8,9 @@ from .response import Response from .responsecodes import ResponseCode from .content import * from .patchers import patchers +from .logger import log import os +import traceback @dataclass class Route: @@ -28,7 +30,7 @@ class Route: return response except Exception as e: - print(e) + log.error(tracebark.format_exc) return error_page(500) def matches(self, request: 'Request') -> bool: @@ -46,7 +48,6 @@ routes = [ [ (lambda form_data: ( (lambda time: ( - print('\n\nFORM DATA!!!!',form_data,request, '\n\n'), f:=open(f'./files/posts-to-homepage/post_{time}.txt', 'w'), f.write(f"{form_data['name']}@{time}
{form_data['text']}

"), f.close() @@ -60,7 +61,7 @@ routes = [ parse_file('./home.html').encode('utf-8') ][1] )) - ) if len(request.body.data) > 0 or request.method != Method.POST else error_page(ResponseCode.BAD_REQUEST) + ) if len(request.body.data) > 0 or request.method != Method.POST else error_page(400) ), Route( lambda path: os.path.isdir('.' + path.path), diff --git a/src/lib/server.py b/src/lib/server.py index e115af3..ec4a6d7 100644 --- a/src/lib/server.py +++ b/src/lib/server.py @@ -1,21 +1,30 @@ import socket import threading +import traceback from typing import Callable +from .logger import log + def serve(address: str, port: int, callback: Callable, wrapper: Callable[[socket.socket], socket.socket] = lambda s: s) -> None: server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - while True: - try: - server_socket.bind((address, port)) - server_socket.listen(1) - server_socket = wrapper(server_socket) + log.info(f'server started on {port}') + try: + server_socket.bind((address, port)) + server_socket.listen(1) + server_socket = wrapper(server_socket) - while True: + while True: + try: conn, addr = server_socket.accept() client_connection = threading.Thread(target=callback, args=(conn, addr)) client_connection.start() - except Exception as e: - print(e) - - finally: server_socket.close() + except Exception: + log.warn(traceback.format_exc()) + + except: + log.critical(traceback.format_exc()) + + finally: + server_socket.close() + log.info(f'server on {port} shut down') diff --git a/src/main.py b/src/main.py index c31bef0..e2fa5f5 100644 --- a/src/main.py +++ b/src/main.py @@ -1,4 +1,5 @@ from lib import Request, serve +from lib.logger import log from typing import Tuple import threading import socket @@ -16,7 +17,6 @@ def handle_client(client: socket.socket, addr: Tuple[str, int]) -> None: while (data := client.recv(1024)): request += data - print(len(data), data) if len(data) < 1024: break @@ -25,6 +25,8 @@ def handle_client(client: socket.socket, addr: Tuple[str, int]) -> None: .execute(request, client, addr) \ .send(client) + log.info('destroy thread') + def main() -> None: http_thread = threading.Thread(name='http', target=serve, args=('0.0.0.0', config['http-port'], handle_client)) https_thread = threading.Thread(name='https', target=serve, args=('0.0.0.0', config['https-port'], handle_client), kwargs=dict(wrapper=lambda socket: [