php 由于代理,curl 在浏览器和 CLI 下的行为不同

php curl behaves differently under browser & CLI due to proxy

最初我在尝试弄清楚为什么当我尝试通过 CLI 执行相同的脚本时 php 浏览器下的 curl 行为不同时遇到了问题。

通过打开 CURLOPT_VERBOSE 日志输出并比较 CLI 和浏览器的结果,以下是我看到的差异:

CLI 下的 CURL

* About to connect() to proxy localhost port 3128 (#4)
*   Trying ::1...
* Connection refused
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 3128 (#4)
* Establish HTTP proxy tunnel to someurl.com:443
* Server auth using Basic with user 'some_username'
> CONNECT someurl.com:443 HTTP/1.1
Host: someurl.com:443
Proxy-Connection: Keep-Alive

< HTTP/1.1 407 Proxy Authentication Required
< Mime-Version: 1.0
< Date: Fri, 11 Dec 2020 12:04:46 CST
< Via: 1.1 someotherurl.com:8080 (Cisco-WSA/12.0.1-334)
< Content-Type: text/html
< Connection: close
< Proxy-Connection: close
< Content-Length: 2109
< X-RBT-SCAR: 2.3.4.5:11517381:2000
< Proxy-Authenticate: Basic realm="Cntlm for parent"
* Authentication problem. Ignoring this.
< 
* Received HTTP code 407 from proxy after CONNECT
* Connection #4 to host localhost left intact

浏览器下的 CURL

* About to connect() to someurl.com port 443 (#6)
*   Trying 1.2.3.4...
* Connected to someurl.com (1.2.3.4) port 443 (#6)
* warning: ignoring value of ssl.verifyhost
* skipping SSL peer certificate verification
* SSL connection using TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
* Server certificate:
*   subject: C=US,ST=FL,L=Boca Raton,O=Telit IoT Platforms,OU=secureWISE,CN=someurl.com
*   start date: Apr 15 21:18:15 2020 GMT
*   expire date: May 15 21:18:15 2022 GMT
*   common name: someurl.com
*   issuer: E=support@securewise.net,CN=secureWISE CA-256,OU=SecureWISE Certificate Authority,O=ILS Technology LLC,O=Telit Wireless Solutions Inc,L=Boca Raton,ST=Florida,C=US
* Server auth using Basic with user 'some_username'
> GET /someurl HTTP/1.1
Authorization: Basic SomeAuthKey
Host: someurl.com
Accept: */*

< HTTP/1.1 200 OK
< Date: Fri, 11 Dec 2020 04:07:40 GMT
< Server: Apache-Coyote/1.1
< X-Powered-By: Undertow/1
< Set-Cookie: JSESSIONID=c2BBPwZBjGxCaH5om6unoKaI; path=/
< Set-Cookie: somekey=somevalue; path=/
< Content-Type: text/xml
< Content-Length: 125291
< Content-disposition: attachment; filename=somefilename.xml
< Vary: Accept-Encoding,User-Agent
< SWOrigin: sw_proxy
< Connection: close
< 
* Closing connection 6

我最初的预感是这与代理有关(因为这台电脑确实使用代理上网)

并且查看浏览器日志,似乎跳过了代理。

我还检查了浏览器和 CLI 的 phpinfo(),我可以看到定义了 proxyhttp_proxyhttps_proxy在环境变量中,以及 CLI$_SERVER 下,但 不在浏览器上 ,这让我更相信我的假设是正确的。

所以为了解决这个问题,我尝试在 curl 调用之前添加以下代码:

        if(isset($_SERVER['http_proxy']))
            unset($_SERVER['http_proxy']);       
        if (isset($_SERVER['https_proxy']))
            unset($_SERVER['https_proxy']);
        if (isset($_SERVER['proxy']))
            unset($_SERVER['proxy']);

        if(isset($_ENV['http_proxy']))
            unset($_ENV['http_proxy']);       
        if (isset($_ENV['https_proxy']))
            unset($_ENV['https_proxy']);
        if (isset($_ENV['proxy']))
            unset($_ENV['proxy']);

        curl_setopt($ch, CURLOPT_URL, $target_url);
        curl_setopt($ch, CURLOPT_HEADER, 0);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_USERPWD, "someuser:somepass");
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_TIMEOUT, 10);
        curl_setopt($ch, CURLOPT_VERBOSE, true);
        $result = curl_exec($ch); 
        curl_close($ch);

但是verbose还是显示在CLI下执行时还是会尝试通过代理

对此有什么建议吗?

仔细研究后发现,我所要做的就是通过在 NoProxy 配置中包含 url 来绕过 /etc/cntlm.conf 中的 someurl.com。