xref: /curl/docs/HTTP3.md (revision 86d33001)
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