/** * {@inheritdoc} */ public function start(HttpRequest $request, LoggerInterface $logger = null) { $this->logger = $logger; if ($this->logger) { $this->address = $request->getClientAddress(); $this->logger->debug('Enabled SSE for {address} using HTTP/{version}', ['address' => $this->address, 'version' => $request->getProtocolVersion()]); } }
/** * Send handshake HTTP/1.1 response to the client to enable protocol switching. * * @param BufferedDuplexStreamInterface $socket * @param HttpRequest $request */ protected function sendHandshake(BufferedDuplexStreamInterface $socket, HttpRequest $request) : \Generator { $accept = base64_encode(sha1($request->getHeaderLine('Sec-WebSocket-Key') . self::GUID, true)); $message = sprintf("HTTP/%s 101 Switching Protocols\r\n", $request->getProtocolVersion()); $message .= "Connection: Upgrade\r\n"; $message .= "Upgrade: websocket\r\n"; $message .= sprintf("Sec-WebSocket-Accept: %s\r\n", $accept); $message .= "Sec-WebSocket-Version: 13\r\n"; $message .= "\r\n"; yield from $socket->write($message); }
/** * {@inheritdoc} */ public function upgradeConnection(SocketStream $socket, HttpRequest $request, HttpResponse $response) : \Generator { $endpoint = $response->getAttribute(Endpoint::class); if (!$endpoint instanceof Endpoint) { throw new \InvalidArgumentException('No endpoint object passed to WebSocket handler'); } if ($this->logger) { $this->logger->debug('HTTP/{protocol} connection from {peer} upgraded to WebSocket', ['protocol' => $request->getProtocolVersion(), 'peer' => $socket->getRemoteAddress()]); } $conn = new Connection($socket, false, $response->getHeaderLine('Sec-WebSocket-Protocol')); if ($this->logger) { $conn->setLogger($this->logger); } if ($deflate = $response->getAttribute(PerMessageDeflate::class)) { $conn->enablePerMessageDeflate($deflate); } yield from $this->delegateToEndpoint($conn, $endpoint); }
/** * Convert the given outcome of an action into an HTTP response. * * @param HttpRequest $request * @param mixed $result * @return HttpResponse */ public function respond(HttpRequest $request, $result) : HttpResponse { if ($result instanceof HttpResponse) { return $result->withProtocolVersion($request->getProtocolVersion()); } foreach ($this->responders as $responder) { $response = ($responder->callback)($request, $result); if ($response instanceof HttpResponse) { return $response->withProtocolVersion($request->getProtocolVersion()); } } $reason = \sprintf('Expecting HttpResponse, given %s', \is_object($result) ? \get_class($result) : \gettype($result)); throw new \RuntimeException($reason); }
/** * Normalize the given HTTP response to be sent using FCGI records. * * @param HttpRequest $request * @param HttpResponse $response * @return HttpResponse */ protected function normalizeResponse(HttpRequest $request, HttpResponse $response) : HttpResponse { static $remove = ['Connection', 'Content-Length', 'Keep-Alive', 'Status', 'Trailer', 'Transfer-Encoding', 'Upgrade']; $response = $response->withProtocolVersion($request->getProtocolVersion()); foreach ($remove as $name) { $response = $response->withoutHeader($name); } return $response->withHeader('Date', \gmdate(Http::DATE_RFC1123)); }
/** * 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; }
/** * {@inheritdoc} */ public function isRequestSupported(HttpRequest $request) : bool { return (double) $request->getProtocolVersion() >= 2.0; }
protected function normalizeRequest(HttpRequest $request) : HttpRequest { static $remove = ['Content-Length', 'Expect', 'Keep-Alive', 'TE', 'Trailer', 'Transfer-Encoding']; $version = $request->getProtocolVersion(); switch ($version) { case '1.0': case '1.1': // Everything fine, version is supported. break; default: $request = $request->withProtocolVersion('1.1'); } $tokens = []; foreach ($request->getHeaderTokenValues('Connection') as $token) { if ($token !== 'close' && $token !== 'keep-alive') { $tokens[] = $token; } } if (empty($tokens)) { $request = $request->withoutHeader('Connection'); } else { $request = $request->withHeader('Connection', \implode(', ', $tokens)); } foreach ($remove as $name) { $request = $request->withoutHeader($name); } return $request->withHeader('Date', \gmdate(Http::DATE_RFC1123)); }
/** * Normalize HTTP response object prior to being sent to the client. */ protected function normalizeResponse(HttpRequest $request, HttpResponse $response) : HttpResponse { static $remove = ['Content-Length', 'Keep-Alive', 'Trailer', 'Transfer-Encoding']; $response = $response->withProtocolVersion($request->getProtocolVersion()); foreach ($remove as $name) { $response = $response->withoutHeader($name); } $conn = []; foreach ($response->getHeaderTokenValues('Connection') as $token) { switch ($token) { case 'close': case 'keep-alive': // Ignore these... break; default: $conn[] = $token; } } if (empty($conn)) { $response = $response->withoutHeader('Connection'); } else { $response = $response->withHeader('Connection', \implode(', ', $conn)); } return $response->withHeader('Date', \gmdate(Http::DATE_RFC1123)); }
/** * Create an HTTP file response for the matched file. * * @param HttpRequest $request * @param string $file * @return HttpResponse */ protected function createResponse(HttpRequest $request, string $file) : HttpResponse { $response = new FileResponse($file); $response = $response->withHeader('Cache-Control', \sprintf('public, max-age=%u', $this->ttl)); if ($request->getProtocolVersion() === '1.0') { $response = $response->withHeader('Expires', \gmdate(Http::DATE_RFC1123, \time() + $this->ttl)); } return $response; }