From 72eea35099ed801ad87cd23c3184fb6c24ea93e5 Mon Sep 17 00:00:00 2001 From: Chris Boden Date: Sat, 23 Aug 2014 10:52:27 -0400 Subject: [PATCH 1/5] SecureStream to address SSL buffering problem --- src/SecureStream.php | 92 ++++++++++++++++++++++++++++++++++++++++ src/StreamEncryption.php | 7 ++- 2 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 src/SecureStream.php diff --git a/src/SecureStream.php b/src/SecureStream.php new file mode 100644 index 0000000..53842bd --- /dev/null +++ b/src/SecureStream.php @@ -0,0 +1,92 @@ +stream = $stream; + $this->loop = $loop; + + $stream->on('error', function($error) { + $this->emit('error', [$error, $this]); + }); + $stream->on('end', function() { + $this->emit('end', [$this]); + }); + $stream->on('close', function() { + $this->emit('close', [$this]); + }); + $stream->on('drain', function() { + $this->emit('drain', [$this]); + }); + + $stream->pause(); + + $this->resume(); + } + + public function handleData($stream) + { + $data = stream_get_contents($stream); + + $this->emit('data', [$data, $this]); + + if (!is_resource($stream) || feof($stream)) { + $this->end(); + } + } + + public function pause() + { + $this->loop->removeReadStream($this->stream->stream); + } + + public function resume() + { + if ($this->isReadable()) { + $this->loop->addReadStream($this->stream->stream, [$this, 'handleData']); + } + } + + public function isReadable() + { + return $this->stream->isReadable(); + } + + public function isWritable() + { + return $this->stream->isWritable(); + } + + public function write($data) + { + return $this->stream->write($data); + } + + public function close() + { + return $this->stream->close(); + } + + public function end($data = null) + { + return $this->stream->end($data); + } + + public function pipe(WritableStreamInterface $dest, array $options = array()) + { + return $this->stream->pipe($dest, $options); + } +} \ No newline at end of file diff --git a/src/StreamEncryption.php b/src/StreamEncryption.php index 84b2d28..f6a339f 100644 --- a/src/StreamEncryption.php +++ b/src/StreamEncryption.php @@ -54,8 +54,13 @@ public function toggle(Stream $stream, $toggle) $this->loop->addReadStream($socket, $toggleCrypto); $toggleCrypto(); - return $deferred->promise()->then(function () use ($stream) { + return $deferred->promise()->then(function () use ($stream, $toggle) { + if ($toggle) { + return new SecureStream($stream, $this->loop); + } + $stream->resume(); + return $stream; }, function($error) use ($stream) { $stream->resume(); From ac87c2d36f4864f54be770158d6dd190ad911283 Mon Sep 17 00:00:00 2001 From: Chris Boden Date: Sat, 23 Aug 2014 11:24:05 -0400 Subject: [PATCH 2/5] Fixed wrong emit when piping data through Secure --- src/SecureStream.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/SecureStream.php b/src/SecureStream.php index 53842bd..729527a 100644 --- a/src/SecureStream.php +++ b/src/SecureStream.php @@ -7,6 +7,7 @@ use React\Stream\DuplexStreamInterface; use React\Stream\WritableStreamInterface; use React\Stream\Stream; +use React\Stream\Util; class SecureStream implements DuplexStreamInterface { @@ -87,6 +88,8 @@ public function end($data = null) public function pipe(WritableStreamInterface $dest, array $options = array()) { - return $this->stream->pipe($dest, $options); + Util::pipe($this, $dest, $options); + + return $dest; } } \ No newline at end of file From 44ab73cffe85d499ee2eb100e9d5be05c7ef6c85 Mon Sep 17 00:00:00 2001 From: Chris Boden Date: Sat, 23 Aug 2014 11:35:20 -0400 Subject: [PATCH 3/5] Unwrap SecureStream, better decorating API --- src/SecureStream.php | 25 ++++++++++++++----------- src/StreamEncryption.php | 4 ++++ 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/SecureStream.php b/src/SecureStream.php index 729527a..c185331 100644 --- a/src/SecureStream.php +++ b/src/SecureStream.php @@ -12,13 +12,16 @@ class SecureStream implements DuplexStreamInterface { use EventEmitterTrait; - + + public $stream; + + public $decorating; protected $loop; - protected $stream; public function __construct(Stream $stream, LoopInterface $loop) { - $this->stream = $stream; - $this->loop = $loop; + $this->stream = $stream->stream; + $this->decorating = $stream; + $this->loop = $loop; $stream->on('error', function($error) { $this->emit('error', [$error, $this]); @@ -51,39 +54,39 @@ public function handleData($stream) public function pause() { - $this->loop->removeReadStream($this->stream->stream); + $this->loop->removeReadStream($this->decorating->stream); } public function resume() { if ($this->isReadable()) { - $this->loop->addReadStream($this->stream->stream, [$this, 'handleData']); + $this->loop->addReadStream($this->decorating->stream, [$this, 'handleData']); } } public function isReadable() { - return $this->stream->isReadable(); + return $this->decorating->isReadable(); } public function isWritable() { - return $this->stream->isWritable(); + return $this->decorating->isWritable(); } public function write($data) { - return $this->stream->write($data); + return $this->decorating->write($data); } public function close() { - return $this->stream->close(); + return $this->decorating->close(); } public function end($data = null) { - return $this->stream->end($data); + return $this->decorating->end($data); } public function pipe(WritableStreamInterface $dest, array $options = array()) diff --git a/src/StreamEncryption.php b/src/StreamEncryption.php index f6a339f..79c71b8 100644 --- a/src/StreamEncryption.php +++ b/src/StreamEncryption.php @@ -36,6 +36,10 @@ public function disable(Stream $stream) public function toggle(Stream $stream, $toggle) { + if (__NAMESPACE__ . '\SecureStream' === get_class($stream)) { + $stream = $stream->decorating; + } + // pause actual stream instance to continue operation on raw stream socket $stream->pause(); From aa071b84e68f217e71a7fae6514d6f63e7077e64 Mon Sep 17 00:00:00 2001 From: Chris Boden Date: Sun, 31 Aug 2014 08:28:17 -0400 Subject: [PATCH 4/5] Only do file_get_contents on PHP versions needed --- src/StreamEncryption.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/StreamEncryption.php b/src/StreamEncryption.php index 79c71b8..de0eba0 100644 --- a/src/StreamEncryption.php +++ b/src/StreamEncryption.php @@ -18,10 +18,23 @@ class StreamEncryption private $errstr; private $errno; + + private $wrapSecure = false; public function __construct(LoopInterface $loop) { $this->loop = $loop; + + // See https://bugs.php.net/bug.php?id=65137 + // On versions affected by this bug we need to fread the stream until we + // get an empty string back because the buffer indicator could be wrong + if ( + PHP_VERSION_ID < 50433 + || (PHP_VERSION_ID >= 50000 && PHP_VERSION_ID < 50517) + || PHP_VERSION_ID === 50600 + ) { + $this->wrapSecure = true; + } } public function enable(Stream $stream) @@ -59,7 +72,7 @@ public function toggle(Stream $stream, $toggle) $toggleCrypto(); return $deferred->promise()->then(function () use ($stream, $toggle) { - if ($toggle) { + if ($toggle && $this->wrapSecure) { return new SecureStream($stream, $this->loop); } From 08ff406ad01b82177359bd5d4814f77ee8566ace Mon Sep 17 00:00:00 2001 From: Chris Boden Date: Mon, 1 Sep 2014 20:51:47 -0400 Subject: [PATCH 5/5] Fix version check to 5.5 --- src/StreamEncryption.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/StreamEncryption.php b/src/StreamEncryption.php index de0eba0..d001130 100644 --- a/src/StreamEncryption.php +++ b/src/StreamEncryption.php @@ -30,7 +30,7 @@ public function __construct(LoopInterface $loop) // get an empty string back because the buffer indicator could be wrong if ( PHP_VERSION_ID < 50433 - || (PHP_VERSION_ID >= 50000 && PHP_VERSION_ID < 50517) + || (PHP_VERSION_ID >= 50500 && PHP_VERSION_ID < 50517) || PHP_VERSION_ID === 50600 ) { $this->wrapSecure = true;