如何强制 CURL 请求 http/1.1?或者可能还有其他问题,不确定

Posted

技术标签:

【中文标题】如何强制 CURL 请求 http/1.1?或者可能还有其他问题,不确定【英文标题】:How to force CURL to ask for http/1.1? Or maybe there's another issue, not sure 【发布时间】:2017-06-13 03:51:44 【问题描述】:

我有一段代码(我们将其命名为 Code A)在一个框架中工作,我想让它在另一个框架中工作。工作代码使用这样的 CURL 发出成功的 POST 请求(使用 CURLOPT_VERBOSE 的请求):

* Connected to android.clients.google.com (216.58.209.238) port 443 (#0)
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
*   CAfile: /Applications/MAMP/Library/OpenSSL/cert.pem
    CApath: none
* SSL connection using TLSv1.2 / ECDHE-ECDSA-AES128-GCM-SHA256
* ALPN, server accepted to use http/1.1
* Server certificate:
*  subject: C=US; ST=California; L=Mountain View; O=Google Inc; CN=*.google.com
*  start date: Jan 18 19:17:59 2017 GMT
*  expire date: Apr 12 18:51:00 2017 GMT
*  subjectAltName: host "android.clients.google.com" matched cert's "android.clients.google.com"
*  issuer: C=US; O=Google Inc; CN=Google Internet Authority G2
*  SSL certificate verify ok.
> POST /auth HTTP/1.1
Host: android.clients.google.com
Accept: */*
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Content-Length: 97

这里的 http 框架(具体来说是 yii2/httpclient)有太多的依赖项,无法将其带到另一个项目中,所以我试图像这样在低级别重新创建它(我们将其命名为 Code B):

<?php 
$ch = curl_init();
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, 'post-data-here');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_URL, 'https://android.clients.google.com/auth');
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Content-Type: application/x-www-form-urlencoded; charset=UTF-8"]); // just because I'm desperate
curl_setopt($ch, CURLOPT_VERBOSE, true);
$content = curl_exec($ch);

我希望得到相同的结果,但这就是我得到的:

* Connected to android.clients.google.com (216.58.209.238) port 443 (#0)
* TLS 1.2 connection using TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
* Server certificate: *.google.com
* Server certificate: Google Internet Authority G2
* Server certificate: GeoTrust Global CA
> POST /auth HTTP/1.1
Host: android.clients.google.com
Accept: */*
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Content-Length: 97
* upload completely sent off: 97 out of 97 bytes
< HTTP/1.1 200 OK
< Content-Type: text/plain; charset=utf-8
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: Mon, 01 Jan 1990 00:00:00 GMT
< Date: Fri, 27 Jan 2017 12:07:12 GMT
< X-Content-Type-Options: nosniff
< X-Frame-Options: SAMEORIGIN
< X-XSS-Protection: 1; mode=block
< Server: GSE
< Alt-Svc: clear
< Accept-Ranges: none
< Vary: Accept-Encoding
< Transfer-Encoding: chunked
<html>
<HEAD>
<TITLE>HTTP Version Not Supported</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000">
<H1>HTTP Version Not Supported</H1>
<H2>Error 505</H2>
</BODY>
</HTML>

我得到“错误 505:不支持 HTTP 版本”,而不是有效响应。我看到的唯一区别是工作代码尝试“ALPN,提供 http/1.1”,而后一个代码不这样做。以及之后的证书部分,但它从未在代码 A 中提到过,所以我不确定它会做什么来提供它。

两个版本的代码在同一台服务器上运行,相同版本的 PHP (5.6) 和 CURL (7.51.0)。详细日志的差异在发送任何数据之前就开始了,所以我猜这与任何数据或标头设置不正确无关。

到目前为止我尝试了什么(效果很小或没有效果):

    curl_setopt($ch, CURLOPT_SSL_ENABLE_ALPN, 不管什么) - 不起作用,因为 CURLOPT_SSL_ENABLE_ALPN 根本没有定义(虽然它必须在这个版本的 CURL 中,但不知道出了什么问题) curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 不管) curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 不管什么) curl_setopt($ch, CURLOPT_SSLVERSION, 不管) 其他一些令人绝望的愚蠢的东西,我什至羞于在这里展示

我试图尽可能深入地学习工作代码,但它似乎对简单的 HTTP POST 没有任何作用。我跟踪了它制作的每个 curl_setopt ,似乎只有我在代码中使用的这些 curl_setopt ,没有多余的。尽管如此,它仍然有效,而我的代码却没有。

