public function addExecFutureClient(ExecFuture $future) { $io_channel = new PhutilExecChannel($future); $protocol_channel = new PhutilPHPObjectProtocolChannel($io_channel); $server_channel = new PhutilConsoleServerChannel($protocol_channel); $io_channel->setStderrHandler(array($server_channel, 'didReceiveStderr')); return $this->addClient($server_channel); }
public function setCommandChannelFromExecFuture(ExecFuture $exec_future) { $exec_channel = new PhutilExecChannel($exec_future); $exec_channel->setStderrHandler(array($this, 'writeErrorIOCallback')); $this->execFuture = $exec_future; $this->commandChannel = $exec_channel; return $this; }
public function testCloseExecWriteChannel() { $future = new ExecFuture('cat'); // If this test breaks, we want to explode, not hang forever. $future->setTimeout(5); $exec_channel = new PhutilExecChannel($future); $exec_channel->write('quack'); $exec_channel->closeWriteChannel(); // If `closeWriteChannel()` did what it is supposed to, this will just // echo "quack" and exit with no error code. If the channel did not close, // this will time out after 5 seconds and throw. $future->resolvex(); $this->assertTrue(true); }
protected function identifyRepository() { // NOTE: In SVN, we need to read the first few protocol frames before we // can determine which repository the user is trying to access. We're // going to peek at the data on the wire to identify the repository. $io_channel = $this->getIOChannel(); // Before the client will send us the first protocol frame, we need to send // it a connection frame with server capabilities. To figure out the // correct frame we're going to start `svnserve`, read the frame from it, // send it to the client, then kill the subprocess. // TODO: This is pretty inelegant and the protocol frame will change very // rarely. We could cache it if we can find a reasonable way to dirty the // cache. $command = csprintf('svnserve -t'); $command = PhabricatorDaemon::sudoCommandAsDaemonUser($command); $future = new ExecFuture('%C', $command); $exec_channel = new PhutilExecChannel($future); $exec_protocol = new DiffusionSubversionWireProtocol(); while (true) { PhutilChannel::waitForAny(array($exec_channel)); $exec_channel->update(); $exec_message = $exec_channel->read(); if ($exec_message !== null) { $messages = $exec_protocol->writeData($exec_message); if ($messages) { $message = head($messages); $raw = $message['raw']; // Write the greeting frame to the client. $io_channel->write($raw); // Kill the subprocess. $future->resolveKill(); break; } } if (!$exec_channel->isOpenForReading()) { throw new Exception(pht('%s subprocess exited before emitting a protocol frame.', 'svnserve')); } } $io_protocol = new DiffusionSubversionWireProtocol(); while (true) { PhutilChannel::waitForAny(array($io_channel)); $io_channel->update(); $in_message = $io_channel->read(); if ($in_message !== null) { $this->peekBuffer .= $in_message; if (strlen($this->peekBuffer) > 1024 * 1024) { throw new Exception(pht('Client transmitted more than 1MB of data without transmitting ' . 'a recognizable protocol frame.')); } $messages = $io_protocol->writeData($in_message); if ($messages) { $message = head($messages); $struct = $message['structure']; // This is the: // // ( version ( cap1 ... ) url ... ) // // The `url` allows us to identify the repository. $uri = $struct[2]['value']; $path = $this->getPathFromSubversionURI($uri); return $this->loadRepositoryWithPath($path, PhabricatorRepositoryType::REPOSITORY_TYPE_SVN); } } if (!$io_channel->isOpenForReading()) { throw new Exception(pht('Client closed connection before sending a complete protocol ' . 'frame.')); } // If the client has disconnected, kill the subprocess and bail. if (!$io_channel->isOpenForWriting()) { throw new Exception(pht('Client closed connection before receiving response.')); } } }