1
0
forked from nat/sludge

add logging, update readme

This commit is contained in:
gnat 2024-08-14 14:00:27 -07:00
parent 6175fafd20
commit bdc96b5700
10 changed files with 128 additions and 23 deletions

3
.gitignore vendored
View File

@ -1,3 +1,6 @@
config.yaml
log
# ---> Python
# Byte-compiled / optimized / DLL files
__pycache__/

View File

@ -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.<br>
ex:
```html
<p>lorem ipsum $[echo foo]</p>
```
would resolve to
```html
<p>lorem ipsum foo</p>
```
### variables in html
in addition to the above, you can have variables embedded in html.
```html
<p>lorem ipsum {aoeu}</p>
```
would normally resolve to
```html
<p>lorem ipsum {aoeu}</p>
```
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
<p>lorem ipsum foo</p>
```
<br>
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')
)
)
```

View File

@ -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:
</html>
''').encode('utf-8')
)
print(aoeu)
return aoeu
def page(title, body):
return parse("""

22
src/lib/logger.py Normal file
View File

@ -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')

View File

@ -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' + ' (/&#x73;&#x6c;&#x28c;&#x64;&#x361;&#x292;/)' 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
]

View File

@ -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):

View File

@ -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]})'

View File

@ -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"<i style='font-family: MapleMonoItalic'>{form_data['name']}</i>@{time}<br>{form_data['text']}<br><br>"),
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),

View File

@ -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')

View File

@ -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: [