Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.803 diff -u -F^f -r1.803 common.inc --- includes/common.inc 11 Oct 2008 02:32:32 -0000 1.803 +++ includes/common.inc 11 Oct 2008 17:11:46 -0000 @@ -398,22 +398,36 @@ function drupal_access_denied() { * * @param $url * A string containing a fully qualified URI. - * @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 $options + * An associative array of options, with the following keys: + * - 'headers' + * An array containing an HTTP header => value pair. + * - 'method' (default 'GET') + * A string defining the HTTP request to use. + * - 'data' + * A string containing data to include in the request. + * - 'retry' (default 3) + * An integer representing how many times to retry the request + * in case of a redirect. + * - 'timeout' (default 20) + * The number of seconds before the timeout of the request. * @return * An object containing the HTTP request headers, response code, headers, * data and redirect status. */ -function drupal_http_request($url, $headers = array(), $method = 'GET', $data = NULL, $retry = 3) { +function drupal_http_request($url, $options = array()) { global $db_prefix; static $self_test = FALSE; + + // Merge in defaults. + $options += array( + 'headers' => array(), + 'method' => 'GET', + 'data' => NULL, + 'retry' => 3, + 'timeout' => 20, + ); + $result = new stdClass(); // Try to clear the drupal_http_request_fails variable if it's set. We // can't tie this call to any error because there is no surefire way to @@ -448,13 +462,13 @@ function drupal_http_request($url, $head case 'http': $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, $options['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, $options['timeout']); break; default: $result->error = 'invalid schema ' . $uri['scheme']; @@ -483,7 +497,7 @@ function drupal_http_request($url, $head // host that do not take into account the port number. 'Host' => "Host: $host", 'User-Agent' => 'User-Agent: Drupal (+http://drupal.org/)', - 'Content-Length' => 'Content-Length: ' . strlen($data) + 'Content-Length' => 'Content-Length: ' . strlen($options['data']) ); // If the server url has a user then attempt to use basic authentication @@ -498,21 +512,24 @@ function drupal_http_request($url, $head // same time won't interfere with each other as they would if the database // prefix were stored statically in a file or database variable. if (preg_match("/^simpletest\d+/", $db_prefix)) { - $headers['User-Agent'] = $db_prefix; + $options['headers']['User-Agent'] = $db_prefix; } - foreach ($headers as $header => $value) { + foreach ($options['headers'] as $header => $value) { $defaults[$header] = $header . ': ' . $value; } - $request = $method . ' ' . $path . " HTTP/1.0\r\n"; + $request = $options['method'] . ' ' . $path . " HTTP/1.0\r\n"; $request .= implode("\r\n", $defaults); $request .= "\r\n\r\n"; - if ($data) { - $request .= $data . "\r\n"; + if ($options['data']) { + $request .= $options['data'] . "\r\n"; } $result->request = $request; + // Set a timeout on the stream + stream_set_timeout($fp, $options['timeout']); + fwrite($fp, $request); // Fetch response. @@ -520,6 +537,14 @@ function drupal_http_request($url, $head while (!feof($fp) && $chunk = fread($fp, 1024)) { $response .= $chunk; } + + // Determine whether the response timed out during read + $response_metadata = stream_get_meta_data($fp); + if (array_key_exists('timed_out', $response_metadata) && $response_metadata['timed_out']) { + $result->error = 'Connection timed out while reading response'; + return $result; + } + fclose($fp); // Parse response. @@ -564,9 +589,9 @@ function drupal_http_request($url, $head case 307: // Moved temporarily $location = $result->headers['Location']; - if ($retry) { - $result = drupal_http_request($location, $headers, $method, $data, --$retry); - $result->redirect_code = $code; + if ($options['retry']) { + $options['retry']--; + $result = drupal_http_request($result->headers['Location'], $options); } $result->redirect_url = $location; @@ -2704,7 +2729,7 @@ function drupal_system_listing($mask, $d /** * Hands off structured Drupal arrays to type-specific *_alter implementations. - * + * * This dispatch function hands off structured Drupal arrays to type-specific * *_alter implementations. It ensures a consistent interface for all altering * operations. Index: includes/xmlrpc.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/xmlrpc.inc,v retrieving revision 1.53 diff -u -F^f -r1.53 xmlrpc.inc --- includes/xmlrpc.inc 31 Aug 2008 12:50:45 -0000 1.53 +++ includes/xmlrpc.inc 11 Oct 2008 17:11:47 -0000 @@ -439,7 +439,7 @@ function _xmlrpc() { $method = array_shift($args); } $xmlrpc_request = xmlrpc_request($method, $args); - $result = drupal_http_request($url, array("Content-Type" => "text/xml"), 'POST', $xmlrpc_request->xml); + $result = drupal_http_request($url, array('headers' => array("Content-Type" => "text/xml"), 'method' => 'POST', 'data' => $xmlrpc_request->xml)); if ($result->code != 200) { xmlrpc_error($result->code, $result->error); return FALSE; Index: modules/aggregator/aggregator.module =================================================================== RCS file: /cvs/drupal/drupal/modules/aggregator/aggregator.module,v retrieving revision 1.394 diff -u -F^f -r1.394 aggregator.module --- modules/aggregator/aggregator.module 9 Oct 2008 15:15:49 -0000 1.394 +++ modules/aggregator/aggregator.module 11 Oct 2008 17:11:47 -0000 @@ -593,7 +593,7 @@ function aggregator_refresh($feed) { } // Request feed. - $result = drupal_http_request($feed['url'], $headers); + $result = drupal_http_request($feed['url'], array('headers' => $headers)); // Process HTTP response code. switch ($result->code) { Index: modules/openid/openid.module =================================================================== RCS file: /cvs/drupal/drupal/modules/openid/openid.module,v retrieving revision 1.30 diff -u -F^f -r1.30 openid.module --- modules/openid/openid.module 6 Oct 2008 11:30:11 -0000 1.30 +++ modules/openid/openid.module 11 Oct 2008 17:11:48 -0000 @@ -272,7 +272,7 @@ function openid_discovery($claimed_id) { if ($url['scheme'] == 'http' || $url['scheme'] == 'https') { // For regular URLs, try Yadis resolution first, then HTML-based discovery $headers = array('Accept' => 'application/xrds+xml'); - $result = drupal_http_request($xrds_url, $headers); + $result = drupal_http_request($xrds_url, array('headers' => $headers)); if (!isset($result->error)) { if (isset($result->headers['Content-Type']) && preg_match("/application\/xrds\+xml/", $result->headers['Content-Type'])) { @@ -290,7 +290,7 @@ function openid_discovery($claimed_id) { } if (!empty($xrds_url)) { $headers = array('Accept' => 'application/xrds+xml'); - $xrds_result = drupal_http_request($xrds_url, $headers); + $xrds_result = drupal_http_request($xrds_url, array('headers' => $headers)); if (!isset($xrds_result->error)) { $services = xrds_parse($xrds_result->data); } @@ -348,7 +348,7 @@ function openid_association($op_endpoint $assoc_request = openid_association_request($public); $assoc_message = _openid_encode_message(_openid_create_message($assoc_request)); $assoc_headers = array('Content-Type' => 'application/x-www-form-urlencoded; charset=utf-8'); - $assoc_result = drupal_http_request($op_endpoint, $assoc_headers, 'POST', $assoc_message); + $assoc_result = drupal_http_request($op_endpoint, array('headers' => $assoc_headers, 'method' => 'POST', 'data' => $assoc_message)); if (isset($assoc_result->error)) { module_invoke('system', 'check_http_request'); return FALSE; @@ -510,7 +510,7 @@ function openid_verify_assertion($op_end $request['openid.mode'] = 'check_authentication'; $message = _openid_create_message($request); $headers = array('Content-Type' => 'application/x-www-form-urlencoded; charset=utf-8'); - $result = drupal_http_request($op_endpoint, $headers, 'POST', _openid_encode_message($message)); + $result = drupal_http_request($op_endpoint, array('headers' => $headers, 'method' => 'POST', 'data' => _openid_encode_message($message))); if (!isset($result->error)) { $response = _openid_parse_message($result->data); if (strtolower(trim($response['is_valid'])) == 'true') {