1<!-- 2Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 3 4SPDX-License-Identifier: curl 5--> 6 7# HTTP3 (and QUIC) 8 9## Resources 10 11[HTTP/3 Explained](https://http3-explained.haxx.se/en/) - the online free 12book describing the protocols involved. 13 14[quicwg.org](https://quicwg.org/) - home of the official protocol drafts 15 16## QUIC libraries 17 18QUIC libraries we are using: 19 20[ngtcp2](https://github.com/ngtcp2/ngtcp2) 21 22[quiche](https://github.com/cloudflare/quiche) - **EXPERIMENTAL** 23 24[OpenSSL 3.2+ QUIC](https://github.com/openssl/openssl) - **EXPERIMENTAL** 25 26[msh3](https://github.com/nibanks/msh3) (with [msquic](https://github.com/microsoft/msquic)) - **EXPERIMENTAL** 27 28## Experimental 29 30HTTP/3 support in curl is considered **EXPERIMENTAL** until further notice 31when built to use *quiche* or *msh3*. Only the *ngtcp2* backend is not 32experimental. 33 34Further development and tweaking of the HTTP/3 support in curl happens in the 35master branch using pull-requests, just like ordinary changes. 36 37To fix before we remove the experimental label: 38 39 - the used QUIC library needs to consider itself non-beta 40 - it is fine to "leave" individual backends as experimental if necessary 41 42# ngtcp2 version 43 44Building curl with ngtcp2 involves 3 components: `ngtcp2` itself, `nghttp3` and a QUIC supporting TLS library. The supported TLS libraries are covered below. 45 46 * `ngtcp2`: v1.2.0 47 * `nghttp3`: v1.1.0 48 49## Build with quictls 50 51OpenSSL does not offer the required APIs for building a QUIC client. You need 52to use a TLS library that has such APIs and that works with *ngtcp2*. 53 54Build quictls: 55 56 % git clone --depth 1 -b openssl-3.1.4+quic https://github.com/quictls/openssl 57 % cd openssl 58 % ./config enable-tls1_3 --prefix=<somewhere1> 59 % make 60 % make install 61 62Build nghttp3: 63 64 % cd .. 65 % git clone -b v1.1.0 https://github.com/ngtcp2/nghttp3 66 % cd nghttp3 67 % git submodule update --init 68 % autoreconf -fi 69 % ./configure --prefix=<somewhere2> --enable-lib-only 70 % make 71 % make install 72 73Build ngtcp2: 74 75 % cd .. 76 % git clone -b v1.2.0 https://github.com/ngtcp2/ngtcp2 77 % cd ngtcp2 78 % autoreconf -fi 79 % ./configure PKG_CONFIG_PATH=<somewhere1>/lib/pkgconfig:<somewhere2>/lib/pkgconfig LDFLAGS="-Wl,-rpath,<somewhere1>/lib" --prefix=<somewhere3> --enable-lib-only 80 % make 81 % make install 82 83Build curl: 84 85 % cd .. 86 % git clone https://github.com/curl/curl 87 % cd curl 88 % autoreconf -fi 89 % LDFLAGS="-Wl,-rpath,<somewhere1>/lib" ./configure --with-openssl=<somewhere1> --with-nghttp3=<somewhere2> --with-ngtcp2=<somewhere3> 90 % make 91 % make install 92 93For OpenSSL 3.0.0 or later builds on Linux for x86_64 architecture, substitute all occurrences of "/lib" with "/lib64" 94 95## Build with GnuTLS 96 97Build GnuTLS: 98 99 % git clone --depth 1 https://gitlab.com/gnutls/gnutls.git 100 % cd gnutls 101 % ./bootstrap 102 % ./configure --prefix=<somewhere1> 103 % make 104 % make install 105 106Build nghttp3: 107 108 % cd .. 109 % git clone -b v1.1.0 https://github.com/ngtcp2/nghttp3 110 % cd nghttp3 111 % git submodule update --init 112 % autoreconf -fi 113 % ./configure --prefix=<somewhere2> --enable-lib-only 114 % make 115 % make install 116 117Build ngtcp2: 118 119 % cd .. 120 % git clone -b v1.2.0 https://github.com/ngtcp2/ngtcp2 121 % cd ngtcp2 122 % autoreconf -fi 123 % ./configure PKG_CONFIG_PATH=<somewhere1>/lib/pkgconfig:<somewhere2>/lib/pkgconfig LDFLAGS="-Wl,-rpath,<somewhere1>/lib" --prefix=<somewhere3> --enable-lib-only --with-gnutls 124 % make 125 % make install 126 127Build curl: 128 129 % cd .. 130 % git clone https://github.com/curl/curl 131 % cd curl 132 % autoreconf -fi 133 % ./configure --with-gnutls=<somewhere1> --with-nghttp3=<somewhere2> --with-ngtcp2=<somewhere3> 134 % make 135 % make install 136 137## Build with wolfSSL 138 139Build wolfSSL: 140 141 % git clone https://github.com/wolfSSL/wolfssl.git 142 % cd wolfssl 143 % autoreconf -fi 144 % ./configure --prefix=<somewhere1> --enable-quic --enable-session-ticket --enable-earlydata --enable-psk --enable-harden --enable-altcertchains 145 % make 146 % make install 147 148Build nghttp3: 149 150 % cd .. 151 % git clone -b v1.1.0 https://github.com/ngtcp2/nghttp3 152 % cd nghttp3 153 % git submodule update --init 154 % autoreconf -fi 155 % ./configure --prefix=<somewhere2> --enable-lib-only 156 % make 157 % make install 158 159Build ngtcp2: 160 161 % cd .. 162 % git clone -b v1.2.0 https://github.com/ngtcp2/ngtcp2 163 % cd ngtcp2 164 % autoreconf -fi 165 % ./configure PKG_CONFIG_PATH=<somewhere1>/lib/pkgconfig:<somewhere2>/lib/pkgconfig LDFLAGS="-Wl,-rpath,<somewhere1>/lib" --prefix=<somewhere3> --enable-lib-only --with-wolfssl 166 % make 167 % make install 168 169Build curl: 170 171 % cd .. 172 % git clone https://github.com/curl/curl 173 % cd curl 174 % autoreconf -fi 175 % ./configure --with-wolfssl=<somewhere1> --with-nghttp3=<somewhere2> --with-ngtcp2=<somewhere3> 176 % make 177 % make install 178 179# quiche version 180 181quiche support is **EXPERIMENTAL** 182 183Since the quiche build manages its dependencies, curl can be built against the latest version. You are *probably* able to build against their main branch, but in case of problems, we recommend their latest release tag. 184 185## Build 186 187Build quiche and BoringSSL: 188 189 % git clone --recursive -b 0.22.0 https://github.com/cloudflare/quiche 190 % cd quiche 191 % cargo build --package quiche --release --features ffi,pkg-config-meta,qlog 192 % ln -s libquiche.so target/release/libquiche.so.0 193 % mkdir quiche/deps/boringssl/src/lib 194 % ln -vnf $(find target/release -name libcrypto.a -o -name libssl.a) quiche/deps/boringssl/src/lib/ 195 196Build curl: 197 198 % cd .. 199 % git clone https://github.com/curl/curl 200 % cd curl 201 % autoreconf -fi 202 % ./configure LDFLAGS="-Wl,-rpath,$PWD/../quiche/target/release" --with-openssl=$PWD/../quiche/quiche/deps/boringssl/src --with-quiche=$PWD/../quiche/target/release 203 % make 204 % make install 205 206 If `make install` results in `Permission denied` error, you need to prepend 207 it with `sudo`. 208 209# OpenSSL version 210 211QUIC support is **EXPERIMENTAL** 212 213Build OpenSSL 3.3.1: 214 215 % cd .. 216 % git clone -b openssl-3.3.1 https://github.com/openssl/openssl 217 % cd openssl 218 % ./config enable-tls1_3 --prefix=<somewhere> --libdir=lib 219 % make 220 % make install 221 222Build nghttp3: 223 224 % cd .. 225 % git clone -b v1.1.0 https://github.com/ngtcp2/nghttp3 226 % cd nghttp3 227 % git submodule update --init 228 % autoreconf -fi 229 % ./configure --prefix=<somewhere2> --enable-lib-only 230 % make 231 % make install 232 233Build curl: 234 235 % cd .. 236 % git clone https://github.com/curl/curl 237 % cd curl 238 % autoreconf -fi 239 % LDFLAGS="-Wl,-rpath,<somewhere>/lib" ./configure --with-openssl=<somewhere> --with-openssl-quic --with-nghttp3=<somewhere2> 240 % make 241 % make install 242 243You can build curl with cmake: 244 245 % cd .. 246 % git clone https://github.com/curl/curl 247 % cd curl 248 % cmake . -B bld -DCURL_USE_OPENSSL=ON -DUSE_OPENSSL_QUIC=ON 249 % cmake --build bld 250 % cmake --install bld 251 252 If `make install` results in `Permission denied` error, you need to prepend 253 it with `sudo`. 254 255# msh3 (msquic) version 256 257**Note**: The msquic HTTP/3 backend is immature and is not properly functional 258one as of September 2023. Feel free to help us test it and improve it, but 259there is no point in filing bugs about it just yet. 260 261msh3 support is **EXPERIMENTAL** 262 263## Build Linux (with quictls fork of OpenSSL) 264 265Build msh3: 266 267 % git clone -b v0.6.0 --depth 1 --recursive https://github.com/nibanks/msh3 268 % cd msh3 && mkdir build && cd build 269 % cmake -G 'Unix Makefiles' -DCMAKE_BUILD_TYPE=RelWithDebInfo .. 270 % cmake --build . 271 % cmake --install . 272 273Build curl: 274 275 % git clone https://github.com/curl/curl 276 % cd curl 277 % autoreconf -fi 278 % ./configure LDFLAGS="-Wl,-rpath,/usr/local/lib" --with-msh3=/usr/local --with-openssl 279 % make 280 % make install 281 282Run from `/usr/local/bin/curl`. 283 284## Build Windows 285 286Build msh3: 287 288 % git clone -b v0.6.0 --depth 1 --recursive https://github.com/nibanks/msh3 289 % cd msh3 && mkdir build && cd build 290 % cmake -G 'Visual Studio 17 2022' -DCMAKE_BUILD_TYPE=RelWithDebInfo .. 291 % cmake --build . --config Release 292 % cmake --install . --config Release 293 294**Note** - On Windows, Schannel is used for TLS support by default. If you 295with to use (the quictls fork of) OpenSSL, specify the `-DQUIC_TLS=openssl` 296option to the generate command above. Also note that OpenSSL brings with it an 297additional set of build dependencies not specified here. 298 299Build curl (in [Visual Studio Command 300prompt](../winbuild/README.md#open-a-command-prompt)): 301 302 % git clone https://github.com/curl/curl 303 % cd curl/winbuild 304 % nmake /f Makefile.vc mode=dll WITH_MSH3=dll MSH3_PATH="C:/Program Files/msh3" MACHINE=x64 305 306**Note** - If you encounter a build error with `tool_hugehelp.c` being 307missing, rename `tool_hugehelp.c.cvs` in the same directory to 308`tool_hugehelp.c` and then run `nmake` again. 309 310Run in the `C:/Program Files/msh3/lib` directory, copy `curl.exe` to that 311directory, or copy `msquic.dll` and `msh3.dll` from that directory to the 312`curl.exe` directory. For example: 313 314 % C:\Program Files\msh3\lib> F:\curl\builds\libcurl-vc-x64-release-dll-ipv6-sspi-schannel-msh3\bin\curl.exe --http3 https://curl.se/ 315 316# `--http3` 317 318Use only HTTP/3: 319 320 % curl --http3-only https://example.org:4433/ 321 322Use HTTP/3 with fallback to HTTP/2 or HTTP/1.1 (see "HTTPS eyeballing" below): 323 324 % curl --http3 https://example.org:4433/ 325 326Upgrade via Alt-Svc: 327 328 % curl --alt-svc altsvc.cache https://curl.se/ 329 330See this [list of public HTTP/3 servers](https://bagder.github.io/HTTP3-test/) 331 332### HTTPS eyeballing 333 334With option `--http3` curl attempts earlier HTTP versions as well should the 335connect attempt via HTTP/3 not succeed "fast enough". This strategy is similar 336to IPv4/6 happy eyeballing where the alternate address family is used in 337parallel after a short delay. 338 339The IPv4/6 eyeballing has a default of 200ms and you may override that via 340`--happy-eyeballs-timeout-ms value`. Since HTTP/3 is still relatively new, we 341decided to use this timeout also for the HTTP eyeballing - with a slight 342twist. 343 344The `happy-eyeballs-timeout-ms` value is the **hard** timeout, meaning after 345that time expired, a TLS connection is opened in addition to negotiate HTTP/2 346or HTTP/1.1. At half of that value - currently - is the **soft** timeout. The 347soft timeout fires, when there has been **no data at all** seen from the 348server on the HTTP/3 connection. 349 350So, without you specifying anything, the hard timeout is 200ms and the soft is 100ms: 351 352 * Ideally, the whole QUIC handshake happens and curl has an HTTP/3 connection 353 in less than 100ms. 354 * When QUIC is not supported (or UDP does not work for this network path), no 355 reply is seen and the HTTP/2 TLS+TCP connection starts 100ms later. 356 * In the worst case, UDP replies start before 100ms, but drag on. This starts 357 the TLS+TCP connection after 200ms. 358 * When the QUIC handshake fails, the TLS+TCP connection is attempted right 359 away. For example, when the QUIC server presents the wrong certificate. 360 361The whole transfer only fails, when **both** QUIC and TLS+TCP fail to 362handshake or time out. 363 364Note that all this happens in addition to IP version happy eyeballing. If the 365name resolution for the server gives more than one IP address, curl tries all 366those until one succeeds - just as with all other protocols. If those IP 367addresses contain both IPv6 and IPv4, those attempts happen, delayed, in 368parallel (the actual eyeballing). 369 370## Known Bugs 371 372Check out the [list of known HTTP3 bugs](https://curl.se/docs/knownbugs.html#HTTP3). 373 374# HTTP/3 Test server 375 376This is not advice on how to run anything in production. This is for 377development and experimenting. 378 379## Prerequisite(s) 380 381An existing local HTTP/1.1 server that hosts files. Preferably also a few huge 382ones. You can easily create huge local files like `truncate -s=8G 8GB` - they 383are huge but do not occupy that much space on disk since they are just big 384holes. 385 386In a Debian setup you can install apache2. It runs on port 80 and has a 387document root in `/var/www/html`. Download the 8GB file from apache with `curl 388localhost/8GB -o dev/null` 389 390In this description we setup and run an HTTP/3 reverse-proxy in front of the 391HTTP/1 server. 392 393## Setup 394 395You can select either or both of these server solutions. 396 397### nghttpx 398 399Get, build and install quictls, nghttp3 and ngtcp2 as described 400above. 401 402Get, build and install nghttp2: 403 404 % git clone https://github.com/nghttp2/nghttp2.git 405 % cd nghttp2 406 % autoreconf -fi 407 % PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/home/daniel/build-quictls/lib/pkgconfig:/home/daniel/build-nghttp3/lib/pkgconfig:/home/daniel/build-ngtcp2/lib/pkgconfig LDFLAGS=-L/home/daniel/build-quictls/lib CFLAGS=-I/home/daniel/build-quictls/include ./configure --enable-maintainer-mode --prefix=/home/daniel/build-nghttp2 --disable-shared --enable-app --enable-http3 --without-jemalloc --without-libxml2 --without-systemd 408 % make && make install 409 410Run the local h3 server on port 9443, make it proxy all traffic through to 411HTTP/1 on localhost port 80. For local toying, we can just use the test cert 412that exists in curl's test dir. 413 414 % CERT=$CURLSRC/tests/stunnel.pem 415 % $HOME/bin/nghttpx $CERT $CERT --backend=localhost,80 \ 416 --frontend="localhost,9443;quic" 417 418### Caddy 419 420[Install Caddy](https://caddyserver.com/docs/install). For easiest use, the binary 421should be either in your PATH or your current directory. 422 423Create a `Caddyfile` with the following content: 424~~~ 425localhost:7443 { 426 respond "Hello, world! you are using {http.request.proto}" 427} 428~~~ 429 430Then run Caddy: 431 432 % ./caddy start 433 434Making requests to `https://localhost:7443` should tell you which protocol is being used. 435 436You can change the hard-coded response to something more useful by replacing `respond` 437with `reverse_proxy` or `file_server`, for example: `reverse_proxy localhost:80` 438