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