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