blob: 076f1ad86504288995443ddea6d62ff313575a74 [file] [log] [blame]
# Copyright (c) 2025 Project CHIP Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import http.server
import json
import urllib.parse
from typing import List, Optional, Type
from route_configuration import Configuration, Route
from router import match_route
def createMockServerHandler(config: Configuration) -> Type[http.server.BaseHTTPRequestHandler]:
"""
Creates a custom HTTP request handler class configured with predefined routes and responses.
This factory function generates a new HTTP request handler class that processes incoming
HTTP requests according to the provided routing configuration. The handler supports
route matching based on HTTP method, path, and query parameters, returning predefined
responses for matched routes.
Args:
config (Configuration): A Configuration object containing route definitions.
Each route should specify:
- HTTP method (GET, POST, PUT, DELETE)
- URL path pattern
- Response details (status code, headers, body)
- Optional query parameters for matching
Returns:
Type[http.server.BaseHTTPRequestHandler]: A new handler class that:
- Processes standard HTTP methods (GET, POST, PUT, DELETE)
- Matches incoming requests against configured routes
- Returns JSON or plain text responses based on configuration
- Provides 404 responses for unmatched routes
Example Configuration:
{
"routing": [
{
"method": "GET",
"path": "/api/users",
"response": {
"status": 200,
"headers": {"Content-Type": "application/json"},
"body": {"users": []}
}
}
]
}
Note:
- The handler automatically sets appropriate Content-Type headers
- JSON responses are automatically encoded to UTF-8
- Non-JSON responses are converted to strings before encoding
- All responses include standard HTTP headers and status codes
"""
class MockServerHandler(http.server.BaseHTTPRequestHandler):
def _set_headers(self, status_code=200, headers=None) -> None:
self.send_response(status_code)
if headers:
for key, value in headers.items():
self.send_header(key, value)
self.end_headers()
def do_GET(self) -> None:
self.handle_request()
def do_POST(self) -> None:
self.handle_request()
def do_PUT(self) -> None:
self.handle_request()
def do_DELETE(self) -> None:
self.handle_request()
def handle_request(self) -> None:
parsed_path: urllib.parse.ParseResult = urllib.parse.urlparse(self.path)
path: str = parsed_path.path
query_params: dict[str, list[str]] = urllib.parse.parse_qs(parsed_path.query)
# Find the matching route from the configuration
routes: List[Route] = config.routing
route: Optional[Route] = match_route(routes, self.command, path, query_params)
if not route:
# No matching route found; return a 404 error response
self._set_headers(404, {"Content-Type": "application/json"})
self.wfile.write(json.dumps({}).encode("utf-8"))
return
# Use the static response defined in the configuration
self._set_headers(route.response.status, route.response.headers)
if route.response.headers.get("Content-Type") == "application/json":
self.wfile.write(json.dumps(route.response.body).encode("utf-8"))
else:
self.wfile.write(str(route.response.body).encode("utf-8"))
return MockServerHandler