php 如何强制 CURL 请求 http/1.1?或者也许还有另一个问题,不确定

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/41894411/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-26 02:29:53  来源:igfitidea点击:

How to force CURL to ask for http/1.1? Or maybe there's another issue, not sure

phpsslcurl

提问by Andrey

I've got a piece of code (let's name it Code A) working in a framework and I want to make it work in another framework. The working piece of code makes a successful POST request using CURL like this (request with CURLOPT_VERBOSE on):

我有一段代码(让我们将其命名为代码 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

The http framework here (yii2/httpclient, to be specific) has too many dependencies to bring it to the other project so I'm trying to recreate it on low level like this (let's name it Code B):

这里的 http 框架(具体来说是 yii2/httpclient)有太多的依赖,无法将它带到另一个项目,所以我试图在像这样的低级别重新创建它(让我们将其命名为代码 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);

I'm expecting that to have the same result, but this is what I get:

我期待有相同的结果,但这是我得到的:

* 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>

And instead of a valid response I get "Error 505: HTTP Version Not Supported". The only difference I see is that the working code tries to "ALPN, offering http/1.1" while the latter code doesn't do that. And the certificate part after that, but it's never ever mentioned in code A so I'm not sure what does it do to provide it.

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

Both versions of code run on the same server, same version of PHP (5.6) and CURL (7.51.0). The difference in verbose log starts BEFORE any data being sent, so I guess it's not about any data or headers being set incorrectly.

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

What I tried so far (with little or no effect):

到目前为止我尝试过的(效果很小或没有效果):

  1. curl_setopt($ch, CURLOPT_SSL_ENABLE_ALPN, whatever) - does not work because CURLOPT_SSL_ENABLE_ALPN is not defined at all (although it must be in this version of CURL, not sure what's wrong)
  2. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, whatever)
  3. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, whatever)
  4. curl_setopt($ch, CURLOPT_SSLVERSION, whatever)
  5. some other desperate silly stuff I'm even ashamed to show here
  1. curl_setopt($ch, CURLOPT_SSL_ENABLE_ALPN,whatever) - 不起作用,因为 CURLOPT_SSL_ENABLE_ALPN 根本没有定义(虽然它必须在这个版本的 CURL 中,不知道有什么问题)
  2. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 无论如何)
  3. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 无论如何)
  4. curl_setopt($ch, CURLOPT_SSLVERSION, 无论如何)
  5. 其他一些我什至羞于在此展示的绝望愚蠢的东西

I've tried to learn the working code as deep as I could but it seems that it doesn't do anything over the simple HTTP POST. I tracked every curl_setopt it makes and it seems there are only these curl_setopt I used in my code, nothing extra. Still, it works and my code doesn't.

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

I've tried making the same using the command line:

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

$ 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"

Got the correct result:

得到正确的结果:

*   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

采纳答案by Andrey

Okay, I figured it out.

好吧,我想通了。

Actually, BEFORE that piece of code in Code B there was another request executed using yet another library (Httpful, to be exact) and it seems that request messed things up somehow. I did not know that something can affect a request performed with a clean curl_init().

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

Anyway, when I replaced all those Httpful calls prior to the request in question with low level ones, everything worked fine.

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

回答by Daniel Stenberg

Error 505: HTTP Version Not Supportedis not an error string that is returned by curl/libcurl, that sounds like contentthat you receive from the server you're communicating with. If you would show us the full HTTP response including headers, we could probably have seen that.

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

So, all your playing around with different curl options was to no gain because curl worked fine every single time. You could also verify that with using the curl command line tool against the host you're trying to get to work:

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

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

This command line shows that both the TLS and HTTP "layers" are fine.

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

Another question heregot a similar error when they passed in the wrong data (not url encoded). Maybe you have something similar and because of your switch to a new framework you missed it?

当他们传入错误的数据(不是 url 编码的)时,这里的另一个问题出现了类似的错误。也许你有类似的东西,因为你转向了一个新的框架,你错过了它?