Exemplo n.º 1
0
 private function collectProcessGarbage()
 {
     foreach ($this->processes as $key => $procHandle) {
         $info = proc_get_status($procHandle);
         if ($info["running"]) {
             continue;
         }
         $this->defunctProcessCount--;
         proc_close($procHandle);
         unset($this->processes[$key]);
         if ($this->expectedFailures > 0) {
             $this->expectedFailures--;
             continue;
         }
         if (!$this->stopPromisor) {
             $this->spawn();
         }
     }
     // If we've reaped all known dead processes we can stop checking
     if (empty($this->defunctProcessCount)) {
         \Amp\disable($this->procGarbageWatcher);
     }
     if ($this->stopPromisor && empty($this->processes)) {
         \Amp\cancel($this->procGarbageWatcher);
         if ($this->stopPromisor !== true) {
             \Amp\immediately([$this->stopPromisor, "succeed"]);
         }
         $this->stopPromisor = true;
     }
 }
Exemplo n.º 2
0
 private function onWritable()
 {
     if ($this->isDead) {
         return;
     }
     if ($this->writeBuffer === "") {
         $this->writeBuffer = implode("", $this->writeQueue);
         $this->writeQueue = [];
     }
     $bytes = @fwrite($this->ipcSock, $this->writeBuffer);
     if ($bytes === false) {
         $this->onDeadIpcSock();
         return;
     }
     if ($bytes !== \strlen($this->writeBuffer)) {
         $this->writeBuffer = substr($this->writeBuffer, $bytes);
         return;
     }
     if ($this->writeQueue) {
         $this->writeBuffer = implode("", $this->writeQueue);
         $this->writeQueue = [];
         return;
     }
     $this->writeBuffer = "";
     \Amp\disable($this->writeWatcherId);
 }
Exemplo n.º 3
0
 private function onWritable()
 {
     if ($this->isDead) {
         return;
     }
     if ($this->writeBuffer === "") {
         $this->writeBuffer = implode("", $this->writeQueue);
         $this->writeQueue = [];
     }
     $bytes = @fwrite($this->ipcSock, $this->writeBuffer);
     if ($bytes === false) {
         $this->onDeadIpcSock();
         return;
     }
     if ($bytes !== \strlen($this->writeBuffer)) {
         $this->writeBuffer = substr($this->writeBuffer, $bytes);
         return;
     }
     if ($this->writeQueue) {
         $this->writeBuffer = implode("", $this->writeQueue);
         $this->writeQueue = [];
         return;
     }
     $this->writeBuffer = "";
     if ($this->stopPromisor) {
         \Amp\cancel($this->writeWatcherId);
         $promisor = $this->stopPromisor;
         $this->stopPromisor = null;
         $promisor->succeed();
     } else {
         \Amp\disable($this->writeWatcherId);
     }
 }
Exemplo n.º 4
0
 private function checkoutExistingSocket($uri, $options)
 {
     if (empty($this->sockets[$uri])) {
         return null;
     }
     $needsRebind = false;
     foreach ($this->sockets[$uri] as $socketId => $poolStruct) {
         if (!$poolStruct->isAvailable) {
             continue;
         } elseif ($this->isSocketDead($poolStruct->resource)) {
             unset($this->sockets[$uri][$socketId]);
         } elseif (($bindToIp = @stream_context_get_options($poolStruct->resource)['socket']['bindto']) && $bindToIp == $options[self::OP_BINDTO]) {
             $poolStruct->isAvailable = false;
             \Amp\disable($poolStruct->idleWatcher);
             return $poolStruct->resource;
         } elseif ($bindToIp) {
             $needsRebind = true;
         } else {
             $poolStruct->isAvailable = false;
             \Amp\disable($poolStruct->idleWatcher);
             return $poolStruct->resource;
         }
     }
     $this->needsRebind = $needsRebind;
     return null;
 }
