[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

/src/applications/diffusion/ssh/ -> DiffusionSSHMercurialWireClientProtocolChannel.php (source)

   1  <?php
   2  
   3  final class DiffusionSSHMercurialWireClientProtocolChannel
   4    extends PhutilProtocolChannel {
   5  
   6    private $buffer = '';
   7    private $state = 'command';
   8    private $expectArgumentCount;
   9    private $argumentName;
  10    private $expectBytes;
  11    private $command;
  12    private $arguments;
  13    private $raw;
  14  
  15    protected function encodeMessage($message) {
  16      return $message;
  17    }
  18  
  19    private function initializeState($last_command = null) {
  20      if ($last_command == 'unbundle') {
  21        $this->command = '<raw-data>';
  22        $this->state = 'data-length';
  23      } else {
  24        $this->state = 'command';
  25      }
  26      $this->expectArgumentCount = null;
  27      $this->expectBytes = null;
  28      $this->command = null;
  29      $this->argumentName = null;
  30      $this->arguments = array();
  31      $this->raw = '';
  32    }
  33  
  34    private function readProtocolLine() {
  35      $pos = strpos($this->buffer, "\n");
  36  
  37      if ($pos === false) {
  38        return null;
  39      }
  40  
  41      $line = substr($this->buffer, 0, $pos);
  42  
  43      $this->raw .= $line."\n";
  44      $this->buffer = substr($this->buffer, $pos + 1);
  45  
  46      return $line;
  47    }
  48  
  49    private function readProtocolBytes() {
  50      if (strlen($this->buffer) < $this->expectBytes) {
  51        return null;
  52      }
  53  
  54      $bytes = substr($this->buffer, 0, $this->expectBytes);
  55      $this->raw .= $bytes;
  56      $this->buffer = substr($this->buffer, $this->expectBytes);
  57  
  58      return $bytes;
  59    }
  60  
  61    private function newMessageAndResetState() {
  62      $message = array(
  63        'command' => $this->command,
  64        'arguments' => $this->arguments,
  65        'raw' => $this->raw,
  66      );
  67      $this->initializeState($this->command);
  68      return $message;
  69    }
  70  
  71    private function newDataMessage($bytes) {
  72      $message = array(
  73        'command' => '<raw-data>',
  74        'raw' => strlen($bytes)."\n".$bytes,
  75      );
  76      return $message;
  77    }
  78  
  79    protected function decodeStream($data) {
  80      $this->buffer .= $data;
  81  
  82      $out = array();
  83      $messages = array();
  84  
  85      while (true) {
  86        if ($this->state == 'command') {
  87          $this->initializeState();
  88  
  89          // We're reading a command. It looks like:
  90          //
  91          //   <command>
  92  
  93          $line = $this->readProtocolLine();
  94          if ($line === null) {
  95            break;
  96          }
  97  
  98          $this->command = $line;
  99          $this->state = 'arguments';
 100        } else if ($this->state == 'arguments') {
 101  
 102          // Check if we're still waiting for arguments.
 103          $args = DiffusionMercurialWireProtocol::getCommandArgs($this->command);
 104          $have = array_select_keys($this->arguments, $args);
 105          if (count($have) == count($args)) {
 106            // We have all the arguments. Emit a message and read the next
 107            // command.
 108            $messages[] = $this->newMessageAndResetState();
 109          } else {
 110            // We're still reading arguments. They can either look like:
 111            //
 112            //   <name> <length(value)>
 113            //   <value>
 114            //   ...
 115            //
 116            // ...or like this:
 117            //
 118            //   * <count>
 119            //   <name1> <length(value1)>
 120            //   <value1>
 121            //   ...
 122  
 123            $line = $this->readProtocolLine();
 124            if ($line === null) {
 125              break;
 126            }
 127  
 128            list($arg, $size) = explode(' ', $line, 2);
 129            $size = (int)$size;
 130  
 131            if ($arg != '*') {
 132              $this->expectBytes = $size;
 133              $this->argumentName = $arg;
 134              $this->state = 'value';
 135            } else {
 136              $this->arguments['*'] = array();
 137              $this->expectArgumentCount = $size;
 138              $this->state = 'argv';
 139            }
 140          }
 141        } else if ($this->state == 'value' || $this->state == 'argv-value') {
 142  
 143          // We're reading the value of an argument. We just need to wait for
 144          // the right number of bytes to show up.
 145  
 146          $bytes = $this->readProtocolBytes();
 147          if ($bytes === null) {
 148            break;
 149          }
 150  
 151          if ($this->state == 'argv-value') {
 152            $this->arguments['*'][$this->argumentName] = $bytes;
 153            $this->state = 'argv';
 154          } else {
 155            $this->arguments[$this->argumentName] = $bytes;
 156            $this->state = 'arguments';
 157          }
 158  
 159  
 160        } else if ($this->state == 'argv') {
 161  
 162          // We're reading a variable number of arguments. We need to wait for
 163          // the arguments to arrive.
 164  
 165          if ($this->expectArgumentCount) {
 166            $line = $this->readProtocolLine();
 167            if ($line === null) {
 168              break;
 169            }
 170  
 171            list($arg, $size) = explode(' ', $line, 2);
 172            $size = (int)$size;
 173  
 174            $this->expectBytes = $size;
 175            $this->argumentName = $arg;
 176            $this->state = 'argv-value';
 177  
 178            $this->expectArgumentCount--;
 179          } else {
 180            $this->state = 'arguments';
 181          }
 182        } else if ($this->state == 'data-length') {
 183          $line = $this->readProtocolLine();
 184          if ($line === null) {
 185            break;
 186          }
 187          $this->expectBytes = (int)$line;
 188          if (!$this->expectBytes) {
 189            $messages[] = $this->newDataMessage('');
 190            $this->initializeState();
 191          } else {
 192            $this->state = 'data-bytes';
 193          }
 194        } else if ($this->state == 'data-bytes') {
 195          $bytes = substr($this->buffer, 0, $this->expectBytes);
 196          $this->buffer = substr($this->buffer, strlen($bytes));
 197          $this->expectBytes -= strlen($bytes);
 198  
 199          $messages[] = $this->newDataMessage($bytes);
 200  
 201          if (!$this->expectBytes) {
 202            // We've finished reading this chunk, so go read the next chunk.
 203            $this->state = 'data-length';
 204          } else {
 205            // We're waiting for more data, and have read everything available
 206            // to us so far.
 207            break;
 208          }
 209        } else {
 210          throw new Exception("Bad parser state '{$this->state}'!");
 211        }
 212      }
 213  
 214      return $messages;
 215    }
 216  
 217  }


Generated: Sun Nov 30 09:20:46 2014 Cross-referenced by PHPXref 0.7.1