如何处理来自代理的额外 HTTP 标头?
Posted
技术标签:
【中文标题】如何处理来自代理的额外 HTTP 标头?【英文标题】:What to do with extra HTTP header from proxy? 【发布时间】:2013-06-02 15:56:51 【问题描述】:我们的环境需要为非现场服务使用出站代理。通常这不是问题。在这种使用 Twilio 的情况下,返回的额外标头会破坏客户端。
传出标题:
POST /2010-04-01/Accounts/FOO/SMS/Messages.json HTTP/1.1
Authorization: Basic FOO==
User-Agent: twilio-php/3.10.0
Host: api.twilio.com
Accept: */*
Accept-Charset: utf-8
Content-Type: application/x-www-form-urlencoded
Content-Length: 108
响应标头:
HTTP/1.0 200 Connection established
HTTP/1.1 201 Created
Server: nginx
Date: Thu, 06 Jun 2013 14:39:24 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 551
Connection: close
X-Powered-By: PHP/5.3.11
我只能假设代理正在添加额外的 HTTP 标头。
Twilio 客户端会检查:
list($head, $body) = ($parts[0] == 'HTTP/1.1 100 Continue')
据我了解,curl 有时或版本会在请求中自动添加 Expect 标头,响应中会返回 HTTP 100,但在这种情况下不是,响应为 200连接已建立。值得添加一个空的 Expect: 或一个 Expect:bacon 并没有改变结果。
我真的不想在这里过多地破解 Twilio 客户端,我特别想避免只添加 || $parts[0] == 'HTTP/1.0 200 Connection established' 因为看起来很乱。
是否可以发送一个请求标头来抑制/隐藏额外的标头?或者,我没有看到忽略它的 curl 选项?
出站代理是 Linux/Squid
【问题讨论】:
这真的让我大吃一惊。 @TheSurrican 那是什么? 根据规范,http 响应中只有一个“状态行”,然后是标头定义。也就是说,如果我没看错……w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6 201状态的措辞也很有趣w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.2 【参考方案1】:代理问题是很多脚本都面临的问题。我可以在互联网上找到的首选解决方案是简单地添加以下代码行。
<?php
// cURL automatically handles Proxy rewrites, remove the "HTTP/1.0 200 Connection established" string
if (false !== stripos($response, "HTTP/1.0 200 Connection established\r\n\r\n"))
$response = str_ireplace("HTTP/1.0 200 Connection established\r\n\r\n", '', $response);
?>
现在将它添加到 twilio 客户端确实有点混乱。幸运的是,您可以使用命名空间来重新创建本机函数。请参阅以下示例。
<?php
namespace FakeCurl;
//create curl_exec function with same name, but its created in the FakeCurl namespace now.
function curl_exec($ch)
//execute the actual curl_exec function in the main namespace
$response = \curl_exec($ch);
// cURL automatically handles Proxy rewrites, remove the "HTTP/1.0 200 Connection established" string
if (false !== stripos($response, "HTTP/1.0 200 Connection established\r\n\r\n"))
$response = str_ireplace("HTTP/1.0 200 Connection established\r\n\r\n", '', $response);
return "ADDED TO RESPONSE\r\n\r\n".$response;
//make a regular curl request, no alterations.
$curl = curl_init();
curl_setopt_array( $curl, array(
CURLOPT_HEADER => true,
CURLOPT_NOBODY => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_URL => 'http://***.com' ) );
$response = curl_exec( $curl );
curl_close( $curl );
echo '<pre>'.$response.'</pre>';
?>
输出
ADDED TO RESPONSE
HTTP/1.1 200 OK
Cache-Control: public, max-age=11
Content-Length: 191951
Content-Type: text/html; charset=utf-8
Expires: Wed, 12 Jun 2013 07:09:02 GMT
Last-Modified: Wed, 12 Jun 2013 07:08:02 GMT
Vary: *
X-Frame-Options: SAMEORIGIN
Date: Wed, 12 Jun 2013 07:08:49 GMT
所以要与 twilio 客户端一起使用,您需要在脚本的最顶部放置以下内容:
<?php
namespace FakeCurl;
function curl_exec($ch)
$response = \curl_exec($ch);
// cURL automatically handles Proxy rewrites, remove the "HTTP/1.0 200 Connection established" string
if (false !== stripos($response, "HTTP/1.0 200 Connection established\r\n\r\n"))
$response = str_ireplace("HTTP/1.0 200 Connection established\r\n\r\n", '', $response);
return $response;
include("twilio.php");
?>
如果命名空间选项由于某种原因失败,我会在 twilio 客户端之外添加一个简单的函数。
<?php
function fixProxyResponse($response)
// cURL automatically handles Proxy rewrites, remove the "HTTP/1.0 200 Connection established" string
if (false !== stripos($response, "HTTP/1.0 200 Connection established\r\n\r\n"))
$response = str_ireplace("HTTP/1.0 200 Connection established\r\n\r\n", '', $response);
return $response;
然后更改 twilio 脚本 TinyHttp.php
并仅更改以下行 (~linenr 63)
if ($response = curl_exec($curl))
$parts = explode("\r\n\r\n", $response, 3);
list($head, $body) = ($parts[0] == 'HTTP/1.1 100 Continue')
到
if ($response = curl_exec($curl))
$parts = explode("\r\n\r\n", fixProxyResponse($response), 3);
list($head, $body) = ($parts[0] == 'HTTP/1.1 100 Continue')
【讨论】:
必须等待一个小时才能获奖,但谢谢。您已经证实了我的想法,即我无法摆脱从 Twilio 修改 TinyHttp 代码,我将继续这样做。 等到赏金结束总是好的。有时,当它排在首位时,人们会想出很好的解决方案。 顺便问一下,命名空间选项不起作用吗?那么你根本不需要改变它。 不幸的是,这是对仍然在 centos 5.x 中的站点的更新。我们很快就会迁移它,但现在,对于这个补丁,我必须不使用命名空间。糟透了。【参考方案2】:一些非常晚的澄清。当您通过代理连接到 SSL/TLS 服务器时,代理会使用 HTTP CONNECT 建立隧道。这在RFC2817 和过期的tunneling spec 中有所涉及,而不是在 RFC2616 中。
原始隧道规范要求代理在成功连接到服务器后向客户端返回“200 连接已建立”,这就是您所看到的。在连接变得透明并且您从服务器获得实际响应之前,这可能会跟随更多标头,然后是一个空行。所以,你得到两组标题。 RFC 2817 放宽了这一点,并允许任何 2xx 响应 CONNECT 请求。
这意味着,简而言之,您不能依赖于使用上面的 php 代码检测和删除单个标题行。可能不止一行,第一行可能没有 200 代码,并且可能不包含“已建立连接”字符串。您必须准备好检测两套完整的标头。
cURL 在 2004 年 7.11.1 之前删除了初始连接响应,但现在将所有内容发送回客户端。详情请见here。
【讨论】:
以上是关于如何处理来自代理的额外 HTTP 标头?的主要内容,如果未能解决你的问题,请参考以下文章
使用 HttpClient.GetFromJsonAsync(),如何处理基于 HttpStatusCode 的 HttpRequestException 而无需额外的 SendAsync 调用?
在播放结果上设置 HTTP 标头(如过期) - 以及如何处理 ETag?