Example #1
0
 /**
  * {@inheritdoc}
  */
 public function writeRequest(Socket $socket, Request $request, float $timeout = 0) : \Generator
 {
     $written = (yield from $socket->write($this->encoder->encodeRequest($request)));
     $stream = $request->getBody();
     if ($stream->isReadable()) {
         $written += (yield from Stream\pipe($stream, $socket, false, 0, null, $timeout));
     }
     return $written;
 }
Example #2
0
 /**
  * @param \Icicle\Stream\Structures\Buffer $buffer
  * @param \Icicle\Socket\Socket $socket
  * @param float|int $timeout
  *
  * @return \Generator
  *
  * @throws \Icicle\Http\Exception\MessageException
  * @throws \Icicle\Http\Exception\ParseException
  */
 protected function readHeaders(Buffer $buffer, Socket $socket, float $timeout = 0) : \Generator
 {
     $size = 0;
     $headers = [];
     do {
         while (false === ($position = $buffer->search("\r\n"))) {
             if ($buffer->getLength() >= $this->maxSize) {
                 throw new MessageException(Response::REQUEST_HEADER_TOO_LARGE, sprintf('Message header exceeded maximum size of %d bytes.', $this->maxSize));
             }
             $buffer->push(yield from $socket->read(0, null, $timeout));
         }
         $length = $position + 2;
         $line = $buffer->shift($length);
         if (2 === $length) {
             return $headers;
         }
         $size += $length;
         $parts = explode(':', $line, 2);
         if (2 !== count($parts)) {
             throw new ParseException('Found header without colon.');
         }
         list($name, $value) = $parts;
         $name = Message\decode($name);
         $value = Message\decode(trim($value));
         // No check for case as Message class will automatically combine similarly named headers.
         if (!isset($headers[$name])) {
             $headers[$name] = [$value];
         } else {
             $headers[$name][] = $value;
         }
     } while ($size < $this->maxSize);
     throw new MessageException(Response::REQUEST_HEADER_TOO_LARGE, sprintf('Message header exceeded maximum size of %d bytes.', $this->maxSize));
 }
