public function __invoke(HttpRequest $request, HttpDriverContext $context) : \Generator { $path = \str_replace('+', '%20', $request->getRequestTarget()); $routing = new RoutingContext($this->container, $this->compiler, $path, $request->getMethod(), $this->routable->getMiddlewares()); $this->routeRequest($routing); if (!$routing->isMatch()) { return $routing->createNoMatchResponse(); } $matches = []; if ($request->hasHeader('Content-Type')) { $type = $request->getContentType()->getMediaType(); foreach ($routing->getMatches() as $match) { if ($match->handler->canConsume($type)) { $matches[] = $match; } } } else { foreach ($routing->getMatches() as $match) { if ($match->handler->isConsumer()) { continue; } $matches[] = $match; } } if (!$matches) { return $routing->createUnsupportedMediaTypeResponse(); } $accept = $request->getAccept(); $result = $matches[0]; foreach ($accept->getMediaTypes() as $media) { foreach ($matches as $match) { foreach ($match->handler->getProducedMediaTypes() as $type) { if ($media->is($type)) { $result = $match; break 3; } } } } if (empty($result->middlewares)) { return yield from $this->dispatchRequest($request, $context, $result); } $next = new NextMiddleware($result->middlewares, function (HttpRequest $request) use($context, $result) { return yield from $this->dispatchRequest($request, $context, $result); }, $this->logger); return yield from $next($request); }
/** * Check for a pre-parsed HTTP/2 connection preface. */ protected function isPrefaceRequest(HttpRequest $request) : bool { if ($request->getMethod() !== 'PRI') { return false; } if ($request->getRequestTarget() !== '*') { return false; } if ($request->getProtocolVersion() !== '2.0') { return false; } return true; }
protected function sendDeferredResponse(SocketStream $socket, HttpRequest $request, HttpResponse $response, bool $nobody, bool $close) : \Generator { if ($this->logger) { $this->logger->info('{ip} "{method} {target} HTTP/{protocol}" {status} {size}', ['ip' => $request->getClientAddress(), 'method' => $request->getMethod(), 'target' => $request->getRequestTarget(), 'protocol' => $request->getProtocolVersion(), 'status' => $response->getStatusCode(), 'size' => '-']); } if (!$nobody) { $close = true; } (yield $socket->write($this->serializeHeaders($response, $close, null, $nobody, true) . "\r\n")); (yield $socket->flush()); if ($nobody) { $response->getBody()->close(false); return !$close; } $watcher = null; $task = new Coroutine(function () use(&$watcher, $socket, $request, $response) { $body = $response->getBody(); $body->start($request, $this->logger); $bodyStream = (yield $body->getReadableStream()); $e = null; try { while (null !== ($chunk = (yield $bodyStream->read()))) { (yield $socket->write($chunk)); } } catch (StreamClosedException $e) { // Client disconnected from server. } finally { try { $bodyStream->close(); } finally { $body->close($e ? true : false); } $watcher->cancel(new \RuntimeException()); } return false; }, true); $watcher = new Coroutine(function () use($socket, $task) { $socket = $socket->getSocket(); while (\is_resource($socket) && !\feof($socket)) { (yield new AwaitRead($socket)); } $task->cancel(new StreamClosedException('Client disconnected')); }); return (yield $task); }