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