HTTP & Routingv2.0
HTTP
High-performance PSR-7/PSR-15/PSR-17 HTTP message implementations, middleware stack, and SAPI emitter for the MonkeysLegion framework. Zero external dependencies beyond PSR interfaces.
Installation
composer require monkeyscloud/monkeyslegion-http
Features
| Feature | Description |
|---|---|
| PSR-7 Messages | Immutable ServerRequest, Response, JsonResponse, Stream, Uri |
| PSR-15 Middleware | 14 production-ready middleware components |
| PSR-17 Factories | HttpFactory for all message types |
| SAPI Emitter | Chunked streaming response emitter |
| Error Handler | OOM-safe error handler with PSR-3 logging |
| Content Negotiation | Accept header parsing and payload selection |
| Helper Functions | response(), json(), redirect(), html() |
| PHP 8.4 Native | final classes, readonly properties, match expressions |
Quick Start
use MonkeysLegion\Http\Message\ServerRequest;
use MonkeysLegion\Http\Message\JsonResponse;
use MonkeysLegion\Http\Emitter\SapiEmitter;
// Build request from PHP superglobals
$request = ServerRequest::fromGlobals();
// Convenience accessors
$email = $request->input('user.email'); // Dot-notation body access
$token = $request->bearerToken(); // Authorization: Bearer ...
$ip = $request->ip(); // Client IP
$agent = $request->userAgent(); // User-Agent header
$hash = $request->fingerprint(); // SHA-256 request fingerprint
// Inspection helpers
$request->isJson(); // Expects JSON?
$request->isSecure(); // HTTPS?
$request->isAjax(); // XMLHttpRequest?
$request->isMethod('POST');
// Create responses
$response = new JsonResponse(['status' => 'ok'], 200);
// Emit to client
(new SapiEmitter())->emit($response);
ServerRequest Convenience API
// Dot-notation input access
$email = $request->input('user.email', 'default@example.com');
// Get all parsed body fields
$all = $request->all();
// Get only specific fields
$credentials = $request->only(['email', 'password']);
// Bearer token extraction
$jwt = $request->bearerToken();
// Request fingerprint for dedup/caching
$fingerprint = $request->fingerprint(); // SHA-256 of method|path|query|body
JsonResponse
use MonkeysLegion\Http\Message\JsonResponse;
// Simple JSON response
$response = new JsonResponse(['users' => $users]);
// With status code
$response = new JsonResponse(['error' => 'Not Found'], 404);
// Envelope format: { status, data, meta, message }
$response = (new JsonResponse($data))
->withEnvelope(message: 'Success');
// With pagination metadata
$response = (new JsonResponse($items))
->withPagination(page: 1, perPage: 25, total: 100);
Middleware Stack
Available Middleware
| Middleware | Purpose |
|---|---|
AuthMiddleware | Bearer token authentication with JWT support |
CorsMiddleware | Full CORS preflight handling |
CsrfMiddleware | CSRF token verification |
RateLimitMiddleware | Sliding-window rate limiting with PSR-16 cache |
SecurityHeadersMiddleware | Security headers (strict/relaxed/api presets) |
ETagMiddleware | Conditional GET with ETag/If-None-Match |
IpFilterMiddleware | IP whitelist/blacklist filtering |
RequestIdMiddleware | UUID request correlation ID |
RequestSizeLimitMiddleware | Request body size enforcement |
TimingMiddleware | X-Response-Time header |
LoggingMiddleware | PSR-3 request/response logging |
TrustedProxyMiddleware | X-Forwarded-* header handling |
ContentNegotiationMiddleware | Accept header content negotiation |
ErrorHandlerMiddleware | Exception → HTTP response conversion |
Middleware Dispatcher
O(1) cursor-based dispatcher — no array_shift overhead:
use MonkeysLegion\Http\MiddlewareDispatcher;
use MonkeysLegion\Http\CoreRequestHandler;
$dispatcher = new MiddlewareDispatcher(
middlewareStack: [
new RequestIdMiddleware(),
new SecurityHeadersMiddleware('strict'),
new CorsMiddleware(
allowedOrigins: ['https://example.com'],
allowedMethods: ['GET', 'POST', 'PUT', 'DELETE'],
),
new RateLimitMiddleware(maxRequests: 100, windowSeconds: 60),
new AuthMiddleware(requiredToken: 'my-api-key'),
],
finalHandler: new CoreRequestHandler($router),
);
$response = $dispatcher->handle($request);
CORS Middleware
use MonkeysLegion\Http\Middleware\CorsMiddleware;
$cors = new CorsMiddleware(
allowedOrigins: ['https://app.example.com'],
allowedMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization', 'X-Request-ID'],
exposedHeaders: ['X-Request-ID', 'X-Response-Time'],
maxAge: 3600,
allowCredentials: true,
);
Rate Limiter
use MonkeysLegion\Http\Middleware\RateLimitMiddleware;
$limiter = new RateLimitMiddleware(
maxRequests: 100, // Requests per window
windowSeconds: 60, // 1-minute window
cache: $psr16Cache, // Optional PSR-16 cache (falls back to in-memory)
);
// Per-route override via request attribute:
// $request = $request->withAttribute('rate_limit', 10);
Security Headers
use MonkeysLegion\Http\Middleware\SecurityHeadersMiddleware;
// Three built-in presets
$strict = new SecurityHeadersMiddleware('strict'); // Production APIs
$relaxed = new SecurityHeadersMiddleware('relaxed'); // Development
$api = new SecurityHeadersMiddleware('api'); // API-optimized
// Custom overrides
$custom = new SecurityHeadersMiddleware('strict', [
'Content-Security-Policy' => "default-src 'self'",
]);
Auth Middleware
use MonkeysLegion\Http\Middleware\AuthMiddleware;
$auth = new AuthMiddleware(
requiredToken: 'my-secret-token',
publicPaths: ['/health', '/login', '/register'],
jwtDecoder: fn(string $token) => JWT::decode($token, $key), // Optional
);
Error Handler
Global error handler with OOM protection, recursive-exception guards, and PSR-3 logging:
use MonkeysLegion\Core\Error\ErrorHandler;
use MonkeysLegion\Core\Error\Renderer\ErrorRendererInterface;
use MonkeysLegion\Http\Error\Renderer\JsonErrorRenderer;
use MonkeysLegion\Core\Error\Renderer\HtmlErrorRenderer;
$handler = new ErrorHandler();
$handler->useRenderer(new JsonErrorRenderer());
$handler->useLogger($psrLogger);
$handler->register();
// Custom renderer example:
final class CustomErrorRenderer implements ErrorRendererInterface {
public function render(\Throwable $exception, bool $debug = false): string {
return 'Custom error output';
}
public function getContentType(): string {
return 'text/plain';
}
}
$handler->useRenderer(new CustomErrorRenderer());
Error Renderers
| Renderer | Output |
|---|---|
JsonErrorRenderer | Structured JSON error response |
HtmlErrorRenderer | Styled HTML error page |
Legacy HTTP namespace aliases are deprecated for these two types:
MonkeysLegion\Http\Error\Renderer\ErrorRendererInterfaceMonkeysLegion\Http\Error\Renderer\PlainTextErrorRendererUse Core namespace instead:
MonkeysLegion\Core\Error\Renderer\ErrorRendererInterfaceMonkeysLegion\Core\Error\Renderer\PlainTextErrorRenderer
PSR-17 Factory
use MonkeysLegion\Http\Factory\HttpFactory;
$factory = new HttpFactory();
$response = $factory->createResponse(200, 'OK');
$stream = $factory->createStream('Hello');
$uri = $factory->createUri('https://example.com/api');
$request = $factory->createServerRequest('GET', $uri);
SAPI Emitter
use MonkeysLegion\Http\Emitter\SapiEmitter;
$emitter = new SapiEmitter(chunkSize: 8192);
$emitter->emit($response);
Features:
- Auto-injects
Content-Lengthwhen body size is known - Guards against
headers_sent()— throws instead of silent corruption - Configurable chunk size for streaming large responses
Helper Functions
// Plain text response
$r = response('Hello World', 200, ['X-Custom' => 'value']);
// JSON response
$r = json(['status' => 'ok']);
// Redirect response
$r = redirect('/dashboard', 302);
// HTML response
$r = html('<h1>Hello</h1>');
Requirements
- PHP 8.4+
psr/http-message^2.0psr/http-server-middleware^1.0psr/http-server-handler^1.0psr/http-factory^1.1
Optional
psr/simple-cache^3.0 — ForRateLimitMiddlewarepersistent storagepsr/log^3.0 — ForErrorHandlerandLoggingMiddleware
License
MIT