/**
  * @param float $delay
  * @param mixed $expected
  * @param mixed $actual
  */
 private function assertEqualsAfterDelay($delay, $expected, $actual)
 {
     Loop\timer($delay, function () use($expected, $actual) {
         $this->assertEquals($expected, $actual);
         Loop\stop();
     });
 }
Example #2
0
 /**
  * {@inheritdoc}
  */
 public function delay(float $time) : PromiseInterface
 {
     return new Promise(function (callable $resolve) use(&$timer, $time) {
         $timer = Loop\timer($time, function () use($resolve) {
             $resolve($this);
         });
     }, function () use(&$timer) {
         $timer->stop();
     });
 }
Example #3
0
 /**
  * {@inheritdoc}
  */
 public function delay(float $time) : Awaitable
 {
     return new Promise(function (callable $resolve) use($time) {
         $timer = Loop\timer($time, function () use($resolve) {
             $resolve($this);
         });
         return function () use($timer) {
             $timer->stop();
         };
     });
 }
Example #4
0
 /**
  * {@inheritdoc}
  */
 public function delay(float $time) : Awaitable
 {
     if (null !== $this->result) {
         return $this->unwrap()->delay($time);
     }
     ++$this->children;
     $future = new self(function (Throwable $exception) use(&$timer) {
         if (null !== $timer) {
             $timer->stop();
         }
         if (0 === --$this->children) {
             $this->cancel($exception);
         }
     });
     $onFulfilled = function () use(&$timer, $time, $future) {
         $timer = Loop\timer($time, function () use($future) {
             $future->resolve($this->result);
         });
     };
     $onRejected = function () use($future) {
         $future->resolve($this->result);
     };
     $this->done($onFulfilled, $onRejected);
     return $future;
 }
Example #5
0
 /**
  * {@inheritdoc}
  */
 public function delay(float $time) : PromiseInterface
 {
     if (null !== $this->result) {
         return $this->unwrap()->delay($time);
     }
     ++$this->children;
     return new self(function (callable $resolve) use($time) {
         $this->onFulfilled(function () use(&$timer, $time, $resolve) {
             $timer = Loop\timer($time, function () use($resolve) {
                 $resolve($this->result);
             });
         });
         $this->onRejected(function () use($resolve) {
             $resolve($this->result);
         });
         return function (Throwable $exception) use(&$timer) {
             if (null !== $timer) {
                 $timer->stop();
             }
             if (0 === --$this->children) {
                 $this->cancel($exception);
             }
         };
     });
 }
 public function testStampedeProtection()
 {
     Coroutine\create(function () {
         $cache = new MemoryDriver();
         file_put_contents(__DIR__ . "/stampede", 1);
         $factory = function () use(&$counter) {
             $count = (int) file_get_contents(__DIR__ . "/stampede");
             file_put_contents(__DIR__ . "/stampede", ++$count);
             (yield $count);
         };
         $cache->set("counter", $factory);
         $cache->set("counter", $factory);
         $cache->set("counter", $factory);
         $cache->get("counter");
         $cache->get("counter");
         // resolve the first "counter" value
         (yield $cache->get("counter"));
         // fetch the second "counter" value from the cache memory store
         $actual = (yield $cache->get("counter"));
         // first check to see that the count stored in the filesystem
         // is correct...
         Loop\timer(0.5, function () {
             $count = (int) file_get_contents(__DIR__ . "/stampede");
             $this->assertEquals(2, $count);
             unlink(__DIR__ . "/stampede");
         });
         // then check to see that the count stored in the cache
         // is correct...
         $this->assertEqualsAfterDelay(2, $actual);
     })->done();
     Loop\run();
 }
Example #7
0
 /**
  * @param string $key
  * @param mixed $value
  * @param int $expiration
  */
 protected function put(string $key, $value, int $expiration = 0)
 {
     $this->data[$key] = $value;
     if (isset($this->timers[$key])) {
         $this->timers[$key]->stop();
     }
     if ($expiration) {
         $this->timers[$key] = Loop\timer($expiration, $this->timerCallback, $key);
     } else {
         unset($this->timers[$key]);
     }
 }
