-
-
Notifications
You must be signed in to change notification settings - Fork 145
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Decode chunked transfer encoding for incoming requests #106
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would love to get this in 👍
Can you look into the failing tests and the below remarks?
src/ChunkedDecoder.php
Outdated
use React\Stream\Util; | ||
use Exception; | ||
|
||
class ChunkedDecoder extends EventEmitter implements ReadableStreamInterface |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@internal
?
src/Server.php
Outdated
// attach remote ip to the request as metadata | ||
$request->remoteAddress = $conn->getRemoteAddress(); | ||
|
||
$conn->removeListener('data', $listener); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Duplicate? (Line 34)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right. Removed this line in one of the latest
src/Server.php
Outdated
@@ -64,7 +66,21 @@ public function handleRequest(ConnectionInterface $conn, Request $request, $body | |||
return; | |||
} | |||
|
|||
$header = $request->getHeaders(); | |||
$stream = $conn; | |||
if (!empty($header['Transfer-Encoding']) && $header['Transfer-Encoding'] === 'chunked') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should take advantage of #103 to match case insensitive.
$this->emit('request', array($request, $response)); | ||
$request->emit('data', array($bodyBuffer)); | ||
$conn->emit('data', array($bodyBuffer)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a heads up: Will likely cause a merge conflict with #108.
1df0cad
to
df840b1
Compare
Updated. Give me your feedback 👍 |
9324551
to
0e50877
Compare
Rebased to resolve merge conflicts. |
src/ChunkedDecoder.php
Outdated
private function handleChunkHeader($data) | ||
{ | ||
$hexValue = strtok($this->buffer . $data, static::CRLF); | ||
if ($this->isLineComplete($this->buffer . $data, $hexValue, strlen($hexValue))) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this be simplified? How about using strpos()
and substr()
here? Also could probably use an early return instead.
src/ChunkedDecoder.php
Outdated
$chunk = substr($this->buffer . $data, 0, $this->chunkSize); | ||
$this->actualChunksize = strlen($chunk); | ||
|
||
if ($this->chunkSize == $this->actualChunksize) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
===
everywhere?
src/ChunkedDecoder.php
Outdated
$this->chunkHeaderComplete = true; | ||
|
||
$data = substr($this->buffer . $data, strlen($hexValue) + 2); | ||
$this->buffer = ''; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is really hard to keep track of. It looks like the buffer is modified in a couple of methods here? Can this buffering logic be simplified?
(see also above for strpos()
etc.
src/ChunkedDecoder.php
Outdated
* @param string $chunk - chunk which will be emitted | ||
* @return string - rest data string | ||
*/ | ||
private function sendChunk($data, $chunk) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is being "sent" here?
src/ChunkedDecoder.php
Outdated
private function sendChunk($data, $chunk) | ||
{ | ||
if ($this->chunkSize == 0 && $this->isLineComplete($this->buffer . $data, $chunk, $this->chunkSize)) { | ||
$this->emit('end', array()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be followed by a close()
call?
src/ChunkedDecoder.php
Outdated
if (! $this->closed) { | ||
$this->emit('end'); | ||
$this->close(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if there's still data in the buffer when the underlying stream emits an end
event?
tests/ChunkedDecoderTest.php
Outdated
|
||
public function testEnd() | ||
{ | ||
$this->parser->on('end', $this->expectCallableOnce(array())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
empty array? see also below
tests/ChunkedDecoderTest.php
Outdated
|
||
public function testCompletlySplitted() | ||
{ | ||
$this->parser->on('data', $this->expectCallableOnceWith('welt')); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to indicate the whole chunk body is being buffered here? What if the chunk header says the chunk body will be several GiB?
$this->input->removeListener('data', array($this, 'handleChunkHeader')); | ||
$this->input->on('data', array($this, 'handleChunkData')); | ||
if ($data !== '') { | ||
$this->input->emit('data', array($data)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO the decoder should have no control over emitting events on an external entity (which may have multiple listeners for this event).
Will try a different approach. |
This ensures that only decoded body data will be emitted via the request object.
Resolves / closes #96