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/1.1 回退的 HTTP/2。如果 Web 服務器不支持 HTTP/2,Curl 將無縫使用 HTTP/1.1。
對於 HTTP/3,Curl 的行為方式相同。Curl 有一種稱為 HTTPS Eyeball 的方法,它嘗試建立 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 選項一樣簡單:
$ch = curl_init("https://php.watch/");
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_3);
curl_exec($ch);
CURL_HTTP_VERSION_3 常量僅在 PHP 8.4 及更高版本中可用。
為了確保在未聲明 CURL_HTTP_VERSION_3 常量的情況下的兼容性,可以將其聲明為 user-land,或者簡單地將 constant 值傳遞給 curl_setopt 函數。
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 請求:
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 版本。
請注意,在未使用 HTTP/3 支持構建的 Curl 擴展上使用CURL_HTTP_VERSION_3 將導致請求在curl_setopt和curl_exec調用中都返回 false。
以下代碼段使用 CURL_HTTP_VERSION_3ONLY (= 31),它告訴 Curl 使用 HTTP/3 而不使用回退 。如果遠程服務器和 Curl 不支持 HTTP/3,則請求會失敗。
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。
CURL_HTTP_VERSION_3 還是 CURL_VERSION_HTTP3?
CURL_VERSION_HTTP3是一個功能標誌 ,併為其分配了一個值,該值可用於按位運算以確定 HTTP/3 支持 。自 Curl 7.66 和 PHP 8.2 起可用。CURL_HTTP_VERSION_3和CURL_HTTP_VERSION_3ONLY是 Curl 選項CURLOPT_HTTP_VERSION的選項。CURL_HTTP_VERSION_3是在 Curl 7.66 中添加的,並且 ...3 僅在 Curl 7.88 中。CURL_VERSION_HTTP3和CURL_HTTP_VERSION_3並不相同。
檢測 PHP Curl 擴展中的 HTTP/3 支持
聲明 PHP 常量 CURL_VERSION_HTTP3、CURL_HTTP_VERSION_3 和 CURL_HTTP_VERSION_3ONLY 的事實並不意味着 Curl 是使用 HTTP/3 支持構建的。
有三種方法可以檢查 Curl 擴展是否支持 HTTP/3。
檢查 phpinfo 輸出
phpinfo() 輸出和 php -i 顯示 Curl 擴展是否使用 HTTP/3 支持構建:
phpinfo 輸出顯示 HTTP/3 支持
帶 curl_version 功能
curl_version=() 函數 return 數組的 features 值是 Curl 支持的所有功能的位掩碼。
以下代碼片段顯示了使用 curl_version() 函數和功能標誌 CURL_VERSION_HTTP3 檢測 HTTP/3 支持:
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:
$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/3 的 PHP Curl 擴展
HTTP/3 標準本身仍處於 Proposed Standard 狀態,這比成為 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 的多次測試。觀看,使用 ngtcp2、nghttp3 和 WolfSSL 構建 Curl 產生了最佳結果。一些包含修補版本的 OpenSSL 的組合在嘗試執行請求時根本不起作用,並且出現段錯誤。
- 構建依賴項
必須在系統上安裝 C 編譯器、make 工具和其他基本要素等構建工具。
PHP 的 PHP。Watch 包含適用於 Debian/Ubuntu 和 Fedora/RHEL 系統的指南。 -
使用 ngtcp2、nghttp3 和 WolfSSL 構建 Curl
大多數最新的説明都可以在 Curl 文檔中找到 。# 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 擴展
由於上面對 Curl 的make install調用,Curl 二進制文件和 libcurl 是在系統範圍內安裝的。當 PHP 使用 Curl 擴展編譯時,它現在會選擇支持 HTTP/3 的新 libcurl 版本。
Debian/Ubuntu 和 Fedora/RHEL 系統指南中有關編譯 PHP 的詳細指南應列出所有步驟。
確保使用 --with-curl 對 PHP 進行 ./configure 作。如果 Curl 沒有在系統範圍內安裝(即不在 /usr/local 中),也可以在此處指定目錄。
編譯 PHP 的快速指南如下:
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 請求:
$ch = curl_init("https://php.watch/");
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_3);
curl_exec($ch);
- 如果遠程服務器不支持 HTTP/3,它會嘗試使用 HTTP/2 或 HTTP/1.1,具體取決於可用性和支持。
- 如果 Curl 不支持 HTTP/3,則
curl_setopt和curl_exec調用都會失敗。
HTTP/3 請求可以更具防禦性地發出,如下面的代碼片段所示。它會檢查 Curl 是否內置了 HTTP/3 支持,檢查是否聲明瞭 CURL_HTTP_VERSION_3 常量,並允許 Curl 在 HTTP/3 不可用時回退到不同的 HTTP 版本:
$ch = curl_init("https://php.watch/");
if (defined('CURL_VERSION_HTTP3') && defined('CURL_HTTP_VERSION_3') && curl_version()['features'] & CURL_VERSION_HTTP3) {
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_3);
}
curl_exec($ch);