Compare commits

..

281 Commits

Author SHA1 Message Date
Tatsuhiro Tsujikawa
66eba46c8e fixup! nghttpx: Send nghttpx-0rtt-uniq header if request is replayable 2017-11-26 10:28:21 +09:00
Tatsuhiro Tsujikawa
abcdca91ba nghttpx: Postpone early data processing if CH replay detected 2017-11-26 10:28:21 +09:00
Tatsuhiro Tsujikawa
5e59577e93 nghttpx: Send nghttpx-0rtt-uniq header if request is replayable
The incoming nghttpx-0rtt-uniq header fields from inbound client are
stripped by default.  Use --no-strip-incoming-nghttpx-0rtt-uniq in
order not to strip them.
2017-11-26 10:28:21 +09:00
Tatsuhiro Tsujikawa
8c6612d338 nghttpx: Implement TLSv1.3 0-RTT anti-replay with ClientHello cache 2017-11-26 10:28:21 +09:00
Tatsuhiro Tsujikawa
b71d9ea58e Remove SSL_ERROR_WANT_WRITE handling 2017-11-26 10:28:21 +09:00
Tatsuhiro Tsujikawa
aca99d42f1 Honor SSL_read semantics 2017-11-26 10:28:21 +09:00
Tatsuhiro Tsujikawa
90a9a804d0 nghttpx: Add TLSv1.3 0-RTT early data support 2017-11-26 10:28:21 +09:00
Tatsuhiro Tsujikawa
97f1735cf5 Bump up version number to 1.29.0 2017-11-25 23:04:03 +09:00
Tatsuhiro Tsujikawa
939ad5ddbe Update manual pages 2017-11-25 22:19:11 +09:00
Tatsuhiro Tsujikawa
24d92b979d Add deprecation warning when spdylay support is enabled 2017-11-25 22:16:23 +09:00
Tatsuhiro Tsujikawa
4c92ff1843 Bump up version number to 1.28.0, LT revision to 29:0:15 2017-11-25 22:10:02 +09:00
Tatsuhiro Tsujikawa
280db5c6ba Update neverbleed 2017-11-25 21:27:14 +09:00
Tatsuhiro Tsujikawa
7fbcb2d005 Merge pull request #1074 from nghttp2/fix-doc
Fix doc
2017-11-24 22:59:04 +09:00
Tatsuhiro Tsujikawa
53aeb2c3d7 Fix doc 2017-11-24 22:26:29 +09:00
Tatsuhiro Tsujikawa
ff200bfcf3 clang-format-5.0 2017-11-23 14:19:12 +09:00
Tatsuhiro Tsujikawa
fee3151fd2 Switch to clang-format-5.0 2017-11-23 14:16:42 +09:00
Tatsuhiro Tsujikawa
99a85159ae Update manual pages 2017-11-21 23:27:02 +09:00
Tatsuhiro Tsujikawa
2a981a3f56 Merge pull request #1066 from nghttp2/nghttpx-add-affinity-cookie-secure
nghttpx: Add affinity-cookie-secure parameter to backend option
2017-11-21 23:24:40 +09:00
Tatsuhiro Tsujikawa
0028275d7b nghttpx: Add affinity-cookie-secure parameter to backend option 2017-11-21 22:29:22 +09:00
Tatsuhiro Tsujikawa
ee8bfddfc9 Merge pull request #1063 from nghttp2/error_callback2
Error callback2
2017-11-21 21:25:53 +09:00
Tatsuhiro Tsujikawa
194acb1f2c src: Use nghttp2_error_callback2 2017-11-19 16:51:52 +09:00
Tatsuhiro Tsujikawa
43a2a70ae7 Add nghttp2_error_callback2
nghttp2_error_callback2 is an extended version of the existing
nghttp2_error_callback by adding error code parameter.  This
deprecates nghttp2_error_callback.
2017-11-19 16:51:52 +09:00
Tatsuhiro Tsujikawa
73344ae9aa nghttpx: Use plain hex string format for client serial 2017-11-17 00:04:23 +09:00
Tatsuhiro Tsujikawa
c479f6122f Merge pull request #1060 from nghttp2/nghttpx-add-client-serial
Nghttpx add client serial
2017-11-16 23:26:35 +09:00
Tatsuhiro Tsujikawa
eca0a3025b nghttpx: Add $tls_client_serial log variable 2017-11-16 22:53:54 +09:00
Tatsuhiro Tsujikawa
4720c5cb3d nghttpx: Make client serial available in mruby script 2017-11-16 22:53:54 +09:00
Tatsuhiro Tsujikawa
cd55ab28ab nghttpx: Add function to get serial number from certificate 2017-11-16 22:53:54 +09:00
Tatsuhiro Tsujikawa
d402cfdf16 Merge pull request #1057 from nghttp2/nghttpx-add-tls-client-issuer-name
Add tls_client_issuer_name log variable and expose it to mruby
2017-11-16 01:16:11 +09:00
Tatsuhiro Tsujikawa
22502182d0 Add tls_client_issuer_name log variable and expose it to mruby 2017-11-15 23:41:47 +09:00
Tatsuhiro Tsujikawa
05e1fd5e77 Update manual pages 2017-11-04 17:53:51 +09:00
Tatsuhiro Tsujikawa
943d7923f9 Add Session Affinity section to nghttpx howto 2017-11-04 17:49:09 +09:00
Tatsuhiro Tsujikawa
568ecbfb28 doc: Add missing port 2017-11-04 17:48:46 +09:00
Tatsuhiro Tsujikawa
f5ddd7f43b nghttpx: Make initial_addr_idx_ unsigned 2017-11-04 17:30:56 +09:00
Tatsuhiro Tsujikawa
88abbce7e7 nghttpx: Fix compile error with gcc 2017-11-04 17:30:27 +09:00
Tatsuhiro Tsujikawa
16e9036568 nghttpx: Fix affinity retry 2017-11-04 17:13:45 +09:00
Tatsuhiro Tsujikawa
fa7945c627 nghttpx: Refactor 2017-11-04 15:55:25 +09:00
Tatsuhiro Tsujikawa
daca43f0dd nghttpx: Fix stalled backend connection on retry 2017-11-04 15:46:08 +09:00
Tatsuhiro Tsujikawa
16bc11e670 nghttpx: Remove duplicated util::make_socket_nodelay 2017-11-04 13:00:17 +09:00
Tatsuhiro Tsujikawa
6f7e94cdba Merge pull request #1047 from PiotrSikora/go_vet
integration: Fix issues reported by the `go vet` tool.
2017-11-03 23:02:37 +09:00
Piotr Sikora
61efa15a14 integration: Fix issues reported by the go vet tool.
Signed-off-by: Piotr Sikora <piotrsikora@google.com>
2017-11-02 21:13:25 -07:00
Tatsuhiro Tsujikawa
8c0ea56bb8 Merge pull request #1036 from nghttp2/nghttpx-affinity-cookie
nghttpx: Cookie based session affinity
2017-11-01 22:45:38 +09:00
Tatsuhiro Tsujikawa
549053710b nghttpx: Refactor 2017-11-01 22:33:49 +09:00
Tatsuhiro Tsujikawa
6010d39325 integration: Add tests 2017-11-01 22:18:03 +09:00
Tatsuhiro Tsujikawa
be5c39a1cf src: Add tests 2017-11-01 22:18:03 +09:00
Tatsuhiro Tsujikawa
b8fda6808b nghttpx: Cookie based session affinity 2017-11-01 22:18:03 +09:00
Tatsuhiro Tsujikawa
e29b9c1261 Merge pull request #1045 from nghttp2/nghttpx-sha1-fingerprint
Nghttpx sha1 fingerprint
2017-10-31 23:07:39 +09:00
Tatsuhiro Tsujikawa
539e27812b nghttpx: Add tls_client_fingerprint_sha1 to mruby and accesslog
Also tls_client_fingerprint is renamed to
tls_client_fingerprint_sha256.
2017-10-31 21:41:40 +09:00
Tatsuhiro Tsujikawa
7008afd40e nghttpx: Refactor get_x509_fingerprint to accept hash function 2017-10-31 21:28:16 +09:00
Tatsuhiro Tsujikawa
77a41756db Merge pull request #1041 from nghttp2/fix-examples-client-server
examples: Make client and server work with libevent-2.1.8
2017-10-30 17:30:20 +09:00
Tatsuhiro Tsujikawa
b15045d60e Merge pull request #1040 from nghttp2/nghttpx-mruby-add-more-tls-vars
nghttpx: Add more TLS related attributes to mruby Env object
2017-10-29 23:14:16 +09:00
Tatsuhiro Tsujikawa
03084f7517 examples: Make client and server work with libevent-2.1.8 2017-10-29 23:09:23 +09:00
Tatsuhiro Tsujikawa
60baca27e4 nghttpx: Add more TLS related attributes to mruby Env object
The added attributes are:

* tls_cipher
* tls_protocol
* tls_session_id
* tls_session_reused
* alpn
2017-10-29 22:42:30 +09:00
Tatsuhiro Tsujikawa
86990db236 Merge pull request #1038 from nghttp2/nghttpx-add-more-logging-vars
nghttpx: Add client fingerprint and subject name to accesslog
2017-10-29 22:21:10 +09:00
Tatsuhiro Tsujikawa
cb376bcd80 nghttpx: Add client fingerprint and subject name to accesslog 2017-10-29 21:47:00 +09:00
Tatsuhiro Tsujikawa
f2b8edd1e2 nghttpx: Fix memory leak 2017-10-29 21:46:12 +09:00
Tatsuhiro Tsujikawa
c4f8afcfde nghttpx: Get TLS info only when it is necessary when writing accesslog 2017-10-29 21:22:33 +09:00
Tatsuhiro Tsujikawa
1a1a216d5a Merge pull request #1037 from nghttp2/nghttpx-mruby-tls-client-vars
nghttpx: Add client fingerprint and subject name to mruby env
2017-10-29 21:13:20 +09:00
Tatsuhiro Tsujikawa
9f80a82c1a nghttpx: Add client fingerprint and subject name to mruby env 2017-10-29 19:54:42 +09:00
Tatsuhiro Tsujikawa
c573c80bd3 nghttpx: Pass a pointer to SSL instead of TLSSessionInfo to LogSpec 2017-10-29 19:47:39 +09:00
Tatsuhiro Tsujikawa
3cd6817e21 Fix typos 2017-10-29 16:54:21 +09:00
Tatsuhiro Tsujikawa
d4a69658a1 Add another warning about mruby 2017-10-29 16:18:20 +09:00
Tatsuhiro Tsujikawa
8e06fe4971 Fix typo 2017-10-29 16:14:30 +09:00
Tatsuhiro Tsujikawa
aaeeec8f1c Fix typos 2017-10-28 22:25:42 +09:00
Tatsuhiro Tsujikawa
66d5e24606 Bump up version number to 1.28.0-DEV 2017-10-25 21:20:16 +09:00
Tatsuhiro Tsujikawa
e8907a073f Update manual pages 2017-10-24 23:10:37 +09:00
Tatsuhiro Tsujikawa
9656ac0254 Fix sphinx warnings 2017-10-24 23:10:23 +09:00
Tatsuhiro Tsujikawa
75624617ce Bump up version number to 1.27.0 2017-10-24 22:51:17 +09:00
Tatsuhiro Tsujikawa
1a8f6578b3 Update AUTHORS 2017-10-24 22:49:51 +09:00
Tatsuhiro Tsujikawa
4f0548b018 travis: Compile mruby and neverbleed in make distcheck
gcc is upgraded to gcc-7.
2017-10-24 21:41:10 +09:00
Tatsuhiro Tsujikawa
5119e82b93 src: Fix memory leak in unit test 2017-10-24 21:40:30 +09:00
Tatsuhiro Tsujikawa
3be5856c82 nghttpx: Fix unused function warnings 2017-10-24 21:40:30 +09:00
Tatsuhiro Tsujikawa
a319143901 nghttpx: Fix bug that header fields are missing in HTTP/1.0 response 2017-10-22 01:11:32 +09:00
Tatsuhiro Tsujikawa
17c88d60c7 Compile with g++ 7.2.1 2017-10-19 23:04:12 +09:00
Tatsuhiro Tsujikawa
7601511fdf Merge pull request #1029 from lazyhamster/master
Fixed accidental compiler flags concatenation for MSVC.
2017-10-19 21:21:46 +09:00
Tatsuhiro Tsujikawa
f507b5eee4 nghttpx: Use an existing h2 backend connection as much as possible
h2load measurement reveals that this strategy is 3 times faster than
the previous implementations.
2017-10-19 21:15:08 +09:00
LazyHamster
93821165be Fixed accidental compiler flags concatenation for MSVC. 2017-10-18 15:25:17 +03:00
Tatsuhiro Tsujikawa
aaa0b858e4 Amend some macro comments 2017-10-14 11:50:16 +09:00
Tatsuhiro Tsujikawa
5fa1938691 clang-format 2017-10-14 11:45:41 +09:00
Tatsuhiro Tsujikawa
56ee3d4820 Merge pull request #1027 from dermojo/mingw
Support for Windows / MinGW
2017-10-14 11:44:57 +09:00
Daniel Evers
c2d9a1ed6f Support for Windows / MinGW 2017-10-12 18:15:12 +02:00
Tatsuhiro Tsujikawa
fcf9ab2798 Merge pull request #1023 from nckx/fix-fuzz-typo
fuzz: Fix typo (‘-fsanitize=adres’) in README.rst
2017-10-12 21:00:18 +09:00
Tobias Geerinckx-Rice
35e445bd04 fuzz: Fix typo (‘-fsanitize=adres’) in README.rst 2017-10-10 22:14:25 +02:00
Tatsuhiro Tsujikawa
88ce3c31b7 Merge pull request #1020 from FireBurn/master
Reduce libxml2 version requirement to 2.6.26
2017-10-07 21:17:33 +09:00
Mike Lothian
16320a0f81 Reduce libxml2 version requirement to 2.6.26
It's currently set at 2.7.7 but reducing this down to 2.6.26 allows
nghttp2 to be built on RHEL5
2017-10-04 14:25:12 +01:00
Tatsuhiro Tsujikawa
8c72fb3539 Merge pull request #1018 from nghttp2/h2load-verbose-h2-headers
h2load: Print out h2 header fields with --verbose option
2017-10-01 12:49:13 +09:00
Tatsuhiro Tsujikawa
8ffe389daa h2load: Print out h2 header fields with --verbose option 2017-09-22 18:12:20 +09:00
Tatsuhiro Tsujikawa
189a4516a1 Merge pull request #1016 from nghttp2/nghttpx-non-final-response
nghttpx: Send non-final response to HTTP/1.1 or HTTP/2 client only
2017-09-21 22:28:51 +09:00
Tatsuhiro Tsujikawa
2576855ded nghttpx: Send non-final response to HTTP/1.1 or HTTP/2 client only 2017-09-21 21:42:56 +09:00
Tatsuhiro Tsujikawa
7d4d48a35e Add timing-based load-testing section to h2load howto 2017-09-21 17:57:32 +09:00
Tatsuhiro Tsujikawa
cc6f759190 src: Add static to constexpr char[] 2017-09-20 23:54:10 +09:00
Tatsuhiro Tsujikawa
c23fc86a23 Bump up version number to 1.27.0-DEV 2017-09-20 22:52:43 +09:00
Tatsuhiro Tsujikawa
d2324bdda1 Update bash_completion 2017-09-20 22:42:14 +09:00
Tatsuhiro Tsujikawa
6f0ae9d49a Update manual pages 2017-09-20 22:41:56 +09:00
Tatsuhiro Tsujikawa
0389af5724 Bump up version number to 1.26.0 2017-09-20 22:29:38 +09:00
Tatsuhiro Tsujikawa
1766e25f45 Update AUTHORS 2017-09-20 22:28:43 +09:00
Tatsuhiro Tsujikawa
323001238a clang-format 2017-09-20 22:08:22 +09:00
Tatsuhiro Tsujikawa
91f062f873 src: Fix compile error 2017-09-20 22:08:08 +09:00
Tatsuhiro Tsujikawa
650a0cfbff Merge pull request #1013 from marcbachmann/patch-1
Fix some typos in the nghttpx how-to doc
2017-09-13 23:59:07 +09:00
Marc Bachmann
e6b8b3d1d3 docs: Fix some typos in the nghttpx how-to 2017-09-11 22:10:03 +02:00
Tatsuhiro Tsujikawa
a170023f23 nghttpx: Verify OCSP response using trusted CA certificates 2017-09-01 21:35:38 +09:00
Tatsuhiro Tsujikawa
4be4c0cddc Revert "nghttpx: Verify OCSP response using trusted CA certificates"
This reverts commit 59c78d5809.
2017-08-30 22:27:02 +09:00
Tatsuhiro Tsujikawa
0de9d374df Merge pull request #1002 from GitaiQAQ/master
Just fix unreachable links and remove ndk...
2017-08-26 22:39:24 +09:00
Tatsuhiro Tsujikawa
0df199198a Merge pull request #1001 from rlei/master
Fix OCSP related error when building with BoringSSL
2017-08-26 22:00:56 +09:00
Gitai
7646e376e0 Fix unreachable 2017-08-26 12:54:03 +08:00
Rick Lei
5996798a34 Fix OCSP related error when building with BoringSSL
BoringSSL has no "openssl/ocsp.h" nor most OCSP related APIs used in
shrpx_tls.cc. This commit add ifdefs to disable related code to allow
building nghttp2 with BoringSSL (again).

It's possible to use !defined(OPENSSL_IS_BORINGSSL), but since BoringSSL
defines OPENSSL_NO_OCSP which is more specific, I chose to go with the
latter one.
2017-08-24 11:56:46 -04:00
Tatsuhiro Tsujikawa
6fec532012 Merge pull request #998 from nghttp2/h2load-fix-timing-script-stall
Fix bug that timing script stalls with -m1
2017-08-24 21:17:43 +09:00
Tatsuhiro Tsujikawa
15713e0b7c h2load: Ignore -n for timing-based mode instead of requiring -n=0 2017-08-23 20:35:01 +09:00
Tatsuhiro Tsujikawa
a6a561af47 Fix bug that timing script stalls with -m1 2017-08-23 20:10:23 +09:00
Tatsuhiro Tsujikawa
09c468a4b4 Merge branch 'sohamm17-master' 2017-08-23 19:22:44 +09:00
Tatsuhiro Tsujikawa
bcda1c2409 Fix assertion failure 2017-08-23 19:22:23 +09:00
Tatsuhiro Tsujikawa
afcd8d9ab1 clang-format 2017-08-23 19:19:00 +09:00
Tatsuhiro Tsujikawa
c9b1c91944 Fix compile error 2017-08-23 19:18:27 +09:00
Tatsuhiro Tsujikawa
5d9434eb09 Merge branch 'master' of https://github.com/sohamm17/nghttp2 into sohamm17-master 2017-08-23 19:16:40 +09:00
Tatsuhiro Tsujikawa
1a44b5d52a Merge pull request #984 from nghttp2/h2load-reservoir-sampling
h2load: Reservoir sampling
2017-08-23 19:00:28 +09:00
Tatsuhiro Tsujikawa
6635ca5e26 Merge pull request #988 from dvetutnev/refactoring_include_directories
Refactoring include directories
2017-08-23 18:59:28 +09:00
Tatsuhiro Tsujikawa
9c6c78833b Bump up version number to 1.26.0-DEV 2017-08-18 23:19:36 +09:00
Tatsuhiro Tsujikawa
9a9ab0813c Update manual pages 2017-08-18 23:00:58 +09:00
Tatsuhiro Tsujikawa
0ccaaa48ce Bump up version number to 1.25.0, LT revision to 28:0:14 2017-08-18 22:38:10 +09:00
Tatsuhiro Tsujikawa
3f2fe98dd1 Update AUTHORS 2017-08-18 22:11:11 +09:00
Tatsuhiro Tsujikawa
0d91e9c255 Update doc 2017-08-18 22:06:43 +09:00
Dmitriy Vetutnev
af926fbe1f Refactoring include directories for build as CMake subdirectory (add_subdirectory(nghttp2)) 2017-08-16 21:28:12 +03:00
Tatsuhiro Tsujikawa
83039ae2d4 h2load: Reservoir sampling 2017-08-14 20:25:02 +09:00
Tatsuhiro Tsujikawa
4c53da6961 Merge pull request #983 from addaleax/static-rcbuf
lib: add nghttp2_rcbuf_is_static()
2017-08-13 09:10:41 +09:00
Anna Henningsen
eb306f463e lib: add nghttp2_rcbuf_is_static()
Add a `nghttp2_rcbuf_is_static()` method to tell whether a rcbuf
is statically allocated.

This can be useful for language bindings that wish to avoid
creating duplicate strings for these buffers; concretely, I am
planning to use this in the Node HTTP/2 module that is being
introduced.
2017-08-12 17:48:14 +02:00
Tatsuhiro Tsujikawa
788835c5fd Merge pull request #980 from nghttp2/fix-forwarded-for-with-proxyprotocol
Fix bug that forwarded for is not affected by proxy protocol
2017-08-09 23:34:43 +09:00
Tatsuhiro Tsujikawa
4d76606fa2 Fix bug that forwarded for is not affected by proxy protocol 2017-08-09 22:44:14 +09:00
Soham Sinha
1baf7d34b3 Duration watcher and warmup watcher is initialised in Worker constructor. Statistic calculation is removed from duration watcher call_back, it's done in free_client. 2017-08-08 17:26:37 -04:00
Soham Sinha
c78159469a Added a function to free a client from Worker's list of client, if the client is destroyed 2017-08-07 18:58:12 -04:00
Soham Sinha
b72ca0289c formatting issue 2017-08-04 14:20:00 -04:00
Soham Sinha
46f670f8a2 concurrent connections are created in timing-based mode. Some safety asserts added. 2017-08-03 16:15:14 -04:00
Soham Sinha
4b44362b9f minor style changes 2017-08-01 20:22:20 -04:00
Soham Sinha
d068a29798 removed unnecessary code 2017-08-01 19:51:47 -04:00
Soham Sinha
0836a51408 Handling requests starting in warm-up phase and ending in MAIN_DURATION 2017-08-01 18:29:00 -04:00
Soham Sinha
566cee8fe7 MAIN_DURATION is initiliazed in Worker constructor, MAIN_DURATION check is removed from two functions because those functions are needed in warm-up phase as well. 2017-08-01 17:45:52 -04:00
Soham Sinha
e85698e131 MAIN_DURATION is initiliazed in Worker constructor, MAIN_DURATION check is removed from two functions because those functions are needed in warm-up phase as well. 2017-08-01 17:45:18 -04:00
Soham Sinha
5f3c541c4c enabled --duration option. 2017-07-28 17:31:13 -04:00
Soham Sinha
3c43e00d8a Timing (#1)
* Adding timing-sensitive load test option in h2load.

* more checks added for parameters

* A worker thread can control its clients' warmup and main duration.

* Changed warmup to an enum variable.

* removed unnecessary call to ev_timer_stop

* assertion is done before starting main measurement phase

* phase variable is implemented only inside the Worker class

* enum to enum class

* else indentation corrected

* check added for timing-based test when duration CB is called explicitly

* New argument is introduced for timing-based benchmarking.

* styling corrections

* duration watcher initialization is pushed back into warmup timeout

* Warmup and Duration timer is moved to Worker instead of clients. Now both timers and phase belongs to the Workers.

* some client functions are modified to return if it's not main_duration phase. client is not destructed but sessions are terminated

* outputs are adjusted for thread.

* Needed to check if a session exist before terminating

* formatting

* more formatting

* formatting
2017-07-28 17:08:20 -04:00
Tatsuhiro Tsujikawa
92d686d356 Merge branch 'mruby-1.3.0' 2017-07-28 00:51:48 +09:00
Tatsuhiro Tsujikawa
0f69e9c825 Fix typo 2017-07-28 00:51:34 +09:00
Tatsuhiro Tsujikawa
217d979458 Update mruby to 1.3.0
Fix compile error with mruby 1.3.0
2017-07-28 00:23:25 +09:00
Tatsuhiro Tsujikawa
cc289972fc Merge pull request #958 from sebdeckers/patch-1
fix: typo
2017-07-15 11:44:14 +09:00
Sebastiaan Deckers
c601e603c2 fix: typo
Came up in downstream code review by @lucaslago https://github.com/nodejs/node/pull/14239#discussion_r127539852
2017-07-15 07:46:26 +08:00
Tatsuhiro Tsujikawa
1002c6da1c src: Use llround instead of round 2017-07-12 23:23:47 +09:00
Tatsuhiro Tsujikawa
0911337689 Bump up version number to 1.25.0-DEV 2017-07-02 17:51:24 +09:00
Tatsuhiro Tsujikawa
3bcc416e13 Update manual pages 2017-07-02 13:40:21 +09:00
Tatsuhiro Tsujikawa
65837806f5 Bump up version number to 1.24.0 2017-07-02 13:37:53 +09:00
Tatsuhiro Tsujikawa
b0772dcc66 Update AUTHORS 2017-07-02 13:31:47 +09:00
Tatsuhiro Tsujikawa
c6d65aad3b Merge branch 'nghttp-not-upgrade-without-reason-phrase' 2017-06-28 21:36:13 +09:00
Tatsuhiro Tsujikawa
18dd20ce55 nghttp: Fix bug that upgrade fails if reason-phrase is missing 2017-06-28 01:01:39 +09:00
Tatsuhiro Tsujikawa
0f6d76a501 Merge pull request #947 from bassosimone/patch-1
README.rst: fix typo
2017-06-23 00:33:00 +09:00
Simone Basso
0f1320109f README.rst: fix typo 2017-06-22 17:03:05 +02:00
Tatsuhiro Tsujikawa
defa28c618 Merge pull request #945 from benjaminp/trailer-grammar
fix up grammar in submit_trailer docs
2017-06-20 00:35:46 +09:00
Benjamin Peterson
b7c95be47c fix up grammar in submit_trailer docs 2017-06-18 23:55:53 -07:00
Tatsuhiro Tsujikawa
a18d154e0e Merge pull request #943 from nghttp2/nghttpx-verify-ocsp-resp-with-cacerts
nghttpx: Verify OCSP response using trusted CA certificates
2017-06-15 20:56:44 +09:00
Tatsuhiro Tsujikawa
52195a12ee Merge pull request #941 from nghttp2/nghttpx-tls-min-proto
nghttpx: Set default minimum TLS version to TLSv1.2
2017-06-13 23:01:54 +09:00
Tatsuhiro Tsujikawa
59c78d5809 nghttpx: Verify OCSP response using trusted CA certificates 2017-06-13 23:00:26 +09:00
Tatsuhiro Tsujikawa
be164fc8f9 nghttpx: Set default minimum TLS version to TLSv1.2
Previously, the default minimum TLS version was TLSv1.1, but the
default cipher list didn't include any compatible ciphers with it.
This made handshake fail if TLSv1.1 was negotiated because there was
no shared ciphers.  To make the default settings consistent, the
default minimum TLS version is now TLSv1.2.
2017-06-12 23:54:12 +09:00
Tatsuhiro Tsujikawa
5833ef1efc Merge pull request #938 from benjaminp/fix-clean
fix cleaning in out-of-tree builds
2017-06-12 00:21:10 +09:00
Benjamin Peterson
28f88d46f3 fix cleaning in out-of-tree builds
The altered previously failed if the rst sources hadn't been copied over.
2017-06-11 00:03:36 -07:00
Tatsuhiro Tsujikawa
6ec7683991 nghttpx: Use nocopy version to send trailer headers to backend
It looks like we can use nocopy version here.  We use nocopy version
in frontend in day 1.
2017-06-02 22:38:39 +09:00
Tatsuhiro Tsujikawa
fb2d8f79d6 Update doc 2017-06-02 22:22:44 +09:00
Tatsuhiro Tsujikawa
8f7fa1b1bf nghttpx: Fix crash in OCSP response verification 2017-05-30 23:52:38 +09:00
Tatsuhiro Tsujikawa
e5889ce622 Bump up version number to 1.24.0-DEV 2017-05-26 23:07:50 +09:00
Tatsuhiro Tsujikawa
3a6f83394c Update bash_completion 2017-05-26 22:17:10 +09:00
Tatsuhiro Tsujikawa
acf36f3d1a Update manual pages 2017-05-26 22:16:51 +09:00
Tatsuhiro Tsujikawa
63e6a8bab2 Bump up version number to 1.23.0, LT revision to 27:3:13 2017-05-26 21:37:28 +09:00
Tatsuhiro Tsujikawa
5361cc6bd1 Update authors 2017-05-26 21:34:43 +09:00
Tatsuhiro Tsujikawa
cabac55394 Merge pull request #925 from sohamm17/patch-1
spelling mistake in arguments to build nghttp apps
2017-05-25 23:38:54 +09:00
Tatsuhiro Tsujikawa
db7483ef10 Merge branch 'nghttpx-verify-ocsp' 2017-05-25 23:37:34 +09:00
Tatsuhiro Tsujikawa
4b51ccbefe examples: Attempt to fix OpenSSL link error 2017-05-25 23:24:44 +09:00
Tatsuhiro Tsujikawa
74c2f1257a nghttpx: Add --no-verify-ocsp to disable OCSP response verification 2017-05-25 23:14:58 +09:00
Tatsuhiro Tsujikawa
1428a5e3ae nghttpx: Verify OCSP response
At least we should make sure that the OCSP response is targeted to the
expected certificate.  This is important because we pass the file path
to the external script, and if the file is replaced because of
renewal, and nghttpx has not reloaded its configuration, the
certificate nghttpx has loaded and the one included in the file
differ.  Verifying the OCSP response detects this, and avoids to send
wrong OCSP response.
2017-05-25 23:14:57 +09:00
Tatsuhiro Tsujikawa
fe021c1524 Merge branch 'memchunk-no-unique-ptr' 2017-05-25 00:53:13 +09:00
Tatsuhiro Tsujikawa
c57bf21306 src: memchunks: Don't use std::unique_ptr to avoid potential SO 2017-05-25 00:23:51 +09:00
Soham Sinha
1743b7d92d spelling mistake in arguments to build nghttp apps 2017-05-22 17:20:30 -04:00
Tatsuhiro Tsujikawa
7f31278c4c Update doc 2017-05-22 22:53:49 +09:00
Tatsuhiro Tsujikawa
8401e16a15 nghttpx: Fix compile error with gcc 2017-05-22 22:10:55 +09:00
Tatsuhiro Tsujikawa
07fb5854f3 nghttpx: Compile with openssl 1.0.2 2017-05-22 22:09:34 +09:00
Tatsuhiro Tsujikawa
b56a99bfba Update bash_completion 2017-05-21 11:43:00 +09:00
Tatsuhiro Tsujikawa
b91e4e4df1 Update manual pages 2017-05-21 11:42:46 +09:00
Tatsuhiro Tsujikawa
52a4d6ac31 Merge branch 'nghttpx-fix-cert-selection' 2017-05-21 11:26:12 +09:00
Tatsuhiro Tsujikawa
796ab87b14 nghttpx: Fix certificate selection based on pub key algorithm 2017-05-21 11:12:47 +09:00
Tatsuhiro Tsujikawa
ed1fad3bd4 nghttpx: Call ERR_clear_error()
Call ERR_clear_error() before the OpenSSL function if we use
SSL_get_error() to examine error stack.
2017-05-21 10:32:12 +09:00
Tatsuhiro Tsujikawa
9c1876f542 nghttpx: Fix certificate indexing bug 2017-05-21 00:19:33 +09:00
Tatsuhiro Tsujikawa
7d111d9963 Merge pull request #923 from nghttp2/compile-with-disable-assert
Compile with --disable-assert
2017-05-18 23:49:41 +09:00
Tatsuhiro Tsujikawa
8c2ce0cf3f Merge pull request #922 from nghttp2/nghttpx-ocsp-startup
nghttpx: Run OCSP at startup
2017-05-18 23:49:23 +09:00
Tatsuhiro Tsujikawa
1b442cb16f Compile with --disable-assert 2017-05-18 23:10:44 +09:00
Tatsuhiro Tsujikawa
2bf3680d87 Merge pull request #919 from projectgus/fix_ndebug_compile
nghttp2_session: Allow for compiling library with -DNDEBUG set
2017-05-18 22:37:51 +09:00
Tatsuhiro Tsujikawa
0d4f0f0db5 nghttpx: Run OCSP at startup
With --ocsp-startup option, nghttpx starts accepting connections after
initial attempts to get OCSP responses finish.  It does not matter
some of the attempts fail.  This feature is useful if OCSP responses
must be available before accepting connections.
2017-05-18 22:33:49 +09:00
Angus Gratton
e17ff8fd32 nghttp2_session: Allow for compiling library with -DNDEBUG set 2017-05-17 14:43:06 +10:00
Tatsuhiro Tsujikawa
14edd12304 nghttpx: Refactor the code for the anti-replay 2017-05-14 17:45:35 +09:00
Tatsuhiro Tsujikawa
e6ffdb23a4 nghttpx: Share session_cache_ssl_ctx across threads 2017-05-14 17:43:11 +09:00
Tatsuhiro Tsujikawa
98fdedac06 Merge pull request #917 from Tapanito/patch-1
Update docs
2017-05-13 10:27:22 +09:00
Tapanito
255037264a updated docs 2017-05-12 16:35:44 +01:00
Tatsuhiro Tsujikawa
d3fcbe9a02 Merge branch 'invalid-header-field-error' 2017-05-12 21:37:20 +09:00
Tatsuhiro Tsujikawa
bcdd588c6e Merge branch 'nghttpx-wildcard-path-match' 2017-05-11 23:50:56 +09:00
Tatsuhiro Tsujikawa
b5007d45f7 nghttpx: Wildcard path matching
This commit adds wildcard path matching.  If path pattern given in
backend option ends with "*", it is considered as wildcard path.  "*"
must match at least one character.  All paths which include wildcard
path without last "*" as prefix, and are strictly longer than wildcard
path without last "*" are matched.
2017-05-11 22:15:28 +09:00
Tatsuhiro Tsujikawa
a584cf5a4f Use clang-format-4.0 2017-04-30 15:45:53 +09:00
Tatsuhiro Tsujikawa
77f7a2fa7f Update doc 2017-04-29 22:21:21 +09:00
Tatsuhiro Tsujikawa
f2c539dc70 Clarify the effect of nghttp2_option_set_no_http_messaging 2017-04-29 21:00:20 +09:00
Tatsuhiro Tsujikawa
78d7160a99 Treat incoming invalid regular header field as stream error
Previously, the incoming invalid regular header field was ignored by
default.  With this commit, they are now treated as stream error, and
the stream is reset by default.  The error code used is now
PROTOCOL_ERROR, instead of INTERNAL_ERROR.
2017-04-28 23:46:06 +09:00
Tatsuhiro Tsujikawa
196673bbce nghttp: Remove unused short option 'g' 2017-04-28 22:39:12 +09:00
Tatsuhiro Tsujikawa
794d13082c Merge branch 'nghttp-no-verify-peer' 2017-04-28 22:36:23 +09:00
Tatsuhiro Tsujikawa
5f5cf4107e nghttpx: Reseve rcbufs_ 2017-04-28 22:31:09 +09:00
Tatsuhiro Tsujikawa
6f3ec54b9f nghttp: Add -y, --no-verify-peer option to suppress peer verify warn 2017-04-28 09:53:37 +09:00
Tatsuhiro Tsujikawa
58043a6b04 nghttpx: Guard the presence of TLS1_3_VERSION 2017-04-27 23:13:15 +09:00
Tatsuhiro Tsujikawa
a885315ef5 Merge branch 'nghttpx-unrecognized-sni' 2017-04-27 22:57:54 +09:00
Tatsuhiro Tsujikawa
d7581525ac nghttpx: Update TLSv1.3 TLS record overhead 2017-04-27 22:57:06 +09:00
Tatsuhiro Tsujikawa
385068eb91 Merge branch 'altsvc-invalid-callback' 2017-04-27 22:35:25 +09:00
Tatsuhiro Tsujikawa
1085f68018 nghttpx: Return SSL_TLSEXT_ERR_NOACK if server name is not recognized
With this commit, SSL_TLSEXT_ERR_NOACK is returned from
servername_callback, which removes server_name extension from
ServerHello.  CertLookupTree is now used even if the number of server
certificate is one.  It is better to exercise it regularly.
2017-04-27 22:25:58 +09:00
Tatsuhiro Tsujikawa
21af775ce0 Call nghttp2_on_invalid_frame_callback if altsvc validation fails 2017-04-27 18:53:43 +09:00
Tatsuhiro Tsujikawa
bf16fee6e9 Merge pull request #903 from nghttp2/nghttpx-forward-multiple-header-fields
nghttpx: Forward multiple via, xff, and xfp header fields
2017-04-26 22:32:33 +09:00
Tatsuhiro Tsujikawa
2358a2137a Refactor predicate functions 2017-04-26 22:31:43 +09:00
Tatsuhiro Tsujikawa
66baa7dc25 Estimate header block size after predicate function succeeds 2017-04-26 22:15:53 +09:00
Tatsuhiro Tsujikawa
d63b4c1034 nghttpx: Forward multiple via, xff, and xfp header fields
Previously, for Via, X-Forwarded-For, and X-Forwarded-Proto header
field, nghttpx only forwarded the last header field of each.  With
this commit, nghttpx forwards all of them if it is configured to do
so.
2017-04-26 21:23:13 +09:00
Tatsuhiro Tsujikawa
963e220a1c Bump up version number to 1.23.0-DEV 2017-04-24 22:34:13 +09:00
Tatsuhiro Tsujikawa
2f146e4d4c Update manual pages 2017-04-24 21:41:15 +09:00
Tatsuhiro Tsujikawa
f796eede5a Bump up version number to 1.22.0, LT revision to 27:2:13 2017-04-24 21:32:18 +09:00
Tatsuhiro Tsujikawa
c89453be95 Update AUTHORS 2017-04-24 21:29:12 +09:00
Tatsuhiro Tsujikawa
c3f5f5ca36 nghttpx: Clarify --conf option behaviour 2017-04-20 22:25:38 +09:00
Tatsuhiro Tsujikawa
911d12f7c4 nghttpx: Add log when loading configuration file 2017-04-20 22:22:29 +09:00
Tatsuhiro Tsujikawa
34d3c45d35 Update manual pages 2017-04-19 23:03:08 +09:00
Tatsuhiro Tsujikawa
17614312e0 Merge pull request #892 from nghttp2/nghttpx-sni-fwd
nghttpx: SNI based backend server selection
2017-04-19 21:22:15 +09:00
Tatsuhiro Tsujikawa
977779ae8d Merge pull request #896 from nghttp2/nghttpx-add-tls-sni-log-var
nghttpx: Add $tls_sni access log variable
2017-04-19 21:21:01 +09:00
Tatsuhiro Tsujikawa
a2e35a0757 nghttpx: Add $tls_sni access log variable 2017-04-18 22:44:26 +09:00
Tatsuhiro Tsujikawa
a4a2b6403b nghttpx: Use SHRPX_LOGF_TLS_* instead of SHRPX_LOGF_SSL_* 2017-04-18 22:34:08 +09:00
Tatsuhiro Tsujikawa
8ce8e289c9 Merge pull request #895 from nghttp2/nghttpx-rename-ssl-log-vars
nghttpx: Rename ssl_* log variables as tls_*
2017-04-18 22:31:44 +09:00
Tatsuhiro Tsujikawa
03be97e437 nghttpx: Rename ssl_* log variables as tls_*
The exiting ssl_* log variables still work for backward compatibility.
2017-04-18 22:11:05 +09:00
Tatsuhiro Tsujikawa
2c5cf5a82a Merge pull request #894 from nghttp2/nghttpx-fix-path-match
nghttpx: Fix path matching bug
2017-04-18 21:28:21 +09:00
Tatsuhiro Tsujikawa
0a2d1965df nghttpx: Fix path matching bug
Previously, if path is empty or path does not start with "/", nghttpx
did not try to match with wildcard pattern.  This commit fixes it.
2017-04-18 21:03:50 +09:00
Tatsuhiro Tsujikawa
c8a5f1e335 nghttpx: SNI based backend server selection 2017-04-16 23:47:10 +09:00
Tatsuhiro Tsujikawa
5e00cf9620 Update doc 2017-04-14 00:55:31 +09:00
Tatsuhiro Tsujikawa
ce6370e25c Merge pull request #886 from lstefani/patch-1
Update nghttp2_session.c
2017-04-14 00:16:06 +09:00
Tatsuhiro Tsujikawa
3f8c1e4b34 tests: Fix failmalloc tests 2017-04-14 00:00:36 +09:00
lstefani
25cda200be Update nghttp2_session.c
Add missing free call on error in inflight_settings_new().
2017-04-13 10:00:15 -04:00
Tatsuhiro Tsujikawa
a1bc83a2ba Merge pull request #881 from mway/dev/request-priority
Support specifying stream priority via session::submit()
2017-04-12 23:36:40 +09:00
Matt Way
bc3949db9e Support specifying stream priority via session::submit() 2017-04-12 10:07:16 -04:00
Tatsuhiro Tsujikawa
6cfa885207 nghttpx: Remove unused lambda capture 2017-04-12 22:09:44 +09:00
Tatsuhiro Tsujikawa
899588e0b5 Update bash_completion 2017-04-09 16:10:07 +09:00
Tatsuhiro Tsujikawa
49af52a68d Update manual pages 2017-04-09 16:09:44 +09:00
Tatsuhiro Tsujikawa
ec908af19c integration: Use nip.io instead of xip.io 2017-04-09 16:06:37 +09:00
Tatsuhiro Tsujikawa
e61ac4682e Merge branch 'nghttpx-xfp-take2' 2017-04-09 16:02:53 +09:00
Tatsuhiro Tsujikawa
4d10dce61d nghttpx: Only send SCT for leaf certificate 2017-04-09 14:38:18 +09:00
Tatsuhiro Tsujikawa
c569830dfc Merge branch 'nghttpx-tls13-sct' 2017-04-09 14:24:00 +09:00
Tatsuhiro Tsujikawa
2d9fd87029 nghttpx: Enable signed_certificate_timestamp extension for TLSv1.3 2017-04-09 14:11:49 +09:00
Tatsuhiro Tsujikawa
2670bfb8ba integration: Add tests for X-Forwarded-Proto handling 2017-04-08 18:46:37 +09:00
Tatsuhiro Tsujikawa
cc9190ab37 nghttpx: Add options for X-Forwarded-Proto header field
This commit adds 2 new options to handle X-Forwarded-Proto header
field.  The --no-add-x-forwarded-proto option makes nghttpx not to
append X-Forwarded-Proto value.  The
--no-strip-incoming-x-forwarded-proto option prevents nghttpx from
stripping the header field from client.

Previously, nghttpx always strips incoming header field, and set its
own header field.  This commit preserves this behaviour, and adds
additional knobs.
2017-04-08 18:46:36 +09:00
Tatsuhiro Tsujikawa
980570de71 Revert "nghttpx: Add options for X-Forwarded-Proto header field"
This reverts commit 8c0b2c684a.
2017-04-08 18:37:54 +09:00
Tatsuhiro Tsujikawa
ef92b54db3 Revert "integration: Add tests for X-Forwarded-Proto handling"
This reverts commit 6aa581d2f0.
2017-04-08 18:34:10 +09:00
Tatsuhiro Tsujikawa
0130124cea Revert "doc: Add migration doc for nghttpx"
This reverts commit 82b326e684.
2017-04-08 18:34:07 +09:00
Tatsuhiro Tsujikawa
e2a7e867f9 integration: Add more tests for 204 status code 2017-04-07 22:36:46 +09:00
Tatsuhiro Tsujikawa
32ce0ce5d9 Merge branch 'nghttpx-fix-204-handling' 2017-04-07 22:11:39 +09:00
Tatsuhiro Tsujikawa
28082ff5f5 integration: Add tests for 204 status code 2017-04-07 21:46:33 +09:00
Tatsuhiro Tsujikawa
46ccc4332c nghttpx: Fix bug that 204 from h1 backend is always treated as error 2017-04-07 21:45:13 +09:00
Tatsuhiro Tsujikawa
3a1217e667 Update bash_completion 2017-04-06 20:26:32 +09:00
Tatsuhiro Tsujikawa
39fd0c1278 Update manual pages 2017-04-06 20:26:08 +09:00
Tatsuhiro Tsujikawa
4e6bd54dd1 Merge branch 'nghttpx-single-process' 2017-04-06 20:18:33 +09:00
Tatsuhiro Tsujikawa
5c9f46a6b0 Merge branch 'nghttp-verify-server-certificate' 2017-04-06 20:17:29 +09:00
Tatsuhiro Tsujikawa
7d53866157 Update doc 2017-04-06 20:14:49 +09:00
Tatsuhiro Tsujikawa
9a2e948c42 Update doc
Add section to promote the build from tar archives.
2017-04-06 20:13:00 +09:00
Tatsuhiro Tsujikawa
223e971c7e nghttpx: Add --single-process option
With --single-process option, nghttpx will run in a single process
mode where master and worker are unified into one process.  nghttpx
still spawns additional process for neverbleed.  In the single process
mode, signal handling is disabled.
2017-04-06 20:02:57 +09:00
Tatsuhiro Tsujikawa
df814223ff Merge branch 'nghttpx-xfp' 2017-04-06 19:36:27 +09:00
Tatsuhiro Tsujikawa
82b326e684 doc: Add migration doc for nghttpx 2017-04-06 19:24:02 +09:00
Tatsuhiro Tsujikawa
6aa581d2f0 integration: Add tests for X-Forwarded-Proto handling 2017-04-06 19:20:08 +09:00
Tatsuhiro Tsujikawa
8c0b2c684a nghttpx: Add options for X-Forwarded-Proto header field
This commit adds 2 new options to handle X-Forwarded-Proto header
field.  The --add-x-forwarded-proto option makes nghttpx append
X-Forwarded-Proto value.  The --strip-incoming-x-forwarded-proto
option makes nghttpx to strip the header field from client.

Previously, nghttpx always strips incoming header field, and set its
own header field.  This commit changes this behaviour.  Now nghttpx
does not strip, and append X-Forwarded-Proto header field by default.
The X-Forwarded-For, and Forwarded header fields are also handled in
the same way.  To recover the old behaviour, use
--add-x-forwarded-proto and --strip-incoming-x-forwarded-proto
options.
2017-04-06 19:17:36 +09:00
Tatsuhiro Tsujikawa
62324781bd integration: Close channel instead of writing struct 2017-04-05 23:57:33 +09:00
Tatsuhiro Tsujikawa
7ae0b2dc09 nghttp: Verify server certificate and show warning if it fails 2017-04-01 17:49:57 +09:00
Tatsuhiro Tsujikawa
058122b804 nghttpx: Rename shrpx_ssl.{h,cc} as shrpx_tls.{h,cc}
The namespace shrpx::ssl was also renamed as shrpx::tls.
2017-04-01 15:12:28 +09:00
Tatsuhiro Tsujikawa
69f63c529d src: Rename ssl.{h,cc} as tls.{h,cc}
nghttp2::ssl namespace was also renamed as nghttp2::tls.
2017-04-01 15:12:28 +09:00
Tatsuhiro Tsujikawa
e17a6b29b6 nghttpx: Use 502 as server error code 2017-04-01 14:04:55 +09:00
Tatsuhiro Tsujikawa
b12c2a13c0 nghttpx: Fail handshake if server certificate verification fails
Previously, we drop connection if server certificate verification
fails after handshake.  With this commit, we fail handshake if that
happens.
2017-04-01 13:41:41 +09:00
Tatsuhiro Tsujikawa
236c835abc nghttpx: Don't enable SSL_MODE_AUTO_RETRY since we do non-blocking I/O 2017-04-01 12:05:07 +09:00
Tatsuhiro Tsujikawa
b41a5afe04 Merge branch 'fix-asio-client-ping' 2017-03-31 22:20:07 +09:00
Tatsuhiro Tsujikawa
ad338bfa44 asio: Fix crash if connect takes longer time than ping interval 2017-03-31 21:17:57 +09:00
Tatsuhiro Tsujikawa
a899522679 asio: Fix compile error 2017-03-31 21:14:08 +09:00
Tatsuhiro Tsujikawa
b9b58c781e nghttpx: Avoid extra TLS handshake calls 2017-03-30 22:23:55 +09:00
Tatsuhiro Tsujikawa
aa1eec4642 nghttpx: Cache client side session inside openssl callback 2017-03-30 21:07:58 +09:00
Tatsuhiro Tsujikawa
0c8d9469ea nghttpx: Use SSL_CTX_set_early_data_enabled with boringssl 2017-03-27 23:58:49 +09:00
Tatsuhiro Tsujikawa
079e1bdffc Revert "nghttpx: Use SSL_CTX_set_early_data_enabled with boringssl"
This reverts commit b4337d1b54.
2017-03-27 23:47:24 +09:00
Tatsuhiro Tsujikawa
b4337d1b54 nghttpx: Use SSL_CTX_set_early_data_enabled with boringssl 2017-03-27 23:29:28 +09:00
Tatsuhiro Tsujikawa
e6a11c5e12 Bump up version number to 1.22.0-DEV 2017-03-27 23:13:56 +09:00
151 changed files with 5494 additions and 1492 deletions

View File

@@ -4,7 +4,7 @@ AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlinesLeft: false
AlignEscapedNewlines: Right
AlignOperands: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
@@ -31,14 +31,20 @@ BraceWrapping:
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeInheritanceComma: false
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
@@ -46,7 +52,11 @@ Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
@@ -68,6 +78,7 @@ NamespaceIndentation: None
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
@@ -77,7 +88,9 @@ PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
ReflowComments: true
SortIncludes: false
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false

View File

@@ -1,3 +1,4 @@
dist: trusty
env:
matrix:
- CI_BUILD=cmake
@@ -6,15 +7,13 @@ language: cpp
compiler:
- clang
- gcc
sudo: false
sudo: required
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- george-edison55-precise-backports
packages:
- g++-4.9
- libstdc++-4.9-dev
- g++-7
- autoconf
- automake
- autotools-dev
@@ -33,29 +32,18 @@ addons:
- cmake-data
before_install:
- $CC --version
- if [ "$CXX" = "g++" ]; then export CXX="g++-4.9" CC="gcc-4.9"; fi
- if [ "$CXX" = "g++" ]; then export CXX="g++-7" CC="gcc-7"; fi
- $CC --version
- go version
- cmake --version
before_script:
# First build spdylay, since integration tests require it.
# spdylay is going to be built under third-party/spdylay
- cd third-party
- git clone https://github.com/tatsuhiro-t/spdylay.git
- cd spdylay
- autoreconf -i
# Don't use ASAN for spdylay since failmalloc does not work with it.
- ./configure --disable-src --disable-examples
- make check
- export SPDYLAY_HOME=$PWD
- cd ../..
# Now build nghttp2
- if [ "$CI_BUILD" = "autotools" ]; then autoreconf -i; fi
- git submodule update --init
- if [ "$CI_BUILD" = "autotools" ]; then ./configure --enable-werror --with-mruby --with-neverbleed LIBSPDYLAY_CFLAGS="-I$SPDYLAY_HOME/lib/includes" LIBSPDYLAY_LIBS="-L$SPDYLAY_HOME/lib/.libs -lspdylay" CPPFLAGS=-fsanitize=address LDFLAGS=-fsanitize=address; fi
- if [ "$CI_BUILD" = "cmake" ]; then cmake -DENABLE_WERROR=1 -DWITH_MRUBY=1 -DWITH_NEVERBLEED=1 -DSPDYLAY_INCLUDE_DIR="$SPDYLAY_HOME/lib/includes" -DSPDYLAY_LIBRARY="$SPDYLAY_HOME/lib/.libs/libspdylay.so"; fi
- if [ "$CI_BUILD" = "autotools" ]; then ./configure --with-mruby; 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; fi
- if [ "$CI_BUILD" = "autotools" ]; then make distcheck DISTCHECK_CONFIGURE_FLAGS="--with-mruby --with-neverbleed --enable-werror CPPFLAGS=-fsanitize=address LDFLAGS=\"-fsanitize=address -fuse-ld=gold\""; fi
- if [ "$CI_BUILD" = "cmake" ]; then make check; fi
# As of April, 23, 2016, golang http2 build fails, probably because
# the default go version is too old.

19
AUTHORS
View File

@@ -21,21 +21,25 @@ Amir Pakdel
Anders Bakken
Andreas Pohl
Andy Davies
Angus Gratton
Anna Henningsen
Ant Bryan
Benedikt Christoph Wolters
Benedikt Christoph Wolters
Bernard Spil
Benjamin Peterson
Bernard Spil
Brian Card
Brian Suh
Daniel Evers
Daniel Stenberg
Dave Reisner
David Beitey
David Weekly
Dmitriy Vetutnev
Etienne Cimon
Fabian Möller
Fabian Wiesel
Gabi Davar
Gitai
Google Inc.
Jacob Champion
Jan-E
@@ -50,11 +54,15 @@ Kenny (kang-yen) Peng
Kenny Peng
Kit Chan
Kyle Schomp
LazyHamster
Lucas Pardue
MATSUMOTO Ryosuke
Marc Bachmann
Matt Rudary
Matt Way
Mike Conlen
Mike Frysinger
Mike Lothian
Nicholas Hurley
Nora Shoemaker
Peeyush Aggarwal
@@ -63,15 +71,21 @@ Piotr Sikora
Raul Gutierrez Segales
Remo E
Reza Tavakoli
Rick Lei
Ross Smith II
Scott Mitchell
Sebastiaan Deckers
Simone Basso
Soham Sinha
Stefan Eissing
Stephen Ludin
Sunpoet Po-Chuan Hsieh
Svante Signell
Syohei YOSHIDA
Tapanito
Tatsuhiko Kubo
Tatsuhiro Tsujikawa
Tobias Geerinckx-Rice
Tom Harwood
Tomasz Buchert
Tomasz Torcz
@@ -89,6 +103,7 @@ dalf
es
fangdingjun
kumagi
lstefani
makovich
mod-h2-dev
moparisthebest

View File

@@ -24,13 +24,13 @@
cmake_minimum_required(VERSION 3.0)
# XXX using 1.8.90 instead of 1.9.0-DEV
project(nghttp2 VERSION 1.21.0)
project(nghttp2 VERSION 1.28.90)
# See versioning rule:
# http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
set(LT_CURRENT 27)
set(LT_REVISION 1)
set(LT_AGE 13)
set(LT_CURRENT 29)
set(LT_REVISION 0)
set(LT_AGE 15)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
include(Version)
@@ -79,7 +79,7 @@ else()
set(ENABLE_PYTHON_BINDINGS_DEFAULT OFF)
endif()
find_package(LibXml2 2.7.7)
find_package(LibXml2 2.6.26)
set(WITH_LIBXML2_DEFAULT ${LIBXML2_FOUND})
find_package(Jemalloc)
set(WITH_JEMALLOC_DEFAULT ${JEMALLOC_FOUND})
@@ -106,7 +106,7 @@ endif()
foreach(_build_type "Release" "MinSizeRel" "RelWithDebInfo")
foreach(_lang C CXX)
string(TOUPPER "CMAKE_${_lang}_FLAGS_${_build_type}" _var)
string(REGEX REPLACE "(^| )[/-]D *NDEBUG($| )" "" ${_var} "${${_var}}")
string(REGEX REPLACE "(^| )[/-]D *NDEBUG($| )" " " ${_var} "${${_var}}")
endforeach()
endforeach()

View File

@@ -10,39 +10,47 @@
#
# $ sudo docker run -v /path/to/dest:/out nghttp2-android cp /root/build/nghttp2/src/nghttpx /out
FROM ubuntu:vivid
# Only use standalone-toolchain for reduce size
FROM ubuntu:xenial
MAINTAINER Tatsuhiro Tsujikawa
ENV ANDROID_HOME /root/android
ENV PREFIX $ANDROID_HOME/usr/local
ENV ANDROID_HOME /root
ENV TOOLCHAIN $ANDROID_HOME/toolchain
ENV PATH $TOOLCHAIN/bin:$PATH
# It would be better to use nearest ubuntu archive mirror for faster
# downloads.
# RUN sed -ie 's/archive\.ubuntu/jp.archive.ubuntu/g' /etc/apt/sources.list
ENV NDK_VERSION r14b
RUN apt-get update
# genisoimage, libc6-i386 and lib32stdc++6 are required to decompress ndk.
RUN apt-get install -y make binutils autoconf automake autotools-dev libtool \
pkg-config git curl dpkg-dev libxml2-dev \
genisoimage libc6-i386 lib32stdc++6
WORKDIR /root
RUN apt-get update && \
apt-get install -y unzip make binutils autoconf \
automake autotools-dev libtool pkg-config git \
curl dpkg-dev libxml2-dev genisoimage libc6-i386 \
lib32stdc++6 python&& \
rm -rf /var/cache/apk/*
# Install toolchain
RUN curl -L -O https://dl.google.com/android/repository/android-ndk-$NDK_VERSION-linux-x86_64.zip && \
unzip -q android-ndk-$NDK_VERSION-linux-x86_64.zip && \
rm android-ndk-$NDK_VERSION-linux-x86_64.zip && \
mkdir -p $ANDROID_HOME/toolchain && \
$ANDROID_HOME/android-ndk-$NDK_VERSION/build/tools/make-standalone-toolchain.sh \
--install-dir=$ANDROID_HOME/toolchain \
--toolchain=arm-linux-androideabi-4.9 \
--force && \
rm -r android-ndk-$NDK_VERSION
ENV PREFIX /root/usr/local
# Setup version of libraries
ENV OPENSSL_VERSION 1.0.2d
ENV SPDYLAY_VERSION v1.4.0
ENV LIBEV_VERSION 4.19
ENV ZLIB_VERSION 1.2.8
ENV CARES_VERSION 1.13.0
ENV NGHTTP2_VERSION v1.24.0
WORKDIR /root/build
RUN curl -L -O http://dl.google.com/android/ndk/android-ndk-r10d-linux-x86_64.bin && \
chmod a+x android-ndk-r10d-linux-x86_64.bin && \
./android-ndk-r10d-linux-x86_64.bin && \
rm android-ndk-r10d-linux-x86_64.bin
WORKDIR /root/build/android-ndk-r10d
RUN /bin/bash build/tools/make-standalone-toolchain.sh \
--install-dir=$ANDROID_HOME/toolchain \
--toolchain=arm-linux-androideabi-4.9 --llvm-version=3.5 \
--system=linux-x86_64
WORKDIR /root/build
RUN git clone https://github.com/tatsuhiro-t/spdylay
RUN git clone https://github.com/tatsuhiro-t/spdylay -b $SPDYLAY_VERSION --depth 1
WORKDIR /root/build/spdylay
RUN autoreconf -i && \
./configure \
@@ -59,22 +67,22 @@ RUN autoreconf -i && \
make install
WORKDIR /root/build
RUN curl -L -O https://www.openssl.org/source/openssl-1.0.2d.tar.gz && \
tar xf openssl-1.0.2d.tar.gz && \
rm openssl-1.0.2d.tar.gz
RUN curl -L -O https://www.openssl.org/source/openssl-$OPENSSL_VERSION.tar.gz && \
tar xf openssl-$OPENSSL_VERSION.tar.gz && \
rm openssl-$OPENSSL_VERSION.tar.gz
WORKDIR /root/build/openssl-1.0.2d
WORKDIR /root/build/openssl-$OPENSSL_VERSION
RUN export CROSS_COMPILE=$TOOLCHAIN/bin/arm-linux-androideabi- && \
./Configure --prefix=$PREFIX android && \
make && make install_sw
WORKDIR /root/build
RUN curl -L -O http://dist.schmorp.de/libev/libev-4.19.tar.gz && \
RUN curl -L -O http://dist.schmorp.de/libev/Attic/libev-$LIBEV_VERSION.tar.gz && \
curl -L -O https://gist.github.com/tatsuhiro-t/48c45f08950f587180ed/raw/80a8f003b5d1091eae497c5995bbaa68096e739b/libev-4.19-android.patch && \
tar xf libev-4.19.tar.gz && \
rm libev-4.19.tar.gz
tar xf libev-$LIBEV_VERSION.tar.gz && \
rm libev-$LIBEV_VERSION.tar.gz
WORKDIR /root/build/libev-4.19
WORKDIR /root/build/libev-$LIBEV_VERSION
RUN patch -p1 < ../libev-4.19-android.patch && \
./configure \
--host=arm-linux-androideabi \
@@ -87,11 +95,11 @@ RUN patch -p1 < ../libev-4.19-android.patch && \
make install
WORKDIR /root/build
RUN curl -L -O http://zlib.net/zlib-1.2.8.tar.gz && \
tar xf zlib-1.2.8.tar.gz && \
rm zlib-1.2.8.tar.gz
RUN curl -L -O https://downloads.sourceforge.net/project/libpng/zlib/$ZLIB_VERSION/zlib-$ZLIB_VERSION.tar.gz && \
tar xf zlib-$ZLIB_VERSION.tar.gz && \
rm zlib-$ZLIB_VERSION.tar.gz
WORKDIR /root/build/zlib-1.2.8
WORKDIR /root/build/zlib-$ZLIB_VERSION
RUN HOST=arm-linux-androideabi \
CC=$HOST-gcc \
AR=$HOST-ar \
@@ -105,11 +113,26 @@ RUN HOST=arm-linux-androideabi \
--static && \
make install
WORKDIR /root/build
RUN git clone https://github.com/nghttp2/nghttp2
RUN curl -L -O https://c-ares.haxx.se/download/c-ares-$CARES_VERSION.tar.gz && \
tar xf c-ares-$CARES_VERSION.tar.gz && \
rm c-ares-$CARES_VERSION.tar.gz
WORKDIR /root/build/c-ares-$CARES_VERSION
RUN ./configure \
--host=arm-linux-androideabi \
--build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \
--prefix=$PREFIX \
--disable-shared && \
make install
WORKDIR /root/build
RUN git clone https://github.com/nghttp2/nghttp2 -b $NGHTTP2_VERSION --depth 1
WORKDIR /root/build/nghttp2
RUN autoreconf -i && \
./configure \
--enable-app \
--disable-shared \
--host=arm-linux-androideabi \
--build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \
@@ -118,11 +141,10 @@ RUN autoreconf -i && \
--disable-python-bindings \
--disable-examples \
--disable-threads \
LIBSPDYLAY_CFLAGS=-I$PREFIX/usr/local/include \
LIBSPDYLAY_LIBS="-L$PREFIX/usr/local/lib -lspdylay" \
CPPFLAGS="-fPIE -I$PREFIX/include" \
CXXFLAGS="-fno-strict-aliasing" \
PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \
LDFLAGS="-fPIE -pie -L$PREFIX/lib" && \
CC="$TOOLCHAIN"/bin/arm-linux-androideabi-clang \
CXX="$TOOLCHAIN"/bin/arm-linux-androideabi-clang++ \
CPPFLAGS="-fPIE -I$PREFIX/include" \
PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \
LDFLAGS="-fPIE -pie -L$PREFIX/lib" && \
make && \
arm-linux-androideabi-strip src/nghttpx src/nghttpd src/nghttp

View File

@@ -87,7 +87,7 @@ enabled. SPDY support will be removed soon.
To enable ``-a`` option (getting linked assets from the downloaded
resource) in ``nghttp``, the following package is required:
* libxml2 >= 2.7.7
* libxml2 >= 2.6.26
To enable systemd support in nghttpx, the following package is
required:
@@ -157,22 +157,8 @@ minimizes the risk of private key leakage when serious bug like
Heartbleed is exploited. The neverbleed is disabled by default. To
enable it, use ``--with-neverbleed`` configure option.
Building from git
-----------------
Building from git is easy, but please be sure that at least autoconf 2.68 is
used:
.. code-block:: text
$ git submodule update --init
$ autoreconf -i
$ automake
$ autoconf
$ ./configure
$ make
To compile the source code, gcc >= 4.8.3 or clang >= 3.4 is required.
In order to compile the source code, gcc >= 4.8.3 or clang >= 3.4 is
required.
.. note::
@@ -219,6 +205,40 @@ To compile the source code, gcc >= 4.8.3 or clang >= 3.4 is required.
responsible to specify the correct values to these variables. For
complete list of these variables, run ``./configure -h``.
Building nghttp2 from release tar archive
-----------------------------------------
The nghttp2 project regularly releases tar archives which includes
nghttp2 source code, and generated build files. They can be
downloaded from `Releases
<https://github.com/nghttp2/nghttp2/releases>`_ page.
Building nghttp2 from git requires autotools development packages.
Building from tar archives does not require them, and thus it is much
easier. The usual build step is as follows:
.. code-block:: text
$ tar xf nghttp2-X.Y.Z.tar.bz2
$ cd nghttp2-X.Y.Z
$ ./configure
$ make
Building from git
-----------------
Building from git is easy, but please be sure that at least autoconf 2.68 is
used:
.. code-block:: text
$ git submodule update --init
$ autoreconf -i
$ automake
$ autoconf
$ ./configure
$ make
Notes for building on Windows (MSVC)
------------------------------------
@@ -265,6 +285,18 @@ If you want to compile the applications under ``examples/``, you need
to remove or rename the ``event.h`` from libev's installation, because
it conflicts with libevent's installation.
Notes for installation on Linux systems
--------------------------------------------
After installing nghttp2 tool suite with ``make install`` one might experience a similar error:
.. code-block:: text
nghttpx: error while loading shared libraries: libnghttp2.so.14: cannot open shared object file: No such file or directory
This means that the tool is unable to locate the ``libnghttp2.so`` shared library.
To update the shared library cache run ``sudo ldconfig``.
Building the documentation
--------------------------

View File

@@ -25,7 +25,7 @@ dnl Do not change user variables!
dnl http://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html
AC_PREREQ(2.61)
AC_INIT([nghttp2], [1.21.0], [t-tujikawa@users.sourceforge.net])
AC_INIT([nghttp2], [1.29.0-DEV], [t-tujikawa@users.sourceforge.net])
AC_CONFIG_AUX_DIR([.])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_HEADERS([config.h])
@@ -44,9 +44,9 @@ m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
dnl See versioning rule:
dnl http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
AC_SUBST(LT_CURRENT, 27)
AC_SUBST(LT_REVISION, 1)
AC_SUBST(LT_AGE, 13)
AC_SUBST(LT_CURRENT, 29)
AC_SUBST(LT_REVISION, 0)
AC_SUBST(LT_AGE, 15)
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"`
@@ -119,7 +119,7 @@ AC_ARG_WITH([jemalloc],
AC_ARG_WITH([spdylay],
[AS_HELP_STRING([--with-spdylay],
[Use spdylay [default=no]])],
[(Deprecated) Use spdylay [default=no]])],
[request_spdylay=$withval], [request_spdylay=no])
AC_ARG_WITH([systemd],
@@ -410,7 +410,7 @@ if test "x${request_systemd}" = "xyes" &&
fi
# libxml2 (for src/nghttp)
PKG_CHECK_MODULES([LIBXML2], [libxml-2.0 >= 2.7.7],
PKG_CHECK_MODULES([LIBXML2], [libxml-2.0 >= 2.6.26],
[have_libxml2=yes], [have_libxml2=no])
if test "x${have_libxml2}" = "xyes"; then
AC_DEFINE([HAVE_LIBXML2], [1], [Define to 1 if you have `libxml2` library.])
@@ -804,6 +804,9 @@ if test "x$werror" != "xno"; then
AX_CHECK_COMPILE_FLAG([-Werror], [CXXFLAGS="$CXXFLAGS -Werror"])
AX_CHECK_COMPILE_FLAG([-Wformat-security], [CXXFLAGS="$CXXFLAGS -Wformat-security"])
AX_CHECK_COMPILE_FLAG([-Wsometimes-uninitialized], [CXXFLAGS="$CXXFLAGS -Wsometimes-uninitialized"])
# Disable noexcept-type warning of g++-7. This is not harmful as
# long as all source files are compiled with the same compiler.
AX_CHECK_COMPILE_FLAG([-Wno-noexcept-type], [CXXFLAGS="$CXXFLAGS -Wno-noexcept-type"])
AC_LANG_POP()
fi
@@ -946,3 +949,7 @@ AC_MSG_NOTICE([summary of build options:
Python bindings:${enable_python_bindings}
Threading: ${enable_threads}
])
if test "x${have_spdylay}" == "xyes"; then
AC_MSG_WARN([spdylay support was deprecated, and will be removed in v1.29.0.])
fi

View File

@@ -49,6 +49,7 @@ set(APIDOCS
nghttp2_rcbuf_decref.rst
nghttp2_rcbuf_get_buf.rst
nghttp2_rcbuf_incref.rst
nghttp2_rcbuf_is_static.rst
nghttp2_select_next_protocol.rst
nghttp2_session_callbacks_del.rst
nghttp2_session_callbacks_new.rst

View File

@@ -74,12 +74,14 @@ APIDOCS= \
nghttp2_rcbuf_decref.rst \
nghttp2_rcbuf_get_buf.rst \
nghttp2_rcbuf_incref.rst \
nghttp2_rcbuf_is_static.rst \
nghttp2_select_next_protocol.rst \
nghttp2_session_callbacks_del.rst \
nghttp2_session_callbacks_new.rst \
nghttp2_session_callbacks_set_before_frame_send_callback.rst \
nghttp2_session_callbacks_set_data_source_read_length_callback.rst \
nghttp2_session_callbacks_set_error_callback.rst \
nghttp2_session_callbacks_set_error_callback2.rst \
nghttp2_session_callbacks_set_on_begin_frame_callback.rst \
nghttp2_session_callbacks_set_on_begin_headers_callback.rst \
nghttp2_session_callbacks_set_on_data_chunk_recv_callback.rst \
@@ -267,7 +269,7 @@ apiref.rst: \
$(APIDOCS): apiref.rst
clean-local:
[ $(srcdir) = $(builddir) ] || for i in $(RST_FILES); do [ -e $(builddir)/$$i ] && rm -f $(builddir)/$$i; done
if [ $(srcdir) != $(builddir) ]; then for i in $(RST_FILES); do rm -f $(builddir)/$$i; done fi
-rm -f apiref.rst
-rm -f $(APIDOCS)
-rm -rf $(BUILDDIR)/*

View File

@@ -13,6 +13,7 @@ import re
from docutils import nodes
from docutils.parsers.rst import directives
from docutils.parsers.rst import Directive
from sphinx import addnodes
from sphinx import version_info
@@ -21,10 +22,8 @@ from sphinx.locale import l_, _
from sphinx.domains import Domain, ObjType, Index
from sphinx.directives import ObjectDescription
from sphinx.util.nodes import make_refnode
from sphinx.util.compat import Directive
from sphinx.util.docfields import Field, GroupedField, TypedField
# REs for Ruby signatures
rb_sig_re = re.compile(
r'''^ ([\w.]*\.)? # class name(s)

View File

@@ -8,7 +8,7 @@ _h2load()
_get_comp_words_by_ref cur prev
case $cur in
-*)
COMPREPLY=( $( compgen -W '--connection-window-bits --clients --verbose --ciphers --rate --no-tls-proto --header-table-size --requests --base-uri --h1 --threads --npn-list --rate-period --data --version --connection-inactivity-timeout --timing-script-file --encoder-header-table-size --max-concurrent-streams --connection-active-timeout --input-file --help --window-bits --header ' -- "$cur" ) )
COMPREPLY=( $( compgen -W '--connection-window-bits --clients --verbose --ciphers --rate --no-tls-proto --header-table-size --requests --base-uri --h1 --threads --npn-list --rate-period --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" ) )
;;
*)
_filedir

View File

@@ -8,7 +8,7 @@ _nghttp()
_get_comp_words_by_ref cur prev
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 --header ' -- "$cur" ) )
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" ) )
;;
*)
_filedir

View File

@@ -8,7 +8,7 @@ _nghttpx()
_get_comp_words_by_ref cur prev
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 --frontend-keep-alive-timeout --backend-request-buffer --max-request-header-fields --fastopen --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 --no-via --tls-session-cache-memcached-cert-file --no-http2-cipher-black-list --mruby-file --client-no-http2-cipher-black-list --stream-read-timeout --client-ciphers --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 --rlimit-nofile --tls-ticket-key-memcached-cert-file --ocsp-update-interval --forwarded-by --tls-session-cache-memcached-private-key-file --error-page --backend-write-timeout --tls-dyn-rec-warmup-threshold --tls-ticket-key-memcached-max-retry --frontend-http2-window-size --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 --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 --no-server-push --no-location-rewrite --single-thread --tls-session-cache-memcached --no-ocsp --backend-response-buffer --tls-min-proto-version --workers --add-forwarded --worker-write-rate --add-request-header --backend-http2-settings-timeout --subcert --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 --user --add-x-forwarded-for --frontend-read-timeout --tls-ticket-key-memcached-max-fail --backlog --write-burst --backend-connections-per-host --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 --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 --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 '--worker-read-rate --include --frontend-http2-dump-response-header --tls-ticket-key-file --verify-client-cacert --max-response-header-fields --backend-http2-window-size --frontend-keep-alive-timeout --backend-request-buffer --max-request-header-fields --fastopen --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 --no-via --tls-session-cache-memcached-cert-file --no-http2-cipher-black-list --mruby-file --client-no-http2-cipher-black-list --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 --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 --tls-ticket-key-memcached-max-retry --frontend-http2-window-size --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 --no-server-push --no-location-rewrite --single-thread --tls-session-cache-memcached --no-ocsp --backend-response-buffer --tls-min-proto-version --workers --add-forwarded --worker-write-rate --add-request-header --backend-http2-settings-timeout --subcert --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 --user --add-x-forwarded-for --frontend-read-timeout --tls-ticket-key-memcached-max-fail --backlog --write-burst --backend-connections-per-host --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 --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" ) )
;;
*)
_filedir

View File

@@ -157,7 +157,7 @@ html_theme_path = ['@top_srcdir@/doc/_themes']
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
html_use_smartypants = False
#html_use_smartypants = False
# Custom sidebar templates, maps document names to template names.
html_sidebars = {

2
doc/docutils.conf Normal file
View File

@@ -0,0 +1,2 @@
[parsers]
smart_quotes=no

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "H2LOAD" "1" "Mar 27, 2017" "1.21.0" "nghttp2"
.TH "H2LOAD" "1" "Nov 25, 2017" "1.28.0" "nghttp2"
.SH NAME
h2load \- HTTP/2 benchmarking tool
.
@@ -54,7 +54,9 @@ scheme, host or port values.
Number of requests across all clients. If it is used
with \fI\%\-\-timing\-script\-file\fP option, this option specifies
the number of requests each client performs rather than
the number of requests across all clients.
the number of requests across all clients. This option
is ignored if timing\-based benchmarking is enabled (see
\fI\%\-\-duration\fP option).
.sp
Default: \fB1\fP
.UNINDENT
@@ -170,6 +172,19 @@ option is 1s.
.UNINDENT
.INDENT 0.0
.TP
.B \-D, \-\-duration=<N>
Specifies the main duration for the measurements in case
of timing\-based benchmarking.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-warm\-up\-time=<DURATION>
Specifies the time period before starting the actual
measurements, in case of timing\-based benchmarking.
Needs to provided along with \fI\%\-D\fP option.
.UNINDENT
.INDENT 0.0
.TP
.B \-T, \-\-connection\-active\-timeout=<DURATION>
Specifies the maximum time that h2load is willing to
keep a connection open, regardless of the activity on

View File

@@ -34,7 +34,9 @@ OPTIONS
Number of requests across all clients. If it is used
with :option:`--timing-script-file` option, this option specifies
the number of requests each client performs rather than
the number of requests across all clients.
the number of requests across all clients. This option
is ignored if timing-based benchmarking is enabled (see
:option:`--duration` option).
Default: ``1``
@@ -136,6 +138,17 @@ OPTIONS
the rate option is not used. The default value for this
option is 1s.
.. option:: -D, --duration=<N>
Specifies the main duration for the measurements in case
of timing-based benchmarking.
.. option:: --warm-up-time=<DURATION>
Specifies the time period before starting the actual
measurements, in case of timing-based benchmarking.
Needs to provided along with :option:`-D` option.
.. option:: -T, --connection-active-timeout=<DURATION>
Specifies the maximum time that h2load is willing to

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "NGHTTP" "1" "Mar 27, 2017" "1.21.0" "nghttp2"
.TH "NGHTTP" "1" "Nov 25, 2017" "1.28.0" "nghttp2"
.SH NAME
nghttp \- HTTP/2 client
.
@@ -236,6 +236,12 @@ combined with the \fI\%\-d\fP option.
.UNINDENT
.INDENT 0.0
.TP
.B \-y, \-\-no\-verify\-peer
Suppress warning on server certificate verification
failure.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-version
Display version information and exit.
.UNINDENT

View File

@@ -186,6 +186,11 @@ OPTIONS
Continue interim response. This option is ignored unless
combined with the :option:`-d` option.
.. option:: -y, --no-verify-peer
Suppress warning on server certificate verification
failure.
.. option:: --version
Display version information and exit.

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "NGHTTPD" "1" "Mar 27, 2017" "1.21.0" "nghttp2"
.TH "NGHTTPD" "1" "Nov 25, 2017" "1.28.0" "nghttp2"
.SH NAME
nghttpd \- HTTP/2 server
.

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "NGHTTPX" "1" "Mar 27, 2017" "1.21.0" "nghttp2"
.TH "NGHTTPX" "1" "Nov 25, 2017" "1.28.0" "nghttp2"
.SH NAME
nghttpx \- HTTP/2 proxy
.
@@ -73,14 +73,16 @@ path which ends with "\fI/\fP" also matches the request path
which only lacks trailing \(aq\fI/\fP\(aq (e.g., path "\fI/foo/\fP"
matches request path "\fI/foo\fP"). If it does not end with
"\fI/\fP", it performs exact match against the request path.
If host is given, it performs exact match against the
request host. If host alone is given, "\fI/\fP" is appended
to it, so that it matches all request paths under the
host (e.g., specifying "nghttp2.org" equals to
"nghttp2.org/"). CONNECT method is treated specially.
It does not have path, and we don\(aqt allow empty path.
To workaround this, we assume that CONNECT method has
"\fI/\fP" as path.
If host is given, it performs a match against the
request host. For a request received on the frontend
listener with "sni\-fwd" parameter enabled, SNI host is
used instead of a request host. If host alone is given,
"\fI/\fP" is appended to it, so that it matches all request
paths under the host (e.g., specifying "nghttp2.org"
equals to "nghttp2.org/"). CONNECT method is treated
specially. It does not have path, and we don\(aqt allow
empty path. To workaround this, we assume that CONNECT
method has "\fI/\fP" as path.
.sp
Patterns with host take precedence over patterns with
just path. Then, longer patterns take precedence over
@@ -94,6 +96,18 @@ host pattern "*.nghttp2.org" matches against
match against "nghttp2.org". The exact hosts match
takes precedence over the wildcard hosts match.
.sp
If path part ends with "*", it is treated as wildcard
path. The wildcard path behaves differently from the
normal path. For normal path, match is made around the
boundary of path component separator,"\fI/\fP". On the other
hand, the wildcard path does not take into account the
path component separator. All paths which include the
wildcard path without last "*" as prefix, and are
strictly longer than wildcard path without last "*" are
matched. "*" must match at least one character. For
example, the pattern "\fI/foo*\fP" matches "\fI/foo/\fP" and
"\fI/foobar\fP". But it does not match "\fI/foo\fP", or "\fI/fo\fP".
.sp
If <PATTERN> is omitted or empty string, "\fI/\fP" is used as
pattern, which matches all request paths (catch\-all
pattern). The catch\-all backend must be given.
@@ -166,16 +180,32 @@ state, and this is the default behaviour.
The session affinity is enabled using
"affinity=<METHOD>" parameter. If "ip" is given in
<METHOD>, client IP based session affinity is enabled.
If "none" is given in <METHOD>, session affinity is
disabled, and this is the default. The session affinity
is enabled per <PATTERN>. If at least one backend has
"affinity" parameter, and its <METHOD> is not "none",
session affinity is enabled for all backend servers
sharing the same <PATTERN>. It is advised to set
"affinity" parameter to all backend explicitly if
session affinity is desired. The session affinity may
break if one of the backend gets unreachable, or backend
settings are reloaded or replaced by API.
If "cookie" is given in <METHOD>, cookie based session
affinity is enabled. If "none" is given in <METHOD>,
session affinity is disabled, and this is the default.
The session affinity is enabled per <PATTERN>. If at
least one backend has "affinity" parameter, and its
<METHOD> is not "none", session affinity is enabled for
all backend servers sharing the same <PATTERN>. It is
advised to set "affinity" parameter to all backend
explicitly if session affinity is desired. The session
affinity may break if one of the backend gets
unreachable, or backend settings are reloaded or
replaced by API.
.sp
If "affinity=cookie" is used, the additional
configuration is required.
"affinity\-cookie\-name=<NAME>" must be used to specify a
name of cookie to use. Optionally,
"affinity\-cookie\-path=<PATH>" can be used to specify a
path which cookie is applied. The optional
"affinity\-cookie\-secure=<SECURE>" controls the Secure
attribute of a cookie. The default value is "auto", and
the Secure attribute is determined by a request scheme.
If a request scheme is "https", then Secure attribute is
set. Otherwise, it is not set. If <SECURE> is "yes",
the Secure attribute is always set. If <SECURE> is
"no", the Secure attribute is always omitted.
.sp
By default, name resolution of backend host name is done
at start up, or reloading configuration. If "dns"
@@ -221,6 +251,11 @@ parameters are mutually exclusive.
Optionally, TLS can be disabled by specifying "no\-tls"
parameter. TLS is enabled by default.
.sp
If "sni\-fwd" parameter is used, when performing a match
to select a backend server, SNI host name received from
the client is used instead of the request host. See
\fI\%\-\-backend\fP option about the pattern match.
.sp
To make this frontend as API endpoint, specify "api"
parameter. This is disabled by default. It is
important to limit the access to the API frontend.
@@ -585,11 +620,14 @@ enabled for backend connections.
.INDENT 0.0
.TP
.B \-\-cacert=<PATH>
Set path to trusted CA certificate file used in backend
TLS connections. The file must be in PEM format. It
can contain multiple certificates. If the linked
OpenSSL is configured to load system wide certificates,
they are loaded at startup regardless of this option.
Set path to trusted CA certificate file. It is used in
backend TLS connections to verify peer\(aqs certificate.
It is also used to verify OCSP response from the script
set by \fI\%\-\-fetch\-ocsp\-response\-file\fP\&. The file must be in
PEM format. It can contain multiple certificates. If
the linked OpenSSL is configured to load system wide
certificates, they are loaded at startup regardless of
this option.
.UNINDENT
.INDENT 0.0
.TP
@@ -604,12 +642,12 @@ password protected it\(aqll be requested interactively.
Specify additional certificate and private key file.
nghttpx will choose certificates based on the hostname
indicated by client using TLS SNI extension. If nghttpx
is built with OpenSSL >= 1.0.2, signature algorithms
(e.g., ECDSA+SHA256, RSA+SHA256) presented by client are
also taken into consideration. This allows nghttpx to
send ECDSA certificate to modern clients, while sending
RSA based certificate to older clients. This option can
be used multiple times. To make OCSP stapling work,
is built with OpenSSL >= 1.0.2, the shared elliptic
curves (e.g., P\-256) between client and server are also
taken into consideration. This allows nghttpx to send
ECDSA certificate to modern clients, while sending RSA
based certificate to older clients. This option can be
used multiple times. To make OCSP stapling work,
<CERTPATH> must be absolute path.
.sp
Additional parameter can be specified in <PARAM>. The
@@ -672,10 +710,14 @@ done in case\-insensitive manner. The versions between
\fI\%\-\-tls\-min\-proto\-version\fP and \fI\%\-\-tls\-max\-proto\-version\fP are
enabled. If the protocol list advertised by client does
not overlap this range, you will receive the error
message "unknown protocol". The available versions are:
message "unknown protocol". If a protocol version lower
than TLSv1.2 is specified, make sure that the compatible
ciphers are included in \fI\%\-\-ciphers\fP option. The default
cipher list only includes ciphers compatible with
TLSv1.2 or above. The available versions are:
TLSv1.2, TLSv1.1, and TLSv1.0
.sp
Default: \fBTLSv1.1\fP
Default: \fBTLSv1.2\fP
.UNINDENT
.INDENT 0.0
.TP
@@ -805,6 +847,20 @@ Default: \fB4h\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-ocsp\-startup
Start accepting connections after initial attempts to
get OCSP responses finish. It does not matter some of
the attempts fail. This feature is useful if OCSP
responses must be available before accepting
connections.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-no\-verify\-ocsp
nghttpx does not verify OCSP response.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-no\-ocsp
Disable OCSP stapling.
.UNINDENT
@@ -1142,15 +1198,32 @@ $alpn: ALPN identifier of the protocol which generates
the response. For HTTP/1, ALPN is always http/1.1,
regardless of minor version.
.IP \(bu 2
$ssl_cipher: cipher used for SSL/TLS connection.
$tls_cipher: cipher used for SSL/TLS connection.
.IP \(bu 2
$ssl_protocol: protocol for SSL/TLS connection.
$tls_client_fingerprint_sha256: SHA\-256 fingerprint of
client certificate.
.IP \(bu 2
$ssl_session_id: session ID for SSL/TLS connection.
$tls_client_fingerprint_sha1: SHA\-1 fingerprint of
client certificate.
.IP \(bu 2
$ssl_session_reused: "r" if SSL/TLS session was
$tls_client_subject_name: subject name in client
certificate.
.IP \(bu 2
$tls_client_issuer_name: issuer name in client
certificate.
.IP \(bu 2
$tls_client_serial: serial number in client
certificate.
.IP \(bu 2
$tls_protocol: protocol for SSL/TLS connection.
.IP \(bu 2
$tls_session_id: session ID for SSL/TLS connection.
.IP \(bu 2
$tls_session_reused: "r" if SSL/TLS session was
reused. Otherwise, "."
.IP \(bu 2
$tls_sni: SNI server name for SSL/TLS connection.
.IP \(bu 2
$backend_host: backend host used to fulfill the
request. "\-" if backend host is not available.
.IP \(bu 2
@@ -1207,6 +1280,21 @@ requests.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-no\-add\-x\-forwarded\-proto
Don\(aqt append additional X\-Forwarded\-Proto header field
to the backend request. If inbound client sets
X\-Forwarded\-Proto, and
\fI\%\-\-no\-strip\-incoming\-x\-forwarded\-proto\fP option is used,
they are passed to the backend.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-no\-strip\-incoming\-x\-forwarded\-proto
Don\(aqt strip X\-Forwarded\-Proto header field from inbound
client requests.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-add\-forwarded=<LIST>
Append RFC 7239 Forwarded header field with parameters
specified in comma delimited list <LIST>. The supported
@@ -1460,6 +1548,17 @@ Set path to save PID of this program.
Run this program as <USER>. This option is intended to
be used to drop root privileges.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-single\-process
Run this program in a single process mode for debugging
purpose. Without this option, nghttpx creates at least
2 processes: master and worker processes. If this
option is used, master and worker are unified into a
single process. nghttpx still spawns additional process
if neverbleed is used. In the single process mode, the
signal handling feature is disabled.
.UNINDENT
.SS Scripting
.INDENT 0.0
.TP
@@ -1470,7 +1569,9 @@ Set mruby script file
.INDENT 0.0
.TP
.B \-\-conf=<PATH>
Load configuration from <PATH>.
Load configuration from <PATH>. Please note that
nghttpx always tries to read the default configuration
file if \fI\%\-\-conf\fP is not given.
.sp
Default: \fB/etc/nghttpx/nghttpx.conf\fP
.UNINDENT
@@ -1564,7 +1665,7 @@ follows:
.INDENT 7.0
.TP
.B <datetime>
It is a conbination of date and time when the log is written. It
It is a combination of date and time when the log is written. It
is in ISO 8601 format.
.TP
.B <master\-pid>
@@ -1693,6 +1794,22 @@ be customized using \fI\%\-\-fetch\-ocsp\-response\-file\fP option.
.sp
If OCSP query is failed, previous OCSP response, if any, is continued
to be used.
.sp
\fI\%\-\-fetch\-ocsp\-response\-file\fP option provides wide range of
possibility to manage OCSP response. It can take an arbitrary script
or executable. The requirement is that it supports the command\-line
interface of \fBfetch\-ocsp\-response\fP script, and it must return a
valid DER encoded OCSP response on success. It must return exit code
0 on success, and 75 for temporary error, and the other error code for
generic failure. For large cluster of servers, it is not efficient
for each server to perform OCSP query using \fBfetch\-ocsp\-response\fP\&.
Instead, you can retrieve OCSP response in some way, and store it in a
disk or a shared database. Then specify a program in
\fI\%\-\-fetch\-ocsp\-response\-file\fP to fetch it from those stores.
This could provide a way to share the OCSP response between fleet of
servers, and also any OCSP query strategy can be applied which may be
beyond the ability of nghttpx itself or \fBfetch\-ocsp\-response\fP
script.
.SH TLS SESSION RESUMPTION
.sp
nghttpx supports TLS session resumption through both session ID and
@@ -1703,7 +1820,7 @@ By default, session ID is shared by all worker threads.
.sp
If \fI\%\-\-tls\-session\-cache\-memcached\fP is given, nghttpx will
insert serialized session data to memcached with
\fBnghttpx:tls\-session\-cache:\fP + lowercased hex string of session ID
\fBnghttpx:tls\-session\-cache:\fP + lowercase hex string of session ID
as a memcached entry key, with expiry time 12 hours. Session timeout
is set to 12 hours.
.sp
@@ -1785,6 +1902,17 @@ API is subject to change in the future release.
.UNINDENT
.UNINDENT
.sp
\fBWARNING:\fP
.INDENT 0.0
.INDENT 3.5
Almost all string value returned from method, or attribute is a
fresh new mruby string, which involves memory allocation, and
copies. Therefore, it is strongly recommended to store a return
value in a local variable, and use it, instead of calling method or
accessing attribute repeatedly.
.UNINDENT
.UNINDENT
.sp
nghttpx allows users to extend its capability using mruby scripts.
nghttpx has 2 hook points to execute mruby script: request phase and
response phase. The request phase hook is invoked after all request
@@ -1833,7 +1961,7 @@ Return \fI\%Response\fP object.
.TP
.B attribute [R] ctx
Return Ruby hash object. It persists until request finishes.
So values set in request phase hoo can be retrieved in
So values set in request phase hook can be retrieved in
response phase hook.
.UNINDENT
.INDENT 7.0
@@ -1871,6 +1999,56 @@ Return true if TLS is used on the connection.
.B attribute [R] tls_sni
Return the TLS SNI value which client sent in this connection.
.UNINDENT
.INDENT 7.0
.TP
.B attribute [R] tls_client_fingerprint_sha256
Return the SHA\-256 fingerprint of a client certificate.
.UNINDENT
.INDENT 7.0
.TP
.B attribute [R] tls_client_fingerprint_sha1
Return the SHA\-1 fingerprint of a client certificate.
.UNINDENT
.INDENT 7.0
.TP
.B attribute [R] tls_client_issuer_name
Return the issuer name of a client certificate.
.UNINDENT
.INDENT 7.0
.TP
.B attribute [R] tls_client_subject_name
Return the subject name of a client certificate.
.UNINDENT
.INDENT 7.0
.TP
.B attribute [R] tls_client_serial
Return the serial number of a client certificate.
.UNINDENT
.INDENT 7.0
.TP
.B attribute [R] tls_cipher
Return a TLS cipher negotiated in this connection.
.UNINDENT
.INDENT 7.0
.TP
.B attribute [R] tls_protocol
Return a TLS protocol version negotiated in this connection.
.UNINDENT
.INDENT 7.0
.TP
.B attribute [R] tls_session_id
Return a session ID for this connection in hex string.
.UNINDENT
.INDENT 7.0
.TP
.B attribute [R] tls_session_reused
Return true if, and only if a SSL/TLS session is reused.
.UNINDENT
.INDENT 7.0
.TP
.B attribute [R] alpn
Return ALPN identifier negotiated in this connection.
.UNINDENT
.UNINDENT
.INDENT 0.0
.TP
@@ -2148,7 +2326,7 @@ The replacement is done instantly without breaking existing
connections or requests. It also avoids any process creation as is
the case with hot swapping with signals.
.sp
The one limitation is that only numeric IP address is allowd in
The one limitation is that only numeric IP address is allowed in
\fI\%backend\fP in request body unless "dns" parameter
is used while non numeric hostname is allowed in command\-line or
configuration file is read using \fI\%\-\-conf\fP\&.

View File

@@ -57,14 +57,16 @@ Connections
which only lacks trailing '*/*' (e.g., path "*/foo/*"
matches request path "*/foo*"). If it does not end with
"*/*", it performs exact match against the request path.
If host is given, it performs exact match against the
request host. If host alone is given, "*/*" is appended
to it, so that it matches all request paths under the
host (e.g., specifying "nghttp2.org" equals to
"nghttp2.org/"). CONNECT method is treated specially.
It does not have path, and we don't allow empty path.
To workaround this, we assume that CONNECT method has
"*/*" as path.
If host is given, it performs a match against the
request host. For a request received on the frontend
listener with "sni-fwd" parameter enabled, SNI host is
used instead of a request host. If host alone is given,
"*/*" is appended to it, so that it matches all request
paths under the host (e.g., specifying "nghttp2.org"
equals to "nghttp2.org/"). CONNECT method is treated
specially. It does not have path, and we don't allow
empty path. To workaround this, we assume that CONNECT
method has "*/*" as path.
Patterns with host take precedence over patterns with
just path. Then, longer patterns take precedence over
@@ -78,6 +80,18 @@ Connections
match against "nghttp2.org". The exact hosts match
takes precedence over the wildcard hosts match.
If path part ends with "\*", it is treated as wildcard
path. The wildcard path behaves differently from the
normal path. For normal path, match is made around the
boundary of path component separator,"*/*". On the other
hand, the wildcard path does not take into account the
path component separator. All paths which include the
wildcard path without last "\*" as prefix, and are
strictly longer than wildcard path without last "\*" are
matched. "\*" must match at least one character. For
example, the pattern "*/foo\**" matches "*/foo/*" and
"*/foobar*". But it does not match "*/foo*", or "*/fo*".
If <PATTERN> is omitted or empty string, "*/*" is used as
pattern, which matches all request paths (catch-all
pattern). The catch-all backend must be given.
@@ -150,16 +164,32 @@ Connections
The session affinity is enabled using
"affinity=<METHOD>" parameter. If "ip" is given in
<METHOD>, client IP based session affinity is enabled.
If "none" is given in <METHOD>, session affinity is
disabled, and this is the default. The session affinity
is enabled per <PATTERN>. If at least one backend has
"affinity" parameter, and its <METHOD> is not "none",
session affinity is enabled for all backend servers
sharing the same <PATTERN>. It is advised to set
"affinity" parameter to all backend explicitly if
session affinity is desired. The session affinity may
break if one of the backend gets unreachable, or backend
settings are reloaded or replaced by API.
If "cookie" is given in <METHOD>, cookie based session
affinity is enabled. If "none" is given in <METHOD>,
session affinity is disabled, and this is the default.
The session affinity is enabled per <PATTERN>. If at
least one backend has "affinity" parameter, and its
<METHOD> is not "none", session affinity is enabled for
all backend servers sharing the same <PATTERN>. It is
advised to set "affinity" parameter to all backend
explicitly if session affinity is desired. The session
affinity may break if one of the backend gets
unreachable, or backend settings are reloaded or
replaced by API.
If "affinity=cookie" is used, the additional
configuration is required.
"affinity-cookie-name=<NAME>" must be used to specify a
name of cookie to use. Optionally,
"affinity-cookie-path=<PATH>" can be used to specify a
path which cookie is applied. The optional
"affinity-cookie-secure=<SECURE>" controls the Secure
attribute of a cookie. The default value is "auto", and
the Secure attribute is determined by a request scheme.
If a request scheme is "https", then Secure attribute is
set. Otherwise, it is not set. If <SECURE> is "yes",
the Secure attribute is always set. If <SECURE> is
"no", the Secure attribute is always omitted.
By default, name resolution of backend host name is done
at start up, or reloading configuration. If "dns"
@@ -205,6 +235,11 @@ Connections
Optionally, TLS can be disabled by specifying "no-tls"
parameter. TLS is enabled by default.
If "sni-fwd" parameter is used, when performing a match
to select a backend server, SNI host name received from
the client is used instead of the request host. See
:option:`--backend` option about the pattern match.
To make this frontend as API endpoint, specify "api"
parameter. This is disabled by default. It is
important to limit the access to the API frontend.
@@ -539,11 +574,14 @@ SSL/TLS
.. option:: --cacert=<PATH>
Set path to trusted CA certificate file used in backend
TLS connections. The file must be in PEM format. It
can contain multiple certificates. If the linked
OpenSSL is configured to load system wide certificates,
they are loaded at startup regardless of this option.
Set path to trusted CA certificate file. It is used in
backend TLS connections to verify peer's certificate.
It is also used to verify OCSP response from the script
set by :option:`--fetch-ocsp-response-file`\. The file must be in
PEM format. It can contain multiple certificates. If
the linked OpenSSL is configured to load system wide
certificates, they are loaded at startup regardless of
this option.
.. option:: --private-key-passwd-file=<PATH>
@@ -556,12 +594,12 @@ SSL/TLS
Specify additional certificate and private key file.
nghttpx will choose certificates based on the hostname
indicated by client using TLS SNI extension. If nghttpx
is built with OpenSSL >= 1.0.2, signature algorithms
(e.g., ECDSA+SHA256, RSA+SHA256) presented by client are
also taken into consideration. This allows nghttpx to
send ECDSA certificate to modern clients, while sending
RSA based certificate to older clients. This option can
be used multiple times. To make OCSP stapling work,
is built with OpenSSL >= 1.0.2, the shared elliptic
curves (e.g., P-256) between client and server are also
taken into consideration. This allows nghttpx to send
ECDSA certificate to modern clients, while sending RSA
based certificate to older clients. This option can be
used multiple times. To make OCSP stapling work,
<CERTPATH> must be absolute path.
Additional parameter can be specified in <PARAM>. The
@@ -617,10 +655,14 @@ SSL/TLS
:option:`--tls-min-proto-version` and :option:`\--tls-max-proto-version` are
enabled. If the protocol list advertised by client does
not overlap this range, you will receive the error
message "unknown protocol". The available versions are:
message "unknown protocol". If a protocol version lower
than TLSv1.2 is specified, make sure that the compatible
ciphers are included in :option:`--ciphers` option. The default
cipher list only includes ciphers compatible with
TLSv1.2 or above. The available versions are:
TLSv1.2, TLSv1.1, and TLSv1.0
Default: ``TLSv1.1``
Default: ``TLSv1.2``
.. option:: --tls-max-proto-version=<VER>
@@ -736,6 +778,18 @@ SSL/TLS
Default: ``4h``
.. option:: --ocsp-startup
Start accepting connections after initial attempts to
get OCSP responses finish. It does not matter some of
the attempts fail. This feature is useful if OCSP
responses must be available before accepting
connections.
.. option:: --no-verify-ocsp
nghttpx does not verify OCSP response.
.. option:: --no-ocsp
Disable OCSP stapling.
@@ -1039,11 +1093,22 @@ Logging
* $alpn: ALPN identifier of the protocol which generates
the response. For HTTP/1, ALPN is always http/1.1,
regardless of minor version.
* $ssl_cipher: cipher used for SSL/TLS connection.
* $ssl_protocol: protocol for SSL/TLS connection.
* $ssl_session_id: session ID for SSL/TLS connection.
* $ssl_session_reused: "r" if SSL/TLS session was
* $tls_cipher: cipher used for SSL/TLS connection.
* $tls_client_fingerprint_sha256: SHA-256 fingerprint of
client certificate.
* $tls_client_fingerprint_sha1: SHA-1 fingerprint of
client certificate.
* $tls_client_subject_name: subject name in client
certificate.
* $tls_client_issuer_name: issuer name in client
certificate.
* $tls_client_serial: serial number in client
certificate.
* $tls_protocol: protocol for SSL/TLS connection.
* $tls_session_id: session ID for SSL/TLS connection.
* $tls_session_reused: "r" if SSL/TLS session was
reused. Otherwise, "."
* $tls_sni: SNI server name for SSL/TLS connection.
* $backend_host: backend host used to fulfill the
request. "-" if backend host is not available.
* $backend_port: backend port used to fulfill the
@@ -1094,6 +1159,19 @@ HTTP
Strip X-Forwarded-For header field from inbound client
requests.
.. option:: --no-add-x-forwarded-proto
Don't append additional X-Forwarded-Proto header field
to the backend request. If inbound client sets
X-Forwarded-Proto, and
:option:`--no-strip-incoming-x-forwarded-proto` option is used,
they are passed to the backend.
.. option:: --no-strip-incoming-x-forwarded-proto
Don't strip X-Forwarded-Proto header field from inbound
client requests.
.. option:: --add-forwarded=<LIST>
Append RFC 7239 Forwarded header field with parameters
@@ -1332,6 +1410,16 @@ Process
Run this program as <USER>. This option is intended to
be used to drop root privileges.
.. option:: --single-process
Run this program in a single process mode for debugging
purpose. Without this option, nghttpx creates at least
2 processes: master and worker processes. If this
option is used, master and worker are unified into a
single process. nghttpx still spawns additional process
if neverbleed is used. In the single process mode, the
signal handling feature is disabled.
Scripting
~~~~~~~~~
@@ -1346,7 +1434,9 @@ Misc
.. option:: --conf=<PATH>
Load configuration from <PATH>.
Load configuration from <PATH>. Please note that
nghttpx always tries to read the default configuration
file if :option:`--conf` is not given.
Default: ``/etc/nghttpx/nghttpx.conf``
@@ -1426,7 +1516,7 @@ Error log
<datetime> <master-pid> <current-pid> <thread-id> <level> (<filename>:<line>) <msg>
<datetime>
It is a conbination of date and time when the log is written. It
It is a combination of date and time when the log is written. It
is in ISO 8601 format.
<master-pid>
@@ -1548,6 +1638,22 @@ be customized using :option:`--fetch-ocsp-response-file` option.
If OCSP query is failed, previous OCSP response, if any, is continued
to be used.
:option:`--fetch-ocsp-response-file` option provides wide range of
possibility to manage OCSP response. It can take an arbitrary script
or executable. The requirement is that it supports the command-line
interface of ``fetch-ocsp-response`` script, and it must return a
valid DER encoded OCSP response on success. It must return exit code
0 on success, and 75 for temporary error, and the other error code for
generic failure. For large cluster of servers, it is not efficient
for each server to perform OCSP query using ``fetch-ocsp-response``.
Instead, you can retrieve OCSP response in some way, and store it in a
disk or a shared database. Then specify a program in
:option:`--fetch-ocsp-response-file` to fetch it from those stores.
This could provide a way to share the OCSP response between fleet of
servers, and also any OCSP query strategy can be applied which may be
beyond the ability of nghttpx itself or ``fetch-ocsp-response``
script.
TLS SESSION RESUMPTION
----------------------
@@ -1561,7 +1667,7 @@ By default, session ID is shared by all worker threads.
If :option:`--tls-session-cache-memcached` is given, nghttpx will
insert serialized session data to memcached with
``nghttpx:tls-session-cache:`` + lowercased hex string of session ID
``nghttpx:tls-session-cache:`` + lowercase hex string of session ID
as a memcached entry key, with expiry time 12 hours. Session timeout
is set to 12 hours.
@@ -1643,6 +1749,14 @@ MRUBY SCRIPTING
The current mruby extension API is experimental and not frozen. The
API is subject to change in the future release.
.. warning::
Almost all string value returned from method, or attribute is a
fresh new mruby string, which involves memory allocation, and
copies. Therefore, it is strongly recommended to store a return
value in a local variable, and use it, instead of calling method or
accessing attribute repeatedly.
nghttpx allows users to extend its capability using mruby scripts.
nghttpx has 2 hook points to execute mruby script: request phase and
response phase. The request phase hook is invoked after all request
@@ -1689,7 +1803,7 @@ respectively.
.. rb:attr_reader:: ctx
Return Ruby hash object. It persists until request finishes.
So values set in request phase hoo can be retrieved in
So values set in request phase hook can be retrieved in
response phase hook.
.. rb:attr_reader:: phase
@@ -1721,6 +1835,46 @@ respectively.
Return the TLS SNI value which client sent in this connection.
.. rb:attr_reader:: tls_client_fingerprint_sha256
Return the SHA-256 fingerprint of a client certificate.
.. rb:attr_reader:: tls_client_fingerprint_sha1
Return the SHA-1 fingerprint of a client certificate.
.. rb:attr_reader:: tls_client_issuer_name
Return the issuer name of a client certificate.
.. rb:attr_reader:: tls_client_subject_name
Return the subject name of a client certificate.
.. rb:attr_reader:: tls_client_serial
Return the serial number of a client certificate.
.. rb:attr_reader:: tls_cipher
Return a TLS cipher negotiated in this connection.
.. rb:attr_reader:: tls_protocol
Return a TLS protocol version negotiated in this connection.
.. rb:attr_reader:: tls_session_id
Return a session ID for this connection in hex string.
.. rb:attr_reader:: tls_session_reused
Return true if, and only if a SSL/TLS session is reused.
.. rb:attr_reader:: alpn
Return ALPN identifier negotiated in this connection.
.. rb:class:: Request
Object to represent request from client. The modification to
@@ -1965,7 +2119,7 @@ The replacement is done instantly without breaking existing
connections or requests. It also avoids any process creation as is
the case with hot swapping with signals.
The one limitation is that only numeric IP address is allowd in
The one limitation is that only numeric IP address is allowed in
:option:`backend <--backend>` in request body unless "dns" parameter
is used while non numeric hostname is allowed in command-line or
configuration file is read using :option:`--conf`.

View File

@@ -49,7 +49,7 @@ Error log
<datetime> <master-pid> <current-pid> <thread-id> <level> (<filename>:<line>) <msg>
<datetime>
It is a conbination of date and time when the log is written. It
It is a combination of date and time when the log is written. It
is in ISO 8601 format.
<master-pid>
@@ -171,6 +171,22 @@ be customized using :option:`--fetch-ocsp-response-file` option.
If OCSP query is failed, previous OCSP response, if any, is continued
to be used.
:option:`--fetch-ocsp-response-file` option provides wide range of
possibility to manage OCSP response. It can take an arbitrary script
or executable. The requirement is that it supports the command-line
interface of ``fetch-ocsp-response`` script, and it must return a
valid DER encoded OCSP response on success. It must return exit code
0 on success, and 75 for temporary error, and the other error code for
generic failure. For large cluster of servers, it is not efficient
for each server to perform OCSP query using ``fetch-ocsp-response``.
Instead, you can retrieve OCSP response in some way, and store it in a
disk or a shared database. Then specify a program in
:option:`--fetch-ocsp-response-file` to fetch it from those stores.
This could provide a way to share the OCSP response between fleet of
servers, and also any OCSP query strategy can be applied which may be
beyond the ability of nghttpx itself or ``fetch-ocsp-response``
script.
TLS SESSION RESUMPTION
----------------------
@@ -184,7 +200,7 @@ By default, session ID is shared by all worker threads.
If :option:`--tls-session-cache-memcached` is given, nghttpx will
insert serialized session data to memcached with
``nghttpx:tls-session-cache:`` + lowercased hex string of session ID
``nghttpx:tls-session-cache:`` + lowercase hex string of session ID
as a memcached entry key, with expiry time 12 hours. Session timeout
is set to 12 hours.
@@ -266,6 +282,14 @@ MRUBY SCRIPTING
The current mruby extension API is experimental and not frozen. The
API is subject to change in the future release.
.. warning::
Almost all string value returned from method, or attribute is a
fresh new mruby string, which involves memory allocation, and
copies. Therefore, it is strongly recommended to store a return
value in a local variable, and use it, instead of calling method or
accessing attribute repeatedly.
nghttpx allows users to extend its capability using mruby scripts.
nghttpx has 2 hook points to execute mruby script: request phase and
response phase. The request phase hook is invoked after all request
@@ -312,7 +336,7 @@ respectively.
.. rb:attr_reader:: ctx
Return Ruby hash object. It persists until request finishes.
So values set in request phase hoo can be retrieved in
So values set in request phase hook can be retrieved in
response phase hook.
.. rb:attr_reader:: phase
@@ -344,6 +368,46 @@ respectively.
Return the TLS SNI value which client sent in this connection.
.. rb:attr_reader:: tls_client_fingerprint_sha256
Return the SHA-256 fingerprint of a client certificate.
.. rb:attr_reader:: tls_client_fingerprint_sha1
Return the SHA-1 fingerprint of a client certificate.
.. rb:attr_reader:: tls_client_issuer_name
Return the issuer name of a client certificate.
.. rb:attr_reader:: tls_client_subject_name
Return the subject name of a client certificate.
.. rb:attr_reader:: tls_client_serial
Return the serial number of a client certificate.
.. rb:attr_reader:: tls_cipher
Return a TLS cipher negotiated in this connection.
.. rb:attr_reader:: tls_protocol
Return a TLS protocol version negotiated in this connection.
.. rb:attr_reader:: tls_session_id
Return a session ID for this connection in hex string.
.. rb:attr_reader:: tls_session_reused
Return true if, and only if a SSL/TLS session is reused.
.. rb:attr_reader:: alpn
Return ALPN identifier negotiated in this connection.
.. rb:class:: Request
Object to represent request from client. The modification to
@@ -588,7 +652,7 @@ The replacement is done instantly without breaking existing
connections or requests. It also avoids any process creation as is
the case with hot swapping with signals.
The one limitation is that only numeric IP address is allowd in
The one limitation is that only numeric IP address is allowed in
:option:`backend <--backend>` in request body unless "dns" parameter
is used while non numeric hostname is allowed in command-line or
configuration file is read using :option:`--conf`.

View File

@@ -116,7 +116,10 @@ briefly describe what the library does in this area. In the following
description, without loss of generality we omit CONTINUATION frame
since they must follow HEADERS frame and are processed atomically. In
other words, they are just one big HEADERS frame. To disable these
validations, use `nghttp2_option_set_no_http_messaging()`.
validations, use `nghttp2_option_set_no_http_messaging()`. Please
note that disabling this feature does not change the fundamental
client and server model of HTTP. That is, even if the validation is
disabled, only client can send requests.
For HTTP request, including those carried by PUSH_PROMISE, HTTP
message starts with one HEADERS frame containing request headers. It
@@ -149,13 +152,11 @@ header fields must not appear: "Connection", "Keep-Alive",
Each header field name and value must obey the field-name and
field-value production rules described in `RFC 7230, section
3.2. <https://tools.ietf.org/html/rfc7230#section-3.2>`_.
Additionally, all field name must be lower cased. While the pseudo
header fields must satisfy these rules, we just ignore illegal regular
headers (this means that these header fields are not passed to
application callback). This is because these illegal header fields
are floating around in existing internet and resetting stream just
because of this may break many web sites. This is especially true if
we forward to or translate from HTTP/1 traffic.
Additionally, all field name must be lower cased. The invalid header
fields are treated as stream error, and that stream is reset. If
application wants to treat these headers in their own way, use
`nghttp2_on_invalid_header_callback
<https://nghttp2.org/documentation/types.html#c.nghttp2_on_invalid_header_callback>`_.
For "http" or "https" URIs, ":path" pseudo header fields must start
with "/". The only exception is OPTIONS request, in that case, "*" is

View File

@@ -2,7 +2,7 @@ Building Android binary
=======================
In this article, we briefly describe how to build Android binary using
`Android NDK <http://developer.android.com/tools/sdk/ndk/index.html>`_
`Android NDK <https://developer.android.com/ndk/index.html>`_
cross-compiler on Debian Linux.
The easiest way to build android binary is use Dockerfile.android.

View File

@@ -26,8 +26,7 @@ Coding style
We use clang-format to format source code consistently. The
clang-format configuration file .clang-format is located at the root
directory. Since clang-format produces slightly different results
between versions, we currently use clang-format which comes with
clang-3.9.
between versions, we currently use clang-format-5.0.
To detect any violation to the coding style, we recommend to setup git
pre-commit hook to check coding style of the changes you introduced.
@@ -35,7 +34,7 @@ The pre-commit file is located at the root directory. Copy it under
.git/hooks and make sure that it is executable. The pre-commit script
uses clang-format-diff.py to detect any style errors. If it is not in
your PATH or it exists under different name (e.g.,
clang-format-diff-3.9 in debian), either add it to PATH variable or
clang-format-diff-5.0 in debian), either add it to PATH variable or
add git option ``clangformatdiff.binary`` to point to the script.
For emacs users, integrating clang-format to emacs is very easy.

View File

@@ -12,7 +12,7 @@ Compiling from source
---------------------
h2load is compiled alongside nghttp2 and requires that the
``--enable-apps`` flag is passed to ``./configure`` and `required
``--enable-app`` flag is passed to ``./configure`` and `required
dependencies <https://github.com/nghttp2/nghttp2#requirements>`_ are
available during compilation. For details on compiling, see `nghttp2:
Building from Git
@@ -64,6 +64,25 @@ The benchmarking result looks like this:
See the h2load manual page :ref:`h2load-1-output` section for the
explanation of the above numbers.
Timing-based load-testing
-------------------------
As of v1.26.0, h2load supports timing-based load-testing. This method
performs load-testing in terms of a given duration instead of a
pre-defined number of requests. The new option :option:`--duration`
specifies how long the load-testing takes. For example,
``--duration=10`` makes h2load perform load-testing against a server
for 10 seconds. You can also specify a “warming-up” period with
:option:`--warm-up-time`. If :option:`--duration` is used,
:option:`-n` option is ignored.
The following command performs load-testing for 10 seconds after 5
seconds warming up period:
.. code-block:: text
$ h2load -c100 -m100 --duration=10 --warm-up-time=5 https://localhost
Flow Control
------------

View File

@@ -290,7 +290,7 @@ Normally, client does not stop even after all requests are done unless
connection is lost. To stop client, call
``nghttp2::asio_http2::server::session::shutdown()``.
Recieve server push and enable SSL/TLS
Receive server push and enable SSL/TLS
++++++++++++++++++++++++++++++++++++++
.. code-block:: cpp

View File

@@ -26,7 +26,7 @@ protocol selection will be done via ALPN or NPN.
To turn off encryption on frontend connection, use ``no-tls`` keyword
in :option:`--frontend` option. In this case, SPDY protocol is not
available even if spdylay library is liked to nghttpx. HTTP/2 and
available even if spdylay library is linked to nghttpx. HTTP/2 and
HTTP/1 are available on 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 preface is also supported.
@@ -45,17 +45,17 @@ that default backend protocol is HTTP/1.1. To use HTTP/2 in backend,
you have to specify ``h2`` in ``proto`` keyword in :option:`--backend`
explicitly.
The backend is supposed to be Web server. For example, to make
The backend is supposed to be a Web server. For example, to make
nghttpx listen to encrypted HTTP/2 requests at port 8443, and a
backend Web server is configured to listen to HTTP request at port
8080 in the same host, run nghttpx command-line like this:
backend Web server is configured to listen to HTTP requests at port
8080 on the same host, run nghttpx command-line like this:
.. code-block:: text
$ nghttpx -f0.0.0.0,8443 -b127.0.0.1,8080 /path/to/server.key /path/to/server.crt
Then HTTP/2 enabled client can access to the nghttpx in HTTP/2. For
example, you can send GET request to the server using nghttp:
Then an HTTP/2 enabled client can access the nghttpx server using HTTP/2. For
example, you can send a GET request using nghttp:
.. code-block:: text
@@ -66,19 +66,19 @@ HTTP/2 proxy mode
If nghttpx is invoked with :option:`--http2-proxy` (or its shorthand
:option:`-s`) option, it operates in HTTP/2 proxy mode. The supported
protocols in frontend and backend connections are the same in `default
mode`_. The difference is that this mode acts like forward proxy and
assumes the backend is HTTP proxy server (e.g., Squid, Apache Traffic
Server). HTTP/1 request must include absolute URI in request line.
protocols in frontend and backend connections are the same as in `default
mode`_. The difference is that this mode acts like a forward proxy and
assumes the backend is an HTTP proxy server (e.g., Squid, Apache Traffic
Server). HTTP/1 requests must include an absolute URI in request line.
By default, frontend connection is encrypted. So this mode is also
By default, the frontend connection is encrypted. So this mode is also
called secure proxy. If nghttpx is linked with spdylay, it supports
SPDY protocols and it works as so called SPDY proxy.
To turn off encryption on frontend connection, use ``no-tls`` keyword
To turn off encryption on the frontend connection, use ``no-tls`` keyword
in :option:`--frontend` option.
The backend must be HTTP proxy server. nghttpx supports multiple
The backend must be an HTTP proxy server. nghttpx supports multiple
backend server addresses. It translates incoming requests to HTTP
request to backend server. The backend server performs real proxy
work for each request, for example, dispatching requests to the origin
@@ -92,7 +92,7 @@ connection, use :option:`--backend` option, and specify ``h2`` in
For example, to make nghttpx listen to encrypted HTTP/2 requests at
port 8443, and a backend HTTP proxy server is configured to listen to
HTTP/1 request at port 8080 in the same host, run nghttpx command-line
HTTP/1 requests at port 8080 on the same host, run nghttpx command-line
like this:
.. code-block:: text
@@ -297,13 +297,31 @@ When you write this option in command-line, you should enclose
argument with single or double quotes, since the character ``;`` has a
special meaning in shell.
To route, request to request path whose prefix is ``/foo`` to backend
server ``[::1]:8080``, you can write like so:
To route, request to request path ``/foo`` to backend server
``[::1]:8080``, you can write like so:
.. code-block:: text
backend=::1,8080;/foo
If the last character of path pattern is ``/``, all request paths
which start with that pattern match:
.. code-block:: text
backend=::1,8080;/bar/
The request path ``/bar/buzz`` matches the ``/bar/``.
You can use ``*`` at the end of the path pattern to make it wildcard
pattern. ``*`` must match at least one character:
.. code-block:: text
backend=::1,8080;/sample*
The request path ``/sample1/foo`` matches the ``/sample*`` pattern.
Of course, you can specify both host and request path at the same
time:
@@ -371,7 +389,7 @@ parameter in :option:`--backend` option, like so:
.. code-block:: text
backend=foo.example.com;;dns
backend=foo.example.com,80;;dns
nghttpx will cache resolved addresses for certain period of time. To
change this cache period, use :option:`--dns-cache-timeout`.
@@ -387,6 +405,28 @@ like so:
frontend=*,443;proxyproto
Session affinity
----------------
Two kinds of session affinity are available: client IP, and HTTP
Cookie.
To enable client IP based affinity, specify ``affinity=ip`` parameter
in :option:`--backend` option. If PROXY protocol is enabled, then an
address obtained from PROXY protocol is taken into consideration.
To enable HTTP Cookie based affinity, specify ``affinity=cookie``
parameter, and specify a name of cookie in ``affinity-cookie-name``
parameter. Optionally, a Path attribute can be specified in
``affinity-cookie-path`` parameter:
.. code-block:: text
backend=127.0.0.1,3000;;affinity=cookie;affinity-cookie-name=nghttpxlb;affinity-cookie-path=/
Secure attribute of cookie is set if client connection is protected by
TLS.
PSK cipher suites
-----------------

View File

@@ -124,6 +124,7 @@ remote server. It's defined as::
bev = bufferevent_openssl_socket_new(
evbase, -1, ssl, BUFFEREVENT_SSL_CONNECTING,
BEV_OPT_DEFER_CALLBACKS | BEV_OPT_CLOSE_ON_FREE);
bufferevent_enable(bev, EV_READ | EV_WRITE);
bufferevent_setcb(bev, readcb, writecb, eventcb, session_data);
rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase,
AF_UNSPEC, host, port);

View File

@@ -7,11 +7,8 @@ if(ENABLE_EXAMPLES)
COMPILE_FLAGS "${WARNCXXFLAGS} ${CXX1XCXXFLAGS}")
include_directories(
${CMAKE_SOURCE_DIR}
${CMAKE_SOURCE_DIR}/lib/includes
${CMAKE_BINARY_DIR}/lib/includes
${CMAKE_SOURCE_DIR}/src/includes
${CMAKE_SOURCE_DIR}/third-party
${CMAKE_CURRENT_SOURCE_DIR}
"${CMAKE_CURRENT_SOURCE_DIR}/../third-party"
${LIBEVENT_INCLUDE_DIRS}
${OPENSSL_INCLUDE_DIRS}

View File

@@ -62,11 +62,11 @@ ASIOCPPFLAGS = ${AM_CPPFLAGS} ${BOOST_CPPFLAGS}
ASIOLDADD = $(top_builddir)/lib/libnghttp2.la \
$(top_builddir)/src/libnghttp2_asio.la @JEMALLOC_LIBS@ \
$(top_builddir)/third-party/libhttp-parser.la \
@OPENSSL_LIBS@ \
${BOOST_LDFLAGS} \
${BOOST_ASIO_LIB} \
${BOOST_THREAD_LIB} \
${BOOST_SYSTEM_LIB} \
@OPENSSL_LIBS@ \
@APPLDFLAGS@
asio_sv_SOURCES = asio-sv.cc

View File

@@ -548,6 +548,7 @@ static void initiate_connection(struct event_base *evbase, SSL_CTX *ssl_ctx,
bev = bufferevent_openssl_socket_new(
evbase, -1, ssl, BUFFEREVENT_SSL_CONNECTING,
BEV_OPT_DEFER_CALLBACKS | BEV_OPT_CLOSE_ON_FREE);
bufferevent_enable(bev, EV_READ | EV_WRITE);
bufferevent_setcb(bev, readcb, writecb, eventcb, session_data);
rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase,
AF_UNSPEC, host, port);

View File

@@ -250,6 +250,7 @@ static http2_session_data *create_http2_session_data(app_context *app_ctx,
session_data->bev = bufferevent_openssl_socket_new(
app_ctx->evbase, fd, ssl, BUFFEREVENT_SSL_ACCEPTING,
BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
bufferevent_enable(session_data->bev, EV_READ | EV_WRITE);
rv = getnameinfo(addr, (socklen_t)addrlen, host, sizeof(host), NULL, 0,
NI_NUMERICHOST);
if (rv != 0) {

View File

@@ -23,8 +23,8 @@ following compiler/linker flags:
.. code-block:: text
CPPFLAGS="-fsanitize-coverage=edge -fsanitize=addres"
LDFLAGS="-fsanitize-coverage=edge -fsanitize=addres"
CPPFLAGS="-fsanitize-coverage=edge -fsanitize=address"
LDFLAGS="-fsanitize-coverage=edge -fsanitize=address"
Then, fuzz_target.cc can be built using the following command:

View File

@@ -31,6 +31,7 @@ HEADERS = [
"user-agent",
"date",
"content-type",
"nghttpx-0rtt-uniq",
# disallowed h1 headers
'connection',
'keep-alive',

View File

@@ -163,6 +163,16 @@ OPTIONS = [
"redirect-https-port",
"frontend-max-requests",
"single-thread",
"single-process",
"no-add-x-forwarded-proto",
"no-strip-incoming-x-forwarded-proto",
"ocsp-startup",
"no-verify-ocsp",
"tls-anti-replay-memcached",
"tls-anti-replay-memcached-cert-file",
"tls-anti-replay-memcached-private-key-file",
"tls-anti-replay-memcached-address-family",
"no-strip-incoming-nghttpx-0rtt-uniq",
]
LOGVARS = [
@@ -181,6 +191,16 @@ LOGVARS = [
"ssl_protocol",
"ssl_session_id",
"ssl_session_reused",
"tls_cipher",
"tls_protocol",
"tls_session_id",
"tls_session_reused",
"tls_sni",
"tls_client_fingerprint_sha256",
"tls_client_fingerprint_sha1",
"tls_client_subject_name",
"tls_client_issuer_name",
"tls_client_serial",
"backend_host",
"backend_port",
]

View File

@@ -9,6 +9,7 @@ import (
"golang.org/x/net/websocket"
"io"
"net/http"
"regexp"
"syscall"
"testing"
"time"
@@ -125,6 +126,54 @@ Content-Length: 0
// }
// }
// TestH1H1AffinityCookie tests that affinity cookie is sent back in
// cleartext http.
func TestH1H1AffinityCookie(t *testing.T) {
st := newServerTester([]string{"--affinity-cookie"}, t, noopHandler)
defer st.Close()
res, err := st.http1(requestParam{
name: "TestH1H1AffinityCookie",
})
if err != nil {
t.Fatalf("Error st.http1() = %v", err)
}
if got, want := res.status, 200; got != want {
t.Errorf("status = %v; want %v", got, want)
}
const pattern = `affinity=[0-9a-f]{8}; Path=/foo/bar`
validCookie := regexp.MustCompile(pattern)
if got := res.header.Get("Set-Cookie"); !validCookie.MatchString(got) {
t.Errorf("Set-Cookie: %v; want pattern %v", got, pattern)
}
}
// TestH1H1AffinityCookieTLS tests that affinity cookie is sent back
// in https.
func TestH1H1AffinityCookieTLS(t *testing.T) {
st := newServerTesterTLS([]string{"--alpn-h1", "--affinity-cookie"}, t, noopHandler)
defer st.Close()
res, err := st.http1(requestParam{
name: "TestH1H1AffinityCookieTLS",
})
if err != nil {
t.Fatalf("Error st.http1() = %v", err)
}
if got, want := res.status, 200; got != want {
t.Errorf("status = %v; want %v", got, want)
}
const pattern = `affinity=[0-9a-f]{8}; Path=/foo/bar; Secure`
validCookie := regexp.MustCompile(pattern)
if got := res.header.Get("Set-Cookie"); !validCookie.MatchString(got) {
t.Errorf("Set-Cookie: %v; want pattern %v", got, pattern)
}
}
// TestH1H1GracefulShutdown tests graceful shutdown.
func TestH1H1GracefulShutdown(t *testing.T) {
st := newServerTester(nil, t, noopHandler)
@@ -162,7 +211,7 @@ func TestH1H1GracefulShutdown(t *testing.T) {
want := io.EOF
b := make([]byte, 256)
if _, err := st.conn.Read(b); err == nil || err != want {
t.Errorf("st.conn.Read(): %v; want %v, %v", err, want)
t.Errorf("st.conn.Read(): %v; want %v", err, want)
}
}

View File

@@ -35,6 +35,105 @@ func TestH2H1PlainGET(t *testing.T) {
}
}
// TestH2H1AddXfp tests that server appends :scheme to the existing
// x-forwarded-proto header field.
func TestH2H1AddXfp(t *testing.T) {
st := newServerTester([]string{"--no-strip-incoming-x-forwarded-proto"}, t, func(w http.ResponseWriter, r *http.Request) {
xfp := r.Header.Get("X-Forwarded-Proto")
if got, want := xfp, "foo, http"; got != want {
t.Errorf("X-Forwarded-Proto = %q; want %q", got, want)
}
})
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H1AddXfp",
header: []hpack.HeaderField{
pair("x-forwarded-proto", "foo"),
},
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
if got, want := res.status, 200; got != want {
t.Errorf("status = %v; want %v", got, want)
}
}
// TestH2H1NoAddXfp tests that server does not append :scheme to the
// existing x-forwarded-proto header field.
func TestH2H1NoAddXfp(t *testing.T) {
st := newServerTester([]string{"--no-add-x-forwarded-proto", "--no-strip-incoming-x-forwarded-proto"}, t, func(w http.ResponseWriter, r *http.Request) {
xfp := r.Header.Get("X-Forwarded-Proto")
if got, want := xfp, "foo"; got != want {
t.Errorf("X-Forwarded-Proto = %q; want %q", got, want)
}
})
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H1NoAddXfp",
header: []hpack.HeaderField{
pair("x-forwarded-proto", "foo"),
},
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
if got, want := res.status, 200; got != want {
t.Errorf("status = %v; want %v", got, want)
}
}
// TestH2H1StripXfp tests that server strips incoming
// x-forwarded-proto header field.
func TestH2H1StripXfp(t *testing.T) {
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
xfp := r.Header.Get("X-Forwarded-Proto")
if got, want := xfp, "http"; got != want {
t.Errorf("X-Forwarded-Proto = %q; want %q", got, want)
}
})
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H1StripXfp",
header: []hpack.HeaderField{
pair("x-forwarded-proto", "foo"),
},
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
if got, want := res.status, 200; got != want {
t.Errorf("status = %v; want %v", got, want)
}
}
// TestH2H1StripNoAddXfp tests that server strips incoming
// x-forwarded-proto header field, and does not add another.
func TestH2H1StripNoAddXfp(t *testing.T) {
st := newServerTester([]string{"--no-add-x-forwarded-proto"}, t, func(w http.ResponseWriter, r *http.Request) {
if got, found := r.Header["X-Forwarded-Proto"]; found {
t.Errorf("X-Forwarded-Proto = %q; want nothing", got)
}
})
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H1StripNoAddXfp",
header: []hpack.HeaderField{
pair("x-forwarded-proto", "foo"),
},
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
if got, want := res.status, 200; got != want {
t.Errorf("status = %v; want %v", got, want)
}
}
// TestH2H1AddXff tests that server generates X-Forwarded-For header
// field when forwarding request to backend.
func TestH2H1AddXff(t *testing.T) {
@@ -1014,14 +1113,45 @@ func TestH2H1Upgrade(t *testing.T) {
}
}
// TestH2H1ProxyProtocolV1ForwardedForObfuscated tests that Forwarded
// header field includes obfuscated address even if PROXY protocol
// version 1 containing TCP4 entry is accepted.
func TestH2H1ProxyProtocolV1ForwardedForObfuscated(t *testing.T) {
pattern := fmt.Sprintf(`^for=_[^;]+$`)
validFwd := regexp.MustCompile(pattern)
st := newServerTester([]string{"--accept-proxy-protocol", "--add-x-forwarded-for", "--add-forwarded=for", "--forwarded-for=obfuscated"}, t, func(w http.ResponseWriter, r *http.Request) {
if got := r.Header.Get("Forwarded"); !validFwd.MatchString(got) {
t.Errorf("Forwarded: %v; want pattern %v", got, pattern)
}
})
defer st.Close()
st.conn.Write([]byte("PROXY TCP4 192.168.0.2 192.168.0.100 12345 8080\r\n"))
res, err := st.http2(requestParam{
name: "TestH2H1ProxyProtocolV1ForwardedForObfuscated",
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
if got, want := res.status, 200; got != want {
t.Errorf("res.status: %v; want %v", got, want)
}
}
// TestH2H1ProxyProtocolV1TCP4 tests PROXY protocol version 1
// containing TCP4 entry is accepted and X-Forwarded-For contains
// advertised src address.
func TestH2H1ProxyProtocolV1TCP4(t *testing.T) {
st := newServerTester([]string{"--accept-proxy-protocol", "--add-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) {
st := newServerTester([]string{"--accept-proxy-protocol", "--add-x-forwarded-for", "--add-forwarded=for", "--forwarded-for=ip"}, t, func(w http.ResponseWriter, r *http.Request) {
if got, want := r.Header.Get("X-Forwarded-For"), "192.168.0.2"; got != want {
t.Errorf("X-Forwarded-For: %v; want %v", got, want)
}
if got, want := r.Header.Get("Forwarded"), "for=192.168.0.2"; got != want {
t.Errorf("Forwarded: %v; want %v", got, want)
}
})
defer st.Close()
@@ -1044,10 +1174,13 @@ func TestH2H1ProxyProtocolV1TCP4(t *testing.T) {
// containing TCP6 entry is accepted and X-Forwarded-For contains
// advertised src address.
func TestH2H1ProxyProtocolV1TCP6(t *testing.T) {
st := newServerTester([]string{"--accept-proxy-protocol", "--add-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) {
st := newServerTester([]string{"--accept-proxy-protocol", "--add-x-forwarded-for", "--add-forwarded=for", "--forwarded-for=ip"}, t, func(w http.ResponseWriter, r *http.Request) {
if got, want := r.Header.Get("X-Forwarded-For"), "2001:0db8:85a3:0000:0000:8a2e:0370:7334"; got != want {
t.Errorf("X-Forwarded-For: %v; want %v", got, want)
}
if got, want := r.Header.Get("Forwarded"), `for="[2001:0db8:85a3:0000:0000:8a2e:0370:7334]"`; got != want {
t.Errorf("Forwarded: %v; want %v", got, want)
}
})
defer st.Close()
@@ -1069,9 +1202,12 @@ func TestH2H1ProxyProtocolV1TCP6(t *testing.T) {
// TestH2H1ProxyProtocolV1Unknown tests PROXY protocol version 1
// containing UNKNOWN entry is accepted.
func TestH2H1ProxyProtocolV1Unknown(t *testing.T) {
st := newServerTester([]string{"--accept-proxy-protocol", "--add-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) {
st := newServerTester([]string{"--accept-proxy-protocol", "--add-x-forwarded-for", "--add-forwarded=for", "--forwarded-for=ip"}, t, func(w http.ResponseWriter, r *http.Request) {
if got, notWant := r.Header.Get("X-Forwarded-For"), "192.168.0.2"; got == notWant {
t.Errorf("X-Forwarded-For: %v")
t.Errorf("X-Forwarded-For: %v; want something else", got)
}
if got, notWant := r.Header.Get("Forwarded"), "for=192.168.0.2"; got == notWant {
t.Errorf("Forwarded: %v; want something else", got)
}
})
defer st.Close()
@@ -1449,6 +1585,175 @@ func TestH2H1HTTPSRedirectPort(t *testing.T) {
}
}
// TestH2H1Code204 tests that 204 response without content-length, and
// transfer-encoding is valid.
func TestH2H1Code204(t *testing.T) {
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNoContent)
})
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H1Code204",
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
if got, want := res.status, 204; got != want {
t.Errorf("status = %v; want %v", got, want)
}
}
// TestH2H1Code204CL0 tests that 204 response with content-length: 0
// is allowed.
func TestH2H1Code204CL0(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 204\r\nContent-Length: 0\r\n\r\n")
bufrw.Flush()
})
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H1Code204CL0",
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
if got, want := res.status, 204; got != want {
t.Errorf("status = %v; want %v", got, want)
}
if got, found := res.header["Content-Length"]; found {
t.Errorf("Content-Length = %v, want nothing", got)
}
}
// TestH2H1Code204CLNonzero tests that 204 response with nonzero
// content-length is not allowed.
func TestH2H1Code204CLNonzero(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 204\r\nContent-Length: 1\r\n\r\n")
bufrw.Flush()
})
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H1Code204CLNonzero",
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
if got, want := res.status, 502; got != want {
t.Errorf("status = %v; want %v", got, want)
}
}
// TestH2H1Code204TE tests that 204 response with transfer-encoding is
// not allowed.
func TestH2H1Code204TE(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 204\r\nTransfer-Encoding: chunked\r\n\r\n")
bufrw.Flush()
})
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H1Code204TE",
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
if got, want := res.status, 502; got != want {
t.Errorf("status = %v; want %v", got, want)
}
}
// TestH2H1AffinityCookie tests that affinity cookie is sent back in
// cleartext http.
func TestH2H1AffinityCookie(t *testing.T) {
st := newServerTester([]string{"--affinity-cookie"}, t, noopHandler)
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H1AffinityCookie",
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
if got, want := res.status, 200; got != want {
t.Errorf("status = %v; want %v", got, want)
}
const pattern = `affinity=[0-9a-f]{8}; Path=/foo/bar`
validCookie := regexp.MustCompile(pattern)
if got := res.header.Get("Set-Cookie"); !validCookie.MatchString(got) {
t.Errorf("Set-Cookie: %v; want pattern %v", got, pattern)
}
}
// TestH2H1AffinityCookieTLS tests that affinity cookie is sent back
// in https.
func TestH2H1AffinityCookieTLS(t *testing.T) {
st := newServerTesterTLS([]string{"--affinity-cookie"}, t, noopHandler)
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H1AffinityCookieTLS",
scheme: "https",
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
if got, want := res.status, 200; got != want {
t.Errorf("status = %v; want %v", got, want)
}
const pattern = `affinity=[0-9a-f]{8}; Path=/foo/bar; Secure`
validCookie := regexp.MustCompile(pattern)
if got := res.header.Get("Set-Cookie"); !validCookie.MatchString(got) {
t.Errorf("Set-Cookie: %v; want pattern %v", got, pattern)
}
}
// TestH2H1GracefulShutdown tests graceful shutdown.
func TestH2H1GracefulShutdown(t *testing.T) {
st := newServerTester(nil, t, noopHandler)
@@ -1653,6 +1958,105 @@ func TestH2H2TLSXfp(t *testing.T) {
}
}
// TestH2H2AddXfp tests that server appends :scheme to the existing
// x-forwarded-proto header field.
func TestH2H2AddXfp(t *testing.T) {
st := newServerTesterTLS([]string{"--http2-bridge", "--no-strip-incoming-x-forwarded-proto"}, t, func(w http.ResponseWriter, r *http.Request) {
xfp := r.Header.Get("X-Forwarded-Proto")
if got, want := xfp, "foo, http"; got != want {
t.Errorf("X-Forwarded-Proto = %q; want %q", got, want)
}
})
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H2AddXfp",
header: []hpack.HeaderField{
pair("x-forwarded-proto", "foo"),
},
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
if got, want := res.status, 200; got != want {
t.Errorf("status = %v; want %v", got, want)
}
}
// TestH2H2NoAddXfp tests that server does not append :scheme to the
// existing x-forwarded-proto header field.
func TestH2H2NoAddXfp(t *testing.T) {
st := newServerTesterTLS([]string{"--http2-bridge", "--no-add-x-forwarded-proto", "--no-strip-incoming-x-forwarded-proto"}, t, func(w http.ResponseWriter, r *http.Request) {
xfp := r.Header.Get("X-Forwarded-Proto")
if got, want := xfp, "foo"; got != want {
t.Errorf("X-Forwarded-Proto = %q; want %q", got, want)
}
})
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H2NoAddXfp",
header: []hpack.HeaderField{
pair("x-forwarded-proto", "foo"),
},
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
if got, want := res.status, 200; got != want {
t.Errorf("status = %v; want %v", got, want)
}
}
// TestH2H2StripXfp tests that server strips incoming
// x-forwarded-proto header field.
func TestH2H2StripXfp(t *testing.T) {
st := newServerTesterTLS([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
xfp := r.Header.Get("X-Forwarded-Proto")
if got, want := xfp, "http"; got != want {
t.Errorf("X-Forwarded-Proto = %q; want %q", got, want)
}
})
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H2StripXfp",
header: []hpack.HeaderField{
pair("x-forwarded-proto", "foo"),
},
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
if got, want := res.status, 200; got != want {
t.Errorf("status = %v; want %v", got, want)
}
}
// TestH2H2StripNoAddXfp tests that server strips incoming
// x-forwarded-proto header field, and does not add another.
func TestH2H2StripNoAddXfp(t *testing.T) {
st := newServerTesterTLS([]string{"--http2-bridge", "--no-add-x-forwarded-proto"}, t, func(w http.ResponseWriter, r *http.Request) {
if got, found := r.Header["X-Forwarded-Proto"]; found {
t.Errorf("X-Forwarded-Proto = %q; want nothing", got)
}
})
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H2StripNoAddXfp",
header: []hpack.HeaderField{
pair("x-forwarded-proto", "foo"),
},
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
if got, want := res.status, 200; got != want {
t.Errorf("status = %v; want %v", got, want)
}
}
// TestH2H2AddXff tests that server generates X-Forwarded-For header
// field when forwarding request to backend.
func TestH2H2AddXff(t *testing.T) {
@@ -1961,6 +2365,26 @@ func TestH2H2DNS(t *testing.T) {
}
}
// TestH2H2Code204 tests that 204 response without content-length, and
// transfer-encoding is valid.
func TestH2H2Code204(t *testing.T) {
st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNoContent)
})
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H2Code204",
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
if got, want := res.status, 204; got != want {
t.Errorf("status = %v; want %v", got, want)
}
}
// TestH2APIBackendconfig exercise backendconfig API endpoint routine
// for successful case.
func TestH2APIBackendconfig(t *testing.T) {

View File

@@ -101,7 +101,7 @@ func newServerTesterInternal(src_args []string, t *testing.T, handler http.Handl
args := []string{}
var backendTLS, dns, externalDNS, acceptProxyProtocol, redirectIfNotTLS bool
var backendTLS, dns, externalDNS, acceptProxyProtocol, redirectIfNotTLS, affinityCookie, alpnH1 bool
for _, k := range src_args {
switch k {
@@ -116,6 +116,10 @@ func newServerTesterInternal(src_args []string, t *testing.T, handler http.Handl
acceptProxyProtocol = true
case "--redirect-if-not-tls":
redirectIfNotTLS = true
case "--affinity-cookie":
affinityCookie = true
case "--alpn-h1":
alpnH1 = true
default:
args = append(args, k)
}
@@ -153,8 +157,8 @@ func newServerTesterInternal(src_args []string, t *testing.T, handler http.Handl
if sep == -1 {
t.Fatalf("backendURL.Host %v does not contain separator ':'", backendURL.Host)
}
// We use awesome service xip.io.
b += fmt.Sprintf("%v.xip.io,%v;", backendURL.Host[:sep], backendURL.Host[sep+1:])
// We use awesome service nip.io.
b += fmt.Sprintf("%v.nip.io,%v;", backendURL.Host[:sep], backendURL.Host[sep+1:])
}
if backendTLS {
@@ -168,6 +172,10 @@ func newServerTesterInternal(src_args []string, t *testing.T, handler http.Handl
b += ";redirect-if-not-tls"
}
if affinityCookie {
b += ";affinity=cookie;affinity-cookie-name=affinity;affinity-cookie-path=/foo/bar"
}
noTLS := ";no-tls"
if frontendTLS {
noTLS = ""
@@ -218,7 +226,11 @@ func newServerTesterInternal(src_args []string, t *testing.T, handler http.Handl
tlsConfig = clientConfig
}
tlsConfig.InsecureSkipVerify = true
tlsConfig.NextProtos = []string{"h2", "spdy/3.1"}
if alpnH1 {
tlsConfig.NextProtos = []string{"http/1.1"}
} else {
tlsConfig.NextProtos = []string{"h2", "spdy/3.1"}
}
conn, err = tls.Dial("tcp", authority, tlsConfig)
} else {
conn, err = net.Dial("tcp", authority)
@@ -266,7 +278,7 @@ func (st *serverTester) Close() {
done := make(chan struct{})
go func() {
st.cmd.Wait()
done <- struct{}{}
close(done)
}()
st.cmd.Process.Signal(syscall.SIGQUIT)
@@ -769,7 +781,7 @@ type serverResponse struct {
connErr bool // true if HTTP/2 connection error
spdyGoAwayErrCode spdy.GoAwayStatus // status code received in SPDY RST_STREAM
spdyRstErrCode spdy.RstStreamStatus // status code received in SPDY GOAWAY
connClose bool // Conection: close is included in response header in HTTP/1 test
connClose bool // Connection: close is included in response header in HTTP/1 test
reqHeader http.Header // http request header, currently only sotres pushed request header
pushResponse []*serverResponse // pushed response
}

View File

@@ -44,6 +44,10 @@ set_target_properties(nghttp2 PROPERTIES
VERSION ${LT_VERSION} SOVERSION ${LT_SOVERSION}
C_VISIBILITY_PRESET hidden
)
target_include_directories(nghttp2 INTERFACE
"${CMAKE_CURRENT_BINARY_DIR}/includes"
"${CMAKE_CURRENT_SOURCE_DIR}/includes"
)
if(HAVE_CUNIT)
# Static library (for unittests because of symbol visibility)

View File

@@ -387,6 +387,11 @@ typedef enum {
* Indicates that a processing was canceled.
*/
NGHTTP2_ERR_CANCEL = -535,
/**
* When a local endpoint expects to receive SETTINGS frame, it
* receives an other type of frame.
*/
NGHTTP2_ERR_SETTINGS_EXPECTED = -536,
/**
* The errors < :enum:`NGHTTP2_ERR_FATAL` mean that the library is
* under unexpected condition and processing was terminated (e.g.,
@@ -469,6 +474,15 @@ NGHTTP2_EXTERN void nghttp2_rcbuf_decref(nghttp2_rcbuf *rcbuf);
*/
NGHTTP2_EXTERN nghttp2_vec nghttp2_rcbuf_get_buf(nghttp2_rcbuf *rcbuf);
/**
* @function
*
* Returns nonzero if the underlying buffer is statically allocated,
* and 0 otherwise. This can be useful for language bindings that wish
* to avoid creating duplicate strings for these buffers.
*/
NGHTTP2_EXTERN int nghttp2_rcbuf_is_static(const nghttp2_rcbuf *rcbuf);
/**
* @enum
*
@@ -1741,11 +1755,12 @@ typedef int (*nghttp2_on_header_callback2)(nghttp2_session *session,
* The parameter and behaviour are similar to
* :type:`nghttp2_on_header_callback`. The difference is that this
* callback is only invoked when a invalid header name/value pair is
* received which is silently ignored if this callback is not set.
* Only invalid regular header field are passed to this callback. In
* other words, invalid pseudo header field is not passed to this
* callback. Also header fields which includes upper cased latter are
* also treated as error without passing them to this callback.
* received which is treated as stream error if this callback is not
* set. Only invalid regular header field are passed to this
* callback. In other words, invalid pseudo header field is not
* passed to this callback. Also header fields which includes upper
* cased latter are also treated as error without passing them to this
* callback.
*
* This callback is only considered if HTTP messaging validation is
* turned on (which is on by default, see
@@ -1754,10 +1769,13 @@ typedef int (*nghttp2_on_header_callback2)(nghttp2_session *session,
* With this callback, application inspects the incoming invalid
* field, and it also can reset stream from this callback by returning
* :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. By default, the
* error code is :enum:`NGHTTP2_INTERNAL_ERROR`. To change the error
* error code is :enum:`NGHTTP2_PROTOCOL_ERROR`. To change the error
* code, call `nghttp2_submit_rst_stream()` with the error code of
* choice in addition to returning
* :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`.
*
* If 0 is returned, the header field is ignored, and the stream is
* not reset.
*/
typedef int (*nghttp2_on_invalid_header_callback)(
nghttp2_session *session, const nghttp2_frame *frame, const uint8_t *name,
@@ -1974,6 +1992,9 @@ typedef ssize_t (*nghttp2_pack_extension_callback)(nghttp2_session *session,
* of length |len|. |len| does not include the sentinel NULL
* character.
*
* This function is deprecated. The new application should use
* :type:`nghttp2_error_callback2`.
*
* The format of error message may change between nghttp2 library
* versions. The application should not depend on the particular
* format.
@@ -1990,6 +2011,33 @@ typedef ssize_t (*nghttp2_pack_extension_callback)(nghttp2_session *session,
typedef int (*nghttp2_error_callback)(nghttp2_session *session, const char *msg,
size_t len, void *user_data);
/**
* @functypedef
*
* Callback function invoked when library provides the error code, and
* message. This callback is solely for debugging purpose.
* |lib_error_code| is one of error code defined in
* :enum:`nghttp2_error`. The |msg| is typically NULL-terminated
* string of length |len|, and intended for human consumption. |len|
* does not include the sentinel NULL character.
*
* The format of error message may change between nghttp2 library
* versions. The application should not depend on the particular
* format.
*
* Normally, application should return 0 from this callback. If fatal
* error occurred while doing something in this callback, application
* should return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. In this case,
* library will return immediately with return value
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. Currently, if nonzero value
* is returned from this callback, they are treated as
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`, but application should not
* rely on this details.
*/
typedef int (*nghttp2_error_callback2)(nghttp2_session *session,
int lib_error_code, const char *msg,
size_t len, void *user_data);
struct nghttp2_session_callbacks;
/**
@@ -2254,10 +2302,30 @@ nghttp2_session_callbacks_set_on_extension_chunk_recv_callback(
*
* Sets callback function invoked when library tells error message to
* the application.
*
* This function is deprecated. The new application should use
* `nghttp2_session_callbacks_set_error_callback2()`.
*
* If both :type:`nghttp2_error_callback` and
* :type:`nghttp2_error_callback2` are set, the latter takes
* precedence.
*/
NGHTTP2_EXTERN void nghttp2_session_callbacks_set_error_callback(
nghttp2_session_callbacks *cbs, nghttp2_error_callback error_callback);
/**
* @function
*
* Sets callback function invoked when library tells error code, and
* message to the application.
*
* If both :type:`nghttp2_error_callback` and
* :type:`nghttp2_error_callback2` are set, the latter takes
* precedence.
*/
NGHTTP2_EXTERN void nghttp2_session_callbacks_set_error_callback2(
nghttp2_session_callbacks *cbs, nghttp2_error_callback2 error_callback2);
/**
* @functypedef
*
@@ -2448,7 +2516,10 @@ nghttp2_option_set_no_recv_client_magic(nghttp2_option *option, int val);
* <https://tools.ietf.org/html/rfc7540#section-8>`_. See
* :ref:`http-messaging` section for details. For those applications
* who use nghttp2 library as non-HTTP use, give nonzero to |val| to
* disable this enforcement.
* disable this enforcement. Please note that disabling this feature
* does not change the fundamental client and server model of HTTP.
* That is, even if the validation is disabled, only client can send
* requests.
*/
NGHTTP2_EXTERN void nghttp2_option_set_no_http_messaging(nghttp2_option *option,
int val);
@@ -3802,9 +3873,8 @@ nghttp2_submit_response(nghttp2_session *session, int32_t stream_id,
* Submits trailer fields HEADERS against the stream |stream_id|.
*
* The |nva| is an array of name/value pair :type:`nghttp2_nv` with
* |nvlen| elements. The application is responsible not to include
* pseudo-header fields (header field whose name starts with ":") in
* |nva|.
* |nvlen| elements. The application must not include pseudo-header
* fields (headers whose names starts with ":") in |nva|.
*
* This function creates copies of all name/value pairs in |nva|. It
* also lower-cases all names in |nva|. The order of elements in
@@ -4687,8 +4757,8 @@ nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater,
*
* After this function returns, it is safe to delete the |nva|.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
* This function returns the number of bytes written to |buf| if it
* succeeds, or one of the following negative error codes:
*
* :enum:`NGHTTP2_ERR_NOMEM`
* Out of memory.
@@ -4719,8 +4789,8 @@ NGHTTP2_EXTERN ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater,
*
* After this function returns, it is safe to delete the |nva|.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
* This function returns the number of bytes written to |vec| if it
* succeeds, or one of the following negative error codes:
*
* :enum:`NGHTTP2_ERR_NOMEM`
* Out of memory.

View File

@@ -398,7 +398,7 @@ int nghttp2_bufs_advance(nghttp2_bufs *bufs);
void nghttp2_bufs_seek_last_present(nghttp2_bufs *bufs);
/*
* Returns nonzero if bufs->cur->next is not emtpy.
* Returns nonzero if bufs->cur->next is not empty.
*/
int nghttp2_bufs_next_present(nghttp2_bufs *bufs);

View File

@@ -168,3 +168,8 @@ void nghttp2_session_callbacks_set_error_callback(
nghttp2_session_callbacks *cbs, nghttp2_error_callback error_callback) {
cbs->error_callback = error_callback;
}
void nghttp2_session_callbacks_set_error_callback2(
nghttp2_session_callbacks *cbs, nghttp2_error_callback2 error_callback2) {
cbs->error_callback2 = error_callback2;
}

View File

@@ -119,6 +119,7 @@ struct nghttp2_session_callbacks {
nghttp2_unpack_extension_callback unpack_extension_callback;
nghttp2_on_extension_chunk_recv_callback on_extension_chunk_recv_callback;
nghttp2_error_callback error_callback;
nghttp2_error_callback2 error_callback2;
};
#endif /* NGHTTP2_CALLBACKS_H */

View File

@@ -672,6 +672,9 @@ int nghttp2_frame_pack_altsvc(nghttp2_bufs *bufs, nghttp2_extension *frame) {
nghttp2_buf *buf;
nghttp2_ext_altsvc *altsvc;
/* This is required with --disable-assert. */
(void)rv;
altsvc = frame->payload;
buf = &bufs->head->buf;

View File

@@ -70,7 +70,9 @@
#define NGHTTP2_MAX_PADLEN 256
/* Union of extension frame payload */
typedef union { nghttp2_ext_altsvc altsvc; } nghttp2_ext_frame_payload;
typedef union {
nghttp2_ext_altsvc altsvc;
} nghttp2_ext_frame_payload;
void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd);

View File

@@ -662,9 +662,9 @@ static int hd_context_init(nghttp2_hd_context *context, nghttp2_mem *mem) {
context->mem = mem;
context->bad = 0;
context->hd_table_bufsize_max = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE;
rv = hd_ringbuf_init(&context->hd_table, context->hd_table_bufsize_max /
NGHTTP2_HD_ENTRY_OVERHEAD,
mem);
rv = hd_ringbuf_init(
&context->hd_table,
context->hd_table_bufsize_max / NGHTTP2_HD_ENTRY_OVERHEAD, mem);
if (rv != 0) {
return rv;
}

View File

@@ -211,7 +211,9 @@ typedef struct {
#define HD_MAP_SIZE 128
typedef struct { nghttp2_hd_entry *table[HD_MAP_SIZE]; } nghttp2_hd_map;
typedef struct {
nghttp2_hd_entry *table[HD_MAP_SIZE];
} nghttp2_hd_map;
struct nghttp2_hd_deflater {
nghttp2_hd_context ctx;
@@ -313,7 +315,7 @@ void nghttp2_hd_deflate_free(nghttp2_hd_deflater *deflater);
*
* This function expands |bufs| as necessary to store the result. If
* buffers is full and the process still requires more space, this
* funtion fails and returns NGHTTP2_ERR_HEADER_COMP.
* function fails and returns NGHTTP2_ERR_HEADER_COMP.
*
* After this function returns, it is safe to delete the |nva|.
*

View File

@@ -322,6 +322,9 @@ const char *nghttp2_strerror(int error_code) {
return "Internal error";
case NGHTTP2_ERR_CANCEL:
return "Cancel";
case NGHTTP2_ERR_SETTINGS_EXPECTED:
return "When a local endpoint expects to receive SETTINGS frame, it "
"receives an other type of frame";
case NGHTTP2_ERR_NOMEM:
return "Out of memory";
case NGHTTP2_ERR_CALLBACK_FAILURE:

View File

@@ -112,7 +112,7 @@ struct nghttp2_outbound_item {
nghttp2_ext_frame_payload ext_frame_payload;
nghttp2_aux_data aux_data;
/* The priority used in priority comparion. Smaller is served
ealier. 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.
For DATA frame, cycle is computed by taking into account of
effective weight and frame payload length previously sent, so

View File

@@ -35,14 +35,16 @@
/* Implementation of priority queue */
typedef struct { size_t index; } nghttp2_pq_entry;
typedef struct {
size_t index;
} nghttp2_pq_entry;
typedef struct {
/* The pointer to the pointer to the item stored */
nghttp2_pq_entry **q;
/* Memory allocator */
nghttp2_mem *mem;
/* The number of items sotred */
/* The number of items stored */
size_t length;
/* The maximum number of items this pq can store. This is
automatically extended when length is reached to this value. */
@@ -71,7 +73,7 @@ void nghttp2_pq_free(nghttp2_pq *pq);
/*
* Adds |item| to the priority queue |pq|.
*
* This function returns 0 if it succeds, or one of the following
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM

View File

@@ -36,7 +36,9 @@ typedef struct nghttp2_queue_cell {
struct nghttp2_queue_cell *next;
} nghttp2_queue_cell;
typedef struct { nghttp2_queue_cell *front, *back; } nghttp2_queue;
typedef struct {
nghttp2_queue_cell *front, *back;
} nghttp2_queue;
void nghttp2_queue_init(nghttp2_queue *queue);
void nghttp2_queue_free(nghttp2_queue *queue);

View File

@@ -96,3 +96,7 @@ nghttp2_vec nghttp2_rcbuf_get_buf(nghttp2_rcbuf *rcbuf) {
nghttp2_vec res = {rcbuf->base, rcbuf->len};
return res;
}
int nghttp2_rcbuf_is_static(const nghttp2_rcbuf *rcbuf) {
return rcbuf->ref == -1;
}

View File

@@ -148,14 +148,16 @@ static int check_ext_type_set(const uint8_t *ext_types, uint8_t type) {
}
static int session_call_error_callback(nghttp2_session *session,
const char *fmt, ...) {
int lib_error_code, const char *fmt,
...) {
size_t bufsize;
va_list ap;
char *buf;
int rv;
nghttp2_mem *mem;
if (!session->callbacks.error_callback) {
if (!session->callbacks.error_callback &&
!session->callbacks.error_callback2) {
return 0;
}
@@ -189,8 +191,13 @@ static int session_call_error_callback(nghttp2_session *session,
return 0;
}
rv = session->callbacks.error_callback(session, buf, (size_t)rv,
session->user_data);
if (session->callbacks.error_callback2) {
rv = session->callbacks.error_callback2(session, lib_error_code, buf,
(size_t)rv, session->user_data);
} else {
rv = session->callbacks.error_callback(session, buf, (size_t)rv,
session->user_data);
}
nghttp2_mem_free(mem, buf);
@@ -541,9 +548,8 @@ static int session_new(nghttp2_session **session_ptr,
if (nghttp2_enable_strict_preface) {
nghttp2_inbound_frame *iframe = &(*session_ptr)->iframe;
if (server &&
((*session_ptr)->opt_flags & NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC) ==
0) {
if (server && ((*session_ptr)->opt_flags &
NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC) == 0) {
iframe->state = NGHTTP2_IB_READ_CLIENT_MAGIC;
iframe->payloadleft = NGHTTP2_CLIENT_MAGIC_LEN;
} else {
@@ -682,6 +688,7 @@ static int inflight_settings_new(nghttp2_inflight_settings **settings_ptr,
if (niv > 0) {
(*settings_ptr)->iv = nghttp2_frame_iv_copy(iv, niv, mem);
if (!(*settings_ptr)->iv) {
nghttp2_mem_free(mem, *settings_ptr);
return NGHTTP2_ERR_NOMEM;
}
} else {
@@ -1523,13 +1530,14 @@ static int session_predicate_response_headers_send(nghttp2_session *session,
if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) {
return NGHTTP2_ERR_INVALID_STREAM_ID;
}
if (stream->state == NGHTTP2_STREAM_OPENING) {
switch (stream->state) {
case NGHTTP2_STREAM_OPENING:
return 0;
}
if (stream->state == NGHTTP2_STREAM_CLOSING) {
case NGHTTP2_STREAM_CLOSING:
return NGHTTP2_ERR_STREAM_CLOSING;
default:
return NGHTTP2_ERR_INVALID_STREAM_STATE;
}
return NGHTTP2_ERR_INVALID_STREAM_STATE;
}
/*
@@ -1572,9 +1580,6 @@ session_predicate_push_response_headers_send(nghttp2_session *session,
if (stream->state != NGHTTP2_STREAM_RESERVED) {
return NGHTTP2_ERR_PROTO;
}
if (stream->state == NGHTTP2_STREAM_CLOSING) {
return NGHTTP2_ERR_STREAM_CLOSING;
}
if (session->goaway_flags & NGHTTP2_GOAWAY_RECV) {
return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED;
}
@@ -1609,19 +1614,18 @@ static int session_predicate_headers_send(nghttp2_session *session,
return rv;
}
assert(stream);
if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) {
if (stream->state == NGHTTP2_STREAM_CLOSING) {
return NGHTTP2_ERR_STREAM_CLOSING;
}
switch (stream->state) {
case NGHTTP2_STREAM_OPENED:
return 0;
}
if (stream->state == NGHTTP2_STREAM_OPENED) {
return 0;
}
if (stream->state == NGHTTP2_STREAM_CLOSING) {
case NGHTTP2_STREAM_CLOSING:
return NGHTTP2_ERR_STREAM_CLOSING;
default:
if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) {
return 0;
}
return NGHTTP2_ERR_INVALID_STREAM_STATE;
}
return NGHTTP2_ERR_INVALID_STREAM_STATE;
}
/*
@@ -2067,14 +2071,6 @@ static int session_prep_frame(nghttp2_session *session,
/* We don't call nghttp2_session_adjust_closed_stream() here,
since we don't keep closed stream in client side */
estimated_payloadlen = session_estimate_headers_payload(
session, frame->headers.nva, frame->headers.nvlen,
NGHTTP2_PRIORITY_SPECLEN);
if (estimated_payloadlen > session->max_send_header_block_length) {
return NGHTTP2_ERR_FRAME_SIZE_ERROR;
}
rv = session_predicate_request_headers_send(session, item);
if (rv != 0) {
return rv;
@@ -2086,14 +2082,6 @@ static int session_prep_frame(nghttp2_session *session,
} else {
nghttp2_stream *stream;
estimated_payloadlen = session_estimate_headers_payload(
session, frame->headers.nva, frame->headers.nvlen,
NGHTTP2_PRIORITY_SPECLEN);
if (estimated_payloadlen > session->max_send_header_block_length) {
return NGHTTP2_ERR_FRAME_SIZE_ERROR;
}
stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
if (stream && stream->state == NGHTTP2_STREAM_RESERVED) {
@@ -2120,6 +2108,14 @@ static int session_prep_frame(nghttp2_session *session,
}
}
estimated_payloadlen = session_estimate_headers_payload(
session, frame->headers.nva, frame->headers.nvlen,
NGHTTP2_PRIORITY_SPECLEN);
if (estimated_payloadlen > session->max_send_header_block_length) {
return NGHTTP2_ERR_FRAME_SIZE_ERROR;
}
rv = nghttp2_frame_pack_headers(&session->aob.framebufs, &frame->headers,
&session->hd_deflater);
@@ -2189,18 +2185,11 @@ static int session_prep_frame(nghttp2_session *session,
nghttp2_stream *stream;
size_t estimated_payloadlen;
estimated_payloadlen = session_estimate_headers_payload(
session, frame->push_promise.nva, frame->push_promise.nvlen, 0);
if (estimated_payloadlen > session->max_send_header_block_length) {
return NGHTTP2_ERR_FRAME_SIZE_ERROR;
}
/* stream could be NULL if associated stream was already
closed. */
stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
/* predicte should fail if stream is NULL. */
/* predicate should fail if stream is NULL. */
rv = session_predicate_push_promise_send(session, stream);
if (rv != 0) {
return rv;
@@ -2208,6 +2197,13 @@ static int session_prep_frame(nghttp2_session *session,
assert(stream);
estimated_payloadlen = session_estimate_headers_payload(
session, frame->push_promise.nva, frame->push_promise.nvlen, 0);
if (estimated_payloadlen > session->max_send_header_block_length) {
return NGHTTP2_ERR_FRAME_SIZE_ERROR;
}
rv = nghttp2_frame_pack_push_promise(
&session->aob.framebufs, &frame->push_promise, &session->hd_deflater);
if (rv != 0) {
@@ -3331,7 +3327,7 @@ static int session_call_on_invalid_header(nghttp2_session *session,
session, frame, nv->name->base, nv->name->len, nv->value->base,
nv->value->len, nv->flags, session->user_data);
} else {
return 0;
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
if (rv == NGHTTP2_ERR_PAUSE || rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
@@ -3421,6 +3417,27 @@ static uint32_t get_error_code_from_lib_error_code(int lib_error_code) {
}
}
/*
* Calls on_invalid_frame_recv_callback if it is set to |session|.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_CALLBACK_FAILURE
* User defined callback function fails.
*/
static int session_call_on_invalid_frame_recv_callback(nghttp2_session *session,
nghttp2_frame *frame,
int lib_error_code) {
if (session->callbacks.on_invalid_frame_recv_callback) {
if (session->callbacks.on_invalid_frame_recv_callback(
session, frame, lib_error_code, session->user_data) != 0) {
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
}
return 0;
}
static int session_handle_invalid_stream2(nghttp2_session *session,
int32_t stream_id,
nghttp2_frame *frame,
@@ -3578,14 +3595,46 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame,
if (subject_stream && session_enforce_http_messaging(session)) {
rv = nghttp2_http_on_header(session, subject_stream, frame, &nv,
trailer);
if (rv == NGHTTP2_ERR_IGN_HTTP_HEADER) {
/* Don't overwrite rv here */
int rv2;
rv2 = session_call_on_invalid_header(session, frame, &nv);
if (rv2 == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
rv = NGHTTP2_ERR_HTTP_HEADER;
} else {
if (rv2 != 0) {
return rv2;
}
/* header is ignored */
DEBUGF("recv: HTTP ignored: type=%u, id=%d, header %.*s: %.*s\n",
frame->hd.type, frame->hd.stream_id, (int)nv.name->len,
nv.name->base, (int)nv.value->len, nv.value->base);
rv2 = session_call_error_callback(
session, NGHTTP2_ERR_HTTP_HEADER,
"Ignoring received invalid HTTP header field: frame type: "
"%u, stream: %d, name: [%.*s], value: [%.*s]",
frame->hd.type, frame->hd.stream_id, (int)nv.name->len,
nv.name->base, (int)nv.value->len, nv.value->base);
if (nghttp2_is_fatal(rv2)) {
return rv2;
}
}
}
if (rv == NGHTTP2_ERR_HTTP_HEADER) {
DEBUGF("recv: HTTP error: type=%u, id=%d, header %.*s: %.*s\n",
frame->hd.type, frame->hd.stream_id, (int)nv.name->len,
nv.name->base, (int)nv.value->len, nv.value->base);
rv = session_call_error_callback(
session, "Invalid HTTP header field was received: frame type: "
"%u, stream: %d, name: [%.*s], value: [%.*s]",
session, NGHTTP2_ERR_HTTP_HEADER,
"Invalid HTTP header field was received: frame type: "
"%u, stream: %d, name: [%.*s], value: [%.*s]",
frame->hd.type, frame->hd.stream_id, (int)nv.name->len,
nv.name->base, (int)nv.value->len, nv.value->base);
@@ -3601,34 +3650,6 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame,
}
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
if (rv == NGHTTP2_ERR_IGN_HTTP_HEADER) {
/* Don't overwrite rv here */
int rv2;
rv2 = session_call_on_invalid_header(session, frame, &nv);
/* This handles NGHTTP2_ERR_PAUSE and
NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE as well */
if (rv2 != 0) {
return rv2;
}
/* header is ignored */
DEBUGF("recv: HTTP ignored: type=%u, id=%d, header %.*s: %.*s\n",
frame->hd.type, frame->hd.stream_id, (int)nv.name->len,
nv.name->base, (int)nv.value->len, nv.value->base);
rv2 = session_call_error_callback(
session,
"Ignoring received invalid HTTP header field: frame type: "
"%u, stream: %d, name: [%.*s], value: [%.*s]",
frame->hd.type, frame->hd.stream_id, (int)nv.name->len,
nv.name->base, (int)nv.value->len, nv.value->base);
if (nghttp2_is_fatal(rv2)) {
return rv2;
}
}
}
if (rv == 0) {
rv = session_call_on_header(session, frame, &nv);
@@ -3767,7 +3788,7 @@ int nghttp2_session_on_request_headers_received(nghttp2_session *session,
session, frame, NGHTTP2_ERR_PROTO, "request HEADERS: stream_id == 0");
}
/* If client recieves idle stream from server, it is invalid
/* If client receives idle stream from server, it is invalid
regardless stream ID is even or odd. This is because client is
not expected to receive request from server. */
if (!session->server) {
@@ -4771,11 +4792,13 @@ int nghttp2_session_on_altsvc_received(nghttp2_session *session,
if (frame->hd.stream_id == 0) {
if (altsvc->origin_len == 0) {
return 0;
return session_call_on_invalid_frame_recv_callback(session, frame,
NGHTTP2_ERR_PROTO);
}
} else {
if (altsvc->origin_len > 0) {
return 0;
return session_call_on_invalid_frame_recv_callback(session, frame,
NGHTTP2_ERR_PROTO);
}
stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
@@ -4788,6 +4811,11 @@ int nghttp2_session_on_altsvc_received(nghttp2_session *session,
}
}
if (altsvc->field_value_len == 0) {
return session_call_on_invalid_frame_recv_callback(session, frame,
NGHTTP2_ERR_PROTO);
}
return session_call_on_frame_received(session, frame);
}
@@ -5324,9 +5352,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
iframe->state = NGHTTP2_IB_IGN_ALL;
rv = session_call_error_callback(
session, "Remote peer returned unexpected data while we expected "
"SETTINGS frame. Perhaps, peer does not support HTTP/2 "
"properly.");
session, NGHTTP2_ERR_SETTINGS_EXPECTED,
"Remote peer returned unexpected data while we expected "
"SETTINGS frame. Perhaps, peer does not support HTTP/2 "
"properly.");
if (nghttp2_is_fatal(rv)) {
return rv;
@@ -5567,7 +5596,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
if (iframe->payloadleft) {
nghttp2_settings_entry *min_header_table_size_entry;
/* We allocate iv with addtional one entry, to store the
/* We allocate iv with additional one entry, to store the
minimum header table size. */
iframe->max_niv =
iframe->frame.hd.length / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH + 1;
@@ -5950,7 +5979,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
DEBUGF("recv: origin_len=%zu\n", origin_len);
if (2 + origin_len > iframe->payloadleft) {
if (origin_len > iframe->payloadleft) {
busy = 1;
iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
break;
@@ -6036,9 +6065,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
/* Use promised stream ID for PUSH_PROMISE */
rv = nghttp2_session_add_rst_stream(
session, iframe->frame.hd.type == NGHTTP2_PUSH_PROMISE
? iframe->frame.push_promise.promised_stream_id
: iframe->frame.hd.stream_id,
session,
iframe->frame.hd.type == NGHTTP2_PUSH_PROMISE
? iframe->frame.push_promise.promised_stream_id
: iframe->frame.hd.stream_id,
NGHTTP2_INTERNAL_ERROR);
if (nghttp2_is_fatal(rv)) {
return rv;
@@ -7128,6 +7158,7 @@ uint32_t nghttp2_session_get_remote_settings(nghttp2_session *session,
}
assert(0);
abort(); /* if NDEBUG is set */
}
uint32_t nghttp2_session_get_local_settings(nghttp2_session *session,
@@ -7148,6 +7179,7 @@ uint32_t nghttp2_session_get_local_settings(nghttp2_session *session,
}
assert(0);
abort(); /* if NDEBUG is set */
}
static int nghttp2_session_upgrade_internal(nghttp2_session *session,

View File

@@ -311,7 +311,7 @@ struct nghttp2_session {
/* Unacked local SETTINGS_MAX_CONCURRENT_STREAMS value. We use this
to refuse the incoming stream if it exceeds this value. */
uint32_t pending_local_max_concurrent_stream;
/* The bitwose OR of zero or more of nghttp2_typemask to indicate
/* The bitwise OR of zero or more of nghttp2_typemask to indicate
that the default handling of extension frame is enabled. */
uint32_t builtin_recv_ext_types;
/* Unacked local ENABLE_PUSH value. We use this to refuse
@@ -319,7 +319,7 @@ struct nghttp2_session {
uint8_t pending_enable_push;
/* Nonzero if the session is server side. */
uint8_t server;
/* Flags indicating GOAWAY is sent and/or recieved. The flags are
/* Flags indicating GOAWAY is sent and/or received. The flags are
composed by bitwise OR-ing nghttp2_goaway_flag. */
uint8_t goaway_flags;
/* This flag is used to reduce excessive queuing of WINDOW_UPDATE to
@@ -722,7 +722,7 @@ int nghttp2_session_on_goaway_received(nghttp2_session *session,
nghttp2_frame *frame);
/*
* Called when WINDOW_UPDATE is recieved, assuming |frame| is properly
* Called when WINDOW_UPDATE is received, assuming |frame| is properly
* initialized.
*
* This function returns 0 if it succeeds, or one of the following
@@ -737,7 +737,7 @@ int nghttp2_session_on_window_update_received(nghttp2_session *session,
nghttp2_frame *frame);
/*
* Called when ALTSVC is recieved, assuming |frame| is properly
* Called when ALTSVC is received, assuming |frame| is properly
* initialized.
*
* This function returns 0 if it succeeds, or one of the following

View File

@@ -366,8 +366,9 @@ static void check_queued(nghttp2_stream *stream) {
}
}
if (queued == 0) {
fprintf(stderr, "stream(%p)=%d, stream->queued == 1, and "
"!stream_active(), but no descendants is queued\n",
fprintf(stderr,
"stream(%p)=%d, stream->queued == 1, and "
"!stream_active(), but no descendants is queued\n",
stream, stream->stream_id);
assert(0);
}
@@ -378,9 +379,10 @@ static void check_queued(nghttp2_stream *stream) {
}
} else {
if (stream_active(stream) || !nghttp2_pq_empty(&stream->obq)) {
fprintf(stderr, "stream(%p) = %d, stream->queued == 0, but "
"stream_active(stream) == %d and "
"nghttp2_pq_size(&stream->obq) = %zu\n",
fprintf(stderr,
"stream(%p) = %d, stream->queued == 0, but "
"stream_active(stream) == %d and "
"nghttp2_pq_size(&stream->obq) = %zu\n",
stream, stream->stream_id, stream_active(stream),
nghttp2_pq_size(&stream->obq));
assert(0);

View File

@@ -8,11 +8,8 @@ set_source_files_properties(${cxx_sources} PROPERTIES
COMPILE_FLAGS "${WARNCXXFLAGS} ${CXX1XCXXFLAGS}")
include_directories(
"${CMAKE_SOURCE_DIR}/lib/includes"
"${CMAKE_BINARY_DIR}/lib/includes"
"${CMAKE_SOURCE_DIR}/lib"
"${CMAKE_SOURCE_DIR}/src/includes"
"${CMAKE_SOURCE_DIR}/third-party"
"${CMAKE_CURRENT_SOURCE_DIR}/includes"
"${CMAKE_CURRENT_SOURCE_DIR}/../third-party"
${JEMALLOC_INCLUDE_DIRS}
${SPDYLAY_INCLUDE_DIRS}
@@ -48,7 +45,7 @@ if(ENABLE_APP)
set(NGHTTP_SOURCES
${HELPER_OBJECTS}
nghttp.cc
ssl.cc
tls.cc
)
if(HAVE_LIBXML2)
list(APPEND NGHTTP_SOURCES HtmlParser.cc)
@@ -58,7 +55,7 @@ if(ENABLE_APP)
set(NGHTTPD_SOURCES
${HELPER_OBJECTS}
nghttpd.cc
ssl.cc
tls.cc
HttpServer.cc
)
@@ -67,7 +64,7 @@ if(ENABLE_APP)
util.cc
http2.cc h2load.cc
timegm.c
ssl.cc
tls.cc
h2load_http2_session.cc
h2load_http1_session.cc
)
@@ -82,7 +79,7 @@ if(ENABLE_APP)
set(NGHTTPX_SRCS
util.cc http2.cc timegm.c
app_helper.cc
ssl.cc
tls.cc
shrpx_config.cc
shrpx_accept_handler.cc
shrpx_connection_handler.cc
@@ -98,7 +95,7 @@ if(ENABLE_APP)
shrpx_log.cc
shrpx_http.cc
shrpx_io_control.cc
shrpx_ssl.cc
shrpx_tls.cc
shrpx_worker.cc
shrpx_log_config.cc
shrpx_connect_blocker.cc
@@ -152,7 +149,7 @@ if(ENABLE_APP)
if(HAVE_CUNIT)
set(NGHTTPX_UNITTEST_SOURCES
shrpx-unittest.cc
shrpx_ssl_test.cc
shrpx_tls_test.cc
shrpx_downstream_test.cc
shrpx_config_test.cc
shrpx_worker_test.cc
@@ -216,7 +213,7 @@ endif()
if(ENABLE_ASIO_LIB)
set(NGHTTP2_ASIO_SOURCES
util.cc http2.cc
ssl.cc
tls.cc
timegm.c
asio_common.cc
asio_io_service_pool.cc
@@ -252,6 +249,11 @@ if(ENABLE_ASIO_LIB)
${OPENSSL_INCLUDE_DIRS}
${Boost_INCLUDE_DIRS}
)
target_include_directories(nghttp2_asio INTERFACE
"${CMAKE_CURRENT_BINARY_DIR}/../lib/includes"
"${CMAKE_CURRENT_SOURCE_DIR}/../lib/includes"
"${CMAKE_CURRENT_SOURCE_DIR}/includes"
)
target_link_libraries(nghttp2_asio
nghttp2
${OPENSSL_LIBRARIES}

View File

@@ -60,7 +60,7 @@
#include "app_helper.h"
#include "http2.h"
#include "util.h"
#include "ssl.h"
#include "tls.h"
#include "template.h"
#ifndef O_BINARY
@@ -877,7 +877,7 @@ int Http2Handler::connection_made() {
}
}
if (ssl_ && !nghttp2::ssl::check_http2_requirement(ssl_)) {
if (ssl_ && !nghttp2::tls::check_http2_requirement(ssl_)) {
terminate_session(NGHTTP2_INADEQUATE_SECURITY);
}
@@ -1749,8 +1749,8 @@ void fill_callback(nghttp2_session_callbacks *callbacks, const Config *config) {
nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(
callbacks, verbose_on_invalid_frame_recv_callback);
nghttp2_session_callbacks_set_error_callback(callbacks,
verbose_error_callback);
nghttp2_session_callbacks_set_error_callback2(callbacks,
verbose_error_callback);
}
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
@@ -1779,7 +1779,7 @@ struct ClientInfo {
struct Worker {
std::unique_ptr<Sessions> sessions;
ev_async w;
// protectes q
// protects q
std::mutex m;
std::deque<ClientInfo> q;
};
@@ -2122,14 +2122,14 @@ int HttpServer::run() {
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
if (nghttp2::ssl::ssl_ctx_set_proto_versions(
ssl_ctx, nghttp2::ssl::NGHTTP2_TLS_MIN_VERSION,
nghttp2::ssl::NGHTTP2_TLS_MAX_VERSION) != 0) {
if (nghttp2::tls::ssl_ctx_set_proto_versions(
ssl_ctx, nghttp2::tls::NGHTTP2_TLS_MIN_VERSION,
nghttp2::tls::NGHTTP2_TLS_MAX_VERSION) != 0) {
std::cerr << "Could not set TLS versions" << std::endl;
return -1;
}
if (SSL_CTX_set_cipher_list(ssl_ctx, ssl::DEFAULT_CIPHER_LIST) == 0) {
if (SSL_CTX_set_cipher_list(ssl_ctx, tls::DEFAULT_CIPHER_LIST) == 0) {
std::cerr << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
return -1;
}
@@ -2156,7 +2156,7 @@ int HttpServer::run() {
}
SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh);
EC_KEY_free(ecdh);
// #endif // OPENSSL_VERSION_NUBMER < 0x10002000L
// #endif // OPENSSL_VERSION_NUBMER < 0x10002000L
#endif // OPENSSL_NO_EC
@@ -2197,8 +2197,9 @@ int HttpServer::run() {
return -1;
}
if (config_->verify_client) {
SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE |
SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
SSL_CTX_set_verify(ssl_ctx,
SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE |
SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
verify_callback);
}

View File

@@ -81,10 +81,10 @@ endif # HAVE_LIBXML2
nghttp_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} nghttp.cc nghttp.h \
${HTML_PARSER_OBJECTS} ${HTML_PARSER_HFILES} \
ssl.cc ssl.h
tls.cc tls.h
nghttpd_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} nghttpd.cc \
ssl.cc ssl.h \
tls.cc tls.h \
HttpServer.cc HttpServer.h
bin_PROGRAMS += h2load
@@ -92,7 +92,7 @@ bin_PROGRAMS += h2load
h2load_SOURCES = util.cc util.h \
http2.cc http2.h h2load.cc h2load.h \
timegm.c timegm.h \
ssl.cc ssl.h \
tls.cc tls.h \
h2load_session.h \
h2load_http2_session.cc h2load_http2_session.h \
h2load_http1_session.cc h2load_http1_session.h
@@ -104,7 +104,7 @@ endif # HAVE_SPDYLAY
NGHTTPX_SRCS = \
util.cc util.h http2.cc http2.h timegm.c timegm.h base64.h \
app_helper.cc app_helper.h \
ssl.cc ssl.h \
tls.cc tls.h \
shrpx_config.cc shrpx_config.h \
shrpx_error.h \
shrpx_accept_handler.cc shrpx_accept_handler.h \
@@ -122,7 +122,7 @@ NGHTTPX_SRCS = \
shrpx_log.cc shrpx_log.h \
shrpx_http.cc shrpx_http.h \
shrpx_io_control.cc shrpx_io_control.h \
shrpx_ssl.cc shrpx_ssl.h \
shrpx_tls.cc shrpx_tls.h \
shrpx_worker.cc shrpx_worker.h \
shrpx_log_config.cc shrpx_log_config.h \
shrpx_connect_blocker.cc shrpx_connect_blocker.h \
@@ -183,7 +183,7 @@ endif # HAVE_NEVERBLEED
if HAVE_CUNIT
check_PROGRAMS += nghttpx-unittest
nghttpx_unittest_SOURCES = shrpx-unittest.cc \
shrpx_ssl_test.cc shrpx_ssl_test.h \
shrpx_tls_test.cc shrpx_tls_test.h \
shrpx_downstream_test.cc shrpx_downstream_test.h \
shrpx_config_test.cc shrpx_config_test.h \
shrpx_worker_test.cc shrpx_worker_test.h \
@@ -240,7 +240,7 @@ lib_LTLIBRARIES = libnghttp2_asio.la
libnghttp2_asio_la_SOURCES = \
util.cc util.h http2.cc http2.h \
ssl.cc ssl.h \
tls.cc tls.h \
ssl_compat.h \
timegm.c timegm.h \
asio_common.cc asio_common.h \

View File

@@ -27,7 +27,9 @@
#include "nghttp2_config.h"
#ifndef _WIN32
#include <sys/uio.h>
#endif // !_WIN32
#include <cassert>
@@ -271,6 +273,6 @@ ByteRef make_byte_ref(BlockAllocator &alloc, size_t size) {
return {dst, size};
}
} // namespace aria2
} // namespace nghttp2
#endif // ALLOCATOR_H

View File

@@ -155,7 +155,7 @@ void print_nv(nghttp2_nv *nva, size_t nvlen) {
print_nv(nva);
}
}
} // namelen
} // namespace
void print_timer() {
auto millis = get_timer();
@@ -327,8 +327,9 @@ void print_frame(print_type ptype, const nghttp2_frame *frame) {
break;
case NGHTTP2_GOAWAY:
print_frame_attr_indent();
fprintf(outfile, "(last_stream_id=%d, error_code=%s(0x%02x), "
"opaque_data(%u)=[%s])\n",
fprintf(outfile,
"(last_stream_id=%d, error_code=%s(0x%02x), "
"opaque_data(%u)=[%s])\n",
frame->goaway.last_stream_id,
nghttp2_http2_strerror(frame->goaway.error_code),
frame->goaway.error_code,
@@ -425,8 +426,8 @@ int verbose_on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
return 0;
}
int verbose_error_callback(nghttp2_session *session, const char *msg,
size_t len, void *user_data) {
int verbose_error_callback(nghttp2_session *session, int lib_error_code,
const char *msg, size_t len, void *user_data) {
print_timer();
fprintf(outfile, " [ERROR] %.*s\n", (int)len, msg);
fflush(outfile);

View File

@@ -60,8 +60,8 @@ int verbose_on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
int32_t stream_id, const uint8_t *data,
size_t len, void *user_data);
int verbose_error_callback(nghttp2_session *session, const char *msg,
size_t len, void *user_data);
int verbose_error_callback(nghttp2_session *session, int lib_error_code,
const char *msg, size_t len, void *user_data);
// Returns difference between |a| and |b| in milliseconds, assuming
// |a| is more recent than |b|.

View File

@@ -96,29 +96,48 @@ boost::asio::io_service &session::io_service() const {
const request *session::submit(boost::system::error_code &ec,
const std::string &method,
const std::string &uri, header_map h) const {
return impl_->submit(ec, method, uri, generator_cb(), std::move(h));
const std::string &uri, header_map h,
priority_spec prio) const {
return impl_->submit(ec, method, uri, generator_cb(), std::move(h),
std::move(prio));
}
const request *session::submit(boost::system::error_code &ec,
const std::string &method,
const std::string &uri, std::string data,
header_map h) const {
header_map h, priority_spec prio) const {
return impl_->submit(ec, method, uri, string_generator(std::move(data)),
std::move(h));
std::move(h), std::move(prio));
}
const request *session::submit(boost::system::error_code &ec,
const std::string &method,
const std::string &uri, generator_cb cb,
header_map h) const {
return impl_->submit(ec, method, uri, std::move(cb), std::move(h));
header_map h, priority_spec prio) const {
return impl_->submit(ec, method, uri, std::move(cb), std::move(h),
std::move(prio));
}
void session::read_timeout(const boost::posix_time::time_duration &t) {
impl_->read_timeout(t);
}
priority_spec::priority_spec(const int32_t stream_id, const int32_t weight,
const bool exclusive)
: valid_(true) {
nghttp2_priority_spec_init(&spec_, stream_id, weight, exclusive);
}
const nghttp2_priority_spec *priority_spec::get() const {
if (!valid_) {
return nullptr;
}
return &spec_;
}
const bool priority_spec::valid() const { return valid_; }
} // namespace client
} // namespace asio_http2
} // nghttp2
} // namespace nghttp2

View File

@@ -45,9 +45,9 @@ session_impl::session_impl(
io_service_(io_service),
resolver_(io_service),
deadline_(io_service),
ping_(io_service),
connect_timeout_(connect_timeout),
read_timeout_(boost::posix_time::seconds(60)),
ping_(io_service),
session_(nullptr),
data_pending_(nullptr),
data_pendinglen_(0),
@@ -84,7 +84,6 @@ void session_impl::start_resolve(const std::string &host,
});
deadline_.async_wait(std::bind(&session_impl::handle_deadline, self));
start_ping();
}
void session_impl::handle_deadline() {
@@ -135,6 +134,8 @@ void session_impl::connected(tcp::resolver::iterator endpoint_it) {
do_write();
do_read();
start_ping();
auto &connect_cb = on_connect();
if (connect_cb) {
connect_cb(endpoint_it);
@@ -478,7 +479,7 @@ std::unique_ptr<stream> session_impl::create_stream() {
const request *session_impl::submit(boost::system::error_code &ec,
const std::string &method,
const std::string &uri, generator_cb cb,
header_map h) {
header_map h, priority_spec prio) {
ec.clear();
if (stopped_) {
@@ -558,7 +559,7 @@ const request *session_impl::submit(boost::system::error_code &ec,
prdptr = &prd;
}
auto stream_id = nghttp2_submit_request(session_, nullptr, nva.data(),
auto stream_id = nghttp2_submit_request(session_, prio.get(), nva.data(),
nva.size(), prdptr, strm.get());
if (stream_id < 0) {
ec = make_error_code(static_cast<nghttp2_error>(stream_id));
@@ -755,4 +756,4 @@ void session_impl::read_timeout(const boost::posix_time::time_duration &t) {
} // namespace client
} // namespace asio_http2
} // nghttp2
} // namespace nghttp2

View File

@@ -70,7 +70,7 @@ public:
const request *submit(boost::system::error_code &ec,
const std::string &method, const std::string &uri,
generator_cb cb, header_map h);
generator_cb cb, header_map h, priority_spec spec);
virtual void start_connect(tcp::resolver::iterator endpoint_it) = 0;
virtual tcp::socket &socket() = 0;

View File

@@ -45,8 +45,9 @@ session_tls_impl::~session_tls_impl() {}
void session_tls_impl::start_connect(tcp::resolver::iterator endpoint_it) {
auto self = std::static_pointer_cast<session_tls_impl>(shared_from_this());
boost::asio::async_connect(
socket(), endpoint_it, [self](const boost::system::error_code &ec,
tcp::resolver::iterator endpoint_it) {
socket(), endpoint_it,
[self](const boost::system::error_code &ec,
tcp::resolver::iterator endpoint_it) {
if (self->stopped()) {
return;
}

View File

@@ -28,7 +28,7 @@
#include <boost/asio/ssl.hpp>
#include "ssl.h"
#include "tls.h"
#include "util.h"
namespace nghttp2 {

View File

@@ -24,6 +24,7 @@
*/
#include "asio_common.h"
#include <fcntl.h>
#include <memory>
#include "util.h"

View File

@@ -169,6 +169,6 @@ private:
} // namespace server
} // namespace asio_http2
} // namespace nghttp
} // namespace nghttp2
#endif // ASIO_SERVER_HTTP2_HANDLER_H

View File

@@ -28,7 +28,7 @@
#include "asio_server.h"
#include "util.h"
#include "ssl.h"
#include "tls.h"
#include "template.h"
namespace nghttp2 {

View File

@@ -28,7 +28,7 @@
#include <boost/asio/ssl.hpp>
#include "ssl.h"
#include "tls.h"
#include "util.h"
namespace nghttp2 {
@@ -72,7 +72,7 @@ configure_tls_context_easy(boost::system::error_code &ec,
SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
SSL_CTX_set_mode(ctx, SSL_MODE_RELEASE_BUFFERS);
SSL_CTX_set_cipher_list(ctx, ssl::DEFAULT_CIPHER_LIST);
SSL_CTX_set_cipher_list(ctx, tls::DEFAULT_CIPHER_LIST);
#ifndef OPENSSL_NO_EC
auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);

View File

@@ -59,7 +59,7 @@
#ifdef HAVE_SPDYLAY
#include "h2load_spdy_session.h"
#endif // HAVE_SPDYLAY
#include "ssl.h"
#include "tls.h"
#include "http2.h"
#include "util.h"
#include "template.h"
@@ -79,7 +79,7 @@ bool recorded(const std::chrono::steady_clock::time_point &t) {
} // namespace
Config::Config()
: ciphers(ssl::DEFAULT_CIPHER_LIST),
: ciphers(tls::DEFAULT_CIPHER_LIST),
data_length(-1),
addrs(nullptr),
nreqs(1),
@@ -90,6 +90,8 @@ Config::Config()
connection_window_bits(30),
rate(0),
rate_period(1.0),
duration(0.0),
warm_up_time(0.0),
conn_active_timeout(0.),
conn_inactivity_timeout(0.),
no_tls_proto(PROTO_HTTP2),
@@ -118,6 +120,7 @@ Config::~Config() {
}
bool Config::is_rate_mode() const { return (this->rate != 0); }
bool Config::is_timing_based_mode() const { return (this->duration > 0); }
bool Config::has_base_uri() const { return (!this->base_uri.empty()); }
Config config;
@@ -151,33 +154,12 @@ std::mt19937 gen(rd());
} // namespace
namespace {
void sampling_init(Sampling &smp, size_t total, size_t max_samples) {
void sampling_init(Sampling &smp, size_t max_samples) {
smp.n = 0;
if (total <= max_samples) {
smp.interval = 0.;
smp.point = 0.;
return;
}
smp.interval = static_cast<double>(total) / max_samples;
std::uniform_real_distribution<> dis(0., smp.interval);
smp.point = dis(gen);
smp.max_samples = max_samples;
}
} // namespace
namespace {
bool sampling_should_pick(Sampling &smp) {
return smp.interval == 0. || smp.n == ceil(smp.point);
}
} // namespace
namespace {
void sampling_advance_point(Sampling &smp) { smp.point += smp.interval; }
} // namespace
namespace {
void writecb(struct ev_loop *loop, ev_io *w, int revents) {
auto client = static_cast<Client *>(w->data);
@@ -190,6 +172,7 @@ void writecb(struct ev_loop *loop, ev_io *w, int revents) {
rv = client->connect();
if (rv != 0) {
client->fail();
client->worker->free_client(client);
delete client;
return;
}
@@ -197,6 +180,7 @@ void writecb(struct ev_loop *loop, ev_io *w, int revents) {
}
if (rv != 0) {
client->fail();
client->worker->free_client(client);
delete client;
}
}
@@ -210,6 +194,7 @@ void readcb(struct ev_loop *loop, ev_io *w, int revents) {
if (client->try_again_or_fail() == 0) {
return;
}
client->worker->free_client(client);
delete client;
return;
}
@@ -241,16 +226,71 @@ void rate_period_timeout_w_cb(struct ev_loop *loop, ev_timer *w, int revents) {
std::cerr << "client could not connect to host" << std::endl;
client->fail();
} else {
client.release();
if (worker->config->is_timing_based_mode()) {
worker->clients.push_back(client.release());
} else {
client.release();
}
}
worker->report_rate_progress();
}
if (worker->nconns_made >= worker->nclients) {
ev_timer_stop(worker->loop, w);
if (!worker->config->is_timing_based_mode()) {
if (worker->nconns_made >= worker->nclients) {
ev_timer_stop(worker->loop, w);
}
} else {
// To check whether all created clients are pushed correctly
assert(worker->nclients == worker->clients.size());
}
}
} // namespace
namespace {
// Called when the duration for infinite number of requests are over
void duration_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
auto worker = static_cast<Worker *>(w->data);
worker->current_phase = Phase::DURATION_OVER;
std::cout << "Main benchmark duration is over for thread #" << worker->id
<< ". Stopping all clients." << std::endl;
worker->stop_all_clients();
std::cout << "Stopped all clients for thread #" << worker->id << std::endl;
}
} // namespace
namespace {
// Called when the warmup duration for infinite number of requests are over
void warmup_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
auto worker = static_cast<Worker *>(w->data);
std::cout << "Warm-up phase is over for thread #" << worker->id << "."
<< std::endl;
std::cout << "Main benchmark duration is started for thread #" << worker->id
<< "." << std::endl;
assert(worker->stats.req_started == 0);
assert(worker->stats.req_done == 0);
for (auto client : worker->clients) {
if (client) {
assert(client->req_todo == 0);
assert(client->req_left == 1);
assert(client->req_inflight == 0);
assert(client->req_started == 0);
assert(client->req_done == 0);
client->record_client_start_time();
client->clear_connect_times();
client->record_connect_start_time();
}
}
worker->current_phase = Phase::MAIN_DURATION;
ev_timer_start(worker->loop, &worker->duration_watcher);
}
} // namespace
namespace {
// Called when an a connection has been inactive for a set period of time
// or a fixed amount of time after all requests have been made on a
@@ -269,8 +309,7 @@ void conn_timeout_cb(EV_P_ ev_timer *w, int revents) {
namespace {
bool check_stop_client_request_timeout(Client *client, ev_timer *w) {
if (client->req_left == 0 ||
client->streams.size() >= client->session->max_concurrent_streams()) {
if (client->req_left == 0) {
// no more requests to make, stop timer
ev_timer_stop(client->worker->loop, w);
return true;
@@ -284,6 +323,11 @@ namespace {
void client_request_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
auto client = static_cast<Client *>(w->data);
if (client->streams.size() >= (size_t)config.max_concurrent_streams) {
ev_timer_stop(client->worker->loop, w);
return;
}
if (client->submit_request() != 0) {
ev_timer_stop(client->worker->loop, w);
client->process_request_failure();
@@ -336,6 +380,11 @@ Client::Client(uint32_t id, Worker *worker, size_t req_todo)
fd(-1),
new_connection_requested(false),
final(false) {
if (req_todo == 0) { // this means infinite number of requests are to be made
// This ensures that number of requests are unbounded
// Just a positive number is fine, we chose the first positive number
req_left = 1;
}
ev_io_init(&wev, writecb, 0, EV_WRITE);
ev_io_init(&rev, readcb, 0, EV_READ);
@@ -361,10 +410,7 @@ Client::~Client() {
SSL_free(ssl);
}
if (sampling_should_pick(worker->client_smp)) {
sampling_advance_point(worker->client_smp);
worker->sample_client_stat(&cstat);
}
worker->sample_client_stat(&cstat);
++worker->client_smp.n;
}
@@ -407,9 +453,17 @@ int Client::make_socket(addrinfo *addr) {
int Client::connect() {
int rv;
record_client_start_time();
clear_connect_times();
record_connect_start_time();
if (!worker->config->is_timing_based_mode() ||
worker->current_phase == Phase::MAIN_DURATION) {
record_client_start_time();
clear_connect_times();
record_connect_start_time();
} else if (worker->current_phase == Phase::INITIAL_IDLE) {
worker->current_phase = Phase::WARM_UP;
std::cout << "Warm-up started for thread #" << worker->id << "."
<< std::endl;
ev_timer_start(worker->loop, &worker->warmup_watcher);
}
if (worker->config->conn_inactivity_timeout > 0.) {
ev_timer_again(worker->loop, &conn_inactivity_watcher);
@@ -467,13 +521,17 @@ int Client::try_again_or_fail() {
if (new_connection_requested) {
new_connection_requested = false;
if (req_left) {
// At the moment, we don't have a facility to re-start request
// already in in-flight. Make them fail.
worker->stats.req_failed += req_inflight;
worker->stats.req_error += req_inflight;
req_inflight = 0;
if (req_left) {
if (worker->current_phase == Phase::MAIN_DURATION) {
// At the moment, we don't have a facility to re-start request
// already in in-flight. Make them fail.
worker->stats.req_failed += req_inflight;
worker->stats.req_error += req_inflight;
req_inflight = 0;
}
// Keep using current address
if (connect() == 0) {
@@ -529,11 +587,16 @@ int Client::submit_request() {
return -1;
}
if (worker->current_phase != Phase::MAIN_DURATION) {
return 0;
}
++worker->stats.req_started;
--req_left;
++req_started;
++req_inflight;
if (!worker->config->is_timing_based_mode()) {
--req_left;
}
// if an active timeout is set and this is the last request to be submitted
// on this connection, start the active timeout.
if (worker->config->conn_active_timeout > 0. && req_left == 0) {
@@ -544,6 +607,10 @@ int Client::submit_request() {
}
void Client::process_timedout_streams() {
if (worker->current_phase != Phase::MAIN_DURATION) {
return;
}
for (auto &p : streams) {
auto &req_stat = p.second.req_stat;
if (!req_stat.completed) {
@@ -557,6 +624,10 @@ void Client::process_timedout_streams() {
}
void Client::process_abandoned_streams() {
if (worker->current_phase != Phase::MAIN_DURATION) {
return;
}
auto req_abandoned = req_inflight + req_left;
worker->stats.req_failed += req_abandoned;
@@ -567,6 +638,10 @@ void Client::process_abandoned_streams() {
}
void Client::process_request_failure() {
if (worker->current_phase != Phase::MAIN_DURATION) {
return;
}
worker->stats.req_failed += req_left;
worker->stats.req_error += req_left;
@@ -575,6 +650,8 @@ void Client::process_request_failure() {
if (req_inflight == 0) {
terminate_session();
}
std::cout << "Process Request Failure:" << worker->stats.req_failed
<< std::endl;
}
namespace {
@@ -625,7 +702,7 @@ void Client::report_tls_info() {
if (worker->id == 0 && !worker->tls_info_report_done) {
worker->tls_info_report_done = true;
auto cipher = SSL_get_current_cipher(ssl);
std::cout << "TLS Protocol: " << ssl::get_tls_protocol(ssl) << "\n"
std::cout << "TLS Protocol: " << tls::get_tls_protocol(ssl) << "\n"
<< "Cipher: " << SSL_CIPHER_get_name(cipher) << std::endl;
print_server_tmp_key(ssl);
}
@@ -653,6 +730,15 @@ void Client::on_header(int32_t stream_id, const uint8_t *name, size_t namelen,
return;
}
auto &stream = (*itr).second;
if (worker->current_phase != Phase::MAIN_DURATION) {
// If the stream is for warm-up phase, then mark as a success
// But we do not update the count for 2xx, 3xx, etc status codes
// Same has been done in on_status_code function
stream.status_success = 1;
return;
}
if (stream.status_success == -1 && namelen == 7 &&
util::streq_l(":status", name, namelen)) {
int status = 0;
@@ -691,6 +777,11 @@ void Client::on_status_code(int32_t stream_id, uint16_t status) {
}
auto &stream = (*itr).second;
if (worker->current_phase != Phase::MAIN_DURATION) {
stream.status_success = 1;
return;
}
if (status >= 200 && status < 300) {
++worker->stats.status[2];
stream.status_success = 1;
@@ -706,40 +797,39 @@ void Client::on_status_code(int32_t stream_id, uint16_t status) {
}
void Client::on_stream_close(int32_t stream_id, bool success, bool final) {
++req_done;
--req_inflight;
if (worker->current_phase == Phase::MAIN_DURATION) {
if (req_inflight > 0) {
--req_inflight;
}
auto req_stat = get_req_stat(stream_id);
if (!req_stat) {
return;
}
auto req_stat = get_req_stat(stream_id);
if (!req_stat) {
return;
}
req_stat->stream_close_time = std::chrono::steady_clock::now();
if (success) {
req_stat->completed = true;
++worker->stats.req_success;
++cstat.req_success;
req_stat->stream_close_time = std::chrono::steady_clock::now();
if (success) {
req_stat->completed = true;
++worker->stats.req_success;
++cstat.req_success;
if (streams[stream_id].status_success == 1) {
++worker->stats.req_status_success;
} else {
++worker->stats.req_failed;
}
if (streams[stream_id].status_success == 1) {
++worker->stats.req_status_success;
worker->sample_req_stat(req_stat);
// Count up in successful cases only
++worker->request_times_smp.n;
} else {
++worker->stats.req_failed;
++worker->stats.req_error;
}
if (sampling_should_pick(worker->request_times_smp)) {
sampling_advance_point(worker->request_times_smp);
worker->sample_req_stat(req_stat);
}
// Count up in successful cases only
++worker->request_times_smp.n;
} else {
++worker->stats.req_failed;
++worker->stats.req_error;
++worker->stats.req_done;
++req_done;
}
++worker->stats.req_done;
worker->report_progress();
streams.erase(stream_id);
if (req_left == 0 && req_inflight == 0) {
@@ -747,10 +837,14 @@ void Client::on_stream_close(int32_t stream_id, bool success, bool final) {
return;
}
if (!config.timing_script && !final && req_left > 0 &&
submit_request() != 0) {
process_request_failure();
return;
if (!final && req_left > 0) {
if (config.timing_script) {
if (!ev_is_active(&request_timeout_watcher)) {
ev_feed_event(worker->loop, &request_timeout_watcher, EV_TIMER);
}
} else if (submit_request() != 0) {
process_request_failure();
}
}
}
@@ -865,7 +959,9 @@ int Client::connection_made() {
record_connect_time();
if (!config.timing_script) {
auto nreq = std::min(req_left, session->max_concurrent_streams());
auto nreq = config.is_timing_based_mode()
? std::max(req_left, session->max_concurrent_streams())
: std::min(req_left, session->max_concurrent_streams());
for (; nreq > 0; --nreq) {
if (submit_request() != 0) {
process_request_failure();
@@ -906,7 +1002,9 @@ int Client::on_read(const uint8_t *data, size_t len) {
if (rv != 0) {
return -1;
}
worker->stats.bytes_total += len;
if (worker->current_phase == Phase::MAIN_DURATION) {
worker->stats.bytes_total += len;
}
signal_write();
return 0;
}
@@ -1176,37 +1274,78 @@ Worker::Worker(uint32_t id, SSL_CTX *ssl_ctx, size_t req_todo, size_t nclients,
rate(rate),
max_samples(max_samples),
next_client_id(0) {
if (!config->is_rate_mode()) {
if (!config->is_rate_mode() && !config->is_timing_based_mode()) {
progress_interval = std::max(static_cast<size_t>(1), req_todo / 10);
} else {
progress_interval = std::max(static_cast<size_t>(1), nclients / 10);
}
// Below timeout is not needed in case of timing-based benchmarking
// create timer that will go off every rate_period
ev_timer_init(&timeout_watcher, rate_period_timeout_w_cb, 0.,
config->rate_period);
timeout_watcher.data = this;
stats.req_stats.reserve(std::min(req_todo, max_samples));
stats.client_stats.reserve(std::min(nclients, max_samples));
if (config->is_timing_based_mode()) {
stats.req_stats.reserve(std::max(req_todo, max_samples));
stats.client_stats.reserve(std::max(nclients, max_samples));
} else {
stats.req_stats.reserve(std::min(req_todo, max_samples));
stats.client_stats.reserve(std::min(nclients, max_samples));
}
sampling_init(request_times_smp, req_todo, max_samples);
sampling_init(client_smp, nclients, max_samples);
sampling_init(request_times_smp, max_samples);
sampling_init(client_smp, max_samples);
ev_timer_init(&duration_watcher, duration_timeout_cb, config->duration, 0.);
duration_watcher.data = this;
ev_timer_init(&warmup_watcher, warmup_timeout_cb, config->warm_up_time, 0.);
warmup_watcher.data = this;
if (config->is_timing_based_mode()) {
current_phase = Phase::INITIAL_IDLE;
} else {
current_phase = Phase::MAIN_DURATION;
}
}
Worker::~Worker() {
ev_timer_stop(loop, &timeout_watcher);
ev_timer_stop(loop, &duration_watcher);
ev_timer_stop(loop, &warmup_watcher);
ev_loop_destroy(loop);
}
void Worker::stop_all_clients() {
for (auto client : clients) {
if (client && client->session) {
client->terminate_session();
}
}
}
void Worker::free_client(Client *deleted_client) {
for (auto &client : clients) {
if (client == deleted_client) {
client->req_todo = client->req_done;
stats.req_todo += client->req_todo;
auto index = &client - &clients[0];
clients[index] = NULL;
return;
}
}
}
void Worker::run() {
if (!config->is_rate_mode()) {
if (!config->is_rate_mode() && !config->is_timing_based_mode()) {
for (size_t i = 0; i < nclients; ++i) {
auto req_todo = nreqs_per_client;
if (nreqs_rem > 0) {
++req_todo;
--nreqs_rem;
}
auto client = make_unique<Client>(next_client_id++, this, req_todo);
if (client->connect() != 0) {
std::cerr << "client could not connect to host" << std::endl;
@@ -1215,27 +1354,45 @@ void Worker::run() {
client.release();
}
}
} else {
} else if (config->is_rate_mode()) {
ev_timer_again(loop, &timeout_watcher);
// call callback so that we don't waste the first rate_period
rate_period_timeout_w_cb(loop, &timeout_watcher, 0);
} else {
// call the callback to start for one single time
rate_period_timeout_w_cb(loop, &timeout_watcher, 0);
}
ev_run(loop, 0);
}
namespace {
template <typename Stats, typename Stat>
void sample(Sampling &smp, Stats &stats, Stat *s) {
++smp.n;
if (stats.size() < smp.max_samples) {
stats.push_back(*s);
return;
}
auto d = std::uniform_int_distribution<unsigned long>(0, smp.n - 1);
auto i = d(gen);
if (i < smp.max_samples) {
stats[i] = *s;
}
}
} // namespace
void Worker::sample_req_stat(RequestStat *req_stat) {
stats.req_stats.push_back(*req_stat);
assert(stats.req_stats.size() <= max_samples);
sample(request_times_smp, stats.req_stats, req_stat);
}
void Worker::sample_client_stat(ClientStat *cstat) {
stats.client_stats.push_back(*cstat);
assert(stats.client_stats.size() <= max_samples);
sample(client_smp, stats.client_stats, cstat);
}
void Worker::report_progress() {
if (id != 0 || config->is_rate_mode() || stats.req_done % progress_interval) {
if (id != 0 || config->is_rate_mode() || stats.req_done % progress_interval ||
config->is_timing_based_mode()) {
return;
}
@@ -1313,14 +1470,10 @@ process_time_stats(const std::vector<std::unique_ptr<Worker>> &workers) {
size_t nclient_times = 0;
for (const auto &w : workers) {
nrequest_times += w->stats.req_stats.size();
if (w->request_times_smp.interval != 0.) {
request_times_sampling = true;
}
request_times_sampling = w->request_times_smp.n > w->stats.req_stats.size();
nclient_times += w->stats.client_stats.size();
if (w->client_smp.interval != 0.) {
client_times_sampling = true;
}
client_times_sampling = w->client_smp.n > w->stats.client_stats.size();
}
std::vector<double> request_times;
@@ -1581,12 +1734,27 @@ std::unique_ptr<Worker> create_worker(uint32_t id, SSL_CTX *ssl_ctx,
<< util::duration_str(config.rate_period) << " ";
}
std::cout << "spawning thread #" << id << ": " << nclients
<< " total client(s). " << rate_report.str() << nreqs
<< " total requests" << std::endl;
if (config.is_timing_based_mode()) {
std::cout << "spawning thread #" << id << ": " << nclients
<< " total client(s). Timing-based test with "
<< config.warm_up_time << "s of warm-up time and "
<< config.duration << "s of main duration for measurements."
<< std::endl;
} else {
std::cout << "spawning thread #" << id << ": " << nclients
<< " total client(s). " << rate_report.str() << nreqs
<< " total requests" << std::endl;
}
return make_unique<Worker>(id, ssl_ctx, nreqs, nclients, rate, max_samples,
&config);
if (config.is_rate_mode()) {
return make_unique<Worker>(id, ssl_ctx, nreqs, nclients, rate, max_samples,
&config);
} else {
// Here rate is same as client because the rate_timeout callback
// will be called only once
return make_unique<Worker>(id, ssl_ctx, nreqs, nclients, nclients,
max_samples, &config);
}
}
} // namespace
@@ -1652,18 +1820,18 @@ Options:
Number of requests across all clients. If it is used
with --timing-script-file option, this option specifies
the number of requests each client performs rather than
the number of requests across all clients.
the number of requests across all clients. This option
is ignored if timing-based benchmarking is enabled (see
--duration option).
Default: )"
<< config.nreqs << R"(
-c, --clients=<N>
Number of concurrent clients. With -r option, this
specifies the maximum number of connections to be made.
Default: )"
<< config.nclients << R"(
Default: )" << config.nclients << R"(
-t, --threads=<N>
Number of native threads.
Default: )"
<< config.nthreads << R"(
Default: )" << config.nthreads << R"(
-i, --input-file=<PATH>
Path of a file with multiple URIs are separated by EOLs.
This option will disable URIs getting from command-line.
@@ -1689,8 +1857,7 @@ Options:
(2**<N>)-1. For SPDY, if <N> is strictly less than 16,
this option is ignored. Otherwise 2**<N> is used for
SPDY.
Default: )"
<< config.connection_window_bits << R"(
Default: )" << config.connection_window_bits << R"(
-H, --header=<HEADER>
Add/Override a header to the requests.
--ciphers=<SUITE>
@@ -1710,8 +1877,7 @@ Options:
Available protocols: )";
#endif // !HAVE_SPDYLAY
out << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"( and
)"
<< NGHTTP2_H1_1 << R"(
)" << NGHTTP2_H1_1 << R"(
Default: )"
<< NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"(
-d, --data=<PATH>
@@ -1737,6 +1903,13 @@ Options:
length of the period in time. This option is ignored if
the rate option is not used. The default value for this
option is 1s.
-D, --duration=<N>
Specifies the main duration for the measurements in case
of timing-based benchmarking.
--warm-up-time=<DURATION>
Specifies the time period before starting the actual
measurements, in case of timing-based benchmarking.
Needs to provided along with -D option.
-T, --connection-active-timeout=<DURATION>
Specifies the maximum time that h2load is willing to
keep a connection open, regardless of the activity on
@@ -1787,8 +1960,7 @@ Options:
NPN. The parameter must be delimited by a single comma
only and any white spaces are treated as a part of
protocol string.
Default: )"
<< DEFAULT_NPN_LIST << R"(
Default: )" << DEFAULT_NPN_LIST << R"(
--h1 Short hand for --npn-list=http/1.1
--no-tls-proto=http/1.1, which effectively force
http/1.1 for both http and https URI.
@@ -1816,16 +1988,15 @@ Options:
The <DURATION> argument is an integer and an optional unit (e.g., 1s
is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms
(hours, minutes, seconds and milliseconds, respectively). If a unit
is omitted, a second is used as unit.)"
<< std::endl;
is omitted, a second is used as unit.)" << std::endl;
}
} // namespace
int main(int argc, char **argv) {
ssl::libssl_init();
tls::libssl_init();
#ifndef NOTHREADS
ssl::LibsslGlobalLock lock;
tls::LibsslGlobalLock lock;
#endif // NOTHREADS
std::string datafile;
@@ -1850,6 +2021,7 @@ int main(int argc, char **argv) {
{"rate", required_argument, nullptr, 'r'},
{"connection-active-timeout", required_argument, nullptr, 'T'},
{"connection-inactivity-timeout", required_argument, nullptr, 'N'},
{"duration", required_argument, nullptr, 'D'},
{"timing-script-file", required_argument, &flag, 3},
{"base-uri", required_argument, nullptr, 'B'},
{"npn-list", required_argument, &flag, 4},
@@ -1857,10 +2029,12 @@ int main(int argc, char **argv) {
{"h1", no_argument, &flag, 6},
{"header-table-size", required_argument, &flag, 7},
{"encoder-header-table-size", required_argument, &flag, 8},
{"warm-up-time", required_argument, &flag, 9},
{nullptr, 0, nullptr, 0}};
int option_index = 0;
auto c = getopt_long(argc, argv, "hvW:c:d:m:n:p:t:w:H:i:r:T:N:B:",
long_options, &option_index);
auto c = getopt_long(argc, argv,
"hvW:c:d:m:n:p:t:w:H:i:r:T:N:D:B:", long_options,
&option_index);
if (c == -1) {
break;
}
@@ -2015,6 +2189,14 @@ int main(int argc, char **argv) {
config.base_uri = arg.str();
break;
}
case 'D':
config.duration = strtoul(optarg, nullptr, 10);
if (config.duration == 0) {
std::cerr << "-D: the main duration for timing-based benchmarking "
<< "must be positive." << std::endl;
exit(EXIT_FAILURE);
}
break;
case 'v':
config.verbose = true;
break;
@@ -2071,6 +2253,14 @@ int main(int argc, char **argv) {
exit(EXIT_FAILURE);
}
break;
case 9:
// --warm-up-time
config.warm_up_time = util::parse_duration_with_unit(optarg);
if (!std::isfinite(config.warm_up_time)) {
std::cerr << "--warm-up-time: value error " << optarg << std::endl;
exit(EXIT_FAILURE);
}
break;
}
break;
default:
@@ -2156,8 +2346,9 @@ int main(int argc, char **argv) {
exit(EXIT_FAILURE);
}
if (config.nreqs == 0) {
std::cerr << "-n: the number of requests must be strictly greater than 0."
if (config.nreqs == 0 && !config.is_timing_based_mode()) {
std::cerr << "-n: the number of requests must be strictly greater than 0 "
"if timing-based test is not being run."
<< std::endl;
exit(EXIT_FAILURE);
}
@@ -2181,7 +2372,8 @@ int main(int argc, char **argv) {
// With timing script, we don't distribute config.nreqs to each
// client or thread.
if (!config.timing_script && config.nreqs < config.nclients) {
if (!config.timing_script && config.nreqs < config.nclients &&
!config.is_timing_based_mode()) {
std::cerr << "-n, -c: the number of requests must be greater than or "
<< "equal to the clients." << std::endl;
exit(EXIT_FAILURE);
@@ -2189,11 +2381,14 @@ int main(int argc, char **argv) {
if (config.nclients < config.nthreads) {
std::cerr << "-c, -t: the number of clients must be greater than or equal "
"to the number of threads."
<< std::endl;
<< "to the number of threads." << std::endl;
exit(EXIT_FAILURE);
}
if (config.is_timing_based_mode()) {
config.nreqs = 0;
}
if (config.is_rate_mode()) {
if (config.rate < config.nthreads) {
std::cerr << "-r, -t: the connection rate must be greater than or equal "
@@ -2242,9 +2437,9 @@ int main(int argc, char **argv) {
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
if (nghttp2::ssl::ssl_ctx_set_proto_versions(
ssl_ctx, nghttp2::ssl::NGHTTP2_TLS_MIN_VERSION,
nghttp2::ssl::NGHTTP2_TLS_MAX_VERSION) != 0) {
if (nghttp2::tls::ssl_ctx_set_proto_versions(
ssl_ctx, nghttp2::tls::NGHTTP2_TLS_MIN_VERSION,
nghttp2::tls::NGHTTP2_TLS_MAX_VERSION) != 0) {
std::cerr << "Could not set TLS versions" << std::endl;
exit(EXIT_FAILURE);
}
@@ -2527,7 +2722,7 @@ int main(int argc, char **argv) {
// Requests which have not been issued due to connection errors, are
// counted towards req_failed and req_error.
auto req_not_issued =
stats.req_todo - stats.req_status_success - stats.req_failed;
(stats.req_todo - stats.req_status_success - stats.req_failed);
stats.req_failed += req_not_issued;
stats.req_error += req_not_issued;
@@ -2538,10 +2733,17 @@ int main(int argc, char **argv) {
double rps = 0;
int64_t bps = 0;
if (duration.count() > 0) {
auto secd = std::chrono::duration_cast<
std::chrono::duration<double, std::chrono::seconds::period>>(duration);
rps = stats.req_success / secd.count();
bps = stats.bytes_total / secd.count();
if (config.is_timing_based_mode()) {
// we only want to consider the main duration if warm-up is given
rps = stats.req_success / config.duration;
bps = stats.bytes_total / config.duration;
} else {
auto secd = std::chrono::duration_cast<
std::chrono::duration<double, std::chrono::seconds::period>>(
duration);
rps = stats.req_success / secd.count();
bps = stats.bytes_total / secd.count();
}
}
double header_space_savings = 0.;
@@ -2554,14 +2756,14 @@ int main(int argc, char **argv) {
finished in )"
<< util::format_duration(duration) << ", " << rps << " req/s, "
<< util::utos_funit(bps) << R"(B/s
requests: )" << stats.req_todo
<< " total, " << stats.req_started << " started, " << stats.req_done
<< " done, " << stats.req_status_success << " succeeded, "
<< stats.req_failed << " failed, " << stats.req_error
<< " errored, " << stats.req_timedout << R"( timeout
status codes: )"
<< stats.status[2] << " 2xx, " << stats.status[3] << " 3xx, "
<< stats.status[4] << " 4xx, " << stats.status[5] << R"( 5xx
requests: )" << stats.req_todo << " total, "
<< stats.req_started << " started, " << stats.req_done << " done, "
<< stats.req_status_success << " succeeded, " << stats.req_failed
<< " failed, " << stats.req_error << " errored, "
<< stats.req_timedout << R"( timeout
status codes: )" << stats.status[2] << " 2xx, "
<< stats.status[3] << " 3xx, " << stats.status[4] << " 4xx, "
<< stats.status[5] << R"( 5xx
traffic: )" << util::utos_funit(stats.bytes_total)
<< "B (" << stats.bytes_total << ") total, "
<< util::utos_funit(stats.bytes_head) << "B (" << stats.bytes_head
@@ -2569,12 +2771,12 @@ traffic: )" << util::utos_funit(stats.bytes_total)
<< "%), " << util::utos_funit(stats.bytes_body) << "B ("
<< stats.bytes_body << R"() data
min max mean sd +/- sd
time for request: )"
<< 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.mean) << " "
<< std::setw(10) << util::format_duration(ts.request.sd)
<< std::setw(9) << util::dtos(ts.request.within_sd) << "%"
time for request: )" << 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.mean) << " " << std::setw(10)
<< util::format_duration(ts.request.sd) << std::setw(9)
<< util::dtos(ts.request.within_sd) << "%"
<< "\ntime for connect: " << std::setw(10)
<< util::format_duration(ts.connect.min) << " " << std::setw(10)
<< util::format_duration(ts.connect.max) << " " << std::setw(10)

View File

@@ -85,6 +85,10 @@ struct Config {
// rate at which connections should be made
size_t rate;
ev_tstamp rate_period;
// amount of time for main measurements in timing-based test
ev_tstamp duration;
// amount of time to wait before starting measurements in timing-based test
ev_tstamp warm_up_time;
// amount of time to wait for activity on a given connection
ev_tstamp conn_active_timeout;
// amount of time to wait after the last request is made on a connection
@@ -118,6 +122,7 @@ struct Config {
~Config();
bool is_rate_mode() const;
bool is_timing_based_mode() const;
bool has_base_uri() const;
};
@@ -139,7 +144,7 @@ struct ClientStat {
// time client end (i.e., client somehow processed all requests it
// is responsible for, and disconnected)
std::chrono::steady_clock::time_point client_end_time;
// The number of requests completed successfull, but not necessarily
// The number of requests completed successful, but not necessarily
// means successful HTTP status code.
size_t req_success;
@@ -180,7 +185,7 @@ struct Stats {
size_t req_started;
// The number of requests finished
size_t req_done;
// The number of requests completed successfull, but not necessarily
// The number of requests completed successful, but not necessarily
// means successful HTTP status code.
size_t req_success;
// The number of requests marked as success. HTTP status code is
@@ -215,15 +220,21 @@ struct Stats {
enum ClientState { CLIENT_IDLE, CLIENT_CONNECTED };
// This type tells whether the client is in warmup phase or not or is over
enum class Phase {
INITIAL_IDLE, // Initial idle state before warm-up phase
WARM_UP, // Warm up phase when no measurements are done
MAIN_DURATION, // Main measurement phase; if timing-based
// test is not run, this is the default phase
DURATION_OVER // This phase occurs after the measurements are over
};
struct Client;
// We use systematic sampling method
// We use reservoir sampling method
struct Sampling {
// sampling interval
double interval;
// cumulative value of interval, and the next point is the integer
// rounded up from this value.
double point;
// maximum number of samples
size_t max_samples;
// number of samples seen, including discarded samples.
size_t n;
};
@@ -253,6 +264,15 @@ struct Worker {
ev_timer timeout_watcher;
// The next client ID this worker assigns
uint32_t next_client_id;
// Keeps track of the current phase (for timing-based experiment) for the
// worker
Phase current_phase;
// We need to keep track of the clients in order to stop them when needed
std::vector<Client *> clients;
// This is only active when there is not a bounded number of requests
// specified
ev_timer duration_watcher;
ev_timer warmup_watcher;
Worker(uint32_t id, SSL_CTX *ssl_ctx, size_t nreq_todo, size_t nclients,
size_t rate, size_t max_samples, Config *config);
@@ -263,6 +283,10 @@ struct Worker {
void sample_client_stat(ClientStat *cstat);
void report_progress();
void report_rate_progress();
// This function calls the destructors of all the clients.
void stop_all_clients();
// This function frees a client from the list of clients for this Worker.
void free_client(Client *);
};
struct Stream {

View File

@@ -26,6 +26,7 @@
#include <cassert>
#include <cerrno>
#include <iostream>
#include "h2load.h"
#include "util.h"
@@ -52,6 +53,15 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
}
client->on_header(frame->hd.stream_id, name, namelen, value, valuelen);
client->worker->stats.bytes_head_decomp += namelen + valuelen;
if (client->worker->config->verbose) {
std::cout << "[stream_id=" << frame->hd.stream_id << "] ";
std::cout.write(reinterpret_cast<const char *>(name), namelen);
std::cout << ": ";
std::cout.write(reinterpret_cast<const char *>(value), valuelen);
std::cout << "\n";
}
return 0;
}
} // namespace
@@ -180,6 +190,9 @@ ssize_t send_callback(nghttp2_session *session, const uint8_t *data,
void Http2Session::on_connect() {
int rv;
// This is required with --disable-assert.
(void)rv;
nghttp2_session_callbacks *callbacks;
nghttp2_session_callbacks_new(&callbacks);

View File

@@ -358,15 +358,21 @@ nghttp2_nv make_nv_nocopy(const StringRef &name, const StringRef &value,
namespace {
void copy_headers_to_nva_internal(std::vector<nghttp2_nv> &nva,
const HeaderRefs &headers, uint8_t nv_flags) {
for (auto &kv : headers) {
if (kv.name.empty() || kv.name[0] == ':') {
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) {
switch (kv->token) {
case HD_COOKIE:
case HD_CONNECTION:
case HD_FORWARDED:
case HD_HOST:
case HD_HTTP2_SETTINGS:
case HD_KEEP_ALIVE:
@@ -375,51 +381,162 @@ void copy_headers_to_nva_internal(std::vector<nghttp2_nv> &nva,
case HD_TE:
case HD_TRANSFER_ENCODING:
case HD_UPGRADE:
case HD_VIA:
case HD_X_FORWARDED_FOR:
case HD_X_FORWARDED_PROTO:
continue;
case HD_FORWARDED:
if (flags & HDOP_STRIP_FORWARDED) {
continue;
}
if (it_forwarded == std::end(headers)) {
it_forwarded = it;
continue;
}
kv = &(*it_forwarded);
it_forwarded = it;
break;
case HD_X_FORWARDED_FOR:
if (flags & HDOP_STRIP_X_FORWARDED_FOR) {
continue;
}
if (it_xff == std::end(headers)) {
it_xff = it;
continue;
}
kv = &(*it_xff);
it_xff = it;
break;
case HD_X_FORWARDED_PROTO:
if (flags & HDOP_STRIP_X_FORWARDED_PROTO) {
continue;
}
if (it_xfp == std::end(headers)) {
it_xfp = it;
continue;
}
kv = &(*it_xfp);
it_xfp = it;
break;
case HD_VIA:
if (flags & HDOP_STRIP_VIA) {
continue;
}
if (it_via == std::end(headers)) {
it_via = it;
continue;
}
kv = &(*it_via);
it_via = it;
break;
case HD_NGHTTPX_0RTT_UNIQ:
if (flags & HDOP_STRIP_NGHTTPX_ZERO_RTT_UNIQ) {
continue;
}
break;
}
nva.push_back(make_nv_internal(kv.name, kv.value, kv.no_index, nv_flags));
nva.push_back(
make_nv_internal(kv->name, kv->value, kv->no_index, nv_flags));
}
}
} // namespace
void copy_headers_to_nva(std::vector<nghttp2_nv> &nva,
const HeaderRefs &headers) {
copy_headers_to_nva_internal(nva, headers, NGHTTP2_NV_FLAG_NONE);
const HeaderRefs &headers, uint32_t flags) {
copy_headers_to_nva_internal(nva, headers, NGHTTP2_NV_FLAG_NONE, flags);
}
void copy_headers_to_nva_nocopy(std::vector<nghttp2_nv> &nva,
const HeaderRefs &headers) {
copy_headers_to_nva_internal(nva, headers, NGHTTP2_NV_FLAG_NO_COPY_NAME |
NGHTTP2_NV_FLAG_NO_COPY_VALUE);
const HeaderRefs &headers, uint32_t flags) {
copy_headers_to_nva_internal(
nva, headers,
NGHTTP2_NV_FLAG_NO_COPY_NAME | NGHTTP2_NV_FLAG_NO_COPY_VALUE, flags);
}
void build_http1_headers_from_headers(DefaultMemchunks *buf,
const HeaderRefs &headers) {
for (auto &kv : headers) {
if (kv.name.empty() || kv.name[0] == ':') {
const HeaderRefs &headers,
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) {
switch (kv->token) {
case HD_CONNECTION:
case HD_COOKIE:
case HD_FORWARDED:
case HD_HOST:
case HD_HTTP2_SETTINGS:
case HD_KEEP_ALIVE:
case HD_PROXY_CONNECTION:
case HD_SERVER:
case HD_UPGRADE:
case HD_VIA:
case HD_X_FORWARDED_FOR:
case HD_X_FORWARDED_PROTO:
continue;
case HD_FORWARDED:
if (flags & HDOP_STRIP_FORWARDED) {
continue;
}
if (it_forwarded == std::end(headers)) {
it_forwarded = it;
continue;
}
kv = &(*it_forwarded);
it_forwarded = it;
break;
case HD_X_FORWARDED_FOR:
if (flags & HDOP_STRIP_X_FORWARDED_FOR) {
continue;
}
if (it_xff == std::end(headers)) {
it_xff = it;
continue;
}
kv = &(*it_xff);
it_xff = it;
break;
case HD_X_FORWARDED_PROTO:
if (flags & HDOP_STRIP_X_FORWARDED_PROTO) {
continue;
}
if (it_xfp == std::end(headers)) {
it_xfp = it;
continue;
}
kv = &(*it_xfp);
it_xfp = it;
break;
case HD_VIA:
if (flags & HDOP_STRIP_VIA) {
continue;
}
if (it_via == std::end(headers)) {
it_via = it;
continue;
}
kv = &(*it_via);
it_via = it;
break;
}
capitalize(buf, kv.name);
capitalize(buf, kv->name);
buf->append(": ");
buf->append(kv.value);
buf->append(kv->value);
buf->append("\r\n");
}
}
@@ -808,6 +925,11 @@ int lookup_token(const uint8_t *name, size_t namelen) {
return HD_X_FORWARDED_PROTO;
}
break;
case 'q':
if (util::streq_l("nghttpx-0rtt-uni", name, 16)) {
return HD_NGHTTPX_0RTT_UNIQ;
}
break;
}
break;
}
@@ -1486,7 +1608,7 @@ template <typename InputIt> InputIt eat_file(InputIt first, InputIt last) {
for (; p != first && *(p - 1) != '/'; --p)
;
if (p == first) {
// this should not happend in normal case, where we expect path
// this should not happened in normal case, where we expect path
// starts with '/'
*first++ = '/';
return first;

View File

@@ -187,24 +187,54 @@ nghttp2_nv make_nv_ls_nocopy(const char (&name)[N], const StringRef &value) {
NGHTTP2_NV_FLAG_NO_COPY_NAME | NGHTTP2_NV_FLAG_NO_COPY_VALUE};
}
enum HeaderBuildOp {
HDOP_NONE,
// Forwarded header fields must be stripped. If this flag is not
// set, all Forwarded header fields other than last one are added.
HDOP_STRIP_FORWARDED = 1,
// X-Forwarded-For header fields must be stripped. If this flag is
// not set, all X-Forwarded-For header fields other than last one
// are added.
HDOP_STRIP_X_FORWARDED_FOR = 1 << 1,
// X-Forwarded-Proto header fields must be stripped. If this flag
// is not set, all X-Forwarded-Proto header fields other than last
// one are added.
HDOP_STRIP_X_FORWARDED_PROTO = 1 << 2,
// Via header fields must be stripped. If this flag is not set, all
// Via header fields other than last one are added.
HDOP_STRIP_VIA = 1 << 3,
// nghttpx-0rtt-uniq header fields must be stripped. If this flag
// is not set, all nghttpx-0rtt-uniq header fields are added.
HDOP_STRIP_NGHTTPX_ZERO_RTT_UNIQ = 1 << 4,
// Strip above all header fields.
HDOP_STRIP_ALL = HDOP_STRIP_FORWARDED | HDOP_STRIP_X_FORWARDED_FOR |
HDOP_STRIP_X_FORWARDED_PROTO | HDOP_STRIP_VIA |
HDOP_STRIP_NGHTTPX_ZERO_RTT_UNIQ,
};
// 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/2 spec and headers
// which require special handling (i.e. via), are not copied.
// 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<nghttp2_nv> &nva,
const HeaderRefs &headers);
const HeaderRefs &headers, uint32_t flags);
// Just like copy_headers_to_nva(), but this adds
// NGHTTP2_NV_FLAG_NO_COPY_NAME and NGHTTP2_NV_FLAG_NO_COPY_VALUE.
void copy_headers_to_nva_nocopy(std::vector<nghttp2_nv> &nva,
const HeaderRefs &headers);
const HeaderRefs &headers, uint32_t flags);
// Appends HTTP/1.1 style header lines to |buf| from headers in
// |headers|. |headers| must be indexed before this call (its
// element's token field is assigned). Certain headers, which
// requires special handling (i.e. via and cookie), are not appended.
// |flags| is one or more of HeaderBuildOp flags. They tell function
// that certain header fields should not be added.
void build_http1_headers_from_headers(DefaultMemchunks *buf,
const HeaderRefs &headers);
const HeaderRefs &headers,
uint32_t flags);
// Return positive window_size_increment if WINDOW_UPDATE should be
// sent for the stream |stream_id|. If |stream_id| == 0, this function
@@ -242,7 +272,7 @@ void erase_header(HeaderRef *hd);
//
// This function returns the new rewritten URI on success. If the
// location URI is not subject to the rewrite, this function returns
// emtpy string.
// empty string.
StringRef rewrite_location_uri(BlockAllocator &balloc, const StringRef &uri,
const http_parser_url &u,
const StringRef &match_host,
@@ -286,6 +316,7 @@ enum {
HD_KEEP_ALIVE,
HD_LINK,
HD_LOCATION,
HD_NGHTTPX_0RTT_UNIQ,
HD_PROXY_CONNECTION,
HD_SERVER,
HD_TE,

View File

@@ -150,11 +150,33 @@ auto headers = HeaderRefs{
{StringRef::from_lit("zulu"), StringRef::from_lit("12")}};
} // namespace
namespace {
auto headers2 = HeaderRefs{
{StringRef::from_lit("x-forwarded-for"), StringRef::from_lit("xff1"), false,
http2::HD_X_FORWARDED_FOR},
{StringRef::from_lit("x-forwarded-for"), StringRef::from_lit("xff2"), false,
http2::HD_X_FORWARDED_FOR},
{StringRef::from_lit("x-forwarded-proto"), StringRef::from_lit("xfp1"),
false, http2::HD_X_FORWARDED_PROTO},
{StringRef::from_lit("x-forwarded-proto"), StringRef::from_lit("xfp2"),
false, http2::HD_X_FORWARDED_PROTO},
{StringRef::from_lit("forwarded"), StringRef::from_lit("fwd1"), false,
http2::HD_FORWARDED},
{StringRef::from_lit("forwarded"), StringRef::from_lit("fwd2"), false,
http2::HD_FORWARDED},
{StringRef::from_lit("via"), StringRef::from_lit("via1"), false,
http2::HD_VIA},
{StringRef::from_lit("via"), StringRef::from_lit("via2"), false,
http2::HD_VIA},
};
} // namespace
void test_http2_copy_headers_to_nva(void) {
auto ans = std::vector<int>{0, 1, 4, 5, 6, 7, 12};
std::vector<nghttp2_nv> nva;
http2::copy_headers_to_nva_nocopy(nva, headers);
http2::copy_headers_to_nva_nocopy(nva, headers,
http2::HDOP_STRIP_X_FORWARDED_FOR);
CU_ASSERT(7 == nva.size());
for (size_t i = 0; i < ans.size(); ++i) {
check_nv(headers[ans[i]], &nva[i]);
@@ -169,7 +191,7 @@ void test_http2_copy_headers_to_nva(void) {
}
nva.clear();
http2::copy_headers_to_nva(nva, headers);
http2::copy_headers_to_nva(nva, headers, http2::HDOP_STRIP_X_FORWARDED_FOR);
CU_ASSERT(7 == nva.size());
for (size_t i = 0; i < ans.size(); ++i) {
check_nv(headers[ans[i]], &nva[i]);
@@ -180,12 +202,27 @@ void test_http2_copy_headers_to_nva(void) {
CU_ASSERT(NGHTTP2_NV_FLAG_NONE == nva[i].flags);
}
}
nva.clear();
auto ans2 = std::vector<int>{0, 2, 4, 6};
http2::copy_headers_to_nva(nva, headers2, http2::HDOP_NONE);
CU_ASSERT(ans2.size() == nva.size());
for (size_t i = 0; i < ans2.size(); ++i) {
check_nv(headers2[ans2[i]], &nva[i]);
}
nva.clear();
http2::copy_headers_to_nva(nva, headers2, http2::HDOP_STRIP_ALL);
CU_ASSERT(nva.empty());
}
void test_http2_build_http1_headers_from_headers(void) {
MemchunkPool pool;
DefaultMemchunks buf(&pool);
http2::build_http1_headers_from_headers(&buf, headers);
http2::build_http1_headers_from_headers(&buf, headers,
http2::HDOP_STRIP_X_FORWARDED_FOR);
auto hdrs = std::string(buf.head->pos, buf.head->last);
CU_ASSERT("Alpha: 0\r\n"
"Bravo: 1\r\n"
@@ -196,6 +233,21 @@ void test_http2_build_http1_headers_from_headers(void) {
"Te: 8\r\n"
"Te: 9\r\n"
"Zulu: 12\r\n" == hdrs);
buf.reset();
http2::build_http1_headers_from_headers(&buf, headers2, http2::HDOP_NONE);
hdrs = std::string(buf.head->pos, buf.head->last);
CU_ASSERT("X-Forwarded-For: xff1\r\n"
"X-Forwarded-Proto: xfp1\r\n"
"Forwarded: fwd1\r\n"
"Via: via1\r\n" == hdrs);
buf.reset();
http2::build_http1_headers_from_headers(&buf, headers2,
http2::HDOP_STRIP_ALL);
CU_ASSERT(0 == buf.rleft());
}
void test_http2_lws(void) {

View File

@@ -118,6 +118,28 @@ private:
std::unique_ptr<request_impl> impl_;
};
// Wrapper around an nghttp2_priority_spec.
class priority_spec {
public:
// The default ctor is used only by sentinel values.
priority_spec() = default;
// Create a priority spec with the given priority settings.
explicit priority_spec(const int32_t stream_id, const int32_t weight,
const bool exclusive = false);
// Return a pointer to a valid nghttp2 priority spec, or null.
const nghttp2_priority_spec *get() const;
// Indicates whether or not this spec is valid (i.e. was constructed with
// values).
const bool valid() const;
private:
nghttp2_priority_spec spec_;
bool valid_ = false;
};
class session_impl;
class session {
@@ -177,7 +199,8 @@ public:
// succeeds, or nullptr and |ec| contains error message.
const request *submit(boost::system::error_code &ec,
const std::string &method, const std::string &uri,
header_map h = header_map{}) const;
header_map h = header_map{},
priority_spec prio = priority_spec()) const;
// Submits request to server using |method| (e.g., "GET"), |uri|
// (e.g., "http://localhost/") and optionally additional header
@@ -186,7 +209,8 @@ public:
// contains error message.
const request *submit(boost::system::error_code &ec,
const std::string &method, const std::string &uri,
std::string data, header_map h = header_map{}) const;
std::string data, header_map h = header_map{},
priority_spec prio = priority_spec()) const;
// Submits request to server using |method| (e.g., "GET"), |uri|
// (e.g., "http://localhost/") and optionally additional header
@@ -195,7 +219,8 @@ public:
// nullptr and |ec| contains error message.
const request *submit(boost::system::error_code &ec,
const std::string &method, const std::string &uri,
generator_cb cb, header_map h = header_map{}) const;
generator_cb cb, header_map h = header_map{},
priority_spec prio = priority_spec()) const;
private:
std::shared_ptr<session_impl> impl_;

View File

@@ -48,7 +48,9 @@
namespace nghttp2 {
typedef struct { int dump_header_table; } inflate_config;
typedef struct {
int dump_header_table;
} inflate_config;
static inflate_config config;

View File

@@ -28,7 +28,15 @@
#include "nghttp2_config.h"
#include <limits.h>
#ifdef _WIN32
/* Structure for scatter/gather I/O. */
struct iovec {
void *iov_base; /* Pointer to data. */
size_t iov_len; /* Length of data. */
};
#else // !_WIN32
#include <sys/uio.h>
#endif // !_WIN32
#include <cassert>
#include <cstring>
@@ -50,23 +58,21 @@ namespace nghttp2 {
#endif // !defined(IOV_MAX) || IOV_MAX >= DEFAULT_WR_IOVCNT
template <size_t N> struct Memchunk {
Memchunk(std::unique_ptr<Memchunk> next_chunk)
: pos(std::begin(buf)),
last(pos),
knext(std::move(next_chunk)),
next(nullptr) {}
Memchunk(Memchunk *next_chunk)
: pos(std::begin(buf)), last(pos), knext(next_chunk), next(nullptr) {}
size_t len() const { return last - pos; }
size_t left() const { return std::end(buf) - last; }
void reset() { pos = last = std::begin(buf); }
std::array<uint8_t, N> buf;
uint8_t *pos, *last;
std::unique_ptr<Memchunk> knext;
Memchunk *knext;
Memchunk *next;
static const size_t size = N;
};
template <typename T> struct Pool {
Pool() : pool(nullptr), freelist(nullptr), poolsize(0) {}
~Pool() { clear(); }
T *get() {
if (freelist) {
auto m = freelist;
@@ -76,9 +82,9 @@ template <typename T> struct Pool {
return m;
}
pool = make_unique<T>(std::move(pool));
pool = new T{pool};
poolsize += T::size;
return pool.get();
return pool;
}
void recycle(T *m) {
m->next = freelist;
@@ -86,11 +92,16 @@ template <typename T> struct Pool {
}
void clear() {
freelist = nullptr;
for (auto p = pool; p;) {
auto knext = p->knext;
delete p;
p = knext;
}
pool = nullptr;
poolsize = 0;
}
using value_type = T;
std::unique_ptr<T> pool;
T *pool;
T *freelist;
size_t poolsize;
};

View File

@@ -42,34 +42,34 @@ void test_pool_recycle(void) {
auto m1 = pool.get();
CU_ASSERT(m1 == pool.pool.get());
CU_ASSERT(m1 == pool.pool);
CU_ASSERT(MemchunkPool::value_type::size == pool.poolsize);
CU_ASSERT(nullptr == pool.freelist);
auto m2 = pool.get();
CU_ASSERT(m2 == pool.pool.get());
CU_ASSERT(m2 == pool.pool);
CU_ASSERT(2 * MemchunkPool::value_type::size == pool.poolsize);
CU_ASSERT(nullptr == pool.freelist);
CU_ASSERT(m1 == m2->knext.get());
CU_ASSERT(nullptr == m1->knext.get());
CU_ASSERT(m1 == m2->knext);
CU_ASSERT(nullptr == m1->knext);
auto m3 = pool.get();
CU_ASSERT(m3 == pool.pool.get());
CU_ASSERT(m3 == pool.pool);
CU_ASSERT(3 * MemchunkPool::value_type::size == pool.poolsize);
CU_ASSERT(nullptr == pool.freelist);
pool.recycle(m3);
CU_ASSERT(m3 == pool.pool.get());
CU_ASSERT(m3 == pool.pool);
CU_ASSERT(3 * MemchunkPool::value_type::size == pool.poolsize);
CU_ASSERT(m3 == pool.freelist);
auto m4 = pool.get();
CU_ASSERT(m3 == m4);
CU_ASSERT(m4 == pool.pool.get());
CU_ASSERT(m4 == pool.pool);
CU_ASSERT(3 * MemchunkPool::value_type::size == pool.poolsize);
CU_ASSERT(nullptr == pool.freelist);

View File

@@ -33,7 +33,11 @@
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif // HAVE_SYS_SOCKET_H
#ifdef _WIN32
#include <ws2tcpip.h>
#else // !_WIN32
#include <sys/un.h>
#endif // !_WIN32
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif // HAVE_NETINET_IN_H
@@ -48,7 +52,9 @@ union sockaddr_union {
sockaddr sa;
sockaddr_in6 in6;
sockaddr_in in;
#ifndef _WIN32
sockaddr_un un;
#endif // !_WIN32
};
struct Address {

View File

@@ -57,7 +57,7 @@
#include "HtmlParser.h"
#include "util.h"
#include "base64.h"
#include "ssl.h"
#include "tls.h"
#include "template.h"
#ifndef O_BINARY
@@ -89,7 +89,11 @@ enum {
namespace {
constexpr auto anchors = std::array<Anchor, 5>{{
{3, 0, 201}, {5, 0, 101}, {7, 0, 1}, {9, 7, 1}, {11, 3, 1},
{3, 0, 201},
{5, 0, 101},
{7, 0, 1},
{9, 7, 1},
{11, 3, 1},
}};
} // namespace
@@ -116,7 +120,8 @@ Config::Config()
no_dep(false),
hexdump(false),
no_push(false),
expect_continue(false) {
expect_continue(false),
verify_peer(true) {
nghttp2_option_new(&http2_option);
nghttp2_option_set_peer_max_concurrent_streams(http2_option,
peer_max_concurrent_streams);
@@ -171,6 +176,8 @@ Request::~Request() { nghttp2_gzip_inflate_del(inflater); }
void Request::init_inflater() {
int rv;
// This is required with --disable-assert.
(void)rv;
rv = nghttp2_gzip_inflate_new(&inflater);
assert(rv == 0);
}
@@ -401,17 +408,10 @@ int htp_msg_begincb(http_parser *htp) {
}
} // namespace
namespace {
int htp_statuscb(http_parser *htp, const char *at, size_t length) {
auto client = static_cast<HttpClient *>(htp->data);
client->upgrade_response_status_code = htp->status_code;
return 0;
}
} // namespace
namespace {
int htp_msg_completecb(http_parser *htp) {
auto client = static_cast<HttpClient *>(htp->data);
client->upgrade_response_status_code = htp->status_code;
client->upgrade_response_complete = true;
return 0;
}
@@ -421,7 +421,7 @@ namespace {
constexpr http_parser_settings htp_hooks = {
htp_msg_begincb, // http_cb on_message_begin;
nullptr, // http_data_cb on_url;
htp_statuscb, // http_data_cb on_status;
nullptr, // http_data_cb on_status;
nullptr, // http_data_cb on_header_field;
nullptr, // http_data_cb on_header_value;
nullptr, // http_cb on_headers_complete;
@@ -646,6 +646,11 @@ int HttpClient::resolve_host(const std::string &host, uint16_t port) {
return 0;
}
namespace {
// Just returns 1 to continue handshake.
int verify_cb(int preverify_ok, X509_STORE_CTX *ctx) { return 1; }
} // namespace
int HttpClient::initiate_connection() {
int rv;
@@ -675,6 +680,17 @@ int HttpClient::initiate_connection() {
const auto &host_string =
config.host_override.empty() ? host : config.host_override;
#if (!defined(LIBRESSL_VERSION_NUMBER) && \
OPENSSL_VERSION_NUMBER >= 0x10002000L) || \
defined(OPENSSL_IS_BORINGSSL)
auto param = SSL_get0_param(ssl);
X509_VERIFY_PARAM_set_hostflags(param, 0);
X509_VERIFY_PARAM_set1_host(param, host_string.c_str(),
host_string.size());
#endif // (!defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >=
// 0x10002000L) || defined(OPENSSL_IS_BORINGSSL)
SSL_set_verify(ssl, SSL_VERIFY_PEER, verify_cb);
if (!util::numeric_host(host_string.c_str())) {
SSL_set_tlsext_host_name(ssl, host_string.c_str());
}
@@ -1295,6 +1311,14 @@ int HttpClient::tls_handshake() {
readfn = &HttpClient::read_tls;
writefn = &HttpClient::write_tls;
if (config.verify_peer) {
auto verify_res = SSL_get_verify_result(ssl);
if (verify_res != X509_V_OK) {
std::cerr << "[WARNING] Certificate verification failed: "
<< X509_verify_cert_error_string(verify_res) << std::endl;
}
}
if (connection_made() != 0) {
return -1;
}
@@ -2247,15 +2271,20 @@ int communicate(
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
if (nghttp2::ssl::ssl_ctx_set_proto_versions(
ssl_ctx, nghttp2::ssl::NGHTTP2_TLS_MIN_VERSION,
nghttp2::ssl::NGHTTP2_TLS_MAX_VERSION) != 0) {
if (SSL_CTX_set_default_verify_paths(ssl_ctx) != 1) {
std::cerr << "[WARNING] Could not load system trusted CA certificates: "
<< ERR_error_string(ERR_get_error(), nullptr) << std::endl;
}
if (nghttp2::tls::ssl_ctx_set_proto_versions(
ssl_ctx, nghttp2::tls::NGHTTP2_TLS_MIN_VERSION,
nghttp2::tls::NGHTTP2_TLS_MAX_VERSION) != 0) {
std::cerr << "[ERROR] Could not set TLS versions" << std::endl;
result = -1;
goto fin;
}
if (SSL_CTX_set_cipher_list(ssl_ctx, ssl::DEFAULT_CIPHER_LIST) == 0) {
if (SSL_CTX_set_cipher_list(ssl_ctx, tls::DEFAULT_CIPHER_LIST) == 0) {
std::cerr << "[ERROR] " << ERR_error_string(ERR_get_error(), nullptr)
<< std::endl;
result = -1;
@@ -2433,8 +2462,8 @@ int run(char **uris, int n) {
nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(
callbacks, verbose_on_invalid_frame_recv_callback);
nghttp2_session_callbacks_set_error_callback(callbacks,
verbose_error_callback);
nghttp2_session_callbacks_set_error_callback2(callbacks,
verbose_error_callback);
}
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
@@ -2701,6 +2730,9 @@ Options:
(up to a short timeout) until the server sends a 100
Continue interim response. This option is ignored unless
combined with the -d option.
-y, --no-verify-peer
Suppress warning on server certificate verification
failure.
--version Display version information and exit.
-h, --help Display this help and exit.
@@ -2718,7 +2750,7 @@ Options:
} // namespace
int main(int argc, char **argv) {
ssl::libssl_init();
tls::libssl_init();
bool color = false;
while (1) {
@@ -2742,6 +2774,7 @@ int main(int argc, char **argv) {
{"header-table-size", required_argument, nullptr, 'c'},
{"padding", required_argument, nullptr, 'b'},
{"har", required_argument, nullptr, 'r'},
{"no-verify-peer", no_argument, nullptr, 'y'},
{"cert", required_argument, &flag, 1},
{"key", required_argument, &flag, 2},
{"color", no_argument, &flag, 3},
@@ -2757,8 +2790,9 @@ int main(int argc, char **argv) {
{"encoder-header-table-size", required_argument, &flag, 14},
{nullptr, 0, nullptr, 0}};
int option_index = 0;
int c = getopt_long(argc, argv, "M:Oab:c:d:gm:np:r:hH:vst:uw:W:",
long_options, &option_index);
int c =
getopt_long(argc, argv, "M:Oab:c:d:m:np:r:hH:vst:uw:yW:", long_options,
&option_index);
if (c == -1) {
break;
}
@@ -2888,6 +2922,9 @@ int main(int argc, char **argv) {
config.min_header_table_size = std::min(config.min_header_table_size, n);
break;
}
case 'y':
config.verify_peer = false;
break;
case '?':
util::show_candidates(argv[optind - 1], long_options);
exit(EXIT_FAILURE);

View File

@@ -96,6 +96,7 @@ struct Config {
bool hexdump;
bool no_push;
bool expect_continue;
bool verify_peer;
};
enum class RequestState { INITIAL, ON_REQUEST, ON_RESPONSE, ON_COMPLETE };

View File

@@ -43,14 +43,14 @@ static size_t deflate_data(uint8_t *out, size_t outlen, const uint8_t *in,
zst.opaque = Z_NULL;
rv = deflateInit(&zst, Z_DEFAULT_COMPRESSION);
assert(rv == Z_OK);
CU_ASSERT(rv == Z_OK);
zst.avail_in = (unsigned int)inlen;
zst.next_in = (uint8_t *)in;
zst.avail_out = (unsigned int)outlen;
zst.next_out = out;
rv = deflate(&zst, Z_SYNC_FLUSH);
assert(rv == Z_OK);
CU_ASSERT(rv == Z_OK);
deflateEnd(&zst);

View File

@@ -48,7 +48,7 @@
#include "app_helper.h"
#include "HttpServer.h"
#include "util.h"
#include "ssl.h"
#include "tls.h"
namespace nghttp2 {
@@ -174,8 +174,7 @@ Options:
--mime-types-file=<PATH>
Path to file that contains MIME media types and the
extensions that represent them.
Default: )"
<< config.mime_types_file << R"(
Default: )" << config.mime_types_file << R"(
--no-content-length
Don't send content-length header field.
--version Display version information and exit.
@@ -190,10 +189,10 @@ Options:
} // namespace
int main(int argc, char **argv) {
ssl::libssl_init();
tls::libssl_init();
#ifndef NOTHREADS
ssl::LibsslGlobalLock lock;
tls::LibsslGlobalLock lock;
#endif // NOTHREADS
Config config;

View File

@@ -30,7 +30,7 @@
#include <string.h>
#include <CUnit/Basic.h>
// include test cases' include files here
#include "shrpx_ssl_test.h"
#include "shrpx_tls_test.h"
#include "shrpx_downstream_test.h"
#include "shrpx_config_test.h"
#include "shrpx_worker_test.h"
@@ -43,7 +43,7 @@
#include "shrpx_http_test.h"
#include "base64_test.h"
#include "shrpx_config.h"
#include "ssl.h"
#include "tls.h"
#include "shrpx_router_test.h"
#include "shrpx_log.h"
@@ -55,7 +55,7 @@ int main(int argc, char *argv[]) {
CU_pSuite pSuite = NULL;
unsigned int num_tests_failed;
nghttp2::ssl::libssl_init();
nghttp2::tls::libssl_init();
shrpx::create_config();
@@ -71,12 +71,12 @@ int main(int argc, char *argv[]) {
}
// add the tests to the suite
if (!CU_add_test(pSuite, "ssl_create_lookup_tree",
shrpx::test_shrpx_ssl_create_lookup_tree) ||
!CU_add_test(pSuite, "ssl_cert_lookup_tree_add_ssl_ctx",
shrpx::test_shrpx_ssl_cert_lookup_tree_add_ssl_ctx) ||
!CU_add_test(pSuite, "ssl_tls_hostname_match",
shrpx::test_shrpx_ssl_tls_hostname_match) ||
if (!CU_add_test(pSuite, "tls_create_lookup_tree",
shrpx::test_shrpx_tls_create_lookup_tree) ||
!CU_add_test(pSuite, "tls_cert_lookup_tree_add_ssl_ctx",
shrpx::test_shrpx_tls_cert_lookup_tree_add_ssl_ctx) ||
!CU_add_test(pSuite, "tls_tls_hostname_match",
shrpx::test_shrpx_tls_tls_hostname_match) ||
!CU_add_test(pSuite, "http2_add_header", shrpx::test_http2_add_header) ||
!CU_add_test(pSuite, "http2_get_header", shrpx::test_http2_get_header) ||
!CU_add_test(pSuite, "http2_copy_headers_to_nva",
@@ -115,6 +115,10 @@ int main(int argc, char *argv[]) {
shrpx::test_downstream_assemble_request_cookie) ||
!CU_add_test(pSuite, "downstream_rewrite_location_response_header",
shrpx::test_downstream_rewrite_location_response_header) ||
!CU_add_test(pSuite, "downstream_supports_non_final_response",
shrpx::test_downstream_supports_non_final_response) ||
!CU_add_test(pSuite, "downstream_find_affinity_cookie",
shrpx::test_downstream_find_affinity_cookie) ||
!CU_add_test(pSuite, "config_parse_header",
shrpx::test_shrpx_config_parse_header) ||
!CU_add_test(pSuite, "config_parse_log_format",
@@ -129,7 +133,11 @@ int main(int argc, char *argv[]) {
shrpx::test_shrpx_http_create_forwarded) ||
!CU_add_test(pSuite, "http_create_via_header_value",
shrpx::test_shrpx_http_create_via_header_value) ||
!CU_add_test(pSuite, "http_create_affinity_cookie",
shrpx::test_shrpx_http_create_affinity_cookie) ||
!CU_add_test(pSuite, "router_match", shrpx::test_shrpx_router_match) ||
!CU_add_test(pSuite, "router_match_wildcard",
shrpx::test_shrpx_router_match_wildcard) ||
!CU_add_test(pSuite, "router_match_prefix",
shrpx::test_shrpx_router_match_prefix) ||
!CU_add_test(pSuite, "util_streq", shrpx::test_util_streq) ||

View File

@@ -76,7 +76,7 @@
#include <nghttp2/nghttp2.h>
#include "shrpx_config.h"
#include "shrpx_ssl.h"
#include "shrpx_tls.h"
#include "shrpx_log_config.h"
#include "shrpx_worker.h"
#include "shrpx_http2_upstream.h"
@@ -88,7 +88,7 @@
#include "shrpx_log.h"
#include "util.h"
#include "app_helper.h"
#include "ssl.h"
#include "tls.h"
#include "template.h"
#include "allocator.h"
#include "ssl_compat.h"
@@ -1205,13 +1205,17 @@ pid_t fork_worker_process(int &main_ipc_fd,
return -1;
}
auto pid = fork();
auto config = get_config();
pid_t pid = 0;
if (!config->single_process) {
pid = fork();
}
if (pid == 0) {
ev_loop_fork(EV_DEFAULT);
auto config = get_config();
for (auto &addr : config->conn.listener.addrs) {
util::make_socket_closeonexec(addr.fd);
}
@@ -1230,22 +1234,37 @@ pid_t fork_worker_process(int &main_ipc_fd,
LOG(FATAL) << "Unblocking all signals failed: "
<< xsi_strerror(error, errbuf.data(), errbuf.size());
nghttp2_Exit(EXIT_FAILURE);
if (config->single_process) {
exit(EXIT_FAILURE);
} else {
nghttp2_Exit(EXIT_FAILURE);
}
}
if (!config->single_process) {
close(ipc_fd[1]);
}
close(ipc_fd[1]);
WorkerProcessConfig wpconf{ipc_fd[0]};
rv = worker_process_event_loop(&wpconf);
if (rv != 0) {
LOG(FATAL) << "Worker process returned error";
nghttp2_Exit(EXIT_FAILURE);
if (config->single_process) {
exit(EXIT_FAILURE);
} else {
nghttp2_Exit(EXIT_FAILURE);
}
}
LOG(NOTICE) << "Worker process shutting down momentarily";
// call exit(...) instead of nghttp2_Exit to get leak sanitizer report
nghttp2_Exit(EXIT_SUCCESS);
if (config->single_process) {
exit(EXIT_SUCCESS);
} else {
nghttp2_Exit(EXIT_SUCCESS);
}
}
// parent process
@@ -1322,7 +1341,7 @@ int event_loop() {
auto loop = ev_default_loop(config->ev_loop_flags);
int ipc_fd;
int ipc_fd = 0;
auto pid = fork_worker_process(ipc_fd, {});
@@ -1373,7 +1392,7 @@ constexpr auto DEFAULT_NPN_LIST = StringRef::from_lit("h2,h2-16,h2-14,"
} // namespace
namespace {
constexpr auto DEFAULT_TLS_MIN_PROTO_VERSION = StringRef::from_lit("TLSv1.1");
constexpr auto DEFAULT_TLS_MIN_PROTO_VERSION = StringRef::from_lit("TLSv1.2");
#ifdef TLS1_3_VERSION
constexpr auto DEFAULT_TLS_MAX_PROTO_VERSION = StringRef::from_lit("TLSv1.3");
#else // !TLS1_3_VERSION
@@ -1419,6 +1438,12 @@ void fill_default_config(Config *config) {
memcachedconf.family = AF_UNSPEC;
}
auto &anti_replayconf = tlsconf.anti_replay;
{
auto &memcachedconf = anti_replayconf.memcached;
memcachedconf.family = AF_UNSPEC;
}
ticketconf.cipher = EVP_aes_128_cbc();
}
@@ -1437,13 +1462,13 @@ void fill_default_config(Config *config) {
}
tlsconf.session_timeout = std::chrono::hours(12);
tlsconf.ciphers = StringRef::from_lit(nghttp2::ssl::DEFAULT_CIPHER_LIST);
tlsconf.ciphers = StringRef::from_lit(nghttp2::tls::DEFAULT_CIPHER_LIST);
tlsconf.client.ciphers =
StringRef::from_lit(nghttp2::ssl::DEFAULT_CIPHER_LIST);
StringRef::from_lit(nghttp2::tls::DEFAULT_CIPHER_LIST);
tlsconf.min_proto_version =
ssl::proto_version_from_string(DEFAULT_TLS_MIN_PROTO_VERSION);
tls::proto_version_from_string(DEFAULT_TLS_MIN_PROTO_VERSION);
tlsconf.max_proto_version =
ssl::proto_version_from_string(DEFAULT_TLS_MAX_PROTO_VERSION);
tls::proto_version_from_string(DEFAULT_TLS_MAX_PROTO_VERSION);
#if OPENSSL_1_1_API || defined(OPENSSL_IS_BORINGSSL)
tlsconf.ecdh_curves = StringRef::from_lit("X25519:P-256:P-384:P-521");
#else // !OPENSSL_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
@@ -1459,6 +1484,9 @@ void fill_default_config(Config *config) {
httpconf.max_response_header_fields = 500;
httpconf.redirect_https_port = StringRef::from_lit("443");
httpconf.max_requests = std::numeric_limits<size_t>::max();
httpconf.xfp.add = true;
httpconf.xfp.strip_incoming = true;
httpconf.zero_rtt_uniq.strip_incoming = true;
auto &http2conf = config->http2;
{
@@ -1644,14 +1672,16 @@ Connections:
which only lacks trailing '/' (e.g., path "/foo/"
matches request path "/foo"). If it does not end with
"/", it performs exact match against the request path.
If host is given, it performs exact match against the
request host. If host alone is given, "/" is appended
to it, so that it matches all request paths under the
host (e.g., specifying "nghttp2.org" equals to
"nghttp2.org/"). CONNECT method is treated specially.
It does not have path, and we don't allow empty path.
To workaround this, we assume that CONNECT method has
"/" as path.
If host is given, it performs a match against the
request host. For a request received on the frontend
listener with "sni-fwd" parameter enabled, SNI host is
used instead of a request host. If host alone is given,
"/" is appended to it, so that it matches all request
paths under the host (e.g., specifying "nghttp2.org"
equals to "nghttp2.org/"). CONNECT method is treated
specially. It does not have path, and we don't allow
empty path. To workaround this, we assume that CONNECT
method has "/" as path.
Patterns with host take precedence over patterns with
just path. Then, longer patterns take precedence over
@@ -1665,6 +1695,18 @@ Connections:
match against "nghttp2.org". The exact hosts match
takes precedence over the wildcard hosts match.
If path part ends with "*", it is treated as wildcard
path. The wildcard path behaves differently from the
normal path. For normal path, match is made around the
boundary of path component separator,"/". On the other
hand, the wildcard path does not take into account the
path component separator. All paths which include the
wildcard path without last "*" as prefix, and are
strictly longer than wildcard path without last "*" are
matched. "*" must match at least one character. For
example, the pattern "/foo*" matches "/foo/" and
"/foobar". But it does not match "/foo", or "/fo".
If <PATTERN> is omitted or empty string, "/" is used as
pattern, which matches all request paths (catch-all
pattern). The catch-all backend must be given.
@@ -1737,16 +1779,32 @@ Connections:
The session affinity is enabled using
"affinity=<METHOD>" parameter. If "ip" is given in
<METHOD>, client IP based session affinity is enabled.
If "none" is given in <METHOD>, session affinity is
disabled, and this is the default. The session affinity
is enabled per <PATTERN>. If at least one backend has
"affinity" parameter, and its <METHOD> is not "none",
session affinity is enabled for all backend servers
sharing the same <PATTERN>. It is advised to set
"affinity" parameter to all backend explicitly if
session affinity is desired. The session affinity may
break if one of the backend gets unreachable, or backend
settings are reloaded or replaced by API.
If "cookie" is given in <METHOD>, cookie based session
affinity is enabled. If "none" is given in <METHOD>,
session affinity is disabled, and this is the default.
The session affinity is enabled per <PATTERN>. If at
least one backend has "affinity" parameter, and its
<METHOD> is not "none", session affinity is enabled for
all backend servers sharing the same <PATTERN>. It is
advised to set "affinity" parameter to all backend
explicitly if session affinity is desired. The session
affinity may break if one of the backend gets
unreachable, or backend settings are reloaded or
replaced by API.
If "affinity=cookie" is used, the additional
configuration is required.
"affinity-cookie-name=<NAME>" must be used to specify a
name of cookie to use. Optionally,
"affinity-cookie-path=<PATH>" can be used to specify a
path which cookie is applied. The optional
"affinity-cookie-secure=<SECURE>" controls the Secure
attribute of a cookie. The default value is "auto", and
the Secure attribute is determined by a request scheme.
If a request scheme is "https", then Secure attribute is
set. Otherwise, it is not set. If <SECURE> is "yes",
the Secure attribute is always set. If <SECURE> is
"no", the Secure attribute is always omitted.
By default, name resolution of backend host name is done
at start up, or reloading configuration. If "dns"
@@ -1790,6 +1848,11 @@ Connections:
Optionally, TLS can be disabled by specifying "no-tls"
parameter. TLS is enabled by default.
If "sni-fwd" parameter is used, when performing a match
to select a backend server, SNI host name received from
the client is used instead of the request host. See
--backend option about the pattern match.
To make this frontend as API endpoint, specify "api"
parameter. This is disabled by default. It is
important to limit the access to the API frontend.
@@ -1834,8 +1897,7 @@ Connections:
Performance:
-n, --workers=<N>
Set the number of worker threads.
Default: )"
<< config->num_worker << R"(
Default: )" << config->num_worker << R"(
--single-thread
Run everything in one thread inside the worker process.
This feature is provided for better debugging
@@ -2017,8 +2079,7 @@ SSL/TLS:
--client-ciphers=<SUITE>
Set allowed cipher list for backend connection. The
format of the string is described in OpenSSL ciphers(1).
Default: )"
<< config->tls.client.ciphers << R"(
Default: )" << config->tls.client.ciphers << R"(
--ecdh-curves=<LIST>
Set supported curve list for frontend connections.
<LIST> is a colon separated list of curve NID or names
@@ -2031,11 +2092,14 @@ SSL/TLS:
Don't verify backend server's certificate if TLS is
enabled for backend connections.
--cacert=<PATH>
Set path to trusted CA certificate file used in backend
TLS connections. The file must be in PEM format. It
can contain multiple certificates. If the linked
OpenSSL is configured to load system wide certificates,
they are loaded at startup regardless of this option.
Set path to trusted CA certificate file. It is used in
backend TLS connections to verify peer's certificate.
It is also used to verify OCSP response from the script
set by --fetch-ocsp-response-file. The file must be in
PEM format. It can contain multiple certificates. If
the linked OpenSSL is configured to load system wide
certificates, they are loaded at startup regardless of
this option.
--private-key-passwd-file=<PATH>
Path to file that contains password for the server's
private key. If none is given and the private key is
@@ -2044,12 +2108,12 @@ SSL/TLS:
Specify additional certificate and private key file.
nghttpx will choose certificates based on the hostname
indicated by client using TLS SNI extension. If nghttpx
is built with OpenSSL >= 1.0.2, signature algorithms
(e.g., ECDSA+SHA256, RSA+SHA256) presented by client are
also taken into consideration. This allows nghttpx to
send ECDSA certificate to modern clients, while sending
RSA based certificate to older clients. This option can
be used multiple times. To make OCSP stapling work,
is built with OpenSSL >= 1.0.2, the shared elliptic
curves (e.g., P-256) between client and server are also
taken into consideration. This allows nghttpx to send
ECDSA certificate to modern clients, while sending RSA
based certificate to older clients. This option can be
used multiple times. To make OCSP stapling work,
<CERTPATH> must be absolute path.
Additional parameter can be specified in <PARAM>. The
@@ -2071,8 +2135,8 @@ SSL/TLS:
NPN. The parameter must be delimited by a single comma
only and any white spaces are treated as a part of
protocol string.
Default: )"
<< DEFAULT_NPN_LIST << R"(
Default: )" << DEFAULT_NPN_LIST
<< R"(
--verify-client
Require and verify client certificate.
--verify-client-cacert=<PATH>
@@ -2091,15 +2155,20 @@ SSL/TLS:
--tls-min-proto-version and --tls-max-proto-version are
enabled. If the protocol list advertised by client does
not overlap this range, you will receive the error
message "unknown protocol". The available versions are:
message "unknown protocol". If a protocol version lower
than TLSv1.2 is specified, make sure that the compatible
ciphers are included in --ciphers option. The default
cipher list only includes ciphers compatible with
TLSv1.2 or above. The available versions are:
)"
#ifdef TLS1_3_VERSION
"TLSv1.3, "
"TLSv1.3, "
#endif // TLS1_3_VERSION
"TLSv1.2, TLSv1.1, and TLSv1.0"
R"(
"TLSv1.2, TLSv1.1, and TLSv1.0"
R"(
Default: )"
<< DEFAULT_TLS_MIN_PROTO_VERSION << R"(
<< DEFAULT_TLS_MIN_PROTO_VERSION
<< R"(
--tls-max-proto-version=<VER>
Specify maximum SSL/TLS protocol. The name matching is
done in case-insensitive manner. The versions between
@@ -2109,10 +2178,10 @@ SSL/TLS:
message "unknown protocol". The available versions are:
)"
#ifdef TLS1_3_VERSION
"TLSv1.3, "
"TLSv1.3, "
#endif // TLS1_3_VERSION
"TLSv1.2, TLSv1.1, and TLSv1.0"
R"(
"TLSv1.2, TLSv1.1, and TLSv1.0"
R"(
Default: )"
<< DEFAULT_TLS_MAX_PROTO_VERSION << R"(
--tls-ticket-key-file=<PATH>
@@ -2194,6 +2263,14 @@ SSL/TLS:
Set interval to update OCSP response cache.
Default: )"
<< util::duration_str(config->tls.ocsp.update_interval) << R"(
--ocsp-startup
Start accepting connections after initial attempts to
get OCSP responses finish. It does not matter some of
the attempts fail. This feature is useful if OCSP
responses must be available before accepting
connections.
--no-verify-ocsp
nghttpx does not verify OCSP response.
--no-ocsp Disable OCSP stapling.
--tls-session-cache-memcached=<HOST>,<PORT>[;tls]
Specify address of memcached server to store session
@@ -2214,6 +2291,25 @@ SSL/TLS:
--tls-session-cache-memcached-private-key-file=<PATH>
Path to client private key for memcached connections to
store session cache.
--tls-anti-replay-memcached=<HOST>,<PORT>[;tls]
Specify address of memcached server to store ClientHello
to avoid 0-RTT early data replay. This enables shared
storage between multiple nghttpx instances. Optionally,
memcached connection can be encrypted with TLS by
specifying "tls" parameter.
--tls-anti-replay-memcached-address-family=(auto|IPv4|IPv6)
Specify address family of memcached connections to store
ClientHello to avoid 0-RTT early data replay. If "auto"
is given, both IPv4 and IPv6 are considered. If "IPv4"
is given, only IPv4 address is considered. If "IPv6" is
given, only IPv6 address is considered.
Default: auto
--tls-anti-replay-memcached-cert-file=<PATH>
Path to client certificate for memcached connections to
store ClientHello to avoid 0-RTT early data replay.
--tls-anti-replay-memcached-private-key-file=<PATH>
Path to client private key for memcached connections to
store ClientHello to avoid 0-RTT early data replay.
--tls-dyn-rec-warmup-threshold=<SIZE>
Specify the threshold size for TLS dynamic record size
behaviour. During a TLS session, after the threshold
@@ -2426,11 +2522,22 @@ Logging:
* $alpn: ALPN identifier of the protocol which generates
the response. For HTTP/1, ALPN is always http/1.1,
regardless of minor version.
* $ssl_cipher: cipher used for SSL/TLS connection.
* $ssl_protocol: protocol for SSL/TLS connection.
* $ssl_session_id: session ID for SSL/TLS connection.
* $ssl_session_reused: "r" if SSL/TLS session was
* $tls_cipher: cipher used for SSL/TLS connection.
* $tls_client_fingerprint_sha256: SHA-256 fingerprint of
client certificate.
* $tls_client_fingerprint_sha1: SHA-1 fingerprint of
client certificate.
* $tls_client_subject_name: subject name in client
certificate.
* $tls_client_issuer_name: issuer name in client
certificate.
* $tls_client_serial: serial number in client
certificate.
* $tls_protocol: protocol for SSL/TLS connection.
* $tls_session_id: session ID for SSL/TLS connection.
* $tls_session_reused: "r" if SSL/TLS session was
reused. Otherwise, "."
* $tls_sni: SNI server name for SSL/TLS connection.
* $backend_host: backend host used to fulfill the
request. "-" if backend host is not available.
* $backend_port: backend port used to fulfill the
@@ -2449,8 +2556,7 @@ Logging:
Set path to write error log. To reopen file, send USR1
signal to nghttpx. stderr will be redirected to the
error log file unless --errorlog-syslog is used.
Default: )"
<< config->logging.error.file << R"(
Default: )" << config->logging.error.file << R"(
--errorlog-syslog
Send error log to syslog. If this option is used,
--errorlog-file option is ignored.
@@ -2466,6 +2572,15 @@ HTTP:
--strip-incoming-x-forwarded-for
Strip X-Forwarded-For header field from inbound client
requests.
--no-add-x-forwarded-proto
Don't append additional X-Forwarded-Proto header field
to the backend request. If inbound client sets
X-Forwarded-Proto, and
--no-strip-incoming-x-forwarded-proto option is used,
they are passed to the backend.
--no-strip-incoming-x-forwarded-proto
Don't strip X-Forwarded-Proto header field from inbound
client requests.
--add-forwarded=<LIST>
Append RFC 7239 Forwarded header field with parameters
specified in comma delimited list <LIST>. The supported
@@ -2501,6 +2616,9 @@ HTTP:
Default: obfuscated
--no-via Don't append to Via header field. If Via header field
is received, it is left unaltered.
--no-strip-incoming-nghttpx-0rtt-uniq
Don't strip nghttpx-0rtt-uniq header field from inbound
client requests.
--no-location-rewrite
Don't rewrite location header field in default mode.
When --http2-proxy is used, location header field will
@@ -2573,8 +2691,8 @@ HTTP:
Specify the port number which appears in Location header
field when redirect to HTTPS URI is made due to
"redirect-if-not-tls" parameter in --backend option.
Default: )"
<< config->http.redirect_https_port << R"(
Default: )" << config->http.redirect_https_port
<< R"(
API:
--api-max-request-body=<SIZE>
@@ -2635,6 +2753,14 @@ Process:
--user=<USER>
Run this program as <USER>. This option is intended to
be used to drop root privileges.
--single-process
Run this program in a single process mode for debugging
purpose. Without this option, nghttpx creates at least
2 processes: master and worker processes. If this
option is used, master and worker are unified into a
single process. nghttpx still spawns additional process
if neverbleed is used. In the single process mode, the
signal handling feature is disabled.
Scripting:
--mruby-file=<PATH>
@@ -2642,9 +2768,10 @@ Scripting:
Misc:
--conf=<PATH>
Load configuration from <PATH>.
Default: )"
<< config->conf_path << R"(
Load configuration from <PATH>. Please note that
nghttpx always tries to read the default configuration
file if --conf is not given.
Default: )" << config->conf_path << R"(
--include=<PATH>
Load additional configurations from <PATH>. File <PATH>
is read when configuration parser encountered this
@@ -2662,8 +2789,7 @@ Misc:
The <DURATION> argument is an integer and an optional unit (e.g., 1s
is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms
(hours, minutes, seconds and milliseconds, respectively). If a unit
is omitted, a second is used as unit.)"
<< std::endl;
is omitted, a second is used as unit.)" << std::endl;
}
} // namespace
@@ -2672,6 +2798,7 @@ int process_options(Config *config,
std::vector<std::pair<StringRef, StringRef>> &cmdcfgs) {
std::array<char, STRERROR_BUFSIZE> errbuf;
if (conf_exists(config->conf_path.c_str())) {
LOG(NOTICE) << "Loading configuration from " << config->conf_path;
std::set<StringRef> include_set;
if (load_config(config, config->conf_path.c_str(), include_set) == -1) {
LOG(FATAL) << "Failed to load configuration from " << config->conf_path;
@@ -2786,7 +2913,7 @@ int process_options(Config *config,
}
if (!tlsconf.tls_proto_list.empty()) {
tlsconf.tls_proto_mask = ssl::create_tls_proto_mask(tlsconf.tls_proto_list);
tlsconf.tls_proto_mask = tls::create_tls_proto_mask(tlsconf.tls_proto_list);
}
// TODO We depends on the ordering of protocol version macro in
@@ -2797,7 +2924,7 @@ int process_options(Config *config,
return -1;
}
if (ssl::set_alpn_prefs(tlsconf.alpn_prefs, tlsconf.npn_list) != 0) {
if (tls::set_alpn_prefs(tlsconf.alpn_prefs, tlsconf.npn_list) != 0) {
return -1;
}
@@ -2821,7 +2948,7 @@ int process_options(Config *config,
upstreamconf.worker_connections = std::numeric_limits<size_t>::max();
}
if (ssl::upstream_tls_enabled(config->conn) &&
if (tls::upstream_tls_enabled(config->conn) &&
(tlsconf.private_key_file.empty() || tlsconf.cert_file.empty())) {
LOG(FATAL) << "TLS private key and certificate files are required. "
"Specify them in command-line, or in configuration file "
@@ -2829,7 +2956,7 @@ int process_options(Config *config,
return -1;
}
if (ssl::upstream_tls_enabled(config->conn) && !tlsconf.ocsp.disabled) {
if (tls::upstream_tls_enabled(config->conn) && !tlsconf.ocsp.disabled) {
struct stat buf;
if (stat(tlsconf.ocsp.fetch_ocsp_response_file.c_str(), &buf) != 0) {
tlsconf.ocsp.disabled = true;
@@ -2897,6 +3024,26 @@ int process_options(Config *config,
}
}
{
auto &memcachedconf = tlsconf.anti_replay.memcached;
if (!memcachedconf.host.empty()) {
auto hostport = util::make_hostport(StringRef{memcachedconf.host},
memcachedconf.port);
if (resolve_hostname(&memcachedconf.addr, memcachedconf.host.c_str(),
memcachedconf.port, memcachedconf.family) == -1) {
LOG(FATAL) << "Resolving memcached address for TLS anti-replay failed: "
<< hostport;
return -1;
}
LOG(NOTICE) << "Memcached address for TLS anti-replay: " << hostport
<< " -> " << util::to_numeric_addr(&memcachedconf.addr);
if (memcachedconf.tls) {
LOG(NOTICE) << "Connection to memcached for TLS anti-replay will be "
"encrypted by TLS";
}
}
}
if (config->rlimit_nofile) {
struct rlimit lim = {static_cast<rlim_t>(config->rlimit_nofile),
static_cast<rlim_t>(config->rlimit_nofile)};
@@ -2994,7 +3141,7 @@ void reload_config(WorkerProcess *wp) {
// already created first default loop.
auto loop = ev_default_loop(new_config->ev_loop_flags);
int ipc_fd;
int ipc_fd = 0;
// fork_worker_process and forked child process assumes new
// configuration can be obtained from get_config().
@@ -3032,10 +3179,10 @@ int main(int argc, char **argv) {
int rv;
std::array<char, STRERROR_BUFSIZE> errbuf;
nghttp2::ssl::libssl_init();
nghttp2::tls::libssl_init();
#ifndef NOTHREADS
nghttp2::ssl::LibsslGlobalLock lock;
nghttp2::tls::LibsslGlobalLock lock;
#endif // NOTHREADS
Log::set_severity_level(NOTICE);
@@ -3122,7 +3269,9 @@ int main(int argc, char **argv) {
{SHRPX_OPT_BACKEND_HTTP_PROXY_URI.c_str(), required_argument, &flag,
26},
{SHRPX_OPT_BACKEND_NO_TLS.c_str(), no_argument, &flag, 27},
{SHRPX_OPT_OCSP_STARTUP.c_str(), no_argument, &flag, 28},
{SHRPX_OPT_FRONTEND_NO_TLS.c_str(), no_argument, &flag, 29},
{SHRPX_OPT_NO_VERIFY_OCSP.c_str(), no_argument, &flag, 30},
{SHRPX_OPT_BACKEND_TLS_SNI_FIELD.c_str(), required_argument, &flag, 31},
{SHRPX_OPT_DH_PARAM_FILE.c_str(), required_argument, &flag, 33},
{SHRPX_OPT_READ_RATE.c_str(), required_argument, &flag, 34},
@@ -3300,6 +3449,20 @@ int main(int argc, char **argv) {
{SHRPX_OPT_FRONTEND_MAX_REQUESTS.c_str(), required_argument, &flag,
155},
{SHRPX_OPT_SINGLE_THREAD.c_str(), no_argument, &flag, 156},
{SHRPX_OPT_NO_ADD_X_FORWARDED_PROTO.c_str(), no_argument, &flag, 157},
{SHRPX_OPT_NO_STRIP_INCOMING_X_FORWARDED_PROTO.c_str(), no_argument,
&flag, 158},
{SHRPX_OPT_SINGLE_PROCESS.c_str(), no_argument, &flag, 159},
{SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED.c_str(), required_argument, &flag,
160},
{SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED_ADDRESS_FAMILY.c_str(),
required_argument, &flag, 161},
{SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED_CERT_FILE.c_str(),
required_argument, &flag, 162},
{SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED_PRIVATE_KEY_FILE.c_str(),
required_argument, &flag, 163},
{SHRPX_OPT_NO_STRIP_INCOMING_NGHTTPX_0RTT_UNIQ.c_str(), no_argument,
&flag, 164},
{nullptr, 0, nullptr, 0}};
int option_index = 0;
@@ -3468,11 +3631,21 @@ int main(int argc, char **argv) {
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_NO_TLS,
StringRef::from_lit("yes"));
break;
case 28:
// --ocsp-startup
cmdcfgs.emplace_back(SHRPX_OPT_OCSP_STARTUP,
StringRef::from_lit("yes"));
break;
case 29:
// --frontend-no-tls
cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_NO_TLS,
StringRef::from_lit("yes"));
break;
case 30:
// --no-verify-ocsp
cmdcfgs.emplace_back(SHRPX_OPT_NO_VERIFY_OCSP,
StringRef::from_lit("yes"));
break;
case 31:
// --backend-tls-sni-field
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_TLS_SNI_FIELD,
@@ -4036,6 +4209,47 @@ int main(int argc, char **argv) {
cmdcfgs.emplace_back(SHRPX_OPT_SINGLE_THREAD,
StringRef::from_lit("yes"));
break;
case 157:
// --no-add-x-forwarded-proto
cmdcfgs.emplace_back(SHRPX_OPT_NO_ADD_X_FORWARDED_PROTO,
StringRef::from_lit("yes"));
break;
case 158:
// --no-strip-incoming-x-forwarded-proto
cmdcfgs.emplace_back(SHRPX_OPT_NO_STRIP_INCOMING_X_FORWARDED_PROTO,
StringRef::from_lit("yes"));
break;
case 159:
// --single-process
cmdcfgs.emplace_back(SHRPX_OPT_SINGLE_PROCESS,
StringRef::from_lit("yes"));
break;
case 160:
// --tls-anti-replay-memcached
cmdcfgs.emplace_back(SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED,
StringRef{optarg});
break;
case 161:
// --tls-anti-replay-memcached-address-family
cmdcfgs.emplace_back(SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED_ADDRESS_FAMILY,
StringRef{optarg});
break;
case 162:
// --tls-anti-replay-memcached-cert-file
cmdcfgs.emplace_back(SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED_CERT_FILE,
StringRef{optarg});
break;
case 163:
// --tls-anti-replay-memcached-private-key-file
cmdcfgs.emplace_back(
SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED_PRIVATE_KEY_FILE,
StringRef{optarg});
break;
case 164:
// --no-strip-incoming-nghttpx-0rtt-uniq
cmdcfgs.emplace_back(SHRPX_OPT_NO_STRIP_INCOMING_NGHTTPX_0RTT_UNIQ,
StringRef::from_lit("yes"));
break;
default:
break;
}

View File

@@ -99,8 +99,6 @@ void AcceptHandler::accept_connection() {
util::make_socket_closeonexec(cfd);
#endif // !HAVE_ACCEPT4
util::make_socket_nodelay(cfd);
conn_hnr_->handle_connection(cfd, &sockaddr.sa, addrlen, faddr_);
}

View File

@@ -38,12 +38,14 @@ namespace {
const std::array<APIEndpoint, 2> &apis() {
static const auto apis = new std::array<APIEndpoint, 2>{{
APIEndpoint{
StringRef::from_lit("/api/v1beta1/backendconfig"), true,
StringRef::from_lit("/api/v1beta1/backendconfig"),
true,
(1 << API_METHOD_POST) | (1 << API_METHOD_PUT),
&APIDownstreamConnection::handle_backendconfig,
},
APIEndpoint{
StringRef::from_lit("/api/v1beta1/configrevision"), true,
StringRef::from_lit("/api/v1beta1/configrevision"),
true,
(1 << API_METHOD_GET),
&APIDownstreamConnection::handle_configrevision,
},
@@ -56,7 +58,8 @@ const std::array<APIEndpoint, 2> &apis() {
namespace {
// The method string. This must be same order of APIMethod.
constexpr StringRef API_METHOD_STRING[] = {
StringRef::from_lit("GET"), StringRef::from_lit("POST"),
StringRef::from_lit("GET"),
StringRef::from_lit("POST"),
StringRef::from_lit("PUT"),
};
} // namespace

View File

@@ -42,7 +42,7 @@
#include "shrpx_config.h"
#include "shrpx_http_downstream_connection.h"
#include "shrpx_http2_downstream_connection.h"
#include "shrpx_ssl.h"
#include "shrpx_tls.h"
#include "shrpx_worker.h"
#include "shrpx_downstream_connection_pool.h"
#include "shrpx_downstream.h"
@@ -56,7 +56,7 @@
#endif // HAVE_SPDYLAY
#include "util.h"
#include "template.h"
#include "ssl.h"
#include "tls.h"
using namespace nghttp2;
@@ -96,10 +96,6 @@ void readcb(struct ev_loop *loop, ev_io *w, int revents) {
delete handler;
return;
}
if (handler->do_write() != 0) {
delete handler;
return;
}
}
} // namespace
@@ -449,25 +445,32 @@ ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl,
*p = '\0';
forwarded_for_ = StringRef{buf.base, p};
} else if (family == AF_INET6) {
// 2 for '[' and ']'
auto len = 2 + ipaddr_.size();
// 1 for terminating NUL.
auto buf = make_byte_ref(balloc_, len + 1);
auto p = buf.base;
*p++ = '[';
p = std::copy(std::begin(ipaddr_), std::end(ipaddr_), p);
*p++ = ']';
*p = '\0';
forwarded_for_ = StringRef{buf.base, p};
} else {
// family == AF_INET or family == AF_UNIX
forwarded_for_ = ipaddr_;
} else if (!faddr_->accept_proxy_protocol &&
!config->conn.upstream.accept_proxy_protocol) {
init_forwarded_for(family, ipaddr_);
}
}
}
void ClientHandler::init_forwarded_for(int family, const StringRef &ipaddr) {
if (family == AF_INET6) {
// 2 for '[' and ']'
auto len = 2 + ipaddr.size();
// 1 for terminating NUL.
auto buf = make_byte_ref(balloc_, len + 1);
auto p = buf.base;
*p++ = '[';
p = std::copy(std::begin(ipaddr), std::end(ipaddr), p);
*p++ = ']';
*p = '\0';
forwarded_for_ = StringRef{buf.base, p};
} else {
// family == AF_INET or family == AF_UNIX
forwarded_for_ = ipaddr;
}
}
void ClientHandler::setup_upstream_io_callback() {
if (conn_.tls.ssl) {
conn_.prepare_server_handshake();
@@ -580,7 +583,7 @@ int ClientHandler::validate_next_proto() {
CLOG(INFO, this) << "The negotiated next protocol: " << proto;
}
if (!ssl::in_proto_list(get_config()->tls.npn_list, proto)) {
if (!tls::in_proto_list(get_config()->tls.npn_list, proto)) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "The negotiated protocol is not supported: " << proto;
}
@@ -696,7 +699,7 @@ void ClientHandler::pool_downstream_connection(
auto &shared_addr = group->shared_addr;
if (shared_addr->affinity == AFFINITY_NONE) {
if (shared_addr->affinity.type == AFFINITY_NONE) {
auto &dconn_pool = group->shared_addr->dconn_pool;
dconn_pool.add_downstream_connection(std::move(dconn));
@@ -801,61 +804,40 @@ bool load_lighter(const DownstreamAddr *lhs, const DownstreamAddr *rhs) {
Http2Session *ClientHandler::select_http2_session(
const std::shared_ptr<DownstreamAddrGroup> &group) {
auto &shared_addr = group->shared_addr;
auto &http2_avail_freelist = shared_addr->http2_avail_freelist;
for (auto session = http2_avail_freelist.head; session;) {
auto next = session->dlnext;
session->remove_from_freelist();
// session may be in graceful shutdown period now.
if (session->max_concurrency_reached(0)) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this)
<< "Maximum streams have been reached for Http2Session(" << session
<< "). Skip it";
}
session = next;
// First count the working backend addresses.
size_t min = 0;
for (const auto &addr : shared_addr->addrs) {
if (addr.proto != PROTO_HTTP2 || addr.connect_blocker->blocked()) {
continue;
}
++min;
}
if (min == 0) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "No working backend address found";
CLOG(INFO, this) << "Use Http2Session " << session
<< " from http2_avail_freelist";
}
return nullptr;
}
auto &http2_avail_freelist = shared_addr->http2_avail_freelist;
if (http2_avail_freelist.size() >= min) {
for (auto session = http2_avail_freelist.head; session;) {
auto next = session->dlnext;
session->remove_from_freelist();
// session may be in graceful shutdown period now.
if (session->max_concurrency_reached(0)) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this)
<< "Maximum streams have been reached for Http2Session("
<< session << "). Skip it";
}
session = next;
continue;
}
if (session->max_concurrency_reached(1)) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "Use Http2Session " << session
<< " from http2_avail_freelist";
CLOG(INFO, this) << "Maximum streams are reached for Http2Session("
<< session << ").";
}
if (session->max_concurrency_reached(1)) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "Maximum streams are reached for Http2Session("
<< session << ").";
}
} else {
session->add_to_avail_freelist();
}
return session;
} else {
session->add_to_avail_freelist();
}
return session;
}
DownstreamAddr *selected_addr = nullptr;
@@ -898,7 +880,12 @@ Http2Session *ClientHandler::select_http2_session(
}
}
assert(selected_addr);
if (selected_addr == nullptr) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "No working backend address found";
}
return nullptr;
}
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "Selected DownstreamAddr=" << selected_addr
@@ -960,6 +947,24 @@ uint32_t next_cycle(const WeightedPri &pri) {
}
} // namespace
uint32_t ClientHandler::get_affinity_cookie(Downstream *downstream,
const StringRef &cookie_name) {
auto h = downstream->find_affinity_cookie(cookie_name);
if (h) {
return h;
}
auto d = std::uniform_int_distribution<uint32_t>(
1, std::numeric_limits<uint32_t>::max());
auto rh = d(worker_->get_randgen());
h = util::hash32(StringRef{reinterpret_cast<uint8_t *>(&rh),
reinterpret_cast<uint8_t *>(&rh) + sizeof(rh)});
downstream->renew_affinity_cookie(h);
return h;
}
std::unique_ptr<DownstreamConnection>
ClientHandler::get_downstream_connection(int &err, Downstream *downstream) {
size_t group_idx;
@@ -983,29 +988,30 @@ ClientHandler::get_downstream_connection(int &err, Downstream *downstream) {
auto &balloc = downstream->get_block_allocator();
// Fast path. If we have one group, it must be catch-all group.
// proxy mode falls in this case.
if (groups.size() == 1) {
group_idx = 0;
} else if (req.method == HTTP_CONNECT) {
// CONNECT method does not have path. But we requires path in
// host-path mapping. As workaround, we assume that path is "/".
group_idx = match_downstream_addr_group(routerconf, req.authority,
StringRef::from_lit("/"), groups,
catch_all, balloc);
} else {
if (!req.authority.empty()) {
group_idx = match_downstream_addr_group(
routerconf, req.authority, req.path, groups, catch_all, balloc);
StringRef authority;
if (faddr_->sni_fwd) {
authority = sni_;
} else if (!req.authority.empty()) {
authority = req.authority;
} else {
auto h = req.fs.header(http2::HD_HOST);
if (h) {
group_idx = match_downstream_addr_group(routerconf, h->value, req.path,
groups, catch_all, balloc);
} else {
group_idx = match_downstream_addr_group(
routerconf, StringRef{}, req.path, groups, catch_all, balloc);
authority = h->value;
}
}
StringRef path;
// CONNECT method does not have path. But we requires path in
// host-path mapping. As workaround, we assume that path is "/".
if (req.method != HTTP_CONNECT) {
path = req.path;
}
group_idx = match_downstream_addr_group(routerconf, authority, path, groups,
catch_all, balloc);
}
if (LOG_ENABLED(INFO)) {
@@ -1024,27 +1030,59 @@ ClientHandler::get_downstream_connection(int &err, Downstream *downstream) {
auto &group = groups[group_idx];
auto &shared_addr = group->shared_addr;
if (shared_addr->affinity == AFFINITY_IP) {
if (!affinity_hash_computed_) {
affinity_hash_ = compute_affinity_from_ip(ipaddr_);
affinity_hash_computed_ = true;
if (shared_addr->affinity.type != AFFINITY_NONE) {
uint32_t hash;
switch (shared_addr->affinity.type) {
case AFFINITY_IP:
if (!affinity_hash_computed_) {
affinity_hash_ = compute_affinity_from_ip(ipaddr_);
affinity_hash_computed_ = true;
}
hash = affinity_hash_;
break;
case AFFINITY_COOKIE:
hash = get_affinity_cookie(downstream, shared_addr->affinity.cookie.name);
break;
default:
assert(0);
}
const auto &affinity_hash = shared_addr->affinity_hash;
auto it = std::lower_bound(
std::begin(affinity_hash), std::end(affinity_hash), affinity_hash_,
std::begin(affinity_hash), std::end(affinity_hash), hash,
[](const AffinityHash &lhs, uint32_t rhs) { return lhs.hash < rhs; });
if (it == std::end(affinity_hash)) {
it = std::begin(affinity_hash);
}
auto aff_idx =
static_cast<size_t>(std::distance(std::begin(affinity_hash), it));
auto idx = (*it).idx;
auto addr = &shared_addr->addrs[idx];
auto &addr = shared_addr->addrs[idx];
if (addr.proto == PROTO_HTTP2) {
auto http2session = select_http2_session_with_affinity(group, &addr);
if (addr->connect_blocker->blocked()) {
size_t i;
for (i = aff_idx + 1; i != aff_idx; ++i) {
if (i == shared_addr->affinity_hash.size()) {
i = 0;
}
addr = &shared_addr->addrs[shared_addr->affinity_hash[i].idx];
if (addr->connect_blocker->blocked()) {
continue;
}
break;
}
if (i == aff_idx) {
err = -1;
return nullptr;
}
aff_idx = i;
}
if (addr->proto == PROTO_HTTP2) {
auto http2session = select_http2_session_with_affinity(group, addr);
auto dconn = make_unique<Http2DownstreamConnection>(http2session);
@@ -1053,11 +1091,11 @@ ClientHandler::get_downstream_connection(int &err, Downstream *downstream) {
return std::move(dconn);
}
auto &dconn_pool = addr.dconn_pool;
auto &dconn_pool = addr->dconn_pool;
auto dconn = dconn_pool->pop_downstream_connection();
if (!dconn) {
dconn = make_unique<HttpDownstreamConnection>(group, idx, conn_.loop,
dconn = make_unique<HttpDownstreamConnection>(group, aff_idx, conn_.loop,
worker_);
}
@@ -1133,7 +1171,7 @@ ClientHandler::get_downstream_connection(int &err, Downstream *downstream) {
}
dconn =
make_unique<HttpDownstreamConnection>(group, -1, conn_.loop, worker_);
make_unique<HttpDownstreamConnection>(group, 0, conn_.loop, worker_);
}
dconn->set_client_handler(this);
@@ -1206,7 +1244,6 @@ void ClientHandler::start_immediate_shutdown() {
}
void ClientHandler::write_accesslog(Downstream *downstream) {
nghttp2::ssl::TLSSessionInfo tls_info;
auto &req = downstream->request();
auto config = get_config();
@@ -1220,10 +1257,15 @@ void ClientHandler::write_accesslog(Downstream *downstream) {
upstream_accesslog(
config->logging.access.format,
LogSpec{
downstream, ipaddr_, alpn_,
nghttp2::ssl::get_tls_session_info(&tls_info, conn_.tls.ssl),
downstream,
ipaddr_,
alpn_,
sni_,
conn_.tls.ssl,
std::chrono::high_resolution_clock::now(), // request_end_time
port_, faddr_->port, config->pid,
port_,
faddr_->port,
config->pid,
});
}
@@ -1462,6 +1504,14 @@ int ClientHandler::proxy_protocol_read() {
<< " bytes read";
}
auto config = get_config();
auto &fwdconf = config->http.forwarded;
if ((fwdconf.params & FORWARDED_FOR) &&
fwdconf.for_node_type == FORWARDED_NODE_IP) {
init_forwarded_for(family, ipaddr_);
}
return on_proxy_protocol_finish();
}
@@ -1487,6 +1537,8 @@ void ClientHandler::set_tls_sni(const StringRef &sni) {
StringRef ClientHandler::get_tls_sni() const { return sni_; }
StringRef ClientHandler::get_alpn() const { return alpn_; }
BlockAllocator &ClientHandler::get_block_allocator() { return balloc_; }
} // namespace shrpx

View File

@@ -125,6 +125,9 @@ public:
Worker *get_worker() const;
// Initializes forwarded_for_.
void init_forwarded_for(int family, const StringRef &ipaddr);
using ReadBuf = DefaultMemchunkBuffer;
ReadBuf *get_rb();
@@ -150,6 +153,11 @@ public:
Http2Session *select_http2_session_with_affinity(
const std::shared_ptr<DownstreamAddrGroup> &group, DownstreamAddr *addr);
// Returns an affinity cookie value for |downstream|. |cookie_name|
// is used to inspect cookie header field in request header fields.
uint32_t get_affinity_cookie(Downstream *downstream,
const StringRef &cookie_name);
const UpstreamAddr *get_upstream_addr() const;
void repeat_read_timer();
@@ -163,6 +171,9 @@ public:
// Returns TLS SNI extension value client sent in this connection.
StringRef get_tls_sni() const;
// Returns ALPN negotiated in this connection.
StringRef get_alpn() const;
BlockAllocator &get_block_allocator();
private:

View File

@@ -53,7 +53,7 @@
#include "http-parser/http_parser.h"
#include "shrpx_log.h"
#include "shrpx_ssl.h"
#include "shrpx_tls.h"
#include "shrpx_http.h"
#include "util.h"
#include "base64.h"
@@ -401,6 +401,11 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
break;
case 7:
switch (name[6]) {
case 'i':
if (util::strieq_l("tls_sn", name, 6)) {
return SHRPX_LOGF_TLS_SNI;
}
break;
case 't':
if (util::strieq_l("reques", name, 6)) {
return SHRPX_LOGF_REQUEST;
@@ -419,6 +424,9 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
if (util::strieq_l("ssl_ciphe", name, 9)) {
return SHRPX_LOGF_SSL_CIPHER;
}
if (util::strieq_l("tls_ciphe", name, 9)) {
return SHRPX_LOGF_TLS_CIPHER;
}
break;
}
break;
@@ -455,6 +463,9 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
if (util::strieq_l("ssl_protoco", name, 11)) {
return SHRPX_LOGF_SSL_PROTOCOL;
}
if (util::strieq_l("tls_protoco", name, 11)) {
return SHRPX_LOGF_TLS_PROTOCOL;
}
break;
case 't':
if (util::strieq_l("backend_hos", name, 11)) {
@@ -472,6 +483,9 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
if (util::strieq_l("ssl_session_i", name, 13)) {
return SHRPX_LOGF_SSL_SESSION_ID;
}
if (util::strieq_l("tls_session_i", name, 13)) {
return SHRPX_LOGF_TLS_SESSION_ID;
}
break;
}
break;
@@ -484,12 +498,60 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
break;
}
break;
case 17:
switch (name[16]) {
case 'l':
if (util::strieq_l("tls_client_seria", name, 16)) {
return SHRPX_LOGF_TLS_CLIENT_SERIAL;
}
break;
}
break;
case 18:
switch (name[17]) {
case 'd':
if (util::strieq_l("ssl_session_reuse", name, 17)) {
return SHRPX_LOGF_SSL_SESSION_REUSED;
}
if (util::strieq_l("tls_session_reuse", name, 17)) {
return SHRPX_LOGF_TLS_SESSION_REUSED;
}
break;
}
break;
case 22:
switch (name[21]) {
case 'e':
if (util::strieq_l("tls_client_issuer_nam", name, 21)) {
return SHRPX_LOGF_TLS_CLIENT_ISSUER_NAME;
}
break;
}
break;
case 23:
switch (name[22]) {
case 'e':
if (util::strieq_l("tls_client_subject_nam", name, 22)) {
return SHRPX_LOGF_TLS_CLIENT_SUBJECT_NAME;
}
break;
}
break;
case 27:
switch (name[26]) {
case '1':
if (util::strieq_l("tls_client_fingerprint_sha", name, 26)) {
return SHRPX_LOGF_TLS_CLIENT_FINGERPRINT_SHA1;
}
break;
}
break;
case 29:
switch (name[28]) {
case '6':
if (util::strieq_l("tls_client_fingerprint_sha25", name, 28)) {
return SHRPX_LOGF_TLS_CLIENT_FINGERPRINT_SHA256;
}
break;
}
break;
@@ -642,7 +704,7 @@ int parse_duration(ev_tstamp *dest, const StringRef &opt,
namespace {
int parse_tls_proto_version(int &dest, const StringRef &opt,
const StringRef &optarg) {
auto v = ssl::proto_version_from_string(optarg);
auto v = tls::proto_version_from_string(optarg);
if (v == -1) {
LOG(ERROR) << opt << ": invalid TLS protocol version: " << optarg;
return -1;
@@ -693,6 +755,7 @@ int parse_memcached_connection_params(MemcachedConnectionParams &out,
struct UpstreamParams {
int alt_mode;
bool tls;
bool sni_fwd;
bool proxyproto;
};
@@ -708,6 +771,8 @@ int parse_upstream_params(UpstreamParams &out, const StringRef &src_params) {
if (util::strieq_l("tls", param)) {
out.tls = true;
} else if (util::strieq_l("sni-fwd", param)) {
out.sni_fwd = true;
} else if (util::strieq_l("no-tls", param)) {
out.tls = false;
} else if (util::strieq_l("api", param)) {
@@ -742,10 +807,10 @@ int parse_upstream_params(UpstreamParams &out, const StringRef &src_params) {
struct DownstreamParams {
StringRef sni;
AffinityConfig affinity;
size_t fall;
size_t rise;
shrpx_proto proto;
shrpx_session_affinity affinity;
bool tls;
bool dns;
bool redirect_if_not_tls;
@@ -815,11 +880,38 @@ int parse_downstream_params(DownstreamParams &out,
} else if (util::istarts_with_l(param, "affinity=")) {
auto valstr = StringRef{first + str_size("affinity="), end};
if (util::strieq_l("none", valstr)) {
out.affinity = AFFINITY_NONE;
out.affinity.type = AFFINITY_NONE;
} else if (util::strieq_l("ip", valstr)) {
out.affinity = AFFINITY_IP;
out.affinity.type = AFFINITY_IP;
} else if (util::strieq_l("cookie", valstr)) {
out.affinity.type = AFFINITY_COOKIE;
} else {
LOG(ERROR) << "backend: affinity: value must be either none or ip";
LOG(ERROR)
<< "backend: affinity: value must be one of none, ip, and cookie";
return -1;
}
} else if (util::istarts_with_l(param, "affinity-cookie-name=")) {
auto val = StringRef{first + str_size("affinity-cookie-name="), end};
if (val.empty()) {
LOG(ERROR)
<< "backend: affinity-cookie-name: non empty string is expected";
return -1;
}
out.affinity.cookie.name = val;
} else if (util::istarts_with_l(param, "affinity-cookie-path=")) {
out.affinity.cookie.path =
StringRef{first + str_size("affinity-cookie-path="), end};
} else if (util::istarts_with_l(param, "affinity-cookie-secure=")) {
auto valstr = StringRef{first + str_size("affinity-cookie-secure="), end};
if (util::strieq_l("auto", valstr)) {
out.affinity.cookie.secure = COOKIE_SECURE_AUTO;
} else if (util::strieq_l("yes", valstr)) {
out.affinity.cookie.secure = COOKIE_SECURE_YES;
} else if (util::strieq_l("no", valstr)) {
out.affinity.cookie.secure = COOKIE_SECURE_NO;
} else {
LOG(ERROR) << "backend: affinity-cookie-secure: value must be one of "
"auto, yes, and no";
return -1;
}
} else if (util::strieq_l("dns", param)) {
@@ -871,6 +963,13 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
return -1;
}
if (params.affinity.type == AFFINITY_COOKIE &&
params.affinity.cookie.name.empty()) {
LOG(ERROR) << "backend: affinity-cookie-name is mandatory if "
"affinity=cookie is specified";
return -1;
}
addr.fall = params.fall;
addr.rise = params.rise;
addr.proto = params.proto;
@@ -915,8 +1014,27 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
if (g.pattern == pattern) {
// Last value wins if we have multiple different affinity
// value under one group.
if (params.affinity != AFFINITY_NONE) {
g.affinity = params.affinity;
if (params.affinity.type != AFFINITY_NONE) {
if (g.affinity.type == AFFINITY_NONE) {
g.affinity.type = params.affinity.type;
if (params.affinity.type == AFFINITY_COOKIE) {
g.affinity.cookie.name = make_string_ref(
downstreamconf.balloc, params.affinity.cookie.name);
if (!params.affinity.cookie.path.empty()) {
g.affinity.cookie.path = make_string_ref(
downstreamconf.balloc, params.affinity.cookie.path);
}
g.affinity.cookie.secure = params.affinity.cookie.secure;
}
} else if (g.affinity.type != params.affinity.type ||
g.affinity.cookie.name != params.affinity.cookie.name ||
g.affinity.cookie.path != params.affinity.cookie.path ||
g.affinity.cookie.secure !=
params.affinity.cookie.secure) {
LOG(ERROR) << "backend: affinity: multiple different affinity "
"configurations found in a single group";
return -1;
}
}
// If at least one backend requires frontend TLS connection,
// enable it for all backends sharing the same pattern.
@@ -936,7 +1054,16 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
addr_groups.emplace_back(pattern);
auto &g = addr_groups.back();
g.addrs.push_back(addr);
g.affinity = params.affinity;
g.affinity.type = params.affinity.type;
if (params.affinity.type == AFFINITY_COOKIE) {
g.affinity.cookie.name =
make_string_ref(downstreamconf.balloc, params.affinity.cookie.name);
if (!params.affinity.cookie.path.empty()) {
g.affinity.cookie.path =
make_string_ref(downstreamconf.balloc, params.affinity.cookie.path);
}
g.affinity.cookie.secure = params.affinity.cookie.secure;
}
g.redirect_if_not_tls = params.redirect_if_not_tls;
if (pattern[0] == '*') {
@@ -947,6 +1074,12 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
auto host = StringRef{std::begin(g.pattern) + 1, path_first};
auto path = StringRef{path_first, std::end(g.pattern)};
auto path_is_wildcard = false;
if (path[path.size() - 1] == '*') {
path = StringRef{std::begin(path), std::begin(path) + path.size() - 1};
path_is_wildcard = true;
}
auto it = std::find_if(
std::begin(wildcard_patterns), std::end(wildcard_patterns),
[&host](const WildcardPattern &wp) { return wp.host == host; });
@@ -955,7 +1088,7 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
wildcard_patterns.emplace_back(host);
auto &router = wildcard_patterns.back().router;
router.add_route(path, idx);
router.add_route(path, idx, path_is_wildcard);
auto iov = make_byte_ref(downstreamconf.balloc, host.size() + 1);
auto p = iov.base;
@@ -965,13 +1098,20 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
rw_router.add_route(rev_host, wildcard_patterns.size() - 1);
} else {
(*it).router.add_route(path, idx);
(*it).router.add_route(path, idx, path_is_wildcard);
}
continue;
}
router.add_route(g.pattern, idx);
auto path_is_wildcard = false;
if (pattern[pattern.size() - 1] == '*') {
pattern = StringRef{std::begin(pattern),
std::begin(pattern) + pattern.size() - 1};
path_is_wildcard = true;
}
router.add_route(pattern, idx, path_is_wildcard);
}
return 0;
}
@@ -1558,6 +1698,11 @@ int option_lookup_token(const char *name, size_t namelen) {
return SHRPX_OPTID_HTTP2_BRIDGE;
}
break;
case 'p':
if (util::strieq_l("ocsp-startu", name, 11)) {
return SHRPX_OPTID_OCSP_STARTUP;
}
break;
case 'y':
if (util::strieq_l("client-prox", name, 11)) {
return SHRPX_OPTID_CLIENT_PROXY;
@@ -1613,6 +1758,11 @@ int option_lookup_token(const char *name, size_t namelen) {
return SHRPX_OPTID_NO_SERVER_PUSH;
}
break;
case 'p':
if (util::strieq_l("no-verify-ocs", name, 13)) {
return SHRPX_OPTID_NO_VERIFY_OCSP;
}
break;
case 's':
if (util::strieq_l("backend-no-tl", name, 13)) {
return SHRPX_OPTID_BACKEND_NO_TLS;
@@ -1620,6 +1770,9 @@ int option_lookup_token(const char *name, size_t namelen) {
if (util::strieq_l("client-cipher", name, 13)) {
return SHRPX_OPTID_CLIENT_CIPHERS;
}
if (util::strieq_l("single-proces", name, 13)) {
return SHRPX_OPTID_SINGLE_PROCESS;
}
break;
case 't':
if (util::strieq_l("tls-proto-lis", name, 13)) {
@@ -1902,6 +2055,11 @@ int option_lookup_token(const char *name, size_t namelen) {
return SHRPX_OPTID_FETCH_OCSP_RESPONSE_FILE;
}
break;
case 'o':
if (util::strieq_l("no-add-x-forwarded-prot", name, 23)) {
return SHRPX_OPTID_NO_ADD_X_FORWARDED_PROTO;
}
break;
case 't':
if (util::strieq_l("listener-disable-timeou", name, 23)) {
return SHRPX_OPTID_LISTENER_DISABLE_TIMEOUT;
@@ -1914,6 +2072,11 @@ int option_lookup_token(const char *name, size_t namelen) {
break;
case 25:
switch (name[24]) {
case 'd':
if (util::strieq_l("tls-anti-replay-memcache", name, 24)) {
return SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED;
}
break;
case 'e':
if (util::strieq_l("backend-http2-window-siz", name, 24)) {
return SHRPX_OPTID_BACKEND_HTTP2_WINDOW_SIZE;
@@ -2097,6 +2260,19 @@ int option_lookup_token(const char *name, size_t namelen) {
if (util::strieq_l("frontend-http2-optimize-window-siz", name, 34)) {
return SHRPX_OPTID_FRONTEND_HTTP2_OPTIMIZE_WINDOW_SIZE;
}
if (util::strieq_l("tls-anti-replay-memcached-cert-fil", name, 34)) {
return SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED_CERT_FILE;
}
break;
case 'o':
if (util::strieq_l("no-strip-incoming-x-forwarded-prot", name, 34)) {
return SHRPX_OPTID_NO_STRIP_INCOMING_X_FORWARDED_PROTO;
}
break;
case 'q':
if (util::strieq_l("no-strip-incoming-nghttpx-0rtt-uni", name, 34)) {
return SHRPX_OPTID_NO_STRIP_INCOMING_NGHTTPX_0RTT_UNIQ;
}
break;
case 'r':
if (util::strieq_l("frontend-http2-dump-response-heade", name, 34)) {
@@ -2175,6 +2351,11 @@ int option_lookup_token(const char *name, size_t namelen) {
return SHRPX_OPTID_BACKEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE;
}
break;
case 'y':
if (util::strieq_l("tls-anti-replay-memcached-address-famil", name, 39)) {
return SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED_ADDRESS_FAMILY;
}
break;
}
break;
case 41:
@@ -2201,6 +2382,12 @@ int option_lookup_token(const char *name, size_t namelen) {
break;
case 42:
switch (name[41]) {
case 'e':
if (util::strieq_l("tls-anti-replay-memcached-private-key-fil", name,
41)) {
return SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED_PRIVATE_KEY_FILE;
}
break;
case 'y':
if (util::strieq_l("tls-session-cache-memcached-address-famil", name,
41)) {
@@ -2283,9 +2470,15 @@ int parse_config(Config *config, int optid, const StringRef &opt,
return -1;
}
if (params.sni_fwd && !params.tls) {
LOG(ERROR) << "frontend: sni_fwd requires tls";
return -1;
}
UpstreamAddr addr{};
addr.fd = -1;
addr.tls = params.tls;
addr.sni_fwd = params.sni_fwd;
addr.alt_mode = params.alt_mode;
addr.accept_proxy_protocol = params.proxyproto;
@@ -2504,14 +2697,16 @@ int parse_config(Config *config, int optid, const StringRef &opt,
<< SHRPX_OPT_FRONTEND;
return 0;
case SHRPX_OPTID_BACKEND_NO_TLS:
LOG(WARN) << opt << ": deprecated. backend connection is not encrypted by "
"default. See also "
LOG(WARN) << opt
<< ": deprecated. backend connection is not encrypted by "
"default. See also "
<< SHRPX_OPT_BACKEND_TLS;
return 0;
case SHRPX_OPTID_BACKEND_TLS_SNI_FIELD:
LOG(WARN) << opt << ": deprecated. Use sni keyword in --backend option. "
"For now, all sni values of all backends are "
"overridden by the given value "
LOG(WARN) << opt
<< ": deprecated. Use sni keyword in --backend option. "
"For now, all sni values of all backends are "
"overridden by the given value "
<< optarg;
config->tls.backend_sni_name = make_string_ref(config->balloc, optarg);
@@ -2622,8 +2817,9 @@ int parse_config(Config *config, int optid, const StringRef &opt,
return 0;
case SHRPX_OPTID_CLIENT:
LOG(ERROR) << opt << ": deprecated. Use frontend=<addr>,<port>;no-tls, "
"backend=<addr>,<port>;;proto=h2;tls";
LOG(ERROR) << opt
<< ": deprecated. Use frontend=<addr>,<port>;no-tls, "
"backend=<addr>,<port>;;proto=h2;tls";
return -1;
case SHRPX_OPTID_INSECURE:
config->tls.insecure = util::strieq_l("yes", optarg);
@@ -2718,8 +2914,9 @@ int parse_config(Config *config, int optid, const StringRef &opt,
return 0;
}
case SHRPX_OPTID_TLS_PROTO_LIST: {
LOG(WARN) << opt << ": deprecated. Use tls-min-proto-version and "
"tls-max-proto-version instead.";
LOG(WARN) << opt
<< ": deprecated. Use tls-min-proto-version and "
"tls-max-proto-version instead.";
auto list = util::split_str(optarg, ',');
config->tls.tls_proto_list.resize(list.size());
for (size_t i = 0; i < list.size(); ++i) {
@@ -2980,7 +3177,8 @@ int parse_config(Config *config, int optid, const StringRef &opt,
return 0;
case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED:
case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED: {
case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED:
case SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED: {
auto addr_end = std::find(std::begin(optarg), std::end(optarg), ';');
auto src_params = StringRef{addr_end, std::end(optarg)};
@@ -3010,6 +3208,13 @@ int parse_config(Config *config, int optid, const StringRef &opt,
memcachedconf.tls = params.tls;
break;
}
case SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED: {
auto &memcachedconf = config->tls.anti_replay.memcached;
memcachedconf.host = make_string_ref(config->balloc, StringRef{host});
memcachedconf.port = port;
memcachedconf.tls = params.tls;
break;
}
};
return 0;
@@ -3157,6 +3362,16 @@ int parse_config(Config *config, int optid, const StringRef &opt,
config->tls.ticket.memcached.private_key_file =
make_string_ref(config->balloc, optarg);
return 0;
case SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED_CERT_FILE:
config->tls.anti_replay.memcached.cert_file =
make_string_ref(config->balloc, optarg);
return 0;
case SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED_PRIVATE_KEY_FILE:
config->tls.anti_replay.memcached.private_key_file =
make_string_ref(config->balloc, optarg);
return 0;
case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_ADDRESS_FAMILY:
return parse_address_family(&config->tls.ticket.memcached.family, opt,
@@ -3164,6 +3379,9 @@ int parse_config(Config *config, int optid, const StringRef &opt,
case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_ADDRESS_FAMILY:
return parse_address_family(&config->tls.session_cache.memcached.family,
opt, optarg);
case SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED_ADDRESS_FAMILY:
return parse_address_family(&config->tls.anti_replay.memcached.family, opt,
optarg);
case SHRPX_OPTID_BACKEND_ADDRESS_FAMILY:
return parse_address_family(&config->conn.downstream->family, opt, optarg);
case SHRPX_OPTID_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS:
@@ -3344,8 +3562,9 @@ int parse_config(Config *config, int optid, const StringRef &opt,
case SHRPX_OPTID_REDIRECT_HTTPS_PORT: {
auto n = util::parse_uint(optarg);
if (n == -1 || n < 0 || n > 65535) {
LOG(ERROR) << opt << ": bad value. Specify an integer in the range [0, "
"65535], inclusive";
LOG(ERROR) << opt
<< ": bad value. Specify an integer in the range [0, "
"65535], inclusive";
return -1;
}
config->http.redirect_https_port = optarg;
@@ -3356,6 +3575,30 @@ int parse_config(Config *config, int optid, const StringRef &opt,
case SHRPX_OPTID_SINGLE_THREAD:
config->single_thread = util::strieq_l("yes", optarg);
return 0;
case SHRPX_OPTID_SINGLE_PROCESS:
config->single_process = util::strieq_l("yes", optarg);
return 0;
case SHRPX_OPTID_NO_ADD_X_FORWARDED_PROTO:
config->http.xfp.add = !util::strieq_l("yes", optarg);
return 0;
case SHRPX_OPTID_NO_STRIP_INCOMING_X_FORWARDED_PROTO:
config->http.xfp.strip_incoming = !util::strieq_l("yes", optarg);
return 0;
case SHRPX_OPTID_OCSP_STARTUP:
config->tls.ocsp.startup = util::strieq_l("yes", optarg);
return 0;
case SHRPX_OPTID_NO_VERIFY_OCSP:
config->tls.ocsp.no_verify = util::strieq_l("yes", optarg);
return 0;
case SHRPX_OPTID_NO_STRIP_INCOMING_NGHTTPX_0RTT_UNIQ:
config->http.zero_rtt_uniq.strip_incoming = !util::strieq_l("yes", optarg);
return 0;
case SHRPX_OPTID_CONF:
LOG(WARN) << "conf: ignored";
@@ -3549,6 +3792,7 @@ StringRef strproto(shrpx_proto proto) {
// gcc needs this.
assert(0);
abort();
}
namespace {
@@ -3713,7 +3957,7 @@ int configure_downstream_group(Config *config, bool http2_proxy,
}
}
if (g.affinity == AFFINITY_IP) {
if (g.affinity.type != AFFINITY_NONE) {
size_t idx = 0;
for (auto &addr : g.addrs) {
StringRef key;

View File

@@ -64,11 +64,11 @@ struct LogFragment;
class ConnectBlocker;
class Http2Session;
namespace ssl {
namespace tls {
class CertLookupTree;
} // namespace ssl
} // namespace tls
constexpr auto SHRPX_OPT_PRIVATE_KEY_FILE =
StringRef::from_lit("private-key-file");
@@ -336,6 +336,23 @@ constexpr auto SHRPX_OPT_REDIRECT_HTTPS_PORT =
constexpr auto SHRPX_OPT_FRONTEND_MAX_REQUESTS =
StringRef::from_lit("frontend-max-requests");
constexpr auto SHRPX_OPT_SINGLE_THREAD = StringRef::from_lit("single-thread");
constexpr auto SHRPX_OPT_SINGLE_PROCESS = StringRef::from_lit("single-process");
constexpr auto SHRPX_OPT_NO_ADD_X_FORWARDED_PROTO =
StringRef::from_lit("no-add-x-forwarded-proto");
constexpr auto SHRPX_OPT_NO_STRIP_INCOMING_X_FORWARDED_PROTO =
StringRef::from_lit("no-strip-incoming-x-forwarded-proto");
constexpr auto SHRPX_OPT_OCSP_STARTUP = StringRef::from_lit("ocsp-startup");
constexpr auto SHRPX_OPT_NO_VERIFY_OCSP = StringRef::from_lit("no-verify-ocsp");
constexpr auto SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED =
StringRef::from_lit("tls-anti-replay-memcached");
constexpr auto SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED_CERT_FILE =
StringRef::from_lit("tls-anti-replay-memcached-cert-file");
constexpr auto SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED_PRIVATE_KEY_FILE =
StringRef::from_lit("tls-anti-replay-memcached-private-key-file");
constexpr auto SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED_ADDRESS_FAMILY =
StringRef::from_lit("tls-anti-replay-memcached-address-family");
constexpr auto SHRPX_OPT_NO_STRIP_INCOMING_NGHTTPX_0RTT_UNIQ =
StringRef::from_lit("no-strip-incoming-nghttpx-0rtt-uniq");
constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8;
@@ -349,6 +366,31 @@ enum shrpx_session_affinity {
AFFINITY_NONE,
// Client IP affinity
AFFINITY_IP,
// Cookie based affinity
AFFINITY_COOKIE,
};
enum shrpx_cookie_secure {
// Secure attribute of session affinity cookie is determined by the
// request scheme.
COOKIE_SECURE_AUTO,
// Secure attribute of session affinity cookie is always set.
COOKIE_SECURE_YES,
// Secure attribute of session affinity cookie is always unset.
COOKIE_SECURE_NO,
};
struct AffinityConfig {
// Type of session affinity.
shrpx_session_affinity type;
struct {
// Name of a cookie to use.
StringRef name;
// Path which a cookie is applied to.
StringRef path;
// Secure attribute
shrpx_cookie_secure secure;
} cookie;
};
enum shrpx_forwarded_param {
@@ -399,6 +441,9 @@ struct UpstreamAddr {
bool host_unix;
// true if TLS is enabled.
bool tls;
// true if SNI host should be used as a host when selecting backend
// server.
bool sni_fwd;
// true if client is supposed to send PROXY protocol v1 header.
bool accept_proxy_protocol;
int fd;
@@ -439,15 +484,15 @@ struct AffinityHash {
struct DownstreamAddrGroupConfig {
DownstreamAddrGroupConfig(const StringRef &pattern)
: pattern(pattern), affinity(AFFINITY_NONE), redirect_if_not_tls(false) {}
: pattern(pattern), affinity{AFFINITY_NONE}, redirect_if_not_tls(false) {}
StringRef pattern;
std::vector<DownstreamAddrConfig> addrs;
// Bunch of session affinity hash. Only used if affinity ==
// AFFINITY_IP.
std::vector<AffinityHash> affinity_hash;
// Session affinity
shrpx_session_affinity affinity;
// Cookie based session affinity configuration.
AffinityConfig affinity;
// true if this group requires that client connection must be TLS,
// and the request must be redirected to https URI.
bool redirect_if_not_tls;
@@ -542,6 +587,23 @@ struct TLSConfig {
} memcached;
} session_cache;
struct {
struct {
Address addr;
uint16_t port;
// Hostname of memcached server. This is also used as SNI field
// if TLS is enabled.
StringRef host;
// Client private key and certificate for authentication
StringRef private_key_file;
StringRef cert_file;
// Address family of memcached connection. One of either
// AF_INET, AF_INET6 or AF_UNSPEC.
int family;
bool tls;
} memcached;
} anti_replay;
// Dynamic record sizing configurations
struct {
size_t warmup_threshold;
@@ -553,6 +615,8 @@ struct TLSConfig {
ev_tstamp update_interval;
StringRef fetch_ocsp_response_file;
bool disabled;
bool startup;
bool no_verify;
} ocsp;
// Client verification configurations
@@ -638,6 +702,13 @@ struct HttpConfig {
bool add;
bool strip_incoming;
} xff;
struct {
bool add;
bool strip_incoming;
} xfp;
struct {
bool strip_incoming;
} zero_rtt_uniq;
std::vector<AltSvc> altsvcs;
std::vector<ErrorPage> error_pages;
HeaderRefs add_request_headers;
@@ -864,6 +935,7 @@ struct Config {
verbose{false},
daemon{false},
http2_proxy{false},
single_process{false},
single_thread{false},
ev_loop_flags{0} {}
~Config();
@@ -905,6 +977,9 @@ struct Config {
bool verbose;
bool daemon;
bool http2_proxy;
// Run nghttpx in single process mode. With this mode, signal
// handling is omitted.
bool single_process;
bool single_thread;
// flags passed to ev_default_loop() and ev_loop_new()
int ev_loop_flags;
@@ -1018,6 +1093,7 @@ enum {
SHRPX_OPTID_MAX_REQUEST_HEADER_FIELDS,
SHRPX_OPTID_MAX_RESPONSE_HEADER_FIELDS,
SHRPX_OPTID_MRUBY_FILE,
SHRPX_OPTID_NO_ADD_X_FORWARDED_PROTO,
SHRPX_OPTID_NO_HOST_REWRITE,
SHRPX_OPTID_NO_HTTP2_CIPHER_BLACK_LIST,
SHRPX_OPTID_NO_KQUEUE,
@@ -1025,8 +1101,12 @@ enum {
SHRPX_OPTID_NO_OCSP,
SHRPX_OPTID_NO_SERVER_PUSH,
SHRPX_OPTID_NO_SERVER_REWRITE,
SHRPX_OPTID_NO_STRIP_INCOMING_NGHTTPX_0RTT_UNIQ,
SHRPX_OPTID_NO_STRIP_INCOMING_X_FORWARDED_PROTO,
SHRPX_OPTID_NO_VERIFY_OCSP,
SHRPX_OPTID_NO_VIA,
SHRPX_OPTID_NPN_LIST,
SHRPX_OPTID_OCSP_STARTUP,
SHRPX_OPTID_OCSP_UPDATE_INTERVAL,
SHRPX_OPTID_PADDING,
SHRPX_OPTID_PID_FILE,
@@ -1040,6 +1120,7 @@ enum {
SHRPX_OPTID_RESPONSE_HEADER_FIELD_BUFFER,
SHRPX_OPTID_RLIMIT_NOFILE,
SHRPX_OPTID_SERVER_NAME,
SHRPX_OPTID_SINGLE_PROCESS,
SHRPX_OPTID_SINGLE_THREAD,
SHRPX_OPTID_STREAM_READ_TIMEOUT,
SHRPX_OPTID_STREAM_WRITE_TIMEOUT,
@@ -1047,6 +1128,10 @@ enum {
SHRPX_OPTID_STRIP_INCOMING_X_FORWARDED_FOR,
SHRPX_OPTID_SUBCERT,
SHRPX_OPTID_SYSLOG_FACILITY,
SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED,
SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED_ADDRESS_FAMILY,
SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED_CERT_FILE,
SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED_PRIVATE_KEY_FILE,
SHRPX_OPTID_TLS_DYN_REC_IDLE_TIMEOUT,
SHRPX_OPTID_TLS_DYN_REC_WARMUP_THRESHOLD,
SHRPX_OPTID_TLS_MAX_PROTO_VERSION,

View File

@@ -163,14 +163,14 @@ void test_shrpx_config_parse_log_format(void) {
void test_shrpx_config_read_tls_ticket_key_file(void) {
char file1[] = "/tmp/nghttpx-unittest.XXXXXX";
auto fd1 = mkstemp(file1);
assert(fd1 != -1);
assert(48 ==
write(fd1, "0..............12..............34..............5", 48));
CU_ASSERT(fd1 != -1);
CU_ASSERT(48 ==
write(fd1, "0..............12..............34..............5", 48));
char file2[] = "/tmp/nghttpx-unittest.XXXXXX";
auto fd2 = mkstemp(file2);
assert(fd2 != -1);
assert(48 ==
write(fd2, "6..............78..............9a..............b", 48));
CU_ASSERT(fd2 != -1);
CU_ASSERT(48 ==
write(fd2, "6..............78..............9a..............b", 48));
close(fd1);
close(fd2);
@@ -204,16 +204,18 @@ void test_shrpx_config_read_tls_ticket_key_file(void) {
void test_shrpx_config_read_tls_ticket_key_file_aes_256(void) {
char file1[] = "/tmp/nghttpx-unittest.XXXXXX";
auto fd1 = mkstemp(file1);
assert(fd1 != -1);
assert(80 == write(fd1, "0..............12..............................34..."
"...........................5",
80));
CU_ASSERT(fd1 != -1);
CU_ASSERT(80 == write(fd1,
"0..............12..............................34..."
"...........................5",
80));
char file2[] = "/tmp/nghttpx-unittest.XXXXXX";
auto fd2 = mkstemp(file2);
assert(fd2 != -1);
assert(80 == write(fd2, "6..............78..............................9a..."
"...........................b",
80));
CU_ASSERT(fd2 != -1);
CU_ASSERT(80 == write(fd2,
"6..............78..............................9a..."
"...........................b",
80));
close(fd1);
close(fd2);

View File

@@ -81,6 +81,6 @@ private:
bool offline_;
};
} // namespace
} // namespace shrpx
#endif // SHRPX_CONNECT_BLOCKER_H

View File

@@ -33,12 +33,11 @@
#include <openssl/err.h>
#include "shrpx_ssl.h"
#include "shrpx_tls.h"
#include "shrpx_memcached_request.h"
#include "shrpx_log.h"
#include "memchunk.h"
#include "util.h"
#include "ssl_compat.h"
using namespace nghttp2;
@@ -60,7 +59,8 @@ Connection::Connection(struct ev_loop *loop, int fd, SSL *ssl,
IOCb readcb, TimerCb timeoutcb, void *data,
size_t tls_dyn_rec_warmup_threshold,
ev_tstamp tls_dyn_rec_idle_timeout, shrpx_proto proto)
: tls{DefaultMemchunks(mcpool), DefaultPeekMemchunks(mcpool)},
: tls{DefaultMemchunks(mcpool), DefaultPeekMemchunks(mcpool),
DefaultMemchunks(mcpool)},
wlimit(loop, &wev, write_limit.rate, write_limit.burst),
rlimit(loop, &rev, read_limit.rate, read_limit.burst, this),
loop(loop),
@@ -92,7 +92,15 @@ Connection::Connection(struct ev_loop *loop, int fd, SSL *ssl,
}
}
Connection::~Connection() { disconnect(); }
Connection::~Connection() {
disconnect();
#if OPENSSL_1_1_1_API
if (tls.ch_md_ctx) {
EVP_MD_CTX_free(tls.ch_md_ctx);
}
#endif // OPENSSL_1_1_1_API
}
void Connection::disconnect() {
if (tls.ssl) {
@@ -110,19 +118,34 @@ void Connection::disconnect() {
tls.cached_session_lookup_req = nullptr;
}
if (tls.anti_replay_req) {
tls.anti_replay_req->canceled = true;
tls.anti_replay_req = nullptr;
}
SSL_shutdown(tls.ssl);
SSL_free(tls.ssl);
tls.ssl = nullptr;
#if OPENSSL_1_1_1_API
if (tls.ch_md_ctx) {
EVP_MD_CTX_reset(tls.ch_md_ctx);
}
#endif // OPENSSL_1_1_1_API
tls.wbuf.reset();
tls.rbuf.reset();
tls.last_write_idle = 0.;
tls.warmup_writelen = 0;
tls.last_writelen = 0;
tls.last_readlen = 0;
tls.handshake_state = 0;
tls.handshake_state = TLS_CONN_NORMAL;
tls.initial_handshake_done = false;
tls.reneg_started = false;
tls.sct_requested = false;
tls.early_data_finish = false;
tls.early_cb_called = false;
tls.postpone_early_data = false;
}
if (fd != -1) {
@@ -140,11 +163,23 @@ void Connection::disconnect() {
wlimit.stopw();
}
void Connection::prepare_client_handshake() { SSL_set_connect_state(tls.ssl); }
void Connection::prepare_client_handshake() {
SSL_set_connect_state(tls.ssl);
// This prevents SSL_read_early_data from being called.
tls.early_data_finish = true;
}
void Connection::prepare_server_handshake() {
SSL_set_accept_state(tls.ssl);
tls.server_handshake = true;
#if OPENSSL_1_1_1_API
if (!tls.ch_md_ctx) {
tls.ch_md_ctx = EVP_MD_CTX_new();
}
EVP_DigestInit_ex(tls.ch_md_ctx, EVP_sha256(), nullptr);
#endif // OPENSSL_1_1_1_API
}
// BIO implementation is inspired by openldap implementation:
@@ -218,7 +253,19 @@ int shrpx_bio_read(BIO *b, char *buf, int len) {
return -1;
}
return rbuf.remove(buf, len);
len = rbuf.remove(buf, len);
if (conn->tls.early_cb_called) {
return len;
}
#if OPENSSL_1_1_1_API
if (EVP_DigestUpdate(conn->tls.ch_md_ctx, buf, len) == 0) {
return -1;
}
#endif // OPENSSL_1_1_1_API
return len;
}
} // namespace
@@ -326,8 +373,9 @@ int Connection::tls_handshake() {
wlimit.stopw();
ev_timer_stop(loop, &wt);
std::array<uint8_t, 16_k> buf;
if (ev_is_active(&rev)) {
std::array<uint8_t, 8_k> buf;
auto nread = read_clear(buf.data(), buf.size());
if (nread < 0) {
if (LOG_ENABLED(INFO)) {
@@ -347,6 +395,7 @@ int Connection::tls_handshake() {
switch (tls.handshake_state) {
case TLS_CONN_WAIT_FOR_SESSION_CACHE:
case TLS_CONN_WAIT_FOR_ANTI_REPLAY:
return SHRPX_ERR_INPROGRESS;
case TLS_CONN_GOT_SESSION_CACHE: {
// Use the same trick invented by @kazuho in h2o project.
@@ -360,7 +409,7 @@ int Connection::tls_handshake() {
auto ssl_opts = SSL_get_options(tls.ssl);
SSL_free(tls.ssl);
auto ssl = ssl::create_ssl(ssl_ctx);
auto ssl = tls::create_ssl(ssl_ctx);
if (!ssl) {
return -1;
}
@@ -380,7 +429,73 @@ int Connection::tls_handshake() {
break;
}
auto rv = SSL_do_handshake(tls.ssl);
int rv;
ERR_clear_error();
#if OPENSSL_1_1_1_API
if (!tls.server_handshake || tls.early_data_finish) {
rv = SSL_do_handshake(tls.ssl);
} else {
for (;;) {
size_t nread;
rv = SSL_read_early_data(tls.ssl, buf.data(), buf.size(), &nread);
if (rv == SSL_READ_EARLY_DATA_ERROR) {
if (SSL_get_error(tls.ssl, rv) == SSL_ERROR_WANT_CLIENT_HELLO_CB) {
if (LOG_ENABLED(INFO)) {
LOG(INFO)
<< "tls: early_cb returns negative return value; handshake "
"interrupted";
}
break;
}
// If we have early data, and server sends ServerHello, assume
// that handshake is completed in server side, and start
// processing request. If we don't exit handshake code here,
// server waits for EndOfEarlyData and Finished message from
// client, which voids the purpose of 0-RTT data. The left
// over of handshake is done through write_tls or read_tls.
if (!tls.postpone_early_data &&
(tls.handshake_state == TLS_CONN_WRITE_STARTED ||
tls.wbuf.rleft()) &&
tls.earlybuf.rleft()) {
rv = 1;
}
break;
}
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "tls: read early data " << nread << " bytes";
}
tls.earlybuf.append(buf.data(), nread);
if (rv == SSL_READ_EARLY_DATA_FINISH) {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "tls: read all early data; total "
<< tls.earlybuf.rleft() << " bytes";
}
tls.early_data_finish = true;
// The same reason stated above.
if (!tls.postpone_early_data &&
(tls.handshake_state == TLS_CONN_WRITE_STARTED ||
tls.wbuf.rleft()) &&
tls.earlybuf.rleft()) {
rv = 1;
} else {
ERR_clear_error();
rv = SSL_do_handshake(tls.ssl);
}
break;
}
}
}
#else // !OPENSSL_1_1_1_API
rv = SSL_do_handshake(tls.ssl);
#endif // !OPENSSL_1_1_1_API
if (rv <= 0) {
auto err = SSL_get_error(tls.ssl, rv);
@@ -394,6 +509,9 @@ int Connection::tls_handshake() {
}
break;
case SSL_ERROR_WANT_WRITE:
#if OPENSSL_1_1_1_API
case SSL_ERROR_WANT_CLIENT_HELLO_CB:
#endif // OPENSSL_1_1_1_API
break;
case SSL_ERROR_SSL:
if (LOG_ENABLED(INFO)) {
@@ -409,7 +527,8 @@ int Connection::tls_handshake() {
}
}
if (tls.handshake_state == TLS_CONN_WAIT_FOR_SESSION_CACHE) {
if (tls.handshake_state == TLS_CONN_WAIT_FOR_SESSION_CACHE ||
tls.handshake_state == TLS_CONN_WAIT_FOR_ANTI_REPLAY) {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "tls: handshake is still in progress";
}
@@ -502,8 +621,8 @@ int Connection::write_tls_pending_handshake() {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "SSL/TLS handshake completed";
nghttp2::ssl::TLSSessionInfo tls_info{};
if (nghttp2::ssl::get_tls_session_info(&tls_info, tls.ssl)) {
nghttp2::tls::TLSSessionInfo tls_info{};
if (nghttp2::tls::get_tls_session_info(&tls_info, tls.ssl)) {
LOG(INFO) << "cipher=" << tls_info.cipher
<< " protocol=" << tls_info.protocol
<< " resumption=" << (tls_info.session_reused ? "yes" : "no")
@@ -530,7 +649,7 @@ int Connection::check_http2_requirement() {
!util::check_h2_is_selected(StringRef{next_proto, next_proto_len})) {
return 0;
}
if (!nghttp2::ssl::check_http2_tls_version(tls.ssl)) {
if (!nghttp2::tls::check_http2_tls_version(tls.ssl)) {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "TLSv1.2 was not negotiated. HTTP/2 must not be used.";
}
@@ -545,7 +664,7 @@ int Connection::check_http2_requirement() {
}
if (check_black_list &&
nghttp2::ssl::check_http2_cipher_black_list(tls.ssl)) {
nghttp2::tls::check_http2_cipher_black_list(tls.ssl)) {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "The negotiated cipher suite is in HTTP/2 cipher suite "
"black list. HTTP/2 must not be used.";
@@ -614,7 +733,23 @@ ssize_t Connection::write_tls(const void *data, size_t len) {
tls.last_write_idle = -1.;
ERR_clear_error();
#if OPENSSL_1_1_1_API
int rv;
if (SSL_is_init_finished(tls.ssl)) {
rv = SSL_write(tls.ssl, data, len);
} else {
size_t nwrite;
rv = SSL_write_early_data(tls.ssl, data, len, &nwrite);
// Use the same semantics with SSL_write.
if (rv == 1) {
rv = nwrite;
}
}
#else // !OPENSSL_1_1_1_API
auto rv = SSL_write(tls.ssl, data, len);
#endif // !OPENSSL_1_1_1_API
if (rv <= 0) {
auto err = SSL_get_error(tls.ssl, rv);
@@ -649,6 +784,14 @@ ssize_t Connection::write_tls(const void *data, size_t len) {
}
ssize_t Connection::read_tls(void *data, size_t len) {
ERR_clear_error();
#if OPENSSL_1_1_1_API
if (tls.earlybuf.rleft()) {
return tls.earlybuf.remove(data, len);
}
#endif // OPENSSL_1_1_1_API
// SSL_read requires the same arguments (buf pointer and its
// length) on SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE.
// rlimit_.avail() or rlimit_.avail() may return different length
@@ -666,6 +809,47 @@ ssize_t Connection::read_tls(void *data, size_t len) {
tls.last_readlen = 0;
}
#if OPENSSL_1_1_1_API
if (!tls.early_data_finish) {
// TLSv1.3 handshake is still going on.
size_t nread;
auto rv = SSL_read_early_data(tls.ssl, data, len, &nread);
if (rv == SSL_READ_EARLY_DATA_ERROR) {
auto err = SSL_get_error(tls.ssl, rv);
switch (err) {
case SSL_ERROR_WANT_READ:
tls.last_readlen = len;
return 0;
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;
}
}
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "tls: read early data " << nread << " bytes";
}
if (rv == SSL_READ_EARLY_DATA_FINISH) {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "tls: read all early data";
}
tls.early_data_finish = true;
// We may have stopped write watcher in write_tls.
wlimit.startw();
}
return nread;
}
#endif // OPENSSL_1_1_1_API
auto rv = SSL_read(tls.ssl, data, len);
if (rv <= 0) {
@@ -800,11 +984,25 @@ int Connection::get_tcp_hint(TCPHint *hint) const {
: 0;
// http://www.slideshare.net/kazuho/programming-tcp-for-responsiveness
// TODO 29 (5 (header) + 8 (explicit nonce) + 16 (tag)) is TLS
// overhead for AES-GCM. For CHACHA20_POLY1305, it is 21 since it
// does not need 8 bytes explicit nonce.
//
// TODO 29 (5 + 8 + 16) is TLS overhead for AES-GCM. For
// CHACHA20_POLY1305, it is 21 since it does not need 8 bytes
// explicit nonce.
auto writable_size = (avail_packets + 2) * (tcp_info.tcpi_snd_mss - 29);
// For TLSv1.3, AES-GCM and CHACHA20_POLY1305 overhead are now 22
// bytes (5 (header) + 1 (ContentType) + 16 (tag)).
size_t tls_overhead;
#ifdef TLS1_3_VERSION
if (SSL_version(tls.ssl) == TLS1_3_VERSION) {
tls_overhead = 22;
} else
#endif // TLS1_3_VERSION
{
tls_overhead = 29;
}
auto writable_size =
(avail_packets + 2) * (tcp_info.tcpi_snd_mss - tls_overhead);
if (writable_size > 16_k) {
writable_size = writable_size & ~(16_k - 1);
} else {
@@ -812,7 +1010,7 @@ int Connection::get_tcp_hint(TCPHint *hint) const {
LOG(INFO) << "writable_size is too small: " << writable_size;
}
// TODO is this required?
writable_size = std::max(writable_size, static_cast<uint32_t>(536 * 2));
writable_size = std::max(writable_size, static_cast<size_t>(536 * 2));
}
// if (LOG_ENABLED(INFO)) {

View File

@@ -32,29 +32,46 @@
#include <ev.h>
#include <openssl/ssl.h>
#include <openssl/evp.h>
#include "shrpx_rate_limit.h"
#include "shrpx_error.h"
#include "memchunk.h"
#include "ssl_compat.h"
namespace shrpx {
struct MemcachedRequest;
namespace tls {
struct TLSSessionCache;
} // namespace tls
enum {
TLS_CONN_NORMAL,
TLS_CONN_WAIT_FOR_SESSION_CACHE,
TLS_CONN_GOT_SESSION_CACHE,
TLS_CONN_CANCEL_SESSION_CACHE,
TLS_CONN_WAIT_FOR_ANTI_REPLAY,
TLS_CONN_WRITE_STARTED,
};
struct TLSConnection {
DefaultMemchunks wbuf;
DefaultPeekMemchunks rbuf;
// Stores TLSv1.3 early data.
DefaultMemchunks earlybuf;
// Message digest of ClientHello in hex string.
StringRef ch_hex_md;
SSL *ssl;
SSL_SESSION *cached_session;
MemcachedRequest *cached_session_lookup_req;
tls::TLSSessionCache *client_session_cache;
#if OPENSSL_1_1_1_API
// Message digest context to calculate ClientHello for anti-replay.
EVP_MD_CTX *ch_md_ctx;
#endif // !OPENSSL_1_1_1_API
MemcachedRequest *anti_replay_req;
ev_tstamp last_write_idle;
size_t warmup_writelen;
// length passed to SSL_write and SSL_read last time. This is
@@ -66,6 +83,20 @@ struct TLSConnection {
bool reneg_started;
// true if ssl is prepared to do handshake as server.
bool server_handshake;
// true if ssl is initialized as server, and client requested
// signed_certificate_timestamp extension.
bool sct_requested;
// true if TLSv1.3 early data has been completely received. Since
// SSL_read_early_data acts like SSL_do_handshake, this field may be
// true even if the negotiated TLS version is TLSv1.2 or earlier.
// This value is also true if this is client side connection for
// convenience.
bool early_data_finish;
// true if early_cb gets called.
bool early_cb_called;
// true if processing early data should be postponed until handshake
// finishes.
bool postpone_early_data;
};
struct TCPHint {

View File

@@ -35,7 +35,7 @@
#include <random>
#include "shrpx_client_handler.h"
#include "shrpx_ssl.h"
#include "shrpx_tls.h"
#include "shrpx_worker.h"
#include "shrpx_config.h"
#include "shrpx_http2_session.h"
@@ -118,7 +118,8 @@ ConnectionHandler::ConnectionHandler(struct ev_loop *loop, std::mt19937 &gen)
tls_ticket_key_memcached_get_retry_count_(0),
tls_ticket_key_memcached_fail_count_(0),
worker_round_robin_cnt_(get_config()->api.enabled ? 1 : 0),
graceful_shutdown_(false) {
graceful_shutdown_(false),
enable_acceptor_on_ocsp_completion_(false) {
ev_timer_init(&disable_acceptor_timer_, acceptor_disable_cb, 0., 0.);
disable_acceptor_timer_.data = this;
@@ -154,7 +155,7 @@ ConnectionHandler::~ConnectionHandler() {
for (auto ssl_ctx : all_ssl_ctx_) {
auto tls_ctx_data =
static_cast<ssl::TLSContextData *>(SSL_CTX_get_app_data(ssl_ctx));
static_cast<tls::TLSContextData *>(SSL_CTX_get_app_data(ssl_ctx));
if (tls_ctx_data) {
delete tls_ctx_data;
}
@@ -199,19 +200,19 @@ void ConnectionHandler::worker_replace_downstream(
}
int ConnectionHandler::create_single_worker() {
cert_tree_ = ssl::create_cert_lookup_tree();
auto sv_ssl_ctx = ssl::setup_server_ssl_context(
cert_tree_ = tls::create_cert_lookup_tree();
auto sv_ssl_ctx = tls::setup_server_ssl_context(
all_ssl_ctx_, indexed_ssl_ctx_, cert_tree_.get()
#ifdef HAVE_NEVERBLEED
,
nb_.get()
#endif // HAVE_NEVERBLEED
);
auto cl_ssl_ctx = ssl::setup_downstream_client_ssl_context(
);
auto cl_ssl_ctx = tls::setup_downstream_client_ssl_context(
#ifdef HAVE_NEVERBLEED
nb_.get()
#endif // HAVE_NEVERBLEED
);
);
if (cl_ssl_ctx) {
all_ssl_ctx_.push_back(cl_ssl_ctx);
@@ -219,22 +220,39 @@ int ConnectionHandler::create_single_worker() {
auto config = get_config();
auto &tlsconf = config->tls;
auto &memcachedconf = config->tls.session_cache.memcached;
SSL_CTX *session_cache_ssl_ctx = nullptr;
if (memcachedconf.tls) {
session_cache_ssl_ctx = ssl::create_ssl_client_context(
{
auto &memcachedconf = config->tls.session_cache.memcached;
if (memcachedconf.tls) {
session_cache_ssl_ctx = tls::create_ssl_client_context(
#ifdef HAVE_NEVERBLEED
nb_.get(),
nb_.get(),
#endif // HAVE_NEVERBLEED
tlsconf.cacert, memcachedconf.cert_file, memcachedconf.private_key_file,
nullptr);
all_ssl_ctx_.push_back(session_cache_ssl_ctx);
tlsconf.cacert, memcachedconf.cert_file,
memcachedconf.private_key_file, nullptr);
all_ssl_ctx_.push_back(session_cache_ssl_ctx);
}
}
SSL_CTX *anti_replay_ssl_ctx = nullptr;
{
auto &memcachedconf = config->tls.anti_replay.memcached;
if (memcachedconf.tls) {
anti_replay_ssl_ctx = tls::create_ssl_client_context(
#ifdef HAVE_NEVERBLEED
nb_.get(),
#endif // HAVE_NEVERBLEED
tlsconf.cacert, memcachedconf.cert_file,
memcachedconf.private_key_file, nullptr);
all_ssl_ctx_.push_back(anti_replay_ssl_ctx);
}
}
single_worker_ = make_unique<Worker>(
loop_, sv_ssl_ctx, cl_ssl_ctx, session_cache_ssl_ctx, cert_tree_.get(),
ticket_keys_, this, config->conn.downstream);
loop_, sv_ssl_ctx, cl_ssl_ctx, session_cache_ssl_ctx, anti_replay_ssl_ctx,
cert_tree_.get(), ticket_keys_, this, config->conn.downstream);
#ifdef HAVE_MRUBY
if (single_worker_->create_mruby_context() != 0) {
return -1;
@@ -248,19 +266,19 @@ int ConnectionHandler::create_worker_thread(size_t num) {
#ifndef NOTHREADS
assert(workers_.size() == 0);
cert_tree_ = ssl::create_cert_lookup_tree();
auto sv_ssl_ctx = ssl::setup_server_ssl_context(
cert_tree_ = tls::create_cert_lookup_tree();
auto sv_ssl_ctx = tls::setup_server_ssl_context(
all_ssl_ctx_, indexed_ssl_ctx_, cert_tree_.get()
#ifdef HAVE_NEVERBLEED
,
nb_.get()
#endif // HAVE_NEVERBLEED
);
auto cl_ssl_ctx = ssl::setup_downstream_client_ssl_context(
);
auto cl_ssl_ctx = tls::setup_downstream_client_ssl_context(
#ifdef HAVE_NEVERBLEED
nb_.get()
#endif // HAVE_NEVERBLEED
);
);
if (cl_ssl_ctx) {
all_ssl_ctx_.push_back(cl_ssl_ctx);
@@ -268,7 +286,6 @@ int ConnectionHandler::create_worker_thread(size_t num) {
auto config = get_config();
auto &tlsconf = config->tls;
auto &memcachedconf = config->tls.session_cache.memcached;
auto &apiconf = config->api;
// We have dedicated worker for API request processing.
@@ -276,12 +293,12 @@ int ConnectionHandler::create_worker_thread(size_t num) {
++num;
}
for (size_t i = 0; i < num; ++i) {
auto loop = ev_loop_new(config->ev_loop_flags);
SSL_CTX *session_cache_ssl_ctx = nullptr;
{
auto &memcachedconf = config->tls.session_cache.memcached;
SSL_CTX *session_cache_ssl_ctx = nullptr;
if (memcachedconf.tls) {
session_cache_ssl_ctx = ssl::create_ssl_client_context(
session_cache_ssl_ctx = tls::create_ssl_client_context(
#ifdef HAVE_NEVERBLEED
nb_.get(),
#endif // HAVE_NEVERBLEED
@@ -289,9 +306,30 @@ int ConnectionHandler::create_worker_thread(size_t num) {
memcachedconf.private_key_file, nullptr);
all_ssl_ctx_.push_back(session_cache_ssl_ctx);
}
auto worker = make_unique<Worker>(
loop, sv_ssl_ctx, cl_ssl_ctx, session_cache_ssl_ctx, cert_tree_.get(),
ticket_keys_, this, config->conn.downstream);
}
SSL_CTX *anti_replay_ssl_ctx = nullptr;
{
auto &memcachedconf = config->tls.anti_replay.memcached;
if (memcachedconf.tls) {
anti_replay_ssl_ctx = tls::create_ssl_client_context(
#ifdef HAVE_NEVERBLEED
nb_.get(),
#endif // HAVE_NEVERBLEED
tlsconf.cacert, memcachedconf.cert_file,
memcachedconf.private_key_file, nullptr);
all_ssl_ctx_.push_back(anti_replay_ssl_ctx);
}
}
for (size_t i = 0; i < num; ++i) {
auto loop = ev_loop_new(config->ev_loop_flags);
auto worker =
make_unique<Worker>(loop, sv_ssl_ctx, cl_ssl_ctx, session_cache_ssl_ctx,
anti_replay_ssl_ctx, cert_tree_.get(), ticket_keys_,
this, config->conn.downstream);
#ifdef HAVE_MRUBY
if (worker->create_mruby_context() != 0) {
return -1;
@@ -384,7 +422,7 @@ int ConnectionHandler::handle_connection(int fd, sockaddr *addr, int addrlen,
}
auto client =
ssl::accept_connection(single_worker_.get(), fd, addr, addrlen, faddr);
tls::accept_connection(single_worker_.get(), fd, addr, addrlen, faddr);
if (!client) {
LLOG(ERROR, this) << "ClientHandler creation failed";
@@ -498,6 +536,9 @@ bool ConnectionHandler::get_graceful_shutdown() const {
}
void ConnectionHandler::cancel_ocsp_update() {
enable_acceptor_on_ocsp_completion_ = false;
ev_timer_stop(loop_, &ocsp_timer_);
if (ocsp_.proc.pid == 0) {
return;
}
@@ -592,7 +633,7 @@ void ConnectionHandler::handle_ocsp_complete() {
auto ssl_ctx = all_ssl_ctx_[ocsp_.next];
auto tls_ctx_data =
static_cast<ssl::TLSContextData *>(SSL_CTX_get_app_data(ssl_ctx));
static_cast<tls::TLSContextData *>(SSL_CTX_get_app_data(ssl_ctx));
auto rstatus = ocsp_.chldev.rstatus;
auto status = WEXITSTATUS(rstatus);
@@ -610,8 +651,13 @@ void ConnectionHandler::handle_ocsp_complete() {
<< " finished successfully";
}
auto config = get_config();
auto &tlsconf = config->tls;
if (tlsconf.ocsp.no_verify ||
tls::verify_ocsp_response(ssl_ctx, ocsp_.resp.data(),
ocsp_.resp.size()) == 0) {
#ifndef OPENSSL_IS_BORINGSSL
{
#ifdef HAVE_ATOMIC_STD_SHARED_PTR
std::atomic_store_explicit(
&tls_ctx_data->ocsp_data,
@@ -622,10 +668,10 @@ void ConnectionHandler::handle_ocsp_complete() {
tls_ctx_data->ocsp_data =
std::make_shared<std::vector<uint8_t>>(std::move(ocsp_.resp));
#endif // !HAVE_ATOMIC_STD_SHARED_PTR
}
#else // OPENSSL_IS_BORINGSSL
SSL_CTX_set_ocsp_response(ssl_ctx, ocsp_.resp.data(), ocsp_.resp.size());
SSL_CTX_set_ocsp_response(ssl_ctx, ocsp_.resp.data(), ocsp_.resp.size());
#endif // OPENSSL_IS_BORINGSSL
}
++ocsp_.next;
proceed_next_cert_ocsp();
@@ -650,12 +696,18 @@ void ConnectionHandler::proceed_next_cert_ocsp() {
// We have updated all ocsp response, and schedule next update.
ev_timer_set(&ocsp_timer_, get_config()->tls.ocsp.update_interval, 0.);
ev_timer_start(loop_, &ocsp_timer_);
if (enable_acceptor_on_ocsp_completion_) {
enable_acceptor_on_ocsp_completion_ = false;
enable_acceptor();
}
return;
}
auto ssl_ctx = all_ssl_ctx_[ocsp_.next];
auto tls_ctx_data =
static_cast<ssl::TLSContextData *>(SSL_CTX_get_app_data(ssl_ctx));
static_cast<tls::TLSContextData *>(SSL_CTX_get_app_data(ssl_ctx));
// client SSL_CTX is also included in all_ssl_ctx_, but has no
// tls_ctx_data.
@@ -777,7 +829,7 @@ SSL_CTX *ConnectionHandler::create_tls_ticket_key_memcached_ssl_ctx() {
auto &tlsconf = config->tls;
auto &memcachedconf = config->tls.ticket.memcached;
auto ssl_ctx = ssl::create_ssl_client_context(
auto ssl_ctx = tls::create_ssl_client_context(
#ifdef HAVE_NEVERBLEED
nb_.get(),
#endif // HAVE_NEVERBLEED
@@ -849,4 +901,8 @@ ConnectionHandler::get_indexed_ssl_ctx(size_t idx) const {
return indexed_ssl_ctx_[idx];
}
void ConnectionHandler::set_enable_acceptor_on_ocsp_completion(bool f) {
enable_acceptor_on_ocsp_completion_ = f;
}
} // namespace shrpx

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