コード例 #1
0
ファイル: Connection.php プロジェクト: koolkode/async-http
 /**
  * Reads the next WebSocket frame from the socket.
  * 
  * This method will unmask frames as needed and asserts frame size constraints.
  * 
  * @return Frame
  */
 protected function readNextFrame() : \Generator
 {
     list($byte1, $byte2) = \array_map('ord', \str_split((yield $this->socket->readBuffer(2, true)), 1));
     $masked = $byte2 & Frame::MASKED ? true : false;
     if ($this->client && $masked) {
         throw new ConnectionException('Received masked frame from server', Frame::PROTOCOL_ERROR);
     }
     if (!$this->client && !$masked) {
         throw new ConnectionException('Received unmasked frame from client', Frame::PROTOCOL_ERROR);
     }
     // Parse extended length fields:
     $len = $byte2 & Frame::LENGTH;
     if ($len === 0x7e) {
         $len = \unpack('n', (yield $this->socket->readBuffer(2, true)))[1];
     } elseif ($len === 0x7f) {
         $lp = \unpack('N2', (yield $this->socket->readBuffer(8, true)));
         // 32 bit int check:
         if (\PHP_INT_MAX === 0x7fffffff) {
             if ($lp[1] !== 0 || $lp[2] < 0) {
                 throw new ConnectionException('Max payload size exceeded', Frame::MESSAGE_TOO_BIG);
             }
             $len = $lp[2];
         } else {
             $len = $lp[1] << 32 | $lp[2];
             if ($len < 0) {
                 throw new ConnectionException('Cannot use most significant bit in 64 bit length field', Frame::MESSAGE_TOO_BIG);
             }
         }
     }
     if ($len < 0) {
         throw new ConnectionException('Payload length must not be negative', Frame::MESSAGE_TOO_BIG);
     }
     if ($len > $this->maxFrameSize) {
         throw new ConnectionException(\sprintf('Maximum frame size of %u bytes exceeded', $this->maxFrameSize), Frame::MESSAGE_TOO_BIG);
     }
     // Read and unmask frame data.
     if ($this->client) {
         $data = (yield $this->socket->readBuffer($len, true));
     } else {
         $key = (yield $this->socket->readBuffer(4, true));
         $data = (yield $this->socket->readBuffer($len, true)) ^ \str_pad($key, $len, $key, \STR_PAD_RIGHT);
     }
     return new Frame($byte1 & Frame::OPCODE, $data, $byte1 & Frame::FINISHED ? true : false, $byte1 & Frame::RESERVED);
 }
コード例 #2
0
ファイル: Driver.php プロジェクト: koolkode/async-http
 /**
  * Perform a direct upgrade of the connection to HTTP/2.
  * 
  * @param HttpDriverContext $context HTTP context related to the HTTP endpoint.
  * @param SocketStream $socket The underlying socket transport.
  * @param HttpRequest $request The HTTP request that caused the connection upgrade.
  * @param callable $action Server action to be performed for each incoming HTTP request.
  */
 protected function upgradeConnectionDirect(HttpDriverContext $context, SocketStream $socket, HttpRequest $request, callable $action) : \Generator
 {
     $preface = (yield $socket->readBuffer(\strlen(Connection::PREFACE_BODY), true));
     if ($preface !== Connection::PREFACE_BODY) {
         throw new StatusException(Http::BAD_REQUEST, 'Invalid HTTP/2 connection preface body');
     }
     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' => Http::SWITCHING_PROTOCOLS, 'size' => '-']);
     }
     $conn = new Connection($socket, new HPack($this->hpackContext), $this->logger);
     (yield $conn->performServerHandshake(null, true));
     if ($this->logger) {
         $this->logger->info('HTTP/{protocol} connection from {peer} upgraded to HTTP/2', ['protocol' => $request->getProtocolVersion(), 'peer' => $socket->getRemoteAddress()]);
     }
     $remotePeer = $socket->getRemoteAddress();
     try {
         while (null !== ($received = (yield $conn->nextRequest($context)))) {
             new Coroutine($this->processRequest($conn, $action, ...$received), true);
         }
     } finally {
         try {
             $conn->shutdown();
         } finally {
             if ($this->logger) {
                 $this->logger->debug('Closed HTTP/2 connection to {peer}', ['peer' => $remotePeer]);
             }
         }
     }
 }
コード例 #3
0
ファイル: Connection.php プロジェクト: koolkode/async-http
 /**
  * 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]);
         }
     }
 }