/** * @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(); }); }
/** * {@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(); }); }
/** * {@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(); }; }); }
/** * {@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; }
/** * {@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(); }
/** * @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]); } }
/** * @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; }); }