forked from nat/sludge
add logging, update readme
This commit is contained in:
parent
6175fafd20
commit
bdc96b5700
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,3 +1,6 @@
|
|||||||
|
config.yaml
|
||||||
|
log
|
||||||
|
|
||||||
# ---> Python
|
# ---> Python
|
||||||
# Byte-compiled / optimized / DLL files
|
# Byte-compiled / optimized / DLL files
|
||||||
__pycache__/
|
__pycache__/
|
||||||
|
62
README.md
62
README.md
@ -1,3 +1,63 @@
|
|||||||
# Sludge: webthing for natalieee.net
|
# sludge: webthing for natalieee.net
|
||||||
it rhymes with kludge.
|
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')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
```
|
||||||
|
@ -45,8 +45,7 @@ def remove_html_tags(input_string: str) -> str:
|
|||||||
|
|
||||||
def error_page(code: int) -> Response:
|
def error_page(code: int) -> Response:
|
||||||
type = ResponseCode(code)
|
type = ResponseCode(code)
|
||||||
print('error page called')
|
return Response(
|
||||||
aoeu= Response(
|
|
||||||
type,
|
type,
|
||||||
{'Content-Type': 'text/html'},
|
{'Content-Type': 'text/html'},
|
||||||
parse(f'''
|
parse(f'''
|
||||||
@ -61,8 +60,6 @@ def error_page(code: int) -> Response:
|
|||||||
</html>
|
</html>
|
||||||
''').encode('utf-8')
|
''').encode('utf-8')
|
||||||
)
|
)
|
||||||
print(aoeu)
|
|
||||||
return aoeu
|
|
||||||
|
|
||||||
def page(title, body):
|
def page(title, body):
|
||||||
return parse("""
|
return parse("""
|
||||||
|
22
src/lib/logger.py
Normal file
22
src/lib/logger.py
Normal 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')
|
||||||
|
|
@ -86,5 +86,10 @@ patchers: List[Patcher] = [
|
|||||||
response.code,
|
response.code,
|
||||||
response.headers,
|
response.headers,
|
||||||
uwuify(response.body).encode('utf-8')
|
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
|
||||||
]
|
]
|
||||||
|
@ -5,6 +5,7 @@ from .path import Path
|
|||||||
from .headers import Headers
|
from .headers import Headers
|
||||||
from .body import Body
|
from .body import Body
|
||||||
from .router import routes
|
from .router import routes
|
||||||
|
from .logger import log
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Request:
|
class Request:
|
||||||
@ -43,12 +44,13 @@ class Request:
|
|||||||
body_start = request_str.find('\r\n\r\n') + 4
|
body_start = request_str.find('\r\n\r\n') + 4
|
||||||
body = Body(request_bytes[body_start:], headers.get('Content-Type') or 'text/plain')
|
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)
|
return cls(method, path, version, headers, body)
|
||||||
|
|
||||||
def match(self):
|
def match(self):
|
||||||
for route in routes:
|
for route in routes:
|
||||||
if route.matches(self):
|
if route.matches(self):
|
||||||
print(route)
|
log.debug(f'matched {self} with {route}')
|
||||||
return route
|
return route
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from socket import socket
|
from socket import socket
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
from .responsecodes import ResponseCode
|
from .responsecodes import ResponseCode
|
||||||
|
from .logger import log
|
||||||
|
|
||||||
class Response:
|
class Response:
|
||||||
def __init__(self, code: ResponseCode, headers: Dict[str, str], body: bytes):
|
def __init__(self, code: ResponseCode, headers: Dict[str, str], body: bytes):
|
||||||
@ -16,6 +17,9 @@ class Response:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def send(self, client: socket) -> None:
|
def send(self, client: socket) -> None:
|
||||||
print(self)
|
log.debug(f'sending {self} to {client}')
|
||||||
client.sendall(self.build_response())
|
client.sendall(self.build_response())
|
||||||
client.close()
|
client.close()
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f'Response(code={self.code}, headers={self.headers}, body={self.body[:256]})'
|
||||||
|
@ -8,7 +8,9 @@ from .response import Response
|
|||||||
from .responsecodes import ResponseCode
|
from .responsecodes import ResponseCode
|
||||||
from .content import *
|
from .content import *
|
||||||
from .patchers import patchers
|
from .patchers import patchers
|
||||||
|
from .logger import log
|
||||||
import os
|
import os
|
||||||
|
import traceback
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Route:
|
class Route:
|
||||||
@ -28,7 +30,7 @@ class Route:
|
|||||||
return response
|
return response
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
log.error(tracebark.format_exc)
|
||||||
return error_page(500)
|
return error_page(500)
|
||||||
|
|
||||||
def matches(self, request: 'Request') -> bool:
|
def matches(self, request: 'Request') -> bool:
|
||||||
@ -46,7 +48,6 @@ routes = [
|
|||||||
[
|
[
|
||||||
(lambda form_data: (
|
(lambda form_data: (
|
||||||
(lambda time: (
|
(lambda time: (
|
||||||
print('\n\nFORM DATA!!!!',form_data,request, '\n\n'),
|
|
||||||
f:=open(f'./files/posts-to-homepage/post_{time}.txt', 'w'),
|
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.write(f"<i style='font-family: MapleMonoItalic'>{form_data['name']}</i>@{time}<br>{form_data['text']}<br><br>"),
|
||||||
f.close()
|
f.close()
|
||||||
@ -60,7 +61,7 @@ routes = [
|
|||||||
parse_file('./home.html').encode('utf-8')
|
parse_file('./home.html').encode('utf-8')
|
||||||
][1]
|
][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(
|
Route(
|
||||||
lambda path: os.path.isdir('.' + path.path),
|
lambda path: os.path.isdir('.' + path.path),
|
||||||
|
@ -1,21 +1,30 @@
|
|||||||
import socket
|
import socket
|
||||||
import threading
|
import threading
|
||||||
|
import traceback
|
||||||
from typing import Callable
|
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:
|
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)
|
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
while True:
|
log.info(f'server started on {port}')
|
||||||
try:
|
try:
|
||||||
server_socket.bind((address, port))
|
server_socket.bind((address, port))
|
||||||
server_socket.listen(1)
|
server_socket.listen(1)
|
||||||
server_socket = wrapper(server_socket)
|
server_socket = wrapper(server_socket)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
|
try:
|
||||||
conn, addr = server_socket.accept()
|
conn, addr = server_socket.accept()
|
||||||
client_connection = threading.Thread(target=callback, args=(conn, addr))
|
client_connection = threading.Thread(target=callback, args=(conn, addr))
|
||||||
client_connection.start()
|
client_connection.start()
|
||||||
|
|
||||||
except Exception as e:
|
except Exception:
|
||||||
print(e)
|
log.warn(traceback.format_exc())
|
||||||
|
|
||||||
finally: server_socket.close()
|
except:
|
||||||
|
log.critical(traceback.format_exc())
|
||||||
|
|
||||||
|
finally:
|
||||||
|
server_socket.close()
|
||||||
|
log.info(f'server on {port} shut down')
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from lib import Request, serve
|
from lib import Request, serve
|
||||||
|
from lib.logger import log
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
import threading
|
import threading
|
||||||
import socket
|
import socket
|
||||||
@ -16,7 +17,6 @@ def handle_client(client: socket.socket, addr: Tuple[str, int]) -> None:
|
|||||||
|
|
||||||
while (data := client.recv(1024)):
|
while (data := client.recv(1024)):
|
||||||
request += data
|
request += data
|
||||||
print(len(data), data)
|
|
||||||
|
|
||||||
if len(data) < 1024: break
|
if len(data) < 1024: break
|
||||||
|
|
||||||
@ -25,6 +25,8 @@ def handle_client(client: socket.socket, addr: Tuple[str, int]) -> None:
|
|||||||
.execute(request, client, addr) \
|
.execute(request, client, addr) \
|
||||||
.send(client)
|
.send(client)
|
||||||
|
|
||||||
|
log.info('destroy thread')
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
http_thread = threading.Thread(name='http', target=serve, args=('0.0.0.0', config['http-port'], handle_client))
|
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: [
|
https_thread = threading.Thread(name='https', target=serve, args=('0.0.0.0', config['https-port'], handle_client), kwargs=dict(wrapper=lambda socket: [
|
||||||
|
Loading…
Reference in New Issue
Block a user