Пример #1
0
 /**
  * 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();
         }
     }
 }
Пример #2
0
 private static function handshake(SocketStream $socket) : \Generator
 {
     $transmitter = new SocketTransmitter($socket);
     list($type, $data) = (yield from $transmitter->receive());
     if ($type !== SocketTransmitter::TYPE_HANDSHAKE || !isset($data['id'])) {
         return $socket->close();
     }
     $id = $data['id'];
     if (empty(self::$handshakes[$id])) {
         return $socket->close();
     }
     list($worker, $defer) = self::$handshakes[$id];
     unset(self::$handshakes[$id]);
     if (empty(self::$handshakes) && self::$serverAwait !== null) {
         self::$serverAwait->cancel(new PoolShutdownException('Handshakes done'));
     }
     $worker->connect($socket, $transmitter);
     self::$sharedWorkers[$id] = $worker;
     $defer->resolve($worker);
 }
Пример #3
0
 /**
  * Coroutine that processes inbound FCGI records.
  */
 protected function handleIncomingRecords() : \Generator
 {
     static $header = 'Cversion/Ctype/nid/nlen/Cpad/x';
     try {
         $peer = $this->socket->getRemoteAddress();
         if ($this->logger) {
             $this->logger->debug('Accepted new FCGI connection from {peer}', ['peer' => $peer]);
         }
         while (true) {
             list($version, $type, $id, $len, $pad) = \array_values(\unpack($header, (yield $this->socket->readBuffer(8, true))));
             $payload = $len > 0 ? (yield $this->socket->readBuffer($len, true)) : '';
             if ($pad > 0) {
                 (yield $this->socket->readBuffer($pad, true));
             }
             $record = new Record($version, $type, $id, $payload);
             switch ($record->type) {
                 case Record::FCGI_BEGIN_REQUEST:
                     list($role, $flags) = \array_values(\unpack('nrole/Cflags/x5', $record->data));
                     if ($role != self::FCGI_RESPONDER) {
                         throw new \RuntimeException('Unsupported FGCI role');
                     }
                     $this->handlers[$id] = new Handler($id, $this, $this->context, $flags & self::FCGI_KEEP_CONNECTION ? true : false);
                     if ($this->logger) {
                         $this->handlers[$id]->setLogger($this->logger);
                     }
                     break;
                 case Record::FCGI_ABORT_REQUEST:
                     if (!(yield $this->closeHandler($id))) {
                         return;
                     }
                     break;
                 case Record::FCGI_PARAMS:
                     $this->handlers[$id]->handleParams($record);
                     break;
                 case Record::FCGI_STDIN:
                     yield from $this->handlers[$id]->handleStdin($record, $this->incoming);
                     break;
             }
         }
     } finally {
         $this->socket->close();
         $this->processor = null;
         if ($this->logger) {
             $this->logger->debug('Closed FCGi connection to {peer}', ['peer' => $peer]);
         }
     }
 }