Merge pull request #743 from crazywhalecc/feat/h3

Feature: add http/3 support to curl.
This commit is contained in:
Marc 2025-06-06 13:53:25 +07:00 committed by GitHub
commit f75ab9f428
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 310 additions and 4 deletions

View File

@ -86,6 +86,8 @@
"libssh2",
"brotli",
"nghttp2",
"nghttp3",
"ngtcp2",
"zstd",
"libcares"
],
@ -615,7 +617,44 @@
"openssl"
],
"lib-suggests": [
"libxml2"
"libxml2",
"nghttp3",
"ngtcp2"
]
},
"nghttp3": {
"source": "nghttp3",
"static-libs-unix": [
"libnghttp3.a"
],
"static-libs-windows": [
"nghttp3.lib"
],
"headers": [
"nghttp3"
],
"lib-depends": [
"openssl"
],
"lib-suggests": [
"ngtcp2"
]
},
"ngtcp2": {
"source": "ngtcp2",
"static-libs-unix": [
"libngtcp2.a",
"libngtcp2_crypto_ossl.a"
],
"static-libs-windows": [
"ngtcp2.lib",
"ngtcp2_crypto_ossl.lib"
],
"headers": [
"ngtcp2"
],
"lib-depends": [
"openssl"
]
},
"onig": {

View File

@ -714,6 +714,26 @@
"path": "COPYING"
}
},
"nghttp3": {
"type": "ghrel",
"repo": "ngtcp2/nghttp3",
"match": "nghttp3.+\\.tar\\.xz",
"prefer-stable": true,
"license": {
"type": "file",
"path": "COPYING"
}
},
"ngtcp2": {
"type": "ghrel",
"repo": "ngtcp2/ngtcp2",
"match": "ngtcp2.+\\.tar\\.xz",
"prefer-stable": true,
"license": {
"type": "file",
"path": "COPYING"
}
},
"onig": {
"type": "ghrel",
"repo": "kkos/oniguruma",

View File

@ -6,6 +6,8 @@ which will be listed one by one here.
## curl
HTTP3 support is not enabled by default, compile with `--with-libs="nghttp2,nghttp3,ngtcp2"` to enable HTTP3 support for PHP >= 8.4.
When using curl to request HTTPS, there may be an `error:80000002:system library::No such file or directory` error.
For details on the solution, see [FAQ - Unable to use ssl](../faq/#unable-to-use-ssl).

View File

@ -4,6 +4,8 @@
## curl
HTTP3 支持默认未启用,需在编译时添加 `--with-libs="nghttp2,nghttp3,ngtcp2"` 以启用 PHP 8.4 及以上版本的 HTTP3 支持。
使用 curl 请求 HTTPS 时,可能存在 `error:80000002:system library::No such file or directory` 错误,
解决办法详见 [FAQ - 无法使用 ssl](../faq/#无法使用-ssl)。

View File

@ -80,6 +80,9 @@ class LinuxBuilder extends UnixBuilderBase
$ret = '';
foreach ($libSpecs as $libName => $arr) {
$lib = $this->getLib($libName);
if ($lib === null && str_starts_with($libName, 'lib')) {
$lib = $this->getExt(substr($libName, 3));
}
$arr = $arr ?? [];

View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace SPC\builder\linux\library;
class nghttp3 extends LinuxLibraryBase
{
use \SPC\builder\unix\library\nghttp3;
public const NAME = 'nghttp3';
}

View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace SPC\builder\linux\library;
class ngtcp2 extends LinuxLibraryBase
{
use \SPC\builder\unix\library\ngtcp2;
public const NAME = 'ngtcp2';
}

View File

@ -55,6 +55,9 @@ class MacOSBuilder extends UnixBuilderBase
$ret = '';
foreach ($lib_specs as $libName => $arr) {
$lib = $this->getLib($libName);
if ($lib === null && str_starts_with($libName, 'lib')) {
$lib = $this->getExt(substr($libName, 3));
}
$arr = $arr ?? [];

View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace SPC\builder\macos\library;
class nghttp3 extends MacOSLibraryBase
{
use \SPC\builder\unix\library\nghttp3;
public const NAME = 'nghttp3';
}

View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace SPC\builder\macos\library;
class ngtcp2 extends MacOSLibraryBase
{
use \SPC\builder\unix\library\ngtcp2;
public const NAME = 'ngtcp2';
}

View File

@ -47,6 +47,24 @@ trait curl
} else {
$extra .= '-DUSE_NGHTTP2=OFF ';
}
// lib:nghttp3
if ($nghttp3 = $this->builder->getLib('nghttp3')) {
$extra .= '-DUSE_NGHTTP3=ON ' .
/* @phpstan-ignore-next-line */
'-DNGHTTP3_LIBRARY="' . $nghttp3->getStaticLibFiles(style: 'cmake') . '" ' .
'-DNGHTTP3_INCLUDE_DIR="' . BUILD_INCLUDE_PATH . '" ';
} else {
$extra .= '-DUSE_NGHTTP3=OFF ';
}
// lib:ngtcp2
if ($ngtcp2 = $this->builder->getLib('ngtcp2')) {
$extra .= '-DUSE_NGTCP2=ON ' .
/* @phpstan-ignore-next-line */
'-DNGTCP2_LIBRARY="' . $ngtcp2->getStaticLibFiles(style: 'cmake') . '" ' .
'-DNGTCP2_INCLUDE_DIR="' . BUILD_INCLUDE_PATH . '" ';
} else {
$extra .= '-DUSE_NGTCP2=OFF ';
}
// lib:ldap
$extra .= $this->builder->getLib('ldap') ? '-DCURL_DISABLE_LDAP=OFF ' : '-DCURL_DISABLE_LDAP=ON ';
// lib:zstd

View File

@ -30,7 +30,6 @@ trait nghttp2
'jansson' => null,
'jemalloc' => null,
'systemd' => null,
'cunit' => null,
]);
[,,$destdir] = SEPARATED_PATH;
@ -41,8 +40,8 @@ trait nghttp2
'./configure ' .
'--enable-static ' .
'--disable-shared ' .
'--with-pic ' .
'--enable-lib-only ' .
'--with-boost=no ' .
$args . ' ' .
'--prefix='
)

View File

@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
trait nghttp3
{
/**
* @throws FileSystemException
* @throws RuntimeException
* @throws WrongUsageException
*/
protected function build(): void
{
$args = $this->builder->makeAutoconfArgs(static::NAME, [
'zlib' => null,
'openssl' => null,
]);
shell()->cd($this->source_dir)
->setEnv([
'CFLAGS' => $this->getLibExtraCFlags(),
'LDFLAGS' => $this->getLibExtraLdFlags(),
'LIBS' => $this->getLibExtraLibs(),
])
->execWithEnv(
'./configure ' .
'--enable-static ' .
'--disable-shared ' .
'--with-pic ' .
'--enable-lib-only ' .
$args . ' ' .
'--prefix='
)
->execWithEnv('make clean')
->execWithEnv("make -j{$this->builder->concurrency}")
->execWithEnv('make install DESTDIR=' . BUILD_ROOT_PATH);
$this->patchPkgconfPrefix(['libnghttp3.pc']);
}
}

View File

@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
trait ngtcp2
{
/**
* @throws FileSystemException
* @throws RuntimeException
* @throws WrongUsageException
*/
protected function build(): void
{
$args = $this->builder->makeAutoconfArgs(static::NAME, [
'zlib' => null,
'openssl' => null,
'libxml2' => null,
'libev' => null,
'jemalloc' => null,
]);
shell()->cd($this->source_dir)
->setEnv([
'CFLAGS' => $this->getLibExtraCFlags(),
'LDFLAGS' => $this->getLibExtraLdFlags(),
'LIBS' => $this->getLibExtraLibs(),
])
->execWithEnv(
'./configure ' .
'--enable-static ' .
'--disable-shared ' .
'--with-pic ' .
'--enable-lib-only ' .
$args . ' ' .
'--prefix='
)
->execWithEnv('make clean')
->execWithEnv("make -j{$this->builder->concurrency}")
->execWithEnv('make install DESTDIR=' . BUILD_ROOT_PATH);
$this->patchPkgconfPrefix(['libngtcp2.pc']);
$this->patchPkgconfPrefix(['libngtcp2_crypto_ossl.pc']);
}
}

View File

@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace SPC\builder\windows\library;
use SPC\store\FileSystem;
class nghttp3 extends WindowsLibraryBase
{
public const NAME = 'nghttp3';
protected function build(): void
{
// reset cmake
FileSystem::resetDir($this->source_dir . '\build');
// start build
cmd()->cd($this->source_dir)
->execWithWrapper(
$this->builder->makeSimpleWrapper('cmake'),
'-B build ' .
'-A x64 ' .
"-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " .
'-DCMAKE_BUILD_TYPE=Release ' .
'-DENABLE_SHARED_LIB=OFF ' .
'-DENABLE_STATIC_LIB=ON ' .
'-DBUILD_STATIC_LIBS=ON ' .
'-DBUILD_SHARED_LIBS=OFF ' .
'-DENABLE_STATIC_CRT=ON ' .
'-DENABLE_LIB_ONLY=ON ' .
'-DCMAKE_INSTALL_PREFIX=' . BUILD_ROOT_PATH . ' '
)
->execWithWrapper(
$this->builder->makeSimpleWrapper('cmake'),
"--build build --config Release --target install -j{$this->builder->concurrency}"
);
}
}

View File

@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace SPC\builder\windows\library;
use SPC\store\FileSystem;
class ngtcp2 extends WindowsLibraryBase
{
public const NAME = 'ngtcp2';
protected function build(): void
{
// reset cmake
FileSystem::resetDir($this->source_dir . '\build');
// start build
cmd()->cd($this->source_dir)
->execWithWrapper(
$this->builder->makeSimpleWrapper('cmake'),
'-B build ' .
'-A x64 ' .
"-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " .
'-DCMAKE_BUILD_TYPE=Release ' .
'-DENABLE_SHARED_LIB=OFF ' .
'-DENABLE_STATIC_LIB=ON ' .
'-DBUILD_STATIC_LIBS=ON ' .
'-DBUILD_SHARED_LIBS=OFF ' .
'-DENABLE_STATIC_CRT=ON ' .
'-DENABLE_LIB_ONLY=ON ' .
'-DCMAKE_INSTALL_PREFIX=' . BUILD_ROOT_PATH . ' '
)
->execWithWrapper(
$this->builder->makeSimpleWrapper('cmake'),
"--build build --config Release --target install -j{$this->builder->concurrency}"
);
}
}