Наследование: extends Promise, implements Icicle\Coroutine\CoroutineInterface
Пример #1
0
 /**
  * Runs the thread code and the initialized function.
  *
  * @codeCoverageIgnore Only executed in thread.
  */
 public function run()
 {
     /* First thing we need to do is re-initialize the class autoloader. If
      * we don't do this first, any object of a class that was loaded after
      * the thread started will just be garbage data and unserializable
      * values (like resources) will be lost. This happens even with
      * thread-safe objects.
      */
     foreach (get_declared_classes() as $className) {
         if (strpos($className, 'ComposerAutoloaderInit') === 0) {
             // Calling getLoader() will register the class loader for us
             $className::getLoader();
             break;
         }
     }
     Loop\loop($loop = Loop\create(false));
     // Disable signals in thread.
     // At this point, the thread environment has been prepared so begin using the thread.
     try {
         $channel = new ChannelledStream(new DuplexPipe($this->socket, false));
     } catch (\Throwable $exception) {
         return;
         // Parent has destroyed Thread object, so just exit.
     }
     $coroutine = new Coroutine($this->execute($channel));
     $coroutine->done();
     $timer = $loop->timer(self::KILL_CHECK_FREQUENCY, true, function () use($loop) {
         if ($this->killed) {
             $loop->stop();
         }
     });
     $timer->unreference();
     $loop->run();
 }
Пример #2
0
 /**
  * Accepts incoming connections as they are made.
  */
 private function accept(SocketServer $server) : Generator
 {
     yield from log()->log(Log::INFO, 'NNTP server listening on %s:%d', $server->getAddress(), $server->getPort());
     while ($server->isOpen()) {
         // Wait for a client to connect.
         $socket = (yield from $server->accept());
         // Handle the client in a separate coroutine.
         $coroutine = new Coroutine($this->handleClient($socket));
         $coroutine->done();
     }
 }
Пример #3
0
 public function testExecuteWithJsonBody()
 {
     $method = $this->createMock(AbstractMethodWithBodyStub::class);
     $method->expects($this->once())->method('getMethodName')->willReturn('/someMethod');
     $method->expects($this->once())->method('getHttpMethod')->willReturn('POST');
     $method->expects($this->once())->method('getParams')->willReturn(['param1' => 'value1', 'param2' => 'value2']);
     $method->expects($this->once())->method('buildResult')->willReturn('result');
     $method->expects($this->once())->method('jsonSerialize')->willReturn(['jsonParam1' => 'jsonValue1', 'jsonParam2' => 'jsonValue2']);
     $responseData = json_encode(['ok' => true, 'result' => 'result'], JSON_UNESCAPED_UNICODE);
     $this->setUpHttpClient($responseData);
     $api = new Api($this->telegramToken, $this->httpClient);
     $coroutine = new Coroutine($api->execute($method));
     $result = $coroutine->wait();
     $this->assertEquals('result', $result);
 }
Пример #4
0
 /**
  * {@inheritdoc}
  */
 public function dispose(Throwable $exception = null)
 {
     if (null === $exception) {
         $exception = new DisposedException('Observable disposed.');
     }
     $this->emitter = null;
     $this->queue->fail($exception);
     if (null !== $this->coroutine) {
         $this->coroutine->cancel($exception);
     }
 }
Пример #5
0
 /**
  * Executes the emitter coroutine.
  */
 private function start()
 {
     Loop\queue(function () {
         // Asynchronously start the observable.
         if (null === $this->emitter) {
             return;
         }
         /**
          * Emits a value from the observable.
          *
          * @coroutine
          *
          * @param mixed $value If $value is an instance of \Icicle\Awaitable\Awaitable, the fulfillment value is
          *     used as the value to emit or the rejection reason is thrown from this coroutine.
          *
          * @return \Generator
          *
          * @resolve mixed The emitted value (the resolution value of $value)
          *
          * @throws \Icicle\Observable\Exception\CompletedError If the observable has been completed.
          */
         $emit = function ($value = null) : \Generator {
             return $this->queue->push($value);
         };
         try {
             $generator = ($this->emitter)($emit);
             if (!$generator instanceof Generator) {
                 throw new UnexpectedTypeError('Generator', $generator);
             }
             $this->coroutine = new Coroutine($generator);
             $this->coroutine->done([$this->queue, 'complete'], [$this->queue, 'fail']);
         } catch (Throwable $exception) {
             $this->queue->fail(new InvalidEmitterError($this->emitter, $exception));
         }
         $this->emitter = null;
     });
 }
Пример #6
0
 /**
  * Processing loop
  */
 public function pump()
 {
     try {
         while ($this->isOpen()) {
             (yield $this->next());
         }
     } catch (\Exception $e) {
         if ($this->outputHeartbeatCoroutine) {
             $this->outputHeartbeatCoroutine->cancel();
             $this->outputHeartbeatCoroutine = null;
         }
         if ($this->inputHeartbeatCoroutine) {
             $this->inputHeartbeatCoroutine->cancel();
             $this->inputHeartbeatCoroutine = null;
         }
         (yield $this->client->close());
     }
 }
Пример #7
0
 /**
  * @coroutine
  *
  * @param \Icicle\Http\Message\Message $message
  * @param float|int $timeout
  *
  * @return \Generator
  *
  * @resolve \Icicle\Http\Message\Message
  *
  * @throws \Icicle\Http\Exception\MessageException
  */
 private function buildIncomingStream(Message $message, float $timeout = 0) : \Generator
 {
     $body = $message->getBody();
     if ($body instanceof SeekableStream && $body->isOpen()) {
         yield from $body->seek(0);
     }
     if (!$body->isReadable()) {
         return $message;
     }
     if (strtolower($message->getHeader('Transfer-Encoding') === 'chunked')) {
         $stream = new ChunkedDecoder($this->hwm);
         $coroutine = new Coroutine(Stream\pipe($body, $stream, true, 0, null, $timeout));
         $coroutine->done(null, [$stream, 'close']);
         $message = $message->withBody($stream);
     } elseif ($message->hasHeader('Content-Length')) {
         $length = (int) $message->getHeader('Content-Length');
         if (0 > $length) {
             throw new MessageException(Response::BAD_REQUEST, 'Content-Length header invalid.');
         }
         $stream = new MemoryStream($this->hwm);
         if (0 === $length) {
             yield from $stream->end();
         } else {
             $coroutine = new Coroutine(Stream\pipe($body, $stream, true, $length, null, $timeout));
             $coroutine->done(null, [$stream, 'close']);
         }
         $message = $message->withBody($stream);
     } elseif ($message instanceof Request) {
         switch ($message->getMethod()) {
             case 'POST':
             case 'PUT':
                 // Post and put messages must have content length or be transfer encoded.
                 throw new MessageException(Response::LENGTH_REQUIRED, 'Content-Length header required.');
             default:
                 // Assume 0 length body.
                 $stream = new MemoryStream();
                 yield from $stream->end();
                 // Creates empty request body.
                 return $message->withBody($stream);
         }
     } elseif (strtolower($message->getHeader('Connection')) !== 'close') {
         throw new MessageException(Response::LENGTH_REQUIRED, 'Content-Length header required.');
     }
     $contentEncoding = strtolower($message->getHeader('Content-Encoding'));
     switch ($contentEncoding) {
         case 'deflate':
             $stream = new ZlibDecoder(ZlibDecoder::DEFLATE, $this->hwm);
             break;
         case 'gzip':
             $stream = new ZlibDecoder(ZlibDecoder::GZIP, $this->hwm);
             break;
         case '':
             // No content encoding.
             return $message;
         default:
             throw new MessageException(Response::BAD_REQUEST, sprintf('Unsupported content encoding received: %s', $contentEncoding));
     }
     $coroutine = new Coroutine(Stream\pipe($message->getBody(), $stream, true, 0, null, $timeout));
     $coroutine->done(null, [$stream, 'close']);
     return $message->withBody($stream);
 }
Пример #8
0
 /**
  * {@inheritdoc}
  */
 public function close()
 {
     if ($this->open && $this->worker->isRunning()) {
         $coroutine = new Coroutine($this->worker->enqueue(new Internal\FileTask('fclose', [], $this->id)));
         $coroutine->done(null, [$this->worker, 'kill']);
     }
     if (!$this->queue->isEmpty()) {
         $exception = new FileException('The file was closed.');
         do {
             $this->queue->shift()->cancel($exception);
         } while (!$this->queue->isEmpty());
     }
     $this->open = false;
     $this->writable = false;
 }
Пример #9
0
 /**
  * Converts a function accepting a callback ($emitter) that invokes the callback when an event is emitted into an
  * observable that emits the arguments passed to the callback function each time the callback function would be
  * invoked.
  *
  * @param callable(mixed ...$args) $emitter Function accepting a callback that periodically emits events.
  * @param callable(callable $callback, \Exception $exception) $onDisposed Called if the observable is disposed.
  *     The callback passed to this function is the callable provided to the $emitter callable given to this
  *     function.
  * @param int $index Position of callback function in emitter function argument list.
  * @param mixed ...$args Other arguments to pass to emitter function.
  *
  * @return \Icicle\Observable\Observable
  */
 function observe(callable $emitter, callable $onDisposed = null, int $index = 0, ...$args) : Observable
 {
     $emitter = function (callable $emit) use(&$callback, $emitter, $index, $args) : \Generator {
         $delayed = new Delayed();
         $reject = [$delayed, 'reject'];
         $callback = function (...$args) use($emit, $reject) {
             $coroutine = new Coroutine($emit($args));
             $coroutine->done(null, $reject);
         };
         if (count($args) < $index) {
             throw new InvalidArgumentError('Too few arguments given to function.');
         }
         array_splice($args, $index, 0, [$callback]);
         $emitter(...$args);
         return (yield $delayed);
     };
     if (null !== $onDisposed) {
         $onDisposed = function (\Throwable $exception) use(&$callback, $onDisposed) {
             $onDisposed($callback, $exception);
         };
     }
     return new Emitter($emitter, $onDisposed);
 }
Пример #10
0
use Icicle\Socket\Server\Server;
use Icicle\Socket\Socket;
$server = (new DefaultServerFactory())->create('localhost', 8080);
$generator = function (Server $server) {
    printf("Server listening on %s:%d\n", $server->getAddress(), $server->getPort());
    $generator = function (Socket $socket) {
        $request = '';
        do {
            $request .= (yield $socket->read(0, "\n"));
        } while (substr($request, -4) !== "\r\n\r\n");
        $message = sprintf("Received the following request:\r\n\r\n%s", $request);
        $data = "HTTP/1.1 200 OK\r\n";
        $data .= "Content-Type: text/plain\r\n";
        $data .= sprintf("Content-Length: %d\r\n", strlen($message));
        $data .= "Connection: close\r\n";
        $data .= "\r\n";
        $data .= $message;
        (yield $socket->write($data));
        $socket->close();
    };
    while ($server->isOpen()) {
        // Handle client in a separate coroutine so this coroutine is not blocked.
        $coroutine = new Coroutine($generator((yield $server->accept())));
        $coroutine->done(null, function (Loop\Exception\Error $exception) {
            printf("Client error: %s\n", $exception->getMessage());
        });
    }
};
$coroutine = new Coroutine($generator($server));
$coroutine->done();
Loop\run();
Пример #11
0
 /**
  * Starts the context execution.
  *
  * @throws \Icicle\Concurrent\Exception\ForkException If forking fails.
  * @throws \Icicle\Stream\Exception\FailureException If creating a socket pair fails.
  */
 public function start()
 {
     if (0 !== $this->oid) {
         throw new StatusError('The context has already been started.');
     }
     list($parent, $child) = Stream\pair();
     switch ($pid = pcntl_fork()) {
         case -1:
             // Failure
             throw new ForkException('Could not fork process!');
         case 0:
             // Child
             // @codeCoverageIgnoreStart
             // Create a new event loop in the fork.
             Loop\loop($loop = Loop\create(false));
             $channel = new ChannelledStream($pipe = new DuplexPipe($parent));
             fclose($child);
             $coroutine = new Coroutine($this->execute($channel));
             $coroutine->done();
             try {
                 $loop->run();
                 $code = 0;
             } catch (\Throwable $exception) {
                 $code = 1;
             }
             $pipe->close();
             exit($code);
             // @codeCoverageIgnoreEnd
         // @codeCoverageIgnoreEnd
         default:
             // Parent
             $this->pid = $pid;
             $this->oid = posix_getpid();
             $this->channel = new ChannelledStream($this->pipe = new DuplexPipe($child));
             fclose($parent);
     }
 }
Пример #12
0
 /**
  * {@inheritdoc}
  */
 public function seek($offset, $whence = SEEK_SET)
 {
     if (!$this->isSeekable()) {
         throw new RuntimeException('Stream is not seekable');
     }
     $promise = new Coroutine($this->stream->seek($offset, $whence));
     try {
         return $promise->wait();
     } catch (\Exception $exception) {
         throw new RuntimeException('Error seeking stream', 0, $exception);
     }
 }
 public function testQueueUnbindDelete()
 {
     $coroutine = new Coroutine\Coroutine($this->goTestQueueUnbindDelete());
     $coroutine->done();
     Loop\run();
 }
Пример #14
0
 /**
  * @coroutine
  *
  * @param \Icicle\Socket\Server\Server $server
  * @param int $cryptoMethod
  * @param float $timeout
  * @param bool $allowPersistent
  *
  * @return \Generator
  */
 private function accept(SocketServer $server, int $cryptoMethod, float $timeout, bool $allowPersistent) : \Generator
 {
     yield from $this->log->log(Log::INFO, 'HTTP server listening on %s:%d', $server->getAddress(), $server->getPort());
     while ($server->isOpen()) {
         try {
             $coroutine = new Coroutine($this->process(yield from $server->accept(), $cryptoMethod, $timeout, $allowPersistent));
             $coroutine->done(null, $this->onError);
         } catch (Throwable $exception) {
             if ($this->isOpen()) {
                 throw $exception;
             }
         }
     }
 }