diff --git a/includes/common.inc b/includes/common.inc index 05af5a7..e1fb44f 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -31,6 +31,12 @@ if (!defined('E_DEPRECATED')) { } /** + * Error code indicating that the request made by drupal_http_request() exceeded + * the specified timeout. + */ +define('HTTP_REQUEST_TIMEOUT', -1); + +/** * Set content for a specified region. * * @param $region @@ -435,11 +441,15 @@ function drupal_access_denied() { * @param $retry * An integer representing how many times to retry the request in case of a * redirect. + * @param $timeout + * A float representing the maximum number of seconds the function call may + * take. The default is 30 seconds. If a timeout occurs, the error code is set + * to the HTTP_REQUEST_TIMEOUT constant. * @return * An object containing the HTTP request headers, response code, protocol, * status message, headers, data and redirect status. */ -function drupal_http_request($url, $headers = array(), $method = 'GET', $data = NULL, $retry = 3) { +function drupal_http_request($url, $headers = array(), $method = 'GET', $data = NULL, $retry = 3, $timeout = 30.0) { global $db_prefix; $result = new stdClass(); @@ -459,18 +469,20 @@ function drupal_http_request($url, $headers = array(), $method = 'GET', $data = return $result; } + timer_start(__FUNCTION__); + switch ($uri['scheme']) { case 'http': case 'feed': $port = isset($uri['port']) ? $uri['port'] : 80; $host = $uri['host'] . ($port != 80 ? ':'. $port : ''); - $fp = @fsockopen($uri['host'], $port, $errno, $errstr, 15); + $fp = @fsockopen($uri['host'], $port, $errno, $errstr, $timeout); break; case 'https': // Note: Only works for PHP 4.3 compiled with OpenSSL. $port = isset($uri['port']) ? $uri['port'] : 443; $host = $uri['host'] . ($port != 443 ? ':'. $port : ''); - $fp = @fsockopen('ssl://'. $uri['host'], $port, $errno, $errstr, 20); + $fp = @fsockopen('ssl://'. $uri['host'], $port, $errno, $errstr, $timeout); break; default: $result->error = 'invalid schema '. $uri['scheme']; @@ -544,11 +556,25 @@ function drupal_http_request($url, $headers = array(), $method = 'GET', $data = $result->request = $request; - fwrite($fp, $request); + // Calculate how much time is left of the original timeout value. + $time_left = $timeout - timer_read(__FUNCTION__) / 1000; + if ($time_left > 0) { + stream_set_timeout($fp, floor($time_left), floor(1000000 * fmod($time_left, 1))); + fwrite($fp, $request); + } // Fetch response. $response = ''; - while (!feof($fp) && $chunk = fread($fp, 1024)) { + while (!feof($fp)) { + // Calculate how much time is left of the original timeout value. + $time_left = $timeout - timer_read(__FUNCTION__) / 1000; + if ($time_left <= 0) { + $result->code = HTTP_REQUEST_TIMEOUT; + $result->error = 'request timed out'; + return $result; + } + stream_set_timeout($fp, floor($time_left), floor(1000000 * fmod($time_left, 1))); + $chunk = fread($fp, 1024); $response .= $chunk; } fclose($fp); @@ -597,9 +623,13 @@ function drupal_http_request($url, $headers = array(), $method = 'GET', $data = case 302: // Moved temporarily case 307: // Moved temporarily $location = $result->headers['Location']; - - if ($retry) { - $result = drupal_http_request($result->headers['Location'], $headers, $method, $data, --$retry); + $timeout -= timer_read(__FUNCTION__) / 1000; + if ($timeout <= 0) { + $result->code = HTTP_REQUEST_TIMEOUT; + $result->error = 'request timed out'; + } + elseif ($retry) { + $result = drupal_http_request($result->headers['Location'], $headers, $method, $data, --$retry, $timeout); $result->redirect_code = $result->code; } $result->redirect_url = $location;