diff -u -p -r1.1222 common.inc --- includes/common.inc 17 Sep 2010 14:53:21 -0000 1.1222 +++ includes/common.inc 18 Sep 2010 11:21:55 -0000 @@ -1,5 +1,5 @@ array(), 'method' => 'GET', + 'protocol' => 'HTTP/1.0', 'data' => NULL, 'max_redirects' => 3, 'timeout' => 30.0, @@ -799,21 +798,67 @@ function drupal_http_request($url, array // stream_socket_client() requires timeout to be a float. $options['timeout'] = (float) $options['timeout']; + // Merge the default headers. + $options['headers'] += array( + 'User-Agent' => 'Drupal (+http://drupal.org/)', + ); + // RFC 2616: "... non-standard ports MUST, default ports MAY be included ...". + // When port is implicit (standard port), allow Header Host without information + // for port, else some rewrite rules could be broken when checking the host + // that do not take into account the port number. + $options['headers']['Host'] = $uri['host']; + if (isset($uri['port'])){ + $options['headers']['Host'] .= ":" . $uri['port']; + } + + // Proxy setup - normal sites don't need a proxy. + $proxy_server = variable_get('proxy_server', ''); + if ($proxy_server && _drupal_http_use_proxy($uri['host'])) { + // Since the url is passed as the path, we won't use the parsed query. + unset($uri['query']); + $uri['path'] = $url; + // Set the scheme so we open a socket to the proxy server. + if ($uri['scheme'] == 'https'){ + $uri['scheme'] = 'proxy_https'; + } + else{ + $uri['scheme'] = 'proxy_http'; + } + if ($proxy_username = variable_get('proxy_username', '')) { + $proxy_password = variable_get('proxy_password', ''); + // As described by RFC-2617 for Basic Authentication Scheme. + // Digest Access Authentication Scheme is not supported (by now) + $options['headers']['Proxy-Authorization'] = 'Basic ' . base64_encode($proxy_username . (!empty($proxy_password) ? ":" . $proxy_password : '')); + } + // Some proxies reject requests with any User-Agent headers, while others + // require a specific one. + $proxy_user_agent = variable_get('proxy_user_agent', ''); + // The default value matches neither condition. + if ($proxy_user_agent === NULL) { + unset($options['headers']['User-Agent']); + } + elseif ($proxy_user_agent) { + $options['headers']['User-Agent'] = $proxy_user_agent; + } + } + + timer_start(__FUNCTION__); + switch ($uri['scheme']) { + case 'proxy_http': + case 'proxy_https': + // Make the socket connection to a proxy server. + $socket = 'tcp://' . $proxy_server . ':' . variable_get('proxy_port', 8080); + break; case 'http': case 'feed': - $port = isset($uri['port']) ? $uri['port'] : 80; - $socket = 'tcp://' . $uri['host'] . ':' . $port; - // RFC 2616: "non-standard ports MUST, default ports MAY be included". - // We don't add the standard port to prevent from breaking rewrite rules - // checking the host that do not take into account the port number. - $options['headers']['Host'] = $uri['host'] . ($port != 80 ? ':' . $port : ''); + $socket = 'tcp://' . $uri['host'] . ':'; + $socket .= isset($uri['port']) ? $uri['port'] : 80; break; case 'https': // Note: Only works when PHP is compiled with OpenSSL support. - $port = isset($uri['port']) ? $uri['port'] : 443; - $socket = 'ssl://' . $uri['host'] . ':' . $port; - $options['headers']['Host'] = $uri['host'] . ($port != 443 ? ':' . $port : ''); + $socket = 'ssl://' . $uri['host'] . ':'; + $socket .= isset($uri['port']) ? $uri['port'] : 443; break; default: $result->error = 'invalid schema ' . $uri['scheme']; @@ -821,12 +866,14 @@ function drupal_http_request($url, array return $result; } - if (empty($options['context'])) { - $fp = @stream_socket_client($socket, $errno, $errstr, $options['timeout']); - } - else { - // Create a stream with context. Allows verification of a SSL certificate. - $fp = @stream_socket_client($socket, $errno, $errstr, $options['timeout'], STREAM_CLIENT_CONNECT, $options['context']); + if (!($fp = $fp_in)){ + if (empty($options['context'])) { + $fp = @stream_socket_client($socket, $errno, $errstr, $options['timeout']); + } + else { + // Create a stream with context. Allows verification of a SSL certificate. + $fp = @stream_socket_client($socket, $errno, $errstr, $options['timeout'], STREAM_CLIENT_CONNECT, $options['context']); + } } // Make sure the socket opened properly. @@ -834,7 +881,9 @@ function drupal_http_request($url, array // When a network error occurs, we use a negative number so it does not // clash with the HTTP status codes. $result->code = -$errno; - $result->error = trim($errstr) ? trim($errstr) : t('Error opening socket @socket', array('@socket' => $socket)); + $result->error = trim($errstr) + ? trim($errstr) + : t('Error opening socket @socket', array('@socket' => $socket)); // Mark that this request failed. This will trigger a check of the web // server's ability to make outgoing HTTP requests the next time that @@ -845,17 +894,28 @@ function drupal_http_request($url, array return $result; } + // As described by RFC-2817 + switch ($uri['scheme']) { + case 'proxy_https': + $options_connect = $options; + $options_connect['method'] = 'CONNECT'; + $options_connect['protocol'] = 'HTTP/1.1'; + if (!($return_connect = drupal_http_request($options['headers']['Host'], $options_connect, $fp))){ + if (!$fp_in){ + fclose($fp); + } + return $result_connect; + } + stream_socket_enable_crypto($fp, true, STREAM_CRYPTO_METHOD_SSLv23_CLIENT); + break; + } + // Construct the path to act on. $path = isset($uri['path']) ? $uri['path'] : '/'; if (isset($uri['query'])) { $path .= '?' . $uri['query']; } - // Merge the default headers. - $options['headers'] += array( - 'User-Agent' => 'Drupal (+http://drupal.org/)', - ); - // Only add Content-Length if we actually have any content or if it is a POST // or PUT request. Some non-standard servers get confused by Content-Length in // at least HEAD/GET requests, and Squid always requires Content-Length in @@ -881,7 +941,7 @@ function drupal_http_request($url, array $options['headers']['User-Agent'] = drupal_generate_test_ua($test_info['test_run_id']); } - $request = $options['method'] . ' ' . $path . " HTTP/1.0\r\n"; + $request = $options['method'] . ' ' . $path . " " . $options['protocol'] . "\r\n"; foreach ($options['headers'] as $name => $value) { $request .= $name . ': ' . trim($value) . "\r\n"; } @@ -914,7 +974,9 @@ function drupal_http_request($url, array $info = stream_get_meta_data($fp); $alive = !$info['eof'] && !$info['timed_out'] && $chunk; } - fclose($fp); + if (!$fp_in){ + fclose($fp); + } if ($info['timed_out']) { $result->code = HTTP_REQUEST_TIMEOUT; @@ -1022,6 +1084,18 @@ function drupal_http_request($url, array return $result; } + +/** + * Helper function for determining hosts excluded from needing a proxy. + * + * @return + * TRUE if a proxy should be used for this host. + */ +function _drupal_http_use_proxy($host) { + $proxy_exceptions = variable_get('proxy_exceptions', array('localhost', '127.0.0.1')); + return !in_array(strtolower($host), $proxy_exceptions, TRUE); +} + /** * @} End of "HTTP handling". */ diff -u -p -r1.50 default.settings.php --- sites/default/default.settings.php 8 Aug 2010 19:35:49 -0000 1.50 +++ sites/default/default.settings.php 18 Sep 2010 11:22:02 -0000 @@ -429,3 +429,18 @@ ini_set('session.cookie_lifetime', 20000 * Remove the leading hash signs to disable. */ # $conf['allow_authorize_operations'] = FALSE; + +/** + * External access proxy settings: + * + * If your site must access the internet via a web proxy then you can enter + * the proxy settings here. Currently only basic authentication is supported + * by using the username and password variables. The proxy_exceptions variable + * is an array of host names to be accessed directly, not via proxy. + */ +# $conf['proxy_server'] = ''; +# $conf['proxy_port'] = 8080; +# $conf['proxy_username'] = ''; +# $conf['proxy_password'] = ''; +# $conf['proxy_exceptions'] = array('127.0.0.1', 'localhost'); +