Generalize SSH passthru for repository hosting
Summary:
Ref T2230. In Git, we can determine if a command is read-only or read/write from the command itself, but this isn't the case in Mercurial or SVN.
For Mercurial and SVN, we need to proxy the protocol that's coming over the wire, look at each request from the client, and then check if it's a read or a write. To support this, provide a more flexible version of `passthruIO`.
The way this will work is:
- The SSH IO channel is wrapped in a `ProtocolChannel` which can parse the the incoming stream into message objects.
- The `willWriteCallback` will look at those messages and determine if they're reads or writes.
- If they're writes, it will check for write permission.
- If we're good to go, the message object is converted back into a byte stream and handed to the underlying command.
Test Plan: Executed `git clone`, `git clone --depth 3`, `git push` (against no-write repo, got error), `git push` (against valid repo).
Reviewers: btrahan
Reviewed By: btrahan
CC: hach-que, asherkin, aran
Maniphest Tasks: T2230
Differential Revision: https://secure.phabricator.com/D7551
2013-11-11 21:12:21 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Proxy an IO channel to an underlying command, with optional callbacks. This
|
|
|
|
* is a mostly a more general version of @{class:PhutilExecPassthru}. This
|
|
|
|
* class is used to proxy Git, SVN and Mercurial traffic to the commands which
|
|
|
|
* can actually serve it.
|
|
|
|
*
|
|
|
|
* Largely, this just reads an IO channel (like stdin from SSH) and writes
|
|
|
|
* the results into a command channel (like a command's stdin). Then it reads
|
|
|
|
* the command channel (like the command's stdout) and writes it into the IO
|
|
|
|
* channel (like stdout from SSH):
|
|
|
|
*
|
|
|
|
* IO Channel Command Channel
|
|
|
|
* stdin -> stdin
|
|
|
|
* stdout <- stdout
|
|
|
|
* stderr <- stderr
|
|
|
|
*
|
|
|
|
* You can provide **read and write callbacks** which are invoked as data
|
|
|
|
* is passed through this class. They allow you to inspect and modify traffic.
|
|
|
|
*
|
|
|
|
* IO Channel Passthru Command Channel
|
|
|
|
* stdout -> willWrite -> stdin
|
|
|
|
* stdin <- willRead <- stdout
|
|
|
|
* stderr <- (identity) <- stderr
|
|
|
|
*
|
|
|
|
* Primarily, this means:
|
|
|
|
*
|
|
|
|
* - the **IO Channel** can be a @{class:PhutilProtocolChannel} if the
|
|
|
|
* **write callback** can convert protocol messages into strings; and
|
|
|
|
* - the **write callback** can inspect and reject requests over the channel,
|
|
|
|
* e.g. to enforce policies.
|
|
|
|
*
|
|
|
|
* In practice, this is used when serving repositories to check each command
|
|
|
|
* issued over SSH and determine if it is a read command or a write command.
|
|
|
|
* Writes can then be checked for appropriate permissions.
|
|
|
|
*/
|
|
|
|
final class PhabricatorSSHPassthruCommand extends Phobject {
|
|
|
|
|
|
|
|
private $commandChannel;
|
|
|
|
private $ioChannel;
|
|
|
|
private $errorChannel;
|
|
|
|
private $execFuture;
|
|
|
|
private $willWriteCallback;
|
|
|
|
private $willReadCallback;
|
|
|
|
|
|
|
|
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 setIOChannel(PhutilChannel $io_channel) {
|
|
|
|
$this->ioChannel = $io_channel;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setErrorChannel(PhutilChannel $error_channel) {
|
|
|
|
$this->errorChannel = $error_channel;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setWillReadCallback($will_read_callback) {
|
|
|
|
$this->willReadCallback = $will_read_callback;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setWillWriteCallback($will_write_callback) {
|
|
|
|
$this->willWriteCallback = $will_write_callback;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function writeErrorIOCallback(PhutilChannel $channel, $data) {
|
|
|
|
$this->errorChannel->write($data);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function execute() {
|
|
|
|
$command_channel = $this->commandChannel;
|
|
|
|
$io_channel = $this->ioChannel;
|
|
|
|
$error_channel = $this->errorChannel;
|
|
|
|
|
|
|
|
if (!$command_channel) {
|
2014-06-09 20:36:49 +02:00
|
|
|
throw new Exception('Set a command channel before calling execute()!');
|
Generalize SSH passthru for repository hosting
Summary:
Ref T2230. In Git, we can determine if a command is read-only or read/write from the command itself, but this isn't the case in Mercurial or SVN.
For Mercurial and SVN, we need to proxy the protocol that's coming over the wire, look at each request from the client, and then check if it's a read or a write. To support this, provide a more flexible version of `passthruIO`.
The way this will work is:
- The SSH IO channel is wrapped in a `ProtocolChannel` which can parse the the incoming stream into message objects.
- The `willWriteCallback` will look at those messages and determine if they're reads or writes.
- If they're writes, it will check for write permission.
- If we're good to go, the message object is converted back into a byte stream and handed to the underlying command.
Test Plan: Executed `git clone`, `git clone --depth 3`, `git push` (against no-write repo, got error), `git push` (against valid repo).
Reviewers: btrahan
Reviewed By: btrahan
CC: hach-que, asherkin, aran
Maniphest Tasks: T2230
Differential Revision: https://secure.phabricator.com/D7551
2013-11-11 21:12:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!$io_channel) {
|
2014-06-09 20:36:49 +02:00
|
|
|
throw new Exception('Set an IO channel before calling execute()!');
|
Generalize SSH passthru for repository hosting
Summary:
Ref T2230. In Git, we can determine if a command is read-only or read/write from the command itself, but this isn't the case in Mercurial or SVN.
For Mercurial and SVN, we need to proxy the protocol that's coming over the wire, look at each request from the client, and then check if it's a read or a write. To support this, provide a more flexible version of `passthruIO`.
The way this will work is:
- The SSH IO channel is wrapped in a `ProtocolChannel` which can parse the the incoming stream into message objects.
- The `willWriteCallback` will look at those messages and determine if they're reads or writes.
- If they're writes, it will check for write permission.
- If we're good to go, the message object is converted back into a byte stream and handed to the underlying command.
Test Plan: Executed `git clone`, `git clone --depth 3`, `git push` (against no-write repo, got error), `git push` (against valid repo).
Reviewers: btrahan
Reviewed By: btrahan
CC: hach-que, asherkin, aran
Maniphest Tasks: T2230
Differential Revision: https://secure.phabricator.com/D7551
2013-11-11 21:12:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!$error_channel) {
|
2014-06-09 20:36:49 +02:00
|
|
|
throw new Exception('Set an error channel before calling execute()!');
|
Generalize SSH passthru for repository hosting
Summary:
Ref T2230. In Git, we can determine if a command is read-only or read/write from the command itself, but this isn't the case in Mercurial or SVN.
For Mercurial and SVN, we need to proxy the protocol that's coming over the wire, look at each request from the client, and then check if it's a read or a write. To support this, provide a more flexible version of `passthruIO`.
The way this will work is:
- The SSH IO channel is wrapped in a `ProtocolChannel` which can parse the the incoming stream into message objects.
- The `willWriteCallback` will look at those messages and determine if they're reads or writes.
- If they're writes, it will check for write permission.
- If we're good to go, the message object is converted back into a byte stream and handed to the underlying command.
Test Plan: Executed `git clone`, `git clone --depth 3`, `git push` (against no-write repo, got error), `git push` (against valid repo).
Reviewers: btrahan
Reviewed By: btrahan
CC: hach-que, asherkin, aran
Maniphest Tasks: T2230
Differential Revision: https://secure.phabricator.com/D7551
2013-11-11 21:12:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$channels = array($command_channel, $io_channel, $error_channel);
|
|
|
|
|
2013-12-16 21:37:32 +01:00
|
|
|
// We want to limit the amount of data we'll hold in memory for this
|
|
|
|
// process. See T4241 for a discussion of this issue in general.
|
|
|
|
|
|
|
|
$buffer_size = (1024 * 1024); // 1MB
|
|
|
|
$io_channel->setReadBufferSize($buffer_size);
|
|
|
|
$command_channel->setReadBufferSize($buffer_size);
|
|
|
|
|
|
|
|
// TODO: This just makes us throw away stderr after the first 1MB, but we
|
|
|
|
// don't currently have the support infrastructure to buffer it correctly.
|
|
|
|
// It's difficult to imagine this causing problems in practice, though.
|
|
|
|
$this->execFuture->getStderrSizeLimit($buffer_size);
|
|
|
|
|
Generalize SSH passthru for repository hosting
Summary:
Ref T2230. In Git, we can determine if a command is read-only or read/write from the command itself, but this isn't the case in Mercurial or SVN.
For Mercurial and SVN, we need to proxy the protocol that's coming over the wire, look at each request from the client, and then check if it's a read or a write. To support this, provide a more flexible version of `passthruIO`.
The way this will work is:
- The SSH IO channel is wrapped in a `ProtocolChannel` which can parse the the incoming stream into message objects.
- The `willWriteCallback` will look at those messages and determine if they're reads or writes.
- If they're writes, it will check for write permission.
- If we're good to go, the message object is converted back into a byte stream and handed to the underlying command.
Test Plan: Executed `git clone`, `git clone --depth 3`, `git push` (against no-write repo, got error), `git push` (against valid repo).
Reviewers: btrahan
Reviewed By: btrahan
CC: hach-que, asherkin, aran
Maniphest Tasks: T2230
Differential Revision: https://secure.phabricator.com/D7551
2013-11-11 21:12:21 +01:00
|
|
|
while (true) {
|
2013-12-15 21:52:31 +01:00
|
|
|
PhutilChannel::waitForAny($channels);
|
Generalize SSH passthru for repository hosting
Summary:
Ref T2230. In Git, we can determine if a command is read-only or read/write from the command itself, but this isn't the case in Mercurial or SVN.
For Mercurial and SVN, we need to proxy the protocol that's coming over the wire, look at each request from the client, and then check if it's a read or a write. To support this, provide a more flexible version of `passthruIO`.
The way this will work is:
- The SSH IO channel is wrapped in a `ProtocolChannel` which can parse the the incoming stream into message objects.
- The `willWriteCallback` will look at those messages and determine if they're reads or writes.
- If they're writes, it will check for write permission.
- If we're good to go, the message object is converted back into a byte stream and handed to the underlying command.
Test Plan: Executed `git clone`, `git clone --depth 3`, `git push` (against no-write repo, got error), `git push` (against valid repo).
Reviewers: btrahan
Reviewed By: btrahan
CC: hach-que, asherkin, aran
Maniphest Tasks: T2230
Differential Revision: https://secure.phabricator.com/D7551
2013-11-11 21:12:21 +01:00
|
|
|
|
|
|
|
$io_channel->update();
|
|
|
|
$command_channel->update();
|
|
|
|
$error_channel->update();
|
|
|
|
|
2013-12-16 21:37:32 +01:00
|
|
|
// If any channel is blocked on the other end, wait for it to flush before
|
|
|
|
// we continue reading. For example, if a user is running `git clone` on
|
|
|
|
// a 1GB repository, the underlying `git-upload-pack` may
|
|
|
|
// be able to produce data much more quickly than we can send it over
|
|
|
|
// the network. If we don't throttle the reads, we may only send a few
|
|
|
|
// MB over the I/O channel in the time it takes to read the entire 1GB off
|
|
|
|
// the command channel. That leaves us with 1GB of data in memory.
|
|
|
|
|
|
|
|
while ($command_channel->isOpen() &&
|
|
|
|
$io_channel->isOpenForWriting() &&
|
|
|
|
($command_channel->getWriteBufferSize() >= $buffer_size ||
|
|
|
|
$io_channel->getWriteBufferSize() >= $buffer_size ||
|
|
|
|
$error_channel->getWriteBufferSize() >= $buffer_size)) {
|
|
|
|
PhutilChannel::waitForActivity(array(), $channels);
|
|
|
|
$io_channel->update();
|
|
|
|
$command_channel->update();
|
|
|
|
$error_channel->update();
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the subprocess has exited and we've read everything from it,
|
|
|
|
// we're all done.
|
|
|
|
$done = !$command_channel->isOpenForReading() &&
|
|
|
|
$command_channel->isReadBufferEmpty();
|
Generalize SSH passthru for repository hosting
Summary:
Ref T2230. In Git, we can determine if a command is read-only or read/write from the command itself, but this isn't the case in Mercurial or SVN.
For Mercurial and SVN, we need to proxy the protocol that's coming over the wire, look at each request from the client, and then check if it's a read or a write. To support this, provide a more flexible version of `passthruIO`.
The way this will work is:
- The SSH IO channel is wrapped in a `ProtocolChannel` which can parse the the incoming stream into message objects.
- The `willWriteCallback` will look at those messages and determine if they're reads or writes.
- If they're writes, it will check for write permission.
- If we're good to go, the message object is converted back into a byte stream and handed to the underlying command.
Test Plan: Executed `git clone`, `git clone --depth 3`, `git push` (against no-write repo, got error), `git push` (against valid repo).
Reviewers: btrahan
Reviewed By: btrahan
CC: hach-que, asherkin, aran
Maniphest Tasks: T2230
Differential Revision: https://secure.phabricator.com/D7551
2013-11-11 21:12:21 +01:00
|
|
|
|
|
|
|
$in_message = $io_channel->read();
|
|
|
|
if ($in_message !== null) {
|
Enable Mercurial reads and writes over SSH
Summary:
Ref T2230. This is substantially more complicated than Git, but mostly because Mercurial's protocol is a like 50 ad-hoc extensions cobbled together. Because we must decode protocol frames in order to determine if a request is read or write, 90% of this is implementing a stream parser for the protocol.
Mercurial's own parser is simpler, but relies on blocking reads. Since we don't even have methods for blocking reads right now and keeping the whole thing non-blocking is conceptually better, I made the parser nonblocking. It ends up being a lot of stuff. I made an effort to cover it reasonably well with unit tests, and to make sure we fail closed (i.e., reject requests) if there are any parts of the protocol I got wrong.
A lot of the complexity is sharable with the HTTP stuff, so it ends up being not-so-bad, just very hard to verify by inspection as clearly correct.
Test Plan:
- Ran `hg clone` over SSH.
- Ran `hg fetch` over SSH.
- Ran `hg push` over SSH, to a read-only repo (error) and a read-write repo (success).
Reviewers: btrahan, asherkin
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T2230
Differential Revision: https://secure.phabricator.com/D7553
2013-11-11 21:18:27 +01:00
|
|
|
$in_message = $this->willWriteData($in_message);
|
|
|
|
if ($in_message !== null) {
|
|
|
|
$command_channel->write($in_message);
|
|
|
|
}
|
Generalize SSH passthru for repository hosting
Summary:
Ref T2230. In Git, we can determine if a command is read-only or read/write from the command itself, but this isn't the case in Mercurial or SVN.
For Mercurial and SVN, we need to proxy the protocol that's coming over the wire, look at each request from the client, and then check if it's a read or a write. To support this, provide a more flexible version of `passthruIO`.
The way this will work is:
- The SSH IO channel is wrapped in a `ProtocolChannel` which can parse the the incoming stream into message objects.
- The `willWriteCallback` will look at those messages and determine if they're reads or writes.
- If they're writes, it will check for write permission.
- If we're good to go, the message object is converted back into a byte stream and handed to the underlying command.
Test Plan: Executed `git clone`, `git clone --depth 3`, `git push` (against no-write repo, got error), `git push` (against valid repo).
Reviewers: btrahan
Reviewed By: btrahan
CC: hach-que, asherkin, aran
Maniphest Tasks: T2230
Differential Revision: https://secure.phabricator.com/D7551
2013-11-11 21:12:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$out_message = $command_channel->read();
|
|
|
|
if ($out_message !== null) {
|
Enable Mercurial reads and writes over SSH
Summary:
Ref T2230. This is substantially more complicated than Git, but mostly because Mercurial's protocol is a like 50 ad-hoc extensions cobbled together. Because we must decode protocol frames in order to determine if a request is read or write, 90% of this is implementing a stream parser for the protocol.
Mercurial's own parser is simpler, but relies on blocking reads. Since we don't even have methods for blocking reads right now and keeping the whole thing non-blocking is conceptually better, I made the parser nonblocking. It ends up being a lot of stuff. I made an effort to cover it reasonably well with unit tests, and to make sure we fail closed (i.e., reject requests) if there are any parts of the protocol I got wrong.
A lot of the complexity is sharable with the HTTP stuff, so it ends up being not-so-bad, just very hard to verify by inspection as clearly correct.
Test Plan:
- Ran `hg clone` over SSH.
- Ran `hg fetch` over SSH.
- Ran `hg push` over SSH, to a read-only repo (error) and a read-write repo (success).
Reviewers: btrahan, asherkin
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T2230
Differential Revision: https://secure.phabricator.com/D7553
2013-11-11 21:18:27 +01:00
|
|
|
$out_message = $this->willReadData($out_message);
|
|
|
|
if ($out_message !== null) {
|
|
|
|
$io_channel->write($out_message);
|
|
|
|
}
|
Generalize SSH passthru for repository hosting
Summary:
Ref T2230. In Git, we can determine if a command is read-only or read/write from the command itself, but this isn't the case in Mercurial or SVN.
For Mercurial and SVN, we need to proxy the protocol that's coming over the wire, look at each request from the client, and then check if it's a read or a write. To support this, provide a more flexible version of `passthruIO`.
The way this will work is:
- The SSH IO channel is wrapped in a `ProtocolChannel` which can parse the the incoming stream into message objects.
- The `willWriteCallback` will look at those messages and determine if they're reads or writes.
- If they're writes, it will check for write permission.
- If we're good to go, the message object is converted back into a byte stream and handed to the underlying command.
Test Plan: Executed `git clone`, `git clone --depth 3`, `git push` (against no-write repo, got error), `git push` (against valid repo).
Reviewers: btrahan
Reviewed By: btrahan
CC: hach-que, asherkin, aran
Maniphest Tasks: T2230
Differential Revision: https://secure.phabricator.com/D7551
2013-11-11 21:12:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// If we have nothing left on stdin, close stdin on the subprocess.
|
|
|
|
if (!$io_channel->isOpenForReading()) {
|
Enable Mercurial reads and writes over SSH
Summary:
Ref T2230. This is substantially more complicated than Git, but mostly because Mercurial's protocol is a like 50 ad-hoc extensions cobbled together. Because we must decode protocol frames in order to determine if a request is read or write, 90% of this is implementing a stream parser for the protocol.
Mercurial's own parser is simpler, but relies on blocking reads. Since we don't even have methods for blocking reads right now and keeping the whole thing non-blocking is conceptually better, I made the parser nonblocking. It ends up being a lot of stuff. I made an effort to cover it reasonably well with unit tests, and to make sure we fail closed (i.e., reject requests) if there are any parts of the protocol I got wrong.
A lot of the complexity is sharable with the HTTP stuff, so it ends up being not-so-bad, just very hard to verify by inspection as clearly correct.
Test Plan:
- Ran `hg clone` over SSH.
- Ran `hg fetch` over SSH.
- Ran `hg push` over SSH, to a read-only repo (error) and a read-write repo (success).
Reviewers: btrahan, asherkin
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T2230
Differential Revision: https://secure.phabricator.com/D7553
2013-11-11 21:18:27 +01:00
|
|
|
$command_channel->closeWriteChannel();
|
Generalize SSH passthru for repository hosting
Summary:
Ref T2230. In Git, we can determine if a command is read-only or read/write from the command itself, but this isn't the case in Mercurial or SVN.
For Mercurial and SVN, we need to proxy the protocol that's coming over the wire, look at each request from the client, and then check if it's a read or a write. To support this, provide a more flexible version of `passthruIO`.
The way this will work is:
- The SSH IO channel is wrapped in a `ProtocolChannel` which can parse the the incoming stream into message objects.
- The `willWriteCallback` will look at those messages and determine if they're reads or writes.
- If they're writes, it will check for write permission.
- If we're good to go, the message object is converted back into a byte stream and handed to the underlying command.
Test Plan: Executed `git clone`, `git clone --depth 3`, `git push` (against no-write repo, got error), `git push` (against valid repo).
Reviewers: btrahan
Reviewed By: btrahan
CC: hach-que, asherkin, aran
Maniphest Tasks: T2230
Differential Revision: https://secure.phabricator.com/D7551
2013-11-11 21:12:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if ($done) {
|
|
|
|
break;
|
|
|
|
}
|
2013-11-11 21:19:06 +01:00
|
|
|
|
|
|
|
// If the client has disconnected, kill the subprocess and bail.
|
|
|
|
if (!$io_channel->isOpenForWriting()) {
|
2013-12-16 21:37:32 +01:00
|
|
|
$this->execFuture
|
|
|
|
->setStdoutSizeLimit(0)
|
|
|
|
->setStderrSizeLimit(0)
|
|
|
|
->setReadBufferSize(null)
|
|
|
|
->resolveKill();
|
2013-11-11 21:19:06 +01:00
|
|
|
break;
|
|
|
|
}
|
Generalize SSH passthru for repository hosting
Summary:
Ref T2230. In Git, we can determine if a command is read-only or read/write from the command itself, but this isn't the case in Mercurial or SVN.
For Mercurial and SVN, we need to proxy the protocol that's coming over the wire, look at each request from the client, and then check if it's a read or a write. To support this, provide a more flexible version of `passthruIO`.
The way this will work is:
- The SSH IO channel is wrapped in a `ProtocolChannel` which can parse the the incoming stream into message objects.
- The `willWriteCallback` will look at those messages and determine if they're reads or writes.
- If they're writes, it will check for write permission.
- If we're good to go, the message object is converted back into a byte stream and handed to the underlying command.
Test Plan: Executed `git clone`, `git clone --depth 3`, `git push` (against no-write repo, got error), `git push` (against valid repo).
Reviewers: btrahan
Reviewed By: btrahan
CC: hach-que, asherkin, aran
Maniphest Tasks: T2230
Differential Revision: https://secure.phabricator.com/D7551
2013-11-11 21:12:21 +01:00
|
|
|
}
|
|
|
|
|
2013-12-16 21:37:32 +01:00
|
|
|
list($err) = $this->execFuture
|
|
|
|
->setStdoutSizeLimit(0)
|
|
|
|
->setStderrSizeLimit(0)
|
|
|
|
->setReadBufferSize(null)
|
|
|
|
->resolve();
|
Generalize SSH passthru for repository hosting
Summary:
Ref T2230. In Git, we can determine if a command is read-only or read/write from the command itself, but this isn't the case in Mercurial or SVN.
For Mercurial and SVN, we need to proxy the protocol that's coming over the wire, look at each request from the client, and then check if it's a read or a write. To support this, provide a more flexible version of `passthruIO`.
The way this will work is:
- The SSH IO channel is wrapped in a `ProtocolChannel` which can parse the the incoming stream into message objects.
- The `willWriteCallback` will look at those messages and determine if they're reads or writes.
- If they're writes, it will check for write permission.
- If we're good to go, the message object is converted back into a byte stream and handed to the underlying command.
Test Plan: Executed `git clone`, `git clone --depth 3`, `git push` (against no-write repo, got error), `git push` (against valid repo).
Reviewers: btrahan
Reviewed By: btrahan
CC: hach-que, asherkin, aran
Maniphest Tasks: T2230
Differential Revision: https://secure.phabricator.com/D7551
2013-11-11 21:12:21 +01:00
|
|
|
|
|
|
|
return $err;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function willWriteData($message) {
|
|
|
|
if ($this->willWriteCallback) {
|
|
|
|
return call_user_func($this->willWriteCallback, $this, $message);
|
|
|
|
} else {
|
|
|
|
if (strlen($message)) {
|
|
|
|
return $message;
|
|
|
|
} else {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public function willReadData($message) {
|
|
|
|
if ($this->willReadCallback) {
|
|
|
|
return call_user_func($this->willReadCallback, $this, $message);
|
|
|
|
} else {
|
|
|
|
if (strlen($message)) {
|
|
|
|
return $message;
|
|
|
|
} else {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|