private function send(string $message, callable $transform = null) { $promisor = new Deferred(); $this->connection->send($message); $this->promisors[] = $promisor; return $transform ? pipe($promisor->promise(), $transform) : $promisor->promise(); }
public function current() { if ($this->isLastChunk) { return $this->applyChunkEncoding(''); } elseif (($current = $this->iterator->current()) === '') { return null; } elseif (is_string($current)) { return $this->applyChunkEncoding($current); } elseif ($current instanceof Promise) { $promisor = new Deferred(); $current->when(function ($error, $result) use($promisor) { if ($error) { $promisor->fail($error); } elseif (is_string($result)) { $promisor->succeed($this->applyChunkEncoding($result)); } else { $promisor->fail(new \DomainException(sprintf('Only string/Promise elements may be chunked; %s provided', gettype($result)))); } }); return $promisor->promise(); } else { // @TODO How to react to an invalid type returned from an iterator? return null; } }
public function send($socket) { $deferred = new Deferred(); stream_set_blocking($socket, false); $data = $this->getRequest(); \Amp\onWritable($socket, function ($writer, $socket) use($deferred, &$data) { if ($bytes = fwrite($socket, $data)) { if ($bytes < \strlen($data)) { $data = substr($data, $bytes); return; } $data = ''; \Amp\onReadable($socket, function ($reader, $socket) use($deferred, &$data) { $data .= $bytes = fgets($socket); if ($bytes == '' || \strlen($bytes) > 32768) { \Amp\cancel($reader); $deferred->succeed(null); } elseif (substr($data, -4) == "\r\n\r\n") { \Amp\cancel($reader); $deferred->succeed($this->parseResponse($data)); } }); } else { $deferred->succeed(null); } \Amp\cancel($writer); }); return $deferred->promise(); }
protected function updateWorker(\Amp\Deferred $deferred, $success, $scripts, $exception = null) { $data = ['success' => $success, 'scripts' => $scripts]; if ($exception !== null) { $data['exception'] = $exception; } $deferred->update($data); }
public function getRoomSessionInfo(Identifier $identifier) : Promise { $deferred = new Deferred(); $this->queue->push([$identifier, $deferred]); if (!$this->haveLoop) { resolve($this->executeActionsFromQueue()); } return $deferred->promise(); }
protected function genericFetch(callable $cb = null) { if ($this->result->userFetched < $this->result->fetchedRows) { $row = $this->result->rows[$this->result->userFetched++]; return new Success($cb ? $cb($row) : $row); } elseif ($this->result->state == ResultProxy::ROWS_FETCHED) { return new Success(null); } else { $deferred = new Deferred(); $this->result->deferreds[ResultProxy::SINGLE_ROW_FETCH][] = [$deferred, null, $cb]; return $deferred->promise(); } }
public function log(int $logLevel, string $message, $extraData = null) : Promise { if (!$this->meetsLogLevel($logLevel)) { return new Success(); } $messages = [$message]; if ($extraData !== null && $this->meetsLogLevel(Level::EXTRA_DATA)) { $messages[] = json_encode($extraData); } $this->writeQueue->push([(new \DateTime())->format('Y-m-d H:i:s'), $messages, $deferred = new Deferred()]); if (!$this->haveWriteLoop) { resolve($this->writeMessagesFromQueue()); } return $deferred->promise(); }
/** * Return a key-value array of headers to add to the outbound request * * @return \Amp\Promise * @TODO */ public function getHeaders() { // @TODO Implement non-blocking php-uv header retrieval. // For now we'll just use the dumb blocking version. // v1.0.0 cannot be a thing until this is implemented. $promisor = new Deferred(); $this->getLength()->when(function ($error, $result) use($promisor) { if ($error) { $promisor->fail($error); } else { $promisor->succeed(['Content-Length' => $result]); } }); return $promisor->promise(); }
public function send($socket) { $deferred = new Deferred(); stream_set_blocking($socket, false); $data = $this->getRequest(); \Amp\onWritable($socket, function ($writer, $socket) use($deferred, &$data) { if ($bytes = fwrite($socket, $data)) { if ($bytes < \strlen($data)) { $data = substr($data, $bytes); return; } $size = 8192; \Amp\onReadable($socket, function ($reader, $socket) use($deferred, &$size) { /* make attention to not read too much data */ $data = stream_socket_recvfrom($socket, $size, STREAM_PEEK); if (false === ($pos = strpos($data, "\r\n\r\n"))) { if (\strlen($data) == $size) { $size *= 2; // unbounded?? } return; } \Amp\cancel($reader); $deferred->succeed($this->parseResponse(fread($socket, $pos + 4))); }); } else { $deferred->succeed(null); } \Amp\cancel($writer); }); return $deferred->promise(); }
private function requestNonce(string $uri) : Promise { $deferred = new Deferred(); $request = (new Request())->setMethod("HEAD")->setUri($uri); $this->http->request($request)->when(function (Throwable $error = null, Response $response = null) use($deferred) { if ($error) { $deferred->fail(new AcmeException("Couldn't fetch nonce!", $error)); } else { if (!$response->hasHeader("replay-nonce")) { $deferred->fail(new AcmeException("Server didn't send required replay-nonce header!")); } list($nonce) = $response->getHeader("replay-nonce"); $deferred->succeed($nonce); } }); return $deferred->promise(); }
public function update(string $key, int $user, int $change) : Promise { $promisor = new Deferred(); $this->redis->hIncrBy($key, $user, $change)->when(function ($error, $result) use($key, $user, $promisor) { if ($error) { $promisor->fail($error); } else { if ($result) { $promisor->succeed($result); } else { $promisor->succeed(pipe($this->redis->hDel($key, $user), function () use($result) { return $result; })); } } }); return $promisor->promise(); }
public function save(string $id, array $data, int $ttl) : Promise { $promisor = new Deferred(); parent::save($id, $data, $ttl)->when(function ($error, $result) use($id, $data, $promisor) { if ($error) { $promisor->fail($error); } else { $data = json_encode($data); $this->client->publish("sess:update", "{$id} {$data}")->when(function ($error) use($result, $promisor) { if ($error) { $promisor->fail(new Exception("failed to publish update", 0, $error)); } else { $promisor->succeed($result); } }); } }); return $promisor->promise(); }
private function requestNonce($uri) { if (!is_string($uri)) { throw new InvalidArgumentException(sprintf("\$uri must be of type string, %s given.", gettype($uri))); } $deferred = new Deferred(); $request = (new Request())->setMethod("HEAD")->setUri($uri); $this->http->request($request)->when(function ($error = null, Response $response = null) use($deferred, $uri) { if ($error) { $deferred->fail(new AcmeException("HEAD request to {$uri} failed, could not obtain a replay nonce.", null, $error)); } else { if (!$response->hasHeader("replay-nonce")) { $deferred->fail(new AcmeException("HTTP response didn't carry replay-nonce header.")); } list($nonce) = $response->getHeader("replay-nonce"); $deferred->succeed($nonce); } }); return $deferred->promise(); }
private function connect() { // If we're in the process of connecting already return that same promise if ($this->connectPromisor) { return $this->connectPromisor->promise(); } // If a read watcher exists we know we're already connected if ($this->readWatcher) { return new Success($this); } $this->connectPromisor = new Deferred(); $socketPromise = connect($this->uri, ["timeout" => $this->timeout]); $onWrite = function ($watcherId) { if ($this->outputBufferLength === 0) { disable($watcherId); return; } $bytes = @fwrite($this->socket, $this->outputBuffer); if ($bytes === 0) { $this->onError(new ConnectException("Connection went away (write)", $code = 1)); } else { $this->outputBuffer = (string) substr($this->outputBuffer, $bytes); $this->outputBufferLength -= $bytes; } }; $socketPromise->when(function ($error, $socket) use($onWrite) { $connectPromisor = $this->connectPromisor; $this->connectPromisor = null; if ($error) { $connectPromisor->fail(new ConnectException("Connection attempt failed", $code = 0, $error)); return; } $this->socket = $socket; foreach ($this->handlers["connect"] as $handler) { $pipelinedCommand = $handler(); if (!empty($pipelinedCommand)) { $this->outputBuffer = $pipelinedCommand . $this->outputBuffer; $this->outputBufferLength += strlen($pipelinedCommand); } } $this->readWatcher = onReadable($this->socket, function () { $read = fread($this->socket, 8192); if ($read != "") { $this->parser->append($read); } elseif (!is_resource($this->socket) || @feof($this->socket)) { $this->onError(new ConnectException("Connection went away (read)", $code = 2)); } }); $this->writeWatcher = onWritable($this->socket, $onWrite, ["enable" => !empty($this->outputBuffer)]); $connectPromisor->succeed(); }); return $this->connectPromisor->promise(); }
private function tunnelThroughProxy(Deferred $promisor, $socket, $authority) { if (empty(stream_context_get_options($socket)['artax*']['is_tunneled'])) { $futureTunnel = $this->tunneler->tunnel($socket, $authority); $futureTunnel->when(function ($error) use($promisor, $socket) { if ($error) { $promisor->fail($error); } else { $promisor->succeed($socket); } }); } else { $promisor->succeed($socket); } }
private function loadFile(string $filePath) : \Generator { if (isset($this->loadPromises[$filePath])) { (yield $this->loadPromises[$filePath]); return $this->dataCache[$filePath]; } $deferred = new Deferred(); $this->loadPromises[$filePath] = $deferred->promise(); $this->lockMutexes[$filePath] = new QueuedExclusiveMutex(); return (yield $this->lockMutexes[$filePath]->withLock(function () use($filePath, $deferred) { try { // we may have been waiting on a lock and it's been populated by now if (!isset($this->dataCache[$filePath])) { $this->dataCache[$filePath] = (yield exists($filePath)) ? json_try_decode((yield get($filePath)), true) : []; } } catch (\Throwable $e) { $this->dataCache[$filePath] = []; } finally { $deferred->succeed(); unset($this->loadPromises[$filePath]); } return $this->dataCache[$filePath]; })); }
private function enqueueAction(callable $callable, Identifier $identifier, ...$args) : Promise { $ident = $identifier->getIdentString(); if (!isset($this->actionQueues[$ident])) { $queue = new Queue(); $this->actionQueues[$ident] = ['queue' => $queue, 'running' => false]; } else { $queue = $this->actionQueues[$ident]['queue']; } $queue->push([$callable, $args, $deferred = new Deferred()]); if (!$this->actionQueues[$ident]['running']) { resolve($this->executeActionsFromQueue($identifier)); } return $deferred->promise(); }
/** * {@inheritdoc} */ public function write($data) { $promisor = new Deferred(); $op = new \StdClass(); $op->type = self::OP_WRITE; $op->position = $this->position; $op->promisor = $promisor; $op->writeData = $data; $this->pendingWriteOps++; if ($this->isActive) { $this->queue[] = $op; } else { \call_user_func($this->incrementor, 1); $this->isActive = true; \eio_write($this->fh, $data, strlen($data), $op->position, $priority = null, [$this, "onWrite"], $op); } return $promisor->promise(); }
/** * Asynchronously request an HTTP resource * * @param mixed[string|\Amp\Artax\Request] An HTTP URI string or an \Amp\Artax\Request instance * @param array $options An array specifying options applicable only for this request * @return \Amp\Promise A promise to resolve the request at some point in the future */ public function request($uriOrRequest, array $options = []) { $promisor = new Deferred(); try { $cycle = new RequestCycle(); $cycle->futureResponse = $promisor; list($cycle->request, $cycle->uri) = $this->generateRequestFromUri($uriOrRequest); $cycle->options = $options ? array_merge($this->options, $options) : $this->options; $body = $cycle->request->getBody(); if ($body instanceof AggregateBody) { $this->processAggregateBody($cycle, $body); } else { $this->finalizeRequest($cycle); } } catch (\Exception $e) { $promisor->fail($e); } return $promisor->promise(); }
protected function genericFetch(callable $cb = null) { if ($this->result->userFetched < $this->result->fetchedRows) { $row = $this->result->rows[$this->result->userFetched++]; return new Success($cb ? $cb($row) : $row); } elseif ($this->result->state == ResultProxy::ROWS_FETCHED) { return new Success(null); } else { $deferred = new Deferred(); /* We need to increment the internal counter, else the next time genericFetch is called, * it'll simply return the row we fetch here instead of fetching a new row * since callback order on promises isn't defined, we can't do this via when() */ $incRow = function ($row) use($cb) { $this->result->userFetched++; return $cb && $row ? $cb($row) : $row; }; $this->result->deferreds[ResultProxy::SINGLE_ROW_FETCH][] = [$deferred, null, $incRow]; return $deferred->promise(); } }
/** * Succeeds with true if an emitted value is available by calling getCurrent() or false if the observable has * resolved. If the observable fails, the returned promise will fail with the same exception. * * @return \Interop\Async\Promise<bool> */ public function next() : Promise { if (isset($this->deferreds[$this->position])) { $future = $this->deferreds[$this->position]; unset($this->values[$this->position], $this->deferreds[$this->position]); $future->resolve(); } ++$this->position; if (\array_key_exists($this->position, $this->values)) { return new Success(true); } if ($this->resolved) { --$this->position; if ($this->exception) { return new Failure($this->exception); } return new Success(false); } $this->deferred = new Deferred(); return $this->deferred->promise(); }
/** * {@inheritdoc} */ public function put($path, $contents) { $flags = \EIO_O_RDWR | \EIO_O_CREAT; $mode = \EIO_S_IRUSR | \EIO_S_IWUSR | \EIO_S_IXUSR; $priority = \EIO_PRI_DEFAULT; $this->incrementPending(); $promisor = new Deferred(); $data = [$contents, $promisor]; \eio_open($path, $flags, $mode, $priority, [$this, "onPutOpen"], $data); return $promisor->promise(); }
private function successfulResultsetFetch() { $deferred =& $this->result->next; if ($this->connInfo->statusFlags & StatusFlags::SERVER_MORE_RESULTS_EXISTS) { $this->parseCallback = [$this, "handleQuery"]; $this->deferreds[] = $deferred ?: ($deferred = new Deferred()); } else { if ($deferred) { $deferred->succeed(null); } else { $deferred = new Success(null); } $this->parseCallback = null; } $this->query = null; $this->ready(); $this->result->updateState(ResultProxy::ROWS_FETCHED); }
function __doRequest($state, $uri, $name, $type) { $server = __loadExistingServer($state, $uri) ?: __loadNewServer($state, $uri); // Get the next available request ID do { $requestId = $state->requestIdCounter++; if ($state->requestIdCounter >= MAX_REQUEST_ID) { $state->requestIdCounter = 1; } } while (isset($state->pendingRequests[$requestId])); // Create question record $question = $state->questionFactory->create($type); $question->setName($name); // Create request message $request = $state->messageFactory->create(MessageTypes::QUERY); $request->getQuestionRecords()->add($question); $request->isRecursionDesired(true); $request->setID($requestId); // Encode request message $requestPacket = $state->encoder->encode($request); if (substr($uri, 0, 6) == "tcp://") { $requestPacket = pack("n", strlen($requestPacket)) . $requestPacket; } // Send request $bytesWritten = \fwrite($server->socket, $requestPacket); if ($bytesWritten === false || isset($packet[$bytesWritten])) { throw new ResolutionException("Request send failed"); } $promisor = new Deferred(); $server->pendingRequests[$requestId] = true; $state->pendingRequests[$requestId] = [$promisor, $name, $type, $uri]; return $promisor->promise(); }
/** * @param string $pattern * @return Promise */ public function pSubscribe($pattern) { $promisor = new Deferred(); $promise = $this->connection->send(["psubscribe", $pattern]); $promise->when(function ($error) use($pattern, $promisor) { if ($error) { $promisor->fail($error); } else { $this->patternPromisors[$pattern][] = $promisor; $promisor->promise()->when(function () use($pattern) { array_shift($this->patternPromisors[$pattern]); }); } }); return $promisor->promise(); }
public function getFields() { if ($this->result->state >= ResultProxy::COLUMNS_FETCHED) { return new Success($this->result->columns); } elseif (isset($this->result->deferreds[ResultProxy::COLUMNS_FETCHED][0])) { return $this->result->deferreds[ResultProxy::COLUMNS_FETCHED][0][0]->promise(); } else { $deferred = new Deferred(); $this->result->deferreds[ResultProxy::COLUMNS_FETCHED][0] = [$deferred, &$this->result->columns, null]; return $deferred->promise(); } }
/** * Write data to the socket * * Upon write completion the returned promise will resolve to an integer indicating * the number of bytes written. If no bytes were written prior to disconnection the * returned promise resolves to NULL. * * @param string $data * @return \Amp\Promise<int|null> */ public function write($data) { $len = \strlen($data); if (!($len && \is_string($data))) { return new amp\Failure(new \LogicException("String of minimum length 1 required")); } $state = $this->state; if (!$this->alive()) { return new amp\Success(null); } if (empty($state->isWriteEnabled)) { amp\enable($this->state->writeWatcherId); $state->isWriteEnabled = true; } $op = new \StdClass(); $op->buffer = $data; $op->size = $len; $op->bytesWritten = null; $op->promisor = $promisor = new amp\Deferred(); $state->writeOperations[] = $op; return $promisor->promise(); }
private function sumMultipartFieldLengths(array $fields) { $lengths = []; foreach ($fields as $field) { if (is_string($field)) { $lengths[] = strlen($field); } else { $lengths[] = $field->getLength(); } } $promisor = new Deferred(); \Amp\all($lengths)->when(function ($error, $result) use($promisor) { if ($error) { $promisor->fail($error); } else { $promisor->succeed(array_sum($result)); } }); return $promisor->promise(); }
private function doPut($path, $contents) : \Generator { $flags = \UV::O_WRONLY | \UV::O_CREAT; $mode = \UV::S_IRWXU | \UV::S_IRUSR; $this->reactor->addRef(); $promise = $this->doFsOpen($path, $flags, $mode); if (!($fh = (yield $promise))) { $this->reactor->delRef(); throw new \RuntimeException("Failed opening write file handle"); } $promisor = new Deferred(); $len = strlen($contents); \uv_fs_write($this->loop, $fh, $contents, $offset = 0, function ($fh, $result) use($promisor, $len) { \uv_fs_close($this->loop, $fh, function () use($promisor, $result, $len) { $this->reactor->delRef(); if ($result < 0) { $promisor->fail(new \RuntimeException(uv_strerror($result))); } else { $promisor->succeed($len); } }); }); (yield new \Amp\CoroutineResult((yield $promisor->promise()))); }
private function finalizeNewConnection(Deferred $promisor, $uri, $socket, $options) { if (--$this->pendingSockets[$uri] === 0) { unset($this->pendingSockets[$uri]); } $socketId = (int) $socket; $poolStruct = new SocketPoolStruct(); $poolStruct->id = $socketId; $poolStruct->uri = $uri; $poolStruct->resource = $socket; $poolStruct->isAvailable = false; $poolStruct->msIdleTimeout = $options[self::OP_MS_IDLE_TIMEOUT]; $this->sockets[$uri][$socketId] = $poolStruct; $this->socketIdUriMap[$socketId] = $uri; $promisor->succeed($poolStruct->resource); if (empty($this->queuedSocketRequests[$uri])) { unset($this->queuedSocketRequests[$uri]); } }