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; } }
/** * Write specified $dataToWrite to the $socket destination stream * * @param resource $socket * @param string $dataToWrite * @return \Amp\Promise */ public function write($socket, $dataToWrite) { $this->promisor = new Deferred(); $this->socket = $socket; $this->buffer = $dataToWrite; \Amp\immediately(function () { $this->doWrite(); }); return $this->promisor->promise(); }
public function addConnection() { \Amp\immediately(function () { if (count($this->connections) >= $this->limit) { return; } $this->connections[] = $conn = new Connection($this->config); end($this->connections); $this->connectionMap[spl_object_hash($conn)] = key($this->connections); $this->connectionPromise = $conn->connect(); $this->connectionPromise->when(function ($error) use($conn) { if ($error) { $this->unmapConnection($conn); if (empty($this->connections)) { $this->virtualConnection->fail($error); } return; } if ($this->config->charset != "utf8mb4" || $this->config->collate != "" && $this->config->collate != "utf8mb4_general_ci") { $conn->setCharset($this->config->charset, $this->config->collate); } }); }); }
/** * @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(); }
public function do(InternalRequest $ireq) { $headers = yield; if ($headers[":status"] == 101) { $yield = (yield $headers); } else { return $headers; // detach if we don't want to establish websocket connection } while ($yield !== null) { $yield = (yield $yield); } \Amp\immediately([$this, "reapClient"], ["cb_data" => $ireq]); }
public function do(InternalRequest $ireq) { $headers = yield; if ($headers[":status"] == 101) { $yield = (yield $headers); } else { return $headers; // detach if we don't want to establish websocket connection } while ($yield !== null) { $yield = (yield $yield); } \Amp\immediately(function () use($ireq) { $client = new Rfc6455Client(); $client->connectedAt = $this->now; $socket = $ireq->client->socket; $client->id = (int) $socket; $client->socket = $socket; $client->writeBuffer = $ireq->client->writeBuffer; $client->serverRefClearer = ($ireq->client->exporter)($ireq->client); $client->parser = $this->parser([$this, "onParse"], $options = ["cb_data" => $client]); $client->readWatcher = \Amp\onReadable($socket, [$this, "onReadable"], $options = ["enable" => true, "cb_data" => $client]); $client->writeWatcher = \Amp\onWritable($socket, [$this, "onWritable"], $options = ["enable" => $client->writeBuffer != "", "cb_data" => $client]); $this->clients[$client->id] = $client; $this->heartbeatTimeouts[$client->id] = $this->now + $this->heartbeatPeriod; resolve($this->tryAppOnOpen($client->id, $ireq->locals["aerys.websocket"])); }); }
private function parseSocketData(RequestCycle $cycle) { try { while ($parsedResponseArr = $cycle->parser->parse()) { if ($parsedResponseArr['headersOnly']) { $data = [Notify::RESPONSE_HEADERS, $parsedResponseArr]; $cycle->futureResponse->update($data); continue; } elseif (isset($cycle->continueWatcher) && $parsedResponseArr['status'] == 100) { $this->proceedFrom100ContinueState($cycle); } else { $this->assignParsedResponse($cycle, $parsedResponseArr); } if ($cycle->parser->getBuffer()) { \Amp\immediately(function () use($cycle) { $this->parseSocketData($cycle); }); } break; } } catch (ParseException $e) { $this->fail($cycle, $e); } }
/** * @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(); }
/** * @param string $name field name * @param int $size <= 0: use last size, if none present, count toward total size, else separate size just respecting value size * @return FieldBody */ public function stream(string $name, int $size = 0) : FieldBody { if ($this->req) { if ($size > 0) { if (!empty($this->curSizes)) { foreach ($this->curSizes[$name] as $partialSize) { $size -= $partialSize; if (!isset($this->sizes[$name])) { $this->usedSize -= $partialSize - \strlen($name); } } } $this->sizes[$name] = $size; $this->body = $this->req->getBody($this->totalSize += $size); } if (!$this->parsing) { $this->parsing = true; \Amp\immediately(function () { return $this->initIncremental(); }); } if (empty($this->bodies[$name])) { $this->bodyPromisors[$name][] = [$body = new Deferred(), $metadata = new Deferred()]; return new FieldBody($body->promise(), $metadata->promise()); } } elseif (empty($this->bodies[$name])) { return new FieldBody(new Success(), new Success([])); } $key = key($this->bodies[$name]); $ret = $this->bodies[$name][$key]; unset($this->bodies[$name][$key], $this->curSizes[$name][$key]); return $ret; }
public function do(InternalRequest $ireq) { $headers = yield; if ($headers[":status"] == 101) { $yield = (yield $headers); } else { return $headers; // detach Middleware otherwise } while ($yield !== null) { $yield = (yield $yield); } \Amp\immediately([$this, "reapClient"], ["cb_data" => $ireq]); }