Guzzle 的 cURL 处理器:捕获代理 CONNECT 响应头
当代理返回的 CONNECT 响应与网站响应都重要时,用 Guzzle 的 cURL 处理器分别捕获每个响应头块。
经由 HTTP 代理的 HTTPS 请求可能先收到一个接受 CONNECT 的响应,随后才收到隧道内网站返回的响应。初次配置路由可参考 Guzzle 的按请求代理选项。第一个响应里的某个头,未必会出现在最终响应对象上。
若要用自选的 HTTP 端点复现,请从HTTP 代理列表中挑一个候选并确认它能接受 HTTPS 隧道。在 Guzzle 7.10 的 cURL 处理器路径下,把每次 header 回调保存为独立的块:
$blocks = [];
$client = new \GuzzleHttp\Client([
'handler' => \GuzzleHttp\HandlerStack::create(
new \GuzzleHttp\Handler\CurlHandler()
),
]);
$response = $client->get('https://www.example.com/', [
'proxy' => 'http://proxy.example:8080',
'curl' => [CURLOPT_SUPPRESS_CONNECT_HEADERS => false],
'on_headers' => function (
\Psr\Http\Message\ResponseInterface $part
) use (&$blocks) {
$blocks[] = [
'status' => $part->getStatusCode(),
'reason' => $part->getReasonPhrase(),
'headers' => $part->getHeaders(),
];
},
]);cURL 扩展必须提供 CURLOPT_SUPPRESS_CONNECT_HEADERS,该选项由 libcurl 7.54.0 引入。将其设为 false 后,CONNECT 响应头才会进入 cURL 的 header 回调。Guzzle 7.10 的处理器会为每个已完成的响应头块构建一个响应,并在该边界调用 on_headers。
请保持块的顺序。认证质询、重定向和信息类响应都可能产生额外的块;不要把全部取值压进一个数组,也不要仅凭状态码是 200 就给响应定性。
返回的响应代表已完成请求的响应,而 blocks 保留了回调收集到的观测。若隧道或 TLS 协商失败,请在调用方应用里处理抛出的 transfer 异常;此时可能根本不存在最终的网站响应。
该行为与上述 cURL 处理器绑定。自定义处理器或 stream 处理器没有义务暴露同样的代理握手回调。把捕获到的代理响应头当作诊断数据,并避免不必要地留存与账户相关的取值。