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