from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer import json class Server(): def __init__(self, directory='.'): self.directory = directory self.get_routes = [] self.post_routes = [] # Register a GET handler def get(self, path, *handlers): self.get_routes.append({'path': path, 'handlers': handlers}) # Register a POST handler def post(self, path, *handlers): self.post_routes.append({'path': path, 'handlers': handlers}) # Create the server def listen(self, port): directory = self.directory post_routes = self.post_routes get_routes = self.get_routes # Create request handler class class Handler(SimpleHTTPRequestHandler): # Patch the constructor with our directory def __init__(self, *args, **kwargs): super().__init__(*args, directory=directory, **kwargs) def send_error(self, code, message=None): if code == 404 and self.path != '/': self.send_response(302) self.send_header('Location', '/') self.end_headers() else: super().send_error(code, message) # JSON helper def send_json_response(self, status_code, data=None): if status_code >= 400: data = {'error': True} self.send_response(status_code) self.send_header('Content-type', 'application/json') self.end_headers() self.wfile.write(bytes(json.dumps(data), 'utf8')) # Loop over routes and execute one if there's a match def handle_routes(self, routes): # Loop over the registered routes for route in routes: # Check if we have a match if self.path == route['path']: try: request = {} # Parse post data if self.headers['Content-Length'] and self.headers['Content-Type'] == 'application/json': content_length = int(self.headers['Content-Length']) raw_post_data = self.rfile.read(content_length) request['post_data'] = json.loads(raw_post_data.decode('utf-8')) # Execute handlers for handler in route['handlers']: response = handler(request) self.send_json_response(200, response) except Exception as e: # If it failed, send internal server error print(f'Exception: {e}') self.send_json_response(500) # Route matched, return True return True # No routes matched, return False return False # Try to match a route aganst a GET request # else fall back to static server def do_GET(self): if not self.handle_routes(get_routes): super().do_GET() # Try to match a route aganst a POST request # else return 404 def do_POST(self): if not self.handle_routes(post_routes): self.send_json_response(404) # Start HTTP server and attach handler print(f'Server listening on port {port}...') with ThreadingHTTPServer(('', port), Handler) as server: server.serve_forever()