Example #3
0
 /**
  * Services requests for a connected client.
  *
  * Manages the server-side state for the remote client and interprets client commands.
  */
 private function handleClient(Socket $socket) : Generator
 {
     yield from log()->log(Log::INFO, 'Accepted client from %s:%d on %s:%d', $socket->getRemoteAddress(), $socket->getRemotePort(), $socket->getLocalAddress(), $socket->getLocalPort());
     // Create a new context object for this client.
     $context = new ClientContext($socket, $this->encoder, $this->accessLayer);
     try {
         // Send a welcome message.
         $welcome = new Response(200, 'Ready');
         yield from $context->writeResponse($welcome);
         // Command loop
         while ($socket->isOpen()) {
             // Parse incoming commands.
             $command = (yield from $context->readCommand());
             // Determine the command name and choose how to handle it.
             $handler = null;
             switch ($command->name()) {
                 // Mandatory commands
                 case 'CAPABILITIES':
                     $handler = $this->getHandler(handlers\CapabilitiesHandler::class);
                     break;
                 case 'HEAD':
                     $handler = $this->getHandler(handlers\HeadHandler::class);
                     break;
                 case 'HELP':
                     $handler = $this->getHandler(handlers\HelpHandler::class);
                     break;
                     // We don't advertise MODE support, but some readers need it
                 // We don't advertise MODE support, but some readers need it
                 case 'MODE':
                     $handler = $this->getHandler(handlers\ModeHandler::class);
                     break;
                 case 'STAT':
                     $handler = $this->getHandler(handlers\StatHandler::class);
                     break;
                 case 'QUIT':
                     yield from $context->writeResponse(new Response(205, 'Closing connection'));
                     break 2;
                     // stop command loop
                     // LIST commands
                 // stop command loop
                 // LIST commands
                 case 'LIST':
                     $handler = $this->getHandler(handlers\ListHandler::class);
                     break;
                     // NEWNEWS commands
                 // NEWNEWS commands
                 case 'NEWNEWS':
                     $handler = $this->getHandler(handlers\NewNewsHandler::class);
                     break;
                     // POST commands
                 // POST commands
                 case 'POST':
                     $handler = $this->getHandler(handlers\PostHandler::class);
                     break;
                     // READER commands
                 // READER commands
                 case 'ARTICLE':
                     $handler = $this->getHandler(handlers\ArticleHandler::class);
                     break;
                 case 'BODY':
                     $handler = $this->getHandler(handlers\BodyHandler::class);
                     break;
                 case 'DATE':
                     $handler = $this->getHandler(handlers\DateHandler::class);
                     break;
                 case 'GROUP':
                     $handler = $this->getHandler(handlers\GroupHandler::class);
                     break;
                 case 'LAST':
                     $handler = $this->getHandler(handlers\LastHandler::class);
                     break;
                 case 'LISTGROUP':
                     $handler = $this->getHandler(handlers\ListGroupHandler::class);
                     break;
                 case 'NEWGROUPS':
                     $handler = $this->getHandler(handlers\NewGroupsHandler::class);
                     break;
                 case 'NEXT':
                     $handler = $this->getHandler(handlers\NextHandler::class);
                     break;
             }
             // Unknown command
             if (!$handler) {
                 yield from $context->writeResponse(new Response(500, 'Unknown command'));
             } else {
                 // Execute the selected command handler.
                 try {
                     yield from $handler->handle($command, $context);
                 } catch (\Throwable $e) {
                     yield from log()->log(Log::ERROR, 'Error handling command: ' . $e);
                     yield from $context->writeResponse(new Response(502, 'Server error'));
                 }
             }
         }
     } catch (UnreadableException $e) {
         // Client disconnected.
     } catch (UnwritableException $e) {
         // Client disconnected.
     } catch (ClosedException $e) {
         // Client disconnected.
     }
     yield from log()->log(Log::INFO, 'Disconnected client from %s:%d on %s:%d', $socket->getRemoteAddress(), $socket->getRemotePort(), $socket->getLocalAddress(), $socket->getLocalPort());
     // Close the connection to the client.
     if ($socket->isOpen()) {
         $socket->close();
     }
 }
 /**
  * @return bool
  */
 public function isCryptoEnabled() : bool
 {
     return $this->socket->isCryptoEnabled();
 }
Example #5
0
 /**
  * @coroutine
  *
  * @param int $code
  * @param \Icicle\Socket\Socket $socket
  *
  * @return \Generator
  *
  * @resolve \Icicle\Http\Message|Response
  */
 private function createErrorResponse(int $code, Socket $socket) : \Generator
 {
     try {
         yield from $this->log->log(Log::NOTICE, 'Error reading request from %s:%d (Status Code: %d)', $socket->getRemoteAddress(), $socket->getRemotePort(), $code);
         $response = $this->handler->onError($code, $socket);
         if ($response instanceof \Generator) {
             $response = (yield from $response);
         } elseif ($response instanceof Awaitable) {
             $response = (yield $response);
         }
         if (!$response instanceof Response) {
             throw new InvalidResultError(sprintf('A %s object was not returned from %::onError().', Response::class, get_class($this->handler)), $response);
         }
     } catch (Throwable $exception) {
         yield from $this->log->log(Log::ERROR, "Uncaught exception when creating response to an error from %s:%d on %s:%d in file %s on line %d: %s", $socket->getRemoteAddress(), $socket->getRemotePort(), $socket->getLocalAddress(), $socket->getLocalPort(), $exception->getFile(), $exception->getLine(), $exception->getMessage());
         $response = (yield from $this->createDefaultErrorResponse(500));
     }
     return $response;
 }