Skip to content

Conversation

legionth
Copy link
Contributor

Currently the ChunkedDecoder and the LengthLimitedStream emit errors, but these errors aren't handled anywhere.
This takes care that theses errors will be forwarded to the request object.

This PR also adds an CloseProtectionStream to protect the TCP connection against a close , when an error occurs in any stream. Instead of closing the TCP stream it will be paused, similar to the current behavior of the request object.

src/Server.php Outdated
// Forward occuring errors on to request
$stream->on('error', function ($error) use ($request) {
$request->emit('error', array($error));
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was probably prepared before #132 was in, this should now be moved to the Request 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Must be slipped through in a rebase 🙈 . It was already moved to the Request object. Removed it.

use React\Stream\Util;

/** @internal */
class CloseProtectionStream extends EventEmitter implements ReadableStreamInterface
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The class LGTM, but I suppose its purpose may be a bit unclear for outsiders ;-) Can you add some documentation here? 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a description. Have look. Tell me if I missed a point.


class CloseProtectionStreamTest extends TestCase
{
public function testCloseEventDoesntCloseInputStream()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So what does it do instead? :-)

$input->expects($this->once())->method('pause');

$protection = new CloseProtectionStream($input);
$protection->pause();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this is a duplicate of a test below?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right. Must be leftover of a previous local version.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@legionth ping?

public function handleError(\Exception $e)
{
$this->emit('error', array($e));
$this->close();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not saying this is wrong, but the close() is probably not needed currently and may be an issue with the future Stream version (in case this is a non-permanent error). I guess it's probably safer to leave the close() out here and leave this up to the input stream?

$protection->on('close', $this->expectCallableOnce());

$input->close();
$input->emit('end', array());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like an invalid sequence of events to me, what is being tested here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was a leftover of previous version. Removed it.

$input->emit('end', array());

$this->assertFalse($protection->isReadable());
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above, is this a duplicate?

$this->emit('close');

// 'pause' the stream avoids additional traffic transferred by this stream
$this->input->pause();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see this covered, but what happens if I call this:

$protection->close();
$protection->resume();

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, this wasn't handled correctly. Fixed it and added some tests for it.

$server->on('request', function ($request, $response) use (&$error){
$request->on('error', function ($ex) use (&$error) {
$error = $ex;
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this use expectCallableOnce() instead? (see also below)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about this, too. But I wanted to ensure that the expected type is an Exception and nothing else. Do you think it is enough to check this behavior in ChunkedDecoderTest?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the curren commits only expectCallableOnce will be called and not the assertion of an Exception.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the updated tests are easier to grasp 👍 IF the exception class is really important here (I'm undecided about this), then you may want to use expectCallableOnceWith(…) and family?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point! Added that in the newest commit.

$this->assertInstanceOf('InvalidArgumentException', $error);
}

public function testInvalidChunkHeaderResultsInErrorResponse()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Afaict this does not "result in error reponse", but actually "result in an error on the request stream"?

These tests (see below) LGTM otherwise, but I'm not sure if they provide much value in ServerTest or if we should re-organize this? Any input would be welcome 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-organize in which way?
I wanted to show that the server can handle the errors thrown by the ChunkedDecoder and the LengthLimitedStream. Do you think these tests are not necessary? Or is it the similar logic for these tests that concerns you?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed the test names.
I consider to reorganize it in a seprated PR. Currently the ServerTest is more an integration test than an unit test. That is completly fine but maybe this should be seperated. I would keep the tests in this PR, because this would affect several other tests too.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The updated tests now look good to me and I agree that it makes sense to re-organize this in a later follow-up PR 👍

@clue clue added this to the v0.6.0 milestone Feb 27, 2017
@legionth legionth force-pushed the error-handling branch 2 times, most recently from 0572c12 to 1318841 Compare February 28, 2017 11:57
Copy link
Member

@clue clue left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tests could use an update, otherwise LGTM 👍

$input->expects($this->once())->method('pause');

$protection = new CloseProtectionStream($input);
$protection->pause();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@legionth ping?

$protection->close();
}

public function testPause()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please check the test names (and below)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactored this, I hope the names are easier to understand.

@legionth legionth changed the title Handle errors from ChunkedDecoder and LengthLimitedStream Protect the TCP connection against close from other streams Feb 28, 2017
Copy link
Member

@clue clue left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! :shipit: 👍

@legionth
Copy link
Contributor Author

legionth commented Mar 1, 2017

Just squashed the commits.

@legionth
Copy link
Contributor Author

legionth commented Mar 1, 2017

Updated the commit history and added an README update.

@WyriHaximus WyriHaximus merged commit a445cc7 into reactphp:master Mar 1, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants