/** * {@inheritdoc} */ public function listen(callable $action) : Awaitable { return new Coroutine(function () use($action) { $factory = clone $this->factory; $factory->setTcpNoDelay(true); $this->server = (yield $factory->createSocketServer()); $port = $this->server->getPort(); $peer = $this->server->getAddress() . ($port ? ':' . $port : ''); $context = new HttpDriverContext($peer, $factory->getPeerName(), $factory->isEncrypted(), $this->middlewares, $this->responders); $pending = new \SplObjectStorage(); try { (yield $this->server->listen(function (SocketStream $socket) use($pending, $context, $action) { $conn = new Connection($socket, $context); if ($this->logger) { $conn->setLogger($this->logger); } $pending->attach($conn); while (null !== ($next = (yield $conn->nextRequest()))) { new Coroutine($this->processRequest($conn, $context, $action, ...$next)); } })); } finally { $this->server = null; foreach ($pending as $task) { $task->cancel(new \RuntimeException('FCGI server stopped')); } } }); }
/** * Assemble an HTTP request from received FCGI params. */ protected function buildRequest() : HttpRequest { static $extra = ['CONTENT_TYPE' => 'Content-Type', 'CONTENT_LENGTH' => 'Content-Length', 'CONTENT_MD5' => 'Content-MD5']; $uri = \strtolower($this->params['REQUEST_SCHEME'] ?? 'http') . '://'; $uri .= $this->params['HTTP_HOST'] ?? $this->context->getPeerName(); if (!empty($this->params['SERVER_PORT'])) { $uri .= ':' . (int) $this->params['SERVER_PORT']; } $uri = Uri::parse($uri . '/' . \ltrim($this->params['REQUEST_URI'] ?? '', '/')); $request = new HttpRequest($uri, $this->params['REQUEST_METHOD'] ?? Http::GET, [], '1.1'); foreach ($this->params as $k => $v) { if ('HTTP_' === \substr($k, 0, 5)) { switch ($k) { case 'HTTP_TRANSFER_ENCODING': case 'HTTP_CONTENT_ENCODING': case 'HTTP_KEEP_ALIVE': // Skip these headers... break; default: $request = $request->withAddedHeader(\str_replace('_', '-', \substr($k, 5)), (string) $v); } } } foreach ($extra as $k => $v) { if (isset($this->params[$k])) { $request = $request->withHeader($v, $this->params[$k]); } } $addresses = [$this->conn->getRemoteAddress()]; if (isset($this->params['REMOTE_ADDR'])) { $addresses = \array_merge([$this->params['REMOTE_ADDR']], $addresses); } $request = $request->withAddress(...$addresses); $request = $request->withAttribute(HttpDriverContext::class, $this->context); return $request->withBody(new StreamBody(new ReadableChannelStream($this->body))); }