Compare commits

...

385 Commits

Author SHA1 Message Date
Tatsuhiro Tsujikawa
254f2b3c42 Remove macos + gcc ci build to speed up ci build time 2022-02-08 19:14:45 +09:00
Tatsuhiro Tsujikawa
633691e164 Remove .travis.yml 2022-02-08 19:13:50 +09:00
Tatsuhiro Tsujikawa
01bcc72f66 nghttpx: Handle EAGAIN/EWOULDBLOCK from sendmsg 2022-02-03 22:20:58 +09:00
Tatsuhiro Tsujikawa
7ca255ff54 Fix docker build error 2022-02-02 22:55:19 +09:00
Tatsuhiro Tsujikawa
6430c98e86 Compile with the latest ngtcp2 2022-02-02 19:19:00 +09:00
Tatsuhiro Tsujikawa
32c2557bb7 Bump nghttp3 2022-01-26 22:58:50 +09:00
Tatsuhiro Tsujikawa
3122a83900 h2load: Set quic error code based on error from ngtcp2_conn_read_pkt 2022-01-23 23:47:40 +09:00
Tatsuhiro Tsujikawa
75272a817e h2load: Fix ngtcp2 callback error code 2022-01-23 23:47:29 +09:00
Tatsuhiro Tsujikawa
809d5af43e nghttpx: Fix invalid error code 2022-01-23 22:30:46 +09:00
Tatsuhiro Tsujikawa
3b549caf90 clang-format 2022-01-23 22:30:46 +09:00
Tatsuhiro Tsujikawa
42b659354d nghttpx: Support h3 trailer fields 2022-01-23 22:30:46 +09:00
Tatsuhiro Tsujikawa
2275327794 nghttpx: Fix the issue that forwarded h3 GET request always has chunked TE 2022-01-23 21:36:35 +09:00
Tatsuhiro Tsujikawa
02a5649343 Require ngtcp2 >= 0.1.0 and nghttp3 >= 0.1.0 2022-01-23 18:34:59 +09:00
Tatsuhiro Tsujikawa
3b0b9a458c Build docker image with ngtcp2 v0.1.0 2022-01-22 18:17:30 +09:00
Tatsuhiro Tsujikawa
6e6388e7c2 Depend on nghttp3 v0.1.1 2022-01-22 18:17:09 +09:00
Tatsuhiro Tsujikawa
ea6f0c641d Merge pull request #1643 from nwtgck/fix-example-quotes
fix quotes in --altsvc example
2022-01-19 19:01:53 +09:00
Tatsuhiro Tsujikawa
c883b18f2d Pin quic dependency 2022-01-16 22:00:27 +09:00
Tatsuhiro Tsujikawa
97e69f7416 Merge branch 'rex4539-typos' 2022-01-16 21:54:15 +09:00
Dimitris Apostolou
ad0c9eebf7 Fix typos 2022-01-16 21:53:44 +09:00
Tatsuhiro Tsujikawa
8a552631b4 Merge pull request #1667 from nghttp2/keep-hd-table-size
Fix decoder table size update
2022-01-11 20:53:08 +09:00
Tatsuhiro Tsujikawa
cff8106908 Merge pull request #1665 from c0bw3b/cleanup/spdy
Remove SPDY option for CMake
2022-01-11 20:14:37 +09:00
Tatsuhiro Tsujikawa
4eb49ac28e Merge pull request #1666 from Kludex/patch-1
Update nghttp2.pyx
2022-01-11 20:13:43 +09:00
Tatsuhiro Tsujikawa
deb390cf85 Fix decoder table size update
When applying new header table size acknowledged with SETTINGS ACK by
an encoder, change the header table size on a decoder only when it
strictly lowers the current maximum table size set by Dynamic Table
Size Update from the encoder or the default size 4096 if no Dynamic
Table Size Update is received.

Previously, the header table size on a decoder is always changed.  If
a maximum size in SETTINGS are increased (e.g., 4096 -> 8192), and
then decreased to the previous value, the decoder incorrectly requires
Dynamic Table Size Update from an encoder.
2022-01-11 19:50:45 +09:00
Tatsuhiro Tsujikawa
d91ae6987d Compile with the latest ngtcp2 2022-01-11 19:40:26 +09:00
Marcelo Trylesinski
8ddb2273b9 Update nghttp2.pyx 2022-01-01 19:18:14 +01:00
Renaud
e1446fd57a Remove SPDY option for CMake
SPDY feature removed in #1091 and release v1.29.0
2022-01-01 15:21:59 +01:00
Tatsuhiro Tsujikawa
02e6cad121 Bump quictls/openssl versions 2021-12-19 20:44:45 +09:00
Tatsuhiro Tsujikawa
0b053e06d8 Merge pull request #1657 from nwtgck/patch-2
Make Docker speak HTTP/3
2021-12-13 18:00:18 +09:00
Ryo Ota
c3c0403dfa Make Docker speak HTTP/3 2021-12-12 23:16:10 +09:00
Ryo Ota
abc15c696d fix quotes in --altsvc example 2021-11-18 09:12:05 +09:00
Tatsuhiro Tsujikawa
344d300cf9 Fix cmake CI build 2021-11-16 22:09:48 +09:00
Tatsuhiro Tsujikawa
dec233b9ef cmake: Disable libbpf build by default 2021-11-16 22:03:30 +09:00
Tatsuhiro Tsujikawa
f695dc999b Merge branch 'robaho-robaho/issue1639' 2021-11-14 16:43:23 +09:00
robaho
f92f81c05a allow setting max frame size for h2load 2021-11-14 16:43:01 +09:00
Tatsuhiro Tsujikawa
3c4449c046 nghttpx: Use nghttp3 error code 2021-11-10 19:18:48 +09:00
Tatsuhiro Tsujikawa
918e4ea46b Compile with the latest ngtcp2 2021-11-09 15:44:06 +09:00
Tatsuhiro Tsujikawa
d14d97ab68 Rename send_stop_sending to stop_sending 2021-11-09 11:13:23 +09:00
Tatsuhiro Tsujikawa
2aed077761 Merge fd_set_recv_ecn to create_quic_server_socket and handle errors 2021-11-06 17:19:02 +09:00
Tatsuhiro Tsujikawa
68b2295f4e nghttpx: Set IP_PMTUDISC_DO explicitly 2021-11-06 17:12:43 +09:00
Tatsuhiro Tsujikawa
528d177847 nghttpx: Fix compile error 2021-11-06 00:16:53 +09:00
Tatsuhiro Tsujikawa
deae6c95b1 nghttpx: Send ECN 2021-11-05 23:23:00 +09:00
Tatsuhiro Tsujikawa
7eb179069d Remove useless assignments 2021-11-05 22:51:36 +09:00
Tatsuhiro Tsujikawa
47c33b8d03 nghttpx: Receive ECN 2021-11-05 20:57:24 +09:00
Tatsuhiro Tsujikawa
94372fbe2a Merge branch 'bagder-version-h3-too' 2021-11-05 18:39:09 +09:00
Daniel Stenberg
9e154297ff shrpx: make nghttpx -v show ngtcp2 and nghttp3 version as well
... if HTTP/3 support is built in.
2021-11-05 18:38:39 +09:00
Tatsuhiro Tsujikawa
c4828dbd7c h2load: Fix assertion failure with the latest ngtcp2 2021-11-03 21:23:26 +09:00
Tatsuhiro Tsujikawa
0d16db2c65 Use quictls 1.1.1l for now because 3.0.0 is much slower 2021-10-30 18:09:18 +09:00
Tatsuhiro Tsujikawa
fb63ef305d Update doc 2021-10-30 18:09:06 +09:00
Tatsuhiro Tsujikawa
089fc81d72 Change qlog file extension to .sqlog
Change qlog file extension to .sqlog because upstream ngtcp2 moves to
JSON-SEQ streaming qlog format.
2021-10-29 21:59:54 +09:00
Tatsuhiro Tsujikawa
c20d175ff2 Guard RLIMIT_MEMLOCK usage 2021-10-29 21:39:17 +09:00
Tatsuhiro Tsujikawa
3985957c4d Merge pull request #1625 from codebytere/fix-unreachable-return
chore: fix `-Wunreachable-code-return`
2021-10-29 21:35:44 +09:00
Tatsuhiro Tsujikawa
845a20b582 Bump version number to 1.47.0-DEV 2021-10-19 18:59:58 +09:00
Tatsuhiro Tsujikawa
7af0c508be Update manual pages 2021-10-19 18:29:19 +09:00
Tatsuhiro Tsujikawa
f8474b25f0 nghttpx: Reduce dgram size if sendmsg fails with EINVAL or EMSGSIZE 2021-10-18 22:37:16 +09:00
Tatsuhiro Tsujikawa
15a8d913ea examples/client: Enable ALPN 2021-10-17 17:25:18 +09:00
Tatsuhiro Tsujikawa
65d3c9047f Replace TLSv23_method with TLS_method 2021-10-17 17:21:09 +09:00
Tatsuhiro Tsujikawa
8c36971ea9 Compile with OPENSSL_NO_DEPRECATED and fix memory leaks 2021-10-17 17:12:14 +09:00
Tatsuhiro Tsujikawa
ba1dff187b Update bash_completion 2021-10-17 14:52:55 +09:00
Tatsuhiro Tsujikawa
8ecacc8ed2 Update manual pages 2021-10-17 14:52:36 +09:00
Tatsuhiro Tsujikawa
9d41896663 nghttpx: Rename --frontend-quic-server-id to --quic-server-id 2021-10-17 14:48:46 +09:00
Tatsuhiro Tsujikawa
18d4a9e4ff Update bash_completion 2021-10-17 14:30:27 +09:00
Tatsuhiro Tsujikawa
1745a30644 Update manual pages 2021-10-17 14:30:27 +09:00
Tatsuhiro Tsujikawa
0cc7c598ff src: TLS_DEFAULT_CIPHERSUITES was deprecated in OpenSSL 3.0.0 2021-10-17 14:30:27 +09:00
Tatsuhiro Tsujikawa
8c4fbb86d8 Bump version number to 1.46.0, LT revision to 35:1:21 2021-10-17 14:00:37 +09:00
Tatsuhiro Tsujikawa
693431312c Fix cmake build 2021-10-15 23:39:44 +09:00
Tatsuhiro Tsujikawa
f3fca2a19a Update doc 2021-10-15 23:24:23 +09:00
Tatsuhiro Tsujikawa
1ce9efc644 nghttpx: Set SCT data when built with boringssl 2021-10-15 23:06:24 +09:00
Tatsuhiro Tsujikawa
7055501efd src: Enable HTTP/3 with boringssl 2021-10-15 22:50:34 +09:00
Tatsuhiro Tsujikawa
c790ee64a4 src: Prefer #ifdef for a single condition 2021-10-15 22:50:26 +09:00
Tatsuhiro Tsujikawa
9fb05d5ea2 Fix compile error under mac os 2021-10-15 19:54:08 +09:00
Tatsuhiro Tsujikawa
3742acaf39 nghttpx: Fix wrong SSL_CTX object usage 2021-10-15 18:19:07 +09:00
Tatsuhiro Tsujikawa
d8282de229 nghttpx: Respect !tls-no-postpone-early-data with boringssl 2021-10-15 00:13:49 +09:00
Tatsuhiro Tsujikawa
3a721a9dd5 nghttpx: Send session ticket after handshake with boringssl 2021-10-15 00:13:49 +09:00
Tatsuhiro Tsujikawa
0b6092446b src: Compile with boringssl for non-http3 build 2021-10-14 23:44:27 +09:00
Shelley Vohr
59a76c6d39 chore: fix -Wunreachable-code-return 2021-10-11 12:56:23 +02:00
Tatsuhiro Tsujikawa
fa7a916ef3 nghttpx: Store initial_rtt in ev_tstamp for consistency 2021-10-10 17:34:58 +09:00
Tatsuhiro Tsujikawa
69c4187100 nghttpx: Add --frontend-quic-initial-rtt option 2021-10-10 17:24:02 +09:00
Tatsuhiro Tsujikawa
07128719c4 Workaround broken version check in AX_PYTHON_DEVEL 2021-10-10 16:52:05 +09:00
Tatsuhiro Tsujikawa
7471fa627d Update ax_python_devel.m4 2021-10-10 16:08:49 +09:00
Tatsuhiro Tsujikawa
d7af5924ff nghttpx: Extend QUIC server ID to 4 bytes 2021-10-10 16:05:48 +09:00
Tatsuhiro Tsujikawa
a48e9d3d80 Add bpf to clang-format 2021-10-06 21:29:15 +09:00
Tatsuhiro Tsujikawa
474a6db00c Compile with gcc 2021-10-06 21:28:17 +09:00
Tatsuhiro Tsujikawa
cb6aea9aa9 Compile with -DNDEBUG 2021-10-06 21:28:00 +09:00
Tatsuhiro Tsujikawa
f4290c6497 Fix compile error 2021-10-04 23:23:03 +09:00
Tatsuhiro Tsujikawa
086b85b8f9 nghttpx: Unload BPF program after setting up all QUIC listeners 2021-10-04 22:39:31 +09:00
Tatsuhiro Tsujikawa
abee658a60 nghttpx: Make sure that ngtcp2_conn_update_pkt_tx_time is called 2021-10-02 19:22:53 +09:00
Tatsuhiro Tsujikawa
87bdc21667 nghttpx: Add --worker-process-grace-shutdown-period option 2021-10-02 18:55:51 +09:00
Tatsuhiro Tsujikawa
3e25ee8181 256k memlock is not enough when reloading happens 2021-09-29 22:16:39 +09:00
Tatsuhiro Tsujikawa
0266c458a3 nghttpx: Add --max-worker-processes option 2021-09-29 22:12:10 +09:00
Tatsuhiro Tsujikawa
d9c7631dcb Fix compile error 2021-09-29 21:05:46 +09:00
Tatsuhiro Tsujikawa
df064fa2ba nghttpx: Unload BPF objects on reload to avoid running out of memlock 2021-09-29 19:33:16 +09:00
Tatsuhiro Tsujikawa
318e0c8447 Guard msghdr_get_local_addr with ENABLE_HTTP3 macro 2021-09-28 22:35:53 +09:00
Tatsuhiro Tsujikawa
17d5503bf2 Update doc 2021-09-28 18:07:31 +09:00
Tatsuhiro Tsujikawa
19b4da6401 nghttpx: Support h3-29 2021-09-26 16:00:47 +09:00
Tatsuhiro Tsujikawa
886dc93f18 nghttpx: Fail h3 connection attempt if no ALPN is negotiated 2021-09-26 16:00:47 +09:00
Tatsuhiro Tsujikawa
407df2822e Remove check for UDP_SEGMENT
Check for UDP_SEGMENT is for debian 10, but now that we have debian
11, remove the check because it breaks cross-build.
2021-09-26 10:29:05 +09:00
Tatsuhiro Tsujikawa
f6da0d342a nghttpx: Fix crash if no keying materials are specified in file 2021-09-24 18:19:01 +09:00
Tatsuhiro Tsujikawa
7271537a15 nghttpx: Add --rlimit-memlock option 2021-09-24 18:13:38 +09:00
Tatsuhiro Tsujikawa
d0e8efac4d nghttpx: Fix bug that reading QUIC secret file fails without line separator 2021-09-24 17:50:28 +09:00
Tatsuhiro Tsujikawa
27e6d56d83 Update doc 2021-09-23 12:02:19 +09:00
Tatsuhiro Tsujikawa
c5122c12cb Update bash_completion 2021-09-23 11:45:23 +09:00
Tatsuhiro Tsujikawa
282050c596 Update manual pages 2021-09-23 11:45:10 +09:00
Tatsuhiro Tsujikawa
308c73bfa2 nghttpx: Read QUIC keying materials from file
Add --frontend-quic-secret-file to read QUIC keying materials from
file.  --frontend-quic-connection-id-encryption-key was removed in
favor of this new option.
2021-09-23 11:18:07 +09:00
Tatsuhiro Tsujikawa
c40309ae8e nghttpx: optarg should be allocated per configuration 2021-09-23 11:01:01 +09:00
Tatsuhiro Tsujikawa
1c7a4ecc7f nghttpx: Rename generate_encrypted_quic_connection_id to generate_quic_connection_id 2021-09-21 23:09:03 +09:00
Tatsuhiro Tsujikawa
80cc623eb2 nghttpx: Allocate server id in Connection ID 2021-09-21 23:08:55 +09:00
Tatsuhiro Tsujikawa
89457fd991 More https 2021-09-21 20:40:57 +09:00
Tatsuhiro Tsujikawa
257043b8fb Fix issue that libev cannot be found with autotools under mac osx 2021-09-21 19:49:56 +09:00
Tatsuhiro Tsujikawa
657d94b992 Fix compile error with libressl 2021-09-21 19:48:38 +09:00
Tatsuhiro Tsujikawa
06dc7d5964 Make sure that nghttp2 can be built from tar archive 2021-09-21 19:48:34 +09:00
Tatsuhiro Tsujikawa
b50079524b Always include optional files to EXTRA_DIST 2021-09-21 19:26:28 +09:00
Tatsuhiro Tsujikawa
cdf1f269ff Add missing cmake files to EXTRA_DIST 2021-09-21 17:53:15 +09:00
Tatsuhiro Tsujikawa
738b562f39 Bump up version number to 1.46.0-DEV 2021-09-20 17:52:07 +09:00
Tatsuhiro Tsujikawa
58499f256b Update bash_completion 2021-09-20 17:07:58 +09:00
Tatsuhiro Tsujikawa
afb455ef80 python3 2021-09-20 17:07:44 +09:00
Tatsuhiro Tsujikawa
f4515e9034 Update manual pages 2021-09-20 17:02:54 +09:00
Tatsuhiro Tsujikawa
aab07d00d7 Bump version number to 1.45.0, LT revision to 35:0:21 2021-09-20 16:58:28 +09:00
Tatsuhiro Tsujikawa
32ecfc6a86 Use https 2021-09-20 16:54:47 +09:00
Tatsuhiro Tsujikawa
e866f9fae7 Update AUTHORS 2021-09-20 16:53:55 +09:00
Tatsuhiro Tsujikawa
a029f6ed2c Rename sphinxcontrib to rubydomain to avoid module loading error
Rename sphinxcontrib to rubydomain to avoid module loading error when
sphinx-build docker image is used.
2021-09-20 13:43:40 +09:00
Tatsuhiro Tsujikawa
5b6e2cb5e0 Allow SPHINXBUILD to be overridden by environment variable 2021-09-20 12:19:26 +09:00
Tatsuhiro Tsujikawa
0264847a37 bpf: Use LINUX_KERNEL_VERSION extern variable 2021-09-19 21:07:40 +09:00
Tatsuhiro Tsujikawa
d276ca0adc Update doc 2021-09-18 19:28:03 +09:00
Tatsuhiro Tsujikawa
6a099ee50a nghttpx: QUIC requires TLS 2021-09-18 19:27:47 +09:00
Tatsuhiro Tsujikawa
be88846972 Build HTTP/3 and eBPF enabled nghttpx with Dockerfile 2021-09-18 18:08:40 +09:00
Tatsuhiro Tsujikawa
9a6b623c25 Update doc 2021-09-18 15:19:15 +09:00
Tatsuhiro Tsujikawa
97b36b8c74 make -C 2021-09-17 23:57:26 +09:00
Tatsuhiro Tsujikawa
0df332e7b8 Update doc 2021-09-17 23:53:35 +09:00
Tatsuhiro Tsujikawa
2d7e6fbb11 Update doc 2021-09-17 21:21:42 +09:00
Tatsuhiro Tsujikawa
fd107ab47c nghttpx: Refactor quic 2021-09-17 19:35:31 +09:00
Tatsuhiro Tsujikawa
1320d7efab nghttpx: Do not accept new connection during graceful shutdown period 2021-09-17 18:30:16 +09:00
Tatsuhiro Tsujikawa
7cdc6cfa6d nghttpx: Store Retry in CloseWait to rate limit its transmission 2021-09-17 17:52:05 +09:00
Tatsuhiro Tsujikawa
095ee9683d integration: Adds tests for chunked response 2021-09-16 22:37:33 +09:00
Tatsuhiro Tsujikawa
1e2081a1c5 Fix integration test error 2021-09-16 22:24:53 +09:00
Tatsuhiro Tsujikawa
e167e07a9a nghttpx: Check that HTTP response message finished safely 2021-09-16 22:00:36 +09:00
Tatsuhiro Tsujikawa
f3b9cd8404 bpf: Add workaround for ubuntu 20.04 2021-09-16 20:20:02 +09:00
Tatsuhiro Tsujikawa
8f9744c07b nghttpx: Pass hashed_scid_ to CloseWait 2021-09-15 23:40:46 +09:00
Tatsuhiro Tsujikawa
684a219e39 nghttpx: Tweak close wait handling 2021-09-15 23:07:46 +09:00
Tatsuhiro Tsujikawa
e2e6d827c7 Update bash_completion 2021-09-15 22:55:38 +09:00
Tatsuhiro Tsujikawa
f0108ece6f Update manual pages 2021-09-15 22:55:21 +09:00
Tatsuhiro Tsujikawa
789b7a5ff1 Update doc 2021-09-15 22:50:47 +09:00
Tatsuhiro Tsujikawa
0961295a82 nghttpx: Transform odcid into hashed cid 2021-09-15 22:19:52 +09:00
Tatsuhiro Tsujikawa
fd060eb9f1 nghttpx: Connection ID encryption 2021-09-15 21:31:03 +09:00
Tatsuhiro Tsujikawa
1feeda4514 nghttpx: Fix bug that worker process never exit 2021-09-15 21:00:41 +09:00
Tatsuhiro Tsujikawa
6d29de0f1e Fix compile error 2021-09-14 17:22:25 +09:00
Tatsuhiro Tsujikawa
74162850f0 nghttpx: Compile with the latest nghttp3 2021-09-14 16:45:11 +09:00
Tatsuhiro Tsujikawa
8903bd1e8a nghttpx: Deal with error from ngtcp2_conn_read_pkt 2021-09-13 23:09:38 +09:00
Tatsuhiro Tsujikawa
4b79a4a10d Add message when checking UDP_SEGMENT in linux/udp.h 2021-09-13 21:46:41 +09:00
Tatsuhiro Tsujikawa
8f419a4869 nghttpx: Add --frontend-quic-congestion-controller option 2021-09-13 21:35:46 +09:00
Tatsuhiro Tsujikawa
fcdac50f79 Should run program 2021-09-13 21:19:18 +09:00
Tatsuhiro Tsujikawa
4541134c88 Add missing include 2021-09-13 21:19:00 +09:00
Tatsuhiro Tsujikawa
b5e5972c2a Update doc 2021-09-12 18:11:11 +09:00
Tatsuhiro Tsujikawa
525d59fdf6 Remove util::make_hostport returning std::string 2021-09-12 18:07:54 +09:00
Tatsuhiro Tsujikawa
00f65afe20 nghttpx: Fix incorrect quic frontend address matching 2021-09-12 18:07:54 +09:00
Tatsuhiro Tsujikawa
fc402f5804 Cleanup 2021-09-12 16:55:56 +09:00
Tatsuhiro Tsujikawa
f74b6d9a43 nghttpx: Add --frontend-quic-require-token option 2021-09-12 15:04:54 +09:00
Tatsuhiro Tsujikawa
ccaf2333ca nghttpx: Enable websocket over h3 2021-09-11 17:56:25 +09:00
Tatsuhiro Tsujikawa
0066bf8eed h2load: Cleanup 2021-09-11 17:00:11 +09:00
Tatsuhiro Tsujikawa
bc8f88f5fa Compile with the latest ngtcp2 2021-09-11 16:57:14 +09:00
Tatsuhiro Tsujikawa
10c9d917ad Fix compile error 2021-09-10 22:55:33 +09:00
Tatsuhiro Tsujikawa
cc5f752f2d nghttpx: Use secure random to create websocket nonce 2021-09-10 22:43:03 +09:00
Tatsuhiro Tsujikawa
39b1a51ff4 Compile with the latest ngtcp2 2021-09-09 23:41:27 +09:00
Tatsuhiro Tsujikawa
a2e2e46af3 Build with OpenSSL v3.0.0 2021-09-09 18:35:35 +09:00
Tatsuhiro Tsujikawa
9d53a7e0a6 Fix compile error 2021-09-07 14:55:30 +09:00
Tatsuhiro Tsujikawa
7ea57eaa18 h2load: Add --max-udp-payload-size option 2021-09-07 14:42:35 +09:00
Tatsuhiro Tsujikawa
1657a425c1 Build with the latest ngtcp2 2021-09-07 13:40:41 +09:00
Tatsuhiro Tsujikawa
e929e92245 nghttpx: Fix typo 2021-09-06 20:59:11 +09:00
Tatsuhiro Tsujikawa
5994e48b28 nghttpx: Add more logging for token validation 2021-09-06 20:58:35 +09:00
Tatsuhiro Tsujikawa
50662c9c9e nghttpx: Guard TLS1_3_VERSION 2021-09-06 20:07:38 +09:00
Tatsuhiro Tsujikawa
addd614e94 nghttpx: Add qlog support 2021-09-06 20:06:38 +09:00
Tatsuhiro Tsujikawa
fbb228050a nghttpx: Fix uninitialized dnf fields 2021-09-06 00:12:04 +09:00
Tatsuhiro Tsujikawa
9bda8e266e nghttpx: Remove BoringSSL early data for QUIC for now 2021-09-05 22:37:20 +09:00
Tatsuhiro Tsujikawa
d977005126 nghttpx: Disable session cache for QUIC since it solely uses ticket 2021-09-05 22:36:43 +09:00
Tatsuhiro Tsujikawa
8b579bc7d0 nghttpx: Always renew TLS ticket for TLSv1.3 2021-09-05 22:26:45 +09:00
Tatsuhiro Tsujikawa
ab16a11aa3 nghttpx: Add --frontend-quic-early-data, disable early data by default 2021-09-05 21:39:45 +09:00
Tatsuhiro Tsujikawa
85347e12de nghttpx: Rate limit Stateless Reset transmission 2021-09-05 19:23:50 +09:00
Tatsuhiro Tsujikawa
67afbbbaa6 nghttpx: Use ngtcp2_cid as a hash key 2021-09-05 19:00:26 +09:00
Tatsuhiro Tsujikawa
b743ee21f0 nghttpx: Implement closing and draining state 2021-09-05 18:01:27 +09:00
Tatsuhiro Tsujikawa
72702a042e Cleanup 2021-09-05 17:47:50 +09:00
Tatsuhiro Tsujikawa
649c69fa9e nghttpx: Do not send CONNECTION_CLOSE on idle timeout 2021-09-04 19:04:22 +09:00
Tatsuhiro Tsujikawa
9fd0b87925 Add missing APIDOCS entry 2021-09-04 18:41:13 +09:00
Tatsuhiro Tsujikawa
1c7a001489 bpf: Take into account entire DCID 2021-09-04 18:31:21 +09:00
Tatsuhiro Tsujikawa
47edc33b0d nghttpx: Use ULOG 2021-09-04 17:48:02 +09:00
Tatsuhiro Tsujikawa
2afad0c650 nghttpx: Use ngtcp2_conn_get_client_initial_dcid 2021-09-04 17:43:24 +09:00
Tatsuhiro Tsujikawa
fb53a6a686 Follow the latest ngtcp2 change 2021-09-04 17:37:00 +09:00
Tatsuhiro Tsujikawa
31b5b78dc1 Use switch to avoid many if-else-if 2021-09-04 17:35:33 +09:00
Tatsuhiro Tsujikawa
2f941c7fb3 Update doc 2021-09-04 17:32:57 +09:00
Tatsuhiro Tsujikawa
ba483b4032 Update doc 2021-09-04 17:30:06 +09:00
Tatsuhiro Tsujikawa
977b0ceee4 make clang-format 2021-09-04 17:27:43 +09:00
Tatsuhiro Tsujikawa
fcc20334da Merge pull request #1613 from mkauf/check_pseudo_header_chars
Stricter checks for pseudo-headers :method and :path
2021-09-04 17:26:47 +09:00
Michael Kaufmann
83c063346d Stricter checks for pseudo-headers :method and :path
Check the allowed characters for ":method" (see RFC 7230, section 3.2.6) and
":path". For ":path", the space and tab characters are now forbidden, but
other special characters are still allowed for compatibility reasons.

Update genvchartbl.py so that it generates the same table as in the code.

Fixes #1611
2021-08-31 21:47:35 +02:00
Tatsuhiro Tsujikawa
c2e29ad06f nghttpx: Enforce worker-frontend-connections for QUIC 2021-08-31 19:48:26 +09:00
Tatsuhiro Tsujikawa
9194d40da7 Update bash_completions 2021-08-31 19:24:41 +09:00
Tatsuhiro Tsujikawa
002073ef57 Update manual pages 2021-08-31 19:24:41 +09:00
Tatsuhiro Tsujikawa
ef3066a1bd nghttpx: Update doc 2021-08-31 19:24:41 +09:00
Tatsuhiro Tsujikawa
65db5b94e4 nghttpx: Document "quic" parameter 2021-08-31 19:24:41 +09:00
Tatsuhiro Tsujikawa
3122038c48 Add HTTP/3 documentation 2021-08-31 19:24:41 +09:00
Tatsuhiro Tsujikawa
54fd0efdfe nghttpx: Return error if quic param is specified when quic is disabled 2021-08-31 17:15:44 +09:00
Tatsuhiro Tsujikawa
f0d1e50d5a cmake: Build bpf 2021-08-31 17:15:37 +09:00
Tatsuhiro Tsujikawa
a87ea20b7c configure: Avoid compile failure 2021-08-31 16:49:55 +09:00
Tatsuhiro Tsujikawa
8e7e40d0cc bpf: Remove redundant -g 2021-08-31 16:49:55 +09:00
Tatsuhiro Tsujikawa
de4d4f6609 Fix cmake Systemd warning 2021-08-31 16:49:55 +09:00
Tatsuhiro Tsujikawa
e01d61484d Fix compile error with cmake 2021-08-31 16:49:55 +09:00
Tatsuhiro Tsujikawa
51f83087f2 nghttpx: Add missing HTTP/3 timer handling 2021-08-31 14:19:31 +09:00
Tatsuhiro Tsujikawa
17012654e1 nghttpx: Add HTTP/3 graceful shutdown 2021-08-31 14:06:59 +09:00
Tatsuhiro Tsujikawa
e998d125ab nghttpx: Send CONNECTION_CLOSE if Retry token validation failed 2021-08-31 13:23:52 +09:00
Tatsuhiro Tsujikawa
95601d3179 nghttpx: Utilize the latest ngtcp2 features 2021-08-31 12:13:06 +09:00
Tatsuhiro Tsujikawa
0566a5833b nghttpx: Fix infinite loop 2021-08-30 18:39:28 +09:00
Tatsuhiro Tsujikawa
c50459b81a bpf: use __builtin_memcpy explicitly to build under ubuntu 20.04 2021-08-30 18:34:26 +09:00
Tatsuhiro Tsujikawa
0e52cf76eb bpf: Add -g flag by default 2021-08-30 18:33:27 +09:00
Tatsuhiro Tsujikawa
0baf725073 Remove unneeded include files 2021-08-30 18:32:58 +09:00
Tatsuhiro Tsujikawa
e77fd7ddb9 Build libbpf on CI 2021-08-29 22:08:42 +09:00
Tatsuhiro Tsujikawa
e5cb5dca61 Fix reuseport_kern.c to work under ubuntu 20.04 2021-08-29 21:09:27 +09:00
Tatsuhiro Tsujikawa
7941b559c5 Fix libbpf configure help message 2021-08-29 21:08:41 +09:00
Tatsuhiro Tsujikawa
58d81dbc52 nghttpx: Send Stateless Reset in response to Short packet only for now 2021-08-29 21:08:41 +09:00
Tatsuhiro Tsujikawa
2b4dc4496f nghttpx: Forward QUIC UDP datagram to lingering worker in graceful shutdown
Forward QUIC UDP datagram to lingering worker process which is in
graceful shutdown.  Both SIGHUP and SIGUSR2 work.  To make this work
correctly, eBPF is required.
2021-08-29 18:35:41 +09:00
Tatsuhiro Tsujikawa
c5e9d0096a nghttpx: Fix crash when ConnectionHandler is deleted while doing memcached request 2021-08-29 17:17:36 +09:00
Tatsuhiro Tsujikawa
c6f9780b1b Add accidentally deleted BPFCFLAGS 2021-08-29 15:47:45 +09:00
Tatsuhiro Tsujikawa
ef694923f7 Provide enum bpf_stats_type if not defined in linux/bpf.h for older kernel 2021-08-29 14:30:59 +09:00
Tatsuhiro Tsujikawa
8d02203bb6 Add LIBBPF_CFLAGS for eBPF program and nghttpx 2021-08-29 13:58:10 +09:00
Tatsuhiro Tsujikawa
1e75be3b5d Find libbpf with pkg-config 2021-08-29 13:43:07 +09:00
Tatsuhiro Tsujikawa
7d13891066 nghttpx: Rename eBPF options 2021-08-27 21:34:18 +09:00
Tatsuhiro Tsujikawa
4292bd7ad9 nghttpx: Set max udp payload size to IPv4 minimum payload size for now 2021-08-27 21:26:50 +09:00
Tatsuhiro Tsujikawa
82cd110dbe nghttpx: Use SHRPX_MAX_UDP_PAYLOAD_SIZE 2021-08-27 21:13:26 +09:00
Tatsuhiro Tsujikawa
d2729193c7 nghttpx: Add --frontend-http3-max-concurrent-streams option 2021-08-27 21:11:03 +09:00
Tatsuhiro Tsujikawa
87fb325357 nghttpx: Add window size options for HTTP/3 connection 2021-08-27 21:02:45 +09:00
Tatsuhiro Tsujikawa
fb8ff7b892 nghttpx: Format duration for frontend-quic-idle-timeout default value 2021-08-27 19:52:24 +09:00
Tatsuhiro Tsujikawa
5aeae7444f nghttpx: Add --frontend-quic-debug-log option 2021-08-27 19:16:25 +09:00
Tatsuhiro Tsujikawa
c9b11e9fbf nghttpx: Rename --quic-idle-timeout to --frontend-quic-idle-timeout 2021-08-27 19:03:06 +09:00
Tatsuhiro Tsujikawa
0005efa508 nghttpx: --frontend-http3-read-timeout should be noop if HTTP/3 is disabled 2021-08-27 18:45:19 +09:00
Tatsuhiro Tsujikawa
6931cb9d65 nghttpx: Add --quic-idle-timeout option 2021-08-27 18:44:33 +09:00
Tatsuhiro Tsujikawa
c1bcf0f11a nghttpx: Do not dump HTTP/3 header fields 2021-08-27 18:35:27 +09:00
Tatsuhiro Tsujikawa
717e7ae8b2 nghttpx: Add --frontend-http3-read-timeout option
Add --frontend-http3-read-timeout option.  QUIC idle timeout option
will be added later.
2021-08-27 18:29:06 +09:00
Tatsuhiro Tsujikawa
bed00fb8e1 nghttpx: Use buf.size() 2021-08-27 18:20:17 +09:00
Tatsuhiro Tsujikawa
2010401b81 nghttpx: Send APPLICATION_CLOSE on app read timeout 2021-08-27 18:19:44 +09:00
Tatsuhiro Tsujikawa
23e09e3b3c Mention libbpf-dev in README.rst 2021-08-26 22:12:54 +09:00
Tatsuhiro Tsujikawa
80c9d46b70 Update doc 2021-08-26 22:10:02 +09:00
Tatsuhiro Tsujikawa
0aa107426c Bump QUIC flavored OpenSSL 2021-08-26 21:41:06 +09:00
Tatsuhiro Tsujikawa
1517c77d9c h2load: Do not use legacy QUIC transport_parameters code point 2021-08-26 21:34:49 +09:00
Tatsuhiro Tsujikawa
51bf79bb8c nghttpx: Add --http2-altsvc option 2021-08-26 21:34:49 +09:00
Tatsuhiro Tsujikawa
d88eadff13 nghttpx: Make sure each quic frontend endpoint has a unique address 2021-08-26 21:34:49 +09:00
Tatsuhiro Tsujikawa
0d35e8e15e nghttpx: Do not allow quic on UNIX domain socket for now 2021-08-26 21:34:49 +09:00
Tatsuhiro Tsujikawa
a0066a1ccf nghttpx: Send NEW_TOKEN and very token from client 2021-08-26 21:34:49 +09:00
Tatsuhiro Tsujikawa
7a5082e8c4 nghttpx: Clean up confusing dcid/scid mixup 2021-08-26 21:34:49 +09:00
Tatsuhiro Tsujikawa
dfc345756c nghttpx: Send Retry packet 2021-08-26 21:34:49 +09:00
Tatsuhiro Tsujikawa
137da6adf6 nghttpx: Generate and set QUIC secrets 2021-08-26 21:34:49 +09:00
Tatsuhiro Tsujikawa
8563ec5a7a nghttpx: Add options to specify eBPF program file path and disable eBPF 2021-08-26 21:34:49 +09:00
Tatsuhiro Tsujikawa
8ac4bee3bc nghttpx: Add eBPF program to steer QUIC datagram to a correct socket 2021-08-26 21:34:49 +09:00
Tatsuhiro Tsujikawa
579fb478b5 nghttpx: Fix heap-use-after-free on initialization failure 2021-08-26 13:26:37 +09:00
Tatsuhiro Tsujikawa
33c580ebbf Forward QUIC packet to the correct worker 2021-08-23 22:21:48 +09:00
Tatsuhiro Tsujikawa
ff389b3e97 Merge branch 'quic' 2021-08-23 18:41:35 +09:00
Tatsuhiro Tsujikawa
50fe8e7852 Check the availability of SSL_is_quic 2021-08-22 23:59:09 +09:00
Tatsuhiro Tsujikawa
cdb6d19989 Enable HTTP/3 build for cmake on CI 2021-08-22 23:54:29 +09:00
Tatsuhiro Tsujikawa
29694e2945 nghttpx: Fix build error regarding RAND_bytes 2021-08-22 23:54:29 +09:00
Tatsuhiro Tsujikawa
9fe08d3913 nghttpx: Fix build without mruby 2021-08-22 23:54:29 +09:00
Tatsuhiro Tsujikawa
c07a0d9005 Allow HTTP/3 in cmake build 2021-08-22 23:54:29 +09:00
Tatsuhiro Tsujikawa
cbd45478e0 Cleanup flags/libs order 2021-08-22 23:54:29 +09:00
Tatsuhiro Tsujikawa
6f243108e9 nghttpx: Fix CI build error 2021-08-22 23:54:29 +09:00
Tatsuhiro Tsujikawa
0dcdf7ae21 Run http3 build on CI 2021-08-22 23:54:29 +09:00
Tatsuhiro Tsujikawa
e7ef2bec8b Rename h2load Dockerfile to Dockerfile-h2load-http3 2021-08-22 23:54:29 +09:00
Tatsuhiro Tsujikawa
4f4dce82c6 Update h2load Dockerfile 2021-08-22 23:54:29 +09:00
Tatsuhiro Tsujikawa
a619e7a88c Define UDP_SEGMENT if linux/udp.h has it 2021-08-22 23:54:29 +09:00
Tatsuhiro Tsujikawa
102d960106 nghttpx: Compile without UDP_SEGMENT 2021-08-22 23:54:29 +09:00
Tatsuhiro Tsujikawa
7de71b29a0 Update doc 2021-08-22 23:54:29 +09:00
Tatsuhiro Tsujikawa
4eced8a393 Build without HTTP/3 support 2021-08-22 23:54:29 +09:00
Tatsuhiro Tsujikawa
710b9c35e5 Merge pull request #1610 from amirlivneh/comment-typo
Fix reference to non-existing nghttp2_option_set_max_send_header_block_size() in comment
2021-08-22 23:35:05 +09:00
Tatsuhiro Tsujikawa
f46984d218 nghttpx: Enable QUIC 0RTT 2021-08-21 18:34:08 +09:00
Tatsuhiro Tsujikawa
44663a7e6e nghttpx: Handle backend reset and early response 2021-08-21 18:34:08 +09:00
Tatsuhiro Tsujikawa
446124f378 nghttpx: Process request body 2021-08-21 18:34:08 +09:00
Tatsuhiro Tsujikawa
c45f2085d5 nghttpx: Fix veccnt assertion 2021-08-21 18:34:08 +09:00
Tatsuhiro Tsujikawa
3abf62b41a nghttpx: Send stateless reset token in TP 2021-08-21 18:34:08 +09:00
Tatsuhiro Tsujikawa
9b2982510e nghttpx: Send stateless reset 2021-08-21 18:34:08 +09:00
Tatsuhiro Tsujikawa
48bb1ebe01 nghttpx: Add configuration to enable ngtcp2 logging (no cmd-line opt yet) 2021-08-21 18:34:08 +09:00
Tatsuhiro Tsujikawa
fe4c6e4c56 nghttpx: Generate stateless reset secret 2021-08-21 18:34:08 +09:00
Tatsuhiro Tsujikawa
37bd9ffc48 nghttpx: Implement http_reset_stream and http_send_stop_sending 2021-08-21 18:34:08 +09:00
Tatsuhiro Tsujikawa
b0548b4944 nghttpx: Complete HTTP request and response 2021-08-21 18:34:08 +09:00
Tatsuhiro Tsujikawa
12425556c1 nghttpx: Extend Downstream stream_id to 64 bits 2021-08-21 18:34:07 +09:00
Tatsuhiro Tsujikawa
3ed2da562b nghttpx: Add HTTP3 skeleton and minor SSL_CTX fix 2021-08-21 18:34:07 +09:00
Tatsuhiro Tsujikawa
354f46d8c5 nghttpx: Add QUIC timeouts 2021-08-21 18:34:07 +09:00
Tatsuhiro Tsujikawa
e70f0db83c nghttpx: QUIC handshake now works 2021-08-21 18:34:07 +09:00
Tatsuhiro Tsujikawa
49b8c56fde nghttpx: Use existing QUIC error object 2021-08-21 18:34:07 +09:00
Tatsuhiro Tsujikawa
940fdd5573 nghttpx: Read quic packet 2021-08-21 18:34:04 +09:00
Tatsuhiro Tsujikawa
ef53db201e nghttpx: Create QUIC SSL_CTX
We choose an easier route to duplicate SSL_CTX for QUIC.
2021-08-21 18:33:39 +09:00
Tatsuhiro Tsujikawa
aeb0b0728d nghttpx: Add QUICConnectionHandler and HTTP3Upstream skeleton 2021-08-21 18:33:39 +09:00
Tatsuhiro Tsujikawa
8b2746abf1 nghttpx: Add QUICListener 2021-08-21 18:33:39 +09:00
Tatsuhiro Tsujikawa
01da060496 nghttpx: Create quic server socket 2021-08-21 18:33:39 +09:00
Tatsuhiro Tsujikawa
20cbd269c4 Compile with the latest ngtcp2 2021-08-21 18:33:39 +09:00
Tatsuhiro Tsujikawa
7c2cd43dfa Compile with the latest ngtcp2 and nghttp3 2021-08-21 18:33:39 +09:00
Tatsuhiro Tsujikawa
de5feff720 Compile with the latest nghttp3 2021-08-21 18:33:39 +09:00
Tatsuhiro Tsujikawa
7342de837d Compile with the latest ngtcp2 2021-08-21 18:33:39 +09:00
Tatsuhiro Tsujikawa
aa2c648918 Just use h3 ALPN 2021-08-21 18:33:39 +09:00
Tatsuhiro Tsujikawa
e914b50d16 Compile with the latest ngtcp2 2021-08-21 18:33:39 +09:00
Tatsuhiro Tsujikawa
f79554f918 Count outgoing packets 2021-08-21 18:33:39 +09:00
Tatsuhiro Tsujikawa
213cc9c4b5 Enlarge receive buffer 2021-08-21 18:33:39 +09:00
Tatsuhiro Tsujikawa
05f3b8fa0f Adopt ngtcp2_crypto_recv_crypto_data_cb 2021-08-21 18:33:39 +09:00
Tatsuhiro Tsujikawa
bc53624133 Do not specify max_udp_payload_size for now 2021-08-21 18:33:39 +09:00
Tatsuhiro Tsujikawa
5944d034da Avoid std::ostringstream 2021-08-21 18:33:39 +09:00
Tatsuhiro Tsujikawa
df400feb61 make clang-format 2021-08-21 18:33:39 +09:00
Hajime Fujita
48e10c57da h2load: Add qlog output support 2021-08-21 18:33:39 +09:00
Tatsuhiro Tsujikawa
1eb818b64c QUIC UDP GSO 2021-08-21 18:33:39 +09:00
Tatsuhiro Tsujikawa
0954932091 Rewrite docker file
Rewrite docker file so that:

- avoid k8s debian-base
- build h2load as statically as possible
2021-08-21 18:33:39 +09:00
Tatsuhiro Tsujikawa
e584d9cd2e Measure the number of UDP datagrams sent and received 2021-08-21 18:33:39 +09:00
Tatsuhiro Tsujikawa
4d140ea6bd Update Dockerfile 2021-08-21 18:33:39 +09:00
Tatsuhiro Tsujikawa
09a2e50fc2 Support both h3 and h3-29 ALPN and their corresponding QUIC versions 2021-08-21 18:33:39 +09:00
Tatsuhiro Tsujikawa
35d8ef33ef Compile with the latest ngtcp2 2021-08-21 18:33:39 +09:00
Tatsuhiro Tsujikawa
f1ff2af47a Deal with 0 length HTTP data write case 2021-08-21 18:33:39 +09:00
Tatsuhiro Tsujikawa
d2d2c31ec7 Follow ngtcp2_conn_writev_stream specification change 2021-08-21 18:33:39 +09:00
Tatsuhiro Tsujikawa
95102c1c6c Compile with the latest ngtcp2 2021-08-21 18:33:39 +09:00
Tatsuhiro Tsujikawa
fa8c16ae01 Compile with the latest ngtcp2 and nghttp3 2021-08-21 18:33:39 +09:00
Tatsuhiro Tsujikawa
7ca2a8213d h2load: Enable --data for HTTP/3 2021-08-21 18:33:39 +09:00
Tatsuhiro Tsujikawa
1c8e5046e5 Compile with the latest ngtcp2 2021-08-21 18:33:39 +09:00
Tatsuhiro Tsujikawa
68a5652733 Build with draft-32 openssl 2021-08-21 18:33:39 +09:00
Tatsuhiro Tsujikawa
6b4be30c64 Cap --window-bits to 26 for QUIC 2021-08-21 18:33:39 +09:00
Tatsuhiro Tsujikawa
6ce952ad4a Set X25519 as default 2021-08-21 18:33:39 +09:00
Tatsuhiro Tsujikawa
5ae62dd9d7 Cap --window-bits to 23 for QUIC 2021-08-21 18:33:39 +09:00
Tatsuhiro Tsujikawa
51987107a2 Compile with the latest ngtcp2 2021-08-21 18:33:39 +09:00
Tatsuhiro Tsujikawa
e4a8c4813c Compile with the latest ngtcp2 2021-08-21 18:33:39 +09:00
Tatsuhiro Tsujikawa
3d708f7dc4 Compile with the latest ngtcp2 2021-08-21 18:33:39 +09:00
George Liu
4b5bcb56bc fix quic branch Dockerfile
libjemalloc1 package doesn't exist as it's now libjemalloc2 named

Get:1 http://security.debian.org/debian-security buster/updates InRelease [65.4 kB]
Get:2 http://deb.debian.org/debian buster InRelease [121 kB]
Get:3 http://deb.debian.org/debian buster-updates InRelease [51.9 kB]
Get:4 http://security.debian.org/debian-security buster/updates/main amd64 Packages [213 kB]
Get:5 http://deb.debian.org/debian buster/main amd64 Packages [7905 kB]
Get:6 http://deb.debian.org/debian buster-updates/main amd64 Packages [7868 B]
Fetched 8364 kB in 1s (6499 kB/s)
Reading package lists...
Reading package lists...
Building dependency tree...
Reading state information...
E: Unable to locate package libjemalloc1

fix reference to OpenSSL 1.1.1 branch

Cloning into 'openssl'...
warning: Could not find remote branch OpenSSL_1_1_1d-quic-draft-29 to clone.
fatal: Remote branch OpenSSL_1_1_1d-quic-draft-29 not found in upstream origin
2021-08-21 18:33:39 +09:00
Tatsuhiro Tsujikawa
10ec8c9558 Compile with the latest ngtcp2 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
3900f758ea QUIC needs termination without session 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
a3346fbad8 Compile with latest ngtcp2 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
f73d58d74e quic draft-29 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
813d5e1ddf Compile with latest ngtcp2 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
acb661df72 Fix bug for platform which does not have SOCK_NONBLOCK 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
4bc7710de9 Fix compile error 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
b8c1f4f138 Compile with latest ngtcp2 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
387b67472c Compile latest ngtcp2 crypto lib 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
b2c099bac6 Use ngtcp2_conn_handle_expiry 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
1acebb1cc4 draft-28 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
8d89a8dcb0 Assert ndatalen 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
a60a34331b Revert "Ensure complete packet is written"
This reverts commit c19046b09f8e66713f0e067f986ed92d676eb6b6.
2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
749015eb86 Ensure complete packet is written 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
4b45142e72 Fix compile error with the latest ngtcp2 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
76009ce7b9 draft-27 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
2722119776 Handle stream limit increment 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
c724585bce Update Dockerfile 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
0b61e46f95 draft-25 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
5c0da486b9 Remove unused member function declaration 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
9701e5e6e4 Fix compile error 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
1684091234 Bump base image and use OpenSSL_1_1_1d-quic-draft-24 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
a93eb8b8f5 Optimize QUIC write 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
c591ab5e6f Only count STREAM data as bytes_total 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
b3fbebed55 Use correct type 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
4621f88441 Follow ngtcp2 API update 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
747edb3a99 quic draft-24 2021-08-21 18:33:38 +09:00
Dmitri Tikhonov
558970e281 Update Dockerfile to use I-D 23 branches of ngtcp2 and openssl 2021-08-21 18:33:38 +09:00
Lucas Pardue
73fd20a608 Add SSLKEYLOGFILE support 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
78c2c33b9e Compile with the latest ngtcp2 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
610add1f59 Send SNI 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
655510ce28 h3-23 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
f7414700f4 Small adjustment of successful HTTP/3 error code
Non-zero successful error code is a bit annoying because ngtcp2 does
not know it.  Enforcing successful application error code to 0 is a
lot simpler.
2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
53a860a5bf Simplify write_quic 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
1aae450303 Handle sending just fine 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
b3a2f8837c Avoid setting 0 to repeat field 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
33d2a93294 Add missing acked_stream_data_offset callback 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
2da0db70de Fix return value 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
8b5cbf8066 Update doc 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
9668563801 Update docker build and doc 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
ff7067f3a3 Compile with the latest ngtcp2 and ngtcp2_crypto_openssl 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
6b8b152444 Remove error handling which does not happen 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
3dbe3b3e7f Follow ngtcp2 API changes and use libngtcp2_crypto_openssl 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
7aa4bff97b quic: Support TLS_AES_128_CCM_SHA256 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
6002fac9f1 h2load: Add --tls13-ciphers option 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
231c6ac862 Add Dockerfile 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
c3eb7e1634 Handle preferred address 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
05a6ee2b49 Show ngtcp2 debug log with --verbose 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
94d76c042d h2load: Add --groups option 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
23ccaa6191 Always call write_quic when timer expires 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
476e9d0a48 h3-22 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
7cd5ed6fc6 Handle Retry 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
750c23f319 quic: Configure settings with options 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
bb36df8b2e h2load: Fix possible deadlock 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
470c43a986 Fix link 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
8ea78e8361 Add build instruction 2021-08-21 18:33:38 +09:00
Tatsuhiro Tsujikawa
9c748d20d5 [WIP] Add QUIC to h2load 2021-08-21 18:33:38 +09:00
Amir Livneh
af15b22b03 Fix reference to non-existing nghttp2_option_set_max_send_header_block_size() in comment 2021-08-19 13:14:58 -04:00
Tatsuhiro Tsujikawa
80c9c705b8 Merge pull request #1607 from nghttp2/dnf
Add "dnf" (= "do not forward") parameter to backend option
2021-08-14 17:35:20 +09:00
Tatsuhiro Tsujikawa
138419d232 Add "dnf" (= "do not forward") parameter to backend option 2021-08-14 17:16:21 +09:00
Tatsuhiro Tsujikawa
8cee15bc5a Merge pull request #1603 from JackyYin/improve-doc
update doc for nghttp2_session_mem_recv
2021-08-10 21:25:41 +09:00
Jacky_Yin
8113974b26 doc: update document for nghttp2_session_mem_recv 2021-08-09 23:54:07 +08:00
Tatsuhiro Tsujikawa
2b70cefd48 Merge pull request #1598 from danbev/programmers_guide_typo
Fix typo in programmers-guide.rst
2021-08-08 15:49:43 +09:00
Tatsuhiro Tsujikawa
16054d4bfd Merge pull request #1602 from lhuang04/file_read_callback_prototype_mismatch
Fix prototype mismatch for function 'file_read_callback'
2021-08-08 15:48:55 +09:00
lhuang04
c2d4a53b67 Fix prototype mismatch for function 'file_read_callback'
Summary:
The [data_flags](https://github.com/lhuang04/nghttp2/blob/master/src/HttpServer.cc#L1078) is defined as `uint32_t*` in definition, but delared as [int*](https://github.com/lhuang04/nghttp2/blob/master/src/HttpServer.h#L245) in the prototype.

```
stderr: error: no previous prototype for function 'file_read_callback' [-Werror,-Wmissing-prototypes]
ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
        ^
```

Test Plan:

Reviewers:

Subscribers:

Tasks:

Tags:
2021-08-07 07:24:21 -07:00
Daniel Bevenius
3448b1c78c Fix typo in programmers-guide.rst 2021-07-26 14:44:15 +02:00
146 changed files with 14323 additions and 726 deletions

View File

@@ -11,6 +11,15 @@ jobs:
os: [ubuntu-20.04, macos-10.15] os: [ubuntu-20.04, macos-10.15]
compiler: [gcc, clang] compiler: [gcc, clang]
buildtool: [autotools, cmake] buildtool: [autotools, cmake]
http3: [http3, no-http3]
openssl: [openssl1, openssl3]
exclude:
- os: macos-10.15
openssl: openssl3
- http3: no-http3
openssl: openssl3
- os: macos-10.15
compiler: gcc
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
@@ -34,6 +43,7 @@ jobs:
libjansson-dev \ libjansson-dev \
libjemalloc-dev \ libjemalloc-dev \
libc-ares-dev \ libc-ares-dev \
libelf-dev \
cmake \ cmake \
cmake-data cmake-data
echo 'CPPFLAGS=-fsanitize=address,undefined -fno-sanitize-recover=undefined -g' >> $GITHUB_ENV echo 'CPPFLAGS=-fsanitize=address,undefined -fno-sanitize-recover=undefined -g' >> $GITHUB_ENV
@@ -52,8 +62,6 @@ jobs:
pkg-config \ pkg-config \
libtool libtool
echo 'PKG_CONFIG_PATH=/usr/local/opt/libressl/lib/pkgconfig:/usr/local/opt/libxml2/lib/pkgconfig' >> $GITHUB_ENV echo 'PKG_CONFIG_PATH=/usr/local/opt/libressl/lib/pkgconfig:/usr/local/opt/libxml2/lib/pkgconfig' >> $GITHUB_ENV
# This fixes infamous 'stdio.h not found' error.
echo 'SDKROOT='"$(xcrun --sdk macosx --show-sdk-path)" >> $GITHUB_ENV
- name: Setup clang (Linux) - name: Setup clang (Linux)
if: runner.os == 'Linux' && matrix.compiler == 'clang' if: runner.os == 'Linux' && matrix.compiler == 'clang'
run: | run: |
@@ -74,26 +82,97 @@ jobs:
run: | run: |
echo 'CC=gcc' >> $GITHUB_ENV echo 'CC=gcc' >> $GITHUB_ENV
echo 'CXX=g++' >> $GITHUB_ENV echo 'CXX=g++' >> $GITHUB_ENV
- name: Build libbpf
if: matrix.http3 == 'http3' && matrix.compiler == 'clang' && runner.os == 'Linux'
run: |
git clone -b v0.4.0 https://github.com/libbpf/libbpf
cd libbpf
PREFIX=$PWD/build make -C src install
EXTRA_AUTOTOOLS_OPTS="--with-libbpf"
EXTRA_CMAKE_OPTS="-DWITH_LIBBPF=1"
echo 'EXTRA_AUTOTOOLS_OPTS='"$EXTRA_AUTOTOOLS_OPTS" >> $GITHUB_ENV
echo 'EXTRA_CMAKE_OPTS='"$EXTRA_CMAKE_OPTS" >> $GITHUB_ENV
- name: Build quictls/openssl v1.1.1
if: matrix.http3 == 'http3' && matrix.openssl == 'openssl1'
run: |
git clone --depth 1 -b OpenSSL_1_1_1m+quic https://github.com/quictls/openssl
cd openssl
./config enable-tls1_3 --prefix=$PWD/build
make -j$(nproc)
make install_sw
- name: Build quictls/openssl v3.0.x
if: matrix.http3 == 'http3' && matrix.openssl == 'openssl3'
run: |
unset CPPFLAGS
unset LDFLAGS
git clone --depth 1 -b openssl-3.0.1+quic https://github.com/quictls/openssl
cd openssl
./config enable-tls1_3 --prefix=$PWD/build --libdir=$PWD/build/lib
make -j$(nproc)
make install_sw
- name: Build nghttp3
if: matrix.http3 == 'http3'
run: |
git clone https://github.com/ngtcp2/nghttp3
cd nghttp3
git checkout 74a222fe0c89b7202bcdaf6ef27a232edffc85e3
autoreconf -i
./configure --prefix=$PWD/build --enable-lib-only
make -j$(nproc) check
make install
- name: Build ngtcp2
if: matrix.http3 == 'http3'
run: |
git clone --depth 1 -b v0.1.0 https://github.com/ngtcp2/ngtcp2
cd ngtcp2
autoreconf -i
./configure --prefix=$PWD/build --enable-lib-only PKG_CONFIG_PATH="../openssl/build/lib/pkgconfig"
make -j$(nproc) check
make install
- name: Setup extra environment variables for HTTP/3
if: matrix.http3 == 'http3'
run: |
PKG_CONFIG_PATH="$PWD/openssl/build/lib/pkgconfig:$PWD/nghttp3/build/lib/pkgconfig:$PWD/ngtcp2/build/lib/pkgconfig:$PWD/libbpf/build/lib64/pkgconfig:$PKG_CONFIG_PATH"
LDFLAGS="$LDFLAGS -Wl,-rpath,$PWD/openssl/build/lib -Wl,-rpath,$PWD/libbpf/build/lib64"
EXTRA_AUTOTOOLS_OPTS="--enable-http3 $EXTRA_AUTOTOOLS_OPTS"
EXTRA_CMAKE_OPTS="-DENABLE_HTTP3=1 $EXTRA_CMAKE_OPTS"
echo 'PKG_CONFIG_PATH='"$PKG_CONFIG_PATH" >> $GITHUB_ENV
echo 'LDFLAGS='"$LDFLAGS" >> $GITHUB_ENV
echo 'EXTRA_AUTOTOOLS_OPTS='"$EXTRA_AUTOTOOLS_OPTS" >> $GITHUB_ENV
echo 'EXTRA_CMAKE_OPTS='"$EXTRA_CMAKE_OPTS" >> $GITHUB_ENV
- name: Setup git submodules - name: Setup git submodules
run: | run: |
git submodule update --init git submodule update --init
- name: Configure autotools - name: Configure autotools
if: matrix.buildtool == 'autotools'
run: | run: |
autoreconf -i autoreconf -i
./configure --enable-werror --with-mruby ./configure
- name: Configure cmake - name: Configure cmake
if: matrix.buildtool == 'cmake' if: matrix.buildtool == 'cmake'
run: | run: |
cmake -DENABLE_WERROR=1 -DWITH_MRUBY=1 -DWITH_NEVERBLEED=1 -DCPPFLAGS="$CPPFLAGS" -DLDFLAGS="$LDFLAGS" . make dist
VERSION=$(grep PACKAGE_VERSION config.h | cut -d' ' -f3 | tr -d '"')
tar xf nghttp2-$VERSION.tar.gz
cd nghttp2-$VERSION
echo 'NGHTTP2_CMAKE_DIR='"$PWD" >> $GITHUB_ENV
# This fixes infamous 'stdio.h not found' error.
echo 'SDKROOT='"$(xcrun --sdk macosx --show-sdk-path)" >> $GITHUB_ENV
cmake -DENABLE_WERROR=1 -DWITH_MRUBY=1 -DWITH_NEVERBLEED=1 -DENABLE_APP=1 $EXTRA_CMAKE_OPTS -DCPPFLAGS="$CPPFLAGS" -DLDFLAGS="$LDFLAGS" .
- name: Build nghttp2 with autotools - name: Build nghttp2 with autotools
if: matrix.buildtool == 'autotools' if: matrix.buildtool == 'autotools'
run: | run: |
make distcheck \ make distcheck \
DISTCHECK_CONFIGURE_FLAGS="--with-mruby --with-neverbleed --enable-werror CPPFLAGS=\"$CPPFLAGS\" LDFLAGS=\"$LDFLAGS\"" DISTCHECK_CONFIGURE_FLAGS="--with-mruby --with-neverbleed --with-libev --enable-werror $EXTRA_AUTOTOOLS_OPTS CPPFLAGS=\"$CPPFLAGS\" LDFLAGS=\"$LDFLAGS\""
- name: Build nghttp2 with cmake - name: Build nghttp2 with cmake
if: matrix.buildtool == 'cmake' if: matrix.buildtool == 'cmake'
run: | run: |
cd $NGHTTP2_CMAKE_DIR
make make
make check make check
- name: Integration test - name: Integration test
@@ -101,5 +180,5 @@ jobs:
# artifacts. # artifacts.
if: matrix.buildtool == 'cmake' if: matrix.buildtool == 'cmake'
run: | run: |
cd integration-tests cd $NGHTTP2_CMAKE_DIR/integration-tests
make itprep it make itprep it

View File

@@ -1,89 +0,0 @@
dist: xenial
os:
- linux
compiler:
- clang
- gcc
env:
matrix:
- CI_BUILD=cmake
- CI_BUILD=autotools
matrix:
include:
- os: osx
compiler: clang
osx_image: xcode10.2
env: CI_BUILD=autotools
language: cpp
sudo: required
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-8
- autoconf
- automake
- autotools-dev
- libtool
- pkg-config
- zlib1g-dev
- libcunit1-dev
- libssl-dev
- libxml2-dev
- libev-dev
- libevent-dev
- libjansson-dev
- libjemalloc-dev
- libc-ares-dev
- cmake
- cmake-data
homebrew:
packages:
- libev
- libevent
- c-ares
- cunit
- libressl
before_install:
- $CC --version
- if [ "$CXX" = "g++" ]; then export CXX="g++-8" CC="gcc-8"; fi
- $CC --version
- go version
- cmake --version
before_script:
- |
if [ "$TRAVIS_OS_NAME" = "linux" ]; then
CPPFLAGS="-fsanitize=address" LDFLAGS="-fsanitize=address -fuse-ld=gold"
fi
- |
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
PKG_CONFIG_PATH="/usr/local/opt/libressl/lib/pkgconfig:/usr/local/opt/libxml2/lib/pkgconfig"
fi
# Now build nghttp2
- git submodule update --init
- |
if [ "$CI_BUILD" = "autotools" ]; then
autoreconf -i && ./configure --with-mruby PKG_CONFIG_PATH=$PKG_CONFIG_PATH
fi
- |
if [ "$CI_BUILD" = "cmake" ]; then
cmake -DENABLE_WERROR=1 -DWITH_MRUBY=1 -DWITH_NEVERBLEED=1
fi
script:
- |
if [ "$CI_BUILD" = "autotools" ]; then
make distcheck DISTCHECK_CONFIGURE_FLAGS="--with-mruby --with-neverbleed --enable-werror CPPFLAGS=$CPPFLAGS LDFLAGS=\"$LDFLAGS\" PKG_CONFIG_PATH=$PKG_CONFIG_PATH"
fi
- |
if [ "$CI_BUILD" = "cmake" ]; then
make && make check
fi
- |
if [ "$CI_BUILD" = "cmake" ]; then
# Integration tests for nghttpx; autotools build erases build
# for packaging test.
cd integration-tests
export GO111MODULE=on
make it
fi

View File

@@ -19,6 +19,7 @@ Alek Storm
Alex Nalivko Alex Nalivko
Alexandros Konstantinakis-Karmis Alexandros Konstantinakis-Karmis
Alexis La Goutte Alexis La Goutte
Amir Livneh
Amir Pakdel Amir Pakdel
Anders Bakken Anders Bakken
Andreas Pohl Andreas Pohl
@@ -34,11 +35,13 @@ Bernard Spil
Brendan Heinonen Brendan Heinonen
Brian Card Brian Card
Brian Suh Brian Suh
Daniel Bevenius
Daniel Evers Daniel Evers
Daniel Stenberg Daniel Stenberg
Dave Reisner Dave Reisner
David Beitey David Beitey
David Weekly David Weekly
Dmitri Tikhonov
Dmitriy Vetutnev Dmitriy Vetutnev
Don Don
Dylan Plecki Dylan Plecki
@@ -48,9 +51,12 @@ Fabian Wiesel
Gabi Davar Gabi Davar
Gaël PORTAY Gaël PORTAY
Geoff Hill Geoff Hill
George Liu
Gitai Gitai
Google Inc. Google Inc.
Hajime Fujita
Jacky Tian Jacky Tian
Jacky_Yin
Jacob Champion Jacob Champion
James M Snell James M Snell
Jan Kundrát Jan Kundrát
@@ -76,6 +82,7 @@ MATSUMOTO Ryosuke
Marc Bachmann Marc Bachmann
Matt Rudary Matt Rudary
Matt Way Matt Way
Michael Kaufmann
Mike Conlen Mike Conlen
Mike Frysinger Mike Frysinger
Mike Lothian Mike Lothian
@@ -127,6 +134,7 @@ es
fangdingjun fangdingjun
jwchoi jwchoi
kumagi kumagi
lhuang04
lstefani lstefani
makovich makovich
mod-h2-dev mod-h2-dev

View File

@@ -24,13 +24,13 @@
cmake_minimum_required(VERSION 3.0) cmake_minimum_required(VERSION 3.0)
# XXX using 1.8.90 instead of 1.9.0-DEV # XXX using 1.8.90 instead of 1.9.0-DEV
project(nghttp2 VERSION 1.44.90) project(nghttp2 VERSION 1.46.90)
# See versioning rule: # See versioning rule:
# http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html # https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
set(LT_CURRENT 34) set(LT_CURRENT 35)
set(LT_REVISION 2) set(LT_REVISION 1)
set(LT_AGE 20) set(LT_AGE 21)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
include(Version) include(Version)
@@ -61,6 +61,18 @@ find_package(OpenSSL 1.0.1)
find_package(Libev 4.11) find_package(Libev 4.11)
find_package(Libcares 1.7.5) find_package(Libcares 1.7.5)
find_package(ZLIB 1.2.3) find_package(ZLIB 1.2.3)
find_package(Libngtcp2 0.0.0)
find_package(Libngtcp2_crypto_openssl 0.0.0)
if(LIBNGTCP2_CRYPTO_OPENSSL_FOUND)
set(HAVE_LIBNGTCP2_CRYPTO_OPENSSL 1)
endif()
find_package(Libnghttp3 0.0.0)
if(WITH_LIBBPF)
find_package(Libbpf 0.4.0)
if(NOT LIBBPF_FOUND)
message(FATAL_ERROR "libbpf was requested (WITH_LIBBPF=1) but not found.")
endif()
endif()
if(OPENSSL_FOUND AND LIBEV_FOUND AND ZLIB_FOUND) if(OPENSSL_FOUND AND LIBEV_FOUND AND ZLIB_FOUND)
set(ENABLE_APP_DEFAULT ON) set(ENABLE_APP_DEFAULT ON)
else() else()
@@ -167,7 +179,7 @@ endif()
# case "$host" in # case "$host" in
# *android*) # *android*)
# android_build=yes # android_build=yes
# # android does not need -pthread, but needs followng 3 libs for C++ # # android does not need -pthread, but needs following 3 libs for C++
# APPLDFLAGS="$APPLDFLAGS -lstdc++ -latomic -lsupc++" # APPLDFLAGS="$APPLDFLAGS -lstdc++ -latomic -lsupc++"
# dl: openssl requires libdl when it is statically linked. # dl: openssl requires libdl when it is statically linked.
@@ -182,9 +194,18 @@ if(HAVE_CUNIT)
endif() endif()
# openssl (for src) # openssl (for src)
include(CheckSymbolExists)
set(HAVE_OPENSSL ${OPENSSL_FOUND}) set(HAVE_OPENSSL ${OPENSSL_FOUND})
if(OPENSSL_FOUND) if(OPENSSL_FOUND)
set(OPENSSL_INCLUDE_DIRS ${OPENSSL_INCLUDE_DIR}) set(OPENSSL_INCLUDE_DIRS ${OPENSSL_INCLUDE_DIR})
cmake_push_check_state()
set(CMAKE_REQUIRED_INCLUDES "${OPENSSL_INCLUDE_DIR}")
set(CMAKE_REQUIRED_LIBRARIES "${OPENSSL_LIBRARIES}")
check_symbol_exists(SSL_is_quic "openssl/ssl.h" HAVE_SSL_IS_QUIC)
if(NOT HAVE_SSL_IS_QUIC)
message(WARNING "OpenSSL in ${OPENSSL_LIBRARIES} dose not have SSL_is_quic. HTTP/3 support cannot be enabled")
endif()
cmake_pop_check_state()
else() else()
set(OPENSSL_INCLUDE_DIRS "") set(OPENSSL_INCLUDE_DIRS "")
set(OPENSSL_LIBRARIES "") set(OPENSSL_LIBRARIES "")
@@ -223,11 +244,31 @@ if(ENABLE_ASIO_LIB)
find_package(Boost 1.54.0 REQUIRED system thread) find_package(Boost 1.54.0 REQUIRED system thread)
endif() endif()
# libbpf (for bpf)
set(HAVE_LIBBPF ${LIBBPF_FOUND})
if(LIBBPF_FOUND)
set(BPFCFLAGS -Wall -O2 -g)
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
# For Debian/Ubuntu
set(EXTRABPFCFLAGS -I/usr/include/${CMAKE_SYSTEM_PROCESSOR}-linux-gnu)
endif()
check_c_source_compiles("
#include <linux/bpf.h>
int main() { enum bpf_stats_type foo; (void)foo; }" HAVE_BPF_STATS_TYPE)
endif()
# The nghttp, nghttpd and nghttpx under src depend on zlib, OpenSSL and libev # The nghttp, nghttpd and nghttpx under src depend on zlib, OpenSSL and libev
if(ENABLE_APP AND NOT (ZLIB_FOUND AND OPENSSL_FOUND AND LIBEV_FOUND)) if(ENABLE_APP AND NOT (ZLIB_FOUND AND OPENSSL_FOUND AND LIBEV_FOUND))
message(FATAL_ERROR "Applications were requested (ENABLE_APP=1) but dependencies are not met.") message(FATAL_ERROR "Applications were requested (ENABLE_APP=1) but dependencies are not met.")
endif() endif()
# HTTP/3 requires quictls/openssl, libngtcp2, libngtcp2_crypto_openssl
# and libnghttp3.
if(ENABLE_HTTP3 AND NOT (HAVE_SSL_IS_QUIC AND LIBNGTCP2_FOUND AND LIBNGTCP2_CRYPTO_OPENSSL_FOUND AND LIBNGHTTP3_FOUND))
message(FATAL_ERROR "HTTP/3 was requested (ENABLE_HTTP3=1) but dependencies are not met.")
endif()
# HPACK tools requires jansson # HPACK tools requires jansson
if(ENABLE_HPACK_TOOLS AND NOT HAVE_JANSSON) if(ENABLE_HPACK_TOOLS AND NOT HAVE_JANSSON)
message(FATAL_ERROR "HPACK tools were requested (ENABLE_HPACK_TOOLS=1) but dependencies are not met.") message(FATAL_ERROR "HPACK tools were requested (ENABLE_HPACK_TOOLS=1) but dependencies are not met.")
@@ -448,11 +489,16 @@ foreach(name
configure_file("${name}.in" "${name}" @ONLY) configure_file("${name}.in" "${name}" @ONLY)
endforeach() endforeach()
if(APPLE)
add_definitions(-D__APPLE_USE_RFC_3542)
endif()
include_directories( include_directories(
"${CMAKE_CURRENT_BINARY_DIR}" # for config.h "${CMAKE_CURRENT_BINARY_DIR}" # for config.h
) )
# For use in src/CMakeLists.txt # For use in src/CMakeLists.txt
set(PKGDATADIR "${CMAKE_INSTALL_FULL_DATADIR}/${CMAKE_PROJECT_NAME}") set(PKGDATADIR "${CMAKE_INSTALL_FULL_DATADIR}/${CMAKE_PROJECT_NAME}")
set(PKGLIBDIR "${CMAKE_INSTALL_FULL_LIBDIR}/${CMAKE_PROJECT_NAME}")
install(FILES README.rst DESTINATION "${CMAKE_INSTALL_DOCDIR}") install(FILES README.rst DESTINATION "${CMAKE_INSTALL_DOCDIR}")
@@ -469,6 +515,7 @@ add_subdirectory(integration-tests)
add_subdirectory(doc) add_subdirectory(doc)
add_subdirectory(contrib) add_subdirectory(contrib)
add_subdirectory(script) add_subdirectory(script)
add_subdirectory(bpf)
string(TOUPPER "${CMAKE_BUILD_TYPE}" _build_type) string(TOUPPER "${CMAKE_BUILD_TYPE}" _build_type)
@@ -499,6 +546,10 @@ message(STATUS "summary of build options:
Libxml2: ${HAVE_LIBXML2} (LIBS='${LIBXML2_LIBRARIES}') Libxml2: ${HAVE_LIBXML2} (LIBS='${LIBXML2_LIBRARIES}')
Libev: ${HAVE_LIBEV} (LIBS='${LIBEV_LIBRARIES}') Libev: ${HAVE_LIBEV} (LIBS='${LIBEV_LIBRARIES}')
Libc-ares: ${HAVE_LIBCARES} (LIBS='${LIBCARES_LIBRARIES}') Libc-ares: ${HAVE_LIBCARES} (LIBS='${LIBCARES_LIBRARIES}')
Libngtcp2: ${HAVE_LIBNGTCP2} (LIBS='${LIBNGTCP2_LIBRARIES}')
Libngtcp2_crypto_openssl: ${HAVE_LIBNGTCP2_CRYPTO_OPENSSL} (LIBS='${LIBNGTCP2_CRYPTO_OPENSSL_LIBRARIES}')
Libnghttp3: ${HAVE_LIBNGHTTP3} (LIBS='${LIBNGHTTP3_LIBRARIES}')
Libbpf: ${HAVE_LIBBPF} (LIBS='${LIBBPF_LIBRARIES}')
Libevent(SSL): ${HAVE_LIBEVENT_OPENSSL} (LIBS='${LIBEVENT_OPENSSL_LIBRARIES}') Libevent(SSL): ${HAVE_LIBEVENT_OPENSSL} (LIBS='${LIBEVENT_OPENSSL_LIBRARIES}')
Jansson: ${HAVE_JANSSON} (LIBS='${JANSSON_LIBRARIES}') Jansson: ${HAVE_JANSSON} (LIBS='${JANSSON_LIBRARIES}')
Jemalloc: ${HAVE_JEMALLOC} (LIBS='${JEMALLOC_LIBRARIES}') Jemalloc: ${HAVE_JEMALLOC} (LIBS='${JEMALLOC_LIBRARIES}')
@@ -517,6 +568,7 @@ message(STATUS "summary of build options:
Examples: ${ENABLE_EXAMPLES} Examples: ${ENABLE_EXAMPLES}
Python bindings:${ENABLE_PYTHON_BINDINGS} Python bindings:${ENABLE_PYTHON_BINDINGS}
Threading: ${ENABLE_THREADS} Threading: ${ENABLE_THREADS}
HTTP/3(EXPERIMENTAL): ${ENABLE_HTTP3}
") ")
if(ENABLE_LIB_ONLY_DISABLED_OTHERS) if(ENABLE_LIB_ONLY_DISABLED_OTHERS)
message("Only the library will be built. To build other components " message("Only the library will be built. To build other components "

View File

@@ -17,14 +17,14 @@ option(ENABLE_LIB_ONLY "Build libnghttp2 only. This is a short hand for -DENAB
option(ENABLE_STATIC_LIB "Build libnghttp2 in static mode also") option(ENABLE_STATIC_LIB "Build libnghttp2 in static mode also")
option(ENABLE_SHARED_LIB "Build libnghttp2 as a shared library" ON) option(ENABLE_SHARED_LIB "Build libnghttp2 as a shared library" ON)
option(ENABLE_STATIC_CRT "Build libnghttp2 against the MS LIBCMT[d]") option(ENABLE_STATIC_CRT "Build libnghttp2 against the MS LIBCMT[d]")
option(ENABLE_HTTP3 "Enable HTTP/3 support" OFF)
option(WITH_LIBXML2 "Use libxml2" option(WITH_LIBXML2 "Use libxml2"
${WITH_LIBXML2_DEFAULT}) ${WITH_LIBXML2_DEFAULT})
option(WITH_JEMALLOC "Use jemalloc" option(WITH_JEMALLOC "Use jemalloc"
${WITH_JEMALLOC_DEFAULT}) ${WITH_JEMALLOC_DEFAULT})
option(WITH_SPDYLAY "Use spdylay"
${WITH_SPDYLAY_DEFAULT})
option(WITH_MRUBY "Use mruby") option(WITH_MRUBY "Use mruby")
option(WITH_NEVERBLEED "Use neverbleed") option(WITH_NEVERBLEED "Use neverbleed")
option(WITH_LIBBPF "Use libbpf")
# vim: ft=cmake: # vim: ft=cmake:

View File

@@ -20,7 +20,7 @@
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
SUBDIRS = lib third-party src examples python tests integration-tests \ SUBDIRS = lib third-party src bpf examples python tests integration-tests \
doc contrib script doc contrib script
# Now with python setuptools, make uninstall will leave many files we # Now with python setuptools, make uninstall will leave many files we
@@ -46,16 +46,20 @@ EXTRA_DIST = nghttpx.conf.sample proxy.pac.sample android-config android-make \
cmake/FindLibevent.cmake \ cmake/FindLibevent.cmake \
cmake/FindJansson.cmake \ cmake/FindJansson.cmake \
cmake/FindLibcares.cmake \ cmake/FindLibcares.cmake \
cmake/FindSystemd.cmake cmake/FindSystemd.cmake \
cmake/FindLibbpf.cmake \
cmake/FindLibnghttp3.cmake \
cmake/FindLibngtcp2.cmake \
cmake/FindLibngtcp2_crypto_openssl.cmake
.PHONY: clang-format .PHONY: clang-format
# Format source files using clang-format. Don't format source files # Format source files using clang-format. Don't format source files
# under third-party directory since we are not responsible for thier # under third-party directory since we are not responsible for their
# coding style. # coding style.
clang-format: clang-format:
CLANGFORMAT=`git config --get clangformat.binary`; \ CLANGFORMAT=`git config --get clangformat.binary`; \
test -z $${CLANGFORMAT} && CLANGFORMAT="clang-format"; \ test -z $${CLANGFORMAT} && CLANGFORMAT="clang-format"; \
$${CLANGFORMAT} -i lib/*.{c,h} lib/includes/nghttp2/*.h \ $${CLANGFORMAT} -i lib/*.{c,h} lib/includes/nghttp2/*.h \
src/*.{c,cc,h} src/includes/nghttp2/*.h examples/*.{c,cc} \ src/*.{c,cc,h} src/includes/nghttp2/*.h examples/*.{c,cc} \
tests/*.{c,h} tests/*.{c,h} bpf/*.c

View File

@@ -32,12 +32,14 @@ Public Test Server
The following endpoints are available to try out our nghttp2 The following endpoints are available to try out our nghttp2
implementation. implementation.
* https://nghttp2.org/ (TLS + ALPN/NPN) * https://nghttp2.org/ (TLS + ALPN/NPN and HTTP/3)
This endpoint supports ``h2``, ``h2-16``, ``h2-14``, and This endpoint supports ``h2``, ``h2-16``, ``h2-14``, and
``http/1.1`` via ALPN/NPN and requires TLSv1.2 for HTTP/2 ``http/1.1`` via ALPN/NPN and requires TLSv1.2 for HTTP/2
connection. connection.
It also supports HTTP/3.
* http://nghttp2.org/ (HTTP Upgrade and HTTP/2 Direct) * http://nghttp2.org/ (HTTP Upgrade and HTTP/2 Direct)
``h2c`` and ``http/1.1``. ``h2c`` and ``http/1.1``.
@@ -145,6 +147,33 @@ minimizes the risk of private key leakage when serious bug like
Heartbleed is exploited. The neverbleed is disabled by default. To Heartbleed is exploited. The neverbleed is disabled by default. To
enable it, use ``--with-neverbleed`` configure option. enable it, use ``--with-neverbleed`` configure option.
To enable the experimental HTTP/3 support for h2load and nghttpx, the
following libraries are required:
* `OpenSSL with QUIC support
<https://github.com/quictls/openssl/tree/OpenSSL_1_1_1m+quic>`_; or
`BoringSSL <https://boringssl.googlesource.com/boringssl/>`_ (commit
f6ef1c560ae5af51e2df5d8d2175bed207b28b8f)
* `ngtcp2 <https://github.com/ngtcp2/ngtcp2>`_
* `nghttp3 <https://github.com/ngtcp2/nghttp3>`_
Use ``--enable-http3`` configure option to enable HTTP/3 feature for
h2load and nghttpx.
In order to build optional eBPF program to direct an incoming QUIC UDP
datagram to a correct socket for nghttpx, the following libraries are
required:
* libbpf-dev >= 0.4.0
Use ``--with-libbpf`` configure option to build eBPF program.
libelf-dev is needed to build libbpf.
For Ubuntu 20.04, you can build libbpf from `the source code
<https://github.com/libbpf/libbpf/releases/tag/v0.4.0>`_. nghttpx
requires eBPF program for reloading its configuration and hot swapping
its executable.
Compiling libnghttp2 C source code requires a C99 compiler. gcc 4.8 Compiling libnghttp2 C source code requires a C99 compiler. gcc 4.8
is known to be adequate. In order to compile the C++ source code, gcc is known to be adequate. In order to compile the C++ source code, gcc
>= 6.0 or clang >= 6.0 is required. C++ source code requires C++14 >= 6.0 or clang >= 6.0 is required. C++ source code requires C++14
@@ -307,6 +336,89 @@ The generated documents will not be installed with ``make install``.
The online documentation is available at The online documentation is available at
https://nghttp2.org/documentation/ https://nghttp2.org/documentation/
Build HTTP/3 enabled h2load and nghttpx
---------------------------------------
To build h2load and nghttpx with HTTP/3 feature enabled, run the
configure script with ``--enable-http3``.
For nghttpx to reload configurations and swapping its executable while
gracefully terminating old worker processes, eBPF is required. Run
the configure script with ``--enable-http3 --with-libbpf`` to build
eBPF program. The QUIC keying material must be set with
``--frontend-quic-secret-file`` in order to keep the existing
connections alive during reload.
The detailed steps to build HTTP/3 enabled h2load and nghttpx follow.
Build custom OpenSSL:
.. code-block:: text
$ git clone --depth 1 -b OpenSSL_1_1_1m+quic https://github.com/quictls/openssl
$ cd openssl
$ ./config --prefix=$PWD/build --openssldir=/etc/ssl
$ make -j$(nproc)
$ make install_sw
$ cd ..
Build nghttp3:
.. code-block:: text
$ git clone https://github.com/ngtcp2/nghttp3
$ cd nghttp3
$ git checkout 74a222fe0c89b7202bcdaf6ef27a232edffc85e3
$ autoreconf -i
$ ./configure --prefix=$PWD/build --enable-lib-only
$ make -j$(nproc)
$ make install
$ cd ..
Build ngtcp2:
.. code-block:: text
$ git clone --depth 1 -b v0.1.0 https://github.com/ngtcp2/ngtcp2
$ cd ngtcp2
$ autoreconf -i
$ ./configure --prefix=$PWD/build --enable-lib-only \
PKG_CONFIG_PATH="$PWD/../openssl/build/lib/pkgconfig"
$ make -j$(nproc)
$ make install
$ cd ..
If your Linux distribution does not have libbpf-dev >= 0.4.0, build
from source:
.. code-block:: text
$ git clone --depth 1 -b v0.4.0 https://github.com/libbpf/libbpf
$ cd libbpf
$ PREFIX=$PWD/build make -C src install
$ cd ..
Build nghttp2:
.. code-block:: text
$ git clone https://github.com/nghttp2/nghttp2
$ cd nghttp2
$ git submodule update --init
$ autoreconf -i
$ ./configure --with-mruby --with-neverbleed --enable-http3 --with-libbpf \
--disable-python-bindings \
CC=clang-12 CXX=clang++-12 \
PKG_CONFIG_PATH="$PWD/../openssl/build/lib/pkgconfig:$PWD/../nghttp3/build/lib/pkgconfig:$PWD/../ngtcp2/build/lib/pkgconfig:$PWD/../libbpf/build/lib64/pkgconfig" \
LDFLAGS="$LDFLAGS -Wl,-rpath,$PWD/../openssl/build/lib -Wl,-rpath,$PWD/../libbpf/build/lib64"
$ make -j$(nproc)
The eBPF program ``reuseport_kern.o`` should be found under bpf
directory. Pass ``--quic-bpf-program-file=bpf/reuseport_kern.o``
option to nghttpx to load it. See also `HTTP/3 section in nghttpx -
HTTP/2 proxy - HOW-TO
<https://nghttp2.org/documentation/nghttpx-howto.html#http-3>`_.
Unit tests Unit tests
---------- ----------
@@ -734,7 +846,7 @@ information. Here is sample output from ``nghttpd``:
nghttpx - proxy nghttpx - proxy
+++++++++++++++ +++++++++++++++
``nghttpx`` is a multi-threaded reverse proxy for HTTP/2, and ``nghttpx`` is a multi-threaded reverse proxy for HTTP/3, HTTP/2, and
HTTP/1.1, and powers http://nghttp2.org and supports HTTP/2 server HTTP/1.1, and powers http://nghttp2.org and supports HTTP/2 server
push. push.
@@ -755,16 +867,16 @@ ticket keys among multiple ``nghttpx`` instances via memcached.
``nghttpx`` has 2 operation modes: ``nghttpx`` has 2 operation modes:
================== ================ ================ ============= ================== ======================== ================ =============
Mode option Frontend Backend Note Mode option Frontend Backend Note
================== ================ ================ ============= ================== ======================== ================ =============
default mode HTTP/2, HTTP/1.1 HTTP/1.1, HTTP/2 Reverse proxy default mode HTTP/3, HTTP/2, HTTP/1.1 HTTP/1.1, HTTP/2 Reverse proxy
``--http2-proxy`` HTTP/2, HTTP/1.1 HTTP/1.1, HTTP/2 Forward proxy ``--http2-proxy`` HTTP/3, HTTP/2, HTTP/1.1 HTTP/1.1, HTTP/2 Forward proxy
================== ================ ================ ============= ================== ======================== ================ =============
The interesting mode at the moment is the default mode. It works like The interesting mode at the moment is the default mode. It works like
a reverse proxy and listens for HTTP/2, and HTTP/1.1 and can be a reverse proxy and listens for HTTP/3, HTTP/2, and HTTP/1.1 and can
deployed as a SSL/TLS terminator for existing web server. be deployed as a SSL/TLS terminator for existing web server.
In all modes, the frontend connections are encrypted by SSL/TLS by In all modes, the frontend connections are encrypted by SSL/TLS by
default. To disable encryption, use the ``no-tls`` keyword in default. To disable encryption, use the ``no-tls`` keyword in
@@ -782,7 +894,7 @@ server:
.. code-block:: text .. code-block:: text
Client <-- (HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/1.1, HTTP/2) --> Web Server Client <-- (HTTP/3, HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/1.1, HTTP/2) --> Web Server
[reverse proxy] [reverse proxy]
With the ``--http2-proxy`` option, it works as forward proxy, and it With the ``--http2-proxy`` option, it works as forward proxy, and it
@@ -790,7 +902,7 @@ is so called secure HTTP/2 proxy:
.. code-block:: text .. code-block:: text
Client <-- (HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/1.1) --> Proxy Client <-- (HTTP/3, HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/1.1) --> Proxy
[secure proxy] (e.g., Squid, ATS) [secure proxy] (e.g., Squid, ATS)
The ``Client`` in the above example needs to be configured to use The ``Client`` in the above example needs to be configured to use
@@ -823,7 +935,7 @@ proxy through an HTTP proxy:
.. code-block:: text .. code-block:: text
Client <-- (HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/2) -- Client <-- (HTTP/3, HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/2) --
--===================---> HTTP/2 Proxy --===================---> HTTP/2 Proxy
(HTTP proxy tunnel) (e.g., nghttpx -s) (HTTP proxy tunnel) (e.g., nghttpx -s)
@@ -831,8 +943,8 @@ proxy through an HTTP proxy:
Benchmarking tool Benchmarking tool
----------------- -----------------
The ``h2load`` program is a benchmarking tool for HTTP/2. The UI of The ``h2load`` program is a benchmarking tool for HTTP/3, HTTP/2, and
``h2load`` is heavily inspired by ``weighttp`` HTTP/1.1. The UI of ``h2load`` is heavily inspired by ``weighttp``
(https://github.com/lighttpd/weighttp). The typical usage is as (https://github.com/lighttpd/weighttp). The typical usage is as
follows: follows:
@@ -875,6 +987,14 @@ threads to avoid saturating a single core on client side.
considered a DOS attack. Please only use it against your private considered a DOS attack. Please only use it against your private
servers. servers.
If the experimental HTTP/3 is enabled, h2load can send requests to
HTTP/3 server. To do this, specify ``h3`` to ``--npn-list`` option
like so:
.. code-block:: text
$ h2load --npn-list h3 https://127.0.0.1:4433
HPACK tools HPACK tools
----------- -----------

13
bpf/CMakeLists.txt Normal file
View File

@@ -0,0 +1,13 @@
if(LIBBPF_FOUND)
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/reuseport_kern.o"
COMMAND ${CMAKE_C_COMPILER} ${BPFCFLAGS} ${EXTRABPFCFLAGS} -I${LIBBPF_INCLUDE_DIRS} -target bpf -c reuseport_kern.c -o "${CMAKE_CURRENT_BINARY_DIR}/reuseport_kern.o"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
VERBATIM)
add_custom_target(bpf ALL
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/reuseport_kern.o"
VERBATIM)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/reuseport_kern.o"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/${CMAKE_PROJECT_NAME}")
endif()

40
bpf/Makefile.am Normal file
View File

@@ -0,0 +1,40 @@
# nghttp2 - HTTP/2 C Library
# Copyright (c) 2021 Tatsuhiro Tsujikawa
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
EXTRA_DIST = CMakeLists.txt reuseport_kern.c
if HAVE_LIBBPF
bpf_pkglibdir = $(pkglibdir)
bpf_pkglib_DATA = reuseport_kern.o
all: $(builddir)/reuseport_kern.o
$(builddir)/reuseport_kern.o: reuseport_kern.c
$(CC) @LIBBPF_CFLAGS@ @BPFCFLAGS@ @EXTRABPFCFLAGS@ \
-target bpf -c $< -o $@
clean-local:
-rm -f reuseport_kern.o
endif # HAVE_LIBBPF

663
bpf/reuseport_kern.c Normal file
View File

@@ -0,0 +1,663 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2021 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <linux/udp.h>
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
/*
* How to compile:
*
* clang-12 -O2 -Wall -target bpf -g -c reuseport_kern.c -o reuseport_kern.o \
* -I/path/to/kernel/include
*
* See
* https://www.kernel.org/doc/Documentation/kbuild/headers_install.txt
* how to install kernel header files.
*/
/* AES_CBC_decrypt_buffer: https://github.com/kokke/tiny-AES-c
License is Public Domain. Commit hash:
12e7744b4919e9d55de75b7ab566326a1c8e7a67 */
#define AES_BLOCKLEN \
16 /* Block length in bytes - AES is 128b block \
only */
#define AES_KEYLEN 16 /* Key length in bytes */
#define AES_keyExpSize 176
struct AES_ctx {
__u8 RoundKey[AES_keyExpSize];
};
/* The number of columns comprising a state in AES. This is a constant
in AES. Value=4 */
#define Nb 4
#define Nk 4 /* The number of 32 bit words in a key. */
#define Nr 10 /* The number of rounds in AES Cipher. */
/* state - array holding the intermediate results during
decryption. */
typedef __u8 state_t[4][4];
/* The lookup-tables are marked const so they can be placed in
read-only storage instead of RAM The numbers below can be computed
dynamically trading ROM for RAM - This can be useful in (embedded)
bootloader applications, where ROM is often limited. */
static const __u8 sbox[256] = {
/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b,
0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26,
0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2,
0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed,
0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f,
0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec,
0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14,
0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d,
0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f,
0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11,
0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f,
0xb0, 0x54, 0xbb, 0x16};
static const __u8 rsbox[256] = {
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e,
0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87,
0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32,
0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49,
0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16,
0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50,
0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05,
0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02,
0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41,
0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8,
0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89,
0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b,
0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59,
0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d,
0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d,
0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63,
0x55, 0x21, 0x0c, 0x7d};
/* The round constant word array, Rcon[i], contains the values given
by x to the power (i-1) being powers of x (x is denoted as {02}) in
the field GF(2^8) */
static const __u8 Rcon[11] = {0x8d, 0x01, 0x02, 0x04, 0x08, 0x10,
0x20, 0x40, 0x80, 0x1b, 0x36};
#define getSBoxValue(num) (sbox[(num)])
/* This function produces Nb(Nr+1) round keys. The round keys are used
in each round to decrypt the states. */
static void KeyExpansion(__u8 *RoundKey, const __u8 *Key) {
unsigned i, j, k;
__u8 tempa[4]; /* Used for the column/row operations */
/* The first round key is the key itself. */
for (i = 0; i < Nk; ++i) {
RoundKey[(i * 4) + 0] = Key[(i * 4) + 0];
RoundKey[(i * 4) + 1] = Key[(i * 4) + 1];
RoundKey[(i * 4) + 2] = Key[(i * 4) + 2];
RoundKey[(i * 4) + 3] = Key[(i * 4) + 3];
}
/* All other round keys are found from the previous round keys. */
for (i = Nk; i < Nb * (Nr + 1); ++i) {
{
k = (i - 1) * 4;
tempa[0] = RoundKey[k + 0];
tempa[1] = RoundKey[k + 1];
tempa[2] = RoundKey[k + 2];
tempa[3] = RoundKey[k + 3];
}
if (i % Nk == 0) {
/* This function shifts the 4 bytes in a word to the left once.
[a0,a1,a2,a3] becomes [a1,a2,a3,a0] */
/* Function RotWord() */
{
const __u8 u8tmp = tempa[0];
tempa[0] = tempa[1];
tempa[1] = tempa[2];
tempa[2] = tempa[3];
tempa[3] = u8tmp;
}
/* SubWord() is a function that takes a four-byte input word and
applies the S-box to each of the four bytes to produce an
output word. */
/* Function Subword() */
{
tempa[0] = getSBoxValue(tempa[0]);
tempa[1] = getSBoxValue(tempa[1]);
tempa[2] = getSBoxValue(tempa[2]);
tempa[3] = getSBoxValue(tempa[3]);
}
tempa[0] = tempa[0] ^ Rcon[i / Nk];
}
j = i * 4;
k = (i - Nk) * 4;
RoundKey[j + 0] = RoundKey[k + 0] ^ tempa[0];
RoundKey[j + 1] = RoundKey[k + 1] ^ tempa[1];
RoundKey[j + 2] = RoundKey[k + 2] ^ tempa[2];
RoundKey[j + 3] = RoundKey[k + 3] ^ tempa[3];
}
}
static void AES_init_ctx(struct AES_ctx *ctx, const __u8 *key) {
KeyExpansion(ctx->RoundKey, key);
}
/* This function adds the round key to state. The round key is added
to the state by an XOR function. */
static void AddRoundKey(__u8 round, state_t *state, const __u8 *RoundKey) {
__u8 i, j;
for (i = 0; i < 4; ++i) {
for (j = 0; j < 4; ++j) {
(*state)[i][j] ^= RoundKey[(round * Nb * 4) + (i * Nb) + j];
}
}
}
static __u8 xtime(__u8 x) { return ((x << 1) ^ (((x >> 7) & 1) * 0x1b)); }
#define Multiply(x, y) \
(((y & 1) * x) ^ ((y >> 1 & 1) * xtime(x)) ^ \
((y >> 2 & 1) * xtime(xtime(x))) ^ \
((y >> 3 & 1) * xtime(xtime(xtime(x)))) ^ \
((y >> 4 & 1) * xtime(xtime(xtime(xtime(x))))))
#define getSBoxInvert(num) (rsbox[(num)])
/* MixColumns function mixes the columns of the state matrix. The
method used to multiply may be difficult to understand for the
inexperienced. Please use the references to gain more
information. */
static void InvMixColumns(state_t *state) {
int i;
__u8 a, b, c, d;
for (i = 0; i < 4; ++i) {
a = (*state)[i][0];
b = (*state)[i][1];
c = (*state)[i][2];
d = (*state)[i][3];
(*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^
Multiply(d, 0x09);
(*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^
Multiply(d, 0x0d);
(*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^
Multiply(d, 0x0b);
(*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^
Multiply(d, 0x0e);
}
}
extern __u32 LINUX_KERNEL_VERSION __kconfig;
/* The SubBytes Function Substitutes the values in the state matrix
with values in an S-box. */
static void InvSubBytes(state_t *state) {
__u8 i, j;
if (LINUX_KERNEL_VERSION < KERNEL_VERSION(5, 10, 0)) {
for (i = 0; i < 4; ++i) {
for (j = 0; j < 4; ++j) {
/* Ubuntu 20.04 LTS kernel 5.4.0 needs this workaround
otherwise "math between map_value pointer and register with
unbounded min value is not allowed". 5.10.0 is a kernel
version that works but it might not be the minimum
version. */
__u8 k = (*state)[j][i];
(*state)[j][i] = k ? getSBoxInvert(k) : getSBoxInvert(0);
}
}
} else {
for (i = 0; i < 4; ++i) {
for (j = 0; j < 4; ++j) {
(*state)[j][i] = getSBoxInvert((*state)[j][i]);
}
}
}
}
static void InvShiftRows(state_t *state) {
__u8 temp;
/* Rotate first row 1 columns to right */
temp = (*state)[3][1];
(*state)[3][1] = (*state)[2][1];
(*state)[2][1] = (*state)[1][1];
(*state)[1][1] = (*state)[0][1];
(*state)[0][1] = temp;
/* Rotate second row 2 columns to right */
temp = (*state)[0][2];
(*state)[0][2] = (*state)[2][2];
(*state)[2][2] = temp;
temp = (*state)[1][2];
(*state)[1][2] = (*state)[3][2];
(*state)[3][2] = temp;
/* Rotate third row 3 columns to right */
temp = (*state)[0][3];
(*state)[0][3] = (*state)[1][3];
(*state)[1][3] = (*state)[2][3];
(*state)[2][3] = (*state)[3][3];
(*state)[3][3] = temp;
}
static void InvCipher(state_t *state, const __u8 *RoundKey) {
/* Add the First round key to the state before starting the
rounds. */
AddRoundKey(Nr, state, RoundKey);
/* There will be Nr rounds. The first Nr-1 rounds are identical.
These Nr rounds are executed in the loop below. Last one without
InvMixColumn() */
InvShiftRows(state);
InvSubBytes(state);
AddRoundKey(Nr - 1, state, RoundKey);
InvMixColumns(state);
InvShiftRows(state);
InvSubBytes(state);
AddRoundKey(Nr - 2, state, RoundKey);
InvMixColumns(state);
InvShiftRows(state);
InvSubBytes(state);
AddRoundKey(Nr - 3, state, RoundKey);
InvMixColumns(state);
InvShiftRows(state);
InvSubBytes(state);
AddRoundKey(Nr - 4, state, RoundKey);
InvMixColumns(state);
InvShiftRows(state);
InvSubBytes(state);
AddRoundKey(Nr - 5, state, RoundKey);
InvMixColumns(state);
InvShiftRows(state);
InvSubBytes(state);
AddRoundKey(Nr - 6, state, RoundKey);
InvMixColumns(state);
InvShiftRows(state);
InvSubBytes(state);
AddRoundKey(Nr - 7, state, RoundKey);
InvMixColumns(state);
InvShiftRows(state);
InvSubBytes(state);
AddRoundKey(Nr - 8, state, RoundKey);
InvMixColumns(state);
InvShiftRows(state);
InvSubBytes(state);
AddRoundKey(Nr - 9, state, RoundKey);
InvMixColumns(state);
InvShiftRows(state);
InvSubBytes(state);
AddRoundKey(Nr - 10, state, RoundKey);
}
static void AES_ECB_decrypt(const struct AES_ctx *ctx, __u8 *buf) {
/* The next function call decrypts the PlainText with the Key using
AES algorithm. */
InvCipher((state_t *)buf, ctx->RoundKey);
}
/* rol32: From linux kernel source code */
/**
* rol32 - rotate a 32-bit value left
* @word: value to rotate
* @shift: bits to roll
*/
static inline __u32 rol32(__u32 word, unsigned int shift) {
return (word << shift) | (word >> ((-shift) & 31));
}
/* jhash.h: Jenkins hash support.
*
* Copyright (C) 2006. Bob Jenkins (bob_jenkins@burtleburtle.net)
*
* https://burtleburtle.net/bob/hash/
*
* These are the credits from Bob's sources:
*
* lookup3.c, by Bob Jenkins, May 2006, Public Domain.
*
* These are functions for producing 32-bit hashes for hash table lookup.
* hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final()
* are externally useful functions. Routines to test the hash are included
* if SELF_TEST is defined. You can use this free for any purpose. It's in
* the public domain. It has no warranty.
*
* Copyright (C) 2009-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
*
* I've modified Bob's hash to be useful in the Linux kernel, and
* any bugs present are my fault.
* Jozsef
*/
/* __jhash_final - final mixing of 3 32-bit values (a,b,c) into c */
#define __jhash_final(a, b, c) \
{ \
c ^= b; \
c -= rol32(b, 14); \
a ^= c; \
a -= rol32(c, 11); \
b ^= a; \
b -= rol32(a, 25); \
c ^= b; \
c -= rol32(b, 16); \
a ^= c; \
a -= rol32(c, 4); \
b ^= a; \
b -= rol32(a, 14); \
c ^= b; \
c -= rol32(b, 24); \
}
/* __jhash_nwords - hash exactly 3, 2 or 1 word(s) */
static inline __u32 __jhash_nwords(__u32 a, __u32 b, __u32 c, __u32 initval) {
a += initval;
b += initval;
c += initval;
__jhash_final(a, b, c);
return c;
}
/* An arbitrary initial parameter */
#define JHASH_INITVAL 0xdeadbeef
static inline __u32 jhash_2words(__u32 a, __u32 b, __u32 initval) {
return __jhash_nwords(a, b, 0, initval + JHASH_INITVAL + (2 << 2));
}
struct bpf_map_def SEC("maps") cid_prefix_map = {
.type = BPF_MAP_TYPE_HASH,
.max_entries = 255,
.key_size = sizeof(__u64),
.value_size = sizeof(__u32),
};
struct bpf_map_def SEC("maps") reuseport_array = {
.type = BPF_MAP_TYPE_REUSEPORT_SOCKARRAY,
.max_entries = 255,
.key_size = sizeof(__u32),
.value_size = sizeof(__u32),
};
struct bpf_map_def SEC("maps") sk_info = {
.type = BPF_MAP_TYPE_ARRAY,
.max_entries = 3,
.key_size = sizeof(__u32),
.value_size = sizeof(__u64),
};
typedef struct quic_hd {
__u8 *dcid;
__u32 dcidlen;
__u32 dcid_offset;
__u8 type;
} quic_hd;
#define SV_DCIDLEN 20
#define MAX_DCIDLEN 20
#define MIN_DCIDLEN 8
#define CID_PREFIXLEN 8
#define CID_PREFIX_OFFSET 1
enum {
NGTCP2_PKT_INITIAL = 0x0,
NGTCP2_PKT_0RTT = 0x1,
NGTCP2_PKT_HANDSHAKE = 0x2,
NGTCP2_PKT_SHORT = 0x40,
};
static inline int parse_quic(quic_hd *qhd, __u8 *data, __u8 *data_end) {
__u8 *p;
__u64 dcidlen;
if (*data & 0x80) {
p = data + 1 + 4;
/* Do not check the actual DCID length because we might not buffer
entire DCID here. */
dcidlen = *p;
if (dcidlen > MAX_DCIDLEN || dcidlen < MIN_DCIDLEN) {
return -1;
}
++p;
qhd->type = (*data & 0x30) >> 4;
qhd->dcid = p;
qhd->dcidlen = dcidlen;
qhd->dcid_offset = 6;
} else {
qhd->type = NGTCP2_PKT_SHORT;
qhd->dcid = data + 1;
qhd->dcidlen = SV_DCIDLEN;
qhd->dcid_offset = 1;
}
return 0;
}
static __u32 hash(const __u8 *data, __u32 datalen, __u32 initval) {
__u32 a, b;
a = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
b = (data[4] << 24) | (data[5] << 16) | (data[6] << 8) | data[7];
return jhash_2words(a, b, initval);
}
static __u32 sk_index_from_dcid(const quic_hd *qhd,
const struct sk_reuseport_md *reuse_md,
__u64 num_socks) {
__u32 len = qhd->dcidlen;
__u32 h = reuse_md->hash;
__u8 hbuf[8];
if (len > 16) {
__builtin_memset(hbuf, 0, sizeof(hbuf));
switch (len) {
case 20:
__builtin_memcpy(hbuf, qhd->dcid + 16, 4);
break;
case 19:
__builtin_memcpy(hbuf, qhd->dcid + 16, 3);
break;
case 18:
__builtin_memcpy(hbuf, qhd->dcid + 16, 2);
break;
case 17:
__builtin_memcpy(hbuf, qhd->dcid + 16, 1);
break;
}
h = hash(hbuf, sizeof(hbuf), h);
len = 16;
}
if (len > 8) {
__builtin_memset(hbuf, 0, sizeof(hbuf));
switch (len) {
case 16:
__builtin_memcpy(hbuf, qhd->dcid + 8, 8);
break;
case 15:
__builtin_memcpy(hbuf, qhd->dcid + 8, 7);
break;
case 14:
__builtin_memcpy(hbuf, qhd->dcid + 8, 6);
break;
case 13:
__builtin_memcpy(hbuf, qhd->dcid + 8, 5);
break;
case 12:
__builtin_memcpy(hbuf, qhd->dcid + 8, 4);
break;
case 11:
__builtin_memcpy(hbuf, qhd->dcid + 8, 3);
break;
case 10:
__builtin_memcpy(hbuf, qhd->dcid + 8, 2);
break;
case 9:
__builtin_memcpy(hbuf, qhd->dcid + 8, 1);
break;
}
h = hash(hbuf, sizeof(hbuf), h);
len = 8;
}
return hash(qhd->dcid, len, h) % num_socks;
}
SEC("sk_reuseport")
int select_reuseport(struct sk_reuseport_md *reuse_md) {
__u32 sk_index, *psk_index;
__u64 *pnum_socks, *pkey;
__u32 zero = 0, key_high_idx = 1, key_low_idx = 2;
int rv;
quic_hd qhd;
__u8 qpktbuf[6 + MAX_DCIDLEN];
struct AES_ctx aes_ctx;
__u8 key[AES_KEYLEN];
__u8 *cid_prefix;
if (bpf_skb_load_bytes(reuse_md, sizeof(struct udphdr), qpktbuf,
sizeof(qpktbuf)) != 0) {
return SK_DROP;
}
pnum_socks = bpf_map_lookup_elem(&sk_info, &zero);
if (pnum_socks == NULL) {
return SK_DROP;
}
pkey = bpf_map_lookup_elem(&sk_info, &key_high_idx);
if (pkey == NULL) {
return SK_DROP;
}
__builtin_memcpy(key, pkey, sizeof(*pkey));
pkey = bpf_map_lookup_elem(&sk_info, &key_low_idx);
if (pkey == NULL) {
return SK_DROP;
}
__builtin_memcpy(key + sizeof(*pkey), pkey, sizeof(*pkey));
rv = parse_quic(&qhd, qpktbuf, qpktbuf + sizeof(qpktbuf));
if (rv != 0) {
return SK_DROP;
}
AES_init_ctx(&aes_ctx, key);
switch (qhd.type) {
case NGTCP2_PKT_INITIAL:
case NGTCP2_PKT_0RTT:
if (qhd.dcidlen == SV_DCIDLEN) {
cid_prefix = qhd.dcid + CID_PREFIX_OFFSET;
AES_ECB_decrypt(&aes_ctx, cid_prefix);
psk_index = bpf_map_lookup_elem(&cid_prefix_map, cid_prefix);
if (psk_index != NULL) {
sk_index = *psk_index;
break;
}
}
sk_index = sk_index_from_dcid(&qhd, reuse_md, *pnum_socks);
break;
case NGTCP2_PKT_HANDSHAKE:
case NGTCP2_PKT_SHORT:
if (qhd.dcidlen != SV_DCIDLEN) {
return SK_DROP;
}
cid_prefix = qhd.dcid + CID_PREFIX_OFFSET;
AES_ECB_decrypt(&aes_ctx, cid_prefix);
psk_index = bpf_map_lookup_elem(&cid_prefix_map, cid_prefix);
if (psk_index == NULL) {
sk_index = sk_index_from_dcid(&qhd, reuse_md, *pnum_socks);
break;
}
sk_index = *psk_index;
break;
default:
return SK_DROP;
}
rv = bpf_sk_select_reuseport(reuse_md, &reuseport_array, &sk_index, 0);
if (rv != 0) {
return SK_DROP;
}
return SK_PASS;
}

32
cmake/FindLibbpf.cmake Normal file
View File

@@ -0,0 +1,32 @@
# - Try to find libbpf
# Once done this will define
# LIBBPF_FOUND - System has libbpf
# LIBBPF_INCLUDE_DIRS - The libbpf include directories
# LIBBPF_LIBRARIES - The libraries needed to use libbpf
find_package(PkgConfig QUIET)
pkg_check_modules(PC_LIBBPF QUIET libbpf)
find_path(LIBBPF_INCLUDE_DIR
NAMES bpf/bpf.h
HINTS ${PC_LIBBPF_INCLUDE_DIRS}
)
find_library(LIBBPF_LIBRARY
NAMES bpf
HINTS ${PC_LIBBPF_LIBRARY_DIRS}
)
include(FindPackageHandleStandardArgs)
# handle the QUIETLY and REQUIRED arguments and set LIBBPF_FOUND
# to TRUE if all listed variables are TRUE and the requested version
# matches.
find_package_handle_standard_args(Libbpf REQUIRED_VARS
LIBBPF_LIBRARY LIBBPF_INCLUDE_DIR
VERSION_VAR LIBBPF_VERSION)
if(LIBBPF_FOUND)
set(LIBBPF_LIBRARIES ${LIBBPF_LIBRARY})
set(LIBBPF_INCLUDE_DIRS ${LIBBPF_INCLUDE_DIR})
endif()
mark_as_advanced(LIBBPF_INCLUDE_DIR LIBBPF_LIBRARY)

View File

@@ -0,0 +1,41 @@
# - Try to find libnghttp3
# Once done this will define
# LIBNGHTTP3_FOUND - System has libnghttp3
# LIBNGHTTP3_INCLUDE_DIRS - The libnghttp3 include directories
# LIBNGHTTP3_LIBRARIES - The libraries needed to use libnghttp3
find_package(PkgConfig QUIET)
pkg_check_modules(PC_LIBNGHTTP3 QUIET libnghttp3)
find_path(LIBNGHTTP3_INCLUDE_DIR
NAMES nghttp3/nghttp3.h
HINTS ${PC_LIBNGHTTP3_INCLUDE_DIRS}
)
find_library(LIBNGHTTP3_LIBRARY
NAMES nghttp3
HINTS ${PC_LIBNGHTTP3_LIBRARY_DIRS}
)
if(LIBNGHTTP3_INCLUDE_DIR)
set(_version_regex "^#define[ \t]+NGHTTP3_VERSION[ \t]+\"([^\"]+)\".*")
file(STRINGS "${LIBNGHTTP3_INCLUDE_DIR}/nghttp3/version.h"
LIBNGHTTP3_VERSION REGEX "${_version_regex}")
string(REGEX REPLACE "${_version_regex}" "\\1"
LIBNGHTTP3_VERSION "${LIBNGHTTP3_VERSION}")
unset(_version_regex)
endif()
include(FindPackageHandleStandardArgs)
# handle the QUIETLY and REQUIRED arguments and set LIBNGHTTP3_FOUND
# to TRUE if all listed variables are TRUE and the requested version
# matches.
find_package_handle_standard_args(Libnghttp3 REQUIRED_VARS
LIBNGHTTP3_LIBRARY LIBNGHTTP3_INCLUDE_DIR
VERSION_VAR LIBNGHTTP3_VERSION)
if(LIBNGHTTP3_FOUND)
set(LIBNGHTTP3_LIBRARIES ${LIBNGHTTP3_LIBRARY})
set(LIBNGHTTP3_INCLUDE_DIRS ${LIBNGHTTP3_INCLUDE_DIR})
endif()
mark_as_advanced(LIBNGHTTP3_INCLUDE_DIR LIBNGHTTP3_LIBRARY)

41
cmake/FindLibngtcp2.cmake Normal file
View File

@@ -0,0 +1,41 @@
# - Try to find libngtcp2
# Once done this will define
# LIBNGTCP2_FOUND - System has libngtcp2
# LIBNGTCP2_INCLUDE_DIRS - The libngtcp2 include directories
# LIBNGTCP2_LIBRARIES - The libraries needed to use libngtcp2
find_package(PkgConfig QUIET)
pkg_check_modules(PC_LIBNGTCP2 QUIET libngtcp2)
find_path(LIBNGTCP2_INCLUDE_DIR
NAMES ngtcp2/ngtcp2.h
HINTS ${PC_LIBNGTCP2_INCLUDE_DIRS}
)
find_library(LIBNGTCP2_LIBRARY
NAMES ngtcp2
HINTS ${PC_LIBNGTCP2_LIBRARY_DIRS}
)
if(LIBNGTCP2_INCLUDE_DIR)
set(_version_regex "^#define[ \t]+NGTCP2_VERSION[ \t]+\"([^\"]+)\".*")
file(STRINGS "${LIBNGTCP2_INCLUDE_DIR}/ngtcp2/version.h"
LIBNGTCP2_VERSION REGEX "${_version_regex}")
string(REGEX REPLACE "${_version_regex}" "\\1"
LIBNGTCP2_VERSION "${LIBNGTCP2_VERSION}")
unset(_version_regex)
endif()
include(FindPackageHandleStandardArgs)
# handle the QUIETLY and REQUIRED arguments and set LIBNGTCP2_FOUND
# to TRUE if all listed variables are TRUE and the requested version
# matches.
find_package_handle_standard_args(Libngtcp2 REQUIRED_VARS
LIBNGTCP2_LIBRARY LIBNGTCP2_INCLUDE_DIR
VERSION_VAR LIBNGTCP2_VERSION)
if(LIBNGTCP2_FOUND)
set(LIBNGTCP2_LIBRARIES ${LIBNGTCP2_LIBRARY})
set(LIBNGTCP2_INCLUDE_DIRS ${LIBNGTCP2_INCLUDE_DIR})
endif()
mark_as_advanced(LIBNGTCP2_INCLUDE_DIR LIBNGTCP2_LIBRARY)

View File

@@ -0,0 +1,43 @@
# - Try to find libngtcp2_crypto_openssl
# Once done this will define
# LIBNGTCP2_CRYPTO_OPENSSL_FOUND - System has libngtcp2_crypto_openssl
# LIBNGTCP2_CRYPTO_OPENSSL_INCLUDE_DIRS - The libngtcp2_crypto_openssl include directories
# LIBNGTCP2_CRYPTO_OPENSSL_LIBRARIES - The libraries needed to use libngtcp2_crypto_openssl
find_package(PkgConfig QUIET)
pkg_check_modules(PC_LIBNGTCP2_CRYPTO_OPENSSL QUIET libngtcp2_crypto_openssl)
find_path(LIBNGTCP2_CRYPTO_OPENSSL_INCLUDE_DIR
NAMES ngtcp2/ngtcp2_crypto_openssl.h
HINTS ${PC_LIBNGTCP2_CRYPTO_OPENSSL_INCLUDE_DIRS}
)
find_library(LIBNGTCP2_CRYPTO_OPENSSL_LIBRARY
NAMES ngtcp2_crypto_openssl
HINTS ${PC_LIBNGTCP2_CRYPTO_OPENSSL_LIBRARY_DIRS}
)
if(LIBNGTCP2_CRYPTO_OPENSSL_INCLUDE_DIR)
set(_version_regex "^#define[ \t]+NGTCP2_VERSION[ \t]+\"([^\"]+)\".*")
file(STRINGS "${LIBNGTCP2_CRYPTO_OPENSSL_INCLUDE_DIR}/ngtcp2/version.h"
LIBNGTCP2_CRYPTO_OPENSSL_VERSION REGEX "${_version_regex}")
string(REGEX REPLACE "${_version_regex}" "\\1"
LIBNGTCP2_CRYPTO_OPENSSL_VERSION "${LIBNGTCP2_CRYPTO_OPENSSL_VERSION}")
unset(_version_regex)
endif()
include(FindPackageHandleStandardArgs)
# handle the QUIETLY and REQUIRED arguments and set
# LIBNGTCP2_CRYPTO_OPENSSL_FOUND to TRUE if all listed variables are
# TRUE and the requested version matches.
find_package_handle_standard_args(Libngtcp2_crypto_openssl REQUIRED_VARS
LIBNGTCP2_CRYPTO_OPENSSL_LIBRARY
LIBNGTCP2_CRYPTO_OPENSSL_INCLUDE_DIR
VERSION_VAR LIBNGTCP2_CRYPTO_OPENSSL_VERSION)
if(LIBNGTCP2_CRYPTO_OPENSSL_FOUND)
set(LIBNGTCP2_CRYPTO_OPENSSL_LIBRARIES ${LIBNGTCP2_CRYPTO_OPENSSL_LIBRARY})
set(LIBNGTCP2_CRYPTO_OPENSSL_INCLUDE_DIRS ${LIBNGTCP2_CRYPTO_OPENSSL_INCLUDE_DIR})
endif()
mark_as_advanced(LIBNGTCP2_CRYPTO_OPENSSL_INCLUDE_DIR
LIBNGTCP2_CRYPTO_OPENSSL_LIBRARY)

View File

@@ -15,5 +15,5 @@ find_library(SYSTEMD_LIBRARIES NAMES systemd ${PC_SYSTEMD_LIBRARY_DIRS})
find_path(SYSTEMD_INCLUDE_DIRS systemd/sd-login.h HINTS ${PC_SYSTEMD_INCLUDE_DIRS}) find_path(SYSTEMD_INCLUDE_DIRS systemd/sd-login.h HINTS ${PC_SYSTEMD_INCLUDE_DIRS})
include(FindPackageHandleStandardArgs) include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(SYSTEMD DEFAULT_MSG SYSTEMD_INCLUDE_DIRS SYSTEMD_LIBRARIES) find_package_handle_standard_args(Systemd DEFAULT_MSG SYSTEMD_INCLUDE_DIRS SYSTEMD_LIBRARIES)
mark_as_advanced(SYSTEMD_INCLUDE_DIRS SYSTEMD_LIBRARIES) mark_as_advanced(SYSTEMD_INCLUDE_DIRS SYSTEMD_LIBRARIES)

View File

@@ -78,3 +78,15 @@
/* Define to 1 if you have the <unistd.h> header file. */ /* Define to 1 if you have the <unistd.h> header file. */
#cmakedefine HAVE_UNISTD_H 1 #cmakedefine HAVE_UNISTD_H 1
/* Define to 1 if HTTP/3 is enabled. */
#cmakedefine ENABLE_HTTP3 1
/* Define to 1 if you have `libbpf` library. */
#cmakedefine HAVE_LIBBPF 1
/* Define to 1 if you have enum bpf_stats_type in linux/bpf.h. */
#cmakedefine HAVE_BPF_STATS_TYPE 1
/* Define to 1 if you have `libngtcp2_crypto_openssl` library. */
#cmakedefine HAVE_LIBNGTCP2_CRYPTO_OPENSSL

View File

@@ -22,10 +22,10 @@ dnl OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
dnl WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. dnl WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
dnl Do not change user variables! dnl Do not change user variables!
dnl http://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html dnl https://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html
AC_PREREQ(2.61) AC_PREREQ(2.61)
AC_INIT([nghttp2], [1.45.0-DEV], [t-tujikawa@users.sourceforge.net]) AC_INIT([nghttp2], [1.47.0-DEV], [t-tujikawa@users.sourceforge.net])
AC_CONFIG_AUX_DIR([.]) AC_CONFIG_AUX_DIR([.])
AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_HEADERS([config.h]) AC_CONFIG_HEADERS([config.h])
@@ -43,10 +43,10 @@ AM_INIT_AUTOMAKE([subdir-objects])
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
dnl See versioning rule: dnl See versioning rule:
dnl http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html dnl https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
AC_SUBST(LT_CURRENT, 34) AC_SUBST(LT_CURRENT, 35)
AC_SUBST(LT_REVISION, 2) AC_SUBST(LT_REVISION, 1)
AC_SUBST(LT_AGE, 20) AC_SUBST(LT_AGE, 21)
major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"` major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"`
minor=`echo $PACKAGE_VERSION |cut -d. -f2 | sed -e "s/[^0-9]//g"` minor=`echo $PACKAGE_VERSION |cut -d. -f2 | sed -e "s/[^0-9]//g"`
@@ -107,6 +107,11 @@ AC_ARG_ENABLE([lib-only],
[Build libnghttp2 only. This is a short hand for --disable-app --disable-examples --disable-hpack-tools --disable-python-bindings])], [Build libnghttp2 only. This is a short hand for --disable-app --disable-examples --disable-hpack-tools --disable-python-bindings])],
[request_lib_only=$enableval], [request_lib_only=no]) [request_lib_only=$enableval], [request_lib_only=no])
AC_ARG_ENABLE([http3],
[AS_HELP_STRING([--enable-http3],
[(EXPERIMENTAL) Enable HTTP/3. This requires ngtcp2, nghttp3, and a custom OpenSSL.])],
[request_http3=$enableval], [request_http3=no])
AC_ARG_WITH([libxml2], AC_ARG_WITH([libxml2],
[AS_HELP_STRING([--with-libxml2], [AS_HELP_STRING([--with-libxml2],
[Use libxml2 [default=check]])], [Use libxml2 [default=check]])],
@@ -172,6 +177,21 @@ AC_ARG_WITH([cython],
[Use cython in given PATH])], [Use cython in given PATH])],
[cython_path=$withval], []) [cython_path=$withval], [])
AC_ARG_WITH([libngtcp2],
[AS_HELP_STRING([--with-libngtcp2],
[Use libngtcp2 [default=check]])],
[request_libngtcp2=$withval], [request_libngtcp2=check])
AC_ARG_WITH([libnghttp3],
[AS_HELP_STRING([--with-libnghttp3],
[Use libnghttp3 [default=check]])],
[request_libnghttp3=$withval], [request_libnghttp3=check])
AC_ARG_WITH([libbpf],
[AS_HELP_STRING([--with-libbpf],
[Use libbpf [default=no]])],
[request_libbpf=$withval], [request_libbpf=no])
dnl Define variables dnl Define variables
AC_ARG_VAR([CYTHON], [the Cython executable]) AC_ARG_VAR([CYTHON], [the Cython executable])
@@ -185,6 +205,8 @@ AC_ARG_VAR([JEMALLOC_LIBS], [linker flags for jemalloc, skipping any checks])
AC_ARG_VAR([LIBTOOL_LDFLAGS], AC_ARG_VAR([LIBTOOL_LDFLAGS],
[libtool specific flags (e.g., -static-libtool-libs)]) [libtool specific flags (e.g., -static-libtool-libs)])
AC_ARG_VAR([BPFCFLAGS], [C compiler flags for bpf program])
dnl Checks for programs dnl Checks for programs
AC_PROG_CC AC_PROG_CC
AC_PROG_CXX AC_PROG_CXX
@@ -198,6 +220,11 @@ PKG_PROG_PKG_CONFIG([0.20])
AM_PATH_PYTHON([3.8],, [:]) AM_PATH_PYTHON([3.8],, [:])
if test "x$request_python_bindings" = "xyes" &&
test "x$PYTHON" = "x:"; then
AC_MSG_ERROR([python was requested (enable-python-bindings) but not found])
fi
if [test "x$request_lib_only" = "xyes"]; then if [test "x$request_lib_only" = "xyes"]; then
request_app=no request_app=no
request_hpack_tools=no request_hpack_tools=no
@@ -205,8 +232,10 @@ if [test "x$request_lib_only" = "xyes"]; then
request_python_bindings=no request_python_bindings=no
fi fi
if [test "x$request_python_bindings" != "xno"]; then if test "x$request_python_bindings" != "xno" &&
AX_PYTHON_DEVEL([>= '3.8']) test "x$PYTHON" != "x:"; then
# version check is broken
AX_PYTHON_DEVEL()
fi fi
if test "x${cython_path}" = "x"; then if test "x${cython_path}" = "x"; then
@@ -245,6 +274,7 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
]], ]],
[[ [[
std::vector<std::future<int>> v; std::vector<std::future<int>> v;
(void)v;
]])], ]])],
[AC_DEFINE([HAVE_STD_FUTURE], [1], [AC_DEFINE([HAVE_STD_FUTURE], [1],
[Define to 1 if you have the `std::future`.]) [Define to 1 if you have the `std::future`.])
@@ -319,7 +349,7 @@ APPLDFLAGS=
case "$host_os" in case "$host_os" in
*android*) *android*)
android_build=yes android_build=yes
# android does not need -pthread, but needs followng 3 libs for C++ # android does not need -pthread, but needs following 3 libs for C++
APPLDFLAGS="$APPLDFLAGS -lstdc++ -latomic -lsupc++" APPLDFLAGS="$APPLDFLAGS -lstdc++ -latomic -lsupc++"
;; ;;
*) *)
@@ -334,6 +364,13 @@ case "$host_os" in
;; ;;
esac esac
case "${build}" in
*-apple-darwin*)
EXTRA_DEFS="-D__APPLE_USE_RFC_3542"
AC_SUBST([EXTRA_DEFS])
;;
esac
# zlib # zlib
have_zlib=no have_zlib=no
if test "x${request_zlib}" != "xno"; then if test "x${request_zlib}" != "xno"; then
@@ -431,6 +468,37 @@ if test "x${request_openssl}" != "xno"; then
[have_openssl=yes], [have_openssl=no]) [have_openssl=yes], [have_openssl=no])
if test "x${have_openssl}" = "xno"; then if test "x${have_openssl}" = "xno"; then
AC_MSG_NOTICE($OPENSSL_PKG_ERRORS) AC_MSG_NOTICE($OPENSSL_PKG_ERRORS)
else
save_CFLAGS="$CFLAGS"
save_LIBS="$LIBS"
CFLAGS="$OPENSSL_CFLAGS $CFLAGS"
LIBS="$OPENSSL_LIBS $LIBS"
# quictls/openssl has SSL_is_quic.
have_ssl_is_quic=no
AC_MSG_CHECKING([for SSL_is_quic])
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
#include <openssl/ssl.h>
]], [[
SSL *ssl = NULL;
SSL_is_quic(ssl);
]])],
[AC_MSG_RESULT([yes]); have_ssl_is_quic=yes],
[AC_MSG_RESULT([no]); have_ssl_is_quic=no])
# boringssl has SSL_set_quic_early_data_context.
AC_MSG_CHECKING([for SSL_set_quic_early_data_context])
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
#include <openssl/ssl.h>
]], [[
SSL *ssl = NULL;
SSL_set_quic_early_data_context(ssl, NULL, 0);
]])],
[AC_MSG_RESULT([yes]); have_boringssl_quic=yes],
[AC_MSG_RESULT([no]); have_boringssl_quic=no])
CFLAGS="$save_CFLAGS"
LIBS="$save_LIBS"
fi fi
fi fi
@@ -454,6 +522,125 @@ if test "x${request_libcares}" = "xyes" &&
AC_MSG_ERROR([libcares was requested (--with-libcares) but not found]) AC_MSG_ERROR([libcares was requested (--with-libcares) but not found])
fi fi
# ngtcp2 (for src)
have_libngtcp2=no
if test "x${request_libngtcp2}" != "xno"; then
PKG_CHECK_MODULES([LIBNGTCP2], [libngtcp2 >= 0.1.0], [have_libngtcp2=yes],
[have_libngtcp2=no])
if test "x${have_libngtcp2}" = "xno"; then
AC_MSG_NOTICE($LIBNGTCP2_PKG_ERRORS)
fi
fi
if test "x${request_libngtcp2}" = "xyes" &&
test "x${have_libngtcp2}" != "xyes"; then
AC_MSG_ERROR([libngtcp2 was requested (--with-libngtcp2) but not found])
fi
# ngtcp2_crypto_openssl (for src)
have_libngtcp2_crypto_openssl=no
if test "x${have_ssl_is_quic}" = "xyes" &&
test "x${request_libngtcp2}" != "xno"; then
PKG_CHECK_MODULES([LIBNGTCP2_CRYPTO_OPENSSL],
[libngtcp2_crypto_openssl >= 0.0.0],
[have_libngtcp2_crypto_openssl=yes],
[have_libngtcp2_crypto_openssl=no])
if test "x${have_libngtcp2_crypto_openssl}" = "xno"; then
AC_MSG_NOTICE($LIBNGTCP2_CRYPTO_OPENSSL_PKG_ERRORS)
else
AC_DEFINE([HAVE_LIBNGTCP2_CRYPTO_OPENSSL], [1],
[Define to 1 if you have `libngtcp2_crypto_openssl` library.])
fi
fi
if test "x${have_ssl_is_quic}" = "xyes" &&
test "x${request_libngtcp2}" = "xyes" &&
test "x${have_libngtcp2_crypto_openssl}" != "xyes"; then
AC_MSG_ERROR([libngtcp2_crypto_openssl was requested (--with-libngtcp2) but not found])
fi
# ngtcp2_crypto_boringssl (for src)
have_libngtcp2_crypto_boringssl=no
if test "x${have_boringssl_quic}" = "xyes" &&
test "x${request_libngtcp2}" != "xno"; then
PKG_CHECK_MODULES([LIBNGTCP2_CRYPTO_BORINGSSL],
[libngtcp2_crypto_boringssl >= 0.0.0],
[have_libngtcp2_crypto_boringssl=yes],
[have_libngtcp2_crypto_boringssl=no])
if test "x${have_libngtcp2_crypto_boringssl}" = "xno"; then
AC_MSG_NOTICE($LIBNGTCP2_CRYPTO_BORINGSSL_PKG_ERRORS)
else
AC_DEFINE([HAVE_LIBNGTCP2_CRYPTO_BORINGSSL], [1],
[Define to 1 if you have `libngtcp2_crypto_boringssl` library.])
fi
fi
if test "x${have_boringssl_quic}" = "xyes" &&
test "x${request_libngtcp2}" = "xyes" &&
test "x${have_libngtcp2_crypto_boringssl}" != "xyes"; then
AC_MSG_ERROR([libngtcp2_crypto_boringssl was requested (--with-libngtcp2) but not found])
fi
# nghttp3 (for src)
have_libnghttp3=no
if test "x${request_libnghttp3}" != "xno"; then
PKG_CHECK_MODULES([LIBNGHTTP3], [libnghttp3 >= 0.1.0], [have_libnghttp3=yes],
[have_libnghttp3=no])
if test "x${have_libnghttp3}" = "xno"; then
AC_MSG_NOTICE($LIBNGHTTP3_PKG_ERRORS)
fi
fi
if test "x${request_libnghttp3}" = "xyes" &&
test "x${have_libnghttp3}" != "xyes"; then
AC_MSG_ERROR([libnghttp3 was requested (--with-libnghttp3) but not found])
fi
# libbpf (for src)
have_libbpf=no
if test "x${request_libbpf}" != "xno"; then
PKG_CHECK_MODULES([LIBBPF], [libbpf >= 0.4.0], [have_libbpf=yes],
[have_libbpf=no])
if test "x${have_libbpf}" = "xyes"; then
AC_DEFINE([HAVE_LIBBPF], [1], [Define to 1 if you have `libbpf` library.])
if test "x${BPFCFLAGS}" = "x"; then
BPFCFLAGS="-Wall -O2 -g"
fi
# Add the include path for Debian
EXTRABPFCFLAGS="-I/usr/include/$host_cpu-$host_os"
AC_SUBST([EXTRABPFCFLAGS])
AC_MSG_CHECKING([whether enum bpf_stats_type is defined in linux/bpf.h])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
[[
#include <linux/bpf.h>
]],
[[
enum bpf_stats_type foo;
(void)foo;
]])],
[have_bpf_stats_type=yes],
[have_bpf_stats_type=no])
if test "x${have_bpf_stats_type}" = "xyes"; then
AC_MSG_RESULT([yes])
AC_DEFINE([HAVE_BPF_STATS_TYPE], [1],
[Define to 1 if you have enum bpf_stats_type in linux/bpf.h.])
else
AC_MSG_RESULT([no])
fi
else
AC_MSG_NOTICE($LIBBPF_PKG_ERRORS)
fi
fi
if test "x${request_libbpf}" = "xyes" &&
test "x${have_libbpf}" != "xyes"; then
AC_MSG_ERROR([libbpf was requested (--with-libbpf) but not found])
fi
AM_CONDITIONAL([HAVE_LIBBPF], [ test "x${have_libbpf}" = "xyes" ])
# libevent_openssl (for examples) # libevent_openssl (for examples)
# 2.0.8 is required because we use evconnlistener_set_error_cb() # 2.0.8 is required because we use evconnlistener_set_error_cb()
have_libevent_openssl=no have_libevent_openssl=no
@@ -598,6 +785,26 @@ fi
AM_CONDITIONAL([ENABLE_APP], [ test "x${enable_app}" = "xyes" ]) AM_CONDITIONAL([ENABLE_APP], [ test "x${enable_app}" = "xyes" ])
# Check HTTP/3 support
enable_http3=no
if test "x${request_http3}" != "xno" &&
(test "x${have_ssl_is_quic}" = "xyes" ||
test "x${have_boringssl_quic}" = "xyes") &&
test "x${have_libngtcp2}" = "xyes" &&
(test "x${have_libngtcp2_crypto_openssl}" = "xyes" ||
test "x${have_libngtcp2_crypto_boringssl}" = "xyes") &&
test "x${have_libnghttp3}" = "xyes"; then
enable_http3=yes
AC_DEFINE([ENABLE_HTTP3], [1], [Define to 1 if HTTP/3 is enabled.])
fi
if test "x${request_http3}" = "xyes" &&
test "x${enable_http3}" != "xyes"; then
AC_MSG_ERROR([HTTP/3 was requested (--enable-http3) but dependencies are not met.])
fi
AM_CONDITIONAL([ENABLE_HTTP3], [ test "x${enable_http3}" = "xyes" ])
enable_hpack_tools=no enable_hpack_tools=no
# HPACK tools requires jansson # HPACK tools requires jansson
if test "x${request_hpack_tools}" != "xno" && if test "x${request_hpack_tools}" != "xno" &&
@@ -939,6 +1146,7 @@ AC_CONFIG_FILES([
src/Makefile src/Makefile
src/includes/Makefile src/includes/Makefile
src/libnghttp2_asio.pc src/libnghttp2_asio.pc
bpf/Makefile
examples/Makefile examples/Makefile
python/Makefile python/Makefile
python/setup.py python/setup.py
@@ -990,7 +1198,11 @@ AC_MSG_NOTICE([summary of build options:
WARNCXXFLAGS: ${WARNCXXFLAGS} WARNCXXFLAGS: ${WARNCXXFLAGS}
CXX1XCXXFLAGS: ${CXX1XCXXFLAGS} CXX1XCXXFLAGS: ${CXX1XCXXFLAGS}
EXTRACFLAG: ${EXTRACFLAG} EXTRACFLAG: ${EXTRACFLAG}
BPFCFLAGS: ${BPFCFLAGS}
EXTRABPFCFLAGS: ${EXTRABPFCFLAGS}
LIBS: ${LIBS} LIBS: ${LIBS}
DEFS: ${DEFS}
EXTRA_DEFS: ${EXTRA_DEFS}
Library: Library:
Shared: ${enable_shared} Shared: ${enable_shared}
Static: ${enable_static} Static: ${enable_static}
@@ -1011,6 +1223,11 @@ AC_MSG_NOTICE([summary of build options:
Libxml2: ${have_libxml2} (CFLAGS='${LIBXML2_CFLAGS}' LIBS='${LIBXML2_LIBS}') Libxml2: ${have_libxml2} (CFLAGS='${LIBXML2_CFLAGS}' LIBS='${LIBXML2_LIBS}')
Libev: ${have_libev} (CFLAGS='${LIBEV_CFLAGS}' LIBS='${LIBEV_LIBS}') Libev: ${have_libev} (CFLAGS='${LIBEV_CFLAGS}' LIBS='${LIBEV_LIBS}')
Libc-ares: ${have_libcares} (CFLAGS='${LIBCARES_CFLAGS}' LIBS='${LIBCARES_LIBS}') Libc-ares: ${have_libcares} (CFLAGS='${LIBCARES_CFLAGS}' LIBS='${LIBCARES_LIBS}')
libngtcp2: ${have_libngtcp2} (CFLAGS='${LIBNGTCP2_CFLAGS}' LIBS='${LIBNGTCP2_LIBS}')
libngtcp2_crypto_openssl: ${have_libngtcp2_crypto_openssl} (CFLAGS='${LIBNGTCP2_CRYPTO_OPENSSL_CFLAGS}' LIBS='${LIBNGTCP2_CRYPTO_OPENSSL_LIBS}')
libngtcp2_crypto_boringssl: ${have_libngtcp2_crypto_boringssl} (CFLAGS='${LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS}' LIBS='${LIBNGTCP2_CRYPTO_BORINGSSL_LIBS}')
libnghttp3: ${have_libnghttp3} (CFLAGS='${LIBNGHTTP3_CFLAGS}' LIBS='${LIBNGHTTP3_LIBS}')
libbpf: ${have_libbpf} (CFLAGS='${LIBBPF_CFLAGS}' LIBS='${LIBBPF_LIBS}')
Libevent(SSL): ${have_libevent_openssl} (CFLAGS='${LIBEVENT_OPENSSL_CFLAGS}' LIBS='${LIBEVENT_OPENSSL_LIBS}') Libevent(SSL): ${have_libevent_openssl} (CFLAGS='${LIBEVENT_OPENSSL_CFLAGS}' LIBS='${LIBEVENT_OPENSSL_LIBS}')
Jansson: ${have_jansson} (CFLAGS='${JANSSON_CFLAGS}' LIBS='${JANSSON_LIBS}') Jansson: ${have_jansson} (CFLAGS='${JANSSON_CFLAGS}' LIBS='${JANSSON_LIBS}')
Jemalloc: ${have_jemalloc} (CFLAGS='${JEMALLOC_CFLAGS}' LIBS='${JEMALLOC_LIBS}') Jemalloc: ${have_jemalloc} (CFLAGS='${JEMALLOC_CFLAGS}' LIBS='${JEMALLOC_LIBS}')
@@ -1032,4 +1249,5 @@ AC_MSG_NOTICE([summary of build options:
Examples: ${enable_examples} Examples: ${enable_examples}
Python bindings:${enable_python_bindings} Python bindings:${enable_python_bindings}
Threading: ${enable_threads} Threading: ${enable_threads}
HTTP/3 (EXPERIMENTAL): ${enable_http3}
]) ])

View File

@@ -184,9 +184,9 @@ set(EXTRA_DIST
sources/python-apiref.rst sources/python-apiref.rst
sources/building-android-binary.rst sources/building-android-binary.rst
sources/contribute.rst sources/contribute.rst
_exts/sphinxcontrib/LICENSE.rubydomain _exts/rubydomain/LICENSE.rubydomain
_exts/sphinxcontrib/__init__.py _exts/rubydomain/__init__.py
_exts/sphinxcontrib/rubydomain.py _exts/rubydomain/rubydomain.py
_themes/sphinx_rtd_theme/__init__.py _themes/sphinx_rtd_theme/__init__.py
_themes/sphinx_rtd_theme/breadcrumbs.html _themes/sphinx_rtd_theme/breadcrumbs.html
_themes/sphinx_rtd_theme/footer.html _themes/sphinx_rtd_theme/footer.html

View File

@@ -30,6 +30,8 @@ APIDOCS= \
nghttp2_check_authority.rst \ nghttp2_check_authority.rst \
nghttp2_check_header_name.rst \ nghttp2_check_header_name.rst \
nghttp2_check_header_value.rst \ nghttp2_check_header_value.rst \
nghttp2_check_method.rst \
nghttp2_check_path.rst \
nghttp2_hd_deflate_bound.rst \ nghttp2_hd_deflate_bound.rst \
nghttp2_hd_deflate_change_table_size.rst \ nghttp2_hd_deflate_change_table_size.rst \
nghttp2_hd_deflate_del.rst \ nghttp2_hd_deflate_del.rst \
@@ -204,9 +206,9 @@ EXTRA_DIST = \
sources/building-android-binary.rst \ sources/building-android-binary.rst \
sources/contribute.rst \ sources/contribute.rst \
sources/security.rst \ sources/security.rst \
_exts/sphinxcontrib/LICENSE.rubydomain \ _exts/rubydomain/LICENSE.rubydomain \
_exts/sphinxcontrib/__init__.py \ _exts/rubydomain/__init__.py \
_exts/sphinxcontrib/rubydomain.py \ _exts/rubydomain/rubydomain.py \
_themes/sphinx_rtd_theme/__init__.py \ _themes/sphinx_rtd_theme/__init__.py \
_themes/sphinx_rtd_theme/breadcrumbs.html \ _themes/sphinx_rtd_theme/breadcrumbs.html \
_themes/sphinx_rtd_theme/footer.html \ _themes/sphinx_rtd_theme/footer.html \
@@ -270,7 +272,7 @@ EXTRA_DIST = \
# You can set these variables from the command line. # You can set these variables from the command line.
SPHINXOPTS = SPHINXOPTS =
SPHINXBUILD = sphinx-build SPHINXBUILD ?= sphinx-build
PAPER = PAPER =
BUILDDIR = manual BUILDDIR = manual

View File

@@ -493,7 +493,7 @@ class RubyModuleIndex(Index):
# list of all modules, sorted by module name # list of all modules, sorted by module name
modules = sorted(_iteritems(self.domain.data['modules']), modules = sorted(_iteritems(self.domain.data['modules']),
key=lambda x: x[0].lower()) key=lambda x: x[0].lower())
# sort out collapsable modules # sort out collapsible modules
prev_modname = '' prev_modname = ''
num_toplevels = 0 num_toplevels = 0
for modname, (docname, synopsis, platforms, deprecated) in modules: for modname, (docname, synopsis, platforms, deprecated) in modules:

View File

@@ -8,7 +8,7 @@ _h2load()
_get_comp_words_by_ref cur prev _get_comp_words_by_ref cur prev
case $cur in case $cur in
-*) -*)
COMPREPLY=( $( compgen -W '--connection-window-bits --clients --verbose --ciphers --rate --no-tls-proto --connect-to --header-table-size --requests --log-file --base-uri --h1 --threads --npn-list --rate-period --rps --data --version --connection-inactivity-timeout --timing-script-file --encoder-header-table-size --max-concurrent-streams --connection-active-timeout --input-file --help --window-bits --warm-up-time --duration --header ' -- "$cur" ) ) COMPREPLY=( $( compgen -W '--requests --clients --threads --input-file --max-concurrent-streams --window-bits --connection-window-bits --header --ciphers --tls13-ciphers --no-tls-proto --data --rate --rate-period --duration --warm-up-time --connection-active-timeout --connection-inactivity-timeout --timing-script-file --base-uri --npn-list --h1 --header-table-size --encoder-header-table-size --log-file --qlog-file-base --connect-to --rps --groups --no-udp-gso --max-udp-payload-size --verbose --version --help ' -- "$cur" ) )
;; ;;
*) *)
_filedir _filedir

View File

@@ -1,8 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals
from __future__ import print_function
import subprocess import subprocess
import io import io
import re import re

View File

@@ -8,7 +8,7 @@ _nghttp()
_get_comp_words_by_ref cur prev _get_comp_words_by_ref cur prev
case $cur in case $cur in
-*) -*)
COMPREPLY=( $( compgen -W '--no-push --verbose --no-dep --get-assets --har --header-table-size --multiply --encoder-header-table-size --padding --hexdump --max-concurrent-streams --continuation --connection-window-bits --peer-max-concurrent-streams --timeout --data --no-content-length --version --color --cert --upgrade --remote-name --trailer --weight --help --key --null-out --window-bits --expect-continue --stat --no-verify-peer --header ' -- "$cur" ) ) COMPREPLY=( $( compgen -W '--verbose --null-out --remote-name --timeout --window-bits --connection-window-bits --get-assets --stat --header --trailer --cert --key --data --multiply --upgrade --weight --peer-max-concurrent-streams --header-table-size --encoder-header-table-size --padding --har --color --continuation --no-content-length --no-dep --hexdump --no-push --max-concurrent-streams --expect-continue --no-verify-peer --version --help ' -- "$cur" ) )
;; ;;
*) *)
_filedir _filedir

View File

@@ -8,7 +8,7 @@ _nghttpd()
_get_comp_words_by_ref cur prev _get_comp_words_by_ref cur prev
case $cur in case $cur in
-*) -*)
COMPREPLY=( $( compgen -W '--htdocs --verbose --daemon --echo-upload --error-gzip --push --header-table-size --encoder-header-table-size --padding --hexdump --max-concurrent-streams --no-tls --connection-window-bits --mime-types-file --no-content-length --workers --version --color --early-response --dh-param-file --trailer --address --window-bits --verify-client --help ' -- "$cur" ) ) COMPREPLY=( $( compgen -W '--address --daemon --verify-client --htdocs --verbose --no-tls --header-table-size --encoder-header-table-size --color --push --padding --max-concurrent-streams --workers --error-gzip --window-bits --connection-window-bits --dh-param-file --early-response --trailer --hexdump --echo-upload --mime-types-file --no-content-length --version --help ' -- "$cur" ) )
;; ;;
*) *)
_filedir _filedir

View File

@@ -8,7 +8,7 @@ _nghttpx()
_get_comp_words_by_ref cur prev _get_comp_words_by_ref cur prev
case $cur in case $cur in
-*) -*)
COMPREPLY=( $( compgen -W '--worker-read-rate --include --frontend-http2-dump-response-header --tls-ticket-key-file --verify-client-cacert --max-response-header-fields --backend-http2-window-size --tls13-client-ciphers --frontend-keep-alive-timeout --backend-request-buffer --max-request-header-fields --backend-connect-timeout --tls-max-proto-version --conf --dns-lookup-timeout --backend-http2-max-concurrent-streams --worker-write-burst --npn-list --dns-max-try --fetch-ocsp-response-file --tls-session-cache-memcached-cert-file --no-via --mruby-file --no-server-push --stream-read-timeout --client-ciphers --ocsp-update-interval --forwarded-for --accesslog-syslog --dns-cache-timeout --frontend-http2-read-timeout --listener-disable-timeout --ciphers --client-psk-secrets --strip-incoming-x-forwarded-for --no-server-rewrite --private-key-passwd-file --backend-keep-alive-timeout --backend-http-proxy-uri --frontend-max-requests --tls-no-postpone-early-data --rlimit-nofile --no-strip-incoming-x-forwarded-proto --tls-ticket-key-memcached-cert-file --no-verify-ocsp --forwarded-by --tls-session-cache-memcached-private-key-file --error-page --ocsp-startup --backend-write-timeout --tls-dyn-rec-warmup-threshold --frontend-http2-window-size --tls-ticket-key-memcached-max-retry --http2-no-cookie-crumbling --worker-read-burst --dh-param-file --accesslog-format --errorlog-syslog --redirect-https-port --request-header-field-buffer --api-max-request-body --frontend-http2-decoder-dynamic-table-size --errorlog-file --frontend-http2-max-concurrent-streams --psk-secrets --frontend-write-timeout --tls-ticket-key-cipher --read-burst --no-add-x-forwarded-proto --backend --server-name --insecure --backend-max-backoff --log-level --host-rewrite --tls-ticket-key-memcached-interval --frontend-http2-setting-timeout --frontend-http2-connection-window-size --worker-frontend-connections --syslog-facility --fastopen --no-location-rewrite --single-thread --no-http2-cipher-block-list --tls-session-cache-memcached --no-ocsp --backend-response-buffer --tls-min-proto-version --workers --add-x-forwarded-for --add-forwarded --worker-write-rate --add-request-header --backend-http2-settings-timeout --subcert --ignore-per-pattern-mruby-error --ecdh-curves --no-kqueue --help --frontend-frame-debug --tls-sct-dir --pid-file --frontend-http2-dump-request-header --daemon --write-rate --altsvc --backend-http2-decoder-dynamic-table-size --no-strip-incoming-early-data --user --verify-client-tolerate-expired --frontend-read-timeout --tls-ticket-key-memcached-max-fail --backlog --write-burst --backend-connections-per-host --tls-max-early-data --response-header-field-buffer --tls-ticket-key-memcached-address-family --padding --tls-session-cache-memcached-address-family --stream-write-timeout --cacert --tls-ticket-key-memcached-private-key-file --accesslog-write-early --backend-address-family --backend-http2-connection-window-size --tls13-ciphers --client-no-http2-cipher-block-list --version --add-response-header --backend-read-timeout --frontend-http2-optimize-window-size --frontend --accesslog-file --http2-proxy --backend-http2-encoder-dynamic-table-size --client-private-key-file --single-process --client-cert-file --tls-ticket-key-memcached --tls-dyn-rec-idle-timeout --frontend-http2-optimize-write-buffer-size --verify-client --frontend-http2-encoder-dynamic-table-size --read-rate --backend-connections-per-frontend --strip-incoming-forwarded ' -- "$cur" ) ) COMPREPLY=( $( compgen -W '--backend --frontend --backlog --backend-address-family --backend-http-proxy-uri --workers --single-thread --read-rate --read-burst --write-rate --write-burst --worker-read-rate --worker-read-burst --worker-write-rate --worker-write-burst --worker-frontend-connections --backend-connections-per-host --backend-connections-per-frontend --rlimit-nofile --rlimit-memlock --backend-request-buffer --backend-response-buffer --fastopen --no-kqueue --frontend-http2-read-timeout --frontend-http3-read-timeout --frontend-read-timeout --frontend-write-timeout --frontend-keep-alive-timeout --stream-read-timeout --stream-write-timeout --backend-read-timeout --backend-write-timeout --backend-connect-timeout --backend-keep-alive-timeout --listener-disable-timeout --frontend-http2-setting-timeout --backend-http2-settings-timeout --backend-max-backoff --ciphers --tls13-ciphers --client-ciphers --tls13-client-ciphers --ecdh-curves --insecure --cacert --private-key-passwd-file --subcert --dh-param-file --npn-list --verify-client --verify-client-cacert --verify-client-tolerate-expired --client-private-key-file --client-cert-file --tls-min-proto-version --tls-max-proto-version --tls-ticket-key-file --tls-ticket-key-memcached --tls-ticket-key-memcached-address-family --tls-ticket-key-memcached-interval --tls-ticket-key-memcached-max-retry --tls-ticket-key-memcached-max-fail --tls-ticket-key-cipher --tls-ticket-key-memcached-cert-file --tls-ticket-key-memcached-private-key-file --fetch-ocsp-response-file --ocsp-update-interval --ocsp-startup --no-verify-ocsp --no-ocsp --tls-session-cache-memcached --tls-session-cache-memcached-address-family --tls-session-cache-memcached-cert-file --tls-session-cache-memcached-private-key-file --tls-dyn-rec-warmup-threshold --tls-dyn-rec-idle-timeout --no-http2-cipher-block-list --client-no-http2-cipher-block-list --tls-sct-dir --psk-secrets --client-psk-secrets --tls-no-postpone-early-data --tls-max-early-data --frontend-http2-max-concurrent-streams --backend-http2-max-concurrent-streams --frontend-http2-window-size --frontend-http2-connection-window-size --backend-http2-window-size --backend-http2-connection-window-size --http2-no-cookie-crumbling --padding --no-server-push --frontend-http2-optimize-write-buffer-size --frontend-http2-optimize-window-size --frontend-http2-encoder-dynamic-table-size --frontend-http2-decoder-dynamic-table-size --backend-http2-encoder-dynamic-table-size --backend-http2-decoder-dynamic-table-size --http2-proxy --log-level --accesslog-file --accesslog-syslog --accesslog-format --accesslog-write-early --errorlog-file --errorlog-syslog --syslog-facility --add-x-forwarded-for --strip-incoming-x-forwarded-for --no-add-x-forwarded-proto --no-strip-incoming-x-forwarded-proto --add-forwarded --strip-incoming-forwarded --forwarded-by --forwarded-for --no-via --no-strip-incoming-early-data --no-location-rewrite --host-rewrite --altsvc --http2-altsvc --add-request-header --add-response-header --request-header-field-buffer --max-request-header-fields --response-header-field-buffer --max-response-header-fields --error-page --server-name --no-server-rewrite --redirect-https-port --api-max-request-body --dns-cache-timeout --dns-lookup-timeout --dns-max-try --frontend-max-requests --frontend-http2-dump-request-header --frontend-http2-dump-response-header --frontend-frame-debug --daemon --pid-file --user --single-process --max-worker-processes --worker-process-grace-shutdown-period --mruby-file --ignore-per-pattern-mruby-error --frontend-quic-idle-timeout --frontend-quic-debug-log --quic-bpf-program-file --frontend-quic-early-data --frontend-quic-qlog-dir --frontend-quic-require-token --frontend-quic-congestion-controller --frontend-quic-secret-file --quic-server-id --frontend-quic-initial-rtt --no-quic-bpf --frontend-http3-window-size --frontend-http3-connection-window-size --frontend-http3-max-window-size --frontend-http3-max-connection-window-size --frontend-http3-max-concurrent-streams --conf --include --version --help ' -- "$cur" ) )
;; ;;
*) *)
_filedir _filedir

View File

@@ -41,7 +41,7 @@ import sys, os
# documentation root, use os.path.abspath to make it absolute, like shown here. # documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.')) #sys.path.insert(0, os.path.abspath('.'))
sys.path.append(os.path.abspath('@top_srcdir@/doc/_exts')) sys.path.insert(0, os.path.abspath('@top_srcdir@/doc/_exts'))
# -- General configuration ----------------------------------------------------- # -- General configuration -----------------------------------------------------
@@ -50,7 +50,7 @@ sys.path.append(os.path.abspath('@top_srcdir@/doc/_exts'))
# Add any Sphinx extension module names here, as strings. They can be extensions # Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinxcontrib.rubydomain'] extensions = ['rubydomain.rubydomain']
# Add any paths that contain templates here, relative to this directory. # Add any paths that contain templates here, relative to this directory.
templates_path = ['@top_srcdir@/_templates'] templates_path = ['@top_srcdir@/_templates']

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText. .\" Man page generated from reStructuredText.
. .
.TH "H2LOAD" "1" "Jul 18, 2021" "1.45.0-DEV" "nghttp2" .TH "H2LOAD" "1" "Oct 19, 2021" "1.46.0" "nghttp2"
.SH NAME .SH NAME
h2load \- HTTP/2 benchmarking tool h2load \- HTTP/2 benchmarking tool
. .
@@ -101,6 +101,7 @@ Default: \fB1\fP
.TP .TP
.B \-w, \-\-window\-bits=<N> .B \-w, \-\-window\-bits=<N>
Sets the stream level initial window size to (2**<N>)\-1. Sets the stream level initial window size to (2**<N>)\-1.
For QUIC, <N> is capped to 26 (roughly 64MiB).
.sp .sp
Default: \fB30\fP Default: \fB30\fP
.UNINDENT .UNINDENT
@@ -120,13 +121,21 @@ Add/Override a header to the requests.
.INDENT 0.0 .INDENT 0.0
.TP .TP
.B \-\-ciphers=<SUITE> .B \-\-ciphers=<SUITE>
Set allowed cipher list. The format of the string is Set allowed cipher list for TLSv1.2 or ealier. The
described in OpenSSL ciphers(1). format of the string is described in OpenSSL ciphers(1).
.sp .sp
Default: \fBECDHE\-ECDSA\-AES256\-GCM\-SHA384:ECDHE\-RSA\-AES256\-GCM\-SHA384:ECDHE\-ECDSA\-CHACHA20\-POLY1305:ECDHE\-RSA\-CHACHA20\-POLY1305:ECDHE\-ECDSA\-AES128\-GCM\-SHA256:ECDHE\-RSA\-AES128\-GCM\-SHA256:ECDHE\-ECDSA\-AES256\-SHA384:ECDHE\-RSA\-AES256\-SHA384:ECDHE\-ECDSA\-AES128\-SHA256:ECDHE\-RSA\-AES128\-SHA256\fP Default: \fBECDHE\-ECDSA\-AES256\-GCM\-SHA384:ECDHE\-RSA\-AES256\-GCM\-SHA384:ECDHE\-ECDSA\-CHACHA20\-POLY1305:ECDHE\-RSA\-CHACHA20\-POLY1305:ECDHE\-ECDSA\-AES128\-GCM\-SHA256:ECDHE\-RSA\-AES128\-GCM\-SHA256:ECDHE\-ECDSA\-AES256\-SHA384:ECDHE\-RSA\-AES256\-SHA384:ECDHE\-ECDSA\-AES128\-SHA256:ECDHE\-RSA\-AES128\-SHA256\fP
.UNINDENT .UNINDENT
.INDENT 0.0 .INDENT 0.0
.TP .TP
.B \-\-tls13\-ciphers=<SUITE>
Set allowed cipher list for TLSv1.3. The format of the
string is described in OpenSSL ciphers(1).
.sp
Default: \fBTLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_CCM_SHA256\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-p, \-\-no\-tls\-proto=<PROTOID> .B \-p, \-\-no\-tls\-proto=<PROTOID>
Specify ALPN identifier of the protocol to be used when Specify ALPN identifier of the protocol to be used when
accessing http URI without SSL/TLS. accessing http URI without SSL/TLS.
@@ -285,6 +294,16 @@ to buffering. Status code is \-1 for failed streams.
.UNINDENT .UNINDENT
.INDENT 0.0 .INDENT 0.0
.TP .TP
.B \-\-qlog\-file\-base=<PATH>
Enable qlog output and specify base file name for qlogs.
Qlog is emitted for each connection.
For a given base name "base", each output file name
becomes "base.M.N.qlog" where M is worker ID and N is
client ID (e.g. "base.0.3.qlog").
Only effective in QUIC runs.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-connect\-to=<HOST>[:<PORT>] .B \-\-connect\-to=<HOST>[:<PORT>]
Host and port to connect instead of using the authority Host and port to connect instead of using the authority
in <URI>. in <URI>.
@@ -297,6 +316,23 @@ Specify request per second for each client. \fI\%\-\-rps\fP and
.UNINDENT .UNINDENT
.INDENT 0.0 .INDENT 0.0
.TP .TP
.B \-\-groups=<GROUPS>
Specify the supported groups.
.sp
Default: \fBX25519:P\-256:P\-384:P\-521\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-no\-udp\-gso
Disable UDP GSO.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-max\-udp\-payload\-size=<SIZE>
Specify the maximum outgoing UDP datagram payload size.
.UNINDENT
.INDENT 0.0
.TP
.B \-v, \-\-verbose .B \-v, \-\-verbose
Output debug information. Output debug information.
.UNINDENT .UNINDENT

View File

@@ -76,6 +76,7 @@ OPTIONS
.. option:: -w, --window-bits=<N> .. option:: -w, --window-bits=<N>
Sets the stream level initial window size to (2\*\*<N>)-1. Sets the stream level initial window size to (2\*\*<N>)-1.
For QUIC, <N> is capped to 26 (roughly 64MiB).
Default: ``30`` Default: ``30``
@@ -92,11 +93,18 @@ OPTIONS
.. option:: --ciphers=<SUITE> .. option:: --ciphers=<SUITE>
Set allowed cipher list. The format of the string is Set allowed cipher list for TLSv1.2 or ealier. The
described in OpenSSL ciphers(1). format of the string is described in OpenSSL ciphers(1).
Default: ``ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256`` Default: ``ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256``
.. option:: --tls13-ciphers=<SUITE>
Set allowed cipher list for TLSv1.3. The format of the
string is described in OpenSSL ciphers(1).
Default: ``TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_CCM_SHA256``
.. option:: -p, --no-tls-proto=<PROTOID> .. option:: -p, --no-tls-proto=<PROTOID>
Specify ALPN identifier of the protocol to be used when Specify ALPN identifier of the protocol to be used when
@@ -240,6 +248,15 @@ OPTIONS
appear slightly out of order with multiple threads due appear slightly out of order with multiple threads due
to buffering. Status code is -1 for failed streams. to buffering. Status code is -1 for failed streams.
.. option:: --qlog-file-base=<PATH>
Enable qlog output and specify base file name for qlogs.
Qlog is emitted for each connection.
For a given base name "base", each output file name
becomes "base.M.N.qlog" where M is worker ID and N is
client ID (e.g. "base.0.3.qlog").
Only effective in QUIC runs.
.. option:: --connect-to=<HOST>[:<PORT>] .. option:: --connect-to=<HOST>[:<PORT>]
Host and port to connect instead of using the authority Host and port to connect instead of using the authority
@@ -250,6 +267,20 @@ OPTIONS
Specify request per second for each client. :option:`--rps` and Specify request per second for each client. :option:`--rps` and
:option:`--timing-script-file` are mutually exclusive. :option:`--timing-script-file` are mutually exclusive.
.. option:: --groups=<GROUPS>
Specify the supported groups.
Default: ``X25519:P-256:P-384:P-521``
.. option:: --no-udp-gso
Disable UDP GSO.
.. option:: --max-udp-payload-size=<SIZE>
Specify the maximum outgoing UDP datagram payload size.
.. option:: -v, --verbose .. option:: -v, --verbose
Output debug information. Output debug information.

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText. .\" Man page generated from reStructuredText.
. .
.TH "NGHTTP" "1" "Jul 18, 2021" "1.45.0-DEV" "nghttp2" .TH "NGHTTP" "1" "Oct 19, 2021" "1.46.0" "nghttp2"
.SH NAME .SH NAME
nghttp \- HTTP/2 client nghttp \- HTTP/2 client
. .

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText. .\" Man page generated from reStructuredText.
. .
.TH "NGHTTPD" "1" "Jul 18, 2021" "1.45.0-DEV" "nghttp2" .TH "NGHTTPD" "1" "Oct 19, 2021" "1.46.0" "nghttp2"
.SH NAME .SH NAME
nghttpd \- HTTP/2 server nghttpd \- HTTP/2 server
. .

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText. .\" Man page generated from reStructuredText.
. .
.TH "NGHTTPX" "1" "Jul 18, 2021" "1.45.0-DEV" "nghttp2" .TH "NGHTTPX" "1" "Oct 19, 2021" "1.46.0" "nghttp2"
.SH NAME .SH NAME
nghttpx \- HTTP/2 proxy nghttpx \- HTTP/2 proxy
. .
@@ -35,7 +35,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
\fBnghttpx\fP [OPTIONS]... [<PRIVATE_KEY> <CERT>] \fBnghttpx\fP [OPTIONS]... [<PRIVATE_KEY> <CERT>]
.SH DESCRIPTION .SH DESCRIPTION
.sp .sp
A reverse proxy for HTTP/2, and HTTP/1. A reverse proxy for HTTP/3, HTTP/2, and HTTP/1.
.INDENT 0.0 .INDENT 0.0
.TP .TP
.B <PRIVATE_KEY> .B <PRIVATE_KEY>
@@ -140,12 +140,13 @@ parameters are: "proto=<PROTO>", "tls",
"affinity=<METHOD>", "dns", "redirect\-if\-not\-tls", "affinity=<METHOD>", "dns", "redirect\-if\-not\-tls",
"upgrade\-scheme", "mruby=<PATH>", "upgrade\-scheme", "mruby=<PATH>",
"read\-timeout=<DURATION>", "write\-timeout=<DURATION>", "read\-timeout=<DURATION>", "write\-timeout=<DURATION>",
"group=<GROUP>", "group\-weight=<N>", and "weight=<N>". "group=<GROUP>", "group\-weight=<N>", "weight=<N>", and
The parameter consists of keyword, and optionally "dnf". The parameter consists of keyword, and
followed by "=" and value. For example, the parameter optionally followed by "=" and value. For example, the
"proto=h2" consists of the keyword "proto" and value parameter "proto=h2" consists of the keyword "proto" and
"h2". The parameter "tls" consists of the keyword "tls" value "h2". The parameter "tls" consists of the keyword
without value. Each parameter is described as follows. "tls" without value. Each parameter is described as
follows.
.sp .sp
The backend application protocol can be specified using The backend application protocol can be specified using
optional "proto" parameter, and in the form of optional "proto" parameter, and in the form of
@@ -276,6 +277,13 @@ weight than weight 2. If this parameter is omitted,
weight becomes 1. "weight" is ignored if session weight becomes 1. "weight" is ignored if session
affinity is enabled. affinity is enabled.
.sp .sp
If "dnf" parameter is specified, an incoming request is
not forwarded to a backend and just consumed along with
the request body (actually a backend server never be
contacted). It is expected that the HTTP response is
generated by mruby script (see "mruby=<PATH>" parameter
above). "dnf" is an abbreviation of "do not forward".
.sp
Since ";" and ":" are used as delimiter, <PATTERN> must Since ";" and ":" are used as delimiter, <PATTERN> must
not contain these characters. In order to include ":" not contain these characters. In order to include ":"
in <PATTERN>, one has to specify "%3A" (which is in <PATTERN>, one has to specify "%3A" (which is
@@ -323,6 +331,12 @@ To accept PROXY protocol version 1 and 2 on frontend
connection, specify "proxyproto" parameter. This is connection, specify "proxyproto" parameter. This is
disabled by default. disabled by default.
.sp .sp
To receive HTTP/3 (QUIC) traffic, specify "quic"
parameter. It makes nghttpx listen on UDP port rather
than TCP port. UNIX domain socket, "api", and
"healthmon" parameters cannot be used with "quic"
parameter.
.sp
Default: \fB*,3000\fP Default: \fB*,3000\fP
.UNINDENT .UNINDENT
.INDENT 0.0 .INDENT 0.0
@@ -489,6 +503,15 @@ Default: \fB0\fP
.UNINDENT .UNINDENT
.INDENT 0.0 .INDENT 0.0
.TP .TP
.B \-\-rlimit\-memlock=<N>
Set maximum number of bytes of memory that may be locked
into RAM. If 0 is given, nghttpx does not set the
limit.
.sp
Default: \fB0\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-backend\-request\-buffer=<SIZE> .B \-\-backend\-request\-buffer=<SIZE>
Set buffer size used to store backend request. Set buffer size used to store backend request.
.sp .sp
@@ -528,6 +551,13 @@ Default: \fB3m\fP
.UNINDENT .UNINDENT
.INDENT 0.0 .INDENT 0.0
.TP .TP
.B \-\-frontend\-http3\-read\-timeout=<DURATION>
Specify read timeout for HTTP/3 frontend connection.
.sp
Default: \fB3m\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-frontend\-read\-timeout=<DURATION> .B \-\-frontend\-read\-timeout=<DURATION>
Specify read timeout for HTTP/1.1 frontend connection. Specify read timeout for HTTP/1.1 frontend connection.
.sp .sp
@@ -1068,12 +1098,13 @@ option. But be aware its implications.
.INDENT 0.0 .INDENT 0.0
.TP .TP
.B \-\-tls\-no\-postpone\-early\-data .B \-\-tls\-no\-postpone\-early\-data
By default, nghttpx postpones forwarding HTTP requests By default, except for QUIC connections, nghttpx
sent in early data, including those sent in partially in postpones forwarding HTTP requests sent in early data,
it, until TLS handshake finishes. If all backend server including those sent in partially in it, until TLS
recognizes "Early\-Data" header field, using this option handshake finishes. If all backend server recognizes
makes nghttpx not postpone forwarding request and get "Early\-Data" header field, using this option makes
full potential of 0\-RTT data. nghttpx not postpone forwarding request and get full
potential of 0\-RTT data.
.UNINDENT .UNINDENT
.INDENT 0.0 .INDENT 0.0
.TP .TP
@@ -1478,13 +1509,21 @@ not be altered regardless of this option.
.UNINDENT .UNINDENT
.INDENT 0.0 .INDENT 0.0
.TP .TP
.B \-\-altsvc=<PROTOID,PORT[,HOST,[ORIGIN]]> .B \-\-altsvc=<PROTOID,PORT[,HOST,[ORIGIN[,PARAMS]]]>
Specify protocol ID, port, host and origin of Specify protocol ID, port, host and origin of
alternative service. <HOST> and <ORIGIN> are optional. alternative service. <HOST>, <ORIGIN> and <PARAMS> are
They are advertised in alt\-svc header field only in optional. Empty <HOST> and <ORIGIN> are allowed and
HTTP/1.1 frontend. This option can be used multiple they are treated as nothing is specified. They are
times to specify multiple alternative services. advertised in alt\-svc header field only in HTTP/1.1
Example: \fI\%\-\-altsvc\fP=h2,443 frontend. This option can be used multiple times to
specify multiple alternative services.
Example: \fI\%\-\-altsvc\fP="h2,443,,,ma=3600; persist=1"
.UNINDENT
.INDENT 0.0
.TP
.B \-\-http2\-altsvc=<PROTOID,PORT[,HOST,[ORIGIN[,PARAMS]]]>
Just like \fI\%\-\-altsvc\fP option, but this altsvc is only sent
in HTTP/2 frontend.
.UNINDENT .UNINDENT
.INDENT 0.0 .INDENT 0.0
.TP .TP
@@ -1675,6 +1714,33 @@ process. nghttpx still spawns additional process if
neverbleed is used. In the single process mode, the neverbleed is used. In the single process mode, the
signal handling feature is disabled. signal handling feature is disabled.
.UNINDENT .UNINDENT
.INDENT 0.0
.TP
.B \-\-max\-worker\-processes=<N>
The maximum number of worker processes. nghttpx spawns
new worker process when it reloads its configuration.
The previous worker process enters graceful termination
period and will terminate when it finishes handling the
existing connections. However, if reloading
configurations happen very frequently, the worker
processes might be piled up if they take a bit long time
to finish the existing connections. With this option,
if the number of worker processes exceeds the given
value, the oldest worker process is terminated
immediately. Specifying 0 means no limit and it is the
default behaviour.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-worker\-process\-grace\-shutdown\-period=<DURATION>
Maximum period for a worker process to terminate
gracefully. When a worker process enters in graceful
shutdown period (e.g., when nghttpx reloads its
configuration) and it does not finish handling the
existing connections in the given period of time, it is
immediately terminated. Specifying 0 means no limit and
it is the default behaviour.
.UNINDENT
.SS Scripting .SS Scripting
.INDENT 0.0 .INDENT 0.0
.TP .TP
@@ -1688,6 +1754,160 @@ Ignore mruby compile error for per\-pattern mruby script
file. If error occurred, it is treated as if no mruby file. If error occurred, it is treated as if no mruby
file were specified for the pattern. file were specified for the pattern.
.UNINDENT .UNINDENT
.SS HTTP/3 and QUIC
.INDENT 0.0
.TP
.B \-\-frontend\-quic\-idle\-timeout=<DURATION>
Specify an idle timeout for QUIC connection.
.sp
Default: \fB30s\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-frontend\-quic\-debug\-log
Output QUIC debug log to \fI/dev/stderr.\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-quic\-bpf\-program\-file=<PATH>
Specify a path to eBPF program file reuseport_kern.o to
direct an incoming QUIC UDP datagram to a correct
socket.
.sp
Default: \fB/usr/local/lib/nghttp2/reuseport_kern.o\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-frontend\-quic\-early\-data
Enable early data on frontend QUIC connections. nghttpx
sends "Early\-Data" header field to a backend server if a
request is received in early data and handshake has not
finished. All backend servers should deal with possibly
replayed requests.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-frontend\-quic\-qlog\-dir=<DIR>
Specify a directory where a qlog file is written for
frontend QUIC connections. A qlog file is created per
each QUIC connection. The file name is ISO8601 basic
format, followed by "\-", server Source Connection ID and
".qlog".
.UNINDENT
.INDENT 0.0
.TP
.B \-\-frontend\-quic\-require\-token
Require an address validation token for a frontend QUIC
connection. Server sends a token in Retry packet or
NEW_TOKEN frame in the previous connection.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-frontend\-quic\-congestion\-controller=<CC>
Specify a congestion controller algorithm for a frontend
QUIC connection. <CC> should be either "cubic" or
"bbr".
.sp
Default: \fBcubic\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-frontend\-quic\-secret\-file=<PATH>
Path to file that contains secure random data to be used
as QUIC keying materials. It is used to derive keys for
encrypting tokens and Connection IDs. It is not used to
encrypt QUIC packets. Each line of this file must
contain exactly 136 bytes hex\-encoded string (when
decoded the byte string is 68 bytes long). The first 2
bits of decoded byte string are used to identify the
keying material. An empty line or a line which starts
\(aq#\(aq is ignored. The file can contain more than one
keying materials. Because the identifier is 2 bits, at
most 4 keying materials are read and the remaining data
is discarded. The first keying material in the file is
primarily used for encryption and decryption for new
connection. The other ones are used to decrypt data for
the existing connections. Specifying multiple keying
materials enables key rotation. Please note that key
rotation does not occur automatically. User should
update files or change options values and restart
nghttpx gracefully. If opening or reading given file
fails, all loaded keying materials are discarded and it
is treated as if none of this option is given. If this
option is not given or an error occurred while opening
or reading a file, a keying material is generated
internally on startup and reload.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-quic\-server\-id=<HEXSTRING>
Specify server ID encoded in Connection ID to identify
this particular server instance. Connection ID is
encrypted and this part is not visible in public. It
must be 4 bytes long and must be encoded in hex string
(which is 8 bytes long). If this option is omitted, a
random server ID is generated on startup and
configuration reload.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-frontend\-quic\-initial\-rtt=<DURATION>
Specify the initial RTT of the frontend QUIC connection.
.sp
Default: \fB333ms\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-no\-quic\-bpf
Disable eBPF.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-frontend\-http3\-window\-size=<SIZE>
Sets the per\-stream initial window size of HTTP/3
frontend connection.
.sp
Default: \fB256K\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-frontend\-http3\-connection\-window\-size=<SIZE>
Sets the per\-connection window size of HTTP/3 frontend
connection.
.sp
Default: \fB1M\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-frontend\-http3\-max\-window\-size=<SIZE>
Sets the maximum per\-stream window size of HTTP/3
frontend connection. The window size is adjusted based
on the receiving rate of stream data. The initial value
is the value specified by \fI\%\-\-frontend\-http3\-window\-size\fP
and the window size grows up to <SIZE> bytes.
.sp
Default: \fB6M\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-frontend\-http3\-max\-connection\-window\-size=<SIZE>
Sets the maximum per\-connection window size of HTTP/3
frontend connection. The window size is adjusted based
on the receiving rate of stream data. The initial value
is the value specified by
\fI\%\-\-frontend\-http3\-connection\-window\-size\fP and the window
size grows up to <SIZE> bytes.
.sp
Default: \fB8M\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-frontend\-http3\-max\-concurrent\-streams=<N>
Set the maximum number of the concurrent streams in one
frontend HTTP/3 connection.
.sp
Default: \fB100\fP
.UNINDENT
.SS Misc .SS Misc
.INDENT 0.0 .INDENT 0.0
.TP .TP

View File

@@ -14,7 +14,7 @@ SYNOPSIS
DESCRIPTION DESCRIPTION
----------- -----------
A reverse proxy for HTTP/2, and HTTP/1. A reverse proxy for HTTP/3, HTTP/2, and HTTP/1.
.. describe:: <PRIVATE_KEY> .. describe:: <PRIVATE_KEY>
@@ -124,12 +124,13 @@ Connections
"affinity=<METHOD>", "dns", "redirect-if-not-tls", "affinity=<METHOD>", "dns", "redirect-if-not-tls",
"upgrade-scheme", "mruby=<PATH>", "upgrade-scheme", "mruby=<PATH>",
"read-timeout=<DURATION>", "write-timeout=<DURATION>", "read-timeout=<DURATION>", "write-timeout=<DURATION>",
"group=<GROUP>", "group-weight=<N>", and "weight=<N>". "group=<GROUP>", "group-weight=<N>", "weight=<N>", and
The parameter consists of keyword, and optionally "dnf". The parameter consists of keyword, and
followed by "=" and value. For example, the parameter optionally followed by "=" and value. For example, the
"proto=h2" consists of the keyword "proto" and value parameter "proto=h2" consists of the keyword "proto" and
"h2". The parameter "tls" consists of the keyword "tls" value "h2". The parameter "tls" consists of the keyword
without value. Each parameter is described as follows. "tls" without value. Each parameter is described as
follows.
The backend application protocol can be specified using The backend application protocol can be specified using
optional "proto" parameter, and in the form of optional "proto" parameter, and in the form of
@@ -260,6 +261,13 @@ Connections
weight becomes 1. "weight" is ignored if session weight becomes 1. "weight" is ignored if session
affinity is enabled. affinity is enabled.
If "dnf" parameter is specified, an incoming request is
not forwarded to a backend and just consumed along with
the request body (actually a backend server never be
contacted). It is expected that the HTTP response is
generated by mruby script (see "mruby=<PATH>" parameter
above). "dnf" is an abbreviation of "do not forward".
Since ";" and ":" are used as delimiter, <PATTERN> must Since ";" and ":" are used as delimiter, <PATTERN> must
not contain these characters. In order to include ":" not contain these characters. In order to include ":"
in <PATTERN>, one has to specify "%3A" (which is in <PATTERN>, one has to specify "%3A" (which is
@@ -307,6 +315,12 @@ Connections
connection, specify "proxyproto" parameter. This is connection, specify "proxyproto" parameter. This is
disabled by default. disabled by default.
To receive HTTP/3 (QUIC) traffic, specify "quic"
parameter. It makes nghttpx listen on UDP port rather
than TCP port. UNIX domain socket, "api", and
"healthmon" parameters cannot be used with "quic"
parameter.
Default: ``*,3000`` Default: ``*,3000``
@@ -458,6 +472,14 @@ Performance
Default: ``0`` Default: ``0``
.. option:: --rlimit-memlock=<N>
Set maximum number of bytes of memory that may be locked
into RAM. If 0 is given, nghttpx does not set the
limit.
Default: ``0``
.. option:: --backend-request-buffer=<SIZE> .. option:: --backend-request-buffer=<SIZE>
Set buffer size used to store backend request. Set buffer size used to store backend request.
@@ -495,6 +517,12 @@ Timeout
Default: ``3m`` Default: ``3m``
.. option:: --frontend-http3-read-timeout=<DURATION>
Specify read timeout for HTTP/3 frontend connection.
Default: ``3m``
.. option:: --frontend-read-timeout=<DURATION> .. option:: --frontend-read-timeout=<DURATION>
Specify read timeout for HTTP/1.1 frontend connection. Specify read timeout for HTTP/1.1 frontend connection.
@@ -983,12 +1011,13 @@ SSL/TLS
.. option:: --tls-no-postpone-early-data .. option:: --tls-no-postpone-early-data
By default, nghttpx postpones forwarding HTTP requests By default, except for QUIC connections, nghttpx
sent in early data, including those sent in partially in postpones forwarding HTTP requests sent in early data,
it, until TLS handshake finishes. If all backend server including those sent in partially in it, until TLS
recognizes "Early-Data" header field, using this option handshake finishes. If all backend server recognizes
makes nghttpx not postpone forwarding request and get "Early-Data" header field, using this option makes
full potential of 0-RTT data. nghttpx not postpone forwarding request and get full
potential of 0-RTT data.
.. option:: --tls-max-early-data=<SIZE> .. option:: --tls-max-early-data=<SIZE>
@@ -1338,14 +1367,21 @@ HTTP
mode. When :option:`--http2-proxy` is used, these headers will mode. When :option:`--http2-proxy` is used, these headers will
not be altered regardless of this option. not be altered regardless of this option.
.. option:: --altsvc=<PROTOID,PORT[,HOST,[ORIGIN]]> .. option:: --altsvc=<PROTOID,PORT[,HOST,[ORIGIN[,PARAMS]]]>
Specify protocol ID, port, host and origin of Specify protocol ID, port, host and origin of
alternative service. <HOST> and <ORIGIN> are optional. alternative service. <HOST>, <ORIGIN> and <PARAMS> are
They are advertised in alt-svc header field only in optional. Empty <HOST> and <ORIGIN> are allowed and
HTTP/1.1 frontend. This option can be used multiple they are treated as nothing is specified. They are
times to specify multiple alternative services. advertised in alt-svc header field only in HTTP/1.1
Example: :option:`--altsvc`\=h2,443 frontend. This option can be used multiple times to
specify multiple alternative services.
Example: :option:`--altsvc`\="h2,443,,,ma=3600; persist=1"
.. option:: --http2-altsvc=<PROTOID,PORT[,HOST,[ORIGIN[,PARAMS]]]>
Just like :option:`--altsvc` option, but this altsvc is only sent
in HTTP/2 frontend.
.. option:: --add-request-header=<HEADER> .. option:: --add-request-header=<HEADER>
@@ -1526,6 +1562,31 @@ Process
neverbleed is used. In the single process mode, the neverbleed is used. In the single process mode, the
signal handling feature is disabled. signal handling feature is disabled.
.. option:: --max-worker-processes=<N>
The maximum number of worker processes. nghttpx spawns
new worker process when it reloads its configuration.
The previous worker process enters graceful termination
period and will terminate when it finishes handling the
existing connections. However, if reloading
configurations happen very frequently, the worker
processes might be piled up if they take a bit long time
to finish the existing connections. With this option,
if the number of worker processes exceeds the given
value, the oldest worker process is terminated
immediately. Specifying 0 means no limit and it is the
default behaviour.
.. option:: --worker-process-grace-shutdown-period=<DURATION>
Maximum period for a worker process to terminate
gracefully. When a worker process enters in graceful
shutdown period (e.g., when nghttpx reloads its
configuration) and it does not finish handling the
existing connections in the given period of time, it is
immediately terminated. Specifying 0 means no limit and
it is the default behaviour.
Scripting Scripting
~~~~~~~~~ ~~~~~~~~~
@@ -1541,6 +1602,147 @@ Scripting
file were specified for the pattern. file were specified for the pattern.
HTTP/3 and QUIC
~~~~~~~~~~~~~~~
.. option:: --frontend-quic-idle-timeout=<DURATION>
Specify an idle timeout for QUIC connection.
Default: ``30s``
.. option:: --frontend-quic-debug-log
Output QUIC debug log to */dev/stderr.*
.. option:: --quic-bpf-program-file=<PATH>
Specify a path to eBPF program file reuseport_kern.o to
direct an incoming QUIC UDP datagram to a correct
socket.
Default: ``/usr/local/lib/nghttp2/reuseport_kern.o``
.. option:: --frontend-quic-early-data
Enable early data on frontend QUIC connections. nghttpx
sends "Early-Data" header field to a backend server if a
request is received in early data and handshake has not
finished. All backend servers should deal with possibly
replayed requests.
.. option:: --frontend-quic-qlog-dir=<DIR>
Specify a directory where a qlog file is written for
frontend QUIC connections. A qlog file is created per
each QUIC connection. The file name is ISO8601 basic
format, followed by "-", server Source Connection ID and
".qlog".
.. option:: --frontend-quic-require-token
Require an address validation token for a frontend QUIC
connection. Server sends a token in Retry packet or
NEW_TOKEN frame in the previous connection.
.. option:: --frontend-quic-congestion-controller=<CC>
Specify a congestion controller algorithm for a frontend
QUIC connection. <CC> should be either "cubic" or
"bbr".
Default: ``cubic``
.. option:: --frontend-quic-secret-file=<PATH>
Path to file that contains secure random data to be used
as QUIC keying materials. It is used to derive keys for
encrypting tokens and Connection IDs. It is not used to
encrypt QUIC packets. Each line of this file must
contain exactly 136 bytes hex-encoded string (when
decoded the byte string is 68 bytes long). The first 2
bits of decoded byte string are used to identify the
keying material. An empty line or a line which starts
'#' is ignored. The file can contain more than one
keying materials. Because the identifier is 2 bits, at
most 4 keying materials are read and the remaining data
is discarded. The first keying material in the file is
primarily used for encryption and decryption for new
connection. The other ones are used to decrypt data for
the existing connections. Specifying multiple keying
materials enables key rotation. Please note that key
rotation does not occur automatically. User should
update files or change options values and restart
nghttpx gracefully. If opening or reading given file
fails, all loaded keying materials are discarded and it
is treated as if none of this option is given. If this
option is not given or an error occurred while opening
or reading a file, a keying material is generated
internally on startup and reload.
.. option:: --quic-server-id=<HEXSTRING>
Specify server ID encoded in Connection ID to identify
this particular server instance. Connection ID is
encrypted and this part is not visible in public. It
must be 4 bytes long and must be encoded in hex string
(which is 8 bytes long). If this option is omitted, a
random server ID is generated on startup and
configuration reload.
.. option:: --frontend-quic-initial-rtt=<DURATION>
Specify the initial RTT of the frontend QUIC connection.
Default: ``333ms``
.. option:: --no-quic-bpf
Disable eBPF.
.. option:: --frontend-http3-window-size=<SIZE>
Sets the per-stream initial window size of HTTP/3
frontend connection.
Default: ``256K``
.. option:: --frontend-http3-connection-window-size=<SIZE>
Sets the per-connection window size of HTTP/3 frontend
connection.
Default: ``1M``
.. option:: --frontend-http3-max-window-size=<SIZE>
Sets the maximum per-stream window size of HTTP/3
frontend connection. The window size is adjusted based
on the receiving rate of stream data. The initial value
is the value specified by :option:`--frontend-http3-window-size`
and the window size grows up to <SIZE> bytes.
Default: ``6M``
.. option:: --frontend-http3-max-connection-window-size=<SIZE>
Sets the maximum per-connection window size of HTTP/3
frontend connection. The window size is adjusted based
on the receiving rate of stream data. The initial value
is the value specified by
:option:`--frontend-http3-connection-window-size` and the window
size grows up to <SIZE> bytes.
Default: ``8M``
.. option:: --frontend-http3-max-concurrent-streams=<N>
Set the maximum number of the concurrent streams in one
frontend HTTP/3 connection.
Default: ``100``
Misc Misc
~~~~ ~~~~

View File

@@ -6,7 +6,7 @@ Architecture
The most notable point in nghttp2 library architecture is it does not The most notable point in nghttp2 library architecture is it does not
perform any I/O. nghttp2 only performs HTTP/2 protocol stuff based on perform any I/O. nghttp2 only performs HTTP/2 protocol stuff based on
input byte strings. It will calls callback functions set by input byte strings. It will call callback functions set by
applications while processing input. The output of nghttp2 is just applications while processing input. The output of nghttp2 is just
byte string. An application is responsible to send these output to byte string. An application is responsible to send these output to
the remote peer. The callback functions may be called while producing the remote peer. The callback functions may be called while producing

View File

@@ -131,3 +131,12 @@ specify ``unix:`` followed by the path to UNIX domain socket. For
example, if UNIX domain socket is ``/tmp/nghttpx.sock``, use example, if UNIX domain socket is ``/tmp/nghttpx.sock``, use
``--base-uri=unix:/tmp/nghttpx.sock``. h2load uses scheme, host and ``--base-uri=unix:/tmp/nghttpx.sock``. h2load uses scheme, host and
port in the first URI in command-line or input file. port in the first URI in command-line or input file.
HTTP/3
------
h2load supports HTTP/3 if it is built with HTTP/3 enabled. HTTP/3
support is experimental.
In order to send HTTP/3 request, specify ``h3`` to
:option:`--npn-list`.

View File

@@ -14,8 +14,8 @@ Default mode
If nghttpx is invoked without :option:`--http2-proxy`, it operates in If nghttpx is invoked without :option:`--http2-proxy`, it operates in
default mode. In this mode, it works as reverse proxy (gateway) for default mode. In this mode, it works as reverse proxy (gateway) for
both HTTP/2 and HTTP/1 clients to backend servers. This is also known HTTP/3, HTTP/2 and HTTP/1 clients to backend servers. This is also
as "HTTP/2 router". known as "HTTP/2 router".
By default, frontend connection is encrypted using SSL/TLS. So By default, frontend connection is encrypted using SSL/TLS. So
server's private key and certificate must be supplied to the command server's private key and certificate must be supplied to the command
@@ -28,6 +28,9 @@ the frontend, and an HTTP/1 connection can be upgraded to HTTP/2 using
HTTP Upgrade. Starting HTTP/2 connection by sending HTTP/2 connection HTTP Upgrade. Starting HTTP/2 connection by sending HTTP/2 connection
preface is also supported. preface is also supported.
In order to receive HTTP/3 traffic, use ``quic`` parameter in
:option:`--frontend` option (.e.g, ``--frontend='*,443;quic'``)
nghttpx can listen on multiple frontend addresses. This is achieved nghttpx can listen on multiple frontend addresses. This is achieved
by using multiple :option:`--frontend` options. For each frontend by using multiple :option:`--frontend` options. For each frontend
address, TLS can be enabled or disabled. address, TLS can be enabled or disabled.
@@ -509,6 +512,60 @@ Bootstrapping WebSockets with HTTP/2 for both frontend and backend
connections. This feature is enabled by default and no configuration connections. This feature is enabled by default and no configuration
is required. is required.
WebSockets over HTTP/3 is also supported.
HTTP/3
------
nghttpx supports HTTP/3 if it is built with HTTP/3 support enabled.
HTTP/3 support is experimental.
In order to listen UDP port to receive HTTP/3 traffic,
:option:`--frontend` option must have ``quic`` parameter:
.. code-block:: text
frontend=*,443;quic
The above example makes nghttpx receive HTTP/3 traffic on UDP
port 443.
nghttpx does not support HTTP/3 on backend connection.
Hot swapping (SIGUSR2) or configuration reload (SIGHUP) require eBPF
program. Without eBPF, old worker processes keep getting HTTP/3
traffic and do not work as intended. The QUIC keying material to
encrypt Connection ID must be set with
:option:`--frontend-quic-secret-file` and must provide the existing
keys in order to keep the existing connections alive during reload.
The construction of Connection ID closely follows Block Cipher CID
Algorithm described in `QUIC-LB draft
<https://datatracker.ietf.org/doc/html/draft-ietf-quic-load-balancers>`_.
A Connection ID that nghttpx generates is always 20 bytes long. It
uses first 2 bits as a configuration ID. The remaining bits in the
first byte are reserved and random. The next 4 bytes are server ID.
The next 4 bytes are used to route UDP datagram to a correct
``SO_REUSEPORT`` socket. The remaining bytes are randomly generated.
The server ID and the next 12 bytes are encrypted with AES-ECB. The
key is derived from the keying materials stored in a file specified by
:option:`--frontend-quic-secret-file`. The first 2 bits of keying
material in the file is used as a configuration ID. The remaining
bits and following 3 bytes are reserved and unused. The next 32 bytes
are used as an initial secret. The remaining 32 bytes are used as a
salt. The encryption key is generated by `HKDF
<https://datatracker.ietf.org/doc/html/rfc5869>`_ with SHA256 and
these keying materials and ``connection id encryption key`` as info.
In order announce that HTTP/3 endpoint is available, you should
specify alt-svc header field. For example, the following options send
alt-svc header field in HTTP/1.1 and HTTP/2 response:
.. code-block:: text
altsvc=h3,443,,,ma=3600
http2-altsvc=h3,443,,,ma=3600
Migration from nghttpx v1.18.x or earlier Migration from nghttpx v1.18.x or earlier
----------------------------------------- -----------------------------------------

78
docker/Dockerfile Normal file
View File

@@ -0,0 +1,78 @@
FROM debian:11 as build
RUN apt-get update && \
apt-get install -y --no-install-recommends \
git clang make binutils autoconf automake autotools-dev libtool \
pkg-config \
zlib1g-dev libev-dev libjemalloc-dev ruby-dev libc-ares-dev bison \
libelf-dev
RUN git clone --depth 1 -b OpenSSL_1_1_1m+quic https://github.com/quictls/openssl && \
cd openssl && \
./config --openssldir=/etc/ssl && \
make -j$(nproc) && \
make install_sw && \
cd .. && \
rm -rf openssl
RUN git clone --depth 1 https://github.com/ngtcp2/nghttp3 && \
cd nghttp3 && \
autoreconf -i && \
./configure --enable-lib-only && \
make -j$(nproc) && \
make install-strip && \
cd .. && \
rm -rf nghttp3
RUN git clone --depth 1 -b v0.1.0 https://github.com/ngtcp2/ngtcp2 && \
cd ngtcp2 && \
autoreconf -i && \
./configure --enable-lib-only \
LIBTOOL_LDFLAGS="-static-libtool-libs" \
OPENSSL_LIBS="-l:libssl.a -l:libcrypto.a -ldl -lpthread" \
PKG_CONFIG_PATH="/usr/local/lib64/pkgconfig" && \
make -j$(nproc) && \
make install-strip && \
cd .. && \
rm -rf ngtcp2
RUN git clone --depth 1 -b v0.4.0 https://github.com/libbpf/libbpf && \
cd libbpf && \
PREFIX=/usr/local make -C src install && \
cd .. && \
rm -rf libbpf
RUN git clone --depth 1 https://github.com/nghttp2/nghttp2.git && \
cd nghttp2 && \
git submodule update --init && \
autoreconf -i && \
./configure --disable-examples --disable-hpack-tools \
--disable-python-bindings --with-mruby --with-neverbleed \
--enable-http3 --with-libbpf \
CC=clang CXX=clang++ \
LIBTOOL_LDFLAGS="-static-libtool-libs" \
OPENSSL_LIBS="-l:libssl.a -l:libcrypto.a -ldl -pthread" \
LIBEV_LIBS="-l:libev.a" \
JEMALLOC_LIBS="-l:libjemalloc.a" \
LIBCARES_LIBS="-l:libcares.a" \
ZLIB_LIBS="-l:libz.a" \
LIBBPF_LIBS="-L/usr/local/lib64 -l:libbpf.a -l:libelf.a" \
LDFLAGS="-static-libgcc -static-libstdc++" \
PKG_CONFIG_PATH="/usr/local/lib64/pkgconfig" && \
make -j$(nproc) install-strip && \
cd .. && \
rm -rf nghttp2
FROM gcr.io/distroless/base-debian11
COPY --from=build \
/usr/local/share/nghttp2/ \
/usr/local/share/nghttp2/
COPY --from=build \
/usr/local/bin/h2load \
/usr/local/bin/nghttpx \
/usr/local/bin/nghttp \
/usr/local/bin/nghttpd \
/usr/local/bin/
COPY --from=build /usr/local/lib/nghttp2/reuseport_kern.o \
/usr/local/lib/nghttp2/

25
docker/README.rst Normal file
View File

@@ -0,0 +1,25 @@
Dockerfile
==========
Dockerfile creates the applications bundled with nghttp2.
These applications are:
- nghttp
- nghttpd
- nghttpx
- h2load
HTTP/3 and eBPF features are enabled.
In order to run nghttpx with HTTP/3 endpoint, you need to run the
image with the escalated privilege and higher memlock value. Here is
the example command-line to run nghttpx to listen to HTTP/3 on port
443, assuming that the current directory contains a private key and a
certificate in server.key and server.crt respectively :
.. code-block:: text
$ docker run --rm -it -v $PWD:/shared --net=host --privileged \
nghttp2 nghttpx \
/shared/server.key /shared/server.crt \
-f'*,443;quic' --rlimit-memlock 524288

View File

@@ -380,6 +380,10 @@ static void init_ssl_ctx(SSL_CTX *ssl_ctx) {
#ifndef OPENSSL_NO_NEXTPROTONEG #ifndef OPENSSL_NO_NEXTPROTONEG
SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL); SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL);
#endif /* !OPENSSL_NO_NEXTPROTONEG */ #endif /* !OPENSSL_NO_NEXTPROTONEG */
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
SSL_CTX_set_alpn_protos(ssl_ctx, (const unsigned char *)"\x02h2", 3);
#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
} }
static void ssl_handshake(SSL *ssl, int fd) { static void ssl_handshake(SSL *ssl, int fd) {
@@ -544,7 +548,7 @@ static void fetch_uri(const struct URI *uri) {
if (fd == -1) { if (fd == -1) {
die("Could not open file descriptor"); die("Could not open file descriptor");
} }
ssl_ctx = SSL_CTX_new(SSLv23_client_method()); ssl_ctx = SSL_CTX_new(TLS_client_method());
if (ssl_ctx == NULL) { if (ssl_ctx == NULL) {
dief("SSL_CTX_new", ERR_error_string(ERR_get_error(), NULL)); dief("SSL_CTX_new", ERR_error_string(ERR_get_error(), NULL));
} }
@@ -715,8 +719,18 @@ int main(int argc, char **argv) {
act.sa_handler = SIG_IGN; act.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &act, 0); sigaction(SIGPIPE, &act, 0);
#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
/* No explicit initialization is required. */
#elif defined(OPENSSL_IS_BORINGSSL)
CRYPTO_library_init();
#else /* !(OPENSSL_VERSION_NUMBER >= 0x1010000fL) && \
!defined(OPENSSL_IS_BORINGSSL) */
OPENSSL_config(NULL);
SSL_load_error_strings(); SSL_load_error_strings();
SSL_library_init(); SSL_library_init();
OpenSSL_add_all_algorithms();
#endif /* !(OPENSSL_VERSION_NUMBER >= 0x1010000fL) && \
!defined(OPENSSL_IS_BORINGSSL) */
rv = parse_uri(&uri, argv[1]); rv = parse_uri(&uri, argv[1]);
if (rv != 0) { if (rv != 0) {

View File

@@ -44,7 +44,7 @@ static void deflate(nghttp2_hd_deflater *deflater,
static int inflate_header_block(nghttp2_hd_inflater *inflater, uint8_t *in, static int inflate_header_block(nghttp2_hd_inflater *inflater, uint8_t *in,
size_t inlen, int final); size_t inlen, int final);
int main() { int main(void) {
int rv; int rv;
nghttp2_hd_deflater *deflater; nghttp2_hd_deflater *deflater;
nghttp2_hd_inflater *inflater; nghttp2_hd_inflater *inflater;

View File

@@ -328,7 +328,7 @@ static int select_next_proto_cb(SSL *ssl, unsigned char **out,
/* Create SSL_CTX. */ /* Create SSL_CTX. */
static SSL_CTX *create_ssl_ctx(void) { static SSL_CTX *create_ssl_ctx(void) {
SSL_CTX *ssl_ctx; SSL_CTX *ssl_ctx;
ssl_ctx = SSL_CTX_new(SSLv23_client_method()); ssl_ctx = SSL_CTX_new(TLS_client_method());
if (!ssl_ctx) { if (!ssl_ctx) {
errx(1, "Could not create SSL/TLS context: %s", errx(1, "Could not create SSL/TLS context: %s",
ERR_error_string(ERR_get_error(), NULL)); ERR_error_string(ERR_get_error(), NULL));
@@ -617,8 +617,18 @@ int main(int argc, char **argv) {
act.sa_handler = SIG_IGN; act.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &act, NULL); sigaction(SIGPIPE, &act, NULL);
#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
/* No explicit initialization is required. */
#elif defined(OPENSSL_IS_BORINGSSL)
CRYPTO_library_init();
#else /* !(OPENSSL_VERSION_NUMBER >= 0x1010000fL) && \
!defined(OPENSSL_IS_BORINGSSL) */
OPENSSL_config(NULL);
SSL_load_error_strings(); SSL_load_error_strings();
SSL_library_init(); SSL_library_init();
OpenSSL_add_all_algorithms();
#endif /* !(OPENSSL_VERSION_NUMBER >= 0x1010000fL) && \
!defined(OPENSSL_IS_BORINGSSL) */
run(argv[1]); run(argv[1]);
return 0; return 0;

View File

@@ -142,9 +142,8 @@ static int alpn_select_proto_cb(SSL *ssl, const unsigned char **out,
/* Create SSL_CTX. */ /* Create SSL_CTX. */
static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) { static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) {
SSL_CTX *ssl_ctx; SSL_CTX *ssl_ctx;
EC_KEY *ecdh;
ssl_ctx = SSL_CTX_new(SSLv23_server_method()); ssl_ctx = SSL_CTX_new(TLS_server_method());
if (!ssl_ctx) { if (!ssl_ctx) {
errx(1, "Could not create SSL/TLS context: %s", errx(1, "Could not create SSL/TLS context: %s",
ERR_error_string(ERR_get_error(), NULL)); ERR_error_string(ERR_get_error(), NULL));
@@ -153,7 +152,14 @@ static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) {
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
SSL_OP_NO_COMPRESSION | SSL_OP_NO_COMPRESSION |
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
if (SSL_CTX_set1_curves_list(ssl_ctx, "P-256") != 1) {
errx(1, "SSL_CTX_set1_curves_list failed: %s",
ERR_error_string(ERR_get_error(), NULL));
}
#else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
{
EC_KEY *ecdh;
ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
if (!ecdh) { if (!ecdh) {
errx(1, "EC_KEY_new_by_curv_name failed: %s", errx(1, "EC_KEY_new_by_curv_name failed: %s",
@@ -161,6 +167,8 @@ static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) {
} }
SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh); SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh);
EC_KEY_free(ecdh); EC_KEY_free(ecdh);
}
#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
if (SSL_CTX_use_PrivateKey_file(ssl_ctx, key_file, SSL_FILETYPE_PEM) != 1) { if (SSL_CTX_use_PrivateKey_file(ssl_ctx, key_file, SSL_FILETYPE_PEM) != 1) {
errx(1, "Could not read private key file %s", key_file); errx(1, "Could not read private key file %s", key_file);
@@ -809,8 +817,18 @@ int main(int argc, char **argv) {
act.sa_handler = SIG_IGN; act.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &act, NULL); sigaction(SIGPIPE, &act, NULL);
#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
/* No explicit initialization is required. */
#elif defined(OPENSSL_IS_BORINGSSL)
CRYPTO_library_init();
#else /* !(OPENSSL_VERSION_NUMBER >= 0x1010000fL) && \
!defined(OPENSSL_IS_BORINGSSL) */
OPENSSL_config(NULL);
SSL_load_error_strings(); SSL_load_error_strings();
SSL_library_init(); SSL_library_init();
OpenSSL_add_all_algorithms();
#endif /* !(OPENSSL_VERSION_NUMBER >= 0x1010000fL) && \
!defined(OPENSSL_IS_BORINGSSL) */
run(argv[1], argv[2], argv[3]); run(argv[1], argv[2], argv[3]);
return 0; return 0;

29
genmethodchartbl.py Executable file
View File

@@ -0,0 +1,29 @@
#!/usr/bin/env python3
import sys
def name(i):
if i < 0x21:
return \
['NUL ', 'SOH ', 'STX ', 'ETX ', 'EOT ', 'ENQ ', 'ACK ', 'BEL ',
'BS ', 'HT ', 'LF ', 'VT ', 'FF ', 'CR ', 'SO ', 'SI ',
'DLE ', 'DC1 ', 'DC2 ', 'DC3 ', 'DC4 ', 'NAK ', 'SYN ', 'ETB ',
'CAN ', 'EM ', 'SUB ', 'ESC ', 'FS ', 'GS ', 'RS ', 'US ',
'SPC '][i]
elif i == 0x7f:
return 'DEL '
for i in range(256):
if chr(i) in ["!" , "#" , "$" , "%" , "&" , "'" , "*",
"+" , "-" , "." , "^" , "_" , "`" , "|" , "~"] or\
('0' <= chr(i) and chr(i) <= '9') or \
('A' <= chr(i) and chr(i) <= 'Z') or \
('a' <= chr(i) and chr(i) <= 'z'):
sys.stdout.write('1 /* {} */, '.format(chr(i)))
elif (0x21 <= i and i < 0x7f):
sys.stdout.write('0 /* {} */, '.format(chr(i)))
elif 0x80 <= i:
sys.stdout.write('0 /* {} */, '.format(hex(i)))
else:
sys.stdout.write('0 /* {} */, '.format(name(i)))
if (i + 1)%4 == 0:
sys.stdout.write('\n')

View File

@@ -177,6 +177,27 @@ OPTIONS = [
"tls13-ciphers", "tls13-ciphers",
"tls13-client-ciphers", "tls13-client-ciphers",
"no-strip-incoming-early-data", "no-strip-incoming-early-data",
"quic-bpf-program-file",
"no-quic-bpf",
"http2-altsvc",
"frontend-http3-read-timeout",
"frontend-quic-idle-timeout",
"frontend-quic-debug-log",
"frontend-http3-window-size",
"frontend-http3-connection-window-size",
"frontend-http3-max-window-size",
"frontend-http3-max-connection-window-size",
"frontend-http3-max-concurrent-streams",
"frontend-quic-early-data",
"frontend-quic-qlog-dir",
"frontend-quic-require-token",
"frontend-quic-congestion-controller",
"quic-server-id",
"frontend-quic-secret-file",
"rlimit-memlock",
"max-worker-processes",
"worker-process-grace-shutdown-period",
"frontend-quic-initial-rtt",
] ]
LOGVARS = [ LOGVARS = [

23
genpathchartbl.py Executable file
View File

@@ -0,0 +1,23 @@
#!/usr/bin/env python3
import sys
def name(i):
if i < 0x21:
return \
['NUL ', 'SOH ', 'STX ', 'ETX ', 'EOT ', 'ENQ ', 'ACK ', 'BEL ',
'BS ', 'HT ', 'LF ', 'VT ', 'FF ', 'CR ', 'SO ', 'SI ',
'DLE ', 'DC1 ', 'DC2 ', 'DC3 ', 'DC4 ', 'NAK ', 'SYN ', 'ETB ',
'CAN ', 'EM ', 'SUB ', 'ESC ', 'FS ', 'GS ', 'RS ', 'US ',
'SPC '][i]
elif i == 0x7f:
return 'DEL '
for i in range(256):
if (0x21 <= i and i < 0x7f):
sys.stdout.write('1 /* {} */, '.format(chr(i)))
elif 0x80 <= i:
sys.stdout.write('1 /* {} */, '.format(hex(i)))
else:
sys.stdout.write('0 /* {} */, '.format(name(i)))
if (i + 1)%4 == 0:
sys.stdout.write('\n')

View File

@@ -20,8 +20,6 @@ for i in range(256):
sys.stdout.write('1 /* {} */, '.format(chr(i))) sys.stdout.write('1 /* {} */, '.format(chr(i)))
elif 0x80 <= i: elif 0x80 <= i:
sys.stdout.write('1 /* {} */, '.format(hex(i))) sys.stdout.write('1 /* {} */, '.format(hex(i)))
elif 0 == i:
sys.stdout.write('1 /* NUL */, ')
else: else:
sys.stdout.write('0 /* {} */, '.format(name(i))) sys.stdout.write('0 /* {} */, '.format(name(i)))
if (i + 1)%4 == 0: if (i + 1)%4 == 0:

View File

@@ -5,14 +5,15 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"golang.org/x/net/http2/hpack"
"golang.org/x/net/websocket"
"io" "io"
"net/http" "net/http"
"regexp" "regexp"
"syscall" "syscall"
"testing" "testing"
"time" "time"
"golang.org/x/net/http2/hpack"
"golang.org/x/net/websocket"
) )
// TestH1H1PlainGET tests whether simple HTTP/1 GET request works. // TestH1H1PlainGET tests whether simple HTTP/1 GET request works.
@@ -34,7 +35,7 @@ func TestH1H1PlainGET(t *testing.T) {
} }
// TestH1H1PlainGETClose tests whether simple HTTP/1 GET request with // TestH1H1PlainGETClose tests whether simple HTTP/1 GET request with
// Connetion: close request header field works. // Connection: close request header field works.
func TestH1H1PlainGETClose(t *testing.T) { func TestH1H1PlainGETClose(t *testing.T) {
st := newServerTester(nil, t, noopHandler) st := newServerTester(nil, t, noopHandler)
defer st.Close() defer st.Close()
@@ -1171,3 +1172,31 @@ Content-Length: 1000000
t.Errorf("status: %v; want %v", got, want) t.Errorf("status: %v; want %v", got, want)
} }
} }
// TestH1H1ChunkedEndsPrematurely tests that an HTTP/1.1 request fails
// if the backend chunked encoded response ends prematurely.
func TestH1H1ChunkedEndsPrematurely(t *testing.T) {
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
hj, ok := w.(http.Hijacker)
if !ok {
http.Error(w, "Could not hijack the connection", http.StatusInternalServerError)
return
}
conn, bufrw, err := hj.Hijack()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer conn.Close()
bufrw.WriteString("HTTP/1.1 200\r\nTransfer-Encoding: chunked\r\n\r\n")
bufrw.Flush()
})
defer st.Close()
_, err := st.http1(requestParam{
name: "TestH1H1ChunkedEndsPrematurely",
})
if err == nil {
t.Fatal("st.http1() should fail")
}
}

View File

@@ -565,7 +565,7 @@ func TestH2H1BadResponseCL(t *testing.T) {
t.Fatalf("Error st.http2() = %v", err) t.Fatalf("Error st.http2() = %v", err)
} }
want := http2.ErrCodeProtocol want := http2.ErrCodeInternal
if res.errCode != want { if res.errCode != want {
t.Errorf("res.errCode = %v; want %v", res.errCode, want) t.Errorf("res.errCode = %v; want %v", res.errCode, want)
} }
@@ -2838,3 +2838,35 @@ func TestH2ResponseBeforeRequestEnd(t *testing.T) {
t.Errorf("res.status: %v; want %v", got, want) t.Errorf("res.status: %v; want %v", got, want)
} }
} }
// TestH2H1ChunkedEndsPrematurely tests that a stream is reset if the
// backend chunked encoded response ends prematurely.
func TestH2H1ChunkedEndsPrematurely(t *testing.T) {
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
hj, ok := w.(http.Hijacker)
if !ok {
http.Error(w, "Could not hijack the connection", http.StatusInternalServerError)
return
}
conn, bufrw, err := hj.Hijack()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer conn.Close()
bufrw.WriteString("HTTP/1.1 200\r\nTransfer-Encoding: chunked\r\n\r\n")
bufrw.Flush()
})
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H1ChunkedEndsPrematurely",
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
if got, want := res.errCode, http2.ErrCodeInternal; got != want {
t.Errorf("res.errCode = %v; want %v", got, want)
}
}

View File

@@ -3054,7 +3054,7 @@ NGHTTP2_EXTERN int nghttp2_session_recv(nghttp2_session *session);
* @function * @function
* *
* Processes data |in| as an input from the remote endpoint. The * Processes data |in| as an input from the remote endpoint. The
* |inlen| indicates the number of bytes in the |in|. * |inlen| indicates the number of bytes to receive in the |in|.
* *
* This function behaves like `nghttp2_session_recv()` except that it * This function behaves like `nghttp2_session_recv()` except that it
* does not use :type:`nghttp2_recv_callback` to receive data; the * does not use :type:`nghttp2_recv_callback` to receive data; the
@@ -3063,7 +3063,7 @@ NGHTTP2_EXTERN int nghttp2_session_recv(nghttp2_session *session);
* are called in the same way as they are in `nghttp2_session_recv()`. * are called in the same way as they are in `nghttp2_session_recv()`.
* *
* In the current implementation, this function always tries to * In the current implementation, this function always tries to
* processes all input data unless either an error occurs or * processes |inlen| bytes of input data unless either an error occurs or
* :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` is returned from * :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` is returned from
* :type:`nghttp2_on_header_callback` or * :type:`nghttp2_on_header_callback` or
* :type:`nghttp2_on_data_chunk_recv_callback`. If * :type:`nghttp2_on_data_chunk_recv_callback`. If
@@ -4839,7 +4839,31 @@ NGHTTP2_EXTERN int nghttp2_check_header_value(const uint8_t *value, size_t len);
/** /**
* @function * @function
* *
* Returns nonzero if the |value| which is supposed to the value of * Returns nonzero if the |value| which is supposed to be the value of
* the :method header field is valid according to
* https://datatracker.ietf.org/doc/html/rfc7231#section-4 and
* https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.6
*/
NGHTTP2_EXTERN int nghttp2_check_method(const uint8_t *value, size_t len);
/**
* @function
*
* Returns nonzero if the |value| which is supposed to be the value of
* the :path header field is valid according to
* https://datatracker.ietf.org/doc/html/rfc7540#section-8.1.2.3
*
* |value| is valid if it merely consists of the allowed characters.
* In particular, it does not check whether |value| follows the syntax
* of path. The allowed characters are all characters valid by
* `nghttp2_check_header_value` minus SPC and HT.
*/
NGHTTP2_EXTERN int nghttp2_check_path(const uint8_t *value, size_t len);
/**
* @function
*
* Returns nonzero if the |value| which is supposed to be the value of the
* :authority or host header field is valid according to * :authority or host header field is valid according to
* https://tools.ietf.org/html/rfc3986#section-3.2 * https://tools.ietf.org/html/rfc3986#section-3.2
* *

View File

@@ -99,7 +99,7 @@ void nghttp2_buf_free(nghttp2_buf *buf, nghttp2_mem *mem);
* |new_cap|. If extensions took place, buffer pointers in |buf| will * |new_cap|. If extensions took place, buffer pointers in |buf| will
* change. * change.
* *
* This function returns 0 if it succeeds, or one of the followings * This function returns 0 if it succeeds, or one of the following
* negative error codes: * negative error codes:
* *
* NGHTTP2_ERR_NOMEM * NGHTTP2_ERR_NOMEM

View File

@@ -654,8 +654,6 @@ int nghttp2_frame_unpack_goaway_payload2(nghttp2_goaway *frame,
var_gift_payloadlen = 0; var_gift_payloadlen = 0;
} }
payloadlen -= var_gift_payloadlen;
if (!var_gift_payloadlen) { if (!var_gift_payloadlen) {
var_gift_payload = NULL; var_gift_payload = NULL;
} else { } else {

View File

@@ -46,7 +46,7 @@
#define NGHTTP2_MAX_FRAME_SIZE_MIN (1 << 14) #define NGHTTP2_MAX_FRAME_SIZE_MIN (1 << 14)
#define NGHTTP2_MAX_PAYLOADLEN 16384 #define NGHTTP2_MAX_PAYLOADLEN 16384
/* The one frame buffer length for tranmission. We may use several of /* The one frame buffer length for transmission. We may use several of
them to support CONTINUATION. To account for Pad Length field, we them to support CONTINUATION. To account for Pad Length field, we
allocate extra 1 byte, which saves extra large memcopying. */ allocate extra 1 byte, which saves extra large memcopying. */
#define NGHTTP2_FRAMEBUF_CHUNKLEN \ #define NGHTTP2_FRAMEBUF_CHUNKLEN \
@@ -57,7 +57,7 @@
/* Maximum headers block size to send, calculated using /* Maximum headers block size to send, calculated using
nghttp2_hd_deflate_bound(). This is the default value, and can be nghttp2_hd_deflate_bound(). This is the default value, and can be
overridden by nghttp2_option_set_max_send_header_block_size(). */ overridden by nghttp2_option_set_max_send_header_block_length(). */
#define NGHTTP2_MAX_HEADERSLEN 65536 #define NGHTTP2_MAX_HEADERSLEN 65536
/* The number of bytes for each SETTINGS entry */ /* The number of bytes for each SETTINGS entry */

View File

@@ -1263,6 +1263,8 @@ int nghttp2_hd_inflate_change_table_size(
return NGHTTP2_ERR_INVALID_STATE; return NGHTTP2_ERR_INVALID_STATE;
} }
inflater->settings_hd_table_bufsize_max = settings_max_dynamic_table_size;
/* It seems that encoder is not required to send dynamic table size /* It seems that encoder is not required to send dynamic table size
update if the table size is not changed after applying update if the table size is not changed after applying
SETTINGS_HEADER_TABLE_SIZE. RFC 7541 is ambiguous here, but this SETTINGS_HEADER_TABLE_SIZE. RFC 7541 is ambiguous here, but this
@@ -1275,13 +1277,12 @@ int nghttp2_hd_inflate_change_table_size(
/* Remember minimum value, and validate that encoder sends the /* Remember minimum value, and validate that encoder sends the
value less than or equal to this. */ value less than or equal to this. */
inflater->min_hd_table_bufsize_max = settings_max_dynamic_table_size; inflater->min_hd_table_bufsize_max = settings_max_dynamic_table_size;
}
inflater->settings_hd_table_bufsize_max = settings_max_dynamic_table_size;
inflater->ctx.hd_table_bufsize_max = settings_max_dynamic_table_size; inflater->ctx.hd_table_bufsize_max = settings_max_dynamic_table_size;
hd_context_shrink_table_size(&inflater->ctx, NULL); hd_context_shrink_table_size(&inflater->ctx, NULL);
}
return 0; return 0;
} }

View File

@@ -507,7 +507,166 @@ int nghttp2_check_header_value(const uint8_t *value, size_t len) {
return 1; return 1;
} }
/* Generated by genauthroitychartbl.py */ /* Generated by genmethodchartbl.py */
static char VALID_METHOD_CHARS[] = {
0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */,
0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */,
0 /* BS */, 0 /* HT */, 0 /* LF */, 0 /* VT */,
0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */,
0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */,
0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */,
0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */,
0 /* SPC */, 1 /* ! */, 0 /* " */, 1 /* # */,
1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
0 /* ( */, 0 /* ) */, 1 /* * */, 1 /* + */,
0 /* , */, 1 /* - */, 1 /* . */, 0 /* / */,
1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */,
1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */,
1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */,
0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */,
0 /* @ */, 1 /* A */, 1 /* B */, 1 /* C */,
1 /* D */, 1 /* E */, 1 /* F */, 1 /* G */,
1 /* H */, 1 /* I */, 1 /* J */, 1 /* K */,
1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */,
1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */,
1 /* T */, 1 /* U */, 1 /* V */, 1 /* W */,
1 /* X */, 1 /* Y */, 1 /* Z */, 0 /* [ */,
0 /* \ */, 0 /* ] */, 1 /* ^ */, 1 /* _ */,
1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */,
1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */,
1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */,
1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */,
1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */,
1 /* | */, 0 /* } */, 1 /* ~ */, 0 /* DEL */,
0 /* 0x80 */, 0 /* 0x81 */, 0 /* 0x82 */, 0 /* 0x83 */,
0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, 0 /* 0x87 */,
0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */,
0 /* 0x90 */, 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */,
0 /* 0x94 */, 0 /* 0x95 */, 0 /* 0x96 */, 0 /* 0x97 */,
0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, 0 /* 0x9b */,
0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */,
0 /* 0xa4 */, 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */,
0 /* 0xa8 */, 0 /* 0xa9 */, 0 /* 0xaa */, 0 /* 0xab */,
0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, 0 /* 0xaf */,
0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */,
0 /* 0xb8 */, 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */,
0 /* 0xbc */, 0 /* 0xbd */, 0 /* 0xbe */, 0 /* 0xbf */,
0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, 0 /* 0xc3 */,
0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */,
0 /* 0xcc */, 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */,
0 /* 0xd0 */, 0 /* 0xd1 */, 0 /* 0xd2 */, 0 /* 0xd3 */,
0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, 0 /* 0xd7 */,
0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */,
0 /* 0xe0 */, 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */,
0 /* 0xe4 */, 0 /* 0xe5 */, 0 /* 0xe6 */, 0 /* 0xe7 */,
0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, 0 /* 0xeb */,
0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */,
0 /* 0xf4 */, 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */,
0 /* 0xf8 */, 0 /* 0xf9 */, 0 /* 0xfa */, 0 /* 0xfb */,
0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, 0 /* 0xff */
};
int nghttp2_check_method(const uint8_t *value, size_t len) {
const uint8_t *last;
if (len == 0) {
return 0;
}
for (last = value + len; value != last; ++value) {
if (!VALID_METHOD_CHARS[*value]) {
return 0;
}
}
return 1;
}
/* Generated by genpathchartbl.py */
static char VALID_PATH_CHARS[] = {
0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */,
0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */,
0 /* BS */, 0 /* HT */, 0 /* LF */, 0 /* VT */,
0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */,
0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */,
0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */,
0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */,
0 /* SPC */, 1 /* ! */, 1 /* " */, 1 /* # */,
1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */,
1 /* , */, 1 /* - */, 1 /* . */, 1 /* / */,
1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */,
1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */,
1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */,
1 /* < */, 1 /* = */, 1 /* > */, 1 /* ? */,
1 /* @ */, 1 /* A */, 1 /* B */, 1 /* C */,
1 /* D */, 1 /* E */, 1 /* F */, 1 /* G */,
1 /* H */, 1 /* I */, 1 /* J */, 1 /* K */,
1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */,
1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */,
1 /* T */, 1 /* U */, 1 /* V */, 1 /* W */,
1 /* X */, 1 /* Y */, 1 /* Z */, 1 /* [ */,
1 /* \ */, 1 /* ] */, 1 /* ^ */, 1 /* _ */,
1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */,
1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */,
1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */,
1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */,
1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
1 /* x */, 1 /* y */, 1 /* z */, 1 /* { */,
1 /* | */, 1 /* } */, 1 /* ~ */, 0 /* DEL */,
1 /* 0x80 */, 1 /* 0x81 */, 1 /* 0x82 */, 1 /* 0x83 */,
1 /* 0x84 */, 1 /* 0x85 */, 1 /* 0x86 */, 1 /* 0x87 */,
1 /* 0x88 */, 1 /* 0x89 */, 1 /* 0x8a */, 1 /* 0x8b */,
1 /* 0x8c */, 1 /* 0x8d */, 1 /* 0x8e */, 1 /* 0x8f */,
1 /* 0x90 */, 1 /* 0x91 */, 1 /* 0x92 */, 1 /* 0x93 */,
1 /* 0x94 */, 1 /* 0x95 */, 1 /* 0x96 */, 1 /* 0x97 */,
1 /* 0x98 */, 1 /* 0x99 */, 1 /* 0x9a */, 1 /* 0x9b */,
1 /* 0x9c */, 1 /* 0x9d */, 1 /* 0x9e */, 1 /* 0x9f */,
1 /* 0xa0 */, 1 /* 0xa1 */, 1 /* 0xa2 */, 1 /* 0xa3 */,
1 /* 0xa4 */, 1 /* 0xa5 */, 1 /* 0xa6 */, 1 /* 0xa7 */,
1 /* 0xa8 */, 1 /* 0xa9 */, 1 /* 0xaa */, 1 /* 0xab */,
1 /* 0xac */, 1 /* 0xad */, 1 /* 0xae */, 1 /* 0xaf */,
1 /* 0xb0 */, 1 /* 0xb1 */, 1 /* 0xb2 */, 1 /* 0xb3 */,
1 /* 0xb4 */, 1 /* 0xb5 */, 1 /* 0xb6 */, 1 /* 0xb7 */,
1 /* 0xb8 */, 1 /* 0xb9 */, 1 /* 0xba */, 1 /* 0xbb */,
1 /* 0xbc */, 1 /* 0xbd */, 1 /* 0xbe */, 1 /* 0xbf */,
1 /* 0xc0 */, 1 /* 0xc1 */, 1 /* 0xc2 */, 1 /* 0xc3 */,
1 /* 0xc4 */, 1 /* 0xc5 */, 1 /* 0xc6 */, 1 /* 0xc7 */,
1 /* 0xc8 */, 1 /* 0xc9 */, 1 /* 0xca */, 1 /* 0xcb */,
1 /* 0xcc */, 1 /* 0xcd */, 1 /* 0xce */, 1 /* 0xcf */,
1 /* 0xd0 */, 1 /* 0xd1 */, 1 /* 0xd2 */, 1 /* 0xd3 */,
1 /* 0xd4 */, 1 /* 0xd5 */, 1 /* 0xd6 */, 1 /* 0xd7 */,
1 /* 0xd8 */, 1 /* 0xd9 */, 1 /* 0xda */, 1 /* 0xdb */,
1 /* 0xdc */, 1 /* 0xdd */, 1 /* 0xde */, 1 /* 0xdf */,
1 /* 0xe0 */, 1 /* 0xe1 */, 1 /* 0xe2 */, 1 /* 0xe3 */,
1 /* 0xe4 */, 1 /* 0xe5 */, 1 /* 0xe6 */, 1 /* 0xe7 */,
1 /* 0xe8 */, 1 /* 0xe9 */, 1 /* 0xea */, 1 /* 0xeb */,
1 /* 0xec */, 1 /* 0xed */, 1 /* 0xee */, 1 /* 0xef */,
1 /* 0xf0 */, 1 /* 0xf1 */, 1 /* 0xf2 */, 1 /* 0xf3 */,
1 /* 0xf4 */, 1 /* 0xf5 */, 1 /* 0xf6 */, 1 /* 0xf7 */,
1 /* 0xf8 */, 1 /* 0xf9 */, 1 /* 0xfa */, 1 /* 0xfb */,
1 /* 0xfc */, 1 /* 0xfd */, 1 /* 0xfe */, 1 /* 0xff */
};
int nghttp2_check_path(const uint8_t *value, size_t len) {
const uint8_t *last;
for (last = value + len; value != last; ++value) {
if (!VALID_PATH_CHARS[*value]) {
return 0;
}
}
return 1;
}
/* Generated by genauthoritychartbl.py */
static char VALID_AUTHORITY_CHARS[] = { static char VALID_AUTHORITY_CHARS[] = {
0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */,
0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */,

View File

@@ -360,12 +360,21 @@ int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
return NGHTTP2_ERR_IGN_HTTP_HEADER; return NGHTTP2_ERR_IGN_HTTP_HEADER;
} }
if (nv->token == NGHTTP2_TOKEN__AUTHORITY || switch (nv->token) {
nv->token == NGHTTP2_TOKEN_HOST) { case NGHTTP2_TOKEN__METHOD:
rv = nghttp2_check_method(nv->value->base, nv->value->len);
break;
case NGHTTP2_TOKEN__PATH:
rv = nghttp2_check_path(nv->value->base, nv->value->len);
break;
case NGHTTP2_TOKEN__AUTHORITY:
case NGHTTP2_TOKEN_HOST:
rv = nghttp2_check_authority(nv->value->base, nv->value->len); rv = nghttp2_check_authority(nv->value->base, nv->value->len);
} else if (nv->token == NGHTTP2_TOKEN__SCHEME) { break;
case NGHTTP2_TOKEN__SCHEME:
rv = check_scheme(nv->value->base, nv->value->len); rv = check_scheme(nv->value->base, nv->value->len);
} else { break;
default:
rv = nghttp2_check_header_value(nv->value->base, nv->value->len); rv = nghttp2_check_header_value(nv->value->base, nv->value->len);
} }

View File

@@ -189,6 +189,7 @@ static int map_resize(nghttp2_map *map, uint32_t new_tablelen,
nghttp2_map_bucket *new_table; nghttp2_map_bucket *new_table;
nghttp2_map_bucket *bkt; nghttp2_map_bucket *bkt;
int rv; int rv;
(void)rv;
new_table = new_table =
nghttp2_mem_calloc(map->mem, new_tablelen, sizeof(nghttp2_map_bucket)); nghttp2_mem_calloc(map->mem, new_tablelen, sizeof(nghttp2_map_bucket));

View File

@@ -42,7 +42,7 @@
#if defined(WIN32) #if defined(WIN32)
/* Windows requires ws2_32 library for ntonl family functions. We /* Windows requires ws2_32 library for ntonl family functions. We
define inline functions for those function so that we don't have define inline functions for those function so that we don't have
dependeny on that lib. */ dependency on that lib. */
# ifdef _MSC_VER # ifdef _MSC_VER
# define STIN static __inline # define STIN static __inline

View File

@@ -111,7 +111,7 @@ struct nghttp2_outbound_item {
to this structure to avoid frequent memory allocation. */ to this structure to avoid frequent memory allocation. */
nghttp2_ext_frame_payload ext_frame_payload; nghttp2_ext_frame_payload ext_frame_payload;
nghttp2_aux_data aux_data; nghttp2_aux_data aux_data;
/* The priority used in priority comparion. Smaller is served /* The priority used in priority comparison. Smaller is served
earlier. For PING, SETTINGS and non-DATA frames (excluding earlier. For PING, SETTINGS and non-DATA frames (excluding
response HEADERS frame) have dedicated cycle value defined above. response HEADERS frame) have dedicated cycle value defined above.
For DATA frame, cycle is computed by taking into account of For DATA frame, cycle is computed by taking into account of

View File

@@ -114,7 +114,7 @@ typedef int (*nghttp2_pq_item_cb)(nghttp2_pq_entry *item, void *arg);
void nghttp2_pq_update(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg); void nghttp2_pq_update(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg);
/* /*
* Applys |fun| to each item in |pq|. The |arg| is passed as arg * Applies |fun| to each item in |pq|. The |arg| is passed as arg
* parameter to callback function. This function must not change the * parameter to callback function. This function must not change the
* ordering key. If the return value from callback is nonzero, this * ordering key. If the return value from callback is nonzero, this
* function returns 1 immediately without iterating remaining items. * function returns 1 immediately without iterating remaining items.

View File

@@ -5341,7 +5341,7 @@ static ssize_t inbound_frame_compute_pad(nghttp2_inbound_frame *iframe) {
/* /*
* This function returns the effective payload length in the data of * This function returns the effective payload length in the data of
* length |readlen| when the remaning payload is |payloadleft|. The * length |readlen| when the remaining payload is |payloadleft|. The
* |payloadleft| does not include |readlen|. If padding was started * |payloadleft| does not include |readlen|. If padding was started
* strictly before this data chunk, this function returns -1. * strictly before this data chunk, this function returns -1.
*/ */

View File

@@ -408,7 +408,7 @@ int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id,
uint32_t error_code); uint32_t error_code);
/* /*
* Adds PING frame. This is a convenient functin built on top of * Adds PING frame. This is a convenient function built on top of
* nghttp2_session_add_frame() to add PING easily. * nghttp2_session_add_frame() to add PING easily.
* *
* If the |opaque_data| is not NULL, it must point to 8 bytes memory * If the |opaque_data| is not NULL, it must point to 8 bytes memory

View File

@@ -33,7 +33,7 @@
#include "nghttp2_frame.h" #include "nghttp2_frame.h"
/* Maximum distance between any two stream's cycle in the same /* Maximum distance between any two stream's cycle in the same
prirority queue. Imagine stream A's cycle is A, and stream B's priority queue. Imagine stream A's cycle is A, and stream B's
cycle is B, and A < B. The cycle is unsigned 32 bit integer, it cycle is B, and A < B. The cycle is unsigned 32 bit integer, it
may get overflow. Because of how we calculate the next cycle may get overflow. Because of how we calculate the next cycle
value, if B - A is less than or equals to value, if B - A is less than or equals to

View File

@@ -492,8 +492,6 @@ int nghttp2_session_set_local_window_size(nghttp2_session *session,
return nghttp2_session_update_recv_stream_window_size(session, stream, 0, return nghttp2_session_update_recv_stream_window_size(session, stream, 0,
1); 1);
} }
return 0;
} }
int nghttp2_submit_altsvc(nghttp2_session *session, uint8_t flags, int nghttp2_submit_altsvc(nghttp2_session *session, uint8_t flags,

View File

@@ -67,7 +67,7 @@
# modified version of the Autoconf Macro, you may extend this special # modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well. # exception to the GPL to apply to your modified version as well.
#serial 21 #serial 23
AU_ALIAS([AC_PYTHON_DEVEL], [AX_PYTHON_DEVEL]) AU_ALIAS([AC_PYTHON_DEVEL], [AX_PYTHON_DEVEL])
AC_DEFUN([AX_PYTHON_DEVEL],[ AC_DEFUN([AX_PYTHON_DEVEL],[
@@ -135,27 +135,45 @@ variable to configure. See ``configure --help'' for reference.
# #
# Check if you have distutils, else fail # Check if you have distutils, else fail
# #
AC_MSG_CHECKING([for the distutils Python package]) AC_MSG_CHECKING([for the sysconfig Python package])
ac_distutils_result=`$PYTHON -c "import distutils" 2>&1` ac_sysconfig_result=`$PYTHON -c "import sysconfig" 2>&1`
if test $? -eq 0; then if test $? -eq 0; then
AC_MSG_RESULT([yes]) AC_MSG_RESULT([yes])
IMPORT_SYSCONFIG="import sysconfig"
else else
AC_MSG_RESULT([no]) AC_MSG_RESULT([no])
AC_MSG_CHECKING([for the distutils Python package])
ac_sysconfig_result=`$PYTHON -c "from distutils import sysconfig" 2>&1`
if test $? -eq 0; then
AC_MSG_RESULT([yes])
IMPORT_SYSCONFIG="from distutils import sysconfig"
else
AC_MSG_ERROR([cannot import Python module "distutils". AC_MSG_ERROR([cannot import Python module "distutils".
Please check your Python installation. The error was: Please check your Python installation. The error was:
$ac_distutils_result]) $ac_sysconfig_result])
PYTHON_VERSION="" PYTHON_VERSION=""
fi fi
fi
# #
# Check for Python include path # Check for Python include path
# #
AC_MSG_CHECKING([for Python include path]) AC_MSG_CHECKING([for Python include path])
if test -z "$PYTHON_CPPFLAGS"; then if test -z "$PYTHON_CPPFLAGS"; then
python_path=`$PYTHON -c "import distutils.sysconfig; \ if test "$IMPORT_SYSCONFIG" = "import sysconfig"; then
print (distutils.sysconfig.get_python_inc ());"` # sysconfig module has different functions
plat_python_path=`$PYTHON -c "import distutils.sysconfig; \ python_path=`$PYTHON -c "$IMPORT_SYSCONFIG; \
print (distutils.sysconfig.get_python_inc (plat_specific=1));"` print (sysconfig.get_path ('include'));"`
plat_python_path=`$PYTHON -c "$IMPORT_SYSCONFIG; \
print (sysconfig.get_path ('platinclude'));"`
else
# old distutils way
python_path=`$PYTHON -c "$IMPORT_SYSCONFIG; \
print (sysconfig.get_python_inc ());"`
plat_python_path=`$PYTHON -c "$IMPORT_SYSCONFIG; \
print (sysconfig.get_python_inc (plat_specific=1));"`
fi
if test -n "${python_path}"; then if test -n "${python_path}"; then
if test "${plat_python_path}" != "${python_path}"; then if test "${plat_python_path}" != "${python_path}"; then
python_path="-I$python_path -I$plat_python_path" python_path="-I$python_path -I$plat_python_path"
@@ -179,7 +197,7 @@ $ac_distutils_result])
# join all versioning strings, on some systems # join all versioning strings, on some systems
# major/minor numbers could be in different list elements # major/minor numbers could be in different list elements
from distutils.sysconfig import * from sysconfig import *
e = get_config_var('VERSION') e = get_config_var('VERSION')
if e is not None: if e is not None:
print(e) print(e)
@@ -202,8 +220,8 @@ EOD`
ac_python_libdir=`cat<<EOD | $PYTHON - ac_python_libdir=`cat<<EOD | $PYTHON -
# There should be only one # There should be only one
import distutils.sysconfig $IMPORT_SYSCONFIG
e = distutils.sysconfig.get_config_var('LIBDIR') e = sysconfig.get_config_var('LIBDIR')
if e is not None: if e is not None:
print (e) print (e)
EOD` EOD`
@@ -211,8 +229,8 @@ EOD`
# Now, for the library: # Now, for the library:
ac_python_library=`cat<<EOD | $PYTHON - ac_python_library=`cat<<EOD | $PYTHON -
import distutils.sysconfig $IMPORT_SYSCONFIG
c = distutils.sysconfig.get_config_vars() c = sysconfig.get_config_vars()
if 'LDVERSION' in c: if 'LDVERSION' in c:
print ('python'+c[['LDVERSION']]) print ('python'+c[['LDVERSION']])
else: else:
@@ -231,7 +249,7 @@ EOD`
else else
# old way: use libpython from python_configdir # old way: use libpython from python_configdir
ac_python_libdir=`$PYTHON -c \ ac_python_libdir=`$PYTHON -c \
"from distutils.sysconfig import get_python_lib as f; \ "from sysconfig import get_python_lib as f; \
import os; \ import os; \
print (os.path.join(f(plat_specific=1, standard_lib=1), 'config'));"` print (os.path.join(f(plat_specific=1, standard_lib=1), 'config'));"`
PYTHON_LIBS="-L$ac_python_libdir -lpython$ac_python_version" PYTHON_LIBS="-L$ac_python_libdir -lpython$ac_python_version"
@@ -252,19 +270,42 @@ EOD`
# #
AC_MSG_CHECKING([for Python site-packages path]) AC_MSG_CHECKING([for Python site-packages path])
if test -z "$PYTHON_SITE_PKG"; then if test -z "$PYTHON_SITE_PKG"; then
PYTHON_SITE_PKG=`$PYTHON -c "import distutils.sysconfig; \ if test "$IMPORT_SYSCONFIG" = "import sysconfig"; then
print (distutils.sysconfig.get_python_lib(0,0));"` PYTHON_SITE_PKG=`$PYTHON -c "$IMPORT_SYSCONFIG; \
print (sysconfig.get_path('purelib'));"`
else
# distutils.sysconfig way
PYTHON_SITE_PKG=`$PYTHON -c "$IMPORT_SYSCONFIG; \
print (sysconfig.get_python_lib(0,0));"`
fi
fi fi
AC_MSG_RESULT([$PYTHON_SITE_PKG]) AC_MSG_RESULT([$PYTHON_SITE_PKG])
AC_SUBST([PYTHON_SITE_PKG]) AC_SUBST([PYTHON_SITE_PKG])
#
# Check for platform-specific site packages
#
AC_MSG_CHECKING([for Python platform specific site-packages path])
if test -z "$PYTHON_SITE_PKG"; then
if test "$IMPORT_SYSCONFIG" = "import sysconfig"; then
PYTHON_PLATFORM_SITE_PKG=`$PYTHON -c "$IMPORT_SYSCONFIG; \
print (sysconfig.get_path('platlib'));"`
else
# distutils.sysconfig way
PYTHON_PLATFORM_SITE_PKG=`$PYTHON -c "$IMPORT_SYSCONFIG; \
print (sysconfig.get_python_lib(1,0));"`
fi
fi
AC_MSG_RESULT([$PYTHON_PLATFORM_SITE_PKG])
AC_SUBST([PYTHON_PLATFORM_SITE_PKG])
# #
# libraries which must be linked in when embedding # libraries which must be linked in when embedding
# #
AC_MSG_CHECKING(python extra libraries) AC_MSG_CHECKING(python extra libraries)
if test -z "$PYTHON_EXTRA_LIBS"; then if test -z "$PYTHON_EXTRA_LIBS"; then
PYTHON_EXTRA_LIBS=`$PYTHON -c "import distutils.sysconfig; \ PYTHON_EXTRA_LIBS=`$PYTHON -c "$IMPORT_SYSCONFIG; \
conf = distutils.sysconfig.get_config_var; \ conf = sysconfig.get_config_var; \
print (conf('LIBS') + ' ' + conf('SYSLIBS'))"` print (conf('LIBS') + ' ' + conf('SYSLIBS'))"`
fi fi
AC_MSG_RESULT([$PYTHON_EXTRA_LIBS]) AC_MSG_RESULT([$PYTHON_EXTRA_LIBS])
@@ -275,8 +316,8 @@ EOD`
# #
AC_MSG_CHECKING(python extra linking flags) AC_MSG_CHECKING(python extra linking flags)
if test -z "$PYTHON_EXTRA_LDFLAGS"; then if test -z "$PYTHON_EXTRA_LDFLAGS"; then
PYTHON_EXTRA_LDFLAGS=`$PYTHON -c "import distutils.sysconfig; \ PYTHON_EXTRA_LDFLAGS=`$PYTHON -c "$IMPORT_SYSCONFIG; \
conf = distutils.sysconfig.get_config_var; \ conf = sysconfig.get_config_var; \
print (conf('LINKFORSHARED'))"` print (conf('LINKFORSHARED'))"`
fi fi
AC_MSG_RESULT([$PYTHON_EXTRA_LDFLAGS]) AC_MSG_RESULT([$PYTHON_EXTRA_LDFLAGS])

View File

@@ -701,7 +701,7 @@ cdef class _HTTP2SessionCoreBase:
if outbuflen == 0: if outbuflen == 0:
break break
if outbuflen < 0: if outbuflen < 0:
raise Exception('nghttp2_session_mem_send faild: {}'.format\ raise Exception('nghttp2_session_mem_send failed: {}'.format\
(_strerror(outbuflen))) (_strerror(outbuflen)))
self.transport.write(outbuf[:outbuflen]) self.transport.write(outbuf[:outbuflen])
@@ -1057,8 +1057,7 @@ if asyncio:
"""HTTP/2 request (stream) handler base class. """HTTP/2 request (stream) handler base class.
The class is used to handle the HTTP/2 stream. By default, it does The class is used to handle the HTTP/2 stream. By default, it does
not nothing. It must be subclassed to handle each event callback nothing. It must be subclassed to handle each event callback method.
method.
The first callback method invoked is on_headers(). It is called The first callback method invoked is on_headers(). It is called
when HEADERS frame, which includes request header fields, is when HEADERS frame, which includes request header fields, is
@@ -1084,7 +1083,7 @@ if asyncio:
address. address.
client_certificate client_certificate
May contain the client certifcate in its non-binary form May contain the client certificate in its non-binary form
stream_id stream_id
Stream ID of this stream Stream ID of this stream

View File

@@ -15,10 +15,14 @@ include_directories(
${JEMALLOC_INCLUDE_DIRS} ${JEMALLOC_INCLUDE_DIRS}
${LIBXML2_INCLUDE_DIRS} ${LIBXML2_INCLUDE_DIRS}
${LIBEV_INCLUDE_DIRS} ${LIBEV_INCLUDE_DIRS}
${LIBNGHTTP3_INCLUDE_DIRS}
${LIBNGTCP2_INCLUDE_DIRS}
${LIBNGTCP2_CRYPTO_OPENSSL_INCLUDE_DIRS}
${OPENSSL_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIRS}
${LIBCARES_INCLUDE_DIRS} ${LIBCARES_INCLUDE_DIRS}
${JANSSON_INCLUDE_DIRS} ${JANSSON_INCLUDE_DIRS}
${ZLIB_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIRS}
${LIBBPF_INCLUDE_DIRS}
) )
# XXX per-target? # XXX per-target?
@@ -27,11 +31,15 @@ link_libraries(
${JEMALLOC_LIBRARIES} ${JEMALLOC_LIBRARIES}
${LIBXML2_LIBRARIES} ${LIBXML2_LIBRARIES}
${LIBEV_LIBRARIES} ${LIBEV_LIBRARIES}
${LIBNGHTTP3_LIBRARIES}
${LIBNGTCP2_LIBRARIES}
${LIBNGTCP2_CRYPTO_OPENSSL_LIBRARIES}
${OPENSSL_LIBRARIES} ${OPENSSL_LIBRARIES}
${LIBCARES_LIBRARIES} ${LIBCARES_LIBRARIES}
${JANSSON_LIBRARIES} ${JANSSON_LIBRARIES}
${ZLIB_LIBRARIES} ${ZLIB_LIBRARIES}
${APP_LIBRARIES} ${APP_LIBRARIES}
${LIBBPF_LIBRARIES}
) )
if(ENABLE_APP) if(ENABLE_APP)
@@ -67,7 +75,13 @@ if(ENABLE_APP)
h2load_http2_session.cc h2load_http2_session.cc
h2load_http1_session.cc h2load_http1_session.cc
) )
if(ENABLE_HTTP3)
list(APPEND H2LOAD_SOURCES
h2load_http3_session.cc
h2load_quic.cc
quic.cc
)
endif()
# Common libnhttpx sources (used for nghttpx and unit tests) # Common libnhttpx sources (used for nghttpx and unit tests)
set(NGHTTPX_SRCS set(NGHTTPX_SRCS
@@ -104,6 +118,7 @@ if(ENABLE_APP)
shrpx_router.cc shrpx_router.cc
shrpx_api_downstream_connection.cc shrpx_api_downstream_connection.cc
shrpx_health_monitor_downstream_connection.cc shrpx_health_monitor_downstream_connection.cc
shrpx_null_downstream_connection.cc
shrpx_exec.cc shrpx_exec.cc
shrpx_dns_resolver.cc shrpx_dns_resolver.cc
shrpx_dual_dns_resolver.cc shrpx_dual_dns_resolver.cc
@@ -119,6 +134,16 @@ if(ENABLE_APP)
shrpx_mruby_module_response.cc shrpx_mruby_module_response.cc
) )
endif() endif()
if(ENABLE_HTTP3)
list(APPEND NGHTTPX_SRCS
shrpx_quic.cc
shrpx_quic_listener.cc
shrpx_quic_connection_handler.cc
shrpx_http3_upstream.cc
http3.cc
quic.cc
)
endif()
add_library(nghttpx_static STATIC ${NGHTTPX_SRCS}) add_library(nghttpx_static STATIC ${NGHTTPX_SRCS})
set_target_properties(nghttpx_static PROPERTIES ARCHIVE_OUTPUT_NAME nghttpx) set_target_properties(nghttpx_static PROPERTIES ARCHIVE_OUTPUT_NAME nghttpx)
@@ -189,7 +214,10 @@ if(ENABLE_APP)
add_executable(nghttpx ${NGHTTPX-bin_SOURCES} $<TARGET_OBJECTS:llhttp> add_executable(nghttpx ${NGHTTPX-bin_SOURCES} $<TARGET_OBJECTS:llhttp>
$<TARGET_OBJECTS:url-parser> $<TARGET_OBJECTS:url-parser>
) )
target_compile_definitions(nghttpx PRIVATE "-DPKGDATADIR=\"${PKGDATADIR}\"") target_compile_definitions(nghttpx PRIVATE
"-DPKGDATADIR=\"${PKGDATADIR}\""
"-DPKGLIBDIR=\"${PKGLIBDIR}\""
)
target_link_libraries(nghttpx nghttpx_static) target_link_libraries(nghttpx nghttpx_static)
add_executable(h2load ${H2LOAD_SOURCES} $<TARGET_OBJECTS:llhttp> add_executable(h2load ${H2LOAD_SOURCES} $<TARGET_OBJECTS:llhttp>
$<TARGET_OBJECTS:url-parser> $<TARGET_OBJECTS:url-parser>

View File

@@ -52,8 +52,13 @@
#include <mutex> #include <mutex>
#include <deque> #include <deque>
#include "ssl_compat.h"
#include <openssl/err.h> #include <openssl/err.h>
#include <openssl/dh.h> #include <openssl/dh.h>
#if OPENSSL_3_0_0_API
# include <openssl/decoder.h>
#endif // OPENSSL_3_0_0_API
#include <zlib.h> #include <zlib.h>
@@ -2105,7 +2110,7 @@ int HttpServer::run() {
std::vector<unsigned char> next_proto; std::vector<unsigned char> next_proto;
if (!config_->no_tls) { if (!config_->no_tls) {
ssl_ctx = SSL_CTX_new(SSLv23_server_method()); ssl_ctx = SSL_CTX_new(TLS_server_method());
if (!ssl_ctx) { if (!ssl_ctx) {
std::cerr << ERR_error_string(ERR_get_error(), nullptr) << std::endl; std::cerr << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
return -1; return -1;
@@ -2138,15 +2143,13 @@ int HttpServer::run() {
SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_SERVER); SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_SERVER);
#ifndef OPENSSL_NO_EC #ifndef OPENSSL_NO_EC
# if !LIBRESSL_LEGACY_API && OPENSSL_VERSION_NUMBER >= 0x10002000L
// Disabled SSL_CTX_set_ecdh_auto, because computational cost of if (SSL_CTX_set1_curves_list(ssl_ctx, "P-256") != 1) {
// chosen curve is much higher than P-256. std::cerr << "SSL_CTX_set1_curves_list failed: "
<< ERR_error_string(ERR_get_error(), nullptr);
// #if OPENSSL_VERSION_NUMBER >= 0x10002000L return -1;
// SSL_CTX_set_ecdh_auto(ssl_ctx, 1); }
// #else // OPENSSL_VERSION_NUBMER < 0x10002000L # else // !(!LIBRESSL_LEGACY_API && OPENSSL_VERSION_NUMBER >= 0x10002000L)
// Use P-256, which is sufficiently secure at the time of this
// writing.
auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
if (ecdh == nullptr) { if (ecdh == nullptr) {
std::cerr << "EC_KEY_new_by_curv_name failed: " std::cerr << "EC_KEY_new_by_curv_name failed: "
@@ -2155,19 +2158,36 @@ int HttpServer::run() {
} }
SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh); SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh);
EC_KEY_free(ecdh); EC_KEY_free(ecdh);
// #endif // OPENSSL_VERSION_NUBMER < 0x10002000L # endif // !(!LIBRESSL_LEGACY_API && OPENSSL_VERSION_NUMBER >= 0x10002000L)
#endif // OPENSSL_NO_EC #endif // OPENSSL_NO_EC
if (!config_->dh_param_file.empty()) { if (!config_->dh_param_file.empty()) {
// Read DH parameters from file // Read DH parameters from file
auto bio = BIO_new_file(config_->dh_param_file.c_str(), "r"); auto bio = BIO_new_file(config_->dh_param_file.c_str(), "rb");
if (bio == nullptr) { if (bio == nullptr) {
std::cerr << "BIO_new_file() failed: " std::cerr << "BIO_new_file() failed: "
<< ERR_error_string(ERR_get_error(), nullptr) << std::endl; << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
return -1; return -1;
} }
#if OPENSSL_3_0_0_API
EVP_PKEY *dh = nullptr;
auto dctx = OSSL_DECODER_CTX_new_for_pkey(
&dh, "PEM", nullptr, "DH", OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS,
nullptr, nullptr);
if (!OSSL_DECODER_from_bio(dctx, bio)) {
std::cerr << "OSSL_DECODER_from_bio() failed: "
<< ERR_error_string(ERR_get_error(), nullptr) << std::endl;
return -1;
}
if (SSL_CTX_set0_tmp_dh_pkey(ssl_ctx, dh) != 1) {
std::cerr << "SSL_CTX_set0_tmp_dh_pkey failed: "
<< ERR_error_string(ERR_get_error(), nullptr) << std::endl;
return -1;
}
#else // !OPENSSL_3_0_0_API
auto dh = PEM_read_bio_DHparams(bio, nullptr, nullptr, nullptr); auto dh = PEM_read_bio_DHparams(bio, nullptr, nullptr, nullptr);
if (dh == nullptr) { if (dh == nullptr) {
@@ -2178,6 +2198,7 @@ int HttpServer::run() {
SSL_CTX_set_tmp_dh(ssl_ctx, dh); SSL_CTX_set_tmp_dh(ssl_ctx, dh);
DH_free(dh); DH_free(dh);
#endif // !OPENSSL_3_0_0_API
BIO_free(bio); BIO_free(bio);
} }

View File

@@ -242,7 +242,7 @@ private:
}; };
ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id, ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
uint8_t *buf, size_t length, int *eof, uint8_t *buf, size_t length, uint32_t *data_flags,
nghttp2_data_source *source, void *user_data); nghttp2_data_source *source, void *user_data);
} // namespace nghttp2 } // namespace nghttp2

View File

@@ -35,6 +35,7 @@ AM_CFLAGS = $(WARNCFLAGS)
AM_CXXFLAGS = $(WARNCXXFLAGS) $(CXX1XCXXFLAGS) AM_CXXFLAGS = $(WARNCXXFLAGS) $(CXX1XCXXFLAGS)
AM_CPPFLAGS = \ AM_CPPFLAGS = \
-DPKGDATADIR='"$(pkgdatadir)"' \ -DPKGDATADIR='"$(pkgdatadir)"' \
-DPKGLIBDIR='"$(pkglibdir)"' \
-I$(top_srcdir)/lib/includes \ -I$(top_srcdir)/lib/includes \
-I$(top_builddir)/lib/includes \ -I$(top_builddir)/lib/includes \
-I$(top_srcdir)/lib \ -I$(top_srcdir)/lib \
@@ -44,10 +45,16 @@ AM_CPPFLAGS = \
@JEMALLOC_CFLAGS@ \ @JEMALLOC_CFLAGS@ \
@LIBXML2_CFLAGS@ \ @LIBXML2_CFLAGS@ \
@LIBEV_CFLAGS@ \ @LIBEV_CFLAGS@ \
@LIBNGHTTP3_CFLAGS@ \
@LIBNGTCP2_CRYPTO_OPENSSL_CFLAGS@ \
@LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS@ \
@LIBNGTCP2_CFLAGS@ \
@OPENSSL_CFLAGS@ \ @OPENSSL_CFLAGS@ \
@LIBCARES_CFLAGS@ \ @LIBCARES_CFLAGS@ \
@JANSSON_CFLAGS@ \ @JANSSON_CFLAGS@ \
@LIBBPF_CFLAGS@ \
@ZLIB_CFLAGS@ \ @ZLIB_CFLAGS@ \
@EXTRA_DEFS@ \
@DEFS@ @DEFS@
AM_LDFLAGS = @LIBTOOL_LDFLAGS@ AM_LDFLAGS = @LIBTOOL_LDFLAGS@
@@ -57,10 +64,15 @@ LDADD = $(top_builddir)/lib/libnghttp2.la \
@JEMALLOC_LIBS@ \ @JEMALLOC_LIBS@ \
@LIBXML2_LIBS@ \ @LIBXML2_LIBS@ \
@LIBEV_LIBS@ \ @LIBEV_LIBS@ \
@LIBNGHTTP3_LIBS@ \
@LIBNGTCP2_CRYPTO_OPENSSL_LIBS@ \
@LIBNGTCP2_CRYPTO_BORINGSSL_LIBS@ \
@LIBNGTCP2_LIBS@ \
@OPENSSL_LIBS@ \ @OPENSSL_LIBS@ \
@LIBCARES_LIBS@ \ @LIBCARES_LIBS@ \
@SYSTEMD_LIBS@ \ @SYSTEMD_LIBS@ \
@JANSSON_LIBS@ \ @JANSSON_LIBS@ \
@LIBBPF_LIBS@ \
@ZLIB_LIBS@ \ @ZLIB_LIBS@ \
@APPLDFLAGS@ @APPLDFLAGS@
@@ -99,6 +111,13 @@ h2load_SOURCES = util.cc util.h \
h2load_http2_session.cc h2load_http2_session.h \ h2load_http2_session.cc h2load_http2_session.h \
h2load_http1_session.cc h2load_http1_session.h h2load_http1_session.cc h2load_http1_session.h
if ENABLE_HTTP3
h2load_SOURCES += \
h2load_http3_session.cc h2load_http3_session.h \
h2load_quic.cc h2load_quic.h \
quic.cc quic.h
endif # ENABLE_HTTP3
NGHTTPX_SRCS = \ NGHTTPX_SRCS = \
util.cc util.h http2.cc http2.h timegm.c timegm.h base64.h \ util.cc util.h http2.cc http2.h timegm.c timegm.h base64.h \
app_helper.cc app_helper.h \ app_helper.cc app_helper.h \
@@ -139,6 +158,7 @@ NGHTTPX_SRCS = \
shrpx_api_downstream_connection.cc shrpx_api_downstream_connection.h \ shrpx_api_downstream_connection.cc shrpx_api_downstream_connection.h \
shrpx_health_monitor_downstream_connection.cc \ shrpx_health_monitor_downstream_connection.cc \
shrpx_health_monitor_downstream_connection.h \ shrpx_health_monitor_downstream_connection.h \
shrpx_null_downstream_connection.cc shrpx_null_downstream_connection.h \
shrpx_exec.cc shrpx_exec.h \ shrpx_exec.cc shrpx_exec.h \
shrpx_dns_resolver.cc shrpx_dns_resolver.h \ shrpx_dns_resolver.cc shrpx_dns_resolver.h \
shrpx_dual_dns_resolver.cc shrpx_dual_dns_resolver.h \ shrpx_dual_dns_resolver.cc shrpx_dual_dns_resolver.h \
@@ -155,6 +175,16 @@ NGHTTPX_SRCS += \
shrpx_mruby_module_response.cc shrpx_mruby_module_response.h shrpx_mruby_module_response.cc shrpx_mruby_module_response.h
endif # HAVE_MRUBY endif # HAVE_MRUBY
if ENABLE_HTTP3
NGHTTPX_SRCS += \
shrpx_quic.cc shrpx_quic.h \
shrpx_quic_listener.cc shrpx_quic_listener.h \
shrpx_quic_connection_handler.cc shrpx_quic_connection_handler.h \
shrpx_http3_upstream.cc shrpx_http3_upstream.h \
http3.cc http3.h \
quic.cc quic.h
endif # ENABLE_HTTP3
noinst_LIBRARIES = libnghttpx.a noinst_LIBRARIES = libnghttpx.a
libnghttpx_a_SOURCES = ${NGHTTPX_SRCS} libnghttpx_a_SOURCES = ${NGHTTPX_SRCS}
libnghttpx_a_CPPFLAGS = ${AM_CPPFLAGS} libnghttpx_a_CPPFLAGS = ${AM_CPPFLAGS}

View File

@@ -34,6 +34,8 @@
#ifdef HAVE_FCNTL_H #ifdef HAVE_FCNTL_H
# include <fcntl.h> # include <fcntl.h>
#endif // HAVE_FCNTL_H #endif // HAVE_FCNTL_H
#include <sys/mman.h>
#include <netinet/udp.h>
#include <cstdio> #include <cstdio>
#include <cassert> #include <cassert>
@@ -48,10 +50,18 @@
#include <openssl/err.h> #include <openssl/err.h>
#ifdef ENABLE_HTTP3
# include <ngtcp2/ngtcp2.h>
#endif // ENABLE_HTTP3
#include "url-parser/url_parser.h" #include "url-parser/url_parser.h"
#include "h2load_http1_session.h" #include "h2load_http1_session.h"
#include "h2load_http2_session.h" #include "h2load_http2_session.h"
#ifdef ENABLE_HTTP3
# include "h2load_http3_session.h"
# include "h2load_quic.h"
#endif // ENABLE_HTTP3
#include "tls.h" #include "tls.h"
#include "http2.h" #include "http2.h"
#include "util.h" #include "util.h"
@@ -71,9 +81,24 @@ bool recorded(const std::chrono::steady_clock::time_point &t) {
} }
} // namespace } // namespace
#if OPENSSL_1_1_1_API
namespace {
std::ofstream keylog_file;
void keylog_callback(const SSL *ssl, const char *line) {
keylog_file.write(line, strlen(line));
keylog_file.put('\n');
keylog_file.flush();
}
} // namespace
#endif // OPENSSL_1_1_1_API
Config::Config() Config::Config()
: ciphers(tls::DEFAULT_CIPHER_LIST), : ciphers(tls::DEFAULT_CIPHER_LIST),
tls13_ciphers("TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_"
"CHACHA20_POLY1305_SHA256:TLS_AES_128_CCM_SHA256"),
groups("X25519:P-256:P-384:P-521"),
data_length(-1), data_length(-1),
data(nullptr),
addrs(nullptr), addrs(nullptr),
nreqs(1), nreqs(1),
nclients(1), nclients(1),
@@ -81,6 +106,7 @@ Config::Config()
max_concurrent_streams(1), max_concurrent_streams(1),
window_bits(30), window_bits(30),
connection_window_bits(30), connection_window_bits(30),
max_frame_size(16_k),
rate(0), rate(0),
rate_period(1.0), rate_period(1.0),
duration(0.0), duration(0.0),
@@ -92,6 +118,7 @@ Config::Config()
encoder_header_table_size(4_k), encoder_header_table_size(4_k),
data_fd(-1), data_fd(-1),
log_fd(-1), log_fd(-1),
qlog_file_base(),
port(0), port(0),
default_port(0), default_port(0),
connect_to_port(0), connect_to_port(0),
@@ -99,7 +126,9 @@ Config::Config()
timing_script(false), timing_script(false),
base_uri_unix(false), base_uri_unix(false),
unix_addr{}, unix_addr{},
rps(0.) {} rps(0.),
no_udp_gso(false),
max_udp_payload_size(0) {}
Config::~Config() { Config::~Config() {
if (addrs) { if (addrs) {
@@ -119,6 +148,14 @@ bool Config::is_rate_mode() const { return (this->rate != 0); }
bool Config::is_timing_based_mode() const { return (this->duration > 0); } bool Config::is_timing_based_mode() const { return (this->duration > 0); }
bool Config::has_base_uri() const { return (!this->base_uri.empty()); } bool Config::has_base_uri() const { return (!this->base_uri.empty()); }
bool Config::rps_enabled() const { return this->rps > 0.0; } bool Config::rps_enabled() const { return this->rps > 0.0; }
bool Config::is_quic() const {
#ifdef ENABLE_HTTP3
return !npn_list.empty() &&
(npn_list[0] == NGHTTP3_ALPN_H3 || npn_list[0] == "\x5h3-29");
#else // !ENABLE_HTTP3
return false;
#endif // !ENABLE_HTTP3
}
Config config; Config config;
namespace { namespace {
@@ -138,7 +175,9 @@ Stats::Stats(size_t req_todo, size_t nclients)
bytes_head(0), bytes_head(0),
bytes_head_decomp(0), bytes_head_decomp(0),
bytes_body(0), bytes_body(0),
status() {} status(),
udp_dgram_recv(0),
udp_dgram_sent(0) {}
Stream::Stream() : req_stat{}, status_success(-1) {} Stream::Stream() : req_stat{}, status_success(-1) {}
@@ -195,8 +234,7 @@ void readcb(struct ev_loop *loop, ev_io *w, int revents) {
delete client; delete client;
return; return;
} }
writecb(loop, &client->wev, revents); client->signal_write();
// client->disconnect() and client->fail() may be called
} }
} // namespace } // namespace
@@ -409,6 +447,9 @@ Client::Client(uint32_t id, Worker *worker, size_t req_todo)
cstat{}, cstat{},
worker(worker), worker(worker),
ssl(nullptr), ssl(nullptr),
#ifdef ENABLE_HTTP3
quic{},
#endif // ENABLE_HTTP3
next_addr(config.addrs), next_addr(config.addrs),
current_addr(nullptr), current_addr(nullptr),
reqidx(0), reqidx(0),
@@ -420,6 +461,7 @@ Client::Client(uint32_t id, Worker *worker, size_t req_todo)
req_done(0), req_done(0),
id(id), id(id),
fd(-1), fd(-1),
local_addr{},
new_connection_requested(false), new_connection_requested(false),
final(false), final(false),
rps_duration_started(0), rps_duration_started(0),
@@ -449,11 +491,22 @@ Client::Client(uint32_t id, Worker *worker, size_t req_todo)
ev_timer_init(&rps_watcher, rps_cb, 0., 0.); ev_timer_init(&rps_watcher, rps_cb, 0., 0.);
rps_watcher.data = this; rps_watcher.data = this;
#ifdef ENABLE_HTTP3
ev_timer_init(&quic.pkt_timer, quic_pkt_timeout_cb, 0., 0.);
quic.pkt_timer.data = this;
#endif // ENABLE_HTTP3
} }
Client::~Client() { Client::~Client() {
disconnect(); disconnect();
#ifdef ENABLE_HTTP3
if (config.is_quic()) {
quic_free();
}
#endif // ENABLE_HTTP3
if (ssl) { if (ssl) {
SSL_free(ssl); SSL_free(ssl);
} }
@@ -466,6 +519,36 @@ int Client::do_read() { return readfn(*this); }
int Client::do_write() { return writefn(*this); } int Client::do_write() { return writefn(*this); }
int Client::make_socket(addrinfo *addr) { int Client::make_socket(addrinfo *addr) {
int rv;
if (config.is_quic()) {
#ifdef ENABLE_HTTP3
fd = util::create_nonblock_udp_socket(addr->ai_family);
if (fd == -1) {
return -1;
}
rv = util::bind_any_addr_udp(fd, addr->ai_family);
if (rv != 0) {
close(fd);
fd = -1;
return -1;
}
socklen_t addrlen = sizeof(local_addr.su.storage);
rv = getsockname(fd, &local_addr.su.sa, &addrlen);
if (rv == -1) {
return -1;
}
local_addr.len = addrlen;
if (quic_init(&local_addr.su.sa, local_addr.len, addr->ai_addr,
addr->ai_addrlen) != 0) {
std::cerr << "quic_init failed" << std::endl;
return -1;
}
#endif // ENABLE_HTTP3
} else {
fd = util::create_nonblock_socket(addr->ai_family); fd = util::create_nonblock_socket(addr->ai_family);
if (fd == -1) { if (fd == -1) {
return -1; return -1;
@@ -475,17 +558,20 @@ int Client::make_socket(addrinfo *addr) {
ssl = SSL_new(worker->ssl_ctx); ssl = SSL_new(worker->ssl_ctx);
} }
auto config = worker->config;
if (!util::numeric_host(config->host.c_str())) {
SSL_set_tlsext_host_name(ssl, config->host.c_str());
}
SSL_set_fd(ssl, fd); SSL_set_fd(ssl, fd);
SSL_set_connect_state(ssl); SSL_set_connect_state(ssl);
} }
}
auto rv = ::connect(fd, addr->ai_addr, addr->ai_addrlen); if (ssl && !util::numeric_host(config.host.c_str())) {
SSL_set_tlsext_host_name(ssl, config.host.c_str());
}
if (config.is_quic()) {
return 0;
}
rv = ::connect(fd, addr->ai_addr, addr->ai_addrlen);
if (rv != 0 && errno != EINPROGRESS) { if (rv != 0 && errno != EINPROGRESS) {
if (ssl) { if (ssl) {
SSL_free(ssl); SSL_free(ssl);
@@ -542,13 +628,22 @@ int Client::connect() {
current_addr = addr; current_addr = addr;
} }
writefn = &Client::connected;
ev_io_set(&rev, fd, EV_READ); ev_io_set(&rev, fd, EV_READ);
ev_io_set(&wev, fd, EV_WRITE); ev_io_set(&wev, fd, EV_WRITE);
ev_io_start(worker->loop, &wev); ev_io_start(worker->loop, &wev);
if (config.is_quic()) {
#ifdef ENABLE_HTTP3
ev_io_start(worker->loop, &rev);
readfn = &Client::read_quic;
writefn = &Client::write_quic;
#endif // ENABLE_HTTP3
} else {
writefn = &Client::connected;
}
return 0; return 0;
} }
@@ -603,6 +698,15 @@ void Client::fail() {
void Client::disconnect() { void Client::disconnect() {
record_client_end_time(); record_client_end_time();
#ifdef ENABLE_HTTP3
if (config.is_quic()) {
quic_close_connection();
}
#endif // ENABLE_HTTP3
#ifdef ENABLE_HTTP3
ev_timer_stop(worker->loop, &quic.pkt_timer);
#endif // ENABLE_HTTP3
ev_timer_stop(worker->loop, &conn_inactivity_watcher); ev_timer_stop(worker->loop, &conn_inactivity_watcher);
ev_timer_stop(worker->loop, &conn_active_watcher); ev_timer_stop(worker->loop, &conn_active_watcher);
ev_timer_stop(worker->loop, &rps_watcher); ev_timer_stop(worker->loop, &rps_watcher);
@@ -726,6 +830,16 @@ void print_server_tmp_key(SSL *ssl) {
std::cout << "DH " << EVP_PKEY_bits(key) << " bits" << std::endl; std::cout << "DH " << EVP_PKEY_bits(key) << " bits" << std::endl;
break; break;
case EVP_PKEY_EC: { case EVP_PKEY_EC: {
# if OPENSSL_3_0_0_API
std::array<char, 64> curve_name;
const char *cname;
if (!EVP_PKEY_get_utf8_string_param(key, "group", curve_name.data(),
curve_name.size(), nullptr)) {
cname = "<unknown>";
} else {
cname = curve_name.data();
}
# else // !OPENSSL_3_0_0_API
auto ec = EVP_PKEY_get1_EC_KEY(key); auto ec = EVP_PKEY_get1_EC_KEY(key);
auto ec_del = defer(EC_KEY_free, ec); auto ec_del = defer(EC_KEY_free, ec);
auto nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec)); auto nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec));
@@ -733,6 +847,7 @@ void print_server_tmp_key(SSL *ssl) {
if (!cname) { if (!cname) {
cname = OBJ_nid2sn(nid); cname = OBJ_nid2sn(nid);
} }
# endif // !OPENSSL_3_0_0_API
std::cout << "ECDH " << cname << " " << EVP_PKEY_bits(key) << " bits" std::cout << "ECDH " << cname << " " << EVP_PKEY_bits(key) << " bits"
<< std::endl; << std::endl;
@@ -765,7 +880,14 @@ void Client::report_app_info() {
} }
void Client::terminate_session() { void Client::terminate_session() {
#ifdef ENABLE_HTTP3
if (config.is_quic()) {
quic.close_requested = true;
}
#endif // ENABLE_HTTP3
if (session) {
session->terminate(); session->terminate();
}
// http1 session needs writecb to tear down session. // http1 session needs writecb to tear down session.
signal_write(); signal_write();
} }
@@ -963,7 +1085,15 @@ int Client::connection_made() {
if (next_proto) { if (next_proto) {
auto proto = StringRef{next_proto, next_proto_len}; auto proto = StringRef{next_proto, next_proto_len};
if (util::check_h2_is_selected(proto)) { if (config.is_quic()) {
#ifdef ENABLE_HTTP3
assert(session);
if (!util::streq(StringRef{&NGHTTP3_ALPN_H3[1]}, proto) &&
!util::streq_l("h3-29", proto)) {
return -1;
}
#endif // ENABLE_HTTP3
} else if (util::check_h2_is_selected(proto)) {
session = std::make_unique<Http2Session>(this); session = std::make_unique<Http2Session>(this);
} else if (util::streq(NGHTTP2_H1_1, proto)) { } else if (util::streq(NGHTTP2_H1_1, proto)) {
session = std::make_unique<Http1Session>(this); session = std::make_unique<Http1Session>(this);
@@ -972,6 +1102,9 @@ int Client::connection_made() {
// Just assign next_proto to selected_proto anyway to show the // Just assign next_proto to selected_proto anyway to show the
// negotiation result. // negotiation result.
selected_proto = proto.str(); selected_proto = proto.str();
} else if (config.is_quic()) {
std::cerr << "QUIC requires ALPN negotiation" << std::endl;
return -1;
} else { } else {
std::cout << "No protocol negotiated. Fallback behaviour may be activated" std::cout << "No protocol negotiated. Fallback behaviour may be activated"
<< std::endl; << std::endl;
@@ -1285,6 +1418,46 @@ int Client::write_tls() {
return 0; return 0;
} }
#ifdef ENABLE_HTTP3
int Client::write_udp(const sockaddr *addr, socklen_t addrlen,
const uint8_t *data, size_t datalen, size_t gso_size) {
iovec msg_iov;
msg_iov.iov_base = const_cast<uint8_t *>(data);
msg_iov.iov_len = datalen;
msghdr msg{};
msg.msg_name = const_cast<sockaddr *>(addr);
msg.msg_namelen = addrlen;
msg.msg_iov = &msg_iov;
msg.msg_iovlen = 1;
# ifdef UDP_SEGMENT
std::array<uint8_t, CMSG_SPACE(sizeof(uint16_t))> msg_ctrl{};
if (gso_size && datalen > gso_size) {
msg.msg_control = msg_ctrl.data();
msg.msg_controllen = msg_ctrl.size();
auto cm = CMSG_FIRSTHDR(&msg);
cm->cmsg_level = SOL_UDP;
cm->cmsg_type = UDP_SEGMENT;
cm->cmsg_len = CMSG_LEN(sizeof(uint16_t));
*(reinterpret_cast<uint16_t *>(CMSG_DATA(cm))) = gso_size;
}
# endif // UDP_SEGMENT
auto nwrite = sendmsg(fd, &msg, 0);
if (nwrite < 0) {
std::cerr << "sendto: errno=" << errno << std::endl;
} else {
++worker->stats.udp_dgram_sent;
}
ev_io_stop(worker->loop, &wev);
return 0;
}
#endif // ENABLE_HTTP3
void Client::record_request_time(RequestStat *req_stat) { void Client::record_request_time(RequestStat *req_stat) {
req_stat->request_time = std::chrono::steady_clock::now(); req_stat->request_time = std::chrono::steady_clock::now();
req_stat->request_wall_time = std::chrono::system_clock::now(); req_stat->request_wall_time = std::chrono::system_clock::now();
@@ -1344,7 +1517,8 @@ int get_ev_loop_flags() {
Worker::Worker(uint32_t id, SSL_CTX *ssl_ctx, size_t req_todo, size_t nclients, Worker::Worker(uint32_t id, SSL_CTX *ssl_ctx, size_t req_todo, size_t nclients,
size_t rate, size_t max_samples, Config *config) size_t rate, size_t max_samples, Config *config)
: stats(req_todo, nclients), : randgen(util::make_mt19937()),
stats(req_todo, nclients),
loop(ev_loop_new(get_ev_loop_flags())), loop(ev_loop_new(get_ev_loop_flags())),
ssl_ctx(ssl_ctx), ssl_ctx(ssl_ctx),
config(config), config(config),
@@ -1403,7 +1577,7 @@ Worker::~Worker() {
void Worker::stop_all_clients() { void Worker::stop_all_clients() {
for (auto client : clients) { for (auto client : clients) {
if (client && client->session) { if (client) {
client->terminate_session(); client->terminate_session();
} }
} }
@@ -1936,8 +2110,14 @@ Options:
http/1.1 is used, this specifies the number of HTTP http/1.1 is used, this specifies the number of HTTP
pipelining requests in-flight. pipelining requests in-flight.
Default: 1 Default: 1
-f, --max-frame-size=<SIZE>
Maximum frame size that the local endpoint is willing to
receive.
Default: )"
<< util::utos_unit(config.max_frame_size) << R"(
-w, --window-bits=<N> -w, --window-bits=<N>
Sets the stream level initial window size to (2**<N>)-1. Sets the stream level initial window size to (2**<N>)-1.
For QUIC, <N> is capped to 26 (roughly 64MiB).
Default: )" Default: )"
<< config.window_bits << R"( << config.window_bits << R"(
-W, --connection-window-bits=<N> -W, --connection-window-bits=<N>
@@ -1948,10 +2128,15 @@ Options:
-H, --header=<HEADER> -H, --header=<HEADER>
Add/Override a header to the requests. Add/Override a header to the requests.
--ciphers=<SUITE> --ciphers=<SUITE>
Set allowed cipher list. The format of the string is Set allowed cipher list for TLSv1.2 or earlier. The
described in OpenSSL ciphers(1). format of the string is described in OpenSSL ciphers(1).
Default: )" Default: )"
<< config.ciphers << R"( << config.ciphers << R"(
--tls13-ciphers=<SUITE>
Set allowed cipher list for TLSv1.3. The format of the
string is described in OpenSSL ciphers(1).
Default: )"
<< config.tls13_ciphers << R"(
-p, --no-tls-proto=<PROTOID> -p, --no-tls-proto=<PROTOID>
Specify ALPN identifier of the protocol to be used when Specify ALPN identifier of the protocol to be used when
accessing http URI without SSL/TLS. accessing http URI without SSL/TLS.
@@ -2065,11 +2250,25 @@ Options:
response time when using one worker thread, but may response time when using one worker thread, but may
appear slightly out of order with multiple threads due appear slightly out of order with multiple threads due
to buffering. Status code is -1 for failed streams. to buffering. Status code is -1 for failed streams.
--qlog-file-base=<PATH>
Enable qlog output and specify base file name for qlogs.
Qlog is emitted for each connection. For a given base
name "base", each output file name becomes
"base.M.N.sqlog" where M is worker ID and N is client ID
(e.g. "base.0.3.sqlog"). Only effective in QUIC runs.
--connect-to=<HOST>[:<PORT>] --connect-to=<HOST>[:<PORT>]
Host and port to connect instead of using the authority Host and port to connect instead of using the authority
in <URI>. in <URI>.
--rps=<N> Specify request per second for each client. --rps and --rps=<N> Specify request per second for each client. --rps and
--timing-script-file are mutually exclusive. --timing-script-file are mutually exclusive.
--groups=<GROUPS>
Specify the supported groups.
Default: )"
<< config.groups << R"(
--no-udp-gso
Disable UDP GSO.
--max-udp-payload-size=<SIZE>
Specify the maximum outgoing UDP datagram payload size.
-v, --verbose -v, --verbose
Output debug information. Output debug information.
--version Display version information and exit. --version Display version information and exit.
@@ -2097,6 +2296,7 @@ int main(int argc, char **argv) {
std::string datafile; std::string datafile;
std::string logfile; std::string logfile;
std::string qlog_base;
bool nreqs_set_manually = false; bool nreqs_set_manually = false;
while (1) { while (1) {
static int flag = 0; static int flag = 0;
@@ -2107,6 +2307,7 @@ int main(int argc, char **argv) {
{"threads", required_argument, nullptr, 't'}, {"threads", required_argument, nullptr, 't'},
{"max-concurrent-streams", required_argument, nullptr, 'm'}, {"max-concurrent-streams", required_argument, nullptr, 'm'},
{"window-bits", required_argument, nullptr, 'w'}, {"window-bits", required_argument, nullptr, 'w'},
{"max-frame-size", required_argument, nullptr, 'f'},
{"connection-window-bits", required_argument, nullptr, 'W'}, {"connection-window-bits", required_argument, nullptr, 'W'},
{"input-file", required_argument, nullptr, 'i'}, {"input-file", required_argument, nullptr, 'i'},
{"header", required_argument, nullptr, 'H'}, {"header", required_argument, nullptr, 'H'},
@@ -2130,10 +2331,15 @@ int main(int argc, char **argv) {
{"log-file", required_argument, &flag, 10}, {"log-file", required_argument, &flag, 10},
{"connect-to", required_argument, &flag, 11}, {"connect-to", required_argument, &flag, 11},
{"rps", required_argument, &flag, 12}, {"rps", required_argument, &flag, 12},
{"groups", required_argument, &flag, 13},
{"tls13-ciphers", required_argument, &flag, 14},
{"no-udp-gso", no_argument, &flag, 15},
{"qlog-file-base", required_argument, &flag, 16},
{"max-udp-payload-size", required_argument, &flag, 17},
{nullptr, 0, nullptr, 0}}; {nullptr, 0, nullptr, 0}};
int option_index = 0; int option_index = 0;
auto c = getopt_long(argc, argv, auto c = getopt_long(argc, argv,
"hvW:c:d:m:n:p:t:w:H:i:r:T:N:D:B:", long_options, "hvW:c:d:m:n:p:t:w:f:H:i:r:T:N:D:B:", long_options,
&option_index); &option_index);
if (c == -1) { if (c == -1) {
break; break;
@@ -2179,6 +2385,24 @@ int main(int argc, char **argv) {
} }
break; break;
} }
case 'f': {
auto n = util::parse_uint_with_unit(optarg);
if (n == -1) {
std::cerr << "--max-frame-size: bad option value: " << optarg
<< std::endl;
exit(EXIT_FAILURE);
}
if (static_cast<uint64_t>(n) < 16_k) {
std::cerr << "--max-frame-size: minimum 16384" << std::endl;
exit(EXIT_FAILURE);
}
if (static_cast<uint64_t>(n) > 16_m - 1) {
std::cerr << "--max-frame-size: maximum 16777215" << std::endl;
exit(EXIT_FAILURE);
}
config.max_frame_size = n;
break;
}
case 'H': { case 'H': {
char *header = optarg; char *header = optarg;
// Skip first possible ':' in the header name // Skip first possible ':' in the header name
@@ -2380,6 +2604,38 @@ int main(int argc, char **argv) {
config.rps = v; config.rps = v;
break; break;
} }
case 13:
// --groups
config.groups = optarg;
break;
case 14:
// --tls13-ciphers
config.tls13_ciphers = optarg;
break;
case 15:
// --no-udp-gso
config.no_udp_gso = true;
break;
case 16:
// --qlog-file-base
qlog_base = optarg;
break;
case 17: {
// --max-udp-payload-size
auto n = util::parse_uint_with_unit(optarg);
if (n == -1) {
std::cerr << "--max-udp-payload-size: bad option value: " << optarg
<< std::endl;
exit(EXIT_FAILURE);
}
if (static_cast<uint64_t>(n) > 64_k) {
std::cerr << "--max-udp-payload-size: must not exceed 65536"
<< std::endl;
exit(EXIT_FAILURE);
}
config.max_udp_payload_size = n;
break;
}
} }
break; break;
default: default:
@@ -2546,6 +2802,13 @@ int main(int argc, char **argv) {
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
config.data_length = data_stat.st_size; config.data_length = data_stat.st_size;
auto addr = mmap(nullptr, config.data_length, PROT_READ, MAP_SHARED,
config.data_fd, 0);
if (addr == MAP_FAILED) {
std::cerr << "-d: Could not mmap file " << datafile << std::endl;
exit(EXIT_FAILURE);
}
config.data = static_cast<uint8_t *>(addr);
} }
if (!logfile.empty()) { if (!logfile.empty()) {
@@ -2557,11 +2820,23 @@ int main(int argc, char **argv) {
} }
} }
if (!qlog_base.empty()) {
if (!config.is_quic()) {
std::cerr
<< "Warning: --qlog-file-base: only effective in quic, ignoring."
<< std::endl;
} else {
#ifdef ENABLE_HTTP3
config.qlog_file_base = qlog_base;
#endif // ENABLE_HTTP3
}
}
struct sigaction act {}; struct sigaction act {};
act.sa_handler = SIG_IGN; act.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &act, nullptr); sigaction(SIGPIPE, &act, nullptr);
auto ssl_ctx = SSL_CTX_new(SSLv23_client_method()); auto ssl_ctx = SSL_CTX_new(TLS_client_method());
if (!ssl_ctx) { if (!ssl_ctx) {
std::cerr << "Failed to create SSL_CTX: " std::cerr << "Failed to create SSL_CTX: "
<< ERR_error_string(ERR_get_error(), nullptr) << std::endl; << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
@@ -2576,7 +2851,12 @@ int main(int argc, char **argv) {
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
if (nghttp2::tls::ssl_ctx_set_proto_versions( if (config.is_quic()) {
#ifdef ENABLE_HTTP3
SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION);
SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION);
#endif // ENABLE_HTTP3
} else if (nghttp2::tls::ssl_ctx_set_proto_versions(
ssl_ctx, nghttp2::tls::NGHTTP2_TLS_MIN_VERSION, ssl_ctx, nghttp2::tls::NGHTTP2_TLS_MIN_VERSION,
nghttp2::tls::NGHTTP2_TLS_MAX_VERSION) != 0) { nghttp2::tls::NGHTTP2_TLS_MAX_VERSION) != 0) {
std::cerr << "Could not set TLS versions" << std::endl; std::cerr << "Could not set TLS versions" << std::endl;
@@ -2590,6 +2870,27 @@ int main(int argc, char **argv) {
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
#if OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
if (SSL_CTX_set_ciphersuites(ssl_ctx, config.tls13_ciphers.c_str()) == 0) {
std::cerr << "SSL_CTX_set_ciphersuites with " << config.tls13_ciphers
<< " failed: " << ERR_error_string(ERR_get_error(), nullptr)
<< std::endl;
exit(EXIT_FAILURE);
}
#endif // OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
#if OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
if (SSL_CTX_set1_groups_list(ssl_ctx, config.groups.c_str()) != 1) {
std::cerr << "SSL_CTX_set1_groups_list failed" << std::endl;
exit(EXIT_FAILURE);
}
#else // !(OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL))
if (SSL_CTX_set1_curves_list(ssl_ctx, config.groups.c_str()) != 1) {
std::cerr << "SSL_CTX_set1_curves_list failed" << std::endl;
exit(EXIT_FAILURE);
}
#endif // !(OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL))
#ifndef OPENSSL_NO_NEXTPROTONEG #ifndef OPENSSL_NO_NEXTPROTONEG
SSL_CTX_set_next_proto_select_cb(ssl_ctx, client_select_next_proto_cb, SSL_CTX_set_next_proto_select_cb(ssl_ctx, client_select_next_proto_cb,
nullptr); nullptr);
@@ -2604,6 +2905,16 @@ int main(int argc, char **argv) {
SSL_CTX_set_alpn_protos(ssl_ctx, proto_list.data(), proto_list.size()); SSL_CTX_set_alpn_protos(ssl_ctx, proto_list.data(), proto_list.size());
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
#if OPENSSL_1_1_1_API
auto keylog_filename = getenv("SSLKEYLOGFILE");
if (keylog_filename) {
keylog_file.open(keylog_filename, std::ios_base::app);
if (keylog_file) {
SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
}
}
#endif // OPENSSL_1_1_1_API
std::string user_agent = "h2load nghttp2/" NGHTTP2_VERSION; std::string user_agent = "h2load nghttp2/" NGHTTP2_VERSION;
Headers shared_nva; Headers shared_nva;
shared_nva.emplace_back(":scheme", config.scheme); shared_nva.emplace_back(":scheme", config.scheme);
@@ -2822,6 +3133,8 @@ int main(int argc, char **argv) {
stats.bytes_head += s.bytes_head; stats.bytes_head += s.bytes_head;
stats.bytes_head_decomp += s.bytes_head_decomp; stats.bytes_head_decomp += s.bytes_head_decomp;
stats.bytes_body += s.bytes_body; stats.bytes_body += s.bytes_body;
stats.udp_dgram_recv += s.udp_dgram_recv;
stats.udp_dgram_sent += s.udp_dgram_sent;
for (size_t i = 0; i < stats.status.size(); ++i) { for (size_t i = 0; i < stats.status.size(); ++i) {
stats.status[i] += s.status[i]; stats.status[i] += s.status[i];
@@ -2880,14 +3193,21 @@ traffic: )" << util::utos_funit(stats.bytes_total)
<< util::utos_funit(stats.bytes_head) << "B (" << stats.bytes_head << util::utos_funit(stats.bytes_head) << "B (" << stats.bytes_head
<< ") headers (space savings " << header_space_savings * 100 << ") headers (space savings " << header_space_savings * 100
<< "%), " << util::utos_funit(stats.bytes_body) << "B (" << "%), " << util::utos_funit(stats.bytes_body) << "B ("
<< stats.bytes_body << R"() data << stats.bytes_body << R"() data)" << std::endl;
min max mean sd +/- sd #ifdef ENABLE_HTTP3
if (config.is_quic()) {
std::cout << "UDP datagram: " << stats.udp_dgram_sent << " sent, "
<< stats.udp_dgram_recv << " received" << std::endl;
}
#endif // ENABLE_HTTP3
std::cout
<< R"( min max mean sd +/- sd
time for request: )" time for request: )"
<< std::setw(10) << util::format_duration(ts.request.min) << " " << std::setw(10) << util::format_duration(ts.request.min) << " "
<< std::setw(10) << util::format_duration(ts.request.max) << " " << std::setw(10) << util::format_duration(ts.request.max) << " "
<< std::setw(10) << util::format_duration(ts.request.mean) << " " << std::setw(10) << util::format_duration(ts.request.mean) << " "
<< std::setw(10) << util::format_duration(ts.request.sd) << std::setw(10) << util::format_duration(ts.request.sd) << std::setw(9)
<< std::setw(9) << util::dtos(ts.request.within_sd) << "%" << util::dtos(ts.request.within_sd) << "%"
<< "\ntime for connect: " << std::setw(10) << "\ntime for connect: " << std::setw(10)
<< util::format_duration(ts.connect.min) << " " << std::setw(10) << util::format_duration(ts.connect.min) << " " << std::setw(10)
<< util::format_duration(ts.connect.max) << " " << std::setw(10) << util::format_duration(ts.connect.max) << " " << std::setw(10)
@@ -2901,8 +3221,8 @@ time for request: )"
<< util::format_duration(ts.ttfb.sd) << std::setw(9) << util::format_duration(ts.ttfb.sd) << std::setw(9)
<< util::dtos(ts.ttfb.within_sd) << "%" << util::dtos(ts.ttfb.within_sd) << "%"
<< "\nreq/s : " << std::setw(10) << ts.rps.min << " " << "\nreq/s : " << std::setw(10) << ts.rps.min << " "
<< std::setw(10) << ts.rps.max << " " << std::setw(10) << std::setw(10) << ts.rps.max << " " << std::setw(10) << ts.rps.mean
<< ts.rps.mean << " " << std::setw(10) << ts.rps.sd << std::setw(9) << " " << std::setw(10) << ts.rps.sd << std::setw(9)
<< util::dtos(ts.rps.within_sd) << "%" << std::endl; << util::dtos(ts.rps.within_sd) << "%" << std::endl;
SSL_CTX_free(ssl_ctx); SSL_CTX_free(ssl_ctx);

View File

@@ -45,11 +45,19 @@
#include <nghttp2/nghttp2.h> #include <nghttp2/nghttp2.h>
#ifdef ENABLE_HTTP3
# include <ngtcp2/ngtcp2.h>
# include <ngtcp2/ngtcp2_crypto.h>
#endif // ENABLE_HTTP3
#include <ev.h> #include <ev.h>
#include <openssl/ssl.h> #include <openssl/ssl.h>
#include "http2.h" #include "http2.h"
#ifdef ENABLE_HTTP3
# include "quic.h"
#endif // ENABLE_HTTP3
#include "memchunk.h" #include "memchunk.h"
#include "template.h" #include "template.h"
@@ -72,8 +80,13 @@ struct Config {
std::string connect_to_host; std::string connect_to_host;
std::string ifile; std::string ifile;
std::string ciphers; std::string ciphers;
std::string tls13_ciphers;
// supported groups (or curves).
std::string groups;
// length of upload data // length of upload data
int64_t data_length; int64_t data_length;
// memory mapped upload data
uint8_t *data;
addrinfo *addrs; addrinfo *addrs;
size_t nreqs; size_t nreqs;
size_t nclients; size_t nclients;
@@ -82,6 +95,7 @@ struct Config {
ssize_t max_concurrent_streams; ssize_t max_concurrent_streams;
size_t window_bits; size_t window_bits;
size_t connection_window_bits; size_t connection_window_bits;
size_t max_frame_size;
// rate at which connections should be made // rate at which connections should be made
size_t rate; size_t rate;
ev_tstamp rate_period; ev_tstamp rate_period;
@@ -100,6 +114,8 @@ struct Config {
int data_fd; int data_fd;
// file descriptor to write per-request stats to. // file descriptor to write per-request stats to.
int log_fd; int log_fd;
// base file name of qlog output files
std::string qlog_file_base;
uint16_t port; uint16_t port;
uint16_t default_port; uint16_t default_port;
uint16_t connect_to_port; uint16_t connect_to_port;
@@ -116,6 +132,10 @@ struct Config {
std::vector<std::string> npn_list; std::vector<std::string> npn_list;
// The number of request per second for each client. // The number of request per second for each client.
double rps; double rps;
// Disables GSO for UDP connections.
bool no_udp_gso;
// The maximum UDP datagram payload size to send.
size_t max_udp_payload_size;
Config(); Config();
~Config(); ~Config();
@@ -124,6 +144,7 @@ struct Config {
bool is_timing_based_mode() const; bool is_timing_based_mode() const;
bool has_base_uri() const; bool has_base_uri() const;
bool rps_enabled() const; bool rps_enabled() const;
bool is_quic() const;
}; };
struct RequestStat { struct RequestStat {
@@ -220,6 +241,10 @@ struct Stats {
std::vector<RequestStat> req_stats; std::vector<RequestStat> req_stats;
// The statistics per client // The statistics per client
std::vector<ClientStat> client_stats; std::vector<ClientStat> client_stats;
// The number of UDP datagrams received.
size_t udp_dgram_recv;
// The number of UDP datagrams sent.
size_t udp_dgram_sent;
}; };
enum ClientState { CLIENT_IDLE, CLIENT_CONNECTED }; enum ClientState { CLIENT_IDLE, CLIENT_CONNECTED };
@@ -245,6 +270,7 @@ struct Sampling {
struct Worker { struct Worker {
MemchunkPool mcpool; MemchunkPool mcpool;
std::mt19937 randgen;
Stats stats; Stats stats;
Sampling request_times_smp; Sampling request_times_smp;
Sampling client_smp; Sampling client_smp;
@@ -309,6 +335,15 @@ struct Client {
std::function<int(Client &)> readfn, writefn; std::function<int(Client &)> readfn, writefn;
Worker *worker; Worker *worker;
SSL *ssl; SSL *ssl;
#ifdef ENABLE_HTTP3
struct {
ev_timer pkt_timer;
ngtcp2_conn *conn;
quic::Error last_error;
bool close_requested;
FILE *qlog_file;
} quic;
#endif // ENABLE_HTTP3
ev_timer request_timeout_watcher; ev_timer request_timeout_watcher;
addrinfo *next_addr; addrinfo *next_addr;
// Address for the current address. When try_new_connection() is // Address for the current address. When try_new_connection() is
@@ -332,6 +367,7 @@ struct Client {
// The client id per worker // The client id per worker
uint32_t id; uint32_t id;
int fd; int fd;
Address local_addr;
ev_timer conn_active_watcher; ev_timer conn_active_watcher;
ev_timer conn_inactivity_watcher; ev_timer conn_inactivity_watcher;
std::string selected_proto; std::string selected_proto;
@@ -419,6 +455,39 @@ struct Client {
void record_client_end_time(); void record_client_end_time();
void signal_write(); void signal_write();
#ifdef ENABLE_HTTP3
// QUIC
int quic_init(const sockaddr *local_addr, socklen_t local_addrlen,
const sockaddr *remote_addr, socklen_t remote_addrlen);
void quic_free();
int read_quic();
int write_quic();
int write_udp(const sockaddr *addr, socklen_t addrlen, const uint8_t *data,
size_t datalen, size_t gso_size);
void quic_close_connection();
int quic_handshake_completed();
int quic_recv_stream_data(uint32_t flags, int64_t stream_id,
const uint8_t *data, size_t datalen);
int quic_acked_stream_data_offset(int64_t stream_id, size_t datalen);
int quic_stream_close(int64_t stream_id, uint64_t app_error_code);
int quic_stream_reset(int64_t stream_id, uint64_t app_error_code);
int quic_stream_stop_sending(int64_t stream_id, uint64_t app_error_code);
int quic_extend_max_local_streams();
int quic_on_rx_secret(ngtcp2_crypto_level level, const uint8_t *secret,
size_t secretlen);
int quic_on_tx_secret(ngtcp2_crypto_level level, const uint8_t *secret,
size_t secretlen);
void quic_set_tls_alert(uint8_t alert);
void quic_write_client_handshake(ngtcp2_crypto_level level,
const uint8_t *data, size_t datalen);
int quic_pkt_timeout();
void quic_restart_pkt_timer();
void quic_write_qlog(const void *data, size_t datalen);
#endif // ENABLE_HTTP3
}; };
} // namespace h2load } // namespace h2load

View File

@@ -215,7 +215,7 @@ void Http2Session::on_connect() {
nghttp2_option_del(opt); nghttp2_option_del(opt);
std::array<nghttp2_settings_entry, 3> iv; std::array<nghttp2_settings_entry, 4> iv;
size_t niv = 2; size_t niv = 2;
iv[0].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; iv[0].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
iv[0].value = 0; iv[0].value = 0;
@@ -227,6 +227,11 @@ void Http2Session::on_connect() {
iv[niv].value = config->header_table_size; iv[niv].value = config->header_table_size;
++niv; ++niv;
} }
if (config->max_frame_size != 16_k) {
iv[niv].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE;
iv[niv].value = config->max_frame_size;
++niv;
}
rv = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, iv.data(), niv); rv = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, iv.data(), niv);

425
src/h2load_http3_session.cc Normal file
View File

@@ -0,0 +1,425 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2019 nghttp2 contributors
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "h2load_http3_session.h"
#include <iostream>
#include <ngtcp2/ngtcp2.h>
#include "h2load.h"
namespace h2load {
Http3Session::Http3Session(Client *client)
: client_(client), conn_(nullptr), npending_request_(0), reqidx_(0) {}
Http3Session::~Http3Session() { nghttp3_conn_del(conn_); }
void Http3Session::on_connect() {}
int Http3Session::submit_request() {
if (npending_request_) {
++npending_request_;
return 0;
}
auto config = client_->worker->config;
reqidx_ = client_->reqidx;
if (++client_->reqidx == config->nva.size()) {
client_->reqidx = 0;
}
auto stream_id = submit_request_internal();
if (stream_id < 0) {
if (stream_id == NGTCP2_ERR_STREAM_ID_BLOCKED) {
++npending_request_;
return 0;
}
return -1;
}
return 0;
}
namespace {
nghttp3_ssize read_data(nghttp3_conn *conn, int64_t stream_id, nghttp3_vec *vec,
size_t veccnt, uint32_t *pflags, void *user_data,
void *stream_user_data) {
auto s = static_cast<Http3Session *>(user_data);
s->read_data(vec, veccnt, pflags);
return 1;
}
} // namespace
void Http3Session::read_data(nghttp3_vec *vec, size_t veccnt,
uint32_t *pflags) {
assert(veccnt > 0);
auto config = client_->worker->config;
vec[0].base = config->data;
vec[0].len = config->data_length;
*pflags |= NGHTTP3_DATA_FLAG_EOF;
}
int64_t Http3Session::submit_request_internal() {
int rv;
int64_t stream_id;
auto config = client_->worker->config;
auto &nva = config->nva[reqidx_];
rv = ngtcp2_conn_open_bidi_stream(client_->quic.conn, &stream_id, nullptr);
if (rv != 0) {
return rv;
}
nghttp3_data_reader dr{};
dr.read_data = h2load::read_data;
rv = nghttp3_conn_submit_request(
conn_, stream_id, reinterpret_cast<nghttp3_nv *>(nva.data()), nva.size(),
config->data_fd == -1 ? nullptr : &dr, nullptr);
if (rv != 0) {
return rv;
}
client_->on_request(stream_id);
auto req_stat = client_->get_req_stat(stream_id);
assert(req_stat);
client_->record_request_time(req_stat);
return stream_id;
}
int Http3Session::on_read(const uint8_t *data, size_t len) { return -1; }
int Http3Session::on_write() { return -1; }
void Http3Session::terminate() {}
size_t Http3Session::max_concurrent_streams() {
return (size_t)client_->worker->config->max_concurrent_streams;
}
namespace {
int stream_close(nghttp3_conn *conn, int64_t stream_id, uint64_t app_error_code,
void *user_data, void *stream_user_data) {
auto s = static_cast<Http3Session *>(user_data);
if (s->stream_close(stream_id, app_error_code) != 0) {
return NGHTTP3_ERR_CALLBACK_FAILURE;
}
return 0;
}
} // namespace
int Http3Session::stream_close(int64_t stream_id, uint64_t app_error_code) {
if (!ngtcp2_is_bidi_stream(stream_id)) {
assert(!ngtcp2_conn_is_local_stream(client_->quic.conn, stream_id));
ngtcp2_conn_extend_max_streams_uni(client_->quic.conn, 1);
}
client_->on_stream_close(stream_id, app_error_code == NGHTTP3_H3_NO_ERROR);
return 0;
}
namespace {
int recv_data(nghttp3_conn *conn, int64_t stream_id, const uint8_t *data,
size_t datalen, void *user_data, void *stream_user_data) {
auto s = static_cast<Http3Session *>(user_data);
s->recv_data(stream_id, data, datalen);
return 0;
}
} // namespace
void Http3Session::recv_data(int64_t stream_id, const uint8_t *data,
size_t datalen) {
client_->record_ttfb();
client_->worker->stats.bytes_body += datalen;
consume(stream_id, datalen);
}
namespace {
int deferred_consume(nghttp3_conn *conn, int64_t stream_id, size_t nconsumed,
void *user_data, void *stream_user_data) {
auto s = static_cast<Http3Session *>(user_data);
s->consume(stream_id, nconsumed);
return 0;
}
} // namespace
void Http3Session::consume(int64_t stream_id, size_t nconsumed) {
ngtcp2_conn_extend_max_stream_offset(client_->quic.conn, stream_id,
nconsumed);
ngtcp2_conn_extend_max_offset(client_->quic.conn, nconsumed);
}
namespace {
int begin_headers(nghttp3_conn *conn, int64_t stream_id, void *user_data,
void *stream_user_data) {
auto s = static_cast<Http3Session *>(user_data);
s->begin_headers(stream_id);
return 0;
}
} // namespace
void Http3Session::begin_headers(int64_t stream_id) {
auto payloadlen = nghttp3_conn_get_frame_payload_left(conn_, stream_id);
assert(payloadlen > 0);
client_->worker->stats.bytes_head += payloadlen;
}
namespace {
int recv_header(nghttp3_conn *conn, int64_t stream_id, int32_t token,
nghttp3_rcbuf *name, nghttp3_rcbuf *value, uint8_t flags,
void *user_data, void *stream_user_data) {
auto s = static_cast<Http3Session *>(user_data);
auto k = nghttp3_rcbuf_get_buf(name);
auto v = nghttp3_rcbuf_get_buf(value);
s->recv_header(stream_id, &k, &v);
return 0;
}
} // namespace
void Http3Session::recv_header(int64_t stream_id, const nghttp3_vec *name,
const nghttp3_vec *value) {
client_->on_header(stream_id, name->base, name->len, value->base, value->len);
client_->worker->stats.bytes_head_decomp += name->len + value->len;
}
namespace {
int stop_sending(nghttp3_conn *conn, int64_t stream_id, uint64_t app_error_code,
void *user_data, void *stream_user_data) {
auto s = static_cast<Http3Session *>(user_data);
if (s->stop_sending(stream_id, app_error_code) != 0) {
return NGHTTP3_ERR_CALLBACK_FAILURE;
}
return 0;
}
} // namespace
int Http3Session::stop_sending(int64_t stream_id, uint64_t app_error_code) {
auto rv = ngtcp2_conn_shutdown_stream_read(client_->quic.conn, stream_id,
app_error_code);
if (rv != 0) {
std::cerr << "ngtcp2_conn_shutdown_stream_read: " << ngtcp2_strerror(rv)
<< std::endl;
return -1;
}
return 0;
}
int Http3Session::close_stream(int64_t stream_id, uint64_t app_error_code) {
auto rv = nghttp3_conn_close_stream(conn_, stream_id, app_error_code);
switch (rv) {
case 0:
return 0;
case NGHTTP3_ERR_STREAM_NOT_FOUND:
if (!ngtcp2_is_bidi_stream(stream_id)) {
assert(!ngtcp2_conn_is_local_stream(client_->quic.conn, stream_id));
ngtcp2_conn_extend_max_streams_uni(client_->quic.conn, 1);
}
return 0;
default:
return -1;
}
}
int Http3Session::shutdown_stream_read(int64_t stream_id) {
auto rv = nghttp3_conn_shutdown_stream_read(conn_, stream_id);
if (rv != 0) {
return -1;
}
return 0;
}
int Http3Session::extend_max_local_streams() {
auto config = client_->worker->config;
for (; npending_request_; --npending_request_) {
auto stream_id = submit_request_internal();
if (stream_id < 0) {
if (stream_id == NGTCP2_ERR_STREAM_ID_BLOCKED) {
return 0;
}
return -1;
}
if (++reqidx_ == config->nva.size()) {
reqidx_ = 0;
}
}
return 0;
}
int Http3Session::init_conn() {
int rv;
assert(conn_ == nullptr);
if (ngtcp2_conn_get_max_local_streams_uni(client_->quic.conn) < 3) {
return -1;
}
nghttp3_callbacks callbacks{
nullptr, // acked_stream_data
h2load::stream_close,
h2load::recv_data,
h2load::deferred_consume,
h2load::begin_headers,
h2load::recv_header,
nullptr, // end_headers
nullptr, // begin_trailers
h2load::recv_header,
nullptr, // end_trailers
h2load::stop_sending,
};
auto config = client_->worker->config;
nghttp3_settings settings;
nghttp3_settings_default(&settings);
settings.qpack_max_dtable_capacity = config->header_table_size;
settings.qpack_blocked_streams = 100;
auto mem = nghttp3_mem_default();
rv = nghttp3_conn_client_new(&conn_, &callbacks, &settings, mem, this);
if (rv != 0) {
std::cerr << "nghttp3_conn_client_new: " << nghttp3_strerror(rv)
<< std::endl;
return -1;
}
int64_t ctrl_stream_id;
rv = ngtcp2_conn_open_uni_stream(client_->quic.conn, &ctrl_stream_id, NULL);
if (rv != 0) {
std::cerr << "ngtcp2_conn_open_uni_stream: " << ngtcp2_strerror(rv)
<< std::endl;
return -1;
}
rv = nghttp3_conn_bind_control_stream(conn_, ctrl_stream_id);
if (rv != 0) {
std::cerr << "nghttp3_conn_bind_control_stream: " << nghttp3_strerror(rv)
<< std::endl;
return -1;
}
int64_t qpack_enc_stream_id, qpack_dec_stream_id;
rv = ngtcp2_conn_open_uni_stream(client_->quic.conn, &qpack_enc_stream_id,
NULL);
if (rv != 0) {
std::cerr << "ngtcp2_conn_open_uni_stream: " << ngtcp2_strerror(rv)
<< std::endl;
return -1;
}
rv = ngtcp2_conn_open_uni_stream(client_->quic.conn, &qpack_dec_stream_id,
NULL);
if (rv != 0) {
std::cerr << "ngtcp2_conn_open_uni_stream: " << ngtcp2_strerror(rv)
<< std::endl;
return -1;
}
rv = nghttp3_conn_bind_qpack_streams(conn_, qpack_enc_stream_id,
qpack_dec_stream_id);
if (rv != 0) {
std::cerr << "nghttp3_conn_bind_qpack_streams: " << nghttp3_strerror(rv)
<< std::endl;
return -1;
}
return 0;
}
ssize_t Http3Session::read_stream(uint32_t flags, int64_t stream_id,
const uint8_t *data, size_t datalen) {
auto nconsumed = nghttp3_conn_read_stream(
conn_, stream_id, data, datalen, flags & NGTCP2_STREAM_DATA_FLAG_FIN);
if (nconsumed < 0) {
std::cerr << "nghttp3_conn_read_stream: " << nghttp3_strerror(nconsumed)
<< std::endl;
client_->quic.last_error = quic::err_application(nconsumed);
return -1;
}
return nconsumed;
}
ssize_t Http3Session::write_stream(int64_t &stream_id, int &fin,
nghttp3_vec *vec, size_t veccnt) {
auto sveccnt =
nghttp3_conn_writev_stream(conn_, &stream_id, &fin, vec, veccnt);
if (sveccnt < 0) {
client_->quic.last_error = quic::err_application(sveccnt);
return -1;
}
return sveccnt;
}
int Http3Session::block_stream(int64_t stream_id) {
auto rv = nghttp3_conn_block_stream(conn_, stream_id);
if (rv != 0) {
client_->quic.last_error = quic::err_application(rv);
return -1;
}
return 0;
}
int Http3Session::shutdown_stream_write(int64_t stream_id) {
auto rv = nghttp3_conn_shutdown_stream_write(conn_, stream_id);
if (rv != 0) {
client_->quic.last_error = quic::err_application(rv);
return -1;
}
return 0;
}
int Http3Session::add_write_offset(int64_t stream_id, size_t ndatalen) {
auto rv = nghttp3_conn_add_write_offset(conn_, stream_id, ndatalen);
if (rv != 0) {
client_->quic.last_error = quic::err_application(rv);
return -1;
}
return 0;
}
int Http3Session::add_ack_offset(int64_t stream_id, size_t datalen) {
auto rv = nghttp3_conn_add_ack_offset(conn_, stream_id, datalen);
if (rv != 0) {
client_->quic.last_error = quic::err_application(rv);
return -1;
}
return 0;
}
} // namespace h2load

View File

@@ -0,0 +1,81 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2019 nghttp2 contributors
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef H2LOAD_HTTP3_SESSION_H
#define H2LOAD_HTTP3_SESSION_H
#include "h2load_session.h"
#include <nghttp3/nghttp3.h>
namespace h2load {
struct Client;
class Http3Session : public Session {
public:
Http3Session(Client *client);
virtual ~Http3Session();
virtual void on_connect();
virtual int submit_request();
virtual int on_read(const uint8_t *data, size_t len);
virtual int on_write();
virtual void terminate();
virtual size_t max_concurrent_streams();
int init_conn();
int stream_close(int64_t stream_id, uint64_t app_error_code);
void recv_data(int64_t stream_id, const uint8_t *data, size_t datalen);
void consume(int64_t stream_id, size_t nconsumed);
void begin_headers(int64_t stream_id);
void recv_header(int64_t stream_id, const nghttp3_vec *name,
const nghttp3_vec *value);
int stop_sending(int64_t stream_id, uint64_t app_error_code);
int close_stream(int64_t stream_id, uint64_t app_error_code);
int shutdown_stream_read(int64_t stream_id);
int extend_max_local_streams();
int64_t submit_request_internal();
ssize_t read_stream(uint32_t flags, int64_t stream_id, const uint8_t *data,
size_t datalen);
ssize_t write_stream(int64_t &stream_id, int &fin, nghttp3_vec *vec,
size_t veccnt);
int block_stream(int64_t stream_id);
int shutdown_stream_write(int64_t stream_id);
int add_write_offset(int64_t stream_id, size_t ndatalen);
int add_ack_offset(int64_t stream_id, size_t datalen);
void read_data(nghttp3_vec *vec, size_t veccnt, uint32_t *pflags);
private:
Client *client_;
nghttp3_conn *conn_;
size_t npending_request_;
size_t reqidx_;
};
} // namespace h2load
#endif // H2LOAD_HTTP3_SESSION_H

790
src/h2load_quic.cc Normal file
View File

@@ -0,0 +1,790 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2019 nghttp2 contributors
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "h2load_quic.h"
#include <netinet/udp.h>
#include <iostream>
#ifdef HAVE_LIBNGTCP2_CRYPTO_OPENSSL
# include <ngtcp2/ngtcp2_crypto_openssl.h>
#endif // HAVE_LIBNGTCP2_CRYPTO_OPENSSL
#ifdef HAVE_LIBNGTCP2_CRYPTO_BORINGSSL
# include <ngtcp2/ngtcp2_crypto_boringssl.h>
#endif // HAVE_LIBNGTCP2_CRYPTO_BORINGSSL
#include <openssl/err.h>
#include <openssl/rand.h>
#include "h2load_http3_session.h"
namespace h2load {
namespace {
int handshake_completed(ngtcp2_conn *conn, void *user_data) {
auto c = static_cast<Client *>(user_data);
if (c->quic_handshake_completed() != 0) {
return NGTCP2_ERR_CALLBACK_FAILURE;
}
return 0;
}
} // namespace
int Client::quic_handshake_completed() { return connection_made(); }
namespace {
int recv_stream_data(ngtcp2_conn *conn, uint32_t flags, int64_t stream_id,
uint64_t offset, const uint8_t *data, size_t datalen,
void *user_data, void *stream_user_data) {
auto c = static_cast<Client *>(user_data);
if (c->quic_recv_stream_data(flags, stream_id, data, datalen) != 0) {
// TODO Better to do this gracefully rather than
// NGTCP2_ERR_CALLBACK_FAILURE. Perhaps, call
// ngtcp2_conn_write_application_close() ?
return NGTCP2_ERR_CALLBACK_FAILURE;
}
return 0;
}
} // namespace
int Client::quic_recv_stream_data(uint32_t flags, int64_t stream_id,
const uint8_t *data, size_t datalen) {
if (worker->current_phase == Phase::MAIN_DURATION) {
worker->stats.bytes_total += datalen;
}
auto s = static_cast<Http3Session *>(session.get());
auto nconsumed = s->read_stream(flags, stream_id, data, datalen);
if (nconsumed == -1) {
return -1;
}
ngtcp2_conn_extend_max_stream_offset(quic.conn, stream_id, nconsumed);
ngtcp2_conn_extend_max_offset(quic.conn, nconsumed);
return 0;
}
namespace {
int acked_stream_data_offset(ngtcp2_conn *conn, int64_t stream_id,
uint64_t offset, uint64_t datalen, void *user_data,
void *stream_user_data) {
auto c = static_cast<Client *>(user_data);
if (c->quic_acked_stream_data_offset(stream_id, datalen) != 0) {
return NGTCP2_ERR_CALLBACK_FAILURE;
}
return 0;
}
} // namespace
int Client::quic_acked_stream_data_offset(int64_t stream_id, size_t datalen) {
auto s = static_cast<Http3Session *>(session.get());
if (s->add_ack_offset(stream_id, datalen) != 0) {
return -1;
}
return 0;
}
namespace {
int stream_close(ngtcp2_conn *conn, uint32_t flags, int64_t stream_id,
uint64_t app_error_code, void *user_data,
void *stream_user_data) {
auto c = static_cast<Client *>(user_data);
if (!(flags & NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET)) {
app_error_code = NGHTTP3_H3_NO_ERROR;
}
if (c->quic_stream_close(stream_id, app_error_code) != 0) {
return NGTCP2_ERR_CALLBACK_FAILURE;
}
return 0;
}
} // namespace
int Client::quic_stream_close(int64_t stream_id, uint64_t app_error_code) {
auto s = static_cast<Http3Session *>(session.get());
if (s->close_stream(stream_id, app_error_code) != 0) {
return -1;
}
return 0;
}
namespace {
int stream_reset(ngtcp2_conn *conn, int64_t stream_id, uint64_t final_size,
uint64_t app_error_code, void *user_data,
void *stream_user_data) {
auto c = static_cast<Client *>(user_data);
if (c->quic_stream_reset(stream_id, app_error_code) != 0) {
return NGTCP2_ERR_CALLBACK_FAILURE;
}
return 0;
}
} // namespace
int Client::quic_stream_reset(int64_t stream_id, uint64_t app_error_code) {
auto s = static_cast<Http3Session *>(session.get());
if (s->shutdown_stream_read(stream_id) != 0) {
return -1;
}
return 0;
}
namespace {
int stream_stop_sending(ngtcp2_conn *conn, int64_t stream_id,
uint64_t app_error_code, void *user_data,
void *stream_user_data) {
auto c = static_cast<Client *>(user_data);
if (c->quic_stream_stop_sending(stream_id, app_error_code) != 0) {
return NGTCP2_ERR_CALLBACK_FAILURE;
}
return 0;
}
} // namespace
int Client::quic_stream_stop_sending(int64_t stream_id,
uint64_t app_error_code) {
auto s = static_cast<Http3Session *>(session.get());
if (s->shutdown_stream_read(stream_id) != 0) {
return -1;
}
return 0;
}
namespace {
int extend_max_local_streams_bidi(ngtcp2_conn *conn, uint64_t max_streams,
void *user_data) {
auto c = static_cast<Client *>(user_data);
if (c->quic_extend_max_local_streams() != 0) {
return NGTCP2_ERR_CALLBACK_FAILURE;
}
return 0;
}
} // namespace
int Client::quic_extend_max_local_streams() {
auto s = static_cast<Http3Session *>(session.get());
if (s->extend_max_local_streams() != 0) {
return NGTCP2_ERR_CALLBACK_FAILURE;
}
return 0;
}
namespace {
int get_new_connection_id(ngtcp2_conn *conn, ngtcp2_cid *cid, uint8_t *token,
size_t cidlen, void *user_data) {
if (RAND_bytes(cid->data, cidlen) != 1) {
return NGTCP2_ERR_CALLBACK_FAILURE;
}
cid->datalen = cidlen;
if (RAND_bytes(token, NGTCP2_STATELESS_RESET_TOKENLEN) != 1) {
return NGTCP2_ERR_CALLBACK_FAILURE;
}
return 0;
}
} // namespace
namespace {
void debug_log_printf(void *user_data, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
fprintf(stderr, "\n");
}
} // namespace
namespace {
int generate_cid(ngtcp2_cid &dest) {
dest.datalen = 8;
if (RAND_bytes(dest.data, dest.datalen) != 1) {
return -1;
}
return 0;
}
} // namespace
namespace {
ngtcp2_tstamp timestamp(struct ev_loop *loop) {
return ev_now(loop) * NGTCP2_SECONDS;
}
} // namespace
#ifdef HAVE_LIBNGTCP2_CRYPTO_OPENSSL
namespace {
int set_encryption_secrets(SSL *ssl, OSSL_ENCRYPTION_LEVEL ossl_level,
const uint8_t *rx_secret, const uint8_t *tx_secret,
size_t secret_len) {
auto c = static_cast<Client *>(SSL_get_app_data(ssl));
auto level = ngtcp2_crypto_openssl_from_ossl_encryption_level(ossl_level);
if (c->quic_on_rx_secret(level, rx_secret, secret_len) != 0) {
return 0;
}
if (c->quic_on_tx_secret(level, tx_secret, secret_len) != 0) {
return 0;
}
return 1;
}
} // namespace
namespace {
int add_handshake_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL ossl_level,
const uint8_t *data, size_t len) {
auto c = static_cast<Client *>(SSL_get_app_data(ssl));
c->quic_write_client_handshake(
ngtcp2_crypto_openssl_from_ossl_encryption_level(ossl_level), data, len);
return 1;
}
} // namespace
namespace {
int flush_flight(SSL *ssl) { return 1; }
} // namespace
namespace {
int send_alert(SSL *ssl, enum ssl_encryption_level_t level, uint8_t alert) {
auto c = static_cast<Client *>(SSL_get_app_data(ssl));
c->quic_set_tls_alert(alert);
return 1;
}
} // namespace
namespace {
auto quic_method = SSL_QUIC_METHOD{
set_encryption_secrets,
add_handshake_data,
flush_flight,
send_alert,
};
} // namespace
#endif // HAVE_LIBNGTCP2_CRYPTO_OPENSSL
#ifdef HAVE_LIBNGTCP2_CRYPTO_BORINGSSL
namespace {
int set_read_secret(SSL *ssl, ssl_encryption_level_t ssl_level,
const SSL_CIPHER *cipher, const uint8_t *secret,
size_t secretlen) {
auto c = static_cast<Client *>(SSL_get_app_data(ssl));
if (c->quic_on_rx_secret(
ngtcp2_crypto_boringssl_from_ssl_encryption_level(ssl_level), secret,
secretlen) != 0) {
return 0;
}
return 1;
}
} // namespace
namespace {
int set_write_secret(SSL *ssl, ssl_encryption_level_t ssl_level,
const SSL_CIPHER *cipher, const uint8_t *secret,
size_t secretlen) {
auto c = static_cast<Client *>(SSL_get_app_data(ssl));
if (c->quic_on_tx_secret(
ngtcp2_crypto_boringssl_from_ssl_encryption_level(ssl_level), secret,
secretlen) != 0) {
return 0;
}
return 1;
}
} // namespace
namespace {
int add_handshake_data(SSL *ssl, ssl_encryption_level_t ssl_level,
const uint8_t *data, size_t len) {
auto c = static_cast<Client *>(SSL_get_app_data(ssl));
c->quic_write_client_handshake(
ngtcp2_crypto_boringssl_from_ssl_encryption_level(ssl_level), data, len);
return 1;
}
} // namespace
namespace {
int flush_flight(SSL *ssl) { return 1; }
} // namespace
namespace {
int send_alert(SSL *ssl, ssl_encryption_level_t level, uint8_t alert) {
auto c = static_cast<Client *>(SSL_get_app_data(ssl));
c->quic_set_tls_alert(alert);
return 1;
}
} // namespace
namespace {
auto quic_method = SSL_QUIC_METHOD{
set_read_secret, set_write_secret, add_handshake_data,
flush_flight, send_alert,
};
} // namespace
#endif // HAVE_LIBNGTCP2_CRYPTO_BORINGSSL
// qlog write callback -- excerpted from ngtcp2/examples/client_base.cc
namespace {
void qlog_write_cb(void *user_data, uint32_t flags, const void *data,
size_t datalen) {
auto c = static_cast<Client *>(user_data);
c->quic_write_qlog(data, datalen);
}
} // namespace
void Client::quic_write_qlog(const void *data, size_t datalen) {
assert(quic.qlog_file != nullptr);
fwrite(data, 1, datalen, quic.qlog_file);
}
namespace {
void rand(uint8_t *dest, size_t destlen, const ngtcp2_rand_ctx *rand_ctx) {
util::random_bytes(dest, dest + destlen,
*static_cast<std::mt19937 *>(rand_ctx->native_handle));
}
} // namespace
int Client::quic_init(const sockaddr *local_addr, socklen_t local_addrlen,
const sockaddr *remote_addr, socklen_t remote_addrlen) {
int rv;
if (!ssl) {
ssl = SSL_new(worker->ssl_ctx);
SSL_set_app_data(ssl, this);
SSL_set_connect_state(ssl);
SSL_set_quic_method(ssl, &quic_method);
SSL_set_quic_use_legacy_codepoint(ssl, 0);
}
auto callbacks = ngtcp2_callbacks{
ngtcp2_crypto_client_initial_cb,
nullptr, // recv_client_initial
ngtcp2_crypto_recv_crypto_data_cb,
h2load::handshake_completed,
nullptr, // recv_version_negotiation
ngtcp2_crypto_encrypt_cb,
ngtcp2_crypto_decrypt_cb,
ngtcp2_crypto_hp_mask_cb,
h2load::recv_stream_data,
h2load::acked_stream_data_offset,
nullptr, // stream_open
h2load::stream_close,
nullptr, // recv_stateless_reset
ngtcp2_crypto_recv_retry_cb,
h2load::extend_max_local_streams_bidi,
nullptr, // extend_max_local_streams_uni
h2load::rand,
get_new_connection_id,
nullptr, // remove_connection_id
ngtcp2_crypto_update_key_cb,
nullptr, // path_validation
nullptr, // select_preferred_addr
h2load::stream_reset,
nullptr, // extend_max_remote_streams_bidi
nullptr, // extend_max_remote_streams_uni
nullptr, // extend_max_stream_data
nullptr, // dcid_status
nullptr, // handshake_confirmed
nullptr, // recv_new_token
ngtcp2_crypto_delete_crypto_aead_ctx_cb,
ngtcp2_crypto_delete_crypto_cipher_ctx_cb,
nullptr, // recv_datagram
nullptr, // ack_datagram
nullptr, // lost_datagram
ngtcp2_crypto_get_path_challenge_data_cb,
h2load::stream_stop_sending,
};
ngtcp2_cid scid, dcid;
if (generate_cid(scid) != 0) {
return -1;
}
if (generate_cid(dcid) != 0) {
return -1;
}
auto config = worker->config;
ngtcp2_settings settings;
ngtcp2_settings_default(&settings);
if (config->verbose) {
settings.log_printf = debug_log_printf;
}
settings.initial_ts = timestamp(worker->loop);
settings.rand_ctx.native_handle = &worker->randgen;
if (!config->qlog_file_base.empty()) {
assert(quic.qlog_file == nullptr);
auto path = config->qlog_file_base;
path += '.';
path += util::utos(worker->id);
path += '.';
path += util::utos(id);
path += ".sqlog";
quic.qlog_file = fopen(path.c_str(), "w");
if (quic.qlog_file == nullptr) {
std::cerr << "Failed to open a qlog file: " << path << std::endl;
return -1;
}
settings.qlog.write = qlog_write_cb;
}
if (config->max_udp_payload_size) {
settings.max_udp_payload_size = config->max_udp_payload_size;
settings.no_udp_payload_size_shaping = 1;
}
ngtcp2_transport_params params;
ngtcp2_transport_params_default(&params);
auto max_stream_data =
std::min((1 << 26) - 1, (1 << config->window_bits) - 1);
params.initial_max_stream_data_bidi_local = max_stream_data;
params.initial_max_stream_data_uni = max_stream_data;
params.initial_max_data = (1 << config->connection_window_bits) - 1;
params.initial_max_streams_bidi = 0;
params.initial_max_streams_uni = 100;
params.max_idle_timeout = 30 * NGTCP2_SECONDS;
auto path = ngtcp2_path{
{
const_cast<sockaddr *>(local_addr),
local_addrlen,
},
{
const_cast<sockaddr *>(remote_addr),
remote_addrlen,
},
};
assert(config->npn_list.size());
uint32_t quic_version;
if (config->npn_list[0] == NGHTTP3_ALPN_H3) {
quic_version = NGTCP2_PROTO_VER_V1;
} else {
quic_version = NGTCP2_PROTO_VER_MIN;
}
rv = ngtcp2_conn_client_new(&quic.conn, &dcid, &scid, &path, quic_version,
&callbacks, &settings, &params, nullptr, this);
if (rv != 0) {
return -1;
}
ngtcp2_conn_set_tls_native_handle(quic.conn, ssl);
return 0;
}
void Client::quic_free() {
ngtcp2_conn_del(quic.conn);
if (quic.qlog_file != nullptr) {
fclose(quic.qlog_file);
quic.qlog_file = nullptr;
}
}
void Client::quic_close_connection() {
if (!quic.conn) {
return;
}
std::array<uint8_t, NGTCP2_MAX_UDP_PAYLOAD_SIZE> buf;
ngtcp2_ssize nwrite;
ngtcp2_path_storage ps;
ngtcp2_path_storage_zero(&ps);
switch (quic.last_error.type) {
case quic::ErrorType::TransportVersionNegotiation:
return;
case quic::ErrorType::Transport:
nwrite = ngtcp2_conn_write_connection_close(
quic.conn, &ps.path, nullptr, buf.data(), buf.size(),
quic.last_error.code, nullptr, 0, timestamp(worker->loop));
break;
case quic::ErrorType::Application:
nwrite = ngtcp2_conn_write_application_close(
quic.conn, &ps.path, nullptr, buf.data(), buf.size(),
quic.last_error.code, nullptr, 0, timestamp(worker->loop));
break;
default:
assert(0);
abort();
}
if (nwrite < 0) {
return;
}
write_udp(reinterpret_cast<sockaddr *>(ps.path.remote.addr),
ps.path.remote.addrlen, buf.data(), nwrite, 0);
}
int Client::quic_on_rx_secret(ngtcp2_crypto_level level, const uint8_t *secret,
size_t secretlen) {
if (ngtcp2_crypto_derive_and_install_rx_key(quic.conn, nullptr, nullptr,
nullptr, level, secret,
secretlen) != 0) {
std::cerr << "ngtcp2_crypto_derive_and_install_rx_key() failed"
<< std::endl;
return -1;
}
if (level == NGTCP2_CRYPTO_LEVEL_APPLICATION) {
auto s = std::make_unique<Http3Session>(this);
if (s->init_conn() == -1) {
return -1;
}
session = std::move(s);
}
return 0;
}
int Client::quic_on_tx_secret(ngtcp2_crypto_level level, const uint8_t *secret,
size_t secretlen) {
if (ngtcp2_crypto_derive_and_install_tx_key(quic.conn, nullptr, nullptr,
nullptr, level, secret,
secretlen) != 0) {
std::cerr << "ngtcp2_crypto_derive_and_install_tx_key() failed"
<< std::endl;
return -1;
}
return 0;
}
void Client::quic_set_tls_alert(uint8_t alert) {
quic.last_error = quic::err_transport_tls(alert);
}
void Client::quic_write_client_handshake(ngtcp2_crypto_level level,
const uint8_t *data, size_t datalen) {
assert(level < 2);
ngtcp2_conn_submit_crypto_data(quic.conn, level, data, datalen);
}
void quic_pkt_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
auto c = static_cast<Client *>(w->data);
if (c->quic_pkt_timeout() != 0) {
c->fail();
c->worker->free_client(c);
delete c;
return;
}
}
int Client::quic_pkt_timeout() {
int rv;
auto now = timestamp(worker->loop);
rv = ngtcp2_conn_handle_expiry(quic.conn, now);
if (rv != 0) {
quic.last_error = quic::err_transport(NGTCP2_ERR_INTERNAL);
return -1;
}
return write_quic();
}
void Client::quic_restart_pkt_timer() {
auto expiry = ngtcp2_conn_get_expiry(quic.conn);
auto now = timestamp(worker->loop);
auto t = expiry > now ? static_cast<ev_tstamp>(expiry - now) / NGTCP2_SECONDS
: 1e-9;
quic.pkt_timer.repeat = t;
ev_timer_again(worker->loop, &quic.pkt_timer);
}
int Client::read_quic() {
std::array<uint8_t, 65536> buf;
sockaddr_union su;
socklen_t addrlen = sizeof(su);
int rv;
size_t pktcnt = 0;
ngtcp2_pkt_info pi{};
for (;;) {
auto nread =
recvfrom(fd, buf.data(), buf.size(), MSG_DONTWAIT, &su.sa, &addrlen);
if (nread == -1) {
return 0;
}
assert(quic.conn);
++worker->stats.udp_dgram_recv;
auto path = ngtcp2_path{
{
&local_addr.su.sa,
static_cast<socklen_t>(local_addr.len),
},
{
&su.sa,
addrlen,
},
};
rv = ngtcp2_conn_read_pkt(quic.conn, &path, &pi, buf.data(), nread,
timestamp(worker->loop));
if (rv != 0) {
std::cerr << "ngtcp2_conn_read_pkt: " << ngtcp2_strerror(rv) << std::endl;
if (!quic.last_error.code) {
quic.last_error = quic::err_transport(rv);
}
return -1;
}
if (++pktcnt == 100) {
break;
}
}
return 0;
}
int Client::write_quic() {
ev_io_stop(worker->loop, &wev);
if (quic.close_requested) {
return -1;
}
std::array<nghttp3_vec, 16> vec;
size_t pktcnt = 0;
auto max_udp_payload_size =
ngtcp2_conn_get_path_max_udp_payload_size(quic.conn);
size_t max_pktcnt =
#ifdef UDP_SEGMENT
worker->config->no_udp_gso
? 1
: std::min(static_cast<size_t>(10),
static_cast<size_t>(64_k / max_udp_payload_size));
#else // !UDP_SEGMENT
1;
#endif // !UDP_SEGMENT
std::array<uint8_t, 64_k> buf;
uint8_t *bufpos = buf.data();
ngtcp2_path_storage ps;
ngtcp2_path_storage_zero(&ps);
auto s = static_cast<Http3Session *>(session.get());
for (;;) {
int64_t stream_id = -1;
int fin = 0;
ssize_t sveccnt = 0;
if (session && ngtcp2_conn_get_max_data_left(quic.conn)) {
sveccnt = s->write_stream(stream_id, fin, vec.data(), vec.size());
if (sveccnt == -1) {
return -1;
}
}
ngtcp2_ssize ndatalen;
auto v = vec.data();
auto vcnt = static_cast<size_t>(sveccnt);
uint32_t flags = NGTCP2_WRITE_STREAM_FLAG_MORE;
if (fin) {
flags |= NGTCP2_WRITE_STREAM_FLAG_FIN;
}
auto nwrite = ngtcp2_conn_writev_stream(
quic.conn, &ps.path, nullptr, bufpos, max_udp_payload_size, &ndatalen,
flags, stream_id, reinterpret_cast<const ngtcp2_vec *>(v), vcnt,
timestamp(worker->loop));
if (nwrite < 0) {
switch (nwrite) {
case NGTCP2_ERR_STREAM_DATA_BLOCKED:
assert(ndatalen == -1);
if (s->block_stream(stream_id) != 0) {
return -1;
}
continue;
case NGTCP2_ERR_STREAM_SHUT_WR:
assert(ndatalen == -1);
if (s->shutdown_stream_write(stream_id) != 0) {
return -1;
}
continue;
case NGTCP2_ERR_WRITE_MORE:
assert(ndatalen >= 0);
if (s->add_write_offset(stream_id, ndatalen) != 0) {
return -1;
}
continue;
}
quic.last_error = quic::err_transport(nwrite);
return -1;
} else if (ndatalen >= 0 && s->add_write_offset(stream_id, ndatalen) != 0) {
return -1;
}
quic_restart_pkt_timer();
if (nwrite == 0) {
if (bufpos - buf.data()) {
write_udp(ps.path.remote.addr, ps.path.remote.addrlen, buf.data(),
bufpos - buf.data(), max_udp_payload_size);
}
return 0;
}
bufpos += nwrite;
// Assume that the path does not change.
if (++pktcnt == max_pktcnt ||
static_cast<size_t>(nwrite) < max_udp_payload_size) {
write_udp(ps.path.remote.addr, ps.path.remote.addrlen, buf.data(),
bufpos - buf.data(), max_udp_payload_size);
signal_write();
return 0;
}
}
}
} // namespace h2load

38
src/h2load_quic.h Normal file
View File

@@ -0,0 +1,38 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2019 nghttp2 contributors
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef H2LOAD_QUIC_H
#define H2LOAD_QUIC_H
#include "nghttp2_config.h"
#include <ev.h>
#include "h2load.h"
namespace h2load {
void quic_pkt_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents);
} // namespace h2load
#endif // H2LOAD_QUIC_H

View File

@@ -916,7 +916,7 @@ void test_http2_rewrite_clean_path(void) {
CU_ASSERT("/delta%3A" == http2::rewrite_clean_path( CU_ASSERT("/delta%3A" == http2::rewrite_clean_path(
balloc, StringRef::from_lit("/delta%3a"))); balloc, StringRef::from_lit("/delta%3a")));
// path component is normalized before mathcing // path component is normalized before matching
CU_ASSERT( CU_ASSERT(
"/alpha/bravo/" == "/alpha/bravo/" ==
http2::rewrite_clean_path( http2::rewrite_clean_path(

206
src/http3.cc Normal file
View File

@@ -0,0 +1,206 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2021 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "http3.h"
namespace nghttp2 {
namespace http3 {
namespace {
nghttp3_nv make_nv_internal(const std::string &name, const std::string &value,
bool never_index, uint8_t nv_flags) {
uint8_t flags;
flags = nv_flags |
(never_index ? NGHTTP3_NV_FLAG_NEVER_INDEX : NGHTTP3_NV_FLAG_NONE);
return {(uint8_t *)name.c_str(), (uint8_t *)value.c_str(), name.size(),
value.size(), flags};
}
} // namespace
namespace {
nghttp3_nv make_nv_internal(const StringRef &name, const StringRef &value,
bool never_index, uint8_t nv_flags) {
uint8_t flags;
flags = nv_flags |
(never_index ? NGHTTP3_NV_FLAG_NEVER_INDEX : NGHTTP3_NV_FLAG_NONE);
return {(uint8_t *)name.c_str(), (uint8_t *)value.c_str(), name.size(),
value.size(), flags};
}
} // namespace
nghttp3_nv make_nv(const std::string &name, const std::string &value,
bool never_index) {
return make_nv_internal(name, value, never_index, NGHTTP3_NV_FLAG_NONE);
}
nghttp3_nv make_nv(const StringRef &name, const StringRef &value,
bool never_index) {
return make_nv_internal(name, value, never_index, NGHTTP3_NV_FLAG_NONE);
}
nghttp3_nv make_nv_nocopy(const std::string &name, const std::string &value,
bool never_index) {
return make_nv_internal(name, value, never_index,
NGHTTP3_NV_FLAG_NO_COPY_NAME |
NGHTTP3_NV_FLAG_NO_COPY_VALUE);
}
nghttp3_nv make_nv_nocopy(const StringRef &name, const StringRef &value,
bool never_index) {
return make_nv_internal(name, value, never_index,
NGHTTP3_NV_FLAG_NO_COPY_NAME |
NGHTTP3_NV_FLAG_NO_COPY_VALUE);
}
namespace {
void copy_headers_to_nva_internal(std::vector<nghttp3_nv> &nva,
const HeaderRefs &headers, uint8_t nv_flags,
uint32_t flags) {
auto it_forwarded = std::end(headers);
auto it_xff = std::end(headers);
auto it_xfp = std::end(headers);
auto it_via = std::end(headers);
for (auto it = std::begin(headers); it != std::end(headers); ++it) {
auto kv = &(*it);
if (kv->name.empty() || kv->name[0] == ':') {
continue;
}
switch (kv->token) {
case http2::HD_COOKIE:
case http2::HD_CONNECTION:
case http2::HD_HOST:
case http2::HD_HTTP2_SETTINGS:
case http2::HD_KEEP_ALIVE:
case http2::HD_PROXY_CONNECTION:
case http2::HD_SERVER:
case http2::HD_TE:
case http2::HD_TRANSFER_ENCODING:
case http2::HD_UPGRADE:
continue;
case http2::HD_EARLY_DATA:
if (flags & http2::HDOP_STRIP_EARLY_DATA) {
continue;
}
break;
case http2::HD_SEC_WEBSOCKET_ACCEPT:
if (flags & http2::HDOP_STRIP_SEC_WEBSOCKET_ACCEPT) {
continue;
}
break;
case http2::HD_SEC_WEBSOCKET_KEY:
if (flags & http2::HDOP_STRIP_SEC_WEBSOCKET_KEY) {
continue;
}
break;
case http2::HD_FORWARDED:
if (flags & http2::HDOP_STRIP_FORWARDED) {
continue;
}
if (it_forwarded == std::end(headers)) {
it_forwarded = it;
continue;
}
kv = &(*it_forwarded);
it_forwarded = it;
break;
case http2::HD_X_FORWARDED_FOR:
if (flags & http2::HDOP_STRIP_X_FORWARDED_FOR) {
continue;
}
if (it_xff == std::end(headers)) {
it_xff = it;
continue;
}
kv = &(*it_xff);
it_xff = it;
break;
case http2::HD_X_FORWARDED_PROTO:
if (flags & http2::HDOP_STRIP_X_FORWARDED_PROTO) {
continue;
}
if (it_xfp == std::end(headers)) {
it_xfp = it;
continue;
}
kv = &(*it_xfp);
it_xfp = it;
break;
case http2::HD_VIA:
if (flags & http2::HDOP_STRIP_VIA) {
continue;
}
if (it_via == std::end(headers)) {
it_via = it;
continue;
}
kv = &(*it_via);
it_via = it;
break;
}
nva.push_back(
make_nv_internal(kv->name, kv->value, kv->no_index, nv_flags));
}
}
} // namespace
void copy_headers_to_nva(std::vector<nghttp3_nv> &nva,
const HeaderRefs &headers, uint32_t flags) {
copy_headers_to_nva_internal(nva, headers, NGHTTP3_NV_FLAG_NONE, flags);
}
void copy_headers_to_nva_nocopy(std::vector<nghttp3_nv> &nva,
const HeaderRefs &headers, uint32_t flags) {
copy_headers_to_nva_internal(
nva, headers,
NGHTTP3_NV_FLAG_NO_COPY_NAME | NGHTTP3_NV_FLAG_NO_COPY_VALUE, flags);
}
int check_nv(const uint8_t *name, size_t namelen, const uint8_t *value,
size_t valuelen) {
if (!nghttp3_check_header_name(name, namelen)) {
return 0;
}
if (!nghttp3_check_header_value(value, valuelen)) {
return 0;
}
return 1;
}
} // namespace http3
} // namespace nghttp2

123
src/http3.h Normal file
View File

@@ -0,0 +1,123 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2021 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef HTTP3_H
#define HTTP3_H
#include "nghttp2_config.h"
#include <cstring>
#include <string>
#include <vector>
#include <nghttp3/nghttp3.h>
#include "http2.h"
#include "template.h"
namespace nghttp2 {
namespace http3 {
// Creates nghttp3_nv using |name| and |value| and returns it. The
// returned value only references the data pointer to name.c_str() and
// value.c_str(). If |no_index| is true, nghttp3_nv flags member has
// NGHTTP3_NV_FLAG_NEVER_INDEX flag set.
nghttp3_nv make_nv(const std::string &name, const std::string &value,
bool never_index = false);
nghttp3_nv make_nv(const StringRef &name, const StringRef &value,
bool never_index = false);
nghttp3_nv make_nv_nocopy(const std::string &name, const std::string &value,
bool never_index = false);
nghttp3_nv make_nv_nocopy(const StringRef &name, const StringRef &value,
bool never_index = false);
// Create nghttp3_nv from string literal |name| and |value|.
template <size_t N, size_t M>
constexpr nghttp3_nv make_nv_ll(const char (&name)[N], const char (&value)[M]) {
return {(uint8_t *)name, (uint8_t *)value, N - 1, M - 1,
NGHTTP3_NV_FLAG_NO_COPY_NAME | NGHTTP3_NV_FLAG_NO_COPY_VALUE};
}
// Create nghttp3_nv from string literal |name| and c-string |value|.
template <size_t N>
nghttp3_nv make_nv_lc(const char (&name)[N], const char *value) {
return {(uint8_t *)name, (uint8_t *)value, N - 1, strlen(value),
NGHTTP3_NV_FLAG_NO_COPY_NAME};
}
template <size_t N>
nghttp3_nv make_nv_lc_nocopy(const char (&name)[N], const char *value) {
return {(uint8_t *)name, (uint8_t *)value, N - 1, strlen(value),
NGHTTP3_NV_FLAG_NO_COPY_NAME | NGHTTP3_NV_FLAG_NO_COPY_VALUE};
}
// Create nghttp3_nv from string literal |name| and std::string
// |value|.
template <size_t N>
nghttp3_nv make_nv_ls(const char (&name)[N], const std::string &value) {
return {(uint8_t *)name, (uint8_t *)value.c_str(), N - 1, value.size(),
NGHTTP3_NV_FLAG_NO_COPY_NAME};
}
template <size_t N>
nghttp3_nv make_nv_ls_nocopy(const char (&name)[N], const std::string &value) {
return {(uint8_t *)name, (uint8_t *)value.c_str(), N - 1, value.size(),
NGHTTP3_NV_FLAG_NO_COPY_NAME | NGHTTP3_NV_FLAG_NO_COPY_VALUE};
}
template <size_t N>
nghttp3_nv make_nv_ls_nocopy(const char (&name)[N], const StringRef &value) {
return {(uint8_t *)name, (uint8_t *)value.c_str(), N - 1, value.size(),
NGHTTP3_NV_FLAG_NO_COPY_NAME | NGHTTP3_NV_FLAG_NO_COPY_VALUE};
}
// Appends headers in |headers| to |nv|. |headers| must be indexed
// before this call (its element's token field is assigned). Certain
// headers, including disallowed headers in HTTP/3 spec and headers
// which require special handling (i.e. via), are not copied. |flags|
// is one or more of HeaderBuildOp flags. They tell function that
// certain header fields should not be added.
void copy_headers_to_nva(std::vector<nghttp3_nv> &nva,
const HeaderRefs &headers, uint32_t flags);
// Just like copy_headers_to_nva(), but this adds
// NGHTTP3_NV_FLAG_NO_COPY_NAME and NGHTTP3_NV_FLAG_NO_COPY_VALUE.
void copy_headers_to_nva_nocopy(std::vector<nghttp3_nv> &nva,
const HeaderRefs &headers, uint32_t flags);
// Checks the header name/value pair using nghttp3_check_header_name()
// and nghttp3_check_header_value(). If both function returns nonzero,
// this function returns nonzero.
int check_nv(const uint8_t *name, size_t namelen, const uint8_t *value,
size_t valuelen);
} // namespace http3
} // namespace nghttp2
#endif // HTTP3_H

View File

@@ -113,13 +113,22 @@ template <typename T> struct Pool {
template <typename Memchunk> struct Memchunks { template <typename Memchunk> struct Memchunks {
Memchunks(Pool<Memchunk> *pool) Memchunks(Pool<Memchunk> *pool)
: pool(pool), head(nullptr), tail(nullptr), len(0) {} : pool(pool),
head(nullptr),
tail(nullptr),
len(0),
mark(nullptr),
mark_pos(nullptr),
mark_offset(0) {}
Memchunks(const Memchunks &) = delete; Memchunks(const Memchunks &) = delete;
Memchunks(Memchunks &&other) noexcept Memchunks(Memchunks &&other) noexcept
: pool{other.pool}, // keep other.pool : pool{other.pool}, // keep other.pool
head{std::exchange(other.head, nullptr)}, head{std::exchange(other.head, nullptr)},
tail{std::exchange(other.tail, nullptr)}, tail{std::exchange(other.tail, nullptr)},
len{std::exchange(other.len, 0)} {} len{std::exchange(other.len, 0)},
mark{std::exchange(other.mark, nullptr)},
mark_pos{std::exchange(other.mark_pos, nullptr)},
mark_offset{std::exchange(other.mark_offset, 0)} {}
Memchunks &operator=(const Memchunks &) = delete; Memchunks &operator=(const Memchunks &) = delete;
Memchunks &operator=(Memchunks &&other) noexcept { Memchunks &operator=(Memchunks &&other) noexcept {
if (this == &other) { if (this == &other) {
@@ -132,6 +141,9 @@ template <typename Memchunk> struct Memchunks {
head = std::exchange(other.head, nullptr); head = std::exchange(other.head, nullptr);
tail = std::exchange(other.tail, nullptr); tail = std::exchange(other.tail, nullptr);
len = std::exchange(other.len, 0); len = std::exchange(other.len, 0);
mark = std::exchange(other.mark, nullptr);
mark_pos = std::exchange(other.mark_pos, nullptr);
mark_offset = std::exchange(other.mark_offset, 0);
return *this; return *this;
} }
@@ -200,6 +212,8 @@ template <typename Memchunk> struct Memchunks {
return len; return len;
} }
size_t remove(void *dest, size_t count) { size_t remove(void *dest, size_t count) {
assert(mark == nullptr);
if (!tail || count == 0) { if (!tail || count == 0) {
return 0; return 0;
} }
@@ -231,6 +245,8 @@ template <typename Memchunk> struct Memchunks {
return first - static_cast<uint8_t *>(dest); return first - static_cast<uint8_t *>(dest);
} }
size_t remove(Memchunks &dest, size_t count) { size_t remove(Memchunks &dest, size_t count) {
assert(mark == nullptr);
if (!tail || count == 0) { if (!tail || count == 0) {
return 0; return 0;
} }
@@ -262,6 +278,7 @@ template <typename Memchunk> struct Memchunks {
} }
size_t remove(Memchunks &dest) { size_t remove(Memchunks &dest) {
assert(pool == dest.pool); assert(pool == dest.pool);
assert(mark == nullptr);
if (head == nullptr) { if (head == nullptr) {
return 0; return 0;
@@ -284,6 +301,8 @@ template <typename Memchunk> struct Memchunks {
return n; return n;
} }
size_t drain(size_t count) { size_t drain(size_t count) {
assert(mark == nullptr);
auto ndata = count; auto ndata = count;
auto m = head; auto m = head;
while (m) { while (m) {
@@ -305,6 +324,38 @@ template <typename Memchunk> struct Memchunks {
} }
return ndata - count; return ndata - count;
} }
size_t drain_mark(size_t count) {
auto ndata = count;
auto m = head;
while (m) {
auto next = m->next;
auto n = std::min(count, m->len());
m->pos += n;
count -= n;
len -= n;
mark_offset -= n;
if (m->len() > 0) {
assert(mark != m || m->pos <= mark_pos);
break;
}
if (mark == m) {
assert(m->pos <= mark_pos);
mark = nullptr;
mark_pos = nullptr;
mark_offset = 0;
}
pool->recycle(m);
m = next;
}
head = m;
if (head == nullptr) {
tail = nullptr;
}
return ndata - count;
}
int riovec(struct iovec *iov, int iovcnt) const { int riovec(struct iovec *iov, int iovcnt) const {
if (!head) { if (!head) {
return 0; return 0;
@@ -317,7 +368,41 @@ template <typename Memchunk> struct Memchunks {
} }
return i; return i;
} }
int riovec_mark(struct iovec *iov, int iovcnt) {
if (!head || iovcnt == 0) {
return 0;
}
int i = 0;
Memchunk *m;
if (mark) {
if (mark_pos != mark->last) {
iov[0].iov_base = mark_pos;
iov[0].iov_len = mark->len() - (mark_pos - mark->pos);
mark_pos = mark->last;
mark_offset += iov[0].iov_len;
i = 1;
}
m = mark->next;
} else {
i = 0;
m = head;
}
for (; i < iovcnt && m; ++i, m = m->next) {
iov[i].iov_base = m->pos;
iov[i].iov_len = m->len();
mark = m;
mark_pos = m->last;
mark_offset += m->len();
}
return i;
}
size_t rleft() const { return len; } size_t rleft() const { return len; }
size_t rleft_mark() const { return len - mark_offset; }
void reset() { void reset() {
for (auto m = head; m;) { for (auto m = head; m;) {
auto next = m->next; auto next = m->next;
@@ -325,12 +410,17 @@ template <typename Memchunk> struct Memchunks {
m = next; m = next;
} }
len = 0; len = 0;
head = tail = nullptr; head = tail = mark = nullptr;
mark_pos = nullptr;
mark_offset = 0;
} }
Pool<Memchunk> *pool; Pool<Memchunk> *pool;
Memchunk *head, *tail; Memchunk *head, *tail;
size_t len; size_t len;
Memchunk *mark;
uint8_t *mark_pos;
size_t mark_offset;
}; };
// Wrapper around Memchunks to offer "peeking" functionality. // Wrapper around Memchunks to offer "peeking" functionality.

View File

@@ -2268,7 +2268,7 @@ int communicate(
auto loop = EV_DEFAULT; auto loop = EV_DEFAULT;
SSL_CTX *ssl_ctx = nullptr; SSL_CTX *ssl_ctx = nullptr;
if (scheme == "https") { if (scheme == "https") {
ssl_ctx = SSL_CTX_new(SSLv23_client_method()); ssl_ctx = SSL_CTX_new(TLS_client_method());
if (!ssl_ctx) { if (!ssl_ctx) {
std::cerr << "[ERROR] Failed to create SSL_CTX: " std::cerr << "[ERROR] Failed to create SSL_CTX: "
<< ERR_error_string(ERR_get_error(), nullptr) << std::endl; << ERR_error_string(ERR_get_error(), nullptr) << std::endl;

60
src/quic.cc Normal file
View File

@@ -0,0 +1,60 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2019 nghttp2 contributors
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "quic.h"
#include <cassert>
#include <ngtcp2/ngtcp2.h>
#include <nghttp3/nghttp3.h>
#include "template.h"
using namespace nghttp2;
namespace quic {
Error err_transport(int liberr) {
if (liberr == NGTCP2_ERR_RECV_VERSION_NEGOTIATION) {
return {ErrorType::TransportVersionNegotiation, 0};
}
return {ErrorType::Transport,
ngtcp2_err_infer_quic_transport_error_code(liberr)};
}
Error err_transport_idle_timeout() {
return {ErrorType::TransportIdleTimeout, 0};
}
Error err_transport_tls(int alert) {
return {ErrorType::Transport, ngtcp2_err_infer_quic_transport_error_code(
NGTCP2_CRYPTO_ERROR | alert)};
}
Error err_application(int liberr) {
return {ErrorType::Application,
nghttp3_err_infer_quic_app_error_code(liberr)};
}
} // namespace quic

56
src/quic.h Normal file
View File

@@ -0,0 +1,56 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2019 nghttp2 contributors
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef QUIC_H
#define QUIC_H
#include "nghttp2_config.h"
#include "stdint.h"
namespace quic {
enum class ErrorType {
Transport,
TransportVersionNegotiation,
TransportIdleTimeout,
Application,
};
struct Error {
Error(ErrorType type, uint64_t code) : type(type), code(code) {}
Error() : type(ErrorType::Transport), code(0) {}
ErrorType type;
uint64_t code;
};
Error err_transport(int liberr);
Error err_transport_idle_timeout();
Error err_transport_tls(int alert);
Error err_application(int liberr);
} // namespace quic
#endif // QUIC_H

View File

@@ -135,6 +135,8 @@ int main(int argc, char *argv[]) {
shrpx::test_shrpx_http_create_via_header_value) || shrpx::test_shrpx_http_create_via_header_value) ||
!CU_add_test(pSuite, "http_create_affinity_cookie", !CU_add_test(pSuite, "http_create_affinity_cookie",
shrpx::test_shrpx_http_create_affinity_cookie) || shrpx::test_shrpx_http_create_affinity_cookie) ||
!CU_add_test(pSuite, "http_create_atlsvc_header_field_value",
shrpx::test_shrpx_http_create_altsvc_header_value) ||
!CU_add_test(pSuite, "router_match", shrpx::test_shrpx_router_match) || !CU_add_test(pSuite, "router_match", shrpx::test_shrpx_router_match) ||
!CU_add_test(pSuite, "router_match_wildcard", !CU_add_test(pSuite, "router_match_wildcard",
shrpx::test_shrpx_router_match_wildcard) || shrpx::test_shrpx_router_match_wildcard) ||
@@ -197,6 +199,7 @@ int main(int argc, char *argv[]) {
shrpx::test_util_extract_host) || shrpx::test_util_extract_host) ||
!CU_add_test(pSuite, "util_split_hostport", !CU_add_test(pSuite, "util_split_hostport",
shrpx::test_util_split_hostport) || shrpx::test_util_split_hostport) ||
!CU_add_test(pSuite, "util_split_str", shrpx::test_util_split_str) ||
!CU_add_test(pSuite, "gzip_inflate", test_nghttp2_gzip_inflate) || !CU_add_test(pSuite, "gzip_inflate", test_nghttp2_gzip_inflate) ||
!CU_add_test(pSuite, "buffer_write", nghttp2::test_buffer_write) || !CU_add_test(pSuite, "buffer_write", nghttp2::test_buffer_write) ||
!CU_add_test(pSuite, "pool_recycle", nghttp2::test_pool_recycle) || !CU_add_test(pSuite, "pool_recycle", nghttp2::test_pool_recycle) ||

File diff suppressed because it is too large Load Diff

View File

@@ -48,4 +48,11 @@
inline int initgroups(const char *user, gid_t group) { return 0; } inline int initgroups(const char *user, gid_t group) { return 0; }
#endif // defined(HAVE_DECL_INITGROUPS) && !HAVE_DECL_INITGROUPS #endif // defined(HAVE_DECL_INITGROUPS) && !HAVE_DECL_INITGROUPS
#ifndef HAVE_BPF_STATS_TYPE
/* Newer kernel should have this defined in linux/bpf.h */
enum bpf_stats_type {
BPF_STATS_RUN_TIME = 0,
};
#endif // !HAVE_BPF_STATS_TYPE
#endif // SHRPX_H #endif // SHRPX_H

View File

@@ -463,7 +463,7 @@ int APIDownstreamConnection::on_read() { return 0; }
int APIDownstreamConnection::on_write() { return 0; } int APIDownstreamConnection::on_write() { return 0; }
void APIDownstreamConnection::on_upstream_change(Upstream *uptream) {} void APIDownstreamConnection::on_upstream_change(Upstream *upstream) {}
bool APIDownstreamConnection::poolable() const { return false; } bool APIDownstreamConnection::poolable() const { return false; }

View File

@@ -81,7 +81,7 @@ public:
virtual int on_read(); virtual int on_read();
virtual int on_write(); virtual int on_write();
virtual void on_upstream_change(Upstream *uptream); virtual void on_upstream_change(Upstream *upstream);
// true if this object is poolable. // true if this object is poolable.
virtual bool poolable() const; virtual bool poolable() const;

View File

@@ -50,6 +50,10 @@
#include "shrpx_connect_blocker.h" #include "shrpx_connect_blocker.h"
#include "shrpx_api_downstream_connection.h" #include "shrpx_api_downstream_connection.h"
#include "shrpx_health_monitor_downstream_connection.h" #include "shrpx_health_monitor_downstream_connection.h"
#include "shrpx_null_downstream_connection.h"
#ifdef ENABLE_HTTP3
# include "shrpx_http3_upstream.h"
#endif // ENABLE_HTTP3
#include "shrpx_log.h" #include "shrpx_log.h"
#include "util.h" #include "util.h"
#include "template.h" #include "template.h"
@@ -285,6 +289,20 @@ int ClientHandler::write_tls() {
} }
} }
#ifdef ENABLE_HTTP3
int ClientHandler::read_quic(const UpstreamAddr *faddr,
const Address &remote_addr,
const Address &local_addr,
const ngtcp2_pkt_info &pi, const uint8_t *data,
size_t datalen) {
auto upstream = static_cast<Http3Upstream *>(upstream_.get());
return upstream->on_read(faddr, remote_addr, local_addr, pi, data, datalen);
}
int ClientHandler::write_quic() { return upstream_->on_write(); }
#endif // ENABLE_HTTP3
int ClientHandler::upstream_noop() { return 0; } int ClientHandler::upstream_noop() { return 0; }
int ClientHandler::upstream_read() { int ClientHandler::upstream_read() {
@@ -401,7 +419,8 @@ ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl,
get_config()->conn.upstream.ratelimit.write, get_config()->conn.upstream.ratelimit.write,
get_config()->conn.upstream.ratelimit.read, writecb, readcb, get_config()->conn.upstream.ratelimit.read, writecb, readcb,
timeoutcb, this, get_config()->tls.dyn_rec.warmup_threshold, timeoutcb, this, get_config()->tls.dyn_rec.warmup_threshold,
get_config()->tls.dyn_rec.idle_timeout, Proto::NONE), get_config()->tls.dyn_rec.idle_timeout,
faddr->quic ? Proto::HTTP3 : Proto::NONE),
ipaddr_(make_string_ref(balloc_, ipaddr)), ipaddr_(make_string_ref(balloc_, ipaddr)),
port_(make_string_ref(balloc_, port)), port_(make_string_ref(balloc_, port)),
faddr_(faddr), faddr_(faddr),
@@ -417,11 +436,14 @@ ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl,
reneg_shutdown_timer_.data = this; reneg_shutdown_timer_.data = this;
if (!faddr->quic) {
conn_.rlimit.startw(); conn_.rlimit.startw();
}
ev_timer_again(conn_.loop, &conn_.rt); ev_timer_again(conn_.loop, &conn_.rt);
auto config = get_config(); auto config = get_config();
if (!faddr->quic) {
if (faddr_->accept_proxy_protocol || if (faddr_->accept_proxy_protocol ||
config->conn.upstream.accept_proxy_protocol) { config->conn.upstream.accept_proxy_protocol) {
read_ = &ClientHandler::read_clear; read_ = &ClientHandler::read_clear;
@@ -431,6 +453,7 @@ ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl,
} else { } else {
setup_upstream_io_callback(); setup_upstream_io_callback();
} }
}
auto &fwdconf = config->http.forwarded; auto &fwdconf = config->http.forwarded;
@@ -491,6 +514,18 @@ void ClientHandler::setup_upstream_io_callback() {
} }
} }
#ifdef ENABLE_HTTP3
void ClientHandler::setup_http3_upstream(
std::unique_ptr<Http3Upstream> &&upstream) {
upstream_ = std::move(upstream);
write_ = &ClientHandler::write_quic;
auto config = get_config();
reset_upstream_read_timeout(config->conn.upstream.timeout.http3_read);
}
#endif // ENABLE_HTTP3
ClientHandler::~ClientHandler() { ClientHandler::~ClientHandler() {
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "Deleting"; CLOG(INFO, this) << "Deleting";
@@ -511,7 +546,8 @@ ClientHandler::~ClientHandler() {
// TODO If backend is http/2, and it is in CONNECTED state, signal // TODO If backend is http/2, and it is in CONNECTED state, signal
// it and make it loopbreak when output is zero. // it and make it loopbreak when output is zero.
if (worker_->get_graceful_shutdown() && worker_stat->num_connections == 0) { if (worker_->get_graceful_shutdown() && worker_stat->num_connections == 0 &&
worker_stat->num_close_waits == 0) {
ev_break(conn_.loop); ev_break(conn_.loop);
} }
@@ -848,7 +884,6 @@ DownstreamAddr *ClientHandler::get_downstream_addr(int &err,
err = -1; err = -1;
return nullptr; return nullptr;
} }
aff_idx = i;
} }
return addr; return addr;
@@ -973,6 +1008,13 @@ ClientHandler::get_downstream_connection(int &err, Downstream *downstream) {
} }
auto &group = groups[group_idx]; auto &group = groups[group_idx];
if (group->shared_addr->dnf) {
auto dconn = std::make_unique<NullDownstreamConnection>(group);
dconn->set_client_handler(this);
return dconn;
}
auto addr = get_downstream_addr(err, group.get(), downstream); auto addr = get_downstream_addr(err, group.get(), downstream);
if (addr == nullptr) { if (addr == nullptr) {
return nullptr; return nullptr;
@@ -1556,4 +1598,13 @@ StringRef ClientHandler::get_alpn() const { return alpn_; }
BlockAllocator &ClientHandler::get_block_allocator() { return balloc_; } BlockAllocator &ClientHandler::get_block_allocator() { return balloc_; }
void ClientHandler::set_alpn_from_conn() {
const unsigned char *alpn;
unsigned int alpnlen;
SSL_get0_alpn_selected(conn_.tls.ssl, &alpn, &alpnlen);
alpn_ = make_string_ref(balloc_, StringRef{alpn, alpnlen});
}
} // namespace shrpx } // namespace shrpx

View File

@@ -53,6 +53,9 @@ class Downstream;
struct WorkerStat; struct WorkerStat;
struct DownstreamAddrGroup; struct DownstreamAddrGroup;
struct DownstreamAddr; struct DownstreamAddr;
#ifdef ENABLE_HTTP3
class Http3Upstream;
#endif // ENABLE_HTTP3
class ClientHandler { class ClientHandler {
public: public:
@@ -143,6 +146,14 @@ public:
void setup_upstream_io_callback(); void setup_upstream_io_callback();
#ifdef ENABLE_HTTP3
void setup_http3_upstream(std::unique_ptr<Http3Upstream> &&upstream);
int read_quic(const UpstreamAddr *faddr, const Address &remote_addr,
const Address &local_addr, const ngtcp2_pkt_info &pi,
const uint8_t *data, size_t datalen);
int write_quic();
#endif // ENABLE_HTTP3
// Returns string suitable for use in "by" parameter of Forwarded // Returns string suitable for use in "by" parameter of Forwarded
// header field. // header field.
StringRef get_forwarded_by() const; StringRef get_forwarded_by() const;
@@ -177,6 +188,8 @@ public:
BlockAllocator &get_block_allocator(); BlockAllocator &get_block_allocator();
void set_alpn_from_conn();
private: private:
// Allocator to allocate memory for connection-wide objects. Make // Allocator to allocate memory for connection-wide objects. Make
// sure that the allocations must be bounded, and not proportional // sure that the allocations must be bounded, and not proportional

View File

@@ -230,10 +230,81 @@ read_tls_ticket_key_file(const std::vector<StringRef> &files,
return ticket_keys; return ticket_keys;
} }
#ifdef ENABLE_HTTP3
std::shared_ptr<QUICKeyingMaterials>
read_quic_secret_file(const StringRef &path) {
constexpr size_t expectedlen =
SHRPX_QUIC_SECRET_RESERVEDLEN + SHRPX_QUIC_SECRETLEN + SHRPX_QUIC_SALTLEN;
auto qkms = std::make_shared<QUICKeyingMaterials>();
auto &kms = qkms->keying_materials;
std::ifstream f(path.c_str());
if (!f) {
LOG(ERROR) << "frontend-quic-secret-file: could not open file " << path;
return nullptr;
}
std::array<char, 4096> buf;
while (f.getline(buf.data(), buf.size())) {
auto len = strlen(buf.data());
if (len == 0 || buf[0] == '#') {
continue;
}
auto s = StringRef{std::begin(buf), std::begin(buf) + len};
if (s.size() != expectedlen * 2 || !util::is_hex_string(s)) {
LOG(ERROR) << "frontend-quic-secret-file: each line must be a "
<< expectedlen * 2 << " bytes hex encoded string";
return nullptr;
}
kms.emplace_back();
auto &qkm = kms.back();
auto p = std::begin(s);
util::decode_hex(std::begin(qkm.reserved),
StringRef{p, p + qkm.reserved.size()});
p += qkm.reserved.size() * 2;
util::decode_hex(std::begin(qkm.secret),
StringRef{p, p + qkm.secret.size()});
p += qkm.secret.size() * 2;
util::decode_hex(std::begin(qkm.salt), StringRef{p, p + qkm.salt.size()});
p += qkm.salt.size() * 2;
assert(static_cast<size_t>(p - std::begin(s)) == expectedlen * 2);
qkm.id = qkm.reserved[0] & 0xc0;
if (kms.size() == 4) {
break;
}
}
if (f.bad() || (!f.eof() && f.fail())) {
LOG(ERROR)
<< "frontend-quic-secret-file: error occurred while reading file "
<< path;
return nullptr;
}
if (kms.empty()) {
LOG(WARN)
<< "frontend-quic-secret-file: no keying materials are present in file "
<< path;
return nullptr;
}
return qkms;
}
#endif // ENABLE_HTTP3
FILE *open_file_for_write(const char *filename) { FILE *open_file_for_write(const char *filename) {
std::array<char, STRERROR_BUFSIZE> errbuf; std::array<char, STRERROR_BUFSIZE> errbuf;
#if defined O_CLOEXEC #ifdef O_CLOEXEC
auto fd = open(filename, O_WRONLY | O_CLOEXEC | O_CREAT | O_TRUNC, auto fd = open(filename, O_WRONLY | O_CLOEXEC | O_CREAT | O_TRUNC,
S_IRUSR | S_IWUSR); S_IRUSR | S_IWUSR);
#else #else
@@ -372,6 +443,57 @@ int parse_int(T *dest, const StringRef &opt, const char *optarg) {
return 0; return 0;
} }
namespace {
int parse_altsvc(AltSvc &altsvc, const StringRef &opt,
const StringRef &optarg) {
// PROTOID, PORT, HOST, ORIGIN, PARAMS.
auto tokens = util::split_str(optarg, ',', 5);
if (tokens.size() < 2) {
// Requires at least protocol_id and port
LOG(ERROR) << opt << ": too few parameters: " << optarg;
return -1;
}
int port;
if (parse_uint(&port, opt, tokens[1]) != 0) {
return -1;
}
if (port < 1 ||
port > static_cast<int>(std::numeric_limits<uint16_t>::max())) {
LOG(ERROR) << opt << ": port is invalid: " << tokens[1];
return -1;
}
altsvc.protocol_id = make_string_ref(config->balloc, tokens[0]);
altsvc.port = port;
altsvc.service = make_string_ref(config->balloc, tokens[1]);
if (tokens.size() > 2) {
if (!tokens[2].empty()) {
altsvc.host = make_string_ref(config->balloc, tokens[2]);
}
if (tokens.size() > 3) {
if (!tokens[3].empty()) {
altsvc.origin = make_string_ref(config->balloc, tokens[3]);
}
if (tokens.size() > 4) {
if (!tokens[4].empty()) {
altsvc.params = make_string_ref(config->balloc, tokens[4]);
}
}
}
}
return 0;
}
} // namespace
namespace { namespace {
// generated by gennghttpxfun.py // generated by gennghttpxfun.py
LogFragmentType log_var_lookup_token(const char *name, size_t namelen) { LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
@@ -785,6 +907,7 @@ struct UpstreamParams {
bool tls; bool tls;
bool sni_fwd; bool sni_fwd;
bool proxyproto; bool proxyproto;
bool quic;
}; };
namespace { namespace {
@@ -819,6 +942,13 @@ int parse_upstream_params(UpstreamParams &out, const StringRef &src_params) {
out.alt_mode = UpstreamAltMode::HEALTHMON; out.alt_mode = UpstreamAltMode::HEALTHMON;
} else if (util::strieq_l("proxyproto", param)) { } else if (util::strieq_l("proxyproto", param)) {
out.proxyproto = true; out.proxyproto = true;
} else if (util::strieq_l("quic", param)) {
#ifdef ENABLE_HTTP3
out.quic = true;
#else // !ENABLE_HTTP3
LOG(ERROR) << "quic: QUIC is disabled at compile time";
return -1;
#endif // !ENABLE_HTTP3
} else if (!param.empty()) { } else if (!param.empty()) {
LOG(ERROR) << "frontend: " << param << ": unknown keyword"; LOG(ERROR) << "frontend: " << param << ": unknown keyword";
return -1; return -1;
@@ -851,6 +981,7 @@ struct DownstreamParams {
bool dns; bool dns;
bool redirect_if_not_tls; bool redirect_if_not_tls;
bool upgrade_scheme; bool upgrade_scheme;
bool dnf;
}; };
namespace { namespace {
@@ -1025,6 +1156,8 @@ int parse_downstream_params(DownstreamParams &out,
return -1; return -1;
} }
out.group_weight = n; out.group_weight = n;
} else if (util::strieq_l("dnf", param)) {
out.dnf = true;
} else if (!param.empty()) { } else if (!param.empty()) {
LOG(ERROR) << "backend: " << param << ": unknown keyword"; LOG(ERROR) << "backend: " << param << ": unknown keyword";
return -1; return -1;
@@ -1089,6 +1222,7 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
addr.sni = make_string_ref(downstreamconf.balloc, params.sni); addr.sni = make_string_ref(downstreamconf.balloc, params.sni);
addr.dns = params.dns; addr.dns = params.dns;
addr.upgrade_scheme = params.upgrade_scheme; addr.upgrade_scheme = params.upgrade_scheme;
addr.dnf = params.dnf;
auto &routerconf = downstreamconf.router; auto &routerconf = downstreamconf.router;
auto &router = routerconf.router; auto &router = routerconf.router;
@@ -1189,6 +1323,14 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
return -1; return -1;
} }
} }
// All backends in the same group must have the same dnf
// setting. If some backends do not specify dnf, and there is
// at least one backend with dnf, it is used for all backends in
// the group. In general, multiple backends are not necessary
// for dnf because there is no need for load balancing.
if (params.dnf) {
g.dnf = true;
}
g.addrs.push_back(addr); g.addrs.push_back(addr);
continue; continue;
@@ -1213,6 +1355,7 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
g.mruby_file = make_string_ref(downstreamconf.balloc, params.mruby); g.mruby_file = make_string_ref(downstreamconf.balloc, params.mruby);
g.timeout.read = params.read_timeout; g.timeout.read = params.read_timeout;
g.timeout.write = params.write_timeout; g.timeout.write = params.write_timeout;
g.dnf = params.dnf;
if (pattern[0] == '*') { if (pattern[0] == '*') {
// wildcard pattern // wildcard pattern
@@ -1795,6 +1938,11 @@ int option_lookup_token(const char *name, size_t namelen) {
return SHRPX_OPTID_SERVER_NAME; return SHRPX_OPTID_SERVER_NAME;
} }
break; break;
case 'f':
if (util::strieq_l("no-quic-bp", name, 10)) {
return SHRPX_OPTID_NO_QUIC_BPF;
}
break;
case 'r': case 'r':
if (util::strieq_l("tls-sct-di", name, 10)) { if (util::strieq_l("tls-sct-di", name, 10)) {
return SHRPX_OPTID_TLS_SCT_DIR; return SHRPX_OPTID_TLS_SCT_DIR;
@@ -1838,6 +1986,11 @@ int option_lookup_token(const char *name, size_t namelen) {
return SHRPX_OPTID_BACKEND_IPV6; return SHRPX_OPTID_BACKEND_IPV6;
} }
break; break;
case 'c':
if (util::strieq_l("http2-altsv", name, 11)) {
return SHRPX_OPTID_HTTP2_ALTSVC;
}
break;
case 'e': case 'e':
if (util::strieq_l("host-rewrit", name, 11)) { if (util::strieq_l("host-rewrit", name, 11)) {
return SHRPX_OPTID_HOST_REWRITE; return SHRPX_OPTID_HOST_REWRITE;
@@ -1901,6 +2054,11 @@ int option_lookup_token(const char *name, size_t namelen) {
break; break;
case 14: case 14:
switch (name[13]) { switch (name[13]) {
case 'd':
if (util::strieq_l("quic-server-i", name, 13)) {
return SHRPX_OPTID_QUIC_SERVER_ID;
}
break;
case 'e': case 'e':
if (util::strieq_l("accesslog-fil", name, 13)) { if (util::strieq_l("accesslog-fil", name, 13)) {
return SHRPX_OPTID_ACCESSLOG_FILE; return SHRPX_OPTID_ACCESSLOG_FILE;
@@ -1911,6 +2069,11 @@ int option_lookup_token(const char *name, size_t namelen) {
return SHRPX_OPTID_NO_SERVER_PUSH; return SHRPX_OPTID_NO_SERVER_PUSH;
} }
break; break;
case 'k':
if (util::strieq_l("rlimit-memloc", name, 13)) {
return SHRPX_OPTID_RLIMIT_MEMLOCK;
}
break;
case 'p': case 'p':
if (util::strieq_l("no-verify-ocs", name, 13)) { if (util::strieq_l("no-verify-ocs", name, 13)) {
return SHRPX_OPTID_NO_VERIFY_OCSP; return SHRPX_OPTID_NO_VERIFY_OCSP;
@@ -2090,6 +2253,9 @@ int option_lookup_token(const char *name, size_t namelen) {
} }
break; break;
case 's': case 's':
if (util::strieq_l("max-worker-processe", name, 19)) {
return SHRPX_OPTID_MAX_WORKER_PROCESSES;
}
if (util::strieq_l("tls13-client-cipher", name, 19)) { if (util::strieq_l("tls13-client-cipher", name, 19)) {
return SHRPX_OPTID_TLS13_CLIENT_CIPHERS; return SHRPX_OPTID_TLS13_CLIENT_CIPHERS;
} }
@@ -2119,6 +2285,11 @@ int option_lookup_token(const char *name, size_t namelen) {
return SHRPX_OPTID_BACKEND_TLS_SNI_FIELD; return SHRPX_OPTID_BACKEND_TLS_SNI_FIELD;
} }
break; break;
case 'e':
if (util::strieq_l("quic-bpf-program-fil", name, 20)) {
return SHRPX_OPTID_QUIC_BPF_PROGRAM_FILE;
}
break;
case 'l': case 'l':
if (util::strieq_l("accept-proxy-protoco", name, 20)) { if (util::strieq_l("accept-proxy-protoco", name, 20)) {
return SHRPX_OPTID_ACCEPT_PROXY_PROTOCOL; return SHRPX_OPTID_ACCEPT_PROXY_PROTOCOL;
@@ -2168,6 +2339,9 @@ int option_lookup_token(const char *name, size_t namelen) {
if (util::strieq_l("backend-request-buffe", name, 21)) { if (util::strieq_l("backend-request-buffe", name, 21)) {
return SHRPX_OPTID_BACKEND_REQUEST_BUFFER; return SHRPX_OPTID_BACKEND_REQUEST_BUFFER;
} }
if (util::strieq_l("frontend-quic-qlog-di", name, 21)) {
return SHRPX_OPTID_FRONTEND_QUIC_QLOG_DIR;
}
break; break;
case 't': case 't':
if (util::strieq_l("frontend-write-timeou", name, 21)) { if (util::strieq_l("frontend-write-timeou", name, 21)) {
@@ -2191,6 +2365,11 @@ int option_lookup_token(const char *name, size_t namelen) {
return SHRPX_OPTID_PRIVATE_KEY_PASSWD_FILE; return SHRPX_OPTID_PRIVATE_KEY_PASSWD_FILE;
} }
break; break;
case 'g':
if (util::strieq_l("frontend-quic-debug-lo", name, 22)) {
return SHRPX_OPTID_FRONTEND_QUIC_DEBUG_LOG;
}
break;
case 'r': case 'r':
if (util::strieq_l("backend-response-buffe", name, 22)) { if (util::strieq_l("backend-response-buffe", name, 22)) {
return SHRPX_OPTID_BACKEND_RESPONSE_BUFFER; return SHRPX_OPTID_BACKEND_RESPONSE_BUFFER;
@@ -2205,6 +2384,11 @@ int option_lookup_token(const char *name, size_t namelen) {
break; break;
case 24: case 24:
switch (name[23]) { switch (name[23]) {
case 'a':
if (util::strieq_l("frontend-quic-early-dat", name, 23)) {
return SHRPX_OPTID_FRONTEND_QUIC_EARLY_DATA;
}
break;
case 'd': case 'd':
if (util::strieq_l("strip-incoming-forwarde", name, 23)) { if (util::strieq_l("strip-incoming-forwarde", name, 23)) {
return SHRPX_OPTID_STRIP_INCOMING_FORWARDED; return SHRPX_OPTID_STRIP_INCOMING_FORWARDED;
@@ -2239,6 +2423,9 @@ int option_lookup_token(const char *name, size_t namelen) {
if (util::strieq_l("backend-http2-window-siz", name, 24)) { if (util::strieq_l("backend-http2-window-siz", name, 24)) {
return SHRPX_OPTID_BACKEND_HTTP2_WINDOW_SIZE; return SHRPX_OPTID_BACKEND_HTTP2_WINDOW_SIZE;
} }
if (util::strieq_l("frontend-quic-secret-fil", name, 24)) {
return SHRPX_OPTID_FRONTEND_QUIC_SECRET_FILE;
}
break; break;
case 'g': case 'g':
if (util::strieq_l("http2-no-cookie-crumblin", name, 24)) { if (util::strieq_l("http2-no-cookie-crumblin", name, 24)) {
@@ -2253,6 +2440,11 @@ int option_lookup_token(const char *name, size_t namelen) {
return SHRPX_OPTID_MAX_REQUEST_HEADER_FIELDS; return SHRPX_OPTID_MAX_REQUEST_HEADER_FIELDS;
} }
break; break;
case 't':
if (util::strieq_l("frontend-quic-initial-rt", name, 24)) {
return SHRPX_OPTID_FRONTEND_QUIC_INITIAL_RTT;
}
break;
} }
break; break;
case 26: case 26:
@@ -2266,6 +2458,9 @@ int option_lookup_token(const char *name, size_t namelen) {
if (util::strieq_l("frontend-http2-window-siz", name, 25)) { if (util::strieq_l("frontend-http2-window-siz", name, 25)) {
return SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_SIZE; return SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_SIZE;
} }
if (util::strieq_l("frontend-http3-window-siz", name, 25)) {
return SHRPX_OPTID_FRONTEND_HTTP3_WINDOW_SIZE;
}
break; break;
case 's': case 's':
if (util::strieq_l("frontend-http2-window-bit", name, 25)) { if (util::strieq_l("frontend-http2-window-bit", name, 25)) {
@@ -2279,6 +2474,9 @@ int option_lookup_token(const char *name, size_t namelen) {
if (util::strieq_l("backend-keep-alive-timeou", name, 25)) { if (util::strieq_l("backend-keep-alive-timeou", name, 25)) {
return SHRPX_OPTID_BACKEND_KEEP_ALIVE_TIMEOUT; return SHRPX_OPTID_BACKEND_KEEP_ALIVE_TIMEOUT;
} }
if (util::strieq_l("frontend-quic-idle-timeou", name, 25)) {
return SHRPX_OPTID_FRONTEND_QUIC_IDLE_TIMEOUT;
}
if (util::strieq_l("no-http2-cipher-black-lis", name, 25)) { if (util::strieq_l("no-http2-cipher-black-lis", name, 25)) {
return SHRPX_OPTID_NO_HTTP2_CIPHER_BLACK_LIST; return SHRPX_OPTID_NO_HTTP2_CIPHER_BLACK_LIST;
} }
@@ -2295,6 +2493,11 @@ int option_lookup_token(const char *name, size_t namelen) {
return SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED; return SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED;
} }
break; break;
case 'n':
if (util::strieq_l("frontend-quic-require-toke", name, 26)) {
return SHRPX_OPTID_FRONTEND_QUIC_REQUIRE_TOKEN;
}
break;
case 'r': case 'r':
if (util::strieq_l("request-header-field-buffe", name, 26)) { if (util::strieq_l("request-header-field-buffe", name, 26)) {
return SHRPX_OPTID_REQUEST_HEADER_FIELD_BUFFER; return SHRPX_OPTID_REQUEST_HEADER_FIELD_BUFFER;
@@ -2309,6 +2512,9 @@ int option_lookup_token(const char *name, size_t namelen) {
if (util::strieq_l("frontend-http2-read-timeou", name, 26)) { if (util::strieq_l("frontend-http2-read-timeou", name, 26)) {
return SHRPX_OPTID_FRONTEND_HTTP2_READ_TIMEOUT; return SHRPX_OPTID_FRONTEND_HTTP2_READ_TIMEOUT;
} }
if (util::strieq_l("frontend-http3-read-timeou", name, 26)) {
return SHRPX_OPTID_FRONTEND_HTTP3_READ_TIMEOUT;
}
if (util::strieq_l("frontend-keep-alive-timeou", name, 26)) { if (util::strieq_l("frontend-keep-alive-timeou", name, 26)) {
return SHRPX_OPTID_FRONTEND_KEEP_ALIVE_TIMEOUT; return SHRPX_OPTID_FRONTEND_KEEP_ALIVE_TIMEOUT;
} }
@@ -2354,6 +2560,11 @@ int option_lookup_token(const char *name, size_t namelen) {
return SHRPX_OPTID_VERIFY_CLIENT_TOLERATE_EXPIRED; return SHRPX_OPTID_VERIFY_CLIENT_TOLERATE_EXPIRED;
} }
break; break;
case 'e':
if (util::strieq_l("frontend-http3-max-window-siz", name, 29)) {
return SHRPX_OPTID_FRONTEND_HTTP3_MAX_WINDOW_SIZE;
}
break;
case 'r': case 'r':
if (util::strieq_l("ignore-per-pattern-mruby-erro", name, 29)) { if (util::strieq_l("ignore-per-pattern-mruby-erro", name, 29)) {
return SHRPX_OPTID_IGNORE_PER_PATTERN_MRUBY_ERROR; return SHRPX_OPTID_IGNORE_PER_PATTERN_MRUBY_ERROR;
@@ -2452,11 +2663,19 @@ int option_lookup_token(const char *name, size_t namelen) {
if (util::strieq_l("frontend-http2-dump-response-heade", name, 34)) { if (util::strieq_l("frontend-http2-dump-response-heade", name, 34)) {
return SHRPX_OPTID_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER; return SHRPX_OPTID_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER;
} }
if (util::strieq_l("frontend-quic-congestion-controlle", name, 34)) {
return SHRPX_OPTID_FRONTEND_QUIC_CONGESTION_CONTROLLER;
}
break; break;
} }
break; break;
case 36: case 36:
switch (name[35]) { switch (name[35]) {
case 'd':
if (util::strieq_l("worker-process-grace-shutdown-perio", name, 35)) {
return SHRPX_OPTID_WORKER_PROCESS_GRACE_SHUTDOWN_PERIOD;
}
break;
case 'e': case 'e':
if (util::strieq_l("backend-http2-connection-window-siz", name, 35)) { if (util::strieq_l("backend-http2-connection-window-siz", name, 35)) {
return SHRPX_OPTID_BACKEND_HTTP2_CONNECTION_WINDOW_SIZE; return SHRPX_OPTID_BACKEND_HTTP2_CONNECTION_WINDOW_SIZE;
@@ -2483,6 +2702,9 @@ int option_lookup_token(const char *name, size_t namelen) {
if (util::strieq_l("frontend-http2-connection-window-siz", name, 36)) { if (util::strieq_l("frontend-http2-connection-window-siz", name, 36)) {
return SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_SIZE; return SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_SIZE;
} }
if (util::strieq_l("frontend-http3-connection-window-siz", name, 36)) {
return SHRPX_OPTID_FRONTEND_HTTP3_CONNECTION_WINDOW_SIZE;
}
if (util::strieq_l("tls-session-cache-memcached-cert-fil", name, 36)) { if (util::strieq_l("tls-session-cache-memcached-cert-fil", name, 36)) {
return SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE; return SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE;
} }
@@ -2494,6 +2716,9 @@ int option_lookup_token(const char *name, size_t namelen) {
if (util::strieq_l("frontend-http2-max-concurrent-stream", name, 36)) { if (util::strieq_l("frontend-http2-max-concurrent-stream", name, 36)) {
return SHRPX_OPTID_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS; return SHRPX_OPTID_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS;
} }
if (util::strieq_l("frontend-http3-max-concurrent-stream", name, 36)) {
return SHRPX_OPTID_FRONTEND_HTTP3_MAX_CONCURRENT_STREAMS;
}
break; break;
} }
break; break;
@@ -2542,6 +2767,10 @@ int option_lookup_token(const char *name, size_t namelen) {
40)) { 40)) {
return SHRPX_OPTID_FRONTEND_HTTP2_OPTIMIZE_WRITE_BUFFER_SIZE; return SHRPX_OPTID_FRONTEND_HTTP2_OPTIMIZE_WRITE_BUFFER_SIZE;
} }
if (util::strieq_l("frontend-http3-max-connection-window-siz", name,
40)) {
return SHRPX_OPTID_FRONTEND_HTTP3_MAX_CONNECTION_WINDOW_SIZE;
}
if (util::strieq_l("tls-ticket-key-memcached-private-key-fil", name, if (util::strieq_l("tls-ticket-key-memcached-private-key-fil", name,
40)) { 40)) {
return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE; return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE;
@@ -2624,7 +2853,6 @@ int parse_config(Config *config, int optid, const StringRef &opt,
return 0; return 0;
} }
case SHRPX_OPTID_FRONTEND: { case SHRPX_OPTID_FRONTEND: {
auto &listenerconf = config->conn.listener;
auto &apiconf = config->api; auto &apiconf = config->api;
auto addr_end = std::find(std::begin(optarg), std::end(optarg), ';'); auto addr_end = std::find(std::begin(optarg), std::end(optarg), ';');
@@ -2642,23 +2870,49 @@ int parse_config(Config *config, int optid, const StringRef &opt,
return -1; return -1;
} }
if (params.quic) {
if (params.alt_mode != UpstreamAltMode::NONE) {
LOG(ERROR) << "frontend: api or healthmon cannot be used with quic";
return -1;
}
if (!params.tls) {
LOG(ERROR) << "frontend: quic requires TLS";
return -1;
}
}
UpstreamAddr addr{}; UpstreamAddr addr{};
addr.fd = -1; addr.fd = -1;
addr.tls = params.tls; addr.tls = params.tls;
addr.sni_fwd = params.sni_fwd; addr.sni_fwd = params.sni_fwd;
addr.alt_mode = params.alt_mode; addr.alt_mode = params.alt_mode;
addr.accept_proxy_protocol = params.proxyproto; addr.accept_proxy_protocol = params.proxyproto;
addr.quic = params.quic;
if (addr.alt_mode == UpstreamAltMode::API) { if (addr.alt_mode == UpstreamAltMode::API) {
apiconf.enabled = true; apiconf.enabled = true;
} }
#ifdef ENABLE_HTTP3
auto &addrs = params.quic ? config->conn.quic_listener.addrs
: config->conn.listener.addrs;
#else // !ENABLE_HTTP3
auto &addrs = config->conn.listener.addrs;
#endif // !ENABLE_HTTP3
if (util::istarts_with(optarg, SHRPX_UNIX_PATH_PREFIX)) { if (util::istarts_with(optarg, SHRPX_UNIX_PATH_PREFIX)) {
if (addr.quic) {
LOG(ERROR) << "frontend: quic cannot be used on UNIX domain socket";
return -1;
}
auto path = std::begin(optarg) + SHRPX_UNIX_PATH_PREFIX.size(); auto path = std::begin(optarg) + SHRPX_UNIX_PATH_PREFIX.size();
addr.host = make_string_ref(config->balloc, StringRef{path, addr_end}); addr.host = make_string_ref(config->balloc, StringRef{path, addr_end});
addr.host_unix = true; addr.host_unix = true;
addr.index = addrs.size();
listenerconf.addrs.push_back(std::move(addr)); addrs.push_back(std::move(addr));
return 0; return 0;
} }
@@ -2673,21 +2927,25 @@ int parse_config(Config *config, int optid, const StringRef &opt,
if (util::numeric_host(host, AF_INET)) { if (util::numeric_host(host, AF_INET)) {
addr.family = AF_INET; addr.family = AF_INET;
listenerconf.addrs.push_back(std::move(addr)); addr.index = addrs.size();
addrs.push_back(std::move(addr));
return 0; return 0;
} }
if (util::numeric_host(host, AF_INET6)) { if (util::numeric_host(host, AF_INET6)) {
addr.family = AF_INET6; addr.family = AF_INET6;
listenerconf.addrs.push_back(std::move(addr)); addr.index = addrs.size();
addrs.push_back(std::move(addr));
return 0; return 0;
} }
addr.family = AF_INET; addr.family = AF_INET;
listenerconf.addrs.push_back(addr); addr.index = addrs.size();
addrs.push_back(addr);
addr.family = AF_INET6; addr.family = AF_INET6;
listenerconf.addrs.push_back(std::move(addr)); addr.index = addrs.size();
addrs.push_back(std::move(addr));
return 0; return 0;
} }
@@ -3136,45 +3394,10 @@ int parse_config(Config *config, int optid, const StringRef &opt,
case SHRPX_OPTID_PADDING: case SHRPX_OPTID_PADDING:
return parse_uint(&config->padding, opt, optarg); return parse_uint(&config->padding, opt, optarg);
case SHRPX_OPTID_ALTSVC: { case SHRPX_OPTID_ALTSVC: {
auto tokens = util::split_str(optarg, ',');
if (tokens.size() < 2) {
// Requires at least protocol_id and port
LOG(ERROR) << opt << ": too few parameters: " << optarg;
return -1;
}
if (tokens.size() > 4) {
// We only need protocol_id, port, host and origin
LOG(ERROR) << opt << ": too many parameters: " << optarg;
return -1;
}
int port;
if (parse_uint(&port, opt, tokens[1]) != 0) {
return -1;
}
if (port < 1 ||
port > static_cast<int>(std::numeric_limits<uint16_t>::max())) {
LOG(ERROR) << opt << ": port is invalid: " << tokens[1];
return -1;
}
AltSvc altsvc{}; AltSvc altsvc{};
altsvc.protocol_id = make_string_ref(config->balloc, tokens[0]); if (parse_altsvc(altsvc, opt, optarg) != 0) {
return -1;
altsvc.port = port;
altsvc.service = make_string_ref(config->balloc, tokens[1]);
if (tokens.size() > 2) {
altsvc.host = make_string_ref(config->balloc, tokens[2]);
if (tokens.size() > 3) {
altsvc.origin = make_string_ref(config->balloc, tokens[3]);
}
} }
config->http.altsvcs.push_back(std::move(altsvc)); config->http.altsvcs.push_back(std::move(altsvc));
@@ -3731,7 +3954,7 @@ int parse_config(Config *config, int optid, const StringRef &opt,
"65535], inclusive"; "65535], inclusive";
return -1; return -1;
} }
config->http.redirect_https_port = optarg; config->http.redirect_https_port = make_string_ref(config->balloc, optarg);
return 0; return 0;
} }
case SHRPX_OPTID_FRONTEND_MAX_REQUESTS: case SHRPX_OPTID_FRONTEND_MAX_REQUESTS:
@@ -3779,6 +4002,168 @@ int parse_config(Config *config, int optid, const StringRef &opt,
config->http.early_data.strip_incoming = !util::strieq_l("yes", optarg); config->http.early_data.strip_incoming = !util::strieq_l("yes", optarg);
return 0; return 0;
case SHRPX_OPTID_QUIC_BPF_PROGRAM_FILE:
#ifdef ENABLE_HTTP3
config->quic.bpf.prog_file = make_string_ref(config->balloc, optarg);
#endif // ENABLE_HTTP3
return 0;
case SHRPX_OPTID_NO_QUIC_BPF:
#ifdef ENABLE_HTTP3
config->quic.bpf.disabled = util::strieq_l("yes", optarg);
#endif // ENABLE_HTTP3
return 0;
case SHRPX_OPTID_HTTP2_ALTSVC: {
AltSvc altsvc{};
if (parse_altsvc(altsvc, opt, optarg) != 0) {
return -1;
}
config->http.http2_altsvcs.push_back(std::move(altsvc));
return 0;
}
case SHRPX_OPTID_FRONTEND_HTTP3_READ_TIMEOUT:
#ifdef ENABLE_HTTP3
return parse_duration(&config->conn.upstream.timeout.http3_read, opt,
optarg);
#else // !ENABLE_HTTP3
return 0;
#endif // !ENABLE_HTTP3
case SHRPX_OPTID_FRONTEND_QUIC_IDLE_TIMEOUT:
#ifdef ENABLE_HTTP3
return parse_duration(&config->quic.upstream.timeout.idle, opt, optarg);
#else // !ENABLE_HTTP3
return 0;
#endif // !ENABLE_HTTP3
case SHRPX_OPTID_FRONTEND_QUIC_DEBUG_LOG:
#ifdef ENABLE_HTTP3
config->quic.upstream.debug.log = util::strieq_l("yes", optarg);
#endif // ENABLE_HTTP3
return 0;
case SHRPX_OPTID_FRONTEND_HTTP3_WINDOW_SIZE:
#ifdef ENABLE_HTTP3
if (parse_uint_with_unit(&config->http3.upstream.window_size, opt,
optarg) != 0) {
return -1;
}
#endif // ENABLE_HTTP3
return 0;
case SHRPX_OPTID_FRONTEND_HTTP3_CONNECTION_WINDOW_SIZE:
#ifdef ENABLE_HTTP3
if (parse_uint_with_unit(&config->http3.upstream.connection_window_size,
opt, optarg) != 0) {
return -1;
}
#endif // ENABLE_HTTP3
return 0;
case SHRPX_OPTID_FRONTEND_HTTP3_MAX_WINDOW_SIZE:
#ifdef ENABLE_HTTP3
if (parse_uint_with_unit(&config->http3.upstream.max_window_size, opt,
optarg) != 0) {
return -1;
}
#endif // ENABLE_HTTP3
return 0;
case SHRPX_OPTID_FRONTEND_HTTP3_MAX_CONNECTION_WINDOW_SIZE:
#ifdef ENABLE_HTTP3
if (parse_uint_with_unit(&config->http3.upstream.max_connection_window_size,
opt, optarg) != 0) {
return -1;
}
#endif // ENABLE_HTTP3
return 0;
case SHRPX_OPTID_FRONTEND_HTTP3_MAX_CONCURRENT_STREAMS:
#ifdef ENABLE_HTTP3
return parse_uint(&config->http3.upstream.max_concurrent_streams, opt,
optarg);
#else // !ENABLE_HTTP3
return 0;
#endif // !ENABLE_HTTP3
case SHRPX_OPTID_FRONTEND_QUIC_EARLY_DATA:
#ifdef ENABLE_HTTP3
config->quic.upstream.early_data = util::strieq_l("yes", optarg);
#endif // ENABLE_HTTP3
return 0;
case SHRPX_OPTID_FRONTEND_QUIC_QLOG_DIR:
#ifdef ENABLE_HTTP3
config->quic.upstream.qlog.dir = make_string_ref(config->balloc, optarg);
#endif // ENABLE_HTTP3
return 0;
case SHRPX_OPTID_FRONTEND_QUIC_REQUIRE_TOKEN:
#ifdef ENABLE_HTTP3
config->quic.upstream.require_token = util::strieq_l("yes", optarg);
#endif // ENABLE_HTTP3
return 0;
case SHRPX_OPTID_FRONTEND_QUIC_CONGESTION_CONTROLLER:
#ifdef ENABLE_HTTP3
if (util::strieq_l("cubic", optarg)) {
config->quic.upstream.congestion_controller = NGTCP2_CC_ALGO_CUBIC;
} else if (util::strieq_l("bbr", optarg)) {
config->quic.upstream.congestion_controller = NGTCP2_CC_ALGO_BBR;
} else {
LOG(ERROR) << opt << ": must be either cubic or bbr";
return -1;
}
#endif // ENABLE_HTTP3
return 0;
case SHRPX_OPTID_QUIC_SERVER_ID:
#ifdef ENABLE_HTTP3
if (optarg.size() != config->quic.server_id.size() * 2 ||
!util::is_hex_string(optarg)) {
LOG(ERROR) << opt << ": must be a hex-string";
return -1;
}
util::decode_hex(std::begin(config->quic.server_id), optarg);
#endif // ENABLE_HTTP3
return 0;
case SHRPX_OPTID_FRONTEND_QUIC_SECRET_FILE:
#ifdef ENABLE_HTTP3
config->quic.upstream.secret_file = make_string_ref(config->balloc, optarg);
#endif // ENABLE_HTTP3
return 0;
case SHRPX_OPTID_RLIMIT_MEMLOCK: {
int n;
if (parse_uint(&n, opt, optarg) != 0) {
return -1;
}
if (n < 0) {
LOG(ERROR) << opt << ": specify the integer more than or equal to 0";
return -1;
}
config->rlimit_memlock = n;
return 0;
}
case SHRPX_OPTID_MAX_WORKER_PROCESSES:
return parse_uint(&config->max_worker_processes, opt, optarg);
case SHRPX_OPTID_WORKER_PROCESS_GRACE_SHUTDOWN_PERIOD:
return parse_duration(&config->worker_process_grace_shutdown_period, opt,
optarg);
case SHRPX_OPTID_FRONTEND_QUIC_INITIAL_RTT: {
#ifdef ENABLE_HTTP3
return parse_duration(&config->quic.upstream.initial_rtt, opt, optarg);
#endif // ENABLE_HTTP3
return 0;
}
case SHRPX_OPTID_CONF: case SHRPX_OPTID_CONF:
LOG(WARN) << "conf: ignored"; LOG(WARN) << "conf: ignored";
@@ -3967,6 +4352,8 @@ StringRef strproto(Proto proto) {
return StringRef::from_lit("http/1.1"); return StringRef::from_lit("http/1.1");
case Proto::HTTP2: case Proto::HTTP2:
return StringRef::from_lit("h2"); return StringRef::from_lit("h2");
case Proto::HTTP3:
return StringRef::from_lit("h3");
case Proto::MEMCACHED: case Proto::MEMCACHED:
return StringRef::from_lit("memcached"); return StringRef::from_lit("memcached");
} }
@@ -4076,7 +4463,7 @@ int configure_downstream_group(Config *config, bool http2_proxy,
if (!g.mruby_file.empty()) { if (!g.mruby_file.empty()) {
if (mruby::create_mruby_context(g.mruby_file) == nullptr) { if (mruby::create_mruby_context(g.mruby_file) == nullptr) {
LOG(config->ignore_per_pattern_mruby_error ? ERROR : FATAL) LOG(config->ignore_per_pattern_mruby_error ? ERROR : FATAL)
<< "backend: Could not compile mruby flie for pattern " << "backend: Could not compile mruby file for pattern "
<< g.pattern; << g.pattern;
if (!config->ignore_per_pattern_mruby_error) { if (!config->ignore_per_pattern_mruby_error) {
return -1; return -1;
@@ -4111,6 +4498,8 @@ int configure_downstream_group(Config *config, bool http2_proxy,
auto resolve_flags = numeric_addr_only ? AI_NUMERICHOST | AI_NUMERICSERV : 0; auto resolve_flags = numeric_addr_only ? AI_NUMERICHOST | AI_NUMERICSERV : 0;
std::array<char, util::max_hostport> hostport_buf;
for (auto &g : addr_groups) { for (auto &g : addr_groups) {
std::unordered_map<StringRef, uint32_t> wgchk; std::unordered_map<StringRef, uint32_t> wgchk;
for (auto &addr : g.addrs) { for (auto &addr : g.addrs) {
@@ -4156,7 +4545,7 @@ int configure_downstream_group(Config *config, bool http2_proxy,
util::make_http_hostport(downstreamconf.balloc, addr.host, addr.port); util::make_http_hostport(downstreamconf.balloc, addr.host, addr.port);
auto hostport = auto hostport =
util::make_hostport(downstreamconf.balloc, addr.host, addr.port); util::make_hostport(std::begin(hostport_buf), addr.host, addr.port);
if (!addr.dns) { if (!addr.dns) {
if (resolve_hostname(&addr.addr, addr.host.c_str(), addr.port, if (resolve_hostname(&addr.addr, addr.host.c_str(), addr.port,

View File

@@ -51,6 +51,9 @@
#include <nghttp2/nghttp2.h> #include <nghttp2/nghttp2.h>
#include "shrpx_router.h" #include "shrpx_router.h"
#if ENABLE_HTTP3
# include "shrpx_quic.h"
#endif // ENABLE_HTTP3
#include "template.h" #include "template.h"
#include "http2.h" #include "http2.h"
#include "network.h" #include "network.h"
@@ -360,6 +363,44 @@ constexpr auto SHRPX_OPT_TLS13_CLIENT_CIPHERS =
StringRef::from_lit("tls13-client-ciphers"); StringRef::from_lit("tls13-client-ciphers");
constexpr auto SHRPX_OPT_NO_STRIP_INCOMING_EARLY_DATA = constexpr auto SHRPX_OPT_NO_STRIP_INCOMING_EARLY_DATA =
StringRef::from_lit("no-strip-incoming-early-data"); StringRef::from_lit("no-strip-incoming-early-data");
constexpr auto SHRPX_OPT_QUIC_BPF_PROGRAM_FILE =
StringRef::from_lit("quic-bpf-program-file");
constexpr auto SHRPX_OPT_NO_QUIC_BPF = StringRef::from_lit("no-quic-bpf");
constexpr auto SHRPX_OPT_HTTP2_ALTSVC = StringRef::from_lit("http2-altsvc");
constexpr auto SHRPX_OPT_FRONTEND_HTTP3_READ_TIMEOUT =
StringRef::from_lit("frontend-http3-read-timeout");
constexpr auto SHRPX_OPT_FRONTEND_QUIC_IDLE_TIMEOUT =
StringRef::from_lit("frontend-quic-idle-timeout");
constexpr auto SHRPX_OPT_FRONTEND_QUIC_DEBUG_LOG =
StringRef::from_lit("frontend-quic-debug-log");
constexpr auto SHRPX_OPT_FRONTEND_HTTP3_WINDOW_SIZE =
StringRef::from_lit("frontend-http3-window-size");
constexpr auto SHRPX_OPT_FRONTEND_HTTP3_CONNECTION_WINDOW_SIZE =
StringRef::from_lit("frontend-http3-connection-window-size");
constexpr auto SHRPX_OPT_FRONTEND_HTTP3_MAX_WINDOW_SIZE =
StringRef::from_lit("frontend-http3-max-window-size");
constexpr auto SHRPX_OPT_FRONTEND_HTTP3_MAX_CONNECTION_WINDOW_SIZE =
StringRef::from_lit("frontend-http3-max-connection-window-size");
constexpr auto SHRPX_OPT_FRONTEND_HTTP3_MAX_CONCURRENT_STREAMS =
StringRef::from_lit("frontend-http3-max-concurrent-streams");
constexpr auto SHRPX_OPT_FRONTEND_QUIC_EARLY_DATA =
StringRef::from_lit("frontend-quic-early-data");
constexpr auto SHRPX_OPT_FRONTEND_QUIC_QLOG_DIR =
StringRef::from_lit("frontend-quic-qlog-dir");
constexpr auto SHRPX_OPT_FRONTEND_QUIC_REQUIRE_TOKEN =
StringRef::from_lit("frontend-quic-require-token");
constexpr auto SHRPX_OPT_FRONTEND_QUIC_CONGESTION_CONTROLLER =
StringRef::from_lit("frontend-quic-congestion-controller");
constexpr auto SHRPX_OPT_QUIC_SERVER_ID = StringRef::from_lit("quic-server-id");
constexpr auto SHRPX_OPT_FRONTEND_QUIC_SECRET_FILE =
StringRef::from_lit("frontend-quic-secret-file");
constexpr auto SHRPX_OPT_RLIMIT_MEMLOCK = StringRef::from_lit("rlimit-memlock");
constexpr auto SHRPX_OPT_MAX_WORKER_PROCESSES =
StringRef::from_lit("max-worker-processes");
constexpr auto SHRPX_OPT_WORKER_PROCESS_GRACE_SHUTDOWN_PERIOD =
StringRef::from_lit("worker-process-grace-shutdown-period");
constexpr auto SHRPX_OPT_FRONTEND_QUIC_INITIAL_RTT =
StringRef::from_lit("frontend-quic-initial-rtt");
constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8; constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8;
@@ -370,6 +411,7 @@ enum class Proto {
NONE, NONE,
HTTP1, HTTP1,
HTTP2, HTTP2,
HTTP3,
MEMCACHED, MEMCACHED,
}; };
@@ -419,7 +461,7 @@ enum class ForwardedNode {
}; };
struct AltSvc { struct AltSvc {
StringRef protocol_id, host, origin, service; StringRef protocol_id, host, origin, service, params;
uint16_t port; uint16_t port;
}; };
@@ -434,6 +476,8 @@ enum class UpstreamAltMode {
}; };
struct UpstreamAddr { struct UpstreamAddr {
// The unique index of this address.
size_t index;
// The frontend address (e.g., FQDN, hostname, IP address). If // The frontend address (e.g., FQDN, hostname, IP address). If
// |host_unix| is true, this is UNIX domain socket path. This must // |host_unix| is true, this is UNIX domain socket path. This must
// be NULL terminated string. // be NULL terminated string.
@@ -458,6 +502,7 @@ struct UpstreamAddr {
bool sni_fwd; bool sni_fwd;
// true if client is supposed to send PROXY protocol v1 header. // true if client is supposed to send PROXY protocol v1 header.
bool accept_proxy_protocol; bool accept_proxy_protocol;
bool quic;
int fd; int fd;
}; };
@@ -494,6 +539,8 @@ struct DownstreamAddrConfig {
// variant (e.g., "https") when forwarding request to a backend // variant (e.g., "https") when forwarding request to a backend
// connected by TLS connection. // connected by TLS connection.
bool upgrade_scheme; bool upgrade_scheme;
// true if a request should not be forwarded to a backend.
bool dnf;
}; };
// Mapping hash to idx which is an index into // Mapping hash to idx which is an index into
@@ -510,6 +557,7 @@ struct DownstreamAddrGroupConfig {
: pattern(pattern), : pattern(pattern),
affinity{SessionAffinity::NONE}, affinity{SessionAffinity::NONE},
redirect_if_not_tls(false), redirect_if_not_tls(false),
dnf{false},
timeout{} {} timeout{} {}
StringRef pattern; StringRef pattern;
@@ -523,6 +571,8 @@ struct DownstreamAddrGroupConfig {
// true if this group requires that client connection must be TLS, // true if this group requires that client connection must be TLS,
// and the request must be redirected to https URI. // and the request must be redirected to https URI.
bool redirect_if_not_tls; bool redirect_if_not_tls;
// true if a request should not be forwarded to a backend.
bool dnf;
// Timeouts for backend connection. // Timeouts for backend connection.
struct { struct {
ev_tstamp read; ev_tstamp read;
@@ -561,6 +611,22 @@ struct TLSCertificate {
std::vector<uint8_t> sct_data; std::vector<uint8_t> sct_data;
}; };
#ifdef ENABLE_HTTP3
struct QUICKeyingMaterial {
std::array<uint8_t, SHRPX_QUIC_SECRET_RESERVEDLEN> reserved;
std::array<uint8_t, SHRPX_QUIC_SECRETLEN> secret;
std::array<uint8_t, SHRPX_QUIC_SALTLEN> salt;
std::array<uint8_t, SHRPX_QUIC_CID_ENCRYPTION_KEYLEN> cid_encryption_key;
// Identifier of this keying material. Only the first 2 bits are
// used.
uint8_t id;
};
struct QUICKeyingMaterials {
std::vector<QUICKeyingMaterial> keying_materials;
};
#endif // ENABLE_HTTP3
struct HttpProxy { struct HttpProxy {
Address addr; Address addr;
// host in http proxy URI // host in http proxy URI
@@ -625,7 +691,7 @@ struct TLSConfig {
ev_tstamp idle_timeout; ev_tstamp idle_timeout;
} dyn_rec; } dyn_rec;
// OCSP realted configurations // OCSP related configurations
struct { struct {
ev_tstamp update_interval; ev_tstamp update_interval;
StringRef fetch_ocsp_response_file; StringRef fetch_ocsp_response_file;
@@ -698,6 +764,42 @@ struct TLSConfig {
bool no_postpone_early_data; bool no_postpone_early_data;
}; };
#ifdef ENABLE_HTTP3
struct QUICConfig {
struct {
struct {
ev_tstamp idle;
} timeout;
struct {
bool log;
} debug;
struct {
StringRef dir;
} qlog;
ngtcp2_cc_algo congestion_controller;
bool early_data;
bool require_token;
StringRef secret_file;
ev_tstamp initial_rtt;
} upstream;
struct {
StringRef prog_file;
bool disabled;
} bpf;
std::array<uint8_t, SHRPX_QUIC_SERVER_IDLEN> server_id;
};
struct Http3Config {
struct {
size_t max_concurrent_streams;
int32_t window_size;
int32_t connection_window_size;
int32_t max_window_size;
int32_t max_connection_window_size;
} upstream;
};
#endif // ENABLE_HTTP3
// custom error page // custom error page
struct ErrorPage { struct ErrorPage {
// not NULL-terminated // not NULL-terminated
@@ -734,6 +836,11 @@ struct HttpConfig {
bool strip_incoming; bool strip_incoming;
} early_data; } early_data;
std::vector<AltSvc> altsvcs; std::vector<AltSvc> altsvcs;
// altsvcs serialized in a wire format.
StringRef altsvc_header_value;
std::vector<AltSvc> http2_altsvcs;
// http2_altsvcs serialized in a wire format.
StringRef http2_altsvc_header_value;
std::vector<ErrorPage> error_pages; std::vector<ErrorPage> error_pages;
HeaderRefs add_request_headers; HeaderRefs add_request_headers;
HeaderRefs add_response_headers; HeaderRefs add_response_headers;
@@ -903,9 +1010,16 @@ struct ConnectionConfig {
int fastopen; int fastopen;
} listener; } listener;
#ifdef ENABLE_HTTP3
struct {
std::vector<UpstreamAddr> addrs;
} quic_listener;
#endif // ENABLE_HTTP3
struct { struct {
struct { struct {
ev_tstamp http2_read; ev_tstamp http2_read;
ev_tstamp http3_read;
ev_tstamp read; ev_tstamp read;
ev_tstamp write; ev_tstamp write;
ev_tstamp idle_read; ev_tstamp idle_read;
@@ -946,6 +1060,9 @@ struct Config {
http{}, http{},
http2{}, http2{},
tls{}, tls{},
#ifdef ENABLE_HTTP3
quic{},
#endif // ENABLE_HTTP3
logging{}, logging{},
conn{}, conn{},
api{}, api{},
@@ -954,6 +1071,7 @@ struct Config {
num_worker{0}, num_worker{0},
padding{0}, padding{0},
rlimit_nofile{0}, rlimit_nofile{0},
rlimit_memlock{0},
uid{0}, uid{0},
gid{0}, gid{0},
pid{0}, pid{0},
@@ -963,7 +1081,10 @@ struct Config {
single_process{false}, single_process{false},
single_thread{false}, single_thread{false},
ignore_per_pattern_mruby_error{false}, ignore_per_pattern_mruby_error{false},
ev_loop_flags{0} {} ev_loop_flags{0},
max_worker_processes{0},
worker_process_grace_shutdown_period{0.} {
}
~Config(); ~Config();
Config(Config &&) = delete; Config(Config &&) = delete;
@@ -979,6 +1100,10 @@ struct Config {
HttpConfig http; HttpConfig http;
Http2Config http2; Http2Config http2;
TLSConfig tls; TLSConfig tls;
#ifdef ENABLE_HTTP3
QUICConfig quic;
Http3Config http3;
#endif // ENABLE_HTTP3
LoggingConfig logging; LoggingConfig logging;
ConnectionConfig conn; ConnectionConfig conn;
APIConfig api; APIConfig api;
@@ -997,6 +1122,7 @@ struct Config {
size_t num_worker; size_t num_worker;
size_t padding; size_t padding;
size_t rlimit_nofile; size_t rlimit_nofile;
size_t rlimit_memlock;
uid_t uid; uid_t uid;
gid_t gid; gid_t gid;
pid_t pid; pid_t pid;
@@ -1011,6 +1137,8 @@ struct Config {
bool ignore_per_pattern_mruby_error; bool ignore_per_pattern_mruby_error;
// flags passed to ev_default_loop() and ev_loop_new() // flags passed to ev_default_loop() and ev_loop_new()
int ev_loop_flags; int ev_loop_flags;
size_t max_worker_processes;
ev_tstamp worker_process_grace_shutdown_period;
}; };
const Config *get_config(); const Config *get_config();
@@ -1103,13 +1231,28 @@ enum {
SHRPX_OPTID_FRONTEND_HTTP2_SETTINGS_TIMEOUT, SHRPX_OPTID_FRONTEND_HTTP2_SETTINGS_TIMEOUT,
SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS, SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS,
SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_SIZE, SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_SIZE,
SHRPX_OPTID_FRONTEND_HTTP3_CONNECTION_WINDOW_SIZE,
SHRPX_OPTID_FRONTEND_HTTP3_MAX_CONCURRENT_STREAMS,
SHRPX_OPTID_FRONTEND_HTTP3_MAX_CONNECTION_WINDOW_SIZE,
SHRPX_OPTID_FRONTEND_HTTP3_MAX_WINDOW_SIZE,
SHRPX_OPTID_FRONTEND_HTTP3_READ_TIMEOUT,
SHRPX_OPTID_FRONTEND_HTTP3_WINDOW_SIZE,
SHRPX_OPTID_FRONTEND_KEEP_ALIVE_TIMEOUT, SHRPX_OPTID_FRONTEND_KEEP_ALIVE_TIMEOUT,
SHRPX_OPTID_FRONTEND_MAX_REQUESTS, SHRPX_OPTID_FRONTEND_MAX_REQUESTS,
SHRPX_OPTID_FRONTEND_NO_TLS, SHRPX_OPTID_FRONTEND_NO_TLS,
SHRPX_OPTID_FRONTEND_QUIC_CONGESTION_CONTROLLER,
SHRPX_OPTID_FRONTEND_QUIC_DEBUG_LOG,
SHRPX_OPTID_FRONTEND_QUIC_EARLY_DATA,
SHRPX_OPTID_FRONTEND_QUIC_IDLE_TIMEOUT,
SHRPX_OPTID_FRONTEND_QUIC_INITIAL_RTT,
SHRPX_OPTID_FRONTEND_QUIC_QLOG_DIR,
SHRPX_OPTID_FRONTEND_QUIC_REQUIRE_TOKEN,
SHRPX_OPTID_FRONTEND_QUIC_SECRET_FILE,
SHRPX_OPTID_FRONTEND_READ_TIMEOUT, SHRPX_OPTID_FRONTEND_READ_TIMEOUT,
SHRPX_OPTID_FRONTEND_WRITE_TIMEOUT, SHRPX_OPTID_FRONTEND_WRITE_TIMEOUT,
SHRPX_OPTID_HEADER_FIELD_BUFFER, SHRPX_OPTID_HEADER_FIELD_BUFFER,
SHRPX_OPTID_HOST_REWRITE, SHRPX_OPTID_HOST_REWRITE,
SHRPX_OPTID_HTTP2_ALTSVC,
SHRPX_OPTID_HTTP2_BRIDGE, SHRPX_OPTID_HTTP2_BRIDGE,
SHRPX_OPTID_HTTP2_MAX_CONCURRENT_STREAMS, SHRPX_OPTID_HTTP2_MAX_CONCURRENT_STREAMS,
SHRPX_OPTID_HTTP2_NO_COOKIE_CRUMBLING, SHRPX_OPTID_HTTP2_NO_COOKIE_CRUMBLING,
@@ -1122,6 +1265,7 @@ enum {
SHRPX_OPTID_MAX_HEADER_FIELDS, SHRPX_OPTID_MAX_HEADER_FIELDS,
SHRPX_OPTID_MAX_REQUEST_HEADER_FIELDS, SHRPX_OPTID_MAX_REQUEST_HEADER_FIELDS,
SHRPX_OPTID_MAX_RESPONSE_HEADER_FIELDS, SHRPX_OPTID_MAX_RESPONSE_HEADER_FIELDS,
SHRPX_OPTID_MAX_WORKER_PROCESSES,
SHRPX_OPTID_MRUBY_FILE, SHRPX_OPTID_MRUBY_FILE,
SHRPX_OPTID_NO_ADD_X_FORWARDED_PROTO, SHRPX_OPTID_NO_ADD_X_FORWARDED_PROTO,
SHRPX_OPTID_NO_HOST_REWRITE, SHRPX_OPTID_NO_HOST_REWRITE,
@@ -1130,6 +1274,7 @@ enum {
SHRPX_OPTID_NO_KQUEUE, SHRPX_OPTID_NO_KQUEUE,
SHRPX_OPTID_NO_LOCATION_REWRITE, SHRPX_OPTID_NO_LOCATION_REWRITE,
SHRPX_OPTID_NO_OCSP, SHRPX_OPTID_NO_OCSP,
SHRPX_OPTID_NO_QUIC_BPF,
SHRPX_OPTID_NO_SERVER_PUSH, SHRPX_OPTID_NO_SERVER_PUSH,
SHRPX_OPTID_NO_SERVER_REWRITE, SHRPX_OPTID_NO_SERVER_REWRITE,
SHRPX_OPTID_NO_STRIP_INCOMING_EARLY_DATA, SHRPX_OPTID_NO_STRIP_INCOMING_EARLY_DATA,
@@ -1144,11 +1289,14 @@ enum {
SHRPX_OPTID_PRIVATE_KEY_FILE, SHRPX_OPTID_PRIVATE_KEY_FILE,
SHRPX_OPTID_PRIVATE_KEY_PASSWD_FILE, SHRPX_OPTID_PRIVATE_KEY_PASSWD_FILE,
SHRPX_OPTID_PSK_SECRETS, SHRPX_OPTID_PSK_SECRETS,
SHRPX_OPTID_QUIC_BPF_PROGRAM_FILE,
SHRPX_OPTID_QUIC_SERVER_ID,
SHRPX_OPTID_READ_BURST, SHRPX_OPTID_READ_BURST,
SHRPX_OPTID_READ_RATE, SHRPX_OPTID_READ_RATE,
SHRPX_OPTID_REDIRECT_HTTPS_PORT, SHRPX_OPTID_REDIRECT_HTTPS_PORT,
SHRPX_OPTID_REQUEST_HEADER_FIELD_BUFFER, SHRPX_OPTID_REQUEST_HEADER_FIELD_BUFFER,
SHRPX_OPTID_RESPONSE_HEADER_FIELD_BUFFER, SHRPX_OPTID_RESPONSE_HEADER_FIELD_BUFFER,
SHRPX_OPTID_RLIMIT_MEMLOCK,
SHRPX_OPTID_RLIMIT_NOFILE, SHRPX_OPTID_RLIMIT_NOFILE,
SHRPX_OPTID_SERVER_NAME, SHRPX_OPTID_SERVER_NAME,
SHRPX_OPTID_SINGLE_PROCESS, SHRPX_OPTID_SINGLE_PROCESS,
@@ -1189,6 +1337,7 @@ enum {
SHRPX_OPTID_VERIFY_CLIENT_CACERT, SHRPX_OPTID_VERIFY_CLIENT_CACERT,
SHRPX_OPTID_VERIFY_CLIENT_TOLERATE_EXPIRED, SHRPX_OPTID_VERIFY_CLIENT_TOLERATE_EXPIRED,
SHRPX_OPTID_WORKER_FRONTEND_CONNECTIONS, SHRPX_OPTID_WORKER_FRONTEND_CONNECTIONS,
SHRPX_OPTID_WORKER_PROCESS_GRACE_SHUTDOWN_PERIOD,
SHRPX_OPTID_WORKER_READ_BURST, SHRPX_OPTID_WORKER_READ_BURST,
SHRPX_OPTID_WORKER_READ_RATE, SHRPX_OPTID_WORKER_READ_RATE,
SHRPX_OPTID_WORKER_WRITE_BURST, SHRPX_OPTID_WORKER_WRITE_BURST,
@@ -1253,6 +1402,11 @@ std::unique_ptr<TicketKeys>
read_tls_ticket_key_file(const std::vector<StringRef> &files, read_tls_ticket_key_file(const std::vector<StringRef> &files,
const EVP_CIPHER *cipher, const EVP_MD *hmac); const EVP_CIPHER *cipher, const EVP_MD *hmac);
#ifdef ENABLE_HTTP3
std::shared_ptr<QUICKeyingMaterials>
read_quic_secret_file(const StringRef &path);
#endif // ENABLE_HTTP3
// Returns string representation of |proto|. // Returns string representation of |proto|.
StringRef strproto(Proto proto); StringRef strproto(Proto proto);

View File

@@ -74,7 +74,7 @@ Connection::Connection(struct ev_loop *loop, int fd, SSL *ssl,
read_timeout(read_timeout) { read_timeout(read_timeout) {
ev_io_init(&wev, writecb, fd, EV_WRITE); ev_io_init(&wev, writecb, fd, EV_WRITE);
ev_io_init(&rev, readcb, fd, EV_READ); ev_io_init(&rev, readcb, proto == Proto::HTTP3 ? 0 : fd, EV_READ);
wev.data = this; wev.data = this;
rev.data = this; rev.data = this;
@@ -128,7 +128,7 @@ void Connection::disconnect() {
tls.early_data_finish = false; tls.early_data_finish = false;
} }
if (fd != -1) { if (proto != Proto::HTTP3 && fd != -1) {
shutdown(fd, SHUT_WR); shutdown(fd, SHUT_WR);
close(fd); close(fd);
fd = -1; fd = -1;
@@ -312,10 +312,13 @@ BIO_METHOD *create_bio_method() {
void Connection::set_ssl(SSL *ssl) { void Connection::set_ssl(SSL *ssl) {
tls.ssl = ssl; tls.ssl = ssl;
if (proto != Proto::HTTP3) {
auto &tlsconf = get_config()->tls; auto &tlsconf = get_config()->tls;
auto bio = BIO_new(tlsconf.bio_method); auto bio = BIO_new(tlsconf.bio_method);
BIO_set_data(bio, this); BIO_set_data(bio, this);
SSL_set_bio(tls.ssl, bio, bio); SSL_set_bio(tls.ssl, bio, bio);
}
SSL_set_app_data(tls.ssl, this); SSL_set_app_data(tls.ssl, this);
} }
@@ -394,11 +397,14 @@ int Connection::tls_handshake() {
ERR_clear_error(); ERR_clear_error();
#if OPENSSL_1_1_1_API #if OPENSSL_1_1_1_API || defined(OPENSSL_IS_BORINGSSL)
auto &tlsconf = get_config()->tls;
#endif // OPENSSL_1_1_1_API || defined(OPENSSL_IS_BORINGSSL)
#if OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
if (!tls.server_handshake || tls.early_data_finish) { if (!tls.server_handshake || tls.early_data_finish) {
rv = SSL_do_handshake(tls.ssl); rv = SSL_do_handshake(tls.ssl);
} else { } else {
auto &tlsconf = get_config()->tls;
for (;;) { for (;;) {
size_t nread; size_t nread;
@@ -446,9 +452,9 @@ int Connection::tls_handshake() {
} }
} }
} }
#else // !OPENSSL_1_1_1_API #else // !(OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL))
rv = SSL_do_handshake(tls.ssl); rv = SSL_do_handshake(tls.ssl);
#endif // !OPENSSL_1_1_1_API #endif // !(OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL))
if (rv <= 0) { if (rv <= 0) {
auto err = SSL_get_error(tls.ssl, rv); auto err = SSL_get_error(tls.ssl, rv);
@@ -496,7 +502,12 @@ int Connection::tls_handshake() {
// Don't send handshake data if handshake was completed in OpenSSL // Don't send handshake data if handshake was completed in OpenSSL
// routine. We have to check HTTP/2 requirement if HTTP/2 was // routine. We have to check HTTP/2 requirement if HTTP/2 was
// negotiated before sending finished message to the peer. // negotiated before sending finished message to the peer.
if (rv != 1 && tls.wbuf.rleft()) { if ((rv != 1
#ifdef OPENSSL_IS_BORINGSSL
|| SSL_in_init(tls.ssl)
#endif // OPENSSL_IS_BORINGSSL
) &&
tls.wbuf.rleft()) {
// First write indicates that resumption stuff has done. // First write indicates that resumption stuff has done.
if (tls.handshake_state != TLSHandshakeState::WRITE_STARTED) { if (tls.handshake_state != TLSHandshakeState::WRITE_STARTED) {
tls.handshake_state = TLSHandshakeState::WRITE_STARTED; tls.handshake_state = TLSHandshakeState::WRITE_STARTED;
@@ -532,6 +543,40 @@ int Connection::tls_handshake() {
return SHRPX_ERR_INPROGRESS; return SHRPX_ERR_INPROGRESS;
} }
#ifdef OPENSSL_IS_BORINGSSL
if (!tlsconf.no_postpone_early_data && SSL_in_early_data(tls.ssl) &&
SSL_in_init(tls.ssl)) {
auto nread = SSL_read(tls.ssl, buf.data(), buf.size());
if (nread <= 0) {
auto err = SSL_get_error(tls.ssl, nread);
switch (err) {
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
break;
case SSL_ERROR_ZERO_RETURN:
return SHRPX_ERR_EOF;
case SSL_ERROR_SSL:
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "SSL_read: "
<< ERR_error_string(ERR_get_error(), nullptr);
}
return SHRPX_ERR_NETWORK;
default:
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "SSL_read: SSL_get_error returned " << err;
}
return SHRPX_ERR_NETWORK;
}
} else {
tls.earlybuf.append(buf.data(), nread);
}
if (SSL_in_init(tls.ssl)) {
return SHRPX_ERR_INPROGRESS;
}
}
#endif // OPENSSL_IS_BORINGSSL
// Handshake was done // Handshake was done
rv = check_http2_requirement(); rv = check_http2_requirement();
@@ -568,6 +613,36 @@ int Connection::write_tls_pending_handshake() {
tls.wbuf.drain(nwrite); tls.wbuf.drain(nwrite);
} }
#ifdef OPENSSL_IS_BORINGSSL
if (!SSL_in_init(tls.ssl)) {
// This will send a session ticket.
auto nwrite = SSL_write(tls.ssl, "", 0);
if (nwrite < 0) {
auto err = SSL_get_error(tls.ssl, nwrite);
switch (err) {
case SSL_ERROR_WANT_READ:
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Close connection due to TLS renegotiation";
}
return SHRPX_ERR_NETWORK;
case SSL_ERROR_WANT_WRITE:
break;
case SSL_ERROR_SSL:
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "SSL_write: "
<< ERR_error_string(ERR_get_error(), nullptr);
}
return SHRPX_ERR_NETWORK;
default:
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "SSL_write: SSL_get_error returned " << err;
}
return SHRPX_ERR_NETWORK;
}
}
}
#endif // OPENSSL_IS_BORINGSSL
// We have to start read watcher, since later stage of code expects // We have to start read watcher, since later stage of code expects
// this. // this.
rlimit.startw(); rlimit.startw();
@@ -678,7 +753,7 @@ ssize_t Connection::write_tls(const void *data, size_t len) {
// length) on SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE. // length) on SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE.
// get_write_limit() may return smaller length than previously // get_write_limit() may return smaller length than previously
// passed to SSL_write, which violates OpenSSL assumption. To avoid // passed to SSL_write, which violates OpenSSL assumption. To avoid
// this, we keep last legnth passed to SSL_write to // this, we keep last length passed to SSL_write to
// tls.last_writelen if SSL_write indicated I/O blocking. // tls.last_writelen if SSL_write indicated I/O blocking.
if (tls.last_writelen == 0) { if (tls.last_writelen == 0) {
len = std::min(len, wlimit.avail()); len = std::min(len, wlimit.avail());
@@ -695,7 +770,7 @@ ssize_t Connection::write_tls(const void *data, size_t len) {
ERR_clear_error(); ERR_clear_error();
#if OPENSSL_1_1_1_API #if OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
int rv; int rv;
if (SSL_is_init_finished(tls.ssl)) { if (SSL_is_init_finished(tls.ssl)) {
rv = SSL_write(tls.ssl, data, len); rv = SSL_write(tls.ssl, data, len);
@@ -707,9 +782,9 @@ ssize_t Connection::write_tls(const void *data, size_t len) {
rv = nwrite; rv = nwrite;
} }
} }
#else // !OPENSSL_1_1_1_API #else // !(OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL))
auto rv = SSL_write(tls.ssl, data, len); auto rv = SSL_write(tls.ssl, data, len);
#endif // !OPENSSL_1_1_1_API #endif // !(OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL))
if (rv <= 0) { if (rv <= 0) {
auto err = SSL_get_error(tls.ssl, rv); auto err = SSL_get_error(tls.ssl, rv);
@@ -756,7 +831,7 @@ ssize_t Connection::read_tls(void *data, size_t len) {
// length) on SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE. // length) on SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE.
// rlimit_.avail() or rlimit_.avail() may return different length // rlimit_.avail() or rlimit_.avail() may return different length
// than the length previously passed to SSL_read, which violates // than the length previously passed to SSL_read, which violates
// OpenSSL assumption. To avoid this, we keep last legnth passed // OpenSSL assumption. To avoid this, we keep last length passed
// to SSL_read to tls_last_readlen_ if SSL_read indicated I/O // to SSL_read to tls_last_readlen_ if SSL_read indicated I/O
// blocking. // blocking.
if (tls.last_readlen == 0) { if (tls.last_readlen == 0) {
@@ -769,7 +844,7 @@ ssize_t Connection::read_tls(void *data, size_t len) {
tls.last_readlen = 0; tls.last_readlen = 0;
} }
#if OPENSSL_1_1_1_API #if OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
if (!tls.early_data_finish) { if (!tls.early_data_finish) {
// TLSv1.3 handshake is still going on. // TLSv1.3 handshake is still going on.
size_t nread; size_t nread;
@@ -808,7 +883,7 @@ ssize_t Connection::read_tls(void *data, size_t len) {
} }
return nread; return nread;
} }
#endif // OPENSSL_1_1_1_API #endif // OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
auto rv = SSL_read(tls.ssl, data, len); auto rv = SSL_read(tls.ssl, data, len);

View File

@@ -45,6 +45,7 @@
#include "shrpx_memcached_dispatcher.h" #include "shrpx_memcached_dispatcher.h"
#include "shrpx_signal.h" #include "shrpx_signal.h"
#include "shrpx_log.h" #include "shrpx_log.h"
#include "xsi_strerror.h"
#include "util.h" #include "util.h"
#include "template.h" #include "template.h"
@@ -112,7 +113,11 @@ void serial_event_async_cb(struct ev_loop *loop, ev_async *w, int revent) {
} // namespace } // namespace
ConnectionHandler::ConnectionHandler(struct ev_loop *loop, std::mt19937 &gen) ConnectionHandler::ConnectionHandler(struct ev_loop *loop, std::mt19937 &gen)
: gen_(gen), :
#ifdef ENABLE_HTTP3
quic_ipc_fd_(-1),
#endif // ENABLE_HTTP3
gen_(gen),
single_worker_(nullptr), single_worker_(nullptr),
loop_(loop), loop_(loop),
#ifdef HAVE_NEVERBLEED #ifdef HAVE_NEVERBLEED
@@ -156,6 +161,19 @@ ConnectionHandler::~ConnectionHandler() {
ev_timer_stop(loop_, &ocsp_timer_); ev_timer_stop(loop_, &ocsp_timer_);
ev_timer_stop(loop_, &disable_acceptor_timer_); ev_timer_stop(loop_, &disable_acceptor_timer_);
#ifdef ENABLE_HTTP3
for (auto ssl_ctx : quic_all_ssl_ctx_) {
if (ssl_ctx == nullptr) {
continue;
}
auto tls_ctx_data =
static_cast<tls::TLSContextData *>(SSL_CTX_get_app_data(ssl_ctx));
delete tls_ctx_data;
SSL_CTX_free(ssl_ctx);
}
#endif // ENABLE_HTTP3
for (auto ssl_ctx : all_ssl_ctx_) { for (auto ssl_ctx : all_ssl_ctx_) {
auto tls_ctx_data = auto tls_ctx_data =
static_cast<tls::TLSContextData *>(SSL_CTX_get_app_data(ssl_ctx)); static_cast<tls::TLSContextData *>(SSL_CTX_get_app_data(ssl_ctx));
@@ -179,24 +197,24 @@ void ConnectionHandler::set_ticket_keys_to_worker(
} }
void ConnectionHandler::worker_reopen_log_files() { void ConnectionHandler::worker_reopen_log_files() {
for (auto &worker : workers_) {
WorkerEvent wev{}; WorkerEvent wev{};
wev.type = WorkerEventType::REOPEN_LOG; wev.type = WorkerEventType::REOPEN_LOG;
for (auto &worker : workers_) { worker->send(std::move(wev));
worker->send(wev);
} }
} }
void ConnectionHandler::worker_replace_downstream( void ConnectionHandler::worker_replace_downstream(
std::shared_ptr<DownstreamConfig> downstreamconf) { std::shared_ptr<DownstreamConfig> downstreamconf) {
for (auto &worker : workers_) {
WorkerEvent wev{}; WorkerEvent wev{};
wev.type = WorkerEventType::REPLACE_DOWNSTREAM; wev.type = WorkerEventType::REPLACE_DOWNSTREAM;
wev.downstreamconf = std::move(downstreamconf); wev.downstreamconf = downstreamconf;
for (auto &worker : workers_) { worker->send(std::move(wev));
worker->send(wev);
} }
} }
@@ -209,6 +227,18 @@ int ConnectionHandler::create_single_worker() {
nb_ nb_
#endif // HAVE_NEVERBLEED #endif // HAVE_NEVERBLEED
); );
#ifdef ENABLE_HTTP3
quic_cert_tree_ = tls::create_cert_lookup_tree();
auto quic_sv_ssl_ctx = tls::setup_quic_server_ssl_context(
quic_all_ssl_ctx_, quic_indexed_ssl_ctx_, quic_cert_tree_.get()
# ifdef HAVE_NEVERBLEED
,
nb_
# endif // HAVE_NEVERBLEED
);
#endif // ENABLE_HTTP3
auto cl_ssl_ctx = tls::setup_downstream_client_ssl_context( auto cl_ssl_ctx = tls::setup_downstream_client_ssl_context(
#ifdef HAVE_NEVERBLEED #ifdef HAVE_NEVERBLEED
nb_ nb_
@@ -217,6 +247,9 @@ int ConnectionHandler::create_single_worker() {
if (cl_ssl_ctx) { if (cl_ssl_ctx) {
all_ssl_ctx_.push_back(cl_ssl_ctx); all_ssl_ctx_.push_back(cl_ssl_ctx);
#ifdef ENABLE_HTTP3
quic_all_ssl_ctx_.push_back(nullptr);
#endif // ENABLE_HTTP3
} }
auto config = get_config(); auto config = get_config();
@@ -233,11 +266,30 @@ int ConnectionHandler::create_single_worker() {
tlsconf.cacert, memcachedconf.cert_file, tlsconf.cacert, memcachedconf.cert_file,
memcachedconf.private_key_file, nullptr); memcachedconf.private_key_file, nullptr);
all_ssl_ctx_.push_back(session_cache_ssl_ctx); all_ssl_ctx_.push_back(session_cache_ssl_ctx);
#ifdef ENABLE_HTTP3
quic_all_ssl_ctx_.push_back(nullptr);
#endif // ENABLE_HTTP3
} }
} }
#if defined(ENABLE_HTTP3) && defined(HAVE_LIBBPF)
quic_bpf_refs_.resize(config->conn.quic_listener.addrs.size());
#endif // ENABLE_HTTP3 && HAVE_LIBBPF
#ifdef ENABLE_HTTP3
assert(cid_prefixes_.size() == 1);
const auto &cid_prefix = cid_prefixes_[0];
#endif // ENABLE_HTTP3
single_worker_ = std::make_unique<Worker>( single_worker_ = std::make_unique<Worker>(
loop_, sv_ssl_ctx, cl_ssl_ctx, session_cache_ssl_ctx, cert_tree_.get(), loop_, sv_ssl_ctx, cl_ssl_ctx, session_cache_ssl_ctx, cert_tree_.get(),
#ifdef ENABLE_HTTP3
quic_sv_ssl_ctx, quic_cert_tree_.get(), cid_prefix.data(),
cid_prefix.size(),
# ifdef HAVE_LIBBPF
/* index = */ 0,
# endif // HAVE_LIBBPF
#endif // ENABLE_HTTP3
ticket_keys_, this, config->conn.downstream); ticket_keys_, this, config->conn.downstream);
#ifdef HAVE_MRUBY #ifdef HAVE_MRUBY
if (single_worker_->create_mruby_context() != 0) { if (single_worker_->create_mruby_context() != 0) {
@@ -245,6 +297,12 @@ int ConnectionHandler::create_single_worker() {
} }
#endif // HAVE_MRUBY #endif // HAVE_MRUBY
#ifdef ENABLE_HTTP3
if (single_worker_->setup_quic_server_socket() != 0) {
return -1;
}
#endif // ENABLE_HTTP3
return 0; return 0;
} }
@@ -260,6 +318,18 @@ int ConnectionHandler::create_worker_thread(size_t num) {
nb_ nb_
# endif // HAVE_NEVERBLEED # endif // HAVE_NEVERBLEED
); );
# ifdef ENABLE_HTTP3
quic_cert_tree_ = tls::create_cert_lookup_tree();
auto quic_sv_ssl_ctx = tls::setup_quic_server_ssl_context(
quic_all_ssl_ctx_, quic_indexed_ssl_ctx_, quic_cert_tree_.get()
# ifdef HAVE_NEVERBLEED
,
nb_
# endif // HAVE_NEVERBLEED
);
# endif // ENABLE_HTTP3
auto cl_ssl_ctx = tls::setup_downstream_client_ssl_context( auto cl_ssl_ctx = tls::setup_downstream_client_ssl_context(
# ifdef HAVE_NEVERBLEED # ifdef HAVE_NEVERBLEED
nb_ nb_
@@ -268,12 +338,19 @@ int ConnectionHandler::create_worker_thread(size_t num) {
if (cl_ssl_ctx) { if (cl_ssl_ctx) {
all_ssl_ctx_.push_back(cl_ssl_ctx); all_ssl_ctx_.push_back(cl_ssl_ctx);
# ifdef ENABLE_HTTP3
quic_all_ssl_ctx_.push_back(nullptr);
# endif // ENABLE_HTTP3
} }
auto config = get_config(); auto config = get_config();
auto &tlsconf = config->tls; auto &tlsconf = config->tls;
auto &apiconf = config->api; auto &apiconf = config->api;
# if defined(ENABLE_HTTP3) && defined(HAVE_LIBBPF)
quic_bpf_refs_.resize(config->conn.quic_listener.addrs.size());
# endif // ENABLE_HTTP3 && HAVE_LIBBPF
// We have dedicated worker for API request processing. // We have dedicated worker for API request processing.
if (apiconf.enabled) { if (apiconf.enabled) {
++num; ++num;
@@ -291,14 +368,32 @@ int ConnectionHandler::create_worker_thread(size_t num) {
tlsconf.cacert, memcachedconf.cert_file, tlsconf.cacert, memcachedconf.cert_file,
memcachedconf.private_key_file, nullptr); memcachedconf.private_key_file, nullptr);
all_ssl_ctx_.push_back(session_cache_ssl_ctx); all_ssl_ctx_.push_back(session_cache_ssl_ctx);
# ifdef ENABLE_HTTP3
quic_all_ssl_ctx_.push_back(nullptr);
# endif // ENABLE_HTTP3
} }
} }
# ifdef ENABLE_HTTP3
assert(cid_prefixes_.size() == num);
# endif // ENABLE_HTTP3
for (size_t i = 0; i < num; ++i) { for (size_t i = 0; i < num; ++i) {
auto loop = ev_loop_new(config->ev_loop_flags); auto loop = ev_loop_new(config->ev_loop_flags);
# ifdef ENABLE_HTTP3
const auto &cid_prefix = cid_prefixes_[i];
# endif // ENABLE_HTTP3
auto worker = std::make_unique<Worker>( auto worker = std::make_unique<Worker>(
loop, sv_ssl_ctx, cl_ssl_ctx, session_cache_ssl_ctx, cert_tree_.get(), loop, sv_ssl_ctx, cl_ssl_ctx, session_cache_ssl_ctx, cert_tree_.get(),
# ifdef ENABLE_HTTP3
quic_sv_ssl_ctx, quic_cert_tree_.get(), cid_prefix.data(),
cid_prefix.size(),
# ifdef HAVE_LIBBPF
i,
# endif // HAVE_LIBBPF
# endif // ENABLE_HTTP3
ticket_keys_, this, config->conn.downstream); ticket_keys_, this, config->conn.downstream);
# ifdef HAVE_MRUBY # ifdef HAVE_MRUBY
if (worker->create_mruby_context() != 0) { if (worker->create_mruby_context() != 0) {
@@ -306,6 +401,13 @@ int ConnectionHandler::create_worker_thread(size_t num) {
} }
# endif // HAVE_MRUBY # endif // HAVE_MRUBY
# ifdef ENABLE_HTTP3
if ((!apiconf.enabled || i != 0) &&
worker->setup_quic_server_socket() != 0) {
return -1;
}
# endif // ENABLE_HTTP3
workers_.push_back(std::move(worker)); workers_.push_back(std::move(worker));
worker_loops_.push_back(loop); worker_loops_.push_back(loop);
@@ -345,15 +447,15 @@ void ConnectionHandler::graceful_shutdown_worker() {
return; return;
} }
WorkerEvent wev{};
wev.type = WorkerEventType::GRACEFUL_SHUTDOWN;
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
LLOG(INFO, this) << "Sending graceful shutdown signal to worker"; LLOG(INFO, this) << "Sending graceful shutdown signal to worker";
} }
for (auto &worker : workers_) { for (auto &worker : workers_) {
worker->send(wev); WorkerEvent wev{};
wev.type = WorkerEventType::GRACEFUL_SHUTDOWN;
worker->send(std::move(wev));
} }
#ifndef NOTHREADS #ifndef NOTHREADS
@@ -436,7 +538,7 @@ int ConnectionHandler::handle_connection(int fd, sockaddr *addr, int addrlen,
wev.client_addrlen = addrlen; wev.client_addrlen = addrlen;
wev.faddr = faddr; wev.faddr = faddr;
worker->send(wev); worker->send(std::move(wev));
return 0; return 0;
} }
@@ -602,6 +704,9 @@ void ConnectionHandler::handle_ocsp_complete() {
ev_child_stop(loop_, &ocsp_.chldev); ev_child_stop(loop_, &ocsp_.chldev);
assert(ocsp_.next < all_ssl_ctx_.size()); assert(ocsp_.next < all_ssl_ctx_.size());
#ifdef ENABLE_HTTP3
assert(all_ssl_ctx_.size() == quic_all_ssl_ctx_.size());
#endif // ENABLE_HTTP3
auto ssl_ctx = all_ssl_ctx_[ocsp_.next]; auto ssl_ctx = all_ssl_ctx_[ocsp_.next];
auto tls_ctx_data = auto tls_ctx_data =
@@ -629,6 +734,32 @@ void ConnectionHandler::handle_ocsp_complete() {
if (tlsconf.ocsp.no_verify || if (tlsconf.ocsp.no_verify ||
tls::verify_ocsp_response(ssl_ctx, ocsp_.resp.data(), tls::verify_ocsp_response(ssl_ctx, ocsp_.resp.data(),
ocsp_.resp.size()) == 0) { ocsp_.resp.size()) == 0) {
#ifdef ENABLE_HTTP3
// We have list of SSL_CTX with the same certificate in
// quic_all_ssl_ctx_ as well. Some SSL_CTXs are missing there in
// that case we get nullptr.
auto quic_ssl_ctx = quic_all_ssl_ctx_[ocsp_.next];
if (quic_ssl_ctx) {
# ifndef OPENSSL_IS_BORINGSSL
auto quic_tls_ctx_data = static_cast<tls::TLSContextData *>(
SSL_CTX_get_app_data(quic_ssl_ctx));
# ifdef HAVE_ATOMIC_STD_SHARED_PTR
std::atomic_store_explicit(
&quic_tls_ctx_data->ocsp_data,
std::make_shared<std::vector<uint8_t>>(ocsp_.resp),
std::memory_order_release);
# else // !HAVE_ATOMIC_STD_SHARED_PTR
std::lock_guard<std::mutex> g(quic_tls_ctx_data->mu);
quic_tls_ctx_data->ocsp_data =
std::make_shared<std::vector<uint8_t>>(ocsp_.resp);
# endif // !HAVE_ATOMIC_STD_SHARED_PTR
# else // OPENSSL_IS_BORINGSSL
SSL_CTX_set_ocsp_response(quic_ssl_ctx, ocsp_.resp.data(),
ocsp_.resp.size());
# endif // OPENSSL_IS_BORINGSSL
}
#endif // ENABLE_HTTP3
#ifndef OPENSSL_IS_BORINGSSL #ifndef OPENSSL_IS_BORINGSSL
# ifdef HAVE_ATOMIC_STD_SHARED_PTR # ifdef HAVE_ATOMIC_STD_SHARED_PTR
std::atomic_store_explicit( std::atomic_store_explicit(
@@ -809,6 +940,9 @@ SSL_CTX *ConnectionHandler::create_tls_ticket_key_memcached_ssl_ctx() {
nullptr); nullptr);
all_ssl_ctx_.push_back(ssl_ctx); all_ssl_ctx_.push_back(ssl_ctx);
#ifdef ENABLE_HTTP3
quic_all_ssl_ctx_.push_back(nullptr);
#endif // ENABLE_HTTP3
return ssl_ctx; return ssl_ctx;
} }
@@ -871,8 +1005,329 @@ ConnectionHandler::get_indexed_ssl_ctx(size_t idx) const {
return indexed_ssl_ctx_[idx]; return indexed_ssl_ctx_[idx];
} }
#ifdef ENABLE_HTTP3
const std::vector<SSL_CTX *> &
ConnectionHandler::get_quic_indexed_ssl_ctx(size_t idx) const {
return quic_indexed_ssl_ctx_[idx];
}
#endif // ENABLE_HTTP3
void ConnectionHandler::set_enable_acceptor_on_ocsp_completion(bool f) { void ConnectionHandler::set_enable_acceptor_on_ocsp_completion(bool f) {
enable_acceptor_on_ocsp_completion_ = f; enable_acceptor_on_ocsp_completion_ = f;
} }
#ifdef ENABLE_HTTP3
int ConnectionHandler::forward_quic_packet(
const UpstreamAddr *faddr, const Address &remote_addr,
const Address &local_addr, const ngtcp2_pkt_info &pi,
const uint8_t *cid_prefix, const uint8_t *data, size_t datalen) {
assert(!get_config()->single_thread);
for (auto &worker : workers_) {
if (!std::equal(cid_prefix, cid_prefix + SHRPX_QUIC_CID_PREFIXLEN,
worker->get_cid_prefix())) {
continue;
}
WorkerEvent wev{};
wev.type = WorkerEventType::QUIC_PKT_FORWARD;
wev.quic_pkt = std::make_unique<QUICPacket>(faddr->index, remote_addr,
local_addr, pi, data, datalen);
worker->send(std::move(wev));
return 0;
}
return -1;
}
void ConnectionHandler::set_quic_keying_materials(
std::shared_ptr<QUICKeyingMaterials> qkms) {
quic_keying_materials_ = std::move(qkms);
}
const std::shared_ptr<QUICKeyingMaterials> &
ConnectionHandler::get_quic_keying_materials() const {
return quic_keying_materials_;
}
void ConnectionHandler::set_cid_prefixes(
const std::vector<std::array<uint8_t, SHRPX_QUIC_CID_PREFIXLEN>>
&cid_prefixes) {
cid_prefixes_ = cid_prefixes;
}
QUICLingeringWorkerProcess *
ConnectionHandler::match_quic_lingering_worker_process_cid_prefix(
const uint8_t *dcid, size_t dcidlen) {
assert(dcidlen >= SHRPX_QUIC_CID_PREFIXLEN);
for (auto &lwps : quic_lingering_worker_processes_) {
for (auto &cid_prefix : lwps.cid_prefixes) {
if (std::equal(std::begin(cid_prefix), std::end(cid_prefix), dcid)) {
return &lwps;
}
}
}
return nullptr;
}
# ifdef HAVE_LIBBPF
std::vector<BPFRef> &ConnectionHandler::get_quic_bpf_refs() {
return quic_bpf_refs_;
}
void ConnectionHandler::unload_bpf_objects() {
std::array<char, STRERROR_BUFSIZE> errbuf;
LOG(NOTICE) << "Unloading BPF objects";
for (auto &ref : quic_bpf_refs_) {
if (ref.obj == nullptr) {
continue;
}
if (bpf_object__unload(ref.obj) != 0) {
LOG(WARN) << "Failed to unload bpf object: "
<< xsi_strerror(errno, errbuf.data(), errbuf.size());
continue;
}
ref.obj = nullptr;
}
}
# endif // HAVE_LIBBPF
void ConnectionHandler::set_quic_ipc_fd(int fd) { quic_ipc_fd_ = fd; }
void ConnectionHandler::set_quic_lingering_worker_processes(
const std::vector<QUICLingeringWorkerProcess> &quic_lwps) {
quic_lingering_worker_processes_ = quic_lwps;
}
int ConnectionHandler::forward_quic_packet_to_lingering_worker_process(
QUICLingeringWorkerProcess *quic_lwp, const Address &remote_addr,
const Address &local_addr, const ngtcp2_pkt_info &pi, const uint8_t *data,
size_t datalen) {
std::array<uint8_t, 512> header;
assert(header.size() >= 1 + 1 + 1 + 1 + sizeof(sockaddr_storage) * 2);
assert(remote_addr.len > 0);
assert(local_addr.len > 0);
auto p = header.data();
*p++ = static_cast<uint8_t>(QUICIPCType::DGRAM_FORWARD);
*p++ = static_cast<uint8_t>(remote_addr.len - 1);
p = std::copy_n(reinterpret_cast<const uint8_t *>(&remote_addr.su),
remote_addr.len, p);
*p++ = static_cast<uint8_t>(local_addr.len - 1);
p = std::copy_n(reinterpret_cast<const uint8_t *>(&local_addr.su),
local_addr.len, p);
*p++ = pi.ecn;
iovec msg_iov[] = {
{
.iov_base = header.data(),
.iov_len = static_cast<size_t>(p - header.data()),
},
{
.iov_base = const_cast<uint8_t *>(data),
.iov_len = datalen,
},
};
msghdr msg{};
msg.msg_iov = msg_iov;
msg.msg_iovlen = array_size(msg_iov);
ssize_t nwrite;
while ((nwrite = sendmsg(quic_lwp->quic_ipc_fd, &msg, 0)) == -1 &&
errno == EINTR)
;
if (nwrite == -1) {
std::array<char, STRERROR_BUFSIZE> errbuf;
auto error = errno;
LOG(ERROR) << "Failed to send QUIC IPC message: "
<< xsi_strerror(error, errbuf.data(), errbuf.size());
return -1;
}
return 0;
}
int ConnectionHandler::quic_ipc_read() {
std::array<uint8_t, 65536> buf;
ssize_t nread;
while ((nread = recv(quic_ipc_fd_, buf.data(), buf.size(), 0)) == -1 &&
errno == EINTR)
;
if (nread == -1) {
std::array<char, STRERROR_BUFSIZE> errbuf;
auto error = errno;
LOG(ERROR) << "Failed to read data from QUIC IPC channel: "
<< xsi_strerror(error, errbuf.data(), errbuf.size());
return -1;
}
if (nread == 0) {
return 0;
}
size_t len = 1 + 1 + 1 + 1;
// Wire format:
// TYPE(1) REMOTE_ADDRLEN(1) REMOTE_ADDR(N) LOCAL_ADDRLEN(1) LOCAL_ADDR(N)
// ECN(1) DGRAM_PAYLOAD(N)
//
// When encoding, REMOTE_ADDRLEN and LOCAL_ADDRLEN are decremented
// by 1.
if (static_cast<size_t>(nread) < len) {
return 0;
}
auto p = buf.data();
if (*p != static_cast<uint8_t>(QUICIPCType::DGRAM_FORWARD)) {
LOG(ERROR) << "Unknown QUICIPCType: " << static_cast<uint32_t>(*p);
return -1;
}
++p;
auto pkt = std::make_unique<QUICPacket>();
auto remote_addrlen = static_cast<size_t>(*p++) + 1;
if (remote_addrlen > sizeof(sockaddr_storage)) {
LOG(ERROR) << "The length of remote address is too large: "
<< remote_addrlen;
return -1;
}
len += remote_addrlen;
if (static_cast<size_t>(nread) < len) {
LOG(ERROR) << "Insufficient QUIC IPC message length";
return -1;
}
pkt->remote_addr.len = remote_addrlen;
memcpy(&pkt->remote_addr.su, p, remote_addrlen);
p += remote_addrlen;
auto local_addrlen = static_cast<size_t>(*p++) + 1;
if (local_addrlen > sizeof(sockaddr_storage)) {
LOG(ERROR) << "The length of local address is too large: " << local_addrlen;
return -1;
}
len += local_addrlen;
if (static_cast<size_t>(nread) < len) {
LOG(ERROR) << "Insufficient QUIC IPC message length";
return -1;
}
pkt->local_addr.len = local_addrlen;
memcpy(&pkt->local_addr.su, p, local_addrlen);
p += local_addrlen;
pkt->pi.ecn = *p++;
auto datalen = nread - (p - buf.data());
pkt->data.assign(p, p + datalen);
// At the moment, UpstreamAddr index is unknown.
pkt->upstream_addr_index = static_cast<size_t>(-1);
uint32_t version;
const uint8_t *dcid;
size_t dcidlen;
const uint8_t *scid;
size_t scidlen;
auto rv =
ngtcp2_pkt_decode_version_cid(&version, &dcid, &dcidlen, &scid, &scidlen,
p, datalen, SHRPX_QUIC_SCIDLEN);
if (rv < 0) {
LOG(ERROR) << "ngtcp2_pkt_decode_version_cid: " << ngtcp2_strerror(rv);
return -1;
}
if (dcidlen != SHRPX_QUIC_SCIDLEN) {
LOG(ERROR) << "DCID length is invalid";
return -1;
}
if (single_worker_) {
auto faddr = single_worker_->find_quic_upstream_addr(pkt->local_addr);
if (faddr == nullptr) {
LOG(ERROR) << "No suitable upstream address found";
return 0;
}
auto quic_conn_handler = single_worker_->get_quic_connection_handler();
// Ignore return value
quic_conn_handler->handle_packet(faddr, pkt->remote_addr, pkt->local_addr,
pkt->pi, pkt->data.data(),
pkt->data.size());
return 0;
}
auto &qkm = quic_keying_materials_->keying_materials.front();
std::array<uint8_t, SHRPX_QUIC_DECRYPTED_DCIDLEN> decrypted_dcid;
if (decrypt_quic_connection_id(decrypted_dcid.data(),
dcid + SHRPX_QUIC_CID_PREFIX_OFFSET,
qkm.cid_encryption_key.data()) != 0) {
return -1;
}
for (auto &worker : workers_) {
if (!std::equal(std::begin(decrypted_dcid),
std::begin(decrypted_dcid) + SHRPX_QUIC_CID_PREFIXLEN,
worker->get_cid_prefix())) {
continue;
}
WorkerEvent wev{
.type = WorkerEventType::QUIC_PKT_FORWARD,
.quic_pkt = std::move(pkt),
};
worker->send(std::move(wev));
return 0;
}
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "No worker to match CID prefix";
}
return 0;
}
#endif // ENABLE_HTTP3
} // namespace shrpx } // namespace shrpx

View File

@@ -40,6 +40,10 @@
# include <future> # include <future>
#endif // NOTHREADS #endif // NOTHREADS
#ifdef HAVE_LIBBPF
# include <bpf/libbpf.h>
#endif // HAVE_LIBBPF
#include <openssl/ssl.h> #include <openssl/ssl.h>
#include <ev.h> #include <ev.h>
@@ -99,6 +103,35 @@ struct SerialEvent {
std::shared_ptr<DownstreamConfig> downstreamconf; std::shared_ptr<DownstreamConfig> downstreamconf;
}; };
#ifdef ENABLE_HTTP3
# ifdef HAVE_LIBBPF
struct BPFRef {
bpf_object *obj;
int reuseport_array;
int cid_prefix_map;
};
# endif // HAVE_LIBBPF
// QUIC IPC message type.
enum class QUICIPCType {
NONE,
// Send forwarded QUIC UDP datagram and its metadata.
DGRAM_FORWARD,
};
// WorkerProcesses which are in graceful shutdown period.
struct QUICLingeringWorkerProcess {
QUICLingeringWorkerProcess(
std::vector<std::array<uint8_t, SHRPX_QUIC_CID_PREFIXLEN>> cid_prefixes,
int quic_ipc_fd)
: cid_prefixes{std::move(cid_prefixes)}, quic_ipc_fd{quic_ipc_fd} {}
std::vector<std::array<uint8_t, SHRPX_QUIC_CID_PREFIXLEN>> cid_prefixes;
// Socket to send QUIC IPC message to this worker process.
int quic_ipc_fd;
};
#endif // ENABLE_HTTP3
class ConnectionHandler { class ConnectionHandler {
public: public:
ConnectionHandler(struct ev_loop *loop, std::mt19937 &gen); ConnectionHandler(struct ev_loop *loop, std::mt19937 &gen);
@@ -130,7 +163,7 @@ public:
// Cancels ocsp update process // Cancels ocsp update process
void cancel_ocsp_update(); void cancel_ocsp_update();
// Starts ocsp update for certficate |cert_file|. // Starts ocsp update for certificate |cert_file|.
int start_ocsp_update(const char *cert_file); int start_ocsp_update(const char *cert_file);
// Reads incoming data from ocsp update process // Reads incoming data from ocsp update process
void read_ocsp_chunk(); void read_ocsp_chunk();
@@ -159,6 +192,45 @@ public:
SSL_CTX *get_ssl_ctx(size_t idx) const; SSL_CTX *get_ssl_ctx(size_t idx) const;
const std::vector<SSL_CTX *> &get_indexed_ssl_ctx(size_t idx) const; const std::vector<SSL_CTX *> &get_indexed_ssl_ctx(size_t idx) const;
#ifdef ENABLE_HTTP3
const std::vector<SSL_CTX *> &get_quic_indexed_ssl_ctx(size_t idx) const;
int forward_quic_packet(const UpstreamAddr *faddr, const Address &remote_addr,
const Address &local_addr, const ngtcp2_pkt_info &pi,
const uint8_t *cid_prefix, const uint8_t *data,
size_t datalen);
void set_quic_keying_materials(std::shared_ptr<QUICKeyingMaterials> qkms);
const std::shared_ptr<QUICKeyingMaterials> &get_quic_keying_materials() const;
void set_cid_prefixes(
const std::vector<std::array<uint8_t, SHRPX_QUIC_CID_PREFIXLEN>>
&cid_prefixes);
void set_quic_lingering_worker_processes(
const std::vector<QUICLingeringWorkerProcess> &quic_lwps);
// Return matching QUICLingeringWorkerProcess which has a CID prefix
// such that |dcid| starts with it. If no such
// QUICLingeringWorkerProcess, it returns nullptr.
QUICLingeringWorkerProcess *
match_quic_lingering_worker_process_cid_prefix(const uint8_t *dcid,
size_t dcidlen);
int forward_quic_packet_to_lingering_worker_process(
QUICLingeringWorkerProcess *quic_lwp, const Address &remote_addr,
const Address &local_addr, const ngtcp2_pkt_info &pi, const uint8_t *data,
size_t datalen);
void set_quic_ipc_fd(int fd);
int quic_ipc_read();
# ifdef HAVE_LIBBPF
std::vector<BPFRef> &get_quic_bpf_refs();
void unload_bpf_objects();
# endif // HAVE_LIBBPF
#endif // ENABLE_HTTP3
#ifdef HAVE_NEVERBLEED #ifdef HAVE_NEVERBLEED
void set_neverbleed(neverbleed_t *nb); void set_neverbleed(neverbleed_t *nb);
@@ -187,6 +259,19 @@ private:
// selection among them are performed by hostname presented by SNI, // selection among them are performed by hostname presented by SNI,
// and signature algorithm presented by client. // and signature algorithm presented by client.
std::vector<std::vector<SSL_CTX *>> indexed_ssl_ctx_; std::vector<std::vector<SSL_CTX *>> indexed_ssl_ctx_;
#ifdef ENABLE_HTTP3
std::vector<std::array<uint8_t, SHRPX_QUIC_CID_PREFIXLEN>> cid_prefixes_;
std::vector<std::array<uint8_t, SHRPX_QUIC_CID_PREFIXLEN>>
lingering_cid_prefixes_;
int quic_ipc_fd_;
std::vector<QUICLingeringWorkerProcess> quic_lingering_worker_processes_;
# ifdef HAVE_LIBBPF
std::vector<BPFRef> quic_bpf_refs_;
# endif // HAVE_LIBBPF
std::shared_ptr<QUICKeyingMaterials> quic_keying_materials_;
std::vector<SSL_CTX *> quic_all_ssl_ctx_;
std::vector<std::vector<SSL_CTX *>> quic_indexed_ssl_ctx_;
#endif // ENABLE_HTTP3
OCSPUpdateContext ocsp_; OCSPUpdateContext ocsp_;
std::mt19937 &gen_; std::mt19937 &gen_;
// ev_loop for each worker // ev_loop for each worker
@@ -203,6 +288,9 @@ private:
// Otherwise, nullptr and workers_ has instances of Worker instead. // Otherwise, nullptr and workers_ has instances of Worker instead.
std::unique_ptr<Worker> single_worker_; std::unique_ptr<Worker> single_worker_;
std::unique_ptr<tls::CertLookupTree> cert_tree_; std::unique_ptr<tls::CertLookupTree> cert_tree_;
#ifdef ENABLE_HTTP3
std::unique_ptr<tls::CertLookupTree> quic_cert_tree_;
#endif // ENABLE_HTTP3
std::unique_ptr<MemcachedDispatcher> tls_ticket_key_memcached_dispatcher_; std::unique_ptr<MemcachedDispatcher> tls_ticket_key_memcached_dispatcher_;
// Current TLS session ticket keys. Note that TLS connection does // Current TLS session ticket keys. Note that TLS connection does
// not refer to this field directly. They use TicketKeys object in // not refer to this field directly. They use TicketKeys object in

View File

@@ -88,7 +88,7 @@ public:
int on_write(int fd); int on_write(int fd);
int on_timeout(); int on_timeout();
// Calls this function when DNS query finished. // Calls this function when DNS query finished.
void on_result(int staus, hostent *hostent); void on_result(int status, hostent *hostent);
void reset_timeout(); void reset_timeout();
void start_rev(int fd); void start_rev(int fd);

View File

@@ -113,7 +113,7 @@ void downstream_wtimeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
// upstream could be nullptr for unittests // upstream could be nullptr for unittests
Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool, Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool,
int32_t stream_id) int64_t stream_id)
: dlnext(nullptr), : dlnext(nullptr),
dlprev(nullptr), dlprev(nullptr),
response_sent_body_length(0), response_sent_body_length(0),
@@ -145,7 +145,8 @@ Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool,
accesslog_written_(false), accesslog_written_(false),
new_affinity_cookie_(false), new_affinity_cookie_(false),
blocked_request_data_eof_(false), blocked_request_data_eof_(false),
expect_100_continue_(false) { expect_100_continue_(false),
stop_reading_(false) {
auto &timeoutconf = get_config()->http2.timeout; auto &timeoutconf = get_config()->http2.timeout;
@@ -164,6 +165,9 @@ Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool,
downstream_wtimer_.data = this; downstream_wtimer_.data = this;
rcbufs_.reserve(32); rcbufs_.reserve(32);
#ifdef ENABLE_HTTP3
rcbufs3_.reserve(32);
#endif // ENABLE_HTTP3
} }
Downstream::~Downstream() { Downstream::~Downstream() {
@@ -203,6 +207,12 @@ Downstream::~Downstream() {
// explicitly. // explicitly.
dconn_.reset(); dconn_.reset();
#ifdef ENABLE_HTTP3
for (auto rcbuf : rcbufs3_) {
nghttp3_rcbuf_decref(rcbuf);
}
#endif // ENABLE_HTTP3
for (auto rcbuf : rcbufs_) { for (auto rcbuf : rcbufs_) {
nghttp2_rcbuf_decref(rcbuf); nghttp2_rcbuf_decref(rcbuf);
} }
@@ -605,9 +615,9 @@ void Downstream::reset_upstream(Upstream *upstream) {
Upstream *Downstream::get_upstream() const { return upstream_; } Upstream *Downstream::get_upstream() const { return upstream_; }
void Downstream::set_stream_id(int32_t stream_id) { stream_id_ = stream_id; } void Downstream::set_stream_id(int64_t stream_id) { stream_id_ = stream_id; }
int32_t Downstream::get_stream_id() const { return stream_id_; } int64_t Downstream::get_stream_id() const { return stream_id_; }
void Downstream::set_request_state(DownstreamState state) { void Downstream::set_request_state(DownstreamState state) {
request_state_ = state; request_state_ = state;
@@ -886,7 +896,8 @@ bool Downstream::get_non_final_response() const {
} }
bool Downstream::supports_non_final_response() const { bool Downstream::supports_non_final_response() const {
return req_.http_major == 2 || (req_.http_major == 1 && req_.http_minor == 1); return req_.http_major == 3 || req_.http_major == 2 ||
(req_.http_major == 1 && req_.http_minor == 1);
} }
bool Downstream::get_upgraded() const { return upgraded_; } bool Downstream::get_upgraded() const { return upgraded_; }
@@ -904,11 +915,11 @@ StringRef Downstream::get_http2_settings() const {
return http2_settings->value; return http2_settings->value;
} }
void Downstream::set_downstream_stream_id(int32_t stream_id) { void Downstream::set_downstream_stream_id(int64_t stream_id) {
downstream_stream_id_ = stream_id; downstream_stream_id_ = stream_id;
} }
int32_t Downstream::get_downstream_stream_id() const { int64_t Downstream::get_downstream_stream_id() const {
return downstream_stream_id_; return downstream_stream_id_;
} }
@@ -937,7 +948,8 @@ bool Downstream::expect_response_trailer() const {
// In HTTP/2, if final response HEADERS does not bear END_STREAM it // In HTTP/2, if final response HEADERS does not bear END_STREAM it
// is possible trailer fields might come, regardless of request // is possible trailer fields might come, regardless of request
// method or status code. // method or status code.
return !resp_.headers_only && resp_.http_major == 2; return !resp_.headers_only &&
(resp_.http_major == 3 || resp_.http_major == 2);
} }
namespace { namespace {
@@ -1115,11 +1127,11 @@ DefaultMemchunks Downstream::pop_response_buf() {
return std::move(response_buf_); return std::move(response_buf_);
} }
void Downstream::set_assoc_stream_id(int32_t stream_id) { void Downstream::set_assoc_stream_id(int64_t stream_id) {
assoc_stream_id_ = stream_id; assoc_stream_id_ = stream_id;
} }
int32_t Downstream::get_assoc_stream_id() const { return assoc_stream_id_; } int64_t Downstream::get_assoc_stream_id() const { return assoc_stream_id_; }
BlockAllocator &Downstream::get_block_allocator() { return balloc_; } BlockAllocator &Downstream::get_block_allocator() { return balloc_; }
@@ -1128,6 +1140,13 @@ void Downstream::add_rcbuf(nghttp2_rcbuf *rcbuf) {
rcbufs_.push_back(rcbuf); rcbufs_.push_back(rcbuf);
} }
#ifdef ENABLE_HTTP3
void Downstream::add_rcbuf(nghttp3_rcbuf *rcbuf) {
nghttp3_rcbuf_incref(rcbuf);
rcbufs3_.push_back(rcbuf);
}
#endif // ENABLE_HTTP3
void Downstream::set_downstream_addr_group( void Downstream::set_downstream_addr_group(
const std::shared_ptr<DownstreamAddrGroup> &group) { const std::shared_ptr<DownstreamAddrGroup> &group) {
group_ = group; group_ = group;
@@ -1169,4 +1188,8 @@ bool Downstream::get_expect_100_continue() const {
return expect_100_continue_; return expect_100_continue_;
} }
bool Downstream::get_stop_reading() const { return stop_reading_; }
void Downstream::set_stop_reading(bool f) { stop_reading_ = f; }
} // namespace shrpx } // namespace shrpx

View File

@@ -38,6 +38,10 @@
#include <nghttp2/nghttp2.h> #include <nghttp2/nghttp2.h>
#ifdef ENABLE_HTTP3
# include <nghttp3/nghttp3.h>
#endif // ENABLE_HTTP3
#include "llhttp.h" #include "llhttp.h"
#include "shrpx_io_control.h" #include "shrpx_io_control.h"
@@ -319,20 +323,20 @@ enum class DispatchState {
class Downstream { class Downstream {
public: public:
Downstream(Upstream *upstream, MemchunkPool *mcpool, int32_t stream_id); Downstream(Upstream *upstream, MemchunkPool *mcpool, int64_t stream_id);
~Downstream(); ~Downstream();
void reset_upstream(Upstream *upstream); void reset_upstream(Upstream *upstream);
Upstream *get_upstream() const; Upstream *get_upstream() const;
void set_stream_id(int32_t stream_id); void set_stream_id(int64_t stream_id);
int32_t get_stream_id() const; int64_t get_stream_id() const;
void set_assoc_stream_id(int32_t stream_id); void set_assoc_stream_id(int64_t stream_id);
int32_t get_assoc_stream_id() const; int64_t get_assoc_stream_id() const;
void pause_read(IOCtrlReason reason); void pause_read(IOCtrlReason reason);
int resume_read(IOCtrlReason reason, size_t consumed); int resume_read(IOCtrlReason reason, size_t consumed);
void force_resume_read(); void force_resume_read();
// Set stream ID for downstream HTTP2 connection. // Set stream ID for downstream HTTP2 connection.
void set_downstream_stream_id(int32_t stream_id); void set_downstream_stream_id(int64_t stream_id);
int32_t get_downstream_stream_id() const; int64_t get_downstream_stream_id() const;
int attach_downstream_connection(std::unique_ptr<DownstreamConnection> dconn); int attach_downstream_connection(std::unique_ptr<DownstreamConnection> dconn);
void detach_downstream_connection(); void detach_downstream_connection();
@@ -488,6 +492,9 @@ public:
BlockAllocator &get_block_allocator(); BlockAllocator &get_block_allocator();
void add_rcbuf(nghttp2_rcbuf *rcbuf); void add_rcbuf(nghttp2_rcbuf *rcbuf);
#ifdef ENABLE_HTTP3
void add_rcbuf(nghttp3_rcbuf *rcbuf);
#endif // ENABLE_HTTP3
void void
set_downstream_addr_group(const std::shared_ptr<DownstreamAddrGroup> &group); set_downstream_addr_group(const std::shared_ptr<DownstreamAddrGroup> &group);
@@ -513,6 +520,9 @@ public:
bool get_expect_100_continue() const; bool get_expect_100_continue() const;
bool get_stop_reading() const;
void set_stop_reading(bool f);
enum { enum {
EVENT_ERROR = 0x1, EVENT_ERROR = 0x1,
EVENT_TIMEOUT = 0x2, EVENT_TIMEOUT = 0x2,
@@ -527,6 +537,9 @@ private:
BlockAllocator balloc_; BlockAllocator balloc_;
std::vector<nghttp2_rcbuf *> rcbufs_; std::vector<nghttp2_rcbuf *> rcbufs_;
#ifdef ENABLE_HTTP3
std::vector<nghttp3_rcbuf *> rcbufs3_;
#endif // ENABLE_HTTP3
Request req_; Request req_;
Response resp_; Response resp_;
@@ -566,12 +579,12 @@ private:
// How many times we tried in backend connection // How many times we tried in backend connection
size_t num_retry_; size_t num_retry_;
// The stream ID in frontend connection // The stream ID in frontend connection
int32_t stream_id_; int64_t stream_id_;
// The associated stream ID in frontend connection if this is pushed // The associated stream ID in frontend connection if this is pushed
// stream. // stream.
int32_t assoc_stream_id_; int64_t assoc_stream_id_;
// stream ID in backend connection // stream ID in backend connection
int32_t downstream_stream_id_; int64_t downstream_stream_id_;
// RST_STREAM error_code from downstream HTTP2 connection // RST_STREAM error_code from downstream HTTP2 connection
uint32_t response_rst_stream_error_code_; uint32_t response_rst_stream_error_code_;
// An affinity cookie value. // An affinity cookie value.
@@ -606,6 +619,7 @@ private:
bool blocked_request_data_eof_; bool blocked_request_data_eof_;
// true if request contains "expect: 100-continue" header field. // true if request contains "expect: 100-continue" header field.
bool expect_100_continue_; bool expect_100_continue_;
bool stop_reading_;
}; };
} // namespace shrpx } // namespace shrpx

View File

@@ -58,7 +58,7 @@ public:
virtual int on_write() = 0; virtual int on_write() = 0;
virtual int on_timeout() { return 0; } virtual int on_timeout() { return 0; }
virtual void on_upstream_change(Upstream *uptream) = 0; virtual void on_upstream_change(Upstream *upstream) = 0;
// true if this object is poolable. // true if this object is poolable.
virtual bool poolable() const = 0; virtual bool poolable() const = 0;

Some files were not shown because too many files have changed in this diff Show More