? modules/simpletest/tests/343502.test ? modules/system/system.http.inc Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.834 diff -u -p -r1.834 common.inc --- includes/common.inc 3 Dec 2008 12:31:37 -0000 1.834 +++ includes/common.inc 6 Dec 2008 20:13:16 -0000 @@ -422,25 +422,41 @@ function drupal_access_denied() { /** * Perform an HTTP request. * - * This is a flexible and powerful HTTP client implementation. Correctly handles - * GET, POST, PUT or any other HTTP requests. Handles redirects. + * This is a flexible and powerful HTTP client implementation. Correctly + * handles GET, POST, PUT or any other HTTP requests. Handles redirects. * * @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. - * @return - * An object containing the HTTP request headers, response code, headers, - * data and redirect status. + * @param $options + * (optional) An array which can have one or more of following keys: + * - headers + * An array containing request headers to send as name/value pairs. + * - method + * A string containing the request method. Defaults to 'GET'. + * - data + * A string containing the request body. Defaults to NULL. + * - max_redirects + * An integer representing how many times a redirect may be followed. + * Defaults to 3. + * @return + * An object which can have one or more of the following parameters: + * - request + * A string containing the request body that was sent. + * - code + * An integer containting the response status code, or the error code if + * an error occurred. + * - redirect_code + * If redirected, an integer containing the initial response status code. + * - redirect_url + * If redirected, a string containing the redirection location. + * - error + * If an error occurred, the error messsage. + * - headers + * An array containing the response headers as name/value pairs. + * - data + * A string containing the response body that was received. */ -function drupal_http_request($url, $headers = array(), $method = 'GET', $data = NULL, $retry = 3) { +function drupal_http_request($url, array $options = array()) { global $db_prefix; static $self_test = FALSE; $result = new stdClass(); @@ -505,19 +521,27 @@ function drupal_http_request($url, $head $path .= '?' . $uri['query']; } - // Create HTTP request. - $defaults = array( + // Merge the default options. + $options += array( + 'headers' => array(), + 'method' => 'GET', + 'data' => NULL, + 'max_redirects' => 3, + ); + + // Merge the default headers. + $options['headers'] += array( // RFC 2616: "non-standard ports MUST, default ports MAY be included". // We don't add the port to prevent from breaking rewrite rules checking the // 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) + 'Host' => $host, + 'User-Agent' => 'Drupal (+http://drupal.org/)', + 'Content-Length' => strlen($options['data']), ); // If the server url has a user then attempt to use basic authentication if (isset($uri['user'])) { - $defaults['Authorization'] = 'Authorization: Basic ' . base64_encode($uri['user'] . (!empty($uri['pass']) ? ":" . $uri['pass'] : '')); + $options['headers']['Authorization'] = 'Basic ' . base64_encode($uri['user'] . (!empty($uri['pass']) ? ":" . $uri['pass'] : '')); } // If the database prefix is being used by SimpleTest to run the tests in a copied @@ -527,18 +551,19 @@ 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, $matches)) { - $headers['User-Agent'] = $matches[0]; + $options['headers']['User-Agent'] = $matches[0]; } - foreach ($headers as $header => $value) { - $defaults[$header] = $header . ': ' . $value; + // Format the headers. + foreach ($options['headers'] as $name => $value) { + $options['headers'][$name] = $name . ': ' . $value; } - $request = $method . ' ' . $path . " HTTP/1.0\r\n"; - $request .= implode("\r\n", $defaults); + $request = $options['method'] . ' ' . $path . " HTTP/1.0\r\n"; + $request .= implode("\r\n", $options['headers']); $request .= "\r\n\r\n"; - if ($data) { - $request .= $data . "\r\n"; + if ($options['data']) { + $request .= $options['data'] . "\r\n"; } $result->request = $request; @@ -551,15 +576,16 @@ function drupal_http_request($url, $head } fclose($fp); - // Parse response. - list($split, $result->data) = explode("\r\n\r\n", $response, 2); - $split = preg_split("/\r\n|\n|\r/", $split); + // Parse response headers from the response body. + list($response, $result->data) = explode("\r\n\r\n", $response, 2); + $response = preg_split("/\r\n|\n|\r/", $response); - list($protocol, $code, $text) = explode(' ', trim(array_shift($split)), 3); + // Parse the response status line. + list($protocol, $code, $status) = explode(' ', trim(array_shift($response)), 3); $result->headers = array(); - // Parse headers. - while ($line = trim(array_shift($split))) { + // Parse the response headers. + while ($line = trim(array_shift($response))) { list($header, $value) = explode(':', $line, 2); if (isset($result->headers[$header]) && $header == 'Set-Cookie') { // RFC 2109: the Set-Cookie response header comprises the token Set- @@ -572,17 +598,53 @@ function drupal_http_request($url, $head } $responses = array( - 100 => 'Continue', 101 => 'Switching Protocols', - 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', - 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 307 => 'Temporary Redirect', - 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Time-out', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Large', 415 => 'Unsupported Media Type', 416 => 'Requested range not satisfiable', 417 => 'Expectation Failed', - 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Time-out', 505 => 'HTTP Version not supported' + 100 => 'Continue', + 101 => 'Switching Protocols', + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 307 => 'Temporary Redirect', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Time-out', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Large', + 415 => 'Unsupported Media Type', + 416 => 'Requested range not satisfiable', + 417 => 'Expectation Failed', + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Time-out', + 505 => 'HTTP Version not supported', ); // RFC 2616 states that all unknown HTTP codes must be treated the same as the // base code in their class. if (!isset($responses[$code])) { $code = floor($code / 100) * 100; } + $result->code = $code; switch ($code) { case 200: // OK @@ -592,19 +654,18 @@ function drupal_http_request($url, $head case 302: // Moved temporarily case 307: // Moved temporarily $location = $result->headers['Location']; - - if ($retry) { - $result = drupal_http_request($location, $headers, $method, $data, --$retry); + if ($options['max_redirects']) { + // Redirect to the new location. + $options['max_redirects']--; + $result = drupal_http_request($location, $options); $result->redirect_code = $code; } $result->redirect_url = $location; - break; default: - $result->error = $text; + $result->error = $status; } - $result->code = $code; return $result; } /** Index: includes/xmlrpc.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/xmlrpc.inc,v retrieving revision 1.54 diff -u -p -r1.54 xmlrpc.inc --- includes/xmlrpc.inc 15 Oct 2008 13:56:07 -0000 1.54 +++ includes/xmlrpc.inc 6 Dec 2008 20:13:20 -0000 @@ -439,7 +439,12 @@ 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); + $options = array( + 'headers' => array('Content-Type' => 'text/xml'), + 'method' => 'POST', + 'data' => $xmlrpc_request->xml, + ); + $result = drupal_http_request($url, $options); 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.401 diff -u -p -r1.401 aggregator.module --- modules/aggregator/aggregator.module 16 Nov 2008 04:38:15 -0000 1.401 +++ modules/aggregator/aggregator.module 6 Dec 2008 20:13:22 -0000 @@ -650,7 +650,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.33 diff -u -p -r1.33 openid.module --- modules/openid/openid.module 22 Nov 2008 10:32:42 -0000 1.33 +++ modules/openid/openid.module 6 Dec 2008 20:13:32 -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); } @@ -347,8 +347,12 @@ function openid_association($op_endpoint // If there is no existing association, then request one $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_options = array( + 'headers' => array('Content-Type' => 'application/x-www-form-urlencoded; charset=utf-8'), + 'method' => 'POST', + 'data' => $assoc_message, + ); + $assoc_result = drupal_http_request($op_endpoint, $assoc_options); if (isset($assoc_result->error)) { module_invoke('system', 'check_http_request'); return FALSE; @@ -509,8 +513,12 @@ function openid_verify_assertion($op_end $request = $response; $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)); + $options = array( + 'headers' => array('Content-Type' => 'application/x-www-form-urlencoded; charset=utf-8'), + 'method' => 'POST', + 'data' => _openid_encode_message($message), + ); + $result = drupal_http_request($op_endpoint, $options); if (!isset($result->error)) { $response = _openid_parse_message($result->data); if (strtolower(trim($response['is_valid'])) == 'true') { Index: modules/simpletest/tests/common.test =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/tests/common.test,v retrieving revision 1.17 diff -u -p -r1.17 common.test --- modules/simpletest/tests/common.test 26 Nov 2008 13:48:49 -0000 1.17 +++ modules/simpletest/tests/common.test 6 Dec 2008 20:13:38 -0000 @@ -244,32 +244,32 @@ class DrupalHTTPRequestTestCase extends } function testDrupalHTTPRequestRedirect() { - $redirect_301 = drupal_http_request(url('system-test/redirect/301', array('absolute' => TRUE)), array(), 'GET', NULL, 1); + $redirect_301 = drupal_http_request(url('system-test/redirect/301', array('absolute' => TRUE)), array('max_redirects' => 1)); $this->assertEqual($redirect_301->redirect_code, 301, t('drupal_http_request follows the 301 redirect.')); - $redirect_301 = drupal_http_request(url('system-test/redirect/301', array('absolute' => TRUE)), array(), 'GET', NULL, 0); - $this->assertFalse(isset($redirect_301->redirect_code), t('drupal_http_request does not follow 301 redirect if $retry = 0.')); + $redirect_301 = drupal_http_request(url('system-test/redirect/301', array('absolute' => TRUE)), array('max_redirects' => 0)); + $this->assertFalse(isset($redirect_301->redirect_code), t('drupal_http_request does not follow 301 redirect if max_redirects = 0.')); - $redirect_invalid = drupal_http_request(url('system-test/redirect-noscheme', array('absolute' => TRUE)), array(), 'GET', NULL, 1); + $redirect_invalid = drupal_http_request(url('system-test/redirect-noscheme', array('absolute' => TRUE)), array('max_redirects' => 1)); $this->assertEqual($redirect_invalid->error, 'missing schema', t('301 redirect to invalid URL returned with error "!error".', array('!error' => $redirect_invalid->error))); - $redirect_invalid = drupal_http_request(url('system-test/redirect-noparse', array('absolute' => TRUE)), array(), 'GET', NULL, 1); + $redirect_invalid = drupal_http_request(url('system-test/redirect-noparse', array('absolute' => TRUE)), array('max_redirects' => 1)); $this->assertEqual($redirect_invalid->error, 'unable to parse URL', t('301 redirect to invalid URL returned with error "!error".', array('!error' => $redirect_invalid->error))); - $redirect_invalid = drupal_http_request(url('system-test/redirect-invalid-scheme', array('absolute' => TRUE)), array(), 'GET', NULL, 1); + $redirect_invalid = drupal_http_request(url('system-test/redirect-invalid-scheme', array('absolute' => TRUE)), array('max_redirects' => 1)); $this->assertEqual($redirect_invalid->error, 'invalid schema ftp', t('301 redirect to invalid URL returned with error "!error".', array('!error' => $redirect_invalid->error))); - $redirect_302 = drupal_http_request(url('system-test/redirect/302', array('absolute' => TRUE)), array(), 'GET', NULL, 1); + $redirect_302 = drupal_http_request(url('system-test/redirect/302', array('absolute' => TRUE)), array('max_redirects' => 1)); $this->assertEqual($redirect_302->redirect_code, 302, t('drupal_http_request follows the 302 redirect.')); - $redirect_302 = drupal_http_request(url('system-test/redirect/302', array('absolute' => TRUE)), array(), 'GET', NULL, 0); + $redirect_302 = drupal_http_request(url('system-test/redirect/302', array('absolute' => TRUE)), array('max_redirects' => 0)); $this->assertFalse(isset($redirect_302->redirect_code), t('drupal_http_request does not follow 302 redirect if $retry = 0.')); - $redirect_307 = drupal_http_request(url('system-test/redirect/307', array('absolute' => TRUE)), array(), 'GET', NULL, 1); + $redirect_307 = drupal_http_request(url('system-test/redirect/307', array('absolute' => TRUE)), array('max_redirects' => 1)); $this->assertEqual($redirect_307->redirect_code, 307, t('drupal_http_request follows the 307 redirect.')); - $redirect_307 = drupal_http_request(url('system-test/redirect/307', array('absolute' => TRUE)), array(), 'GET', NULL, 0); - $this->assertFalse(isset($redirect_307->redirect_code), t('drupal_http_request does not follow 307 redirect if $retry = 0.')); + $redirect_307 = drupal_http_request(url('system-test/redirect/307', array('absolute' => TRUE)), array('max_redirects' => 0)); + $this->assertFalse(isset($redirect_307->redirect_code), t('drupal_http_request does not follow 307 redirect if max_redirects = 0.')); } function testDrupalGetDestination() {