PHP Curl - 当不允许HTTP/0.9时收到该怎么办

19

当我尝试向苹果的推送服务发送一个HTTP/2.0的POST请求时,我遇到了奇怪的行为:

        $http2ch = curl_init();
        curl_setopt($http2ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
        curl_setopt($http2ch, CURLOPT_URL, 'https://api.push.apple.com/3/device/megauniquedevicetokendummy');
        curl_setopt($http2ch, CURLOPT_PORT, 443);
        curl_setopt($http2ch, CURLOPT_HTTPHEADER, $httpHeader);
        curl_setopt($http2ch, CURLOPT_POST, true);
        curl_setopt($http2ch, CURLOPT_POSTFIELDS, $body);
        curl_setopt($http2ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($http2ch, CURLOPT_TIMEOUT, 30);
        curl_setopt($http2ch, CURLOPT_HEADER, 1);

       $result = curl_exec($http2ch);
       if ($result === false) {
           throw new \Exception("Curl failed: " . curl_error($http2ch) . " | " . curl_getinfo($http2ch, CURLINFO_HTTP_CODE));
       }

异常抛出信息为: Curl failed: Received HTTP/0.9 when not allowed | 0。
我明确告诉curl在代码片段的第二行使用HTTP/2.0。是否有人知道这个错误消息的含义,以及为什么curl使用如此老旧的HTTP版本?
我的PHP版本为7.2,curl版本为7.66.0。

你尝试过使用HTTP/1.1或HTTP/1.0吗? - Pavel Lint
@PavelLint,是的,我都试过了,而且不使用这些选项也会导致相同的错误。 - rockZ
听起来像是一个潜在的错误,但我尝试使用curl命令行针对该URL重现该错误时失败了... - Daniel Stenberg
1
我认为我可能找到了问题所在。在我发送请求的服务器上,curl没有编译“nghttp2”。该服务器仅接受HTTP/2连接,而当未编译“nghttp2”时,curl无法实现此功能。使用--http2标志,curl会响应curl: (1) Unsupported protocol。如果我从我的Mac发送完全相同的请求,则可以直接使用。 - rockZ
我刚在 GitLab 服务器上向一个仓库进行 git push 操作时遇到了类似于“不允许接收 HTTP/0.9”的错误,但是当我重试后就成功了。 - Richard
4个回答

29

当服务器是grpc服务器时,也可能会出现这种情况。当使用curl访问grpc服务器或其他非HTTP服务器,并且服务器没有响应一个有效的HTTP状态行,而是curl期望的时候,curl将打印“Received HTTP/0.9 when not allowed”。

如果curl打印类似于“未知协议”的内容,而不是假设它是0.9,可能会更好,因为如今访问像grpc服务器这样的内容比实际的HTTP 0.9服务器要普遍得多。


2
那么,这个问题有什么解决方案?其他的方法对我都不起作用。 - arahpanah
1
@arahpanah 我认为,如果你正在访问一个grpc端点,但是期望的是普通的Web服务器,那么可能存在误解。你认为运行的软件并不是你想的那个,需要使用其他工具而不是curl来访问服务器。对于grpc,通常需要一个定制的客户端来访问该服务(你需要protobuf文件或通过反射进行发现),但是像grpcurl这样的工具也可能起作用。 - d-chord
2
我确认当运行 curl http://<server_ip:139> 对Samba/SMB(Windows)服务器进行测试时,此消息也会显示。 - mirekphd
@arahpanah 的解决方案是使用 --http2-prior-knowledge,它强制 curl 使用仅 HTTP2 连接而不是 HTTP1.1 升级。 - Blackclaws
我在使用Python的http.server时遇到了这个问题。有关响应可能是由于我的响应引起的提示,让我重新排列了send_response() / send_header()调用。最终我发现send_response(200)需要首先调用,然后是send_header()调用,最后是end_header()调用。对我来说,send_response()和send_header()只是在对象中设置东西,所以我没有想到这两者之间的顺序会有所不同。 - Philippe Carphin

9

我找到解决方法了。 请确保curl已经编译了nghttp2。

如果您不确定,可以在终端上使用curl --version命令进行检查。

如果您发现没有nghttp2/{version},则需要重新编译带有nghttp2的curl。

curl --version示例,其中缺少nghttp2:

curl 7.66.0 (amd64-portbld-freebsd12.0) libcurl/7.66.0 OpenSSL/1.1.1d zlib/1.2.11

curl --version 的例子,其中 nghttp2 可用:

curl 7.64.1 (x86_64-apple-darwin19.0) libcurl/7.64.1 (SecureTransport) LibreSSL/2.8.3 zlib/1.2.11 nghttp2/1.39.2

1
在搜索了数小时后,我找到了这个答案。对于任何想要编译带有nghttp2的curl的人来说,这里的说明对我很有用:https://gist.github.com/jjpeleato/3327c2e38fc0fea7d6602401f9849809 如果您已经安装了curl版本,则需要指向新的版本(对我来说是/usr/local/bin/curl)。 - Abhimanyu Talwar
10
即使使用编译了nghttp2的curl,我仍然遇到问题。 - bfontaine
2
一样的问题。我的curl(7.79.1)有nghttp2/1.45.1,会出现HTTP/0.9错误。我正在运行最新的nghttpd Web服务器进行测试。 - Paul
1
我已经编译了带有nghttp2的curl,但我仍然遇到这个问题。 - arahpanah
1
我的curl已经编译了nghttp2,但我仍然遇到这个问题。 - étale-cohomology
显示剩余2条评论

4

我认为这不需要你编译不同版本的curl,而是需要设置选项以允许旧服务器响应http 0.9。PHP有一些关于"CURLOPT_HTTP09_ALLOWED"的注释,这可能与您通过 https://www.php.net/manual/en/function.curl-setopt.php 发布问题时有所不同。

对我来说克服错误的选项是:

curl_setopt($http2ch, CURLOPT_HTTP09_ALLOWED, true);

0
只是分享我的经验。 我将我的PHP和Elasticsearch容器化了。并映射了端口9300:9200。 然后遇到了上述错误。 我将端口映射改为9200:9200,问题就解决了。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接