使用 PHP Curl 扩展进行HTTP/3请求的优化

2023-11-05 14:44:40 浏览数 (2)

简介

HTTP/3 是 HTTP 的第三个主要版本,基于 QUIC。与依赖 TCP 的 HTTP/1.1 和 HTTP/2 不同,HTTP/3 基于名为 QUIC 的多路复用 UDP 协议。HTTP/3 和 TLS 1.3 可以提供巨大的性能和延迟改进。尽管 HTTP/3 更改了很多传输层语义(例如从 TCP 到 UDP 的转变),但请求标头、请求方法、响应和状态代码的 HTTP 语义。

现在所有主流浏览器都支持 HTTP/3,而 HTTP 客户端和 Web 服务器(如 Curl、Nginx 和 Litespeed)则提供实验性支持。Caddy Server 甚至默认启用了 HTTP/3 支持。

利用 Curl 中提供的实验性 HTTP/3 支持,PHP 的 Curl 扩展可以使用 HTTP/3 支持来构建。本文解释了如何使用 HTTP/3 支持编译 PHP Curl 扩展及其依赖项,以及如何使用 PHP 发出 HTTP/3 请求。

如何使用 PHP Curl 扩展发出 HTTP/3 请求

Curl 有一个名为 CURLOPT_HTTP_VERSION 的选项,可用于设置 Curl 处理程序可在 HTTP 请求中使用的 HTTP 版本。默认情况下,当前的 Curl 版本默认为 HTTP/2HTTP/1.1 回退。如果 Web 服务器不支持 HTTP/2,Curl 将无缝使用 HTTP/1.1

对于 HTTP/3,Curl 的行为方式相同。Curl 有一种称为 HTTPS Eyeballing 的方法,它试图建立 QUIC 握手,但硬超时时间为 200 毫秒。这可确保在连接速度足够快时使用 HTTP/3,但不会对不使用 HTTP/3 的请求产生任何重大影响。

要使用 Curl 创建 HTTP/3 条件

  • Curl 必须使用 HTTP/3 支持构建
  • Curl 版本 7.66 或更高版本
  • PHP 8.2 或更高版本

使用 PHP Curl 扩展发出 HTTP/3 请求就像设置 CURLOPT_HTTP_VERSION 选项一样简单:

代码语言:javascript复制
$ch = curl_init("https://php.watch/");
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_3);
curl_exec($ch);

注:PHP 8.3 或任何较旧的 PHP 版本中都没有声明该 CURL_HTTP_VERSION_3 常量。这是修复它的拉取请求。合并拉取请求后,此通知将被删除。

为了确保在未声明 CURL_HTTP_VERSION_3 常量的情况下的兼容性,可以将其声明为 user-land,或者只是将常量值传递给 curl_setopt 函数。

代码语言:javascript复制
if (!defined('CURL_HTTP_VERSION_3')) {
    define('CURL_HTTP_VERSION_3', 30);
}

$ch = curl_init("https://php.watch/");

curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_3);
// or
curl_setopt($ch, CURLOPT_HTTP_VERSION, 30);

curl_setopt($ch, CURLOPT_HEADER, true);
curl_exec($ch);

如果 Curl 支持 HTTP/3,则代码片段会使用 Curl 发出 HTTP/3 请求:

代码语言:javascript复制
HTTP/3 200
strict-transport-security: max-age=31536000;includeSubDomains;preload
content-type: text/html; charset=UTF-8
date: Sat, 28 Oct 2023 18:38:46 GMT
...

Curl 中的 CURL_HTTP_VERSION_3 选项意味着允许 Curl 使用最高 HTTP/3 的 HTTP 版本。如果远程服务器不支持 HTTP/3,Curl 将静默且无缝地回退到服务器和 Curl 都支持的另一个 HTTP 版本。

请注意,CURL_HTTP_VERSION_3 在未使用 HTTP/3 支持构建的 Curl 扩展上使用将导致请求在 和 curl_setopt curl_exec 调用时返回 false

以下代码片段使用 CURL_HTTP_VERSION_3ONLY(= 31 ),它告诉 Curl 使用 CURL_HTTP_VERSION_3ONLY HTTP/3 而不进行回退。如果远程服务器和 Curl 不支持 HTTP/3,则请求将失败。

代码语言:javascript复制
if (!defined('CURL_HTTP_VERSION_3ONLY')) {
    define('CURL_HTTP_VERSION_3ONLY', 31);
}

$ch = curl_init("https://php.watch/");

curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_3ONLY);
// or
curl_setopt($ch, CURLOPT_HTTP_VERSION, 31);

curl_exec($ch);

在使用 Curl 发出 HTTP/3 请求之前,请确保先检查 Curl 是否支持 HTTP/3。

检测 PHP Curl 扩展中的 HTTP/3 支持

声明 PHP 常量 CURL_VERSION_HTTP3CURL_HTTP_VERSION_3 , 和 CURL_HTTP_VERSION_3ONLY 这一事实并不意味着 Curl 是在 HTTP/3 支持下构建的。

有三种方法可以检查 Curl 扩展是否支持 HTTP/3。

phpinfo() 输出并 php -i 显示 Curl 扩展是否是使用 HTTP/3 支持构建的:

phpinfo 输出显示 HTTP/3 支持

curl_version 函数功能

curl_version() 函数返回数组 features 的值是 Curl 支持的所有功能的位掩码。以下代码片段显示了使用 curl_version() 函数和功能标志 CURL_VERSION_HTTP3 检测HTTP/3 支持:

代码语言:javascript复制
if (defined('CURL_VERSION_HTTP3') && curl_version()['features'] & CURL_VERSION_HTTP3) {
    // HTTP/3 supported 
}

调用的 curl_setopt 返回值

CURLOPT_HTTP_VERSION 选项设置为 CURL_HTTP_VERSION_3 时,如果 HTTP/3 未内置到 Curl 扩展中,则 Curl 返回 false

代码语言:javascript复制
$ch = curl_init("https://php.watch/");

$h3_status = curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_3);
if ($h3_status === false) {
    curl_setopt($ch, CURLOPT_HTTP_VERSION, null)
}

curl_exec($ch);

设置 CURLOPT_HTTP_VERSION = null 将重置选择 HTTP/3 的失败尝试,并让 Curl 选择最佳 HTTP 版本。

如何构建支持 HTTP/3PHP Curl 扩展

HTTP/3 标准本身仍处于“建议标准”状态,这比成为 Internet 标准晚了一个级别。虽然大多数主流浏览器已经支持 HTTP/3,但在 Web 服务器之间可能还有其他硬件和软件(其中没有多少首先支持 HTTP/3)不支持 HTTP/3,或者满足 HTTP/3 的基本要求,例如允许 UDP 流量到端口。

Curl 本身对 HTTP/3 的支持也被标记为实验性。此外,Debian/Ubuntu 和 Fedora/RHEL 及其衍生产品中 PHP Curl 扩展的预构建包都不支持 HTTP/3。

在 PHP Curl 扩展中启用 HTTP/3 支持需要使用 Curl 本身所依赖的必要库编译 libcurl,然后使用该 libcurl 编译 Curl 扩展。

不建议对生产系统这样做

Curl 的 HTTP/3 文档提供了使用 HTTP/3 编译 Curl 的最新说明。Curl 可以是不同的加密和传输库,但根据 PHP 的多次测试。观察,用 ngtcp2nghttp3 构建 Curl,并 WolfSSL 产生最佳结果。某些包含 OpenSSL 修补版本的组合根本不起作用,并且在尝试执行请求时出现段错误。

构建依赖

必须在系统上安装构建工具,例如 C 编译器、该 make 工具和其他必需品。

使用 ngtcp2、nghttp3 和 WolfSSL 构建 Curl

代码语言:javascript复制
# Prepare working space
cd ~
mkdir curl
mkdir build
cd curl

# Build WolfSSL
git clone https://github.com/wolfSSL/wolfssl.git
cd wolfssl
autoreconf -fi
./configure --prefix=~/build/~/build/wolfssl --enable-quic --enable-session-ticket --enable-earlydata --enable-psk --enable-harden --enable-altcertchains
make
make install

# Build nghttp3
cd ..
git clone -b v1.0.0 https://github.com/ngtcp2/nghttp3
cd nghttp3
autoreconf -fi
./configure --prefix=~/build/nghttp3 --enable-lib-only
make
make install

# Build ngtcp2
cd ..
git clone -b v1.0.1 https://github.com/ngtcp2/ngtcp2
cd ngtcp2
autoreconf -fi
./configure PKG_CONFIG_PATH=~/build/wolfssl/lib/pkgconfig:~/build/nghttp3/lib/pkgconfig LDFLAGS="-Wl,-rpath,~/build/wolfssl/lib" --prefix=~/build/ngtcp2 --enable-lib-only --with-wolfssl
make
make install

# Build curl
cd ..
git clone https://github.com/curl/curl
cd curl
autoreconf -fi
./configure --with-wolfssl=~/build/wolfssl --with-nghttp3=~/build/nghttp3 --with-ngtcp2=~/build/ngtcp2
make
make install

如有必要 ~/build/wolfssl ,请更改前缀~/build/nghttp3~/build/nghttp3

使用新的 libcurl 构建 PHP Curl 扩展

由于上面对 make install Curl 的调用,Curl 二进制文件和 libcurl 在系统范围内安装。当 PHP 使用 Curl 扩展编译时,它现在会选择支持 HTTP/3 的新 libcurl 版本。

在Debian/Ubuntu和Fedora/RHEL系统的指南中,有关编译PHP的详细指南应该列出所有步骤。

确保 ./configure PHP 使用 --with-curl .如果 Curl 未安装在系统范围内(即不在 /usr/local 中),也可以在此处指定目录。

编译PHP的快速指南如下:

代码语言:javascript复制
sudo apt install build-essential autoconf libtool bison re2c
git clone https://github.com/php/php-src.git --branch=master
cd php-src
./buildconf
./configure --with-curl
make -j $(nproc)
sudo make install

总结

PHP 的 Curl 扩展可以通过对 HTTP/3 的实验性支持来构建。不幸的是,它需要编译 Curl 扩展,这使得依赖操作系统软件包存储库的更新来获取安全和错误修复更新的系统具有挑战性。

以下代码片段演示如何在内置了 HTTP/3 支持的系统上发出 HTTP/3 请求:

代码语言:javascript复制
$ch = curl_init("https://www.tinywan.com/");
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_3);
curl_exec($ch);

0 人点赞