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.20.0 https://github.com/cloudflare/quiche 190 % cd quiche 191 % cargo build --package quiche --release --features ffi,pkg-config-meta,qlog 192 % mkdir quiche/deps/boringssl/src/lib 193 % ln -vnf $(find target/release -name libcrypto.a -o -name libssl.a) quiche/deps/boringssl/src/lib/ 194 195Build curl: 196 197 % cd .. 198 % git clone https://github.com/curl/curl 199 % cd curl 200 % autoreconf -fi 201 % ./configure LDFLAGS="-Wl,-rpath,$PWD/../quiche/target/release" --with-openssl=$PWD/../quiche/quiche/deps/boringssl/src --with-quiche=$PWD/../quiche/target/release 202 % make 203 % make install 204 205 If `make install` results in `Permission denied` error, you need to prepend 206 it with `sudo`. 207 208# OpenSSL version 209 210QUIC support is **EXPERIMENTAL** 211 212Build OpenSSL 3.2.0 213 214 % cd .. 215 % git clone -b openssl-3.2.0 https://github.com/openssl/openssl 216 % cd openssl 217 % ./config enable-tls1_3 --prefix=<somewhere> --libdir=<somewhere>/lib 218 % make 219 % make install 220 221Build nghttp3 222 223 % cd .. 224 % git clone -b v1.1.0 https://github.com/ngtcp2/nghttp3 225 % cd nghttp3 226 % git submodule update --init 227 % autoreconf -fi 228 % ./configure --prefix=<somewhere2> --enable-lib-only 229 % make 230 % make install 231 232Build curl: 233 234 % cd .. 235 % git clone https://github.com/curl/curl 236 % cd curl 237 % autoreconf -fi 238 % LDFLAGS="-Wl,-rpath,<somewhere>/lib" ./configure --with-openssl=<somewhere> --with-openssl-quic --with-nghttp3=<somewhere2> 239 % make 240 % make install 241 242You can build curl with cmake: 243 244 % cd .. 245 % git clone https://github.com/curl/curl 246 % cd curl 247 % cmake . -B build -DCURL_USE_OPENSSL=ON -DUSE_OPENSSL_QUIC=ON 248 % cmake --build build 249 % cmake --install build 250 251 If `make install` results in `Permission denied` error, you need to prepend 252 it with `sudo`. 253 254# msh3 (msquic) version 255 256**Note**: The msquic HTTP/3 backend is immature and is not properly functional 257one as of September 2023. Feel free to help us test it and improve it, but 258there is no point in filing bugs about it just yet. 259 260msh3 support is **EXPERIMENTAL** 261 262## Build Linux (with quictls fork of OpenSSL) 263 264Build msh3: 265 266 % git clone -b v0.6.0 --depth 1 --recursive https://github.com/nibanks/msh3 267 % cd msh3 && mkdir build && cd build 268 % cmake -G 'Unix Makefiles' -DCMAKE_BUILD_TYPE=RelWithDebInfo .. 269 % cmake --build . 270 % cmake --install . 271 272Build curl: 273 274 % git clone https://github.com/curl/curl 275 % cd curl 276 % autoreconf -fi 277 % ./configure LDFLAGS="-Wl,-rpath,/usr/local/lib" --with-msh3=/usr/local --with-openssl 278 % make 279 % make install 280 281Run from `/usr/local/bin/curl`. 282 283## Build Windows 284 285Build msh3: 286 287 % git clone -b v0.6.0 --depth 1 --recursive https://github.com/nibanks/msh3 288 % cd msh3 && mkdir build && cd build 289 % cmake -G 'Visual Studio 17 2022' -DCMAKE_BUILD_TYPE=RelWithDebInfo .. 290 % cmake --build . --config Release 291 % cmake --install . --config Release 292 293**Note** - On Windows, Schannel is used for TLS support by default. If you 294with to use (the quictls fork of) OpenSSL, specify the `-DQUIC_TLS=openssl` 295option to the generate command above. Also note that OpenSSL brings with it an 296additional set of build dependencies not specified here. 297 298Build curl (in [Visual Studio Command 299prompt](../winbuild/README.md#open-a-command-prompt)): 300 301 % git clone https://github.com/curl/curl 302 % cd curl/winbuild 303 % nmake /f Makefile.vc mode=dll WITH_MSH3=dll MSH3_PATH="C:/Program Files/msh3" MACHINE=x64 304 305**Note** - If you encounter a build error with `tool_hugehelp.c` being 306missing, rename `tool_hugehelp.c.cvs` in the same directory to 307`tool_hugehelp.c` and then run `nmake` again. 308 309Run in the `C:/Program Files/msh3/lib` directory, copy `curl.exe` to that 310directory, or copy `msquic.dll` and `msh3.dll` from that directory to the 311`curl.exe` directory. For example: 312 313 % C:\Program Files\msh3\lib> F:\curl\builds\libcurl-vc-x64-release-dll-ipv6-sspi-schannel-msh3\bin\curl.exe --http3 https://curl.se/ 314 315# `--http3` 316 317Use only HTTP/3: 318 319 curl --http3-only https://example.org:4433/ 320 321Use HTTP/3 with fallback to HTTP/2 or HTTP/1.1 (see "HTTPS eyeballing" below): 322 323 curl --http3 https://example.org:4433/ 324 325Upgrade via Alt-Svc: 326 327 curl --alt-svc altsvc.cache https://curl.se/ 328 329See this [list of public HTTP/3 servers](https://bagder.github.io/HTTP3-test/) 330 331### HTTPS eyeballing 332 333With option `--http3` curl attempts earlier HTTP versions as well should the 334connect attempt via HTTP/3 not succeed "fast enough". This strategy is similar 335to IPv4/6 happy eyeballing where the alternate address family is used in 336parallel after a short delay. 337 338The IPv4/6 eyeballing has a default of 200ms and you may override that via 339`--happy-eyeballs-timeout-ms value`. Since HTTP/3 is still relatively new, we 340decided to use this timeout also for the HTTP eyeballing - with a slight 341twist. 342 343The `happy-eyeballs-timeout-ms` value is the **hard** timeout, meaning after 344that time expired, a TLS connection is opened in addition to negotiate HTTP/2 345or HTTP/1.1. At half of that value - currently - is the **soft** timeout. The 346soft timeout fires, when there has been **no data at all** seen from the 347server on the HTTP/3 connection. 348 349So, without you specifying anything, the hard timeout is 200ms and the soft is 100ms: 350 351 * Ideally, the whole QUIC handshake happens and curl has an HTTP/3 connection 352 in less than 100ms. 353 * When QUIC is not supported (or UDP does not work for this network path), no 354 reply is seen and the HTTP/2 TLS+TCP connection starts 100ms later. 355 * In the worst case, UDP replies start before 100ms, but drag on. This starts 356 the TLS+TCP connection after 200ms. 357 * When the QUIC handshake fails, the TLS+TCP connection is attempted right 358 away. For example, when the QUIC server presents the wrong certificate. 359 360The whole transfer only fails, when **both** QUIC and TLS+TCP fail to 361handshake or time out. 362 363Note that all this happens in addition to IP version happy eyeballing. If the 364name resolution for the server gives more than one IP address, curl tries all 365those until one succeeds - just as with all other protocols. If those IP 366addresses contain both IPv6 and IPv4, those attempts happen, delayed, in 367parallel (the actual eyeballing). 368 369## Known Bugs 370 371Check out the [list of known HTTP3 bugs](https://curl.se/docs/knownbugs.html#HTTP3). 372 373# HTTP/3 Test server 374 375This is not advice on how to run anything in production. This is for 376development and experimenting. 377 378## Prerequisite(s) 379 380An existing local HTTP/1.1 server that hosts files. Preferably also a few huge 381ones. You can easily create huge local files like `truncate -s=8G 8GB` - they 382are huge but do not occupy that much space on disk since they are just big 383holes. 384 385In a Debian setup you can install **apache2**. It runs on port 80 and has a 386document root in `/var/www/html`. Download the 8GB file from apache with `curl 387localhost/8GB -o dev/null` 388 389In this description we setup and run an HTTP/3 reverse-proxy in front of the 390HTTP/1 server. 391 392## Setup 393 394You can select either or both of these server solutions. 395 396### nghttpx 397 398Get, build and install **quictls**, **nghttp3** and **ngtcp2** as described 399above. 400 401Get, build and install **nghttp2**: 402 403 git clone https://github.com/nghttp2/nghttp2.git 404 cd nghttp2 405 autoreconf -fi 406 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 407 make && make install 408 409Run the local h3 server on port 9443, make it proxy all traffic through to 410HTTP/1 on localhost port 80. For local toying, we can just use the test cert 411that exists in curl's test dir. 412 413 CERT=$CURLSRC/tests/stunnel.pem 414 $HOME/bin/nghttpx $CERT $CERT --backend=localhost,80 \ 415 --frontend="localhost,9443;quic" 416 417### Caddy 418 419[Install Caddy](https://caddyserver.com/docs/install). For easiest use, the binary 420should be either in your PATH or your current directory. 421 422Create a `Caddyfile` with the following content: 423~~~ 424localhost:7443 { 425 respond "Hello, world! you are using {http.request.proto}" 426} 427~~~ 428 429Then run Caddy: 430 431 ./caddy start 432 433Making requests to `https://localhost:7443` should tell you which protocol is being used. 434 435You can change the hard-coded response to something more useful by replacing `respond` 436with `reverse_proxy` or `file_server`, for example: `reverse_proxy localhost:80` 437