【问题标题】:PHP Curl and headers problemPHP 卷曲和标题问题
【发布时间】:2010-12-22 09:17:52
【问题描述】:

我之前问过这个问题,但又遇到了问题,无法解决。我有一个内部代理服务器和一个内容服务器。代理服务器上的代码是这样的。 (有些 cmets 可能是错的,但留下来表达我的理解):

<?php
session_start();
$data_server_url = "http://my_data_server_url/";
$i_var_prefix="i_var_";

$process_headers_separately=0;
//$process_headers_separately=1;
// WARNING! Has problems with GZIPPED DATA!
// AVOID/REMOVE OPTION ALLTOGETHER
// (Set to 1 if you want to catch received headers
// and send explicit headers to clients)
//-----------------------------------------

// Other important request dependent 'SERVER' variables.
if(isset($_SERVER['HTTPS']))
{ $_POST["${i_var_prefix}_HTTPS"]=$_SERVER['HTTPS']; };

if(isset($_SERVER['REMOTE_ADDR']))
{ $_POST["${i_var_prefix}_REMOTE_ADDR"]=$_SERVER['REMOTE_ADDR']; };

$request_uri="";
if(isset($_SERVER['REQUEST_URI'])) { $request_uri = $_SERVER['REQUEST_URI']; };
$curl_url="${data_server_url}${request_uri}";

$field_array= array(
      'Accept' => 'HTTP_ACCEPT',
      'Accept-Charset' => 'HTTP_ACCEPT_CHARSET',
      'Accept-Encoding' => 'HTTP_ACCEPT_ENCODING',
      'Accept-Language' => 'HTTP_ACCEPT_LANGUAGE',
      'Connection' => 'HTTP_CONNECTION',
      'Host' => 'HTTP_HOST',
      'Referer' => 'HTTP_REFERER',
      'User-Agent' => 'HTTP_USER_AGENT'
      );

$curl_request_headers=array();

foreach ($field_array as $key => $value) {
   if(isset($_SERVER["$value"])) {
      $server_value=$_SERVER["$value"];
      $curl_request_headers[]="$key: $server_value";
   }
};
//------
session_write_close();

//Open connection
$curl_handle = curl_init();
curl_setopt($curl_handle,CURLOPT_COOKIE,session_name()."=".session_id().";");
//Set the url, number of POST vars, POST data
curl_setopt($curl_handle, CURLOPT_URL, $curl_url);
curl_setopt($curl_handle, CURLOPT_POST, count($_POST));
curl_setopt($curl_handle, CURLOPT_POSTFIELDS, $_POST);
curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl_handle, CURLOPT_AUTOREFERER, TRUE);
curl_setopt($curl_handle, CURLOPT_HEADER, $process_headers_separately);
curl_setopt($curl_handle, CURLOPT_HTTPHEADER, $curl_request_headers);
curl_setopt($curl_handle, CURLOPT_ENCODING, "identity");


//Execute post
$result = curl_exec($curl_handle);

//Close connection
curl_close($curl_handle);

if ($process_headers_separately) {
   list($headers,$content)=explode("\r\n\r\n",$result,2);
   foreach (explode("\r\n",$headers) as $hdr) {
      header($hdr);
   }
   echo $content;
} else {
   echo $result;
}    
?>

问题一: 以目前的代码,即使data_server返回的Content-Type是text/plain,客户端看到的content-type也是text/html。 例如,见http://sarcastic-quotes.com/robots.txt 这个请求转到上面的文件。我已经检查了 data server 实际上将 Content-Type 作为 text/plain 返回。但是通过代理,客户端将响应标头中的内容类型视为 text/html。

问题2:注意变量process_headers_separately的使用。如果我将它设置为 1,那么浏览器会尝试下载 gzip 文件而不是显示内容(无论数据服务器返回什么内容类型)。因此,该代码流中存在一些逻辑错误。

我只是想让上面的代码作为一个内部代理,无缝地充当我的数据服务器和客户端之间的桥梁。任何想法都将不胜感激,我真的很困惑如何正确处理上面的标题。

问候, JP


当process_headers_separately=1(浏览器下载文件而不是显示)时,我找到了问题的原因。但这太奇怪了,我无法解决。问题:如果我取消注释行 if(isset($_SERVER['REMOTE_ADDR'])) { $_POST["${i_var_prefix}_REMOTE_ADDR"]=$_SERVER['REMOTE_ADDR']; }; 然后事情开始正常了!奇怪的!

这一定是像 Neil 建议的一些疯狂的空白问题。

无论如何,尝试解决这个问题 - 由于这个疯狂的错误,我已经花了 3 天(之前 2 天,现在 1 天):(。感谢 Neil 和 RF 在这方面的帮助。

【问题讨论】:

  • RabidFire 的 curl_getinfo 方法至少可以正确传输内容类型。但是,我正在尝试“全部接力”方法。在那,无论我尝试什么,浏览器总是试图下载文件。代理服务器似乎再次压缩了整个标题和内容,并且正在发生某种双重压缩。如果我解压缩下载的文件,则解压缩文件的标题为可读文本,实际页面内容为 gzip 压缩数据。
  • 如果您的浏览器正在尝试下载文件,那么您的Content-Type 标头设置为浏览器无法显示的内容,例如application/octet-stream,或者Content-Disposition 标头设置不正确。未解压缩的压缩数据,带有正确的标题,至少应该只显示 gobbledegook。
  • 您能否编辑问题以反映您对代码所做的任何更改?我会再做一个小测验。
  • 我已经放入了新代码,并结合了您和 RF 建议的更改。谢谢
  • 确保来自 CURL 的 Content-Encoding 响应标头与其实际提供的输出之间没有不匹配。如果 CURL 认为可以解压缩内容,它会尝试解压缩,因此您需要使用 gzcompress()gzdeflate() 重新压缩它。除了压缩之外,您的内容类型问题还存在吗?另外,尝试用apache_request_headers() 替换$curl_request_headers。有时,当您使用 mod_rewrite 时,您最终会得到像 $_SERVER['HTTP_REDIRECT_ACCEPT_ENCODING'] 这样的东西,而不是您正在寻找的东西。

标签: php http curl content-type httpresponse


【解决方案1】:

这一切看起来都很干净。我能看到的唯一差异是某些服务器仅使用\n 分隔标头,而不是\r\n。但这可能不是问题。

您可以尝试将var_export(explode("\r\n",$headers),true) 写入文件并查看结果吗?

关于您的 gzip 问题,确保关闭 ?&gt; 后没有尾随空格,或者更安全,将 echo $result 更改为 die($result)。显然,如果您不传递 CURL 响应的标头,Apache 将自己构建,因此您将需要 CURLOPT_HEADER 标志。

CURL 是否为您解压缩响应?如果是这样,您可能需要干预 Content-EncodingContent-Length 响应标头。

n.

【讨论】:

  • 谢谢尼尔。我检查过的 \r\n 正确地进行了拆分。这个空白的东西既棘手又危险——我从来没有在意过它!但是在当前文件中,没有空格。我不认为 curl 正在解压缩响应,因为我将 CURLOPT_ENCODING 设置为“身份”。
  • “die”是否算作错误,或者使用 die 是完全“整洁”的做法?
  • 不,它不会记录任何内容或抛出任何异常。我将它用于很多 AJAX 响应程序的东西,是的,gzcompress 也是如此。 die() 非常适合“最终输出”操作;它基本上意味着“发送这些东西然后拔掉插头”。支持 gzip 只能是一件好事,所以我建议花时间解决它。您的标头不会被压缩,只会压缩您的响应正文,如果需要,您可以使用 gzuncompress() 和 gzinflate() 进一步处理响应正文。
  • curl_getinfo() 更多地用于内部处理而不是中继。您在解析原始响应方面走在了正确的轨道上,因为您不需要翻译任何内容;您可以将其全部转发给客户端。事实上,你也可以在进来的路上做同样的事情;而不是通过 $_SERVER 进行选择,因为您使用的是 apache,只要您不使用 CGI,您就可以使用 apache_request_headers() 代替。
  • CURLOPT_ENCODING 会干扰 Accept-Encoding 请求标头。由于您将请求的 Accept-Encoding 传递给服务器,因此您可能希望省略它。
【解决方案2】:

您可以使用以下代码行:

//Execute post
$result = curl_exec($curl_handle);
$result_info = curl_getinfo($curl_handle);

然后:

} else {
    if (isset($result_info['content_type'])) {
        header("Content-Type: {$result_info['content_type']}");
    }
    echo $result;
}

基本上,您的代理服务器就是返回数据的服务器。因此,这就是您必须设置 Content-Type 的地方。默认情况下,这将是 html。但是,如果您检查 CURL 响应是否具有特定的 Content-Type 并设法设置它,它就会成功! :)

让我们知道这是否适合您。


我有点明白你现在想要做什么。所以这里是如何在你的代理的响应中获取标头:

如果您将 CURLOPT_HEADER 选项设置为 1,您将在输出中收到标题,您可以这样做:

// Grab the headers from the result
if ($process_headers_separately) {
    list($header_string, $content) = explode("\r\n\r\n", $result, 2);
    // OR :
    // $header_string = substr($result, 0, $result_info['header_size']);
    $headers = http_parse_headers($header_string);
    foreach ($headers as $key => $value) {
        header("$key: $value");
    }
}

由于第一个答案的发布者提到的原因,我建议使用 http_parse_headers 函数。我不确定这是否会解决您的 gzipping 问题。让我们知道。

【讨论】:

  • 啊哈。 curl_getinfo 看起来很整洁。请原谅我缺乏这方面的知识 - 但是响应中可以有多个标题吗?我的意思和result_info['content_type']一样,我应该在result_info中寻找其他特定字段吗?
  • 好吧,与您当前的问题无关:响应状态、上次修改时间、时间信息等。curl_getinfo 返回许多东西:php.net/manual/en/function.curl-getinfo.php - 你只能得到一个像所以:$content_type = curl_getinfo($curl_handle, CURLINFO_CONTENT_TYPE)
  • 后者是正确的。请记住,您可能会错过其他重要的标头,例如与缓存相关的标头等。
  • 对不起,但是,是的。我知道由于压缩等原因,这有点棘手,但它是一个更广泛的解决方案,将您从“中继内容类型”带到“中继所有标头”,这是您真正需要的。
  • @JP19 - 按照 Neil 的话,您可以在此页面上使用一些用户贡献的功能:php.net/http_parse_headers :)
猜你喜欢
  • 2017-09-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-14
  • 1970-01-01
  • 1970-01-01
  • 2012-12-15
相关资源
最近更新 更多