updated path handling so that only root paths get registered with the server. root handler functions can delegate however they want to.

This commit is contained in:
Michael Woods
2025-01-04 14:22:35 -05:00
parent bb5f07dbad
commit 8feadeef42
4 changed files with 141 additions and 10 deletions

View File

@@ -193,6 +193,9 @@ class Request(Message):
if ('p' in msg.data) and (type(msg.data['p']) is not str):
raise ValueError("Path of Request must be a string.")
if 'p' in self.data:
self.data['p'] = str(self.data['p']).strip().lower()
if 'm' in msg.data:
if type(msg.data['m']) is not bytes:
raise ValueError("Method of Request must be bytes.")
@@ -207,7 +210,7 @@ class Request(Message):
@path.setter
def path(self, path: str):
self.data['p'] = str(path.strip())
self.data['p'] = str(path).strip().lower()
@property
def method(self) -> Method:

View File

@@ -1,6 +1,7 @@
import pe.app
import packetserver.common
from packetserver.server.constants import default_server_config
from packetserver.server.bulletin import init_bulletins
from copy import deepcopy
import ax25
from pathlib import Path
@@ -13,6 +14,7 @@ from packetserver.server.requests import standard_handlers
import logging
import signal
import time
from typing import Callable
class Server:
@@ -46,6 +48,7 @@ class Server:
conn.root.config['blacklist'] = PersistentList()
if 'users' not in conn.root():
conn.root.users = OOBTree()
init_bulletins(conn.root())
self.app = pe.app.Application()
packetserver.common.PacketServerConnection.receive_subscribers.append(lambda x: self.server_receiver(x))
packetserver.common.PacketServerConnection.connection_subscribers.append(lambda x: self.server_connection_bouncer(x))
@@ -83,6 +86,9 @@ class Server:
logging.debug("running server receiver")
process_incoming_data(conn, self)
def register_path_handler(self, path_root: str, fn: Callable):
self.handlers[path_root.strip().lower()] = fn
def start(self):
self.app.start(self.pe_server, self.pe_port)
self.app.register_callsigns(self.callsign)

View File

@@ -0,0 +1,101 @@
import persistent
import persistent.list
from persistent.mapping import PersistentMapping
import datetime
from typing import Self,Union,Optional
from packetserver.common import PacketServerConnection, Request, Response, Message
from packetserver.server import Server
from packetserver.server.requests import send_404
import ZODB
import logging
def init_bulletins(root: PersistentMapping):
if 'bulletins' not in root:
root['bulletins'] = persistent.list.PersistentList()
if 'bulletin_counter' not in root:
root['bulletin_counter'] = 0
def get_new_bulletin_id(root: PersistentMapping) -> int:
if 'bulletin_counter' not in root:
root['bulletin_counter'] = 1
return 0
else:
current = root['bulletin_counter']
root['bulletin_counter'] = current + 1
return current
class Bulletin(persistent.Persistent):
@classmethod
def get_bulletin_by_id(cls, bid: int, db_root: PersistentMapping) -> Optional[Self]:
for bull in db_root['bulletins']:
if bull.id == bid:
return bull
return None
def __init__(self, author: str, subject: str, text: str):
self.author = author
self.subject = subject
self.body = text
self.created_at = None
self.updated_at = None
self.id = None
@classmethod
def from_dict(cls, bulletin_dict: dict) -> Self:
return Bulletin(bulletin_dict['author'], bulletin_dict['subject'], bulletin_dict['body'])
def write_new(self, db_root: PersistentMapping):
if self.id is None:
self.id = get_new_bulletin_id(db_root)
self.created_at = datetime.datetime.now(datetime.UTC)
self.updated_at = datetime.datetime.now(datetime.UTC)
db_root['bulletins'].append(self)
def update_subject(self, new_text: str):
self.subject = new_text
self.updated_at = datetime.datetime.now(datetime.UTC)
def update_body(self, new_text: str):
self.body = new_text
self.updated_at = datetime.datetime.now(datetime.UTC)
def to_dict(self):
return {
"id": self.id,
"author": self.author,
"subject": self.subject,
"body": self.body,
"created_at": self.created_at.isoformat(),
"updated_at": self.updated_at.isoformat()
}
def handle_bulletin_get(req: Request, conn: PacketServerConnection, server: Server):
response = Response.blank()
with server.db.transaction() as db:
pass
return response
def handle_bulletin_post(req: Request, conn: PacketServerConnection, server: Server):
response = Response.blank()
with server.db.transaction() as db:
pass
return response
def handle_bulletin_update(req: Request, conn: PacketServerConnection, server: Server):
response = Response.blank()
with server.db.transaction() as db:
pass
return response
def handle_bulletin_delete(req: Request, conn: PacketServerConnection, server: Server):
response = Response.blank()
with server.db.transaction() as db:
pass
return response
def bulletin_root_handler(req: Request, conn: PacketServerConnection, server: Server):
logging.debug(f"{req} being processed by bulletin_root_handler")
if req.method is Request.Method.GET:
handle_bulletin_get(req, conn, server)
else:
send_404(conn)

View File

@@ -2,7 +2,16 @@
from msgpack.exceptions import OutOfData
from packetserver.common import Message, Request, Response, PacketServerConnection
from .bulletin import bulletin_root_handler
import logging
from typing import Union
def send_404(conn: PacketServerConnection, payload: Union[bytes, bytearray, str, dict] = ""):
response_404 = Response.blank()
response_404.status_code = 404
response_404.payload = payload
if conn.state.name == "CONNECTED":
conn.send_data(response_404.pack())
def handle_root_get(req: Request, conn: PacketServerConnection,
server: 'packetserver.server.Server'):
@@ -22,15 +31,27 @@ def handle_root_get(req: Request, conn: PacketServerConnection,
'motd': motd
}
if conn.state.name == "CONNECTED":
if conn.state.name == "CONNECTED" and not conn.closing:
logging.debug(f"sending response: {response}, {response.compression}, {response.payload}")
conn.send_data(response.pack())
logging.debug("response sent successfully")
def root_root_handler(req: Request, conn: PacketServerConnection,
server: 'packetserver.server.Server'):
logging.debug(f"{req} got to root_root_handler")
if req.method is Request.Method.GET:
handle_root_get(req, conn, server)
else:
logging.warning(f"unhandled request found: {req}")
response_404 = Response.blank()
response_404.status_code = 404
if (conn.state.name == "CONNECTED") and not conn.closing:
conn.send_data(response_404.pack())
logging.debug(f"Sent 404 in response to {req}")
standard_handlers = {
"": {
"GET": handle_root_get
}
"": root_root_handler,
"bulletin": bulletin_root_handler
}
def handle_request(req: Request, conn: PacketServerConnection,
@@ -40,10 +61,10 @@ def handle_request(req: Request, conn: PacketServerConnection,
if conn.closing:
logging.debug("Connection marked as closing. Ignoring it.")
return
if req.path in server.handlers:
if req.method.name in server.handlers[req.path]:
req_root_path = req.path.split("/")[0]
if req_root_path in server.handlers:
logging.debug(f"found handler for req {req}")
server.handlers[req.path][req.method.name](req, conn, server)
server.handlers[req_root_path](req, conn, server)
return
logging.warning(f"unhandled request found: {req}")
response_404 = Response.blank()