# AMPHP v3 — HTTP Server Patterns --- ## Minimal HTTP Server ```php pushHandler(new StreamHandler(STDOUT)); $server = SocketHttpServer::createForDirectAccess($logger); $server->expose('0.0.0.0:8080'); $server->start( new class implements RequestHandler { public function handleRequest(Request $request): Response { return new Response( status: HttpStatus::OK, headers: ['Content-Type' => 'application/json'], body: json_encode(['status' => 'ok'], JSON_THROW_ON_ERROR), ); } }, new DefaultErrorHandler(), ); if (\extension_loaded('pcntl')) { trapSignal([\SIGTERM, \SIGINT]); $server->stop(); } else { \Revolt\EventLoop::run(); // Windows fallback } ``` --- ## Router with Route Parameters ```php addRoute('GET', '/', new HomeHandler()); // Route parameter (FastRoute syntax) $router->addRoute('GET', '/users/{id:\d+}', new class implements RequestHandler { public function handleRequest(Request $request): Response { $args = $request->getAttribute(Router::class); // ['id' => '42'] $id = (int) $args['id']; return new Response( HttpStatus::OK, ['Content-Type' => 'application/json'], json_encode(['id' => $id], JSON_THROW_ON_ERROR), ); } }); // Inline handler via ClosureRequestHandler $router->addRoute('POST', '/api/users', new ClosureRequestHandler( function (Request $request): Response { $data = json_decode($request->getBody()->buffer(), true, flags: JSON_THROW_ON_ERROR); return new Response( HttpStatus::CREATED, ['Content-Type' => 'application/json'], json_encode(['id' => 1, 'name' => $data['name']], JSON_THROW_ON_ERROR), ); } )); // 404 fallback $router->setFallback(new class implements RequestHandler { public function handleRequest(Request $request): Response { return new Response(HttpStatus::NOT_FOUND, [], 'Not Found'); } }); $server->start($router, $errorHandler); ``` --- ## Middleware ```php handleRequest($request); $elapsed = (microtime(true) - $start) * 1000; error_log(sprintf('%s %s %.1fms', $request->getMethod(), $request->getUri(), $elapsed)); return $response; } } class AuthMiddleware implements Middleware { public function handleRequest(Request $request, RequestHandler $next): Response { if (!$this->isAuthorized($request)) { return new Response(\Amp\Http\HttpStatus::UNAUTHORIZED); } return $next->handleRequest($request); } private function isAuthorized(Request $request): bool { return $request->getHeader('Authorization') === 'Bearer secret'; } } // stackMiddleware: applied left-to-right, first listed = outermost $handler = stackMiddleware($router, new LoggingMiddleware(), new AuthMiddleware()); $server->start($handler, $errorHandler); ``` --- ## Static Files (DocumentRoot + StaticResource) ```php setFallback($docRoot); // SPA-friendly fallback // Single file (supports ETag, Range, If-None-Match automatically) $favicon = new StaticResource($server, $errorHandler, '/var/www/favicon.ico'); $router->addRoute('GET', '/favicon.ico', $favicon); // SPA index.html fallback inside DocumentRoot $docRoot->setFallback( new StaticResource($server, $errorHandler, '/var/www/public/index.html') ); ``` --- ## SocketHttpServer — direct constructor (full control) ```php withDefaultCertificate(new Certificate('/etc/ssl/cert.pem', '/etc/ssl/key.pem')); $server->expose('0.0.0.0:443', (new BindContext())->withTlsContext($tlsContext)); // Disable Nagle's algorithm (useful for benchmarks / small responses) $server->expose('0.0.0.0:8080', (new BindContext())->withTcpNoDelay()); $server->start($requestHandler, new DefaultErrorHandler()); ``` --- ## Behind Proxy (X-Forwarded-For) ```php withSecure() ->withHttpOnly() ->withSameSite(CookieAttributes::SAME_SITE_LAX) ->withMaxAge(3600), ); $handler = stackMiddleware( new class implements \Amp\Http\Server\RequestHandler { public function handleRequest(\Amp\Http\Server\Request $request): \Amp\Http\Server\Response { /** @var Session $session */ $session = $request->getAttribute(Session::class); $userId = $session->get('user_id'); $session->lock(); try { $session->set('user_id', 42); $session->commit(); } finally { $session->unlock(); } return new \Amp\Http\Server\Response(\Amp\Http\HttpStatus::OK, [], "user=$userId"); } }, $sessionMiddleware, ); ```