Exemplo n.º 5
0
 /**
  * We have to keep a static reference of eio event streams
  * because if we don't garbage collection can unload eio's
  * underlying pipe via a system close() call before it's
  * finished and generate a SIGPIPE.
  */
 public function __construct()
 {
     if (empty(self::$stream)) {
         \eio_init();
         self::$stream = \eio_get_event_stream();
     }
     $this->callableDecrementor = function () {
         \call_user_func($this->incrementor, -1);
     };
     $this->incrementor = function ($increment) {
         switch ($increment) {
             case 1:
             case -1:
                 $this->pending += $increment;
                 break;
             default:
                 throw new FilesystemException("Invalid pending event increment; 1 or -1 required");
         }
         if ($this->pending === 0) {
             \Amp\disable($this->watcher);
         } elseif ($this->pending === 1) {
             \Amp\enable($this->watcher);
         }
     };
     $this->watcher = \Amp\onReadable(self::$stream, function () {
         while (\eio_npending()) {
             \eio_poll();
         }
     }, $options = ["enable" => false]);
 }
Exemplo n.º 6
0
 private function onWritable()
 {
     $flush = \pg_flush($this->db);
     if ($flush) {
         // Write was fully flushed; we're finished and can disable the watcher.
         \Amp\disable($this->writeWatcher);
         return;
     }
     if ($flush === FALSE) {
         $this->failCurrentOperation();
     }
 }
Exemplo n.º 7
0
 /**
  * @param int $buffer one of the self::BUFFER_* constants. Determines whether it will buffer the stdout and/or stderr data internally
  * @return Promise is updated with ["out", $data] or ["err", $data] for data received on stdout or stderr
  * That Promise will be resolved to a stdClass object with stdout, stderr (when $buffer is true), exit (holding exit code) and signal (only present when terminated via signal) properties
  */
 public function exec($buffer = self::BUFFER_NONE)
 {
     if ($this->proc) {
         throw new \RuntimeException("Process was already launched");
     }
     $cwd = isset($this->options["cwd"]) ? $this->options["cwd"] : NULL;
     $env = isset($this->options["env"]) ? $this->options["env"] : NULL;
     if (stripos(PHP_OS, "WIN") !== 0) {
         $fds = [["pipe", "r"], ["pipe", "w"], ["pipe", "w"], ["pipe", "w"]];
         $this->proc = @proc_open("{$this->cmd}; echo \$? >&3", $fds, $pipes, $cwd, $env, $this->options);
     } else {
         $options = $this->options;
         $options["bypass_shell"] = true;
         $fds = [["pipe", "r"], ["pipe", "w"], ["pipe", "w"]];
         $this->proc = @proc_open($this->cmd, $fds, $pipes, $cwd, $env, $options);
     }
     if (!$this->proc) {
         return new Failure(new \RuntimeException("Failed executing command: {$this->cmd}"));
     }
     $this->writeBuf = "";
     $this->writeTotal = 0;
     $this->writeCur = 0;
     stream_set_blocking($pipes[0], false);
     stream_set_blocking($pipes[1], false);
     stream_set_blocking($pipes[2], false);
     if (isset($pipes[3])) {
         stream_set_blocking($pipes[3], false);
         $this->openPipes = 3;
     } else {
         $this->openPipes = 2;
     }
     $this->deferred = new Deferred();
     $result = new \stdClass();
     if ($buffer & self::BUFFER_STDOUT) {
         $result->stdout = "";
     }
     if ($buffer & self::BUFFER_STDERR) {
         $result->stderr = "";
     }
     $cleanup = function () use($result) {
         \Amp\cancel($this->stdin);
         $deferreds = $this->writeDeferreds;
         $this->writeDeferreds = [];
         $status = \proc_get_status($this->proc);
         if ($status["running"] === false && $status["signaled"]) {
             $result->signal = $status["termsig"];
             $result->exit = $status["exitcode"];
         }
         if (!isset($this->exit)) {
             $result->exit = proc_close($this->proc);
         }
         unset($this->proc);
         $this->deferred->succeed($result);
         foreach ($deferreds as $deferred) {
             $deferred->fail(new \Exception("Write could not be completed, process finished"));
         }
     };
     $this->stdout = \Amp\onReadable($pipes[1], function ($watcher, $sock) use($result, $cleanup) {
         if ("" == ($data = @\fread($sock, 8192))) {
             \Amp\cancel($watcher);
             if (--$this->openPipes == 0) {
                 \Amp\immediately($cleanup);
             }
         } else {
             if (isset($result->stdout)) {
                 $result->stdout .= $data;
             }
             $this->deferred->update(["out", $data]);
         }
     });
     $this->stderr = \Amp\onReadable($pipes[2], function ($watcher, $sock) use($result, $cleanup) {
         if ("" == ($data = @\fread($sock, 8192))) {
             \Amp\cancel($watcher);
             if (--$this->openPipes == 0) {
                 \Amp\immediately($cleanup);
             }
         } else {
             if (isset($result->stderr)) {
                 $result->stderr .= $data;
             }
             $this->deferred->update(["err", $data]);
         }
     });
     $this->stdin = \Amp\onWritable($pipes[0], function ($watcher, $sock) {
         $this->writeCur += @\fwrite($sock, $this->writeBuf);
         if ($this->writeCur == $this->writeTotal) {
             \Amp\disable($watcher);
         }
         while (($next = key($this->writeDeferreds)) !== null && $next <= $this->writeCur) {
             $this->writeDeferreds[$next]->succeed($this->writeCur);
             unset($this->writeDeferreds[$next]);
         }
     }, ["enable" => false]);
     if (isset($pipes[3])) {
         $this->exit = \Amp\onReadable($pipes[3], function ($watcher, $sock) use($result, $cleanup) {
             stream_set_blocking($sock, true);
             // it should never matter, but just to be really 100% sure.
             $result->exit = (int) stream_get_contents($sock);
             \Amp\cancel($watcher);
             if (--$this->openPipes == 0) {
                 \Amp\immediately($cleanup);
             }
         });
     }
     return $this->deferred->promise();
 }
