Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.756.2.107 diff -u -9 -p -r1.756.2.107 common.inc --- includes/common.inc 15 Dec 2010 21:34:35 -0000 1.756.2.107 +++ includes/common.inc 30 Jan 2011 11:25:30 -0000 @@ -26,18 +26,24 @@ define('SAVED_DELETED', 3); /** * Create E_DEPRECATED constant for older PHP versions (<5.3). */ if (!defined('E_DEPRECATED')) { define('E_DEPRECATED', 8192); } /** + * 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 * Page region the content is assigned to. * @param $data * Content to be set. */ function drupal_set_content($region = NULL, $data = NULL) { static $content = array(); @@ -430,23 +436,27 @@ function drupal_access_denied() { * @param $headers * An array containing an HTTP header => value pair. * @param $method * A string defining the HTTP request to use. * @param $data * A string containing data to include in the request. * @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(); // Parse the URL and make sure we can handle the schema. $uri = parse_url($url); if ($uri == FALSE) { $result->error = 'unable to parse URL'; @@ -454,30 +464,32 @@ function drupal_http_request($url, $head return $result; } if (!isset($uri['scheme'])) { $result->error = 'missing schema'; $result->code = -1002; 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']; $result->code = -1003; return $result; } // Make sure the socket opened properly. if (!$fp) { @@ -539,23 +551,37 @@ function drupal_http_request($url, $head } $request = $method .' '. $path ." HTTP/1.0\r\n"; $request .= implode("\r\n", $defaults); $request .= "\r\n\r\n"; $request .= $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); // Parse response. list($split, $result->data) = explode("\r\n\r\n", $response, 2); $split = preg_split("/\r\n|\n|\r/", $split); list($protocol, $code, $status_message) = explode(' ', trim(array_shift($split)), 3); @@ -592,21 +618,25 @@ function drupal_http_request($url, $head switch ($code) { case 200: // OK case 304: // Not modified break; case 301: // Moved permanently 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; break; default: $result->error = $status_message; }