Skip to content

Add 'read_timeout' option#1611

Merged
sagikazarmark merged 1 commit intoguzzle:masterfrom
arnaud-lb:read-timeout
Feb 23, 2017
Merged

Add 'read_timeout' option#1611
sagikazarmark merged 1 commit intoguzzle:masterfrom
arnaud-lb:read-timeout

Conversation

@arnaud-lb
Copy link
Copy Markdown
Contributor

@arnaud-lb arnaud-lb commented Oct 5, 2016

This adds a 'read_timeout' option, allowing the user to specify a read timeout for reads on the body of streamed requests.

Support is added only to the StreamHandler, since only this handler seems to support stream requests.

This fixes #1516

Copy link
Copy Markdown
Contributor

@jeskew jeskew left a comment

Choose a reason for hiding this comment

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

Could you add a test?

$resource = fopen((string) $request->getUri()->withFragment(''), 'r', null, $context);
$this->lastHeaders = $http_response_header;

if (isset($options['read_timeout'])) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This could use the RequestOptions constant defined in this PR.

Copy link
Copy Markdown
Contributor Author

@arnaud-lb arnaud-lb Oct 7, 2016

Choose a reason for hiding this comment

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

I agree :)

Other uses of the $options array in this class don't make use of RequestOptions constants, so I tried to keep it consistent.

Do you think I still should use the RequestOptions constant ?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You're right; it's better to keep this consistent.

Comment thread src/Handler/StreamHandler.php Outdated
if (isset($options['read_timeout'])) {
$readTimeout = $options['read_timeout'];
$sec = (int) $readTimeout;
$usec = ($readTimeout - $sec) * 1000000;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Wouldn't this always be 0? If a user supplies a readTimeout of 1, then $usec will be set to (1 - 1) * 1000000.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

If will be > 0 if read_timeout is not an integer:

$readTimeout =1.5;
$sec = (int) $readTimeout; // 1
$usec = ($readTimeout - $sec) * 1000000; // (1.5 - 1) * 1000000 == 5000000

However there is one extra 0 here, I'll fix this.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

OIC. I didn't realize that stream_set_timeout required seconds and fractions of seconds to be passed in as separate parameters.

@arnaud-lb
Copy link
Copy Markdown
Contributor Author

@jeskew: Thank you for the review. I updated the PR accordingly.

$this->assertEquals("sleeping 60 seconds ...\n", $line);
$line = fgets($body);
$this->assertFalse($line);
$this->assertFalse(feof($body));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Since you're using the test server, this should explicitly check whether the stream timed out, e.g., $this->assertTrue(stream_get_meta_data($body)['timed_out']);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ok, done :) Is this guaranteed to work in all cases ?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

According to the PHP docs, this value will be true if the stream has timed out. They don't mention any known exceptions.

Copy link
Copy Markdown
Member

@sagikazarmark sagikazarmark left a comment

Choose a reason for hiding this comment

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

Thanks for the PR. Can you please clarify the few things I mentioned? Thank you very much

Comment thread src/RequestOptions.php
*/
const TIMEOUT = 'timeout';

/**
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Maybe mention that it's configured in PHP ini?

Comment thread tests/server.js Outdated
res.write("sleeping 60 seconds ...\n");
setTimeout(function () {
res.end("slept 60 seconds\n");
}, 10000);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is this 60 seconds?

@arnaud-lb arnaud-lb force-pushed the read-timeout branch 2 times, most recently from a8e2b71 to be3ab76 Compare October 17, 2016 09:08
@arnaud-lb
Copy link
Copy Markdown
Contributor Author

@sagikazarmark Thank you for the review, I've updated the PR accordingly

Comment thread tests/server.js
res.write("sleeping 60 seconds ...\n");
setTimeout(function () {
res.end("slept 60 seconds\n");
}, 60*1000);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I am still not 100% sure what's happening here: it says 60 seconds, but in the test the read timeout is only one.

Copy link
Copy Markdown
Contributor Author

@arnaud-lb arnaud-lb Oct 25, 2016

Choose a reason for hiding this comment

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

The server takes 60 seconds to send the end of the response, and the client has a 1 second read timeout. So the client times out while reading the response.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Ah, okay, thank

Copy link
Copy Markdown
Member

@sagikazarmark sagikazarmark left a comment

Choose a reason for hiding this comment

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

@arnaud-lb can you please add some documentation? Apart from that, ready to go.

@arnaud-lb
Copy link
Copy Markdown
Contributor Author

@sagikazarmark: Awesome, added documentation

$readTimeout = $options['read_timeout'];
$sec = (int) $readTimeout;
$usec = ($readTimeout - $sec) * 100000;
stream_set_timeout($resource, $sec, $usec);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

To be honest I wonder if it would be better to pass the timeout to stream_context_create. It supports float values so it wouldn't require the magic above. One could even argue that the same functionality is already available via the undocumented stream_context option. WDYT @arnaud-lb ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

If it makes the code clearer, I'm all for it :) Which stream context option are you thinking about ? The "http.timeout" context option is used to implement Guzzle's "timeout" option, and I believe that it affects only the connection + reading the headers.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hm, indeed, you are right, missed that.

@sagikazarmark sagikazarmark modified the milestone: 6.3.0 Jan 13, 2017
@sagikazarmark
Copy link
Copy Markdown
Member

Thanks for working on this, sorry for the long wait.

@sagikazarmark sagikazarmark merged commit 437c0c3 into guzzle:master Feb 23, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Reading on streamed body doesn't have a timeout

3 participants