/** * Handle an FCGI STDIN record that transfers request body data. * * @param Record $record * @param Channel $incoming */ public function handleStdin(Record $record, Channel $incoming) : \Generator { if (!$this->received) { $this->received = true; $request = $this->buildRequest(); $incoming->send([$this, $request]); } if ($record->data === '') { return $this->body->close(); } return (yield $this->body->send($record->data)); }
/** * Coroutine that handles incoming WebSocket frames. */ protected function processIncomingFrames() : \Generator { $e = null; try { while (true) { $frame = (yield from $this->readNextFrame()); if ($frame->isControlFrame()) { if (!(yield from $this->handleControlFrame($frame))) { break; } } else { switch ($frame->opcode) { case Frame::TEXT: yield from $this->handleTextFrame($frame); break; case Frame::BINARY: yield from $this->handleBinaryFrame($frame); break; case Frame::CONTINUATION: yield from $this->handleContinuationFrame($frame); break; } } } } catch (\Throwable $e) { $this->messages->close($e); } finally { $this->messages->close(); try { foreach ($this->pings as $defer) { $defer->fail($e ?? new \RuntimeException('WebSocket connection closed')); } } finally { $this->pings = []; } try { if ($this->socket->isAlive()) { $reason = $e === null || $e->getCode() === 0 ? Frame::NORMAL_CLOSURE : $e->getCode(); (yield $this->writer->sendFrame(new Frame(Frame::CONNECTION_CLOSE, \pack('n', $reason)))); } } finally { if ($this->logger) { $this->logger->debug('WebSocket connection to {peer} closed', ['peer' => $this->socket->getRemoteAddress()]); } $this->socket->close(); } } }
protected function wireMapper(callable $map, Channel $input, int $concurrency = 1) : Channel { $output = new Channel($concurrency * 2); $executor = new Executor($concurrency); $closed = false; $count = 0; $n = 0; $job = null; $job = function () use(&$job, &$count, &$n, &$closed, $executor, $map, $input, $output) { try { if ($this->eof === ($val = (yield $input->receive($this->eof)))) { if (--$count < 1) { $output->close(); } return; } $val = $map($val, $n++); if ($val instanceof \Generator) { $val = (yield from $val); } if (!$closed) { (yield $output->send($val)); $executor->execute($job); } } catch (\Throwable $e) { if (!$closed) { $closed = true; $output->close($e); } } }; for ($i = 0; $i < $concurrency; $i++) { $count++; $executor->execute($job); } return $output; }
protected function socketWriter($socket, Channel $channel, callable $transform = null) : \Generator { $writer = new SocketWriter($socket); $len = 0; try { while (null !== ($chunk = (yield $channel->receive()))) { $len += (yield $writer->write($transform ? $transform($chunk) : $chunk)); } return $len; } catch (\Throwable $e) { $channel->close($e); throw $e; } finally { $writer->dispose(); } }
/** * Coroutine that parses incoming requests and queues them into the request pipeline. * * @param DuplexStream $socket Stream being used to transmit HTTP messages. * @param Channel $pipeline HTTP request pipeline. * @param HttpRequest $request First HTTP request within the pipeline. */ protected function parseIncomingRequests(HttpDriverContext $context, SocketStream $socket, Channel $pipeline, HttpRequest $request) : \Generator { try { do { $request = $request ?? (yield from $this->parseNextRequest($context, $socket)); $close = $this->shouldConnectionBeClosed($request); (yield $pipeline->send([$request, $close])); (yield ((yield $request->getBody()->getReadableStream()))->getAwaitable()); $request = null; } while (!$close); $pipeline->close(); } catch (\Throwable $e) { $pipeline->close($e); } }
/** * Closes the event source. */ public function close() { $this->channel->close(); }