Uh oh!
There was an error while loading. Please reload this page.
Optional maximum length argument for the buffer function - #3
Conversation
clue
left a comment
There was a problem hiding this comment.
Nice progress, would love to get this feature in! ![]()
| 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. |
There was a problem hiding this comment.
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 👍
There was a problem hiding this comment.
Changed it, but using a limit of 0 is kinda pointless 🤐
There was a problem hiding this comment.
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 :-)
| 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.) |
There was a problem hiding this comment.
Note that the promise will also be rejected when a stream error is emitted or when the promise is cancelled.
| $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); |
| $buffer .= $data; | ||
| if ($maxLength > 0 && strlen($buffer) > $maxLength) { | ||
| $deferred->reject(new \RuntimeException('Buffer exceeded maximum length')); | ||
| } |
There was a problem hiding this comment.
Should probably also stop buffering in the background?
There was a problem hiding this comment.
Actually when the promise is rejected it is automatically removing the buffer listener
WyriHaximus
commented
Oct 10, 2017
@clue updated the PR, will squash it once all feedback has been worked in to it and both of you approved the PR 👍 |
| if ($maxLength !== null && strlen($buffer) > (int)$maxLength) { | ||
| $deferred->reject(new \RuntimeException('Buffer exceeded maximum length')); | ||
| } | ||
| }; |
There was a problem hiding this comment.
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'));
}
};There was a problem hiding this comment.
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 👍
010e235 to
7f505f3Compare| $bufferer = function ($data) use (&$buffer, &$bufferer, $stream, $deferred, $maxLength) { | ||
| $buffer .= $data; | ||
| if ($maxLength !== null && isset($buffer[$maxLength])) { | ||
| $deferred->reject(new \RuntimeException('Buffer exceeded maximum length')); |
There was a problem hiding this comment.
This doesn't unregister the data callback, so it might result in a double resolution of the deferred, no?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
7f505f3 to
0ab3812Compare| * Creates a `Promise` which resolves with the stream data buffer | ||
| * | ||
| * @param ReadableStreamInterface $stream | ||
| * @param int $maxLength |
There was a problem hiding this comment.
@param int|null $maxLength maximum number of bytes to buffer or null=unlimited? See also signature in README.
| $bufferer = function ($data) use (&$buffer, $deferred, $maxLength) { | ||
| $buffer .= $data; | ||
| if ($maxLength !== null && isset($buffer[$maxLength])) { | ||
| $deferred->reject(new \RuntimeException('Buffer exceeded maximum length')); |
There was a problem hiding this comment.
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 👍
There was a problem hiding this comment.
Same as my comment here reactphp/http#232 (comment), OverflowException would be nice.
| public function testMaximumSize() | ||
| { | ||
| $this->setExpectedException('\RuntimeException', 'Buffer exceeded maximum length'); |
There was a problem hiding this comment.
Should be moved down to the Block\await()?
8784edc to
5835e5bCompareWyriHaximus
commented
Oct 13, 2017
5835e5b to
2744bebCompareandig
commented
Oct 15, 2017
Can this PR be readied for merge + release as it blocks HTTP Server? |
| $bufferer = function ($data) use (&$buffer, $deferred, $maxLength) { | ||
| $buffer .= $data; | ||
| if ($maxLength !== null && isset($buffer[$maxLength])) { | ||
| $deferred->reject(new \OverflowException('Buffer exceeded maximum length')); |
There was a problem hiding this comment.
LGTM, but should be added to documentation 👍
clue
left a comment
There was a problem hiding this comment.
Nice, squashed changes, now let's get this in! ![]()
WyriHaximus
commented
Oct 17, 2017
@clue awesome! Thanks for the squashing 👍 |
As discussed on hangouts added an optional maximum length argument to the buffer function.