442c442 < 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, $fp_in = NULL) { 461c461,514 < --- > > // Merge the default options. > $options = array( > 'headers' => $headers, > 'method' => $method, > 'protocol' => 'HTTP/1.0', > 'data' => $data, > 'max_redirects' => $retry, > 'timeout' => 30.0, > 'context' => NULL, > ); > $timeout = (float) $options['timeout']; > > // Merge the default headers. > // > // 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'] += array( > 'User-Agent' => 'Drupal (+http://drupal.org/)', > 'Host' => isset($uri['port']) ? (":" . $uri['port']) : $uri['host'], > ); > // Proxy setup - normal sites don't need a proxy. > $proxy_server = ($options['method'] == 'CONNECT') > ? '': 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. > $uri['scheme'] = ($uri['scheme'] == 'https') ? 'proxy_https' : 'proxy_http'; > > $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 . ":" . $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__); > 462a516,520 > case 'proxy_http': > case 'proxy_https': > // Make the socket connection to a proxy server. > $socket = 'tcp://' . $proxy_server . ':' . variable_get('proxy_port', 8080); > break; 465,468c523,527 < $port = isset($uri['port']) ? $uri['port'] : 80; < $host = $uri['host'] . ($port != 80 ? ':'. $port : ''); < $fp = @fsockopen($uri['host'], $port, $errno, $errstr, 15); < break; --- > if (!isset($uri['port'])) { > $uri['port'] = 80; > } > $socket = 'tcp://' . $uri['host'] . ':' . $uri['port']; > break; 470,473c529,533 < // 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); --- > // Note: Only works when PHP is compiled with OpenSSL support. > if (!isset($uri['port'])) { > $uri['port'] = 443; > } > $socket = 'ssl://' . $uri['host'] . ':' . $uri['port']; 480c540,544 < --- > // Use of a context in the stream allows verification of a SSL certificate. > if (!($fp = $fp_in)) > $fp = empty($options['context']) > ? @stream_socket_client($socket, $errno, $errstr, $timeout) > : @stream_socket_client($socket, $errno, $errstr, $timeout, STREAM_CLIENT_CONNECT, $options['context']); 486c550 < $result->error = trim($errstr); --- > $result->error = trim($errstr) ? trim($errstr) : t('Error opening socket @socket', array('@socket' => $socket)); 496c560,580 < --- > // As described by RFC-2817 > switch ($uri['scheme']) { > case 'proxy_https': > $options_connect = array( > 'headers' => array(), > 'method' => 'CONNECT', > 'protocol' => 'HTTP/1.0' > ); > $options_connect['headers']['Host'] > = $options['headers']['Host']; > $options_connect['headers']['User-Agent'] > = $options['headers']['User-Agent']; > $options_connect['headers']['Proxy-Authorization'] > = $options['headers']['Proxy-Authorization']; > if (!($result_connect = drupal_http_request($url, $options_connect['headers'], $options_connect['method'], NULL, 3, $fp))) { > ($fp_in) ? TRUE : fclose($fp); > return $result_connect; > } > stream_socket_enable_crypto($fp, TRUE, STREAM_CRYPTO_METHOD_SSLv23_CLIENT); > break; > } 502,511d585 < < // Create HTTP request. < $defaults = 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/)', < ); < 516,518c590,592 < $content_length = strlen($data); < if ($content_length > 0 || $method == 'POST' || $method == 'PUT') { < $defaults['Content-Length'] = 'Content-Length: '. $content_length; --- > $content_length = strlen($options['data']); > if ($content_length > 0 || $options['method'] == 'POST' || $options['method'] == 'PUT') { > $options['headers']['Content-Length'] = $content_length; 523c597 < $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'] : '')); 533c607 < $defaults['User-Agent'] = 'User-Agent: ' . $matches[0]; --- > $options['headers']['User-Agent'] = 'User-Agent: ' . $matches[0]; 536,537c610,616 < foreach ($headers as $header => $value) { < $defaults[$header] = $header .': '. $value; --- > // Build request > switch ($options['method']) { > case 'CONNECT': > $request = $options['method'] . ' ' . $uri['host'] . ':' . $uri['port']; > break; > default: > $request = $options['method'] . ' ' . $path; 539,544c618,623 < < $request = $method .' '. $path ." HTTP/1.0\r\n"; < $request .= implode("\r\n", $defaults); < $request .= "\r\n\r\n"; < $request .= $data; < --- > $request .= ' ' . $options['protocol'] . "\r\n"; > // Add its headers to the request > foreach ($options['headers'] as $name => $value) { > $request .= $name . ': ' . trim($value) . "\r\n"; > } > $request .= "\r\n" . $options['data']; 547c626,655 < fwrite($fp, $request); --- > // Send request and then fetch response: status line, message headers and > // message body, if any. > // Due to PHP bugs like http://bugs.php.net/bug.php?id=43782 > // and http://bugs.php.net/bug.php?id=46049 we can't rely on feof(), but > // instead must invoke stream_get_meta_data() each iteration. > $request_sent = FALSE; > do { // do {} while(true); > $info = stream_get_meta_data($fp); > > $timeout = ($info['timed_out']) ? 0.0 : ((float)$options['timeout']) - timer_read(__FUNCTION__) / 1000.0; > if ($timeout <= 0.0) { > $result->code = HTTP_REQUEST_TIMEOUT; > $result->error = 'request timed out'; > } > // RETURN !! > if (isset($result->error)) { > ($fp_in) ? TRUE : fclose($fp); > return $result; > } > stream_set_timeout($fp, floor($timeout), floor(1000000 * fmod($timeout, 1))); > if (!$request_sent) { > if (fwrite($fp, $request) === FALSE) { > $result->code = -$errno; > $result->error = trim($errstr) ? trim($errstr) : t('Error writing on socket'); > } > $request_sent = TRUE; > continue; > } > // BREAK !! > if ($info['eof'] && !$info['unread_bytes']) break; 549,572c657,697 < // Fetch response. < $response = ''; < while (!feof($fp) && $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); < $result->protocol = $protocol; < $result->status_message = $status_message; < < $result->headers = array(); < < // Parse headers. < while ($line = trim(array_shift($split))) { < 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- < // Cookie:, followed by a comma-separated list of one or more cookies. < $result->headers[$header] .= ','. trim($value); --- > if (!isset($status)) { > $status = fscanf($fp, "%[^ ]%*[ ]%[^ ]%*[ ]%[^\r\n]\r\n"); > if (!isset($status[0]) || !isset($status[1]) || !isset($status[2])) { > $result->code = -$errno; > $result->error = trim($errstr) ? trim($errstr) : t('Error reading status on socket'); > continue; > } > $result->protocol = $status[0]; > $result->code = $status[1]; > $result->status_message = $status[2]; > $result->headers = array(); > $result->data = ''; > > $blank_line = FALSE; > } > elseif (!$blank_line) { > $header = fscanf($fp, "%[^: ]%*[: ]%[^\r\n]\r\n"); > if (!isset($header[0])) { > $result->code = -$errno; > $result->error = trim($errstr) ? trim($errstr) : t('Error reading header on socket'); > continue; > } > if (!isset($header[1])) { > $message_body_length = __drupal_http_message_body_length($options, $result); > $blank_line = TRUE; > continue; > } > > // PB > //$header[0] = strtolower($header[0]); > //\ PB > > // Merging cookies > // RFC 2109, the Set-Cookie response header comprises the token Set- > // Cookie:, followed by a comma-separated list of one or more cookies. > // RFC 2965, equally for Set-Cookie2 > if (($header[0] == 'Set-Cookie') || ($header[0] == 'Set-Cookie2')) > if (isset($result->headers[$header[0]])) { > $header[1] = $result->headers[$header[0]] . ',' . $header[1]; > } > $result->headers += array($header[0] => $header[1]); 574,575c699,708 < else { < $result->headers[$header] = trim($value); --- > else{ > $ldata = $message_body_length - strlen($result->data); > // BREAK !! > if ($ldata <= 0) break; > if (($rdata = fread($fp, ($ldata<1024) ? $ldata : 1024)) === FALSE) { > $result->code = -$errno; > $result->error = trim($errstr) ? trim($errstr) : t('Error reading data on socket'); > continue; > } > $result->data .= $rdata; 577c710,712 < } --- > } while (TRUE); > > ($fp_in) ? TRUE : fclose($fp); 580,584c715,754 < 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', 588,589c758,759 < if (!isset($responses[$code])) { < $code = floor($code / 100) * 100; --- > if (!isset($responses[$result->code])) { > $result->code = floor($result->code / 100) * 100; 592c762 < switch ($code) { --- > switch ($result->code) { 601,603c771,779 < if ($retry) { < $result = drupal_http_request($result->headers['Location'], $headers, $method, $data, --$retry); < $result->redirect_code = $result->code; --- > if ($options['max_redirects']) { > // Redirect to the new location. > $options['max_redirects']--; > $redirect_code = $result->code; > $result = drupal_http_request($location, $options['headers'], $options['method'], $options['data'], $options['max_redirects']); > $result->redirect_code = $redirect_code; > } > if (!isset($result->redirect_url)) { > $result->redirect_url = $location; 605d780 < $result->redirect_url = $location; 609c784 < $result->error = $status_message; --- > $result->error = $result->status_message; 612d786 < $result->code = $code; 615a790,831 > * Helper function to determine message body length of the HTTP message > * > * @return > * message body length of the HTTP message as described by RFC 2616 > * section 4.4. Return PHP_INT_MAX if length cannot be determined > */ > function __drupal_http_message_body_length($options, $result) { > > // RFC 2817, " ... data to be tunneled may be sent inmediately after > // the blank line ..." > if ($options['method'] == 'CONNECT') return 0; > > // Responses without message body > if ($options['method'] == 'HEAD') return 0; > if ($result->code == 100) return 0; > if ($result->code == 101) return 0; > if ($result->code == 204) return 0; > if ($result->code == 304) return 0; > > // ToDo, length specified by Transfer-Encoding header > > // Length specified by Content-Length header > if (!isset($result->headers['Transfer-Encoding']) > && isset($result->headers['Content-Length'])) > return $result->headers['Content-Length']; > > // ToDo, length specified by media type "multipart/byteranges" > > return PHP_INT_MAX; > } > > /** > * 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); > } > /**