Skip to content
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

Optional maximum length argument for the buffer function #3

Merged
merged 1 commit into from
Oct 17, 2017

Conversation

WyriHaximus
Copy link
Member

As discussed on hangouts added an optional maximum length argument to the buffer function.

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 progress, would love to get this feature in! :shipit:

README.md Outdated
a `Promise` which resolves with the stream data buffer.
The `buffer(ReadableStreamInterface $stream, int $maxLength = 0)` function can be used to create
a `Promise` which resolves with the stream data buffer. With an optional maximum length argument
which defaults to no limit.
Copy link
Member

Choose a reason for hiding this comment

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

IMO 0 is a valid limit to ensure no data is sent. I think a default null parameter to mean "no limit" makes more sense here 👍

Copy link
Member Author

Choose a reason for hiding this comment

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

Changed it, but using a limit of 0 is kinda pointless 🤐

Copy link
Member

Choose a reason for hiding this comment

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

I agree that there aren't many use cases for this, but IMO it makes more sense as a extreme value instead of "no value" which would be closer to the other end of the range :-)

Copy link
Member Author

Choose a reason for hiding this comment

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

True =D

README.md Outdated
var_dump(json_decode($contents));
}, function ($error) {
// Reaching here when the stream buffer goes above the max size.
// (In this example that is 1024 bytes.)
Copy link
Member

Choose a reason for hiding this comment

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

Note that the promise will also be rejected when a stream error is emitted or when the promise is cancelled.

};
$stream->on('data', $bufferer);

$promise = new Promise\Promise(function ($resolve, $reject) use ($stream, &$buffer) {
$promise = new Promise\Promise(function ($resolve, $reject) use ($stream, $deferred, &$buffer) {
$deferred->promise()->done($resolve, $reject);
Copy link
Member

Choose a reason for hiding this comment

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

This requires Promise v2 API.

$buffer .= $data;
if ($maxLength > 0 && strlen($buffer) > $maxLength) {
$deferred->reject(new \RuntimeException('Buffer exceeded maximum length'));
}
Copy link
Member

Choose a reason for hiding this comment

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

Should probably also stop buffering in the background?

Copy link
Member Author

Choose a reason for hiding this comment

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

Actually when the promise is rejected it is automatically removing the buffer listener

@WyriHaximus
Copy link
Member Author

@clue updated the PR, will squash it once all feedback has been worked in to it and both of you approved the PR 👍

$buffer .= $data;
if ($maxLength !== null && strlen($buffer) > (int)$maxLength) {
$deferred->reject(new \RuntimeException('Buffer exceeded maximum length'));
}
};
Copy link
Member

Choose a reason for hiding this comment

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

Probably micro-optimization, but what about incremental buffer size counting?

$buffer = '';
$bufferLength = 0;

$bufferer = function ($data) use (&$buffer, &$bufferLength, &$bufferer, $deferred, $maxLength) {
    $buffer .= $data;

    if (null === $maxLength) {
        return;
    }

    $bufferLength += strlen($data);

    if ($bufferLength > $maxLength) {
        $deferred->reject(new \RuntimeException('Buffer exceeded maximum length'));
    }
};

Copy link
Member

Choose a reason for hiding this comment

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

Talking about optimizations, strlen() is actually O(1), so not much would be gained :-) As an alternative, you can use isset($buffer[$maxLength]) to avoid the expensive function call 👍

@WyriHaximus
Copy link
Member Author

WyriHaximus commented Oct 11, 2017

@jsor @clue applied your suggestions and squashed the commits 👍

$buffer .= $data;
if ($maxLength !== null && isset($buffer[$maxLength])) {
$deferred->reject(new \RuntimeException('Buffer exceeded maximum length'));
Copy link
Contributor

Choose a reason for hiding this comment

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

This doesn't unregister the data callback, so it might result in a double resolution of the deferred, no?

Copy link
Member

Choose a reason for hiding this comment

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

There is no double resolution in react/promise, first come, first serve. Subsequent resolutions are ignored.

There is room for improvement for this function in general, but that's something for a follow-up PR.

Copy link
Member Author

Choose a reason for hiding this comment

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

What @jsor says, but also when rejecting there the error passes through https://github.com/WyriHaximus-labs/promise-stream/blob/7f505f3915e331b90b6e3a6040a33d4104243e15/src/functions.php#L49-L55 which does remove the listener

@@ -12,22 +12,29 @@
* Creates a `Promise` which resolves with the stream data buffer
*
* @param ReadableStreamInterface $stream
* @param int $maxLength
Copy link
Member

Choose a reason for hiding this comment

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

@param int|null $maxLength maximum number of bytes to buffer or null=unlimited? See also signature in README.

$buffer .= $data;
if ($maxLength !== null && isset($buffer[$maxLength])) {
$deferred->reject(new \RuntimeException('Buffer exceeded maximum length'));
Copy link
Member

Choose a reason for hiding this comment

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

I would suggest using an OverflowException here and explicitly document this in the README. This way, the user can easily discern normal stream errors from buffer overflow errors 👍

Copy link

Choose a reason for hiding this comment

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

Same as my comment here reactphp/http#232 (comment), OverflowException would be nice.


public function testMaximumSize()
{
$this->setExpectedException('\RuntimeException', 'Buffer exceeded maximum length');
Copy link
Member

Choose a reason for hiding this comment

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

Should be moved down to the Block\await()?

@WyriHaximus
Copy link
Member Author

@clue @andig Updated per your suggestions 👍 /cc @jsor

@andig
Copy link

andig commented Oct 15, 2017

Can this PR be readied for merge + release as it blocks HTTP Server?

$buffer .= $data;
if ($maxLength !== null && isset($buffer[$maxLength])) {
$deferred->reject(new \OverflowException('Buffer exceeded maximum length'));
Copy link
Member

Choose a reason for hiding this comment

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

LGTM, but should be added to documentation 👍

Copy link
Member Author

Choose a reason for hiding this comment

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

Will do 👍

Copy link
Member Author

Choose a reason for hiding this comment

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

@clue done

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, squashed changes, now let's get this in! :shipit:

@WyriHaximus
Copy link
Member Author

@clue awesome! Thanks for the squashing 👍

@jsor jsor merged commit 6a88554 into reactphp:master Oct 17, 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.

5 participants