Example #8
0
 /**
  * @param float $interval
  * @param int $maxSize
  * @param int $maxFrames
  *
  * @return \Icicle\Observable\Observable
  */
 private function createObservable(float $interval, int $maxSize, int $maxFrames) : Observable
 {
     return new Emitter(function (callable $emit) use($interval, $maxSize, $maxFrames) : \Generator {
         /** @var \Icicle\WebSocket\Protocol\Rfc6455Frame[] $frames */
         $frames = [];
         $size = 0;
         $ping = Loop\periodic($interval, Coroutine\wrap(function () use(&$pong, &$expected) {
             try {
                 yield from $this->ping($expected = base64_encode(random_bytes(self::PING_DATA_LENGTH)));
                 if (null === $pong) {
                     $pong = Loop\timer($this->timeout, Coroutine\wrap(function () {
                         yield from $this->close(Close::VIOLATION);
                         $this->transporter->close();
                     }));
                     $pong->unreference();
                 } else {
                     $pong->again();
                 }
             } catch (\Throwable $exception) {
                 $this->transporter->close();
             }
         }));
         $ping->unreference();
         try {
             while ($this->transporter->isOpen()) {
                 /** @var \Icicle\WebSocket\Protocol\Rfc6455Frame $frame */
                 $frame = (yield from $this->transporter->read($maxSize - $size));
                 $ping->again();
                 switch ($type = $frame->getType()) {
                     case Frame::CLOSE:
                         // Close connection.
                         if (!$this->closed) {
                             // Respond with close frame if one has not been sent.
                             yield from $this->close(Close::NORMAL);
                         }
                         $data = $frame->getData();
                         if (2 > strlen($data)) {
                             return new Close(Close::NO_STATUS);
                         }
                         $bytes = unpack('ncode', substr($data, 0, 2));
                         $data = (string) substr($data, 2);
                         if (!preg_match('//u', $data)) {
                             throw new DataException('Invalid UTF-8 data received.');
                         }
                         return new Close($bytes['code'], $data);
                     case Frame::PING:
                         // Respond with pong frame.
                         yield from $this->pong($frame->getData());
                         continue;
                     case Frame::PONG:
                         // Cancel timeout set by sending ping frame.
                         // Only stop timer if ping sent and pong data matches ping data.
                         if (null !== $pong && $frame->getData() === $expected) {
                             $pong->stop();
                         }
                         continue;
                     case Frame::CONTINUATION:
                         $count = count($frames);
                         if (0 === $count) {
                             throw new ProtocolException('Received orphan continuation frame.');
                         }
                         $frames[] = $frame;
                         $size += $frame->getSize();
                         if (!$frame->isFinal()) {
                             if ($count + 1 >= $maxFrames) {
                                 throw new PolicyException('Too many frames in message.');
                             }
                             continue;
                         }
                         $data = '';
                         for ($i = $count; $i >= 0; --$i) {
                             $frame = $frames[$i];
                             $data = $frame->getData() . $data;
                         }
                         $frames = [];
                         $size = 0;
                         $type = $frame->getType();
                         if ($type === Frame::TEXT && !preg_match('//u', $data)) {
                             throw new DataException('Invalid UTF-8 data received.');
                         }
                         yield from $emit(new Message($data, $type === Frame::BINARY));
                         continue;
                     case Frame::TEXT:
                     case Frame::BINARY:
                         if (!empty($frames)) {
                             throw new ProtocolException('Expected continuation data frame.');
                         }
                         if (!$frame->isFinal()) {
                             $size = $frame->getSize();
                             $frames[] = $frame;
                             continue;
                         }
                         $data = $frame->getData();
                         if ($type === Frame::TEXT && !preg_match('//u', $data)) {
                             throw new DataException('Invalid UTF-8 data received.');
                         }
                         yield from $emit(new Message($data, $type === Frame::BINARY));
                         continue;
                     default:
                         throw new ProtocolException('Received unrecognized frame type.');
                 }
             }
         } catch (ConnectionException $exception) {
             $close = new Close($exception->getReasonCode(), $exception->getMessage());
         } catch (SocketException $exception) {
             $close = new Close(Close::ABNORMAL, $exception->getMessage());
         } catch (StreamException $exception) {
             $close = new Close(Close::ABNORMAL, $exception->getMessage());
         } catch (TimeoutException $exception) {
             $close = new Close(Close::GOING_AWAY, $exception->getMessage());
         } finally {
             $ping->stop();
             if (null !== $pong) {
                 $pong->stop();
             }
         }
         if (!isset($close)) {
             $close = new Close(Close::ABNORMAL, 'Peer unexpectedly disconnected.');
         }
         if ($this->isOpen()) {
             yield from $this->close($close->getCode());
         }
         return $close;
     });
 }