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