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

Add 'read_timeout' option #1611

Merged
merged 1 commit into from
Feb 23, 2017
Merged

Conversation

arnaud-lb
Copy link
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
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
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
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
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.

if (isset($options['read_timeout'])) {
$readTimeout = $options['read_timeout'];
$sec = (int) $readTimeout;
$usec = ($readTimeout - $sec) * 1000000;
Copy link
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
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
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
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
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
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
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
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

@@ -238,6 +238,12 @@
const TIMEOUT = 'timeout';

/**
* read_timeout: (float, default=default_socket_timeout) Float describing
Copy link
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?

res.write("sleeping 60 seconds ...\n");
setTimeout(function () {
res.end("slept 60 seconds\n");
}, 10000);
Copy link
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
Contributor Author

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

res.write("sleeping 60 seconds ...\n");
setTimeout(function () {
res.end("slept 60 seconds\n");
}, 60*1000);
Copy link
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
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
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
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
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
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
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
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
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