🚧 progress

This commit is contained in:
Joe Kaufeld 2024-08-01 18:09:12 -04:00
parent 7f88c01156
commit 738adee6c2
5 changed files with 91 additions and 14 deletions

View File

@ -27,3 +27,7 @@ class ParseError(SpiderwebException):
class GeneralException(SpiderwebException): class GeneralException(SpiderwebException):
pass pass
class UnusedMiddleware(SpiderwebException):
pass

View File

@ -12,6 +12,7 @@ from typing import Callable, Any
from spiderweb.converters import * # noqa: F403 from spiderweb.converters import * # noqa: F403
from spiderweb.exceptions import APIError, ConfigError, ParseError, GeneralException from spiderweb.exceptions import APIError, ConfigError, ParseError, GeneralException
from spiderweb.request import Request
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -95,7 +96,7 @@ class APIServer(HTTPServer):
self.add_route(route, method) self.add_route(route, method)
try: try:
super().__init__(server_address, HandlerClass) super().__init__(server_address, self.handler_class)
except OSError: except OSError:
raise GeneralException("Port already in use.") raise GeneralException("Port already in use.")
@ -137,21 +138,32 @@ class APIHandler(BaseHTTPRequestHandler):
# BaseHTTPRequestHandler uses for some weird reason # BaseHTTPRequestHandler uses for some weird reason
_routes = {} _routes = {}
def get_request(self):
return Request(
content="",
body="",
method=self.command,
headers=self.headers,
path=self.path
)
def do_GET(self): def do_GET(self):
self.do_action() request = self.get_request()
self.handle_request(request)
def do_POST(self): def do_POST(self):
content = "{}" content = "{}"
if self.headers["Content-Length"]: if self.headers["Content-Length"]:
length = int(self.headers["Content-Length"]) length = int(self.headers["Content-Length"])
content = self.rfile.read(length) content = self.rfile.read(length)
info = None request = self.get_request()
request.content = content
if content: if content:
try: try:
info = json.loads(content) request.json()
except json.JSONDecodeError: except json.JSONDecodeError:
raise APIError(400, "Invalid JSON", content) raise APIError(400, "Invalid JSON", content)
self.do_action(info) self.handle_request(request)
def get_route(self, path) -> tuple[Callable, dict[str, Any]]: def get_route(self, path) -> tuple[Callable, dict[str, Any]]:
for option in self._routes.keys(): for option in self._routes.keys():
@ -161,23 +173,22 @@ class APIHandler(BaseHTTPRequestHandler):
) )
raise APIError(404, "No route found") raise APIError(404, "No route found")
def do_action(self, info=None): def handle_request(self, request):
info = info or {}
try: try:
url = urlparse.urlparse(self.path) request.url = urlparse.urlparse(request.path)
handler, additional_args = self.get_route(url.path) handler, additional_args = self.get_route(request.url.path)
if url.query: if request.url.query:
params = urlparse.parse_qs(url.query) params = urlparse.parse_qs(request.url.query)
else: else:
params = {} params = {}
info.update(params) request.query_params = params
if handler: if handler:
try: try:
response = handler(info, **additional_args) response = handler(request, **additional_args)
self.send_response(200) self.send_response(200)
if response is None: if response is None:
response = "" response = ""

View File

@ -0,0 +1,31 @@
from typing import Optional, NoReturn
from spiderweb.request import Request
from spiderweb.response import HttpResponse
class SpiderwebMiddleware:
"""
All middleware should inherit from this class and have the following
(optional!) methods:
process_request(self, request) -> None or Response
process_response(self, request, response) -> None
Middleware can be used to modify requests and responses in a variety of ways.
If one of the two methods is not defined, the request or response will be passed
through unmodified.
If `process_request` returns
"""
def process_request(self, request: Request) -> HttpResponse | None:
# example of a middleware that sets a flag on the request
request.spiderweb = True
def process_response(self, request: Request, response: HttpResponse) -> NoReturn:
# example of a middleware that sets a header on the response
if hasattr(request, 'spiderweb'):
response['X-Spiderweb'] = 'true'
return response

17
spiderweb/request.py Normal file
View File

@ -0,0 +1,17 @@
import json
class Request:
def __init__(self, content=None, body=None, method=None, headers=None, path=None, url=None, query_params=None):
self.content: str = content
self.body: str = body
self.method: str = method
self.headers: dict[str] = headers
self.path: str = path
self.url = url
self.query_params = query_params
def json(self):
return json.loads(self.content)

14
spiderweb/response.py Normal file
View File

@ -0,0 +1,14 @@
class HttpResponse:
...
class JsonResponse(HttpResponse):
...
class RedirectResponse(HttpResponse):
...
class TemplateResponse(HttpResponse):
...