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