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