Exemplo n.º 8
0
 public function parser(Client $client) : \Generator
 {
     $maxHeaderSize = $client->options->maxHeaderSize;
     $maxBodySize = $client->options->maxBodySize;
     $bodyEmitSize = $client->options->ioGranularity;
     $buffer = "";
     do {
         // break potential references
         unset($traceBuffer, $protocol, $method, $uri, $headers);
         $traceBuffer = null;
         $headers = [];
         $contentLength = null;
         $isChunked = false;
         $protocol = null;
         $uri = null;
         $method = null;
         $parseResult = ["id" => 0, "trace" => &$traceBuffer, "protocol" => &$protocol, "method" => &$method, "uri" => &$uri, "headers" => &$headers, "body" => ""];
         if ($client->parserEmitLock) {
             do {
                 if (\strlen($buffer) > $maxHeaderSize + $maxBodySize) {
                     \Amp\disable($client->readWatcher);
                     $client->parserEmitLock = false;
                 }
                 $buffer .= yield;
             } while ($client->parserEmitLock);
             \Amp\enable($client->readWatcher);
         }
         $client->parserEmitLock = true;
         while (1) {
             $buffer = \ltrim($buffer, "\r\n");
             if ($headerPos = \strpos($buffer, "\r\n\r\n")) {
                 $startLineAndHeaders = \substr($buffer, 0, $headerPos + 2);
                 $buffer = (string) \substr($buffer, $headerPos + 4);
                 break;
             } elseif ($maxHeaderSize > 0 && strlen($buffer) > $maxHeaderSize) {
                 $error = "Bad Request: header size violation";
                 break 2;
             }
             $buffer .= yield;
         }
         $startLineEndPos = \strpos($startLineAndHeaders, "\n");
         $startLine = \rtrim(substr($startLineAndHeaders, 0, $startLineEndPos), "\r\n");
         $rawHeaders = \substr($startLineAndHeaders, $startLineEndPos + 1);
         $traceBuffer = $startLineAndHeaders;
         if (!($method = \strtok($startLine, " "))) {
             $error = "Bad Request: invalid request line";
             break;
         }
         if (!($uri = \strtok(" "))) {
             $error = "Bad Request: invalid request line";
             break;
         }
         $protocol = \strtok(" ");
         if (stripos($protocol, "HTTP/") !== 0) {
             $error = "Bad Request: invalid request line";
             break;
         }
         $protocol = \substr($protocol, 5);
         if ($protocol != "1.1" && $protocol != "1.0") {
             // @TODO eventually add an option to disable HTTP/2.0 support???
             if ($protocol == "2.0") {
                 $client->httpDriver = $this->http2;
                 $client->requestParser = $client->httpDriver->parser($client);
                 $client->requestParser->send("{$startLineAndHeaders}\r\n{$buffer}");
                 return;
             } else {
                 $error = HttpDriver::BAD_VERSION;
                 break;
             }
         }
         if ($rawHeaders) {
             if (\strpos($rawHeaders, "\n ") || \strpos($rawHeaders, "\n\t")) {
                 $error = "Bad Request: multi-line headers deprecated by RFC 7230";
                 break;
             }
             if (!\preg_match_all(self::HEADER_REGEX, $rawHeaders, $matches)) {
                 $error = "Bad Request: header syntax violation";
                 break;
             }
             list(, $fields, $values) = $matches;
             $headers = [];
             foreach ($fields as $index => $field) {
                 $headers[$field][] = $values[$index];
             }
             if ($headers) {
                 $headers = \array_change_key_case($headers);
             }
             $contentLength = $headers["content-length"][0] ?? null;
             if (isset($headers["transfer-encoding"])) {
                 $value = $headers["transfer-encoding"][0];
                 $isChunked = (bool) \strcasecmp($value, "identity");
             }
             // @TODO validate that the bytes in matched headers match the raw input. If not there is a syntax error.
         }
         if ($contentLength > $maxBodySize) {
             $error = "Bad request: entity too large";
             break;
         } elseif ($method == "HEAD" || $method == "TRACE" || $method == "OPTIONS" || $contentLength === 0) {
             // No body allowed for these messages
             $hasBody = false;
         } else {
             $hasBody = $isChunked || $contentLength;
         }
         if (!$hasBody) {
             ($this->parseEmitter)([HttpDriver::RESULT, $parseResult, null], $client);
             continue;
         }
         ($this->parseEmitter)([HttpDriver::ENTITY_HEADERS, $parseResult, null], $client);
         $body = "";
         if ($isChunked) {
             while (1) {
                 while (false === ($lineEndPos = \strpos($buffer, "\r\n"))) {
                     $buffer .= yield;
                 }
                 $line = \substr($buffer, 0, $lineEndPos);
                 $buffer = \substr($buffer, $lineEndPos + 2);
                 $hex = \trim(\ltrim($line, "0")) ?: 0;
                 $chunkLenRemaining = \hexdec($hex);
                 if ($lineEndPos === 0 || $hex != \dechex($chunkLenRemaining)) {
                     $error = "Bad Request: hex chunk size expected";
                     break 2;
                 }
                 if ($chunkLenRemaining === 0) {
                     while (!isset($buffer[1])) {
                         $buffer .= yield;
                     }
                     $firstTwoBytes = \substr($buffer, 0, 2);
                     if ($firstTwoBytes === "\r\n") {
                         $buffer = \substr($buffer, 2);
                         break;
                         // finished ($is_chunked loop)
                     }
                     do {
                         if ($trailerSize = \strpos($buffer, "\r\n\r\n")) {
                             $trailers = \substr($buffer, 0, $trailerSize + 2);
                             $buffer = \substr($buffer, $trailerSize + 4);
                         } else {
                             $buffer .= yield;
                             $trailerSize = \strlen($buffer);
                             $trailers = null;
                         }
                         if ($maxHeaderSize > 0 && $trailerSize > $maxHeaderSize) {
                             $error = "Trailer headers too large";
                             break 3;
                         }
                     } while (!isset($trailers));
                     if (\strpos($trailers, "\n ") || \strpos($trailers, "\n\t")) {
                         $error = "Bad Request: multi-line trailers deprecated by RFC 7230";
                         break 2;
                     }
                     if (!\preg_match_all(self::HEADER_REGEX, $trailers, $matches)) {
                         $error = "Bad Request: trailer syntax violation";
                         break 2;
                     }
                     list(, $fields, $values) = $matches;
                     $trailers = [];
                     foreach ($fields as $index => $field) {
                         $trailers[$field][] = $values[$index];
                     }
                     if ($trailers) {
                         $trailers = \array_change_key_case($trailers);
                         foreach (["transfer-encoding", "content-length", "trailer"] as $remove) {
                             unset($trailers[$remove]);
                         }
                         if ($trailers) {
                             $headers = \array_merge($headers, $trailers);
                         }
                     }
                     break;
                     // finished ($is_chunked loop)
                 } elseif ($chunkLenRemaining > $maxBodySize) {
                     $error = "Bad Request: excessive chunk size";
                     break 2;
                 } else {
                     $bodyBufferSize = 0;
                     while (1) {
                         $bufferLen = \strlen($buffer);
                         // These first two (extreme) edge cases prevent errors where the packet boundary ends after
                         // the \r and before the \n at the end of a chunk.
                         if ($bufferLen === $chunkLenRemaining || $bufferLen === $chunkLenRemaining + 1) {
                             $buffer .= yield;
                             continue;
                         } elseif ($bufferLen >= $chunkLenRemaining + 2) {
                             $body .= substr($buffer, 0, $chunkLenRemaining);
                             $buffer = substr($buffer, $chunkLenRemaining + 2);
                             $bodyBufferSize += $chunkLenRemaining;
                         } else {
                             $body .= $buffer;
                             $bodyBufferSize += $bufferLen;
                             $chunkLenRemaining -= $bufferLen;
                         }
                         if ($bodyBufferSize >= $bodyEmitSize) {
                             ($this->parseEmitter)([HttpDriver::ENTITY_PART, ["id" => 0, "body" => $body], null], $client);
                             $body = '';
                             $bodyBufferSize = 0;
                         }
                         if ($bufferLen >= $chunkLenRemaining + 2) {
                             $chunkLenRemaining = null;
                             continue 2;
                             // next chunk ($is_chunked loop)
                         } else {
                             $buffer = yield;
                         }
                     }
                 }
             }
         } else {
             $bufferDataSize = \strlen($buffer);
             while ($bufferDataSize < $contentLength) {
                 if ($bufferDataSize >= $bodyEmitSize) {
                     ($this->parseEmitter)([HttpDriver::ENTITY_PART, ["id" => 0, "body" => $buffer], null], $client);
                     $buffer = "";
                     $contentLength -= $bufferDataSize;
                 }
                 $buffer .= yield;
                 $bufferDataSize = \strlen($buffer);
             }
             if ($bufferDataSize === $contentLength) {
                 $body = $buffer;
                 $buffer = "";
             } else {
                 $body = substr($buffer, 0, $contentLength);
                 $buffer = (string) \substr($buffer, $contentLength);
             }
         }
         if ($body != "") {
             ($this->parseEmitter)([HttpDriver::ENTITY_PART, ["id" => 0, "body" => $body], null], $client);
         }
         ($this->parseEmitter)([HttpDriver::ENTITY_RESULT, $parseResult, null], $client);
     } while (true);
     // An error occurred...
     // stop parsing here ...
     ($this->parseEmitter)([HttpDriver::ERROR, $parseResult, $error], $client);
     while (1) {
         yield;
     }
 }
Exemplo n.º 9
0
 private function onWritable(string $watcherId, $socket, $client)
 {
     $bytesWritten = @fwrite($socket, $client->writeBuffer);
     if ($bytesWritten === false) {
         if (!is_resource($socket) || @feof($socket)) {
             $client->isDead = true;
             $this->close($client);
         }
     } elseif ($bytesWritten === strlen($client->writeBuffer)) {
         $client->writeBuffer = "";
         \Amp\disable($watcherId);
         if ($client->onWriteDrain) {
             ($client->onWriteDrain)($client);
         }
     } else {
         $client->writeBuffer = substr($client->writeBuffer, $bytesWritten);
         \Amp\enable($watcherId);
     }
 }
Exemplo n.º 10
0
 public function onWritable($watcherId, $socket, Rfc6455Client $client)
 {
     $bytes = @fwrite($socket, $client->writeBuffer);
     $client->bytesSent += $bytes;
     if ($bytes != \strlen($client->writeBuffer)) {
         $client->writeBuffer = substr($client->writeBuffer, $bytes);
     } elseif ($bytes == 0 && $client->closedAt && (!is_resource($socket) || @feof($socket))) {
         // usually read watcher cares about aborted TCP connections, but when
         // $client->closedAt is true, it might be the case that read watcher
         // is already cancelled and we need to ensure that our writing promise
         // is fulfilled in unloadClient() with a failure
         unset($this->closeTimeouts[$client->id]);
         $this->unloadClient($client);
     } else {
         $client->framesSent++;
         $client->writeDeferred->succeed();
         if ($client->writeControlQueue) {
             $key = key($client->writeControlQueue);
             $client->writeBuffer = $client->writeControlQueue[$key];
             $client->lastSentAt = $this->now;
             $client->writeDeferred = $client->writeDeferredControlQueue[$key];
             unset($client->writeControlQueue[$key], $client->writeDeferredControlQueue[$key]);
             while (\strlen($client->writeBuffer) < 65536 && $client->writeControlQueue) {
                 $key = key($client->writeControlQueue);
                 $client->writeBuffer .= $client->writeControlQueue[$key];
                 $client->writeDeferredControlQueue[$key]->succeed($client->writeDeferred);
                 unset($client->writeControlQueue[$key], $client->writeDeferredControlQueue[$key]);
             }
             while (\strlen($client->writeBuffer) < 65536 && $client->writeDataQueue) {
                 $key = key($client->writeDataQueue);
                 $client->writeBuffer .= $client->writeDataQueue[$key];
                 $client->writeDeferredDataQueue[$key]->succeed($client->writeDeferred);
                 unset($client->writeDataQueue[$key], $client->writeDeferredDataQueue[$key]);
             }
         } elseif ($client->closedAt) {
             @stream_socket_shutdown($socket, STREAM_SHUT_WR);
             \Amp\cancel($watcherId);
             $client->writeWatcher = null;
             $client->writeDeferred = null;
             $client->writeBuffer = "";
         } elseif ($client->writeDataQueue) {
             $key = key($client->writeDataQueue);
             $client->writeBuffer = $client->writeDataQueue[$key];
             $client->lastDataSentAt = $this->now;
             $client->lastSentAt = $this->now;
             $client->writeDeferred = $client->writeDeferredDataQueue[$key];
             unset($client->writeDataQueue[$key], $client->writeDeferredDataQueue[$key]);
             while (\strlen($client->writeBuffer) < 65536 && $client->writeDataQueue) {
                 $key = key($client->writeDataQueue);
                 $client->writeBuffer .= $client->writeDataQueue[$key];
                 $client->writeDeferredDataQueue[$key]->succeed($client->writeDeferred);
                 unset($client->writeDataQueue[$key], $client->writeDeferredDataQueue[$key]);
             }
         } else {
             $client->writeDeferred = null;
             $client->writeBuffer = "";
             \Amp\disable($watcherId);
         }
     }
 }
Exemplo n.º 11
0
 private function decrementPending()
 {
     if ($this->pending-- === 1) {
         \Amp\disable($this->watcher);
     }
 }
Exemplo n.º 12
0
 /**
  * @param $watcherId
  */
 public function onWrite($watcherId)
 {
     if ($this->outputBufferLength === 0) {
         \Amp\disable($watcherId);
         return;
     }
     $bytes = fwrite($this->socket, $this->outputBuffer);
     if ($bytes === 0) {
         $this->state = self::STATE_DISCONNECTED;
         throw new ConnectException("Connection went away (write)", $code = 1);
     } else {
         $this->outputBuffer = (string) substr($this->outputBuffer, $bytes);
         $this->outputBufferLength -= $bytes;
     }
 }
Exemplo n.º 13
0
 private function onWritable(string $watcherId, $socket, $client)
 {
     $bytesWritten = @\fwrite($socket, $client->writeBuffer);
     if ($bytesWritten === false) {
         if (!\is_resource($socket) || @\feof($socket)) {
             $client->isDead = true;
             $this->close($client);
         }
     } else {
         if ($bytesWritten === \strlen($client->writeBuffer)) {
             $client->writeBuffer = "";
             \Amp\disable($watcherId);
             if ($client->onWriteDrain) {
                 ($client->onWriteDrain)($client);
             }
         } else {
             $client->writeBuffer = \substr($client->writeBuffer, $bytesWritten);
             \Amp\enable($watcherId);
         }
         $client->bufferSize -= $bytesWritten;
         if ($client->bufferPromisor && $client->bufferSize <= $client->options->softStreamCap) {
             $client->bufferPromisor->succeed();
             $client->bufferPromisor = null;
         }
     }
 }
Exemplo n.º 14
0
function __finalizeResult($state, $serverId, $requestId, $error = null, $result = null)
{
    if (empty($state->pendingRequests[$requestId])) {
        return;
    }
    list($promisor, $name) = $state->pendingRequests[$requestId];
    $server = $state->serverIdMap[$serverId];
    unset($state->pendingRequests[$requestId], $server->pendingRequests[$requestId]);
    if (empty($server->pendingRequests)) {
        $state->serverIdTimeoutMap[$server->id] = $state->now + IDLE_TIMEOUT;
        \Amp\disable($server->watcherId);
        \Amp\enable($state->serverTimeoutWatcher);
    }
    if ($error) {
        $promisor->fail($error);
    } else {
        foreach ($result as $type => $records) {
            $minttl = INF;
            foreach ($records as list(, $ttl)) {
                if ($ttl && $minttl > $ttl) {
                    $minttl = $ttl;
                }
            }
            $state->arrayCache->set("{$name}#{$type}", $records, $minttl);
        }
        $promisor->succeed($result);
    }
}
Exemplo n.º 15
0
 /**
  * @param int $buffer one of the self::BUFFER_* constants. Determines whether it will buffer the stdout and/or stderr data internally
  * @return Promise is updated with ["out", $data] or ["err", $data] for data received on stdout or stderr
  * That Promise will be resolved to a stdClass object with stdout, stderr (when $buffer is true), exit (holding exit code) and signal (only present when terminated via signal) properties
  */
 public function exec($buffer = self::BUFFER_NONE)
 {
     if ($this->proc) {
         throw new \RuntimeException("Process was already launched");
     }
     $fds = [["pipe", "r"], ["pipe", "w"], ["pipe", "w"]];
     $cwd = isset($this->options["cwd"]) ? $this->options["cwd"] : NULL;
     $env = isset($this->options["env"]) ? $this->options["env"] : NULL;
     if (!($this->proc = @proc_open($this->cmd, $fds, $pipes, $cwd, $env, $this->options))) {
         return new Failure(new \RuntimeException("Failed executing command: {$this->cmd}"));
     }
     $this->writeBuf = "";
     $this->writeTotal = 0;
     $this->writeCur = 0;
     stream_set_blocking($pipes[0], false);
     stream_set_blocking($pipes[1], false);
     stream_set_blocking($pipes[2], false);
     $this->deferred = new Deferred();
     $result = new \stdClass();
     if ($buffer & self::BUFFER_STDOUT) {
         $result->stdout = "";
     }
     if ($buffer & self::BUFFER_STDERR) {
         $result->stderr = "";
     }
     $this->stdout = \Amp\onReadable($pipes[1], function ($watcher, $sock) use($result) {
         if ("" == ($data = @fread($sock, 8192))) {
             \Amp\cancel($watcher);
             \Amp\cancel($this->stdin);
             \Amp\immediately(function () use($result) {
                 $status = proc_get_status($this->proc);
                 assert($status["running"] === false);
                 if ($status["signaled"]) {
                     $result->signal = $status["termsig"];
                 }
                 $result->exit = $status["exitcode"];
                 $this->proc = NULL;
                 $this->deferred->succeed($result);
                 foreach ($this->writeDeferreds as $deferred) {
                     $deferred->fail(new \Exception("Write could not be completed, process finished"));
                 }
                 $this->writeDeferreds = [];
             });
         } else {
             isset($result->stdout) && ($result->stdout .= $data);
             $this->deferred->update(["out", $data]);
         }
     });
     $this->stderr = \Amp\onReadable($pipes[2], function ($watcher, $sock) use($result) {
         if ("" == ($data = @fread($sock, 8192))) {
             \Amp\cancel($watcher);
         } else {
             isset($result->stderr) && ($result->stderr .= $data);
             $this->deferred->update(["err", $data]);
         }
     });
     $this->stdin = \Amp\onWritable($pipes[0], function ($watcher, $sock) {
         $this->writeCur += @fwrite($sock, $this->writeBuf);
         if ($this->writeCur == $this->writeTotal) {
             \Amp\disable($watcher);
         }
         while (($next = key($this->writeDeferreds)) !== null && $next <= $this->writeCur) {
             $this->writeDeferreds[$next]->succeed($this->writeCur);
             unset($this->writeDeferreds[$next]);
         }
     }, ["enable" => false]);
     return $this->deferred->promise();
 }
Exemplo n.º 16
0
 public static function reader($watcher, $socket, $info)
 {
     $buffer =& $info[0];
     $data = @fread($socket, 8192);
     if ($data != "") {
         if ($buffer == "") {
             \Amp\enable($info[1]);
         }
         $buffer .= $data;
         if (\strlen($buffer) > self::MAX_INTERMEDIARY_BUFFER) {
             \Amp\disable($watcher);
         }
     } elseif (!is_resource($socket) || @feof($socket)) {
         \Amp\cancel($watcher);
         if ($buffer == "") {
             \Amp\cancel($info[1]);
         } else {
             $info[2] = true;
         }
     }
 }