Guzzle在使用HTTP代理情况下全部返回404的问题

2017-04-11 分类 [Web 开发]

按照laravel的官方教程,我使用composer安装了laravel-installer,然后创建新项目使用命令laravel new project,很快我得到了一个错误:

Crafting application...


  [GuzzleHttp\Exception\ClientException]
  Client error: `GET http://cabinet.laravel.com/latest.zip` resulted in a `404 Not found` response:
  <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
  <html><head>
  <tit (truncated...)

这就很尴尬了,告诉我404,但是我用浏览器输入网址http://cabinet.laravel.com/latest.zip,明显是可以下载的,这是扯蛋呢。由于错误输出被折叠了,我们捕捉这个报错并输出出来,修改vender/laravel/installer/src/NewCommand.php的第125行,如下:

<?php
$response = (new Client)->get('http://cabinet.laravel.com/'.$filename);

修改为

<?php
try {
    $response = (new Client)->get('http://cabinet.laravel.com/'.$filename);
} catch (\GuzzleHttp\Exception\ClientException $e) {
    $response = $e->getResponse();
    $responseBodyAsString = $response->getBody()->getContents();
    echo $responseBodyAsString;
}

接着我们得到了输出:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html><head>
<title>Proxy error: 404 Not found.</title>
</head><body>
<h1>404 Not found</h1>
<p>The following error occurred while trying to access <strong>/latest.zip</strong>:<br><br>
<strong>404 Not found</strong></p>
<hr>Generated Tue, 11 Apr 2017 21:58:05 CST by Polipo on <em>Gargoyle:8123</em>.
</body></html>

明眼人一看就知道,这不是laravel.com的返回啊,其实这是我跑在路由器上的HTTP代理Polipo,在系统中选的是自动发现代理,没想到在CMD下面竟然也被设置了环境变量HTTP_PROXY,更没想到PHP的laravel installer会自动检测代理并使用。
当然最没有想到是竟然会有这么障的程序将请求直接转到了代理服务器上。
到这里我们已经知道了laravel installer使用了GuzzleHttp这个库做网络访问,并且因为HTTP_PROXY环境变量的设置最终导致了这个问题。想要解决问题很简单,在Windows下的CMD上直接输入SET HTTP_PROXY=清除了代理就好了,再次运行laravel new project完美新建工程。
但是问题既然已经出现了,不能坐视不管,再次打开CMD,检查代理的环境变量还在,重新运行laravel new命令,这次我们使用WireShark抓包,可以看到是这样的:
WireShark抓包GuzzleHTTP 看Full request URI,没毛病啊,再看看【正常】使用代理的软件是怎么表现的:
WireShark抓包Chrome 这年头找个纯HTTP的站也不容易了,还好Cocos这种low逼至今没有上HTTPS,直观得看两次请求,最大的区别就是Guzzle的GET使用的是相对于HOST的路径,但Chrome则把绝对路径都放到了GET里面,这会不会是问题的关键呢? 经过一番Google,关键词proxy absolute uri,我找到了一篇RFC文档:RFC2068,节5.1.2明确写了

The absoluteURI form is required when the request is being made to a proxy.

至此问题的根源已经找到了, 就是GuzzleHTTP向代理发送了一个不合法的请求,代理只好假装自己是一个HTTP服务器进行返回:404,没有这个文件,那么GuzzleHTTP是怎么造出了这样一个不合法的请求呢?这就要深入到GuzzleHTTP的代码检查了,这里不再继续下去。