我试过用命令行做同样的事情:

$ curl https://android.clients.google.com/auth -v --http1.1 -X POST --no-alpn --no-npn --data "copypasted-post-data-from-code-B-and-yes-its-urlencoded"

得到正确的结果:

*   Trying 216.58.209.238...
* TCP_NODELAY set
* Connected to android.clients.google.com (216.58.209.238) port 443 (#0)
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
*   CAfile: /Applications/MAMP/Library/OpenSSL/cert.pem
  CApath: none
...
> POST /auth HTTP/1.1
> Host: android.clients.google.com
> User-Agent: curl/7.51.0
> Accept: */*
> Content-Length: 97
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 97 out of 97 bytes
< HTTP/1.1 200 OK
< Content-Type: text/plain; charset=utf-8
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: Mon, 01 Jan 1990 00:00:00 GMT
< Date: Fri, 27 Jan 2017 13:22:27 GMT
< X-Content-Type-Options: nosniff
< X-Frame-Options: SAMEORIGIN
< X-XSS-Protection: 1; mode=block
< Server: GSE
< Alt-Svc: clear
< Accept-Ranges: none
< Vary: Accept-Encoding
< Transfer-Encoding: chunked
<
SID=BAD_COOKIE
LSID=BAD_COOKIE
Auth=here-s-the-data-i-need

【问题讨论】:

正如我在上一节中看到的,提供了一个 ssl 证书,但您没有提供任何证书..所以响应永远不会相同..除此之外 curl 有什么问题吗? 嗯,是的,也是如此,但我也没有在代码 A 中提供该证书路径。我不确定它做了什么来提供它以及我该如何重复它。我的猜测是 CURLOPT_CAINFO、CURLOPT_CAPATH(没有效果,并且在工作代码中的任何地方都没有使用) SSL 证书是一种通过 TCP 协议建立安全连接的签名。但在服务提供商拒绝不安全的连接之前,这不是强制性的。我只想问你对响应有什么问题吗?不是响应标头 我用代码 B 得到的响应是

HTTP Version Not Supported

Error 505

这就是问题所在:) 代码 A 产生了所需的响应,没有错误随便
只需手动定义它然后`if(!defined('CURLOPT_SSL_ENABLE_ALPN'))define('CURLOPT_SSL_ENABLE_ALPN',226); 并确保 curl_setopt(_array?) 返回 bool(true) 【参考方案1】:

好的,我想通了。

实际上,在代码 B 中的那段代码之前,使用另一个库(确切地说是 Httpful)执行了另一个请求,并且似乎该请求以某种方式搞砸了。我不知道某些东西会影响使用干净的 curl_init() 执行的请求。

无论如何,当我用低级别的请求替换之前的所有这些 Httpful 调用时,一切正常。

【讨论】:

【参考方案2】:

Error 505: HTTP Version Not Supported 不是 curl/libcurl 返回的错误字符串,这听起来像您从正在与之通信的服务器收到的 content。如果您向我们展示包括标头在内的完整 HTTP 响应,我们可能已经看到了。

因此,您使用不同的 curl 选项进行的所有尝试都是徒劳的,因为 curl 每次都运行良好。您还可以使用 curl 命令行工具对您尝试工作的主机进行验证:

curl https://android.clients.google.com/auth -v --http1.1 -X POST --no-alpn --no-npn

此命令行显示 TLS 和 HTTP“层”都很好。

另一个问题here 在传入错误数据(不是 url 编码)时遇到了类似的错误。也许您有类似的东西,但由于您切换到新框架而错过了它?

【讨论】:

是的,你是对的,HTTP 代码实际上是 200,看起来 505 是我只在内容中看到的错误。尝试使用代码 B 中完全相同的字符串(它是 url 编码)从命令行发送实际的 POST 并获得所需的内容。我现在将其添加到问题中

以上是关于如何强制 CURL 请求 http/1.1?或者可能还有其他问题,不确定的主要内容,如果未能解决你的问题,请参考以下文章

如何在 PHP 的 cURL POST HTTP 请求中包含授权标头?

如何强制客户端使用 http/2? (而不是回退到 http 1.1)

如何通过php的curl模拟ajax请求,获取其返回值

查看包含 POST 数据的 cURL 请求标头

强制 cURL 在代理上使用 GET 来处理 HTTPs 请求

HTTP入门