Page MenuHomePhabricator

D15954.diff
No OneTemporary

D15954.diff

diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -662,6 +662,7 @@
'DiffusionGitResponse' => 'applications/diffusion/response/DiffusionGitResponse.php',
'DiffusionGitSSHWorkflow' => 'applications/diffusion/ssh/DiffusionGitSSHWorkflow.php',
'DiffusionGitUploadPackSSHWorkflow' => 'applications/diffusion/ssh/DiffusionGitUploadPackSSHWorkflow.php',
+ 'DiffusionGitWireProtocol' => 'applications/diffusion/protocol/DiffusionGitWireProtocol.php',
'DiffusionHistoryController' => 'applications/diffusion/controller/DiffusionHistoryController.php',
'DiffusionHistoryQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php',
'DiffusionHistoryTableView' => 'applications/diffusion/view/DiffusionHistoryTableView.php',
@@ -4906,6 +4907,7 @@
'DiffusionRepositoryClusterEngineLogInterface',
),
'DiffusionGitUploadPackSSHWorkflow' => 'DiffusionGitSSHWorkflow',
+ 'DiffusionGitWireProtocol' => 'Phobject',
'DiffusionHistoryController' => 'DiffusionController',
'DiffusionHistoryQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
'DiffusionHistoryTableView' => 'DiffusionView',
diff --git a/src/applications/diffusion/protocol/DiffusionGitWireProtocol.php b/src/applications/diffusion/protocol/DiffusionGitWireProtocol.php
new file mode 100644
--- /dev/null
+++ b/src/applications/diffusion/protocol/DiffusionGitWireProtocol.php
@@ -0,0 +1,176 @@
+<?php
+
+final class DiffusionGitWireProtocol extends Phobject {
+
+ private $buffer = '';
+ private $state = 'head';
+ private $expectBytes;
+ private $expectPacks;
+
+ private $packType;
+ private $packSize;
+ private $packShift;
+
+ public function writeData($data) {
+ $this->buffer .= $data;
+
+ while (true) {
+ if ($this->state == 'head') {
+ if (strlen($this->buffer) < 4) {
+ break;
+ }
+
+ $head = substr($this->buffer, 0, 4);
+ $this->buffer = substr($this->buffer, 4);
+
+ if ($head == 'PACK') {
+ $this->state = 'pack-version';
+ continue;
+ }
+
+ if (!preg_match('/^[a-f0-9]{4}\z/', $head)) {
+ throw new Exception(
+ pht(
+ 'Expected Git wire protocol frame length header (four hex '.
+ 'characters, like "02af") but got "%s".',
+ $head));
+ }
+
+ $bytes = hexdec($head);
+
+ // NOTE: This header is documented as containing the length of the
+ // "rest of the line", but in practice it appears to include its own
+ // length.
+ if ($bytes >= 4) {
+ $this->expectBytes = ($bytes - 4);
+ } else if (!$bytes) {
+ $this->expectBytes = $bytes;
+ } else {
+ throw new Exception(
+ pht(
+ 'Expected Git wire protocol frame length header with valid '.
+ 'value (0, or >= 4 bytes) but got invalid value ("%s").',
+ $bytes));
+ }
+
+ $this->state = 'body';
+ } else if ($this->state == 'body') {
+ if (strlen($this->buffer) < $this->expectBytes) {
+ break;
+ }
+
+ $body = substr($this->buffer, 0, $this->expectBytes);
+ $this->buffer = substr($this->buffer, $this->expectBytes);
+
+ $this->state = 'head';
+ } else if ($this->state == 'pack-version') {
+ if (strlen($this->buffer) < 4) {
+ break;
+ }
+
+ $version = substr($this->buffer, 0, 4);
+ $this->buffer = substr($this->buffer, 4);
+
+ $expect = "\x00\x00\x00\x02";
+ if ($version !== $expect) {
+ throw new Exception(
+ pht(
+ 'Expected Git wire protocol packfile version 2 (four binary '.
+ 'bytes, "%s") but got "%s".',
+ bin2hex($expect),
+ bin2hex($version)));
+ }
+
+ $this->state = 'pack-length';
+ } else if ($this->state == 'pack-length') {
+ if (strlen($this->buffer) < 4) {
+ break;
+ }
+
+ $length = substr($this->buffer, 0, 4);
+ $this->buffer = substr($this->buffer, 4);
+
+ $value = head(unpack('N', $length));
+ if (!$value) {
+ throw new Exception(
+ pht(
+ 'Expected Git wire protocol pack count (four binary '.
+ 'bytes) but got "%s".',
+ bin2hex($length)));
+ }
+
+ $this->expectPacks = $value;
+ $this->state = 'pack-head';
+ } else if ($this->state == 'pack-head') {
+ if (!$this->expectPacks) {
+ $this->state = 'pack-tail';
+ continue;
+ }
+
+ if (!strlen($this->buffer)) {
+ break;
+ }
+
+ $this->expectPacks--;
+
+ $byte = substr($this->buffer, 0, 1);
+ $this->buffer = substr($this->buffer, 1);
+
+ $byte = ord($byte);
+ $this->packType = ($byte >> 4) & 7;
+ $this->packSize = ($byte & 15);
+ $this->packShift = 4;
+
+ if ($byte & 0x80) {
+ $this->state = 'pack-head-more';
+ } else {
+ $this->state = 'pack-body';
+ }
+ } else if ($this->state == 'pack-head-more') {
+ if (!strlen($this->buffer)) {
+ break;
+ }
+
+ $byte = substr($this->buffer, 0, 1);
+ $this->buffer = substr($this->buffer, 1);
+
+ $byte = ord($byte);
+
+ $this->packSize += ($byte & 0x7F) << $this->packShift;
+ $this->packShift += 7;
+
+ if ($byte & 0x80) {
+ $this->state = 'pack-head-more';
+ } else {
+ $this->state = 'pack-body';
+ }
+ } else if ($this->state == 'pack-body') {
+ $available = strlen($this->buffer);
+ $expect = $this->packSize;
+
+ $read_bytes = min($available, $expect);
+ if ($read_bytes) {
+ $pack_data = substr($this->buffer, 0, $read_bytes);
+ $this->buffer = substr($this->buffer, $read_bytes);
+ $this->packSize -= $read_bytes;
+ }
+
+ if (!$this->expectBytes) {
+ $this->state = 'pack-head';
+ }
+ } else if ($this->state == 'pack-tail') {
+ if (strlen($this->buffer) < 20) {
+ break;
+ }
+
+ $sha1 = substr($this->buffer, 0, 20);
+ $this->buffer = substr($this->buffer, 20);
+
+ $this->state = 'head';
+ }
+ }
+
+ return $data;
+ }
+
+}
diff --git a/src/applications/diffusion/ssh/DiffusionGitReceivePackSSHWorkflow.php b/src/applications/diffusion/ssh/DiffusionGitReceivePackSSHWorkflow.php
--- a/src/applications/diffusion/ssh/DiffusionGitReceivePackSSHWorkflow.php
+++ b/src/applications/diffusion/ssh/DiffusionGitReceivePackSSHWorkflow.php
@@ -2,6 +2,9 @@
final class DiffusionGitReceivePackSSHWorkflow extends DiffusionGitSSHWorkflow {
+ private $inProtocol;
+ private $outProtocol;
+
protected function didConstruct() {
$this->setName('git-receive-pack');
$this->setArguments(
@@ -28,6 +31,7 @@
if ($this->shouldProxy()) {
$command = $this->getProxyCommand();
+ $intercept = false;
$did_synchronize = false;
if ($device) {
@@ -38,6 +42,7 @@
}
} else {
$command = csprintf('git-receive-pack %s', $repository->getLocalPath());
+ $intercept = true;
$did_synchronize = true;
$cluster_engine->synchronizeWorkingCopyBeforeWrite();
@@ -51,7 +56,7 @@
$caught = null;
try {
- $err = $this->executeRepositoryCommand($command);
+ $err = $this->executeRepositoryCommand($command, $intercept);
} catch (Exception $ex) {
$caught = $ex;
}
@@ -76,17 +81,51 @@
return $err;
}
- private function executeRepositoryCommand($command) {
+ private function executeRepositoryCommand($command, $intercept) {
$repository = $this->getRepository();
$command = PhabricatorDaemon::sudoCommandAsDaemonUser($command);
$future = id(new ExecFuture('%C', $command))
->setEnv($this->getEnvironment());
- return $this->newPassthruCommand()
+ $command = $this->newPassthruCommand()
->setIOChannel($this->getIOChannel())
- ->setCommandChannelFromExecFuture($future)
- ->execute();
+ ->setCommandChannelFromExecFuture($future);
+
+ if ($intercept) {
+ $this->inProtocol = new DiffusionGitWireProtocol();
+ $this->outProtocol = new DiffusionGitWireProtocol();
+
+ $command
+ ->setWillReadCallback(array($this, 'willReadMessageCallback'))
+ ->setWillWriteCallback(array($this, 'willWriteMessageCallback'));
+ }
+
+ return $command->execute();
+ }
+
+ public function willReadMessageCallback(
+ PhabricatorSSHPassthruCommand $command,
+ $message) {
+
+ $f = fopen('/tmp/git-response.log', 'a');
+ fwrite($f, $message);
+ fflush($f);
+ fclose($f);
+
+ return $this->outProtocol->writeData($message);
+ }
+
+ public function willWriteMessageCallback(
+ PhabricatorSSHPassthruCommand $command,
+ $message) {
+
+ $f = fopen('/tmp/git-request.log', 'a');
+ fwrite($f, $message);
+ fflush($f);
+ fclose($f);
+
+ return $this->inProtocol->writeData($message);
}
}

File Metadata

Mime Type
text/plain
Expires
Wed, Nov 13, 2:37 AM (1 d, 19 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6712458
Default Alt Text
D15954.diff (9 KB)

Event Timeline