Compare commits

..

200 Commits

Author SHA1 Message Date
Tatsuhiro Tsujikawa
d1f4dafd73 Merge pull request #803 from clemahieu/asio_threading
Holding more shared_ptrs instead of raw ptrs
2017-02-09 20:55:23 +09:00
clemahieu
13fc54c6a9 Holding more shared_ptrs instead of raw ptrs to make sure called objects don't get deleted. 2017-02-08 23:01:37 -06:00
Tatsuhiro Tsujikawa
36a2023310 examples: Fix compile errors for asio server examples 2017-02-05 18:08:31 +09:00
Tatsuhiro Tsujikawa
51e474c097 src: Remove deleted source files 2017-02-05 18:08:01 +09:00
Tatsuhiro Tsujikawa
0df13452aa Merge branch 'asio_threading' of https://github.com/clemahieu/nghttp2 into clemahieu-asio_threading 2017-02-05 17:45:01 +09:00
Tatsuhiro Tsujikawa
aad3e275d1 Merge branch 'clemahieu-acceptor_infinite_loop' 2017-02-04 11:35:43 +09:00
Tatsuhiro Tsujikawa
1649948e78 asio: Add curly brackets to avoid possible well known issue 2017-02-04 11:33:21 +09:00
clemahieu
6d3e010ae7 Infinite loop in acceptor handler. 2017-02-04 11:31:12 +09:00
Tatsuhiro Tsujikawa
7dddac081e clang-format 2017-02-04 11:29:10 +09:00
Tatsuhiro Tsujikawa
588dd33241 Merge branch 'worenga-github-pr-preload' 2017-02-04 11:28:53 +09:00
clemahieu
2d9be885ec Using io_service passed in to server rather than managing our own. 2017-02-03 15:56:23 -06:00
Benedikt Christoph Wolters
14ccb24be5 add support for link rel="preload" for --get-assets 2017-02-01 15:54:15 +01:00
Tatsuhiro Tsujikawa
025ec85144 Merge pull request #790 from nghttp2/nghttpx-backend-frontend-tls-parameter
nghttpx: Add frontend-tls parameter to backend to require client TLS
2017-01-31 21:49:51 +09:00
Tatsuhiro Tsujikawa
bd97886d8e nghttpx: Use stack allocated buffer instead of making std::string 2017-01-29 22:11:33 +09:00
Tatsuhiro Tsujikawa
0b1ddad62b nghttpx: Add frontend-tls parameter to backend to require client TLS 2017-01-28 22:19:14 +09:00
Tatsuhiro Tsujikawa
540853bde8 nghttpx: Fix typo 2017-01-28 22:18:17 +09:00
Tatsuhiro Tsujikawa
c757f7d848 nghttpx: Recommend POST for backendconfig API request 2017-01-28 17:54:00 +09:00
Tatsuhiro Tsujikawa
052f3a3871 Update doc 2017-01-26 23:40:12 +09:00
Tatsuhiro Tsujikawa
2ae83e871b Merge branch 'Sp1l-master' 2017-01-26 20:36:57 +09:00
Tatsuhiro Tsujikawa
1cc08c0a51 nghttpx: Show warning if PSK options are used but not supported 2017-01-26 20:34:58 +09:00
Bernard Spil
16be89f9cc nghttpx: Don't build PSK features with LibreSSL
LibreSSL removed PSK

Signed-off-by: Bernard Spil <brnrd@FreeBSD.org>
2017-01-26 20:21:55 +09:00
Tatsuhiro Tsujikawa
b72c5f104e h2load: Fix wrong req_stat updates 2017-01-26 00:26:35 +09:00
Tatsuhiro Tsujikawa
7e6eb7e02a h2load: Explicitly count the number of requests left and inflight 2017-01-26 00:16:12 +09:00
Tatsuhiro Tsujikawa
712b08e8ed Bump up version number to 1.20.0-DEV 2017-01-25 20:50:21 +09:00
Tatsuhiro Tsujikawa
fbf156d22d Update man pages 2017-01-25 20:30:16 +09:00
Tatsuhiro Tsujikawa
965a0e87e5 Bump up version number to 1.19.0, LT revision to 26:4:12 2017-01-25 20:23:37 +09:00
Tatsuhiro Tsujikawa
2b55ca1ce6 Update AUTHORS 2017-01-25 20:22:05 +09:00
Tatsuhiro Tsujikawa
534c01896c Update bash_completion 2017-01-24 23:05:53 +09:00
Tatsuhiro Tsujikawa
c25197ac40 Update man pages 2017-01-24 23:05:39 +09:00
Tatsuhiro Tsujikawa
becae65257 Fix leak 2017-01-24 22:59:01 +09:00
Tatsuhiro Tsujikawa
ba9f2c3ae2 Compile with Android NDK r13b using clang 2017-01-23 00:32:51 +09:00
Tatsuhiro Tsujikawa
0387525b77 Update doc 2017-01-23 00:30:56 +09:00
Tatsuhiro Tsujikawa
5311185333 nghttpx: Define the maximum number of digits in uint64_t 2017-01-22 22:33:52 +09:00
Tatsuhiro Tsujikawa
2fc2a27ac1 nghttpx: Use char instead of char[] if possible 2017-01-22 22:28:14 +09:00
Tatsuhiro Tsujikawa
db938afd66 nghttpx: Increase default backlog 2017-01-20 23:06:24 +09:00
Tatsuhiro Tsujikawa
89ddc47616 nghttpx: More constexpr 2017-01-20 23:04:48 +09:00
Tatsuhiro Tsujikawa
90b7849af1 Merge branch 'nghttpx-optimize-accesslog' 2017-01-20 23:03:52 +09:00
Tatsuhiro Tsujikawa
3176e872b3 nghttpx: Efficient access.log writer
Write integer to log buffer directly to improve efficiency.  Remove
unused function templates.  Use [first, last) style arguments for
copy() function templates.
2017-01-20 22:42:41 +09:00
Tatsuhiro Tsujikawa
16206d5f67 nghttp: Use std::unique_ptr for html_parser 2017-01-18 00:34:39 +09:00
Tatsuhiro Tsujikawa
0f33749790 nghttp: Take into account scheme and port when parsing HTML links
Previously, when parsing HTML links, we only take into account
overridden host.  But we actually need more variables to consider.  In
this commit, we take into account overridden scheme, host, and port to
parse HTML links.
2017-01-18 00:29:51 +09:00
Tatsuhiro Tsujikawa
5e7e4c0cc0 nghttp: config.headers should be inspected rather than req->req_nva 2017-01-17 23:00:37 +09:00
Tatsuhiro Tsujikawa
25503ad763 Merge branch 'worenga-fix-get-assets' 2017-01-17 21:15:40 +09:00
Benedikt Christoph Wolters
8f513fceca Fix authority for --get-assets if IP adress is used in conjunction with user-defined :authority header 2017-01-17 21:14:36 +09:00
Tatsuhiro Tsujikawa
91af4ed70d Merge branch 'nghttpx-accesslog-write-early' 2017-01-14 11:39:58 +09:00
Tatsuhiro Tsujikawa
685e926494 nghttpx: Add --accesslog-write-early option
--accesslog-write-early option is analogous to HAProxy's logasap.  If
used, nghttpx writes access log when response header fields are
received from backend rather than when request transaction finishes.
2017-01-13 22:12:21 +09:00
Tatsuhiro Tsujikawa
a2afd393ed nghttpx: Remove field from LogSpec which can be got from Downstream 2017-01-11 22:30:12 +09:00
Tatsuhiro Tsujikawa
4e9098bccf Merge branch 'nghttpx-accesslog-timestamp' 2017-01-11 21:14:12 +09:00
Tatsuhiro Tsujikawa
33aa327ef5 nghttpx: Fix access.log timestamp
access.log timestamp is now when request header fields are received,
rather than when access log is written.
2017-01-11 20:47:17 +09:00
Tatsuhiro Tsujikawa
9067ff5eee nghttp: Use nghttp2::ssl::DEFAULT_CIPHER_LIST 2017-01-09 23:50:38 +09:00
Tatsuhiro Tsujikawa
efeede4192 nghttpx: Fix typo 2017-01-09 23:49:10 +09:00
Tatsuhiro Tsujikawa
6a8749873f nghttpx: Add detailed TLS connection logging 2017-01-09 23:32:35 +09:00
Tatsuhiro Tsujikawa
b1b8308555 Update doc 2017-01-09 22:22:04 +09:00
Tatsuhiro Tsujikawa
9b574a5a76 nghttpx: Fix typo 2017-01-09 22:19:19 +09:00
Tatsuhiro Tsujikawa
0567f1f038 Add constexpr to StringRef(const CharT *, size_t) 2017-01-09 21:15:53 +09:00
Tatsuhiro Tsujikawa
4be5de1163 src: Move log related functions from util.cc to shrpx_log.cc 2017-01-09 19:34:40 +09:00
Tatsuhiro Tsujikawa
9db1c9467c src: Add constexpr to long_options 2017-01-09 19:28:00 +09:00
Tatsuhiro Tsujikawa
3444b42d44 src: Add more constexpr 2017-01-09 17:17:48 +09:00
Tatsuhiro Tsujikawa
6595ae26ea src: Add constexpr to const objects 2017-01-09 17:11:37 +09:00
Tatsuhiro Tsujikawa
41d8a3ac09 Document PROX protocol and PSK in nghttpx howto 2017-01-09 16:33:51 +09:00
Tatsuhiro Tsujikawa
175001a8d9 Add migration section from nghttpx v1.18.x 2017-01-09 16:09:30 +09:00
Tatsuhiro Tsujikawa
7cf9e00283 Update bash_completion 2017-01-09 14:56:03 +09:00
Tatsuhiro Tsujikawa
8a3eb3f066 Update man pages 2017-01-09 14:55:51 +09:00
Tatsuhiro Tsujikawa
7e1a0d204b h2load: Show default cipher list in -h 2017-01-09 14:47:32 +09:00
Tatsuhiro Tsujikawa
cbca2e35b5 nghttpx: Show default cipher list in -h 2017-01-09 14:43:13 +09:00
Tatsuhiro Tsujikawa
fc9bdf024f src: Make DEFAULT_CIPHER_LIST constexpr char[] 2017-01-09 14:42:40 +09:00
Tatsuhiro Tsujikawa
3f97e6cd3a Merge pull request #776 from nghttp2/nghttpx-memchunkbuffer
nghttpx: Use Memchunk based read buffer for frontend connection
2017-01-09 13:08:23 +09:00
Tatsuhiro Tsujikawa
4fa150c494 nghttpx: Use Memchunk based read buffer for frontend connection
Previously, we have dedicated read buffer for each frontend
connection.  With this commit, the buffer spaces are only used when
needed, and pooled if they are not used.  This reduces memory usage
for idle client connections.
2017-01-08 23:20:14 +09:00
Tatsuhiro Tsujikawa
e8b2508036 nghttpx: Rename confusing names in HttpDownstreamConnection 2017-01-08 23:09:00 +09:00
Tatsuhiro Tsujikawa
ac399e41ac nghttpx: Update doc
Mention client-ciphers, and no-http2-cipher-black-list options in
psk-secrets and client-psk-secrets options.
2017-01-08 23:04:07 +09:00
Tatsuhiro Tsujikawa
95dd908834 Merge branch 'nghttpx-more-tls-options' 2017-01-08 22:57:21 +09:00
Tatsuhiro Tsujikawa
9c7e54d9b5 nghttpx: Add client-ciphers option
Previously, ciphers option sets cipher list for both frontend and
backend TLS connections.  With this commit, ciphers option only sets
cipher list for frontend connections.  The new client-ciphers option
sets cipher list for backend connection.
2017-01-08 22:40:58 +09:00
Tatsuhiro Tsujikawa
3c03024881 nghttpx: Add client-no-http2-cipher-black-list option
This commit adds client-no-http2-cipher-black-list option to disable
enforcement of HTTP/2 cipher black list on backend HTTP/2 connection.
Previously, existing no-http2-cipher-black-list option disables it for
both frontend and backend connections.  Now no-http2-cipher-black-list
option only disables it for frontend connection.
2017-01-08 22:33:19 +09:00
Tatsuhiro Tsujikawa
36dfc0a56a nghttpx: Reorganize client side TLS configuration 2017-01-08 22:25:30 +09:00
Tatsuhiro Tsujikawa
55bf6cdb15 Merge branch 'nghttpx-psk' 2017-01-08 21:10:07 +09:00
Tatsuhiro Tsujikawa
0abc220013 nghttpx: Fix the bug that no-http2-cipher-black-list does not work
Because of the redundant check in backend HTTP/2 session,
no-http2-cipher-black-list does not work on backend HTTP/2 connection.
This commit fixes it.
2017-01-08 19:43:24 +09:00
Tatsuhiro Tsujikawa
c28900990a h2load: Show custom server temp key such as X25519 2017-01-08 17:58:19 +09:00
Tatsuhiro Tsujikawa
5108193d7b h2load: Fix incorrect return value from spdylay_send_callback 2017-01-08 17:32:35 +09:00
Tatsuhiro Tsujikawa
79a24f5dd9 nghttpx: Add --client-psk-secret option to enable PSK in backend 2017-01-08 00:35:55 +09:00
Tatsuhiro Tsujikawa
83c759572c nghttpx: Add --psk-secret option to enable PSK in frontend connection 2017-01-08 00:35:54 +09:00
Tatsuhiro Tsujikawa
1a07fb000b nghttpx: Enable SCT with OpenSSL 1.1.0 2017-01-06 21:29:04 +09:00
Tatsuhiro Tsujikawa
4aab15999d Merge pull request #769 from alagoutte/pvs
Fix issue reporting by PVS Studio
2017-01-04 20:27:55 +09:00
Tatsuhiro Tsujikawa
441982674f Merge pull request #768 from makovich/master
Update README file
2017-01-04 20:24:04 +09:00
Alexis La Goutte
8256c6e070 libevent-client: fix Incorrect format found by PVS Studio (V576)
Consider checking the fourth actual argument of the 'fprintf' function. The SIGNED integer type argument is expected.
2017-01-03 22:03:53 +01:00
Alexis La Goutte
ae87a44b94 nghttp2_hd: fix It is odd that the body of 'hd_get_num_table_entries' function is fully equivalent to the body of 'get_max_index' function found by PVS Studio (V524) 2017-01-03 22:03:53 +01:00
Alexis La Goutte
87d1692e27 nghttp2_submit: fix Parameter 'flags' is always rewritten in function body before being used found by PVS Studio (V763) 2017-01-03 22:03:53 +01:00
makovich
1d2f008656 Update README file 2017-01-03 23:00:32 +03:00
Tatsuhiro Tsujikawa
b064d8a9ff Merge branch 'nghttpx-fronend-proxyproto' 2017-01-03 17:28:20 +09:00
Tatsuhiro Tsujikawa
528af200b6 Merge branch 'nghttpx-fix-libev-assertion-error' 2017-01-03 17:26:05 +09:00
Tatsuhiro Tsujikawa
c6827a7dac nghttpx: Fix assertion error in libev ev_io_start 2017-01-03 16:43:49 +09:00
Tatsuhiro Tsujikawa
55ecb082ee nghttpx: Handle c-ares success without result 2017-01-03 14:35:05 +09:00
Tatsuhiro Tsujikawa
5f2cf461e6 integration: Avoid nghttpx accept-proxy-protocol option 2017-01-03 14:14:37 +09:00
Tatsuhiro Tsujikawa
b313386988 nghttpx: Add proxyproto to frontend option to accept PROXY protocol
Previously, global accept-proxy-protocol option enables PROXY protocol
support for all frontend listeners, but this was inflexible.  To fix
this issue, accept-proxy-protocol option is now deprecated, and
instead proxyproto parameter in frontend option enables PROXY protocol
support per frontend.
2017-01-03 12:47:03 +09:00
Tatsuhiro Tsujikawa
3933280d29 src: Fix assertion error with boringssl
boringssl says:

/* It is an error to clear any bits that have already been set. (We can't try
 * to get a second close_notify or send two.) */
assert((SSL_get_shutdown(ssl) & mode) == SSL_get_shutdown(ssl));
2017-01-02 11:48:38 +09:00
Tatsuhiro Tsujikawa
2b6073900f Merge branch 'nghttpx-dns-timeout-fix' 2016-12-30 11:39:28 +09:00
Tatsuhiro Tsujikawa
d1ba43a69f nghttpx: Fix bug that DNS timeout was erroneously disabled 2016-12-30 11:09:02 +09:00
Tatsuhiro Tsujikawa
a0779edec4 nghttpx: Fix bug that DNS timeout was ignored 2016-12-30 11:08:26 +09:00
Tatsuhiro Tsujikawa
d70fefe72f Bump up version number to 1.19.0-DEV 2016-12-27 20:55:00 +09:00
Tatsuhiro Tsujikawa
b52db072f1 Update bash_completion 2016-12-27 20:43:30 +09:00
Tatsuhiro Tsujikawa
ab0b98db61 Update man pages 2016-12-27 20:42:46 +09:00
Tatsuhiro Tsujikawa
4245d98653 Bump up version number to 1.18.0, LT revision to 26:3:12 2016-12-27 20:35:31 +09:00
Tatsuhiro Tsujikawa
93b4866f5b Merge branch 'nghttpx-dont-write-after-failure' 2016-12-26 00:49:45 +09:00
Tatsuhiro Tsujikawa
25df164219 nghttpx: Don't write again after failure
Plain write(2) is OK, but SSL_write requires same arguments on retry.
It would be better to avoid calling them again.
2016-12-26 00:35:38 +09:00
Tatsuhiro Tsujikawa
ba03c082e9 Merge branch 'nghttpx-retry-h1-backend-request' 2016-12-25 23:05:29 +09:00
Tatsuhiro Tsujikawa
bcfa333322 nghttpx: Refactor h1 backend retry code 2016-12-25 22:19:51 +09:00
Tatsuhiro Tsujikawa
c4aeadd57d nghttpx: Retry h1 backend request if first write fails 2016-12-25 22:19:51 +09:00
Tatsuhiro Tsujikawa
e6b4454e48 Merge branch 'nghttpx-better-early-final-response-handling' 2016-12-24 23:02:52 +09:00
Tatsuhiro Tsujikawa
3226d21609 Merge pull request #755 from nghttp2/nghttpx-h1-frontend-keep-alive-timeout
nghttpx: Add frontend-keep-alive-timeout option
2016-12-24 23:01:17 +09:00
Tatsuhiro Tsujikawa
3d20c2dce6 nghttpx: Feed read event rather than calling on_read
on_read may fail, but we failed to check its return value most of the
places.  This is because failure means deletion of ClientHandler, but
because of architecture, we cannot delete it.  Feeding read event is
better since we can move call on_read from libev callback.  We can
delete ClientHandler form there.
2016-12-24 22:57:59 +09:00
Tatsuhiro Tsujikawa
cd83d70e7b nghttpx: Don't reset stream if we have already received response 2016-12-24 22:54:22 +09:00
Tatsuhiro Tsujikawa
a0ce5ea9ab nghttpx: Keep reading after backend write failed
Because of bidirectional nature of TCP, we may fail write(2), but have
still pending read in TCP buffer, which may contain response body.  To
forward them, we should keep reading until get EOF from backend.

To avoid stalling HTTP/1 upload when request buffer is full, and we
have received complete response from backend, drop connection in that
case.
2016-12-24 22:50:02 +09:00
Tatsuhiro Tsujikawa
3c600c103f nghttpx: Add frontend-keep-alive-timeout option 2016-12-23 11:01:29 +09:00
Tatsuhiro Tsujikawa
841ac75c3e nghttpx: Clarify that backend-keep-alive-timeout applies to h1 only 2016-12-23 10:49:39 +09:00
Tatsuhiro Tsujikawa
80a96817aa Add c-ares to android build guide 2016-12-21 23:47:34 +09:00
Tatsuhiro Tsujikawa
ecc05e0a1a Add git submodule to required build step 2016-12-21 23:34:28 +09:00
Tatsuhiro Tsujikawa
359730af54 Fix regression in ff64f64e1d 2016-12-21 23:19:10 +09:00
Tatsuhiro Tsujikawa
e9cb19c80e Merge branch 'nghttpx-new-errorlog-format' 2016-12-20 23:14:18 +09:00
Tatsuhiro Tsujikawa
049e064e28 nghttpx: New error log format
To debug multi threaded configuration easier, we added current PID and
thread ID to error log.  Previously, we didn't add date and time if
log level is NOTICE.  In this change, we always write date and time
regardless of log level.
2016-12-20 23:13:19 +09:00
Tatsuhiro Tsujikawa
0463928a1e nghttpx: Fix uninitialized errors found by coverity scan 2016-12-18 22:16:52 +09:00
Tatsuhiro Tsujikawa
02d34c8c4c nghttpx: Fix dead code found by coverity scan 2016-12-18 22:14:26 +09:00
Tatsuhiro Tsujikawa
46acf32c41 Merge pull request #748 from nghttp2/pkg-config-libxml2
Use pkg-config to detect libxml2
2016-12-18 00:46:22 +09:00
Tatsuhiro Tsujikawa
cab0a76795 Use pkg-config to detect libxml2 2016-12-18 00:20:30 +09:00
Tatsuhiro Tsujikawa
0c76cebbfc Merge branch 'travis-make-distcheck' 2016-12-17 22:54:12 +09:00
Tatsuhiro Tsujikawa
5029b85b25 Add -f option to rm rst files not to pause travis build 2016-12-17 22:27:14 +09:00
Tatsuhiro Tsujikawa
0b71d9b828 travis: Do make distcheck for autotools build 2016-12-17 22:18:43 +09:00
Tatsuhiro Tsujikawa
464d7c4ec6 Update doc 2016-12-16 22:09:00 +09:00
Tatsuhiro Tsujikawa
ed21b631ae Update doc 2016-12-16 21:31:26 +09:00
Tatsuhiro Tsujikawa
950e2d9954 Mention dynamic hostname lookup in nghttpx how-to 2016-12-16 21:30:13 +09:00
Tatsuhiro Tsujikawa
71c054a789 Merge pull request #745 from nghttp2/remove-tiny-nghttpd
examples: Delete tiny-nghttpd
2016-12-15 00:18:33 +09:00
Tatsuhiro Tsujikawa
d2f456e5b1 Remove tiny-nghttpd from cmake build system 2016-12-14 23:56:02 +09:00
Tatsuhiro Tsujikawa
30a44b26d3 examples: Delete tiny-nghttpd
nghttpd does the better job in this area.
2016-12-14 23:26:52 +09:00
Tatsuhiro Tsujikawa
7dff758f8b Merge pull request #743 from nghttp2/fix-fetch-ocsp-response-with-openssl-1-1-0
Fix bug that fetch-ocsp-response does not work with OpenSSL 1.1.0
2016-12-12 22:59:40 +09:00
Tatsuhiro Tsujikawa
bd3ececdd8 Fix bug that fetch-ocsp-response does not work with OpenSSL 1.1.0
The syntax of openssl ocsp -header option has been changed in OpenSSL
1.1.0.  And it now does not require -header option anymore.  It looks
like that it is workaround for 1.0.x versions.
2016-12-12 21:22:48 +09:00
Tatsuhiro Tsujikawa
77416b0ac2 Update man pages 2016-12-11 17:01:16 +09:00
Tatsuhiro Tsujikawa
fce9efd341 Mention that non-numeric host is allowed in API call if "dns" is used 2016-12-11 17:00:33 +09:00
Tatsuhiro Tsujikawa
928fda1d70 Update bash_completion 2016-12-11 16:52:14 +09:00
Tatsuhiro Tsujikawa
2d9d654507 Update man pages 2016-12-11 16:52:03 +09:00
Tatsuhiro Tsujikawa
7398e57174 Merge pull request #740 from nghttp2/backend-dns
nghttpx: Dynamic DNS
2016-12-11 16:48:28 +09:00
Tatsuhiro Tsujikawa
503ec82f4d Merge pull request #737 from alagoutte/win
Add Windows CI via AppVeyor
2016-12-11 16:38:46 +09:00
Tatsuhiro Tsujikawa
22bd9fb530 nghttpx: Set DNS cache expire date for error and ok statuses only 2016-12-11 11:49:24 +09:00
Tatsuhiro Tsujikawa
e007b6b031 Add DNS integration tests 2016-12-11 11:32:38 +09:00
Tatsuhiro Tsujikawa
c487cd888f nghttpx: Periodically remove expired DNS cache entries 2016-12-11 10:42:54 +09:00
Tatsuhiro Tsujikawa
fd403a85c8 nghttpx: Just return DNS_STATUS_ERROR
At the moment, we use both resolvers, and if either one is not
DNS_STATUS_IDLE, the other one is also not DNS_STATUS_IDLE.  This may
change if we are going to configure DNS so that either A or AAAA
lookup is done.  In that case, it is better to just return
DNS_STATUS_ERROR in the diff.  This is because the calling side does
not expect DNS_STATUS_IDLE in that case.
2016-12-11 10:39:19 +09:00
Tatsuhiro Tsujikawa
a06a8c36a4 nghttpx: Add --dns-lookup-timeout and --dns-max-try options 2016-12-11 00:50:16 +09:00
Tatsuhiro Tsujikawa
0967ee9cb9 nghttpx: Better logging for DNS resolver 2016-12-10 23:10:18 +09:00
Tatsuhiro Tsujikawa
d66d34f9b9 Add libc-ares detection to cmake 2016-12-10 22:40:18 +09:00
Tatsuhiro Tsujikawa
264a98d106 nghttpx: Call c-ares initialization/cleanup functions 2016-12-10 21:41:03 +09:00
Tatsuhiro Tsujikawa
7c11d2d9bb Require c-ares >= 1.7.5 2016-12-10 21:40:50 +09:00
Tatsuhiro Tsujikawa
b58d7b406f Try c-ares 1.7.5 because it is the latest version travis offers 2016-12-10 21:32:37 +09:00
Tatsuhiro Tsujikawa
5ed9e4c83b Document that c-ares is a required library for nghttpx 2016-12-10 21:09:51 +09:00
Tatsuhiro Tsujikawa
e7da2a669e .travis.yml: Add libc-ares-dev 2016-12-10 21:09:51 +09:00
Tatsuhiro Tsujikawa
d66377d4b6 nghttpx: Add dns-cache-timeout option
This option controls how long cached DNS entries remain valid.
2016-12-10 21:09:51 +09:00
Tatsuhiro Tsujikawa
77a324fa46 nghttpx: Backend API call allows non-numeric host with dns parameter 2016-12-10 21:09:50 +09:00
Tatsuhiro Tsujikawa
38b5cad4e3 nghttpx: Lookup backend host name dynamically
We have added "dns" parameter to backend option.  If specified, name
lookup is done dynamically.  If not, name lookup is done at start up,
or configuration reloading.  nghttpx caches DNS result including error
case in 30 seconds in this commit.  Later commit makes this
configurable.

DNS resolution is done asynchronously using c-ares library.
2016-12-10 21:09:50 +09:00
Alexis La Goutte
29014643a9 Add Windows CI via AppVeyor 2016-12-08 21:17:02 +01:00
Tatsuhiro Tsujikawa
0872f6babe Merge pull request #736 from nghttp2/accept-204-content-length-0
Accept content-length: 0 in 204 response
2016-12-04 13:27:30 +09:00
Tatsuhiro Tsujikawa
b6a9cf9ffa nghttpx: Accept and ignore content-length: 0 in 204 response for now 2016-12-03 14:57:48 +09:00
Tatsuhiro Tsujikawa
5645cad577 Accept and ignore content-length: 0 in 204 response for now 2016-12-03 14:57:16 +09:00
Tatsuhiro Tsujikawa
85ba33c08f nghttpx: Wait for child process to exit
Normally, we don't have wait for child process to exit, since init can
take care of them.  But in containerized environment, pid 0 init might
not be available, and defunct processes can be piled up.  This commit
ensures that OCSP and neverbleed processes are waited for before
worker process exits.
2016-11-30 22:59:02 +09:00
Tatsuhiro Tsujikawa
ff64f64e1d nghttpx: Faster HTTP/1 frontend 2016-11-29 20:42:27 +09:00
Tatsuhiro Tsujikawa
bdd3425028 Bump up version number to 1.18.0-DEV 2016-11-27 14:09:30 +09:00
Tatsuhiro Tsujikawa
70e02cddd3 Update man pages 2016-11-27 10:56:36 +09:00
Tatsuhiro Tsujikawa
d1c0a17cc2 Bump up version number to 1.17.0, LT revision to 26:2:12 2016-11-27 10:53:28 +09:00
Tatsuhiro Tsujikawa
35c5cbbc21 Update AUTHORS 2016-11-27 10:51:03 +09:00
Tatsuhiro Tsujikawa
2ff31bdd2b nghttpx: Remove redundant check 2016-11-26 22:34:24 +09:00
Tatsuhiro Tsujikawa
2fa3d34af1 nghttpx: Use Connection::again_rt() in MemcachedConnection 2016-11-26 19:45:23 +09:00
Tatsuhiro Tsujikawa
fa3452ec68 nghttpx: Use Connection::again_rt() in LiveCheck 2016-11-26 19:45:23 +09:00
Tatsuhiro Tsujikawa
7451f2f212 nghttpx: Fix frequent crash with --backend-http-proxy-uri 2016-11-26 19:45:23 +09:00
Tatsuhiro Tsujikawa
e9ab75a386 nghttpx: Robust backend read timeout 2016-11-26 19:45:23 +09:00
Tatsuhiro Tsujikawa
d83949bc88 asio: server: Call on_close callback on connection close 2016-11-21 22:43:23 +09:00
Tatsuhiro Tsujikawa
50f42a80c9 nghttpx: Fix bug that mishandles response header from h1 backend 2016-11-18 22:33:29 +09:00
Tatsuhiro Tsujikawa
00bd76fc3d Update man pages 2016-11-13 22:26:56 +09:00
Tatsuhiro Tsujikawa
93ea6b581e Update LT revision due to v1.16.1 release 2016-11-13 22:24:31 +09:00
Tatsuhiro Tsujikawa
8e52a5c7f3 Add test for the bug that shift goes beyond 32 bits in decode_length 2016-11-12 23:05:16 +09:00
Tatsuhiro Tsujikawa
0aa35e574a Merge branch 'ruds-master' 2016-11-12 22:55:28 +09:00
Matt Rudary
5a81f2441f Prevent undefined behavior in decode_length 2016-11-11 11:12:43 -05:00
Tatsuhiro Tsujikawa
2b75aff32e nghttpx: Fix bug that zero-length POST is not forwarded 2016-11-11 00:48:32 +09:00
Tatsuhiro Tsujikawa
f4474d57ec nghttpx: Fix compile error with gcc 2016-11-07 23:11:52 +09:00
Tatsuhiro Tsujikawa
d0310c8aee integration: Add te header field test 2016-11-07 22:56:36 +09:00
Tatsuhiro Tsujikawa
8471c9e92e nghttpx: Parse te header field a bit more properly 2016-11-07 22:47:48 +09:00
Tatsuhiro Tsujikawa
f5a4c9d971 nghttpx: Don't copy non-final nva since they are backed by Downstream 2016-11-04 22:06:01 +09:00
Tatsuhiro Tsujikawa
a0dd8918eb nghttpx: Don't mutate *_key_prev_ in add_header 2016-11-04 22:04:42 +09:00
Tatsuhiro Tsujikawa
baa9b1cac0 nghttpx: Remove optional reason-phrase from SPDY :status 2016-11-04 21:16:45 +09:00
Tatsuhiro Tsujikawa
38443d2195 nghttpx: Small optimization 2016-11-04 21:13:22 +09:00
Tatsuhiro Tsujikawa
208d71561a src: Add missing mandatory SP after status code 2016-11-04 02:19:37 +09:00
Tatsuhiro Tsujikawa
25fbc7b435 nghttpx: Reset flags as well 2016-11-04 02:00:50 +09:00
Tatsuhiro Tsujikawa
6bd95d885d Merge pull request #723 from nghttp2/strict-http-framing
Strict http framing
2016-11-03 23:30:38 +09:00
Tatsuhiro Tsujikawa
c171097dea Document that libnghttp2's behaviour about Content-Length 2016-11-03 23:09:30 +09:00
Tatsuhiro Tsujikawa
6bcdb178a5 nghttpx: Header key and value must be string in mruby script 2016-11-03 22:58:45 +09:00
Tatsuhiro Tsujikawa
5e10cc4cad nghttpx: Use gc save/restore around downcase method 2016-11-03 22:47:52 +09:00
Tatsuhiro Tsujikawa
95e6c875f0 nghttpx: Use mrb_ary_ref instead of mrb_ary_entry 2016-11-03 22:44:42 +09:00
Tatsuhiro Tsujikawa
6eb2829ee8 nghttpx: Strip content-length with 204 or 200 to CONNECT in mruby 2016-11-03 22:25:15 +09:00
Tatsuhiro Tsujikawa
6ad9ddcdea Disallow content-length in 1xx, 204, or 200 to a CONNECT request 2016-11-03 17:26:32 +09:00
Tatsuhiro Tsujikawa
e082b7be72 nghttpx: Strict handling for Content-Length or Transfer-Encoding in h1
We now treat Content-Length or Transfer-Encoding as error if they come
with 204 or 1xx status code, or 200 to a CONNECT request in HTTP/1
response.
2016-11-03 17:00:05 +09:00
Tatsuhiro Tsujikawa
da01d8dedb nghttpx: Delete outdated comment 2016-11-03 16:48:24 +09:00
Tatsuhiro Tsujikawa
ca6f6511f2 Avoid memcpy against NULL src 2016-11-01 23:54:21 +09:00
Tatsuhiro Tsujikawa
ee8440408c Merge branch 'remoe-add_cmake_win_version' 2016-10-28 22:24:51 +09:00
Remo E
9cd695a1db MSVC version resource support 2016-10-28 22:24:22 +09:00
Tatsuhiro Tsujikawa
4b45906f46 Merge branch 'dalf-boringssl' 2016-10-28 00:06:06 +09:00
dalf
d448eb54f9 Fix compilation with BoringSSL 2016-10-27 10:00:52 +00:00
Tatsuhiro Tsujikawa
65739fe754 Bump up version number to 1.17.0-DEV 2016-10-24 21:21:24 +09:00
129 changed files with 4635 additions and 2856 deletions

View File

@@ -28,6 +28,7 @@ addons:
- libevent-dev
- libjansson-dev
- libjemalloc-dev
- libc-ares-dev
- cmake
- cmake-data
before_install:
@@ -54,7 +55,8 @@ before_script:
- 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
script:
- make check
- if [ "$CI_BUILD" = "autotools" ]; then make distcheck; 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.
# - cd integration-tests

View File

@@ -21,6 +21,7 @@ Anders Bakken
Andreas Pohl
Andy Davies
Ant Bryan
Benedikt Christoph Wolters
Bernard Spil
Brian Card
Brian Suh
@@ -48,6 +49,7 @@ Kit Chan
Kyle Schomp
Lucas Pardue
MATSUMOTO Ryosuke
Matt Rudary
Mike Conlen
Mike Frysinger
Nicholas Hurley
@@ -78,9 +80,11 @@ Zhuoyun Wei
acesso
ayanamist
bxshi
dalf
es
fangdingjun
kumagi
makovich
mod-h2-dev
moparisthebest
snnn

View File

@@ -24,12 +24,12 @@
cmake_minimum_required(VERSION 3.0)
# XXX using 1.8.90 instead of 1.9.0-DEV
project(nghttp2 VERSION 1.16.1)
project(nghttp2 VERSION 1.19.90)
# See versioning rule:
# http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
set(LT_CURRENT 26)
set(LT_REVISION 1)
set(LT_REVISION 4)
set(LT_AGE 12)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
@@ -59,6 +59,7 @@ find_package(PythonInterp)
# Auto-detection of features that can be toggled
find_package(OpenSSL 1.0.1)
find_package(Libev 4.11)
find_package(Libcares 1.7.5)
find_package(ZLIB 1.2.3)
if(OPENSSL_FOUND AND LIBEV_FOUND AND ZLIB_FOUND)
set(ENABLE_APP_DEFAULT ON)
@@ -207,6 +208,14 @@ if(LIBEVENT_FOUND)
# Must both link the core and openssl libraries.
set(LIBEVENT_OPENSSL_LIBRARIES ${LIBEVENT_LIBRARIES})
endif()
# libc-ares (for src)
set(HAVE_LIBCARES ${LIBCARES_FOUND})
if(LIBCARES_FOUND)
set(LIBCARES_INCLUDE_DIRS ${LIBCARES_INCLUDE_DIR})
else()
set(LIBCARES_INCLUDE_DIRS "")
set(LIBCARES_LIBRARIES "")
endif()
# jansson (for src/nghttp, src/deflatehd and src/inflatehd)
set(HAVE_JANSSON ${JANSSON_FOUND})
# libxml2 (for src/nghttp)
@@ -304,7 +313,6 @@ include(CheckFunctionExists)
check_function_exists(_Exit HAVE__EXIT)
check_function_exists(accept4 HAVE_ACCEPT4)
# timerfd_create was added in linux kernel 2.6.25
include(CheckSymbolExists)
# XXX does this correctly detect initgroups (un)availability on cygwin?
check_symbol_exists(initgroups grp.h HAVE_DECL_INITGROUPS)
@@ -316,13 +324,6 @@ if(NOT HAVE_DECL_INITGROUPS AND HAVE_UNISTD_H)
endif()
endif()
check_function_exists(timerfd_create HAVE_TIMERFD_CREATE)
# Checks for epoll availability, primarily for examples/tiny-nghttpd
check_symbol_exists(epoll_create sys/epoll.h HAVE_EPOLL)
if(HAVE_EPOLL AND HAVE_TIMERFD_CREATE)
set(ENABLE_TINY_NGHTTPD 1)
endif()
set(WARNCFLAGS)
set(WARNCXXFLAGS)
if(CMAKE_C_COMPILER_ID MATCHES "MSVC")
@@ -499,6 +500,7 @@ message(STATUS "summary of build options:
OpenSSL: ${HAVE_OPENSSL} (LIBS='${OPENSSL_LIBRARIES}')
Libxml2: ${HAVE_LIBXML2} (LIBS='${LIBXML2_LIBRARIES}')
Libev: ${HAVE_LIBEV} (LIBS='${LIBEV_LIBRARIES}')
Libc-ares: ${HAVE_LIBCARES} (LIBS='${LIBCARES_LIBRARIES}')
Libevent(SSL): ${HAVE_LIBEVENT_OPENSSL} (LIBS='${LIBEVENT_OPENSSL_LIBRARIES}')
Spdylay: ${HAVE_SPDYLAY} (LIBS='${SPDYLAY_LIBRARIES}')
Jansson: ${HAVE_JANSSON} (LIBS='${JANSSON_LIBRARIES}')

View File

@@ -45,7 +45,8 @@ EXTRA_DIST = nghttpx.conf.sample proxy.pac.sample android-config android-make \
cmake/Version.cmake \
cmake/FindCython.cmake \
cmake/FindLibevent.cmake \
cmake/FindJansson.cmake
cmake/FindJansson.cmake \
cmake/FindLibcares.cmake
.PHONY: clang-format

View File

@@ -70,6 +70,7 @@ are required:
* OpenSSL >= 1.0.1
* libev >= 4.11
* zlib >= 1.2.3
* libc-ares >= 1.7.5
ALPN support requires OpenSSL >= 1.0.2 (released 22 January 2015).
LibreSSL >= 2.2.0 can be used instead of OpenSSL, but OpenSSL has more
@@ -98,6 +99,11 @@ To mitigate heap fragmentation in long running server programs
* jemalloc
.. note::
Alpine Linux currently does not support malloc replacement
due to musl limitations. See details in issue `#762 <https://github.com/nghttp2/nghttp2/issues/762>`_.
libnghttp2_asio C++ library requires the following packages:
* libboost-dev >= 1.54.0
@@ -115,7 +121,7 @@ If you are using Ubuntu 14.04 LTS (trusty) or Debian 7.0 (wheezy) and above run
sudo apt-get install g++ make binutils autoconf automake autotools-dev libtool pkg-config \
zlib1g-dev libcunit1-dev libssl-dev libxml2-dev libev-dev libevent-dev libjansson-dev \
libjemalloc-dev cython python3-dev python-setuptools
libc-ares-dev libjemalloc-dev cython python3-dev python-setuptools
From Ubuntu 15.10, spdylay has been available as a package named
`libspdylay-dev`. For the earlier Ubuntu release, you need to build
@@ -149,6 +155,7 @@ used:
.. code-block:: text
$ git submodule update --init
$ autoreconf -i
$ automake
$ autoconf
@@ -159,8 +166,7 @@ To compile the source code, gcc >= 4.8.3 or clang >= 3.4 is required.
.. note::
To enable mruby support in nghttpx, run ``git submodule update
--init`` before running configure script, and use ``--with-mruby``
To enable mruby support in nghttpx, and use ``--with-mruby``
configure option.
.. note::

View File

@@ -39,8 +39,9 @@ PATH="$TOOLCHAIN"/bin:"$PATH"
--without-libxml2 \
--disable-python-bindings \
--disable-examples \
CC="$TOOLCHAIN"/bin/arm-linux-androideabi-gcc \
CXX="$TOOLCHAIN"/bin/arm-linux-androideabi-g++ \
--disable-threads \
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"

53
appveyor.yml Normal file
View File

@@ -0,0 +1,53 @@
# Notes:
# - Minimal appveyor.yml file is an empty file. All sections are optional.
# - Indent each level of configuration with 2 spaces. Do not use tabs!
# - All section names are case-sensitive.
# - Section names should be unique on each level.
#---------------------------------#
# general configuration #
#---------------------------------#
# version format
#version: 0.10.{build}
# branches to build
branches:
# blacklist
except:
- gh-pages
# Do not build on tags (GitHub only)
skip_tags: true
#---------------------------------#
# environment configuration #
#---------------------------------#
os: Windows Server 2012
# scripts that run after cloning repository
install:
# install Win-Flex-Bison
#- cmd: cinst winflexbison -y
#---------------------------------#
# build configuration #
#---------------------------------#
# scripts to run before build
before_build:
- cmd: cmake .
# scripts to run *after* solution is built and *before* automatic packaging occurs (web apps, NuGet packages, Azure Cloud Services)
# before_package:
# scripts to run after build
# after_build:
# to run your custom scripts instead of automatic MSBuild
build_script:
- cmd: cmake --build .
# to disable automatic builds
# build: off

40
cmake/FindLibcares.cmake Normal file
View File

@@ -0,0 +1,40 @@
# - Try to find libcares
# Once done this will define
# LIBCARES_FOUND - System has libcares
# LIBCARES_INCLUDE_DIRS - The libcares include directories
# LIBCARES_LIBRARIES - The libraries needed to use libcares
find_package(PkgConfig QUIET)
pkg_check_modules(PC_LIBCARES QUIET libcares)
find_path(LIBCARES_INCLUDE_DIR
NAMES ares.h
HINTS ${PC_LIBCARES_INCLUDE_DIRS}
)
find_library(LIBCARES_LIBRARY
NAMES cares
HINTS ${PC_LIBCARES_LIBRARY_DIRS}
)
if(LIBCARES_INCLUDE_DIR)
set(_version_regex "^#define[ \t]+ARES_VERSION_STR[ \t]+\"([^\"]+)\".*")
file(STRINGS "${LIBCARES_INCLUDE_DIR}/ares_version.h"
LIBCARES_VERSION REGEX "${_version_regex}")
string(REGEX REPLACE "${_version_regex}" "\\1"
LIBCARES_VERSION "${LIBCARES_VERSION}")
unset(_version_regex)
endif()
include(FindPackageHandleStandardArgs)
# handle the QUIETLY and REQUIRED arguments and set LIBCARES_FOUND to TRUE
# if all listed variables are TRUE and the requested version matches.
find_package_handle_standard_args(Libcares REQUIRED_VARS
LIBCARES_LIBRARY LIBCARES_INCLUDE_DIR
VERSION_VAR LIBCARES_VERSION)
if(LIBCARES_FOUND)
set(LIBCARES_LIBRARIES ${LIBCARES_LIBRARY})
set(LIBCARES_INCLUDE_DIRS ${LIBCARES_INCLUDE_DIR})
endif()
mark_as_advanced(LIBCARES_INCLUDE_DIR LIBCARES_LIBRARY)

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.16.1], [t-tujikawa@users.sourceforge.net])
AC_INIT([nghttp2], [1.20.0-DEV], [t-tujikawa@users.sourceforge.net])
AC_CONFIG_AUX_DIR([.])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_HEADERS([config.h])
@@ -45,7 +45,7 @@ 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, 26)
AC_SUBST(LT_REVISION, 1)
AC_SUBST(LT_REVISION, 4)
AC_SUBST(LT_AGE, 12)
major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"`
@@ -370,6 +370,13 @@ if test "x${have_openssl}" = "xno"; then
AC_MSG_NOTICE($OPENSSL_PKG_ERRORS)
fi
# c-ares (for src)
PKG_CHECK_MODULES([LIBCARES], [libcares >= 1.7.5], [have_libcares=yes],
[have_libcares=no])
if test "x${have_libcares}" = "xno"; then
AC_MSG_NOTICE($LIBCARES_PKG_ERRORS)
fi
# libevent_openssl (for examples)
# 2.0.8 is required because we use evconnlistener_set_error_cb()
PKG_CHECK_MODULES([LIBEVENT_OPENSSL], [libevent_openssl >= 2.0.8],
@@ -389,15 +396,12 @@ else
fi
# libxml2 (for src/nghttp)
have_libxml2=no
if test "x${request_libxml2}" != "xno"; then
m4_ifdef([AM_PATH_XML2],
[AM_PATH_XML2(2.7.7, [have_libxml2=yes], [have_libxml2=no])],
[AC_MSG_WARN([configure was created without libxml2 detection macro; libxml2 detection is disabled])])
if test "x${have_libxml2}" = "xyes"; then
AC_DEFINE([HAVE_LIBXML2], [1], [Define to 1 if you have `libxml2` library.])
fi
PKG_CHECK_MODULES([LIBXML2], [libxml-2.0 >= 2.7.7],
[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.])
else
AC_MSG_NOTICE($LIBXML2_PKG_ERRORS)
fi
if test "x${request_libxml2}" = "xyes" &&
@@ -479,13 +483,14 @@ if test "x${request_asio_lib}" = "xyes"; then
fi
fi
# The nghttp, nghttpd and nghttpx under src depend on zlib, OpenSSL
# and libev
# The nghttp, nghttpd and nghttpx under src depend on zlib, OpenSSL,
# libev, and libc-ares.
enable_app=no
if test "x${request_app}" != "xno" &&
test "x${have_zlib}" = "xyes" &&
test "x${have_openssl}" = "xyes" &&
test "x${have_libev}" = "xyes"; then
test "x${have_libev}" = "xyes" &&
test "x${have_libcares}" = "xyes"; then
enable_app=yes
fi
@@ -640,6 +645,26 @@ AC_SYS_LARGEFILE
AC_CHECK_MEMBER([struct tm.tm_gmtoff], [have_struct_tm_tm_gmtoff=yes],
[have_struct_tm_tm_gmtoff=no], [[#include <time.h>]])
AC_CHECK_MEMBER([struct sockaddr_in.sin_len],
[AC_DEFINE([HAVE_SOCKADDR_IN_SIN_LEN],[1],
[Define to 1 if struct sockaddr_in has sin_len member.])],
[],
[[
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
]])
AC_CHECK_MEMBER([struct sockaddr_in6.sin6_len],
[AC_DEFINE([HAVE_SOCKADDR_IN6_SIN6_LEN],[1],
[Define to 1 if struct sockaddr_in6 has sin6_len member.])],
[],
[[
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
]])
if test "x$have_struct_tm_tm_gmtoff" = "xyes"; then
AC_DEFINE([HAVE_STRUCT_TM_TM_GMTOFF], [1],
[Define to 1 if you have `struct tm.tm_gmtoff` member.])
@@ -701,13 +726,6 @@ AC_CHECK_DECLS([initgroups], [], [], [[
#include <grp.h>
]])
# Checks for epoll availability, primarily for examples/tiny-nghttpd
AX_HAVE_EPOLL([have_epoll=yes], [have_epoll=no])
AM_CONDITIONAL([ENABLE_TINY_NGHTTPD],
[ test "x${have_epoll}" = "xyes" &&
test "x${have_timerfd_create}" = "xyes"])
save_CFLAGS=$CFLAGS
save_CXXFLAGS=$CXXFLAGS
@@ -888,8 +906,9 @@ AC_MSG_NOTICE([summary of build options:
Failmalloc: ${enable_failmalloc}
Libs:
OpenSSL: ${have_openssl} (CFLAGS='${OPENSSL_CFLAGS}' LIBS='${OPENSSL_LIBS}')
Libxml2: ${have_libxml2} (CFLAGS='${XML_CPPFLAGS}' LIBS='${XML_LIBS}')
Libxml2: ${have_libxml2} (CFLAGS='${LIBXML2_CPPFLAGS}' LIBS='${LIBXML2_LIBS}')
Libev: ${have_libev} (CFLAGS='${LIBEV_CFLAGS}' LIBS='${LIBEV_LIBS}')
Libc-ares ${have_libcares} (CFLAGS='${LIBCARES_CFLAGS}' LIBS='${LIBCARES_LIBS}')
Libevent(SSL): ${have_libevent_openssl} (CFLAGS='${LIBEVENT_OPENSSL_CFLAGS}' LIBS='${LIBEVENT_OPENSSL_LIBS}')
Spdylay: ${have_spdylay} (CFLAGS='${LIBSPDYLAY_CFLAGS}' LIBS='${LIBSPDYLAY_LIBS}')
Jansson: ${have_jansson} (CFLAGS='${JANSSON_CFLAGS}' LIBS='${JANSSON_LIBS}')

View File

@@ -266,7 +266,7 @@ apiref.rst: \
$(APIDOCS): apiref.rst
clean-local:
[ $(srcdir) = $(builddir) ] || for i in $(RST_FILES); do [ -e $(builddir)/$$i ] && rm $(builddir)/$$i; done
[ $(srcdir) = $(builddir) ] || for i in $(RST_FILES); do [ -e $(builddir)/$$i ] && rm -f $(builddir)/$$i; done
-rm -f apiref.rst
-rm -f $(APIDOCS)
-rm -rf $(BUILDDIR)/*

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 --backend-request-buffer --max-request-header-fields --fastopen --tls-ticket-key-memcached --conf --backend-http2-max-concurrent-streams --worker-write-burst --npn-list --fetch-ocsp-response-file --no-via --tls-session-cache-memcached-cert-file --no-http2-cipher-black-list --mruby-file --stream-read-timeout --backend-connect-timeout --forwarded-for --accesslog-syslog --frontend-http2-read-timeout --listener-disable-timeout --ciphers --strip-incoming-x-forwarded-for --no-server-rewrite --private-key-passwd-file --backend-keep-alive-timeout --backend-http-proxy-uri --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 --request-header-field-buffer --api-max-request-body --frontend-http2-decoder-dynamic-table-size --errorlog-file --frontend-http2-max-concurrent-streams --frontend-write-timeout --tls-ticket-key-cipher --read-burst --backend --server-name --insecure --backend-max-backoff --log-level --host-rewrite --tls-proto-list --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 --tls-session-cache-memcached --no-ocsp --frontend-http2-encoder-dynamic-table-size --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 --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 --accept-proxy-protocol --tls-dyn-rec-idle-timeout --frontend-http2-optimize-write-buffer-size --verify-client --backend-response-buffer --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 --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 --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 --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-proto-list --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 --tls-session-cache-memcached --no-ocsp --frontend-http2-encoder-dynamic-table-size --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 --backend-response-buffer --read-rate --backend-connections-per-frontend --strip-incoming-forwarded ' -- "$cur" ) )
;;
*)
_filedir

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "H2LOAD" "1" "Nov 13, 2016" "1.16.1" "nghttp2"
.TH "H2LOAD" "1" "Jan 25, 2017" "1.19.0" "nghttp2"
.SH NAME
h2load \- HTTP/2 benchmarking tool
.
@@ -123,6 +123,8 @@ Add/Override a header to the requests.
.B \-\-ciphers=<SUITE>
Set allowed cipher list. The format of the string is
described in OpenSSL ciphers(1).
.sp
Default: \fBECDHE\-ECDSA\-CHACHA20\-POLY1305:ECDHE\-RSA\-CHACHA20\-POLY1305:ECDHE\-ECDSA\-AES128\-GCM\-SHA256:ECDHE\-RSA\-AES128\-GCM\-SHA256:ECDHE\-ECDSA\-AES256\-GCM\-SHA384:ECDHE\-RSA\-AES256\-GCM\-SHA384:DHE\-RSA\-AES128\-GCM\-SHA256:DHE\-RSA\-AES256\-GCM\-SHA384:ECDHE\-ECDSA\-AES128\-SHA256:ECDHE\-RSA\-AES128\-SHA256:ECDHE\-ECDSA\-AES128\-SHA:ECDHE\-RSA\-AES256\-SHA384:ECDHE\-RSA\-AES128\-SHA:ECDHE\-ECDSA\-AES256\-SHA384:ECDHE\-ECDSA\-AES256\-SHA:ECDHE\-RSA\-AES256\-SHA:DHE\-RSA\-AES128\-SHA256:DHE\-RSA\-AES128\-SHA:DHE\-RSA\-AES256\-SHA256:DHE\-RSA\-AES256\-SHA:ECDHE\-ECDSA\-DES\-CBC3\-SHA:ECDHE\-RSA\-DES\-CBC3\-SHA:EDH\-RSA\-DES\-CBC3\-SHA:AES128\-GCM\-SHA256:AES256\-GCM\-SHA384:AES128\-SHA256:AES256\-SHA256:AES128\-SHA:AES256\-SHA:DES\-CBC3\-SHA:!DSS\fP
.UNINDENT
.INDENT 0.0
.TP

View File

@@ -96,6 +96,8 @@ OPTIONS
Set allowed cipher list. The format of the string is
described in OpenSSL ciphers(1).
Default: ``ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS``
.. option:: -p, --no-tls-proto=<PROTOID>
Specify ALPN identifier of the protocol to be used when

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "NGHTTP" "1" "Nov 13, 2016" "1.16.1" "nghttp2"
.TH "NGHTTP" "1" "Jan 25, 2017" "1.19.0" "nghttp2"
.SH NAME
nghttp \- HTTP/2 client
.

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "NGHTTPD" "1" "Nov 13, 2016" "1.16.1" "nghttp2"
.TH "NGHTTPD" "1" "Jan 25, 2017" "1.19.0" "nghttp2"
.SH NAME
nghttpd \- HTTP/2 server
.

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "NGHTTPX" "1" "Nov 13, 2016" "1.16.1" "nghttp2"
.TH "NGHTTPX" "1" "Jan 25, 2017" "1.19.0" "nghttp2"
.SH NAME
nghttpx \- HTTP/2 proxy
.
@@ -120,13 +120,13 @@ together forming load balancing group.
Several parameters <PARAM> are accepted after <PATTERN>.
The parameters are delimited by ";". The available
parameters are: "proto=<PROTO>", "tls",
"sni=<SNI_HOST>", "fall=<N>", "rise=<N>", and
"affinity=<METHOD>". The parameter consists of keyword,
and optionally followed by "=" and value. For example,
the parameter "proto=h2" consists of the keyword "proto"
and value "h2". The parameter "tls" consists of the
keyword "tls" without value. Each parameter is
described as follows.
"sni=<SNI_HOST>", "fall=<N>", "rise=<N>",
"affinity=<METHOD>", and "dns". The parameter consists
of keyword, and optionally followed by "=" and value.
For example, the parameter "proto=h2" consists of the
keyword "proto" and value "h2". The parameter "tls"
consists of the keyword "tls" without value. Each
parameter is described as follows.
.sp
The backend application protocol can be specified using
optional "proto" parameter, and in the form of
@@ -175,6 +175,14 @@ 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
By default, name resolution of backend host name is done
at start up, or reloading configuration. If "dns"
parameter is given, name resolution takes place
dynamically. This is useful if backend address changes
frequently. If "dns" is given, name resolution of
backend host name at start up, or reloading
configuration is skipped.
.sp
Since ";" and ":" are used as delimiter, <PATTERN> must
not contain these characters. Since ";" has special
meaning in shell, the option value must be quoted.
@@ -210,6 +218,10 @@ specify "healthmon" parameter. This is disabled by
default. Any requests which come through this address
are replied with 200 HTTP status, without no body.
.sp
To accept PROXY protocol version 1 on frontend
connection, specify "proxyproto" parameter. This is
disabled by default.
.sp
Default: \fB*,3000\fP
.UNINDENT
.INDENT 0.0
@@ -217,7 +229,7 @@ Default: \fB*,3000\fP
.B \-\-backlog=<N>
Set listen backlog size.
.sp
Default: \fB512\fP
Default: \fB65536\fP
.UNINDENT
.INDENT 0.0
.TP
@@ -245,11 +257,6 @@ timeouts when connecting and making CONNECT request can
be specified by \fI\%\-\-backend\-read\-timeout\fP and
\fI\%\-\-backend\-write\-timeout\fP options.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-accept\-proxy\-protocol
Accept PROXY protocol version 1 on frontend connection.
.UNINDENT
.SS Performance
.INDENT 0.0
.TP
@@ -426,6 +433,14 @@ Default: \fB30s\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-frontend\-keep\-alive\-timeout=<DURATION>
Specify keep\-alive timeout for frontend HTTP/1
connection.
.sp
Default: \fB1m\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-stream\-read\-timeout=<DURATION>
Specify read timeout for HTTP/2 and SPDY streams. 0
means no timeout.
@@ -465,7 +480,8 @@ Default: \fB30s\fP
.INDENT 0.0
.TP
.B \-\-backend\-keep\-alive\-timeout=<DURATION>
Specify keep\-alive timeout for backend connection.
Specify keep\-alive timeout for backend HTTP/1
connection.
.sp
Default: \fB2s\fP
.UNINDENT
@@ -512,8 +528,18 @@ Default: \fB2m\fP
.INDENT 0.0
.TP
.B \-\-ciphers=<SUITE>
Set allowed cipher list. The format of the string is
described in OpenSSL ciphers(1).
Set allowed cipher list for frontend connection. The
format of the string is described in OpenSSL ciphers(1).
.sp
Default: \fBECDHE\-ECDSA\-CHACHA20\-POLY1305:ECDHE\-RSA\-CHACHA20\-POLY1305:ECDHE\-ECDSA\-AES128\-GCM\-SHA256:ECDHE\-RSA\-AES128\-GCM\-SHA256:ECDHE\-ECDSA\-AES256\-GCM\-SHA384:ECDHE\-RSA\-AES256\-GCM\-SHA384:DHE\-RSA\-AES128\-GCM\-SHA256:DHE\-RSA\-AES256\-GCM\-SHA384:ECDHE\-ECDSA\-AES128\-SHA256:ECDHE\-RSA\-AES128\-SHA256:ECDHE\-ECDSA\-AES128\-SHA:ECDHE\-RSA\-AES256\-SHA384:ECDHE\-RSA\-AES128\-SHA:ECDHE\-ECDSA\-AES256\-SHA384:ECDHE\-ECDSA\-AES256\-SHA:ECDHE\-RSA\-AES256\-SHA:DHE\-RSA\-AES128\-SHA256:DHE\-RSA\-AES128\-SHA:DHE\-RSA\-AES256\-SHA256:DHE\-RSA\-AES256\-SHA:ECDHE\-ECDSA\-DES\-CBC3\-SHA:ECDHE\-RSA\-DES\-CBC3\-SHA:EDH\-RSA\-DES\-CBC3\-SHA:AES128\-GCM\-SHA256:AES256\-GCM\-SHA384:AES128\-SHA256:AES256\-SHA256:AES128\-SHA:AES256\-SHA:DES\-CBC3\-SHA:!DSS\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-client\-ciphers=<SUITE>
Set allowed cipher list for backend connection. The
format of the string is described in OpenSSL ciphers(1).
.sp
Default: \fBECDHE\-ECDSA\-CHACHA20\-POLY1305:ECDHE\-RSA\-CHACHA20\-POLY1305:ECDHE\-ECDSA\-AES128\-GCM\-SHA256:ECDHE\-RSA\-AES128\-GCM\-SHA256:ECDHE\-ECDSA\-AES256\-GCM\-SHA384:ECDHE\-RSA\-AES256\-GCM\-SHA384:DHE\-RSA\-AES128\-GCM\-SHA256:DHE\-RSA\-AES256\-GCM\-SHA384:ECDHE\-ECDSA\-AES128\-SHA256:ECDHE\-RSA\-AES128\-SHA256:ECDHE\-ECDSA\-AES128\-SHA:ECDHE\-RSA\-AES256\-SHA384:ECDHE\-RSA\-AES128\-SHA:ECDHE\-ECDSA\-AES256\-SHA384:ECDHE\-ECDSA\-AES256\-SHA:ECDHE\-RSA\-AES256\-SHA:DHE\-RSA\-AES128\-SHA256:DHE\-RSA\-AES128\-SHA:DHE\-RSA\-AES256\-SHA256:DHE\-RSA\-AES256\-SHA:ECDHE\-ECDSA\-DES\-CBC3\-SHA:ECDHE\-RSA\-DES\-CBC3\-SHA:EDH\-RSA\-DES\-CBC3\-SHA:AES128\-GCM\-SHA256:AES256\-GCM\-SHA384:AES128\-SHA256:AES256\-SHA256:AES128\-SHA:AES256\-SHA:DES\-CBC3\-SHA:!DSS\fP
.UNINDENT
.INDENT 0.0
.TP
@@ -803,9 +829,18 @@ Default: \fB1s\fP
.INDENT 0.0
.TP
.B \-\-no\-http2\-cipher\-black\-list
Allow black listed cipher suite on HTTP/2 connection.
See \fI\%https://tools.ietf.org/html/rfc7540#appendix\-A\fP for
the complete HTTP/2 cipher suites black list.
Allow black listed cipher suite on frontend HTTP/2
connection. See
\fI\%https://tools.ietf.org/html/rfc7540#appendix\-A\fP for the
complete HTTP/2 cipher suites black list.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-client\-no\-http2\-cipher\-black\-list
Allow black listed cipher suite on backend HTTP/2
connection. See
\fI\%https://tools.ietf.org/html/rfc7540#appendix\-A\fP for the
complete HTTP/2 cipher suites black list.
.UNINDENT
.INDENT 0.0
.TP
@@ -819,6 +854,39 @@ argument <CERT>, or certificate option in configuration
file. For additional certificates, use \fI\%\-\-subcert\fP
option. This option requires OpenSSL >= 1.0.2.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-psk\-secrets=<PATH>
Read list of PSK identity and secrets from <PATH>. This
is used for frontend connection. The each line of input
file is formatted as <identity>:<hex\-secret>, where
<identity> is PSK identity, and <hex\-secret> is secret
in hex. An empty line, and line which starts with \(aq#\(aq
are skipped. The default enabled cipher list might not
contain any PSK cipher suite. In that case, desired PSK
cipher suites must be enabled using \fI\%\-\-ciphers\fP option.
The desired PSK cipher suite may be black listed by
HTTP/2. To use those cipher suites with HTTP/2,
consider to use \fI\%\-\-no\-http2\-cipher\-black\-list\fP option.
But be aware its implications.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-client\-psk\-secrets=<PATH>
Read PSK identity and secrets from <PATH>. This is used
for backend connection. The each line of input file is
formatted as <identity>:<hex\-secret>, where <identity>
is PSK identity, and <hex\-secret> is secret in hex. An
empty line, and line which starts with \(aq#\(aq are skipped.
The first identity and secret pair encountered is used.
The default enabled cipher list might not contain any
PSK cipher suite. In that case, desired PSK cipher
suites must be enabled using \fI\%\-\-client\-ciphers\fP option.
The desired PSK cipher suite may be black listed by
HTTP/2. To use those cipher suites with HTTP/2,
consider to use \fI\%\-\-client\-no\-http2\-cipher\-black\-list\fP
option. But be aware its implications.
.UNINDENT
.SS HTTP/2 and SPDY
.INDENT 0.0
.TP
@@ -1057,6 +1125,13 @@ Default: \fB$remote_addr \- \- [$time_local] "$request" $status $body_bytes_sent
.UNINDENT
.INDENT 0.0
.TP
.B \-\-accesslog\-write\-early
Write access log when response header fields are
received from backend rather than when request
transaction finishes.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-errorlog\-file=<PATH>
Set path to write error log. To reopen file, send USR1
signal to nghttpx. stderr will be redirected to the
@@ -1238,7 +1313,7 @@ backend server, the custom error pages are not used.
.B \-\-server\-name=<NAME>
Change server response header field value to <NAME>.
.sp
Default: \fBnghttpx nghttp2/1.16.1\fP
Default: \fBnghttpx nghttp2/1.19.0\fP
.UNINDENT
.INDENT 0.0
.TP
@@ -1255,6 +1330,33 @@ Set the maximum size of request body for API request.
.sp
Default: \fB16K\fP
.UNINDENT
.SS DNS
.INDENT 0.0
.TP
.B \-\-dns\-cache\-timeout=<DURATION>
Set duration that cached DNS results remain valid. Note
that nghttpx caches the unsuccessful results as well.
.sp
Default: \fB10s\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-dns\-lookup\-timeout=<DURATION>
Set timeout that DNS server is given to respond to the
initial DNS query. For the 2nd and later queries,
server is given time based on this timeout, and it is
scaled linearly.
.sp
Default: \fB5s\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-dns\-max\-try=<N>
Set the number of DNS query before nghttpx gives up name
lookup.
.sp
Default: \fB2\fP
.UNINDENT
.SS Debug
.INDENT 0.0
.TP
@@ -1393,6 +1495,35 @@ positional arguments in command\-line, use \fBprivate\-key\-file\fP and
.sp
\fI\%\-\-conf\fP option cannot be used in the configuration file and
will be ignored if specified.
.TP
.B Error log
Error log is written to stderr by default. It can be configured
using \fI\%\-\-errorlog\-file\fP\&. The format of log message is as
follows:
.sp
<datetime> <master\-pid> <current\-pid> <thread\-id> <level> (<filename>:<line>) <msg>
.INDENT 7.0
.TP
.B <datetime>
It is a conbination of date and time when the log is written. It
is in ISO 8601 format.
.TP
.B <master\-pid>
It is a master process ID.
.TP
.B <current\-pid>
It is a process ID which writes this log.
.TP
.B <thread\-id>
It is a thread ID which writes this log. It would be unique
within <current\-pid>.
.TP
.B <filename> and <line>
They are source file name, and line number which produce this log.
.TP
.B <msg>
It is a log message body.
.UNINDENT
.UNINDENT
.SH SIGNALS
.INDENT 0.0
@@ -1936,9 +2067,9 @@ 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
\fI\%backend\fP in request body while non numeric
hostname is allowed in command\-line or configuration file is read
using \fI\%\-\-conf\fP\&.
\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\&.
.SH SEE ALSO
.sp
\fBnghttp(1)\fP, \fBnghttpd(1)\fP, \fBh2load(1)\fP

View File

@@ -104,13 +104,13 @@ Connections
Several parameters <PARAM> are accepted after <PATTERN>.
The parameters are delimited by ";". The available
parameters are: "proto=<PROTO>", "tls",
"sni=<SNI_HOST>", "fall=<N>", "rise=<N>", and
"affinity=<METHOD>". The parameter consists of keyword,
and optionally followed by "=" and value. For example,
the parameter "proto=h2" consists of the keyword "proto"
and value "h2". The parameter "tls" consists of the
keyword "tls" without value. Each parameter is
described as follows.
"sni=<SNI_HOST>", "fall=<N>", "rise=<N>",
"affinity=<METHOD>", and "dns". The parameter consists
of keyword, and optionally followed by "=" and value.
For example, the parameter "proto=h2" consists of the
keyword "proto" and value "h2". The parameter "tls"
consists of the keyword "tls" without value. Each
parameter is described as follows.
The backend application protocol can be specified using
optional "proto" parameter, and in the form of
@@ -159,6 +159,14 @@ Connections
break if one of the backend gets unreachable, or backend
settings are reloaded or replaced by API.
By default, name resolution of backend host name is done
at start up, or reloading configuration. If "dns"
parameter is given, name resolution takes place
dynamically. This is useful if backend address changes
frequently. If "dns" is given, name resolution of
backend host name at start up, or reloading
configuration is skipped.
Since ";" and ":" are used as delimiter, <PATTERN> must
not contain these characters. Since ";" has special
meaning in shell, the option value must be quoted.
@@ -194,6 +202,10 @@ Connections
default. Any requests which come through this address
are replied with 200 HTTP status, without no body.
To accept PROXY protocol version 1 on frontend
connection, specify "proxyproto" parameter. This is
disabled by default.
Default: ``*,3000``
@@ -201,7 +213,7 @@ Connections
Set listen backlog size.
Default: ``512``
Default: ``65536``
.. option:: --backend-address-family=(auto|IPv4|IPv6)
@@ -227,10 +239,6 @@ Connections
be specified by :option:`--backend-read-timeout` and
:option:`--backend-write-timeout` options.
.. option:: --accept-proxy-protocol
Accept PROXY protocol version 1 on frontend connection.
Performance
~~~~~~~~~~~
@@ -391,6 +399,13 @@ Timeout
Default: ``30s``
.. option:: --frontend-keep-alive-timeout=<DURATION>
Specify keep-alive timeout for frontend HTTP/1
connection.
Default: ``1m``
.. option:: --stream-read-timeout=<DURATION>
Specify read timeout for HTTP/2 and SPDY streams. 0
@@ -426,7 +441,8 @@ Timeout
.. option:: --backend-keep-alive-timeout=<DURATION>
Specify keep-alive timeout for backend connection.
Specify keep-alive timeout for backend HTTP/1
connection.
Default: ``2s``
@@ -471,8 +487,17 @@ SSL/TLS
.. option:: --ciphers=<SUITE>
Set allowed cipher list. The format of the string is
described in OpenSSL ciphers(1).
Set allowed cipher list for frontend connection. The
format of the string is described in OpenSSL ciphers(1).
Default: ``ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS``
.. option:: --client-ciphers=<SUITE>
Set allowed cipher list for backend connection. The
format of the string is described in OpenSSL ciphers(1).
Default: ``ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS``
.. option:: --ecdh-curves=<LIST>
@@ -731,9 +756,17 @@ SSL/TLS
.. option:: --no-http2-cipher-black-list
Allow black listed cipher suite on HTTP/2 connection.
See https://tools.ietf.org/html/rfc7540#appendix-A for
the complete HTTP/2 cipher suites black list.
Allow black listed cipher suite on frontend HTTP/2
connection. See
https://tools.ietf.org/html/rfc7540#appendix-A for the
complete HTTP/2 cipher suites black list.
.. option:: --client-no-http2-cipher-black-list
Allow black listed cipher suite on backend HTTP/2
connection. See
https://tools.ietf.org/html/rfc7540#appendix-A for the
complete HTTP/2 cipher suites black list.
.. option:: --tls-sct-dir=<DIR>
@@ -746,6 +779,37 @@ SSL/TLS
file. For additional certificates, use :option:`--subcert`
option. This option requires OpenSSL >= 1.0.2.
.. option:: --psk-secrets=<PATH>
Read list of PSK identity and secrets from <PATH>. This
is used for frontend connection. The each line of input
file is formatted as <identity>:<hex-secret>, where
<identity> is PSK identity, and <hex-secret> is secret
in hex. An empty line, and line which starts with '#'
are skipped. The default enabled cipher list might not
contain any PSK cipher suite. In that case, desired PSK
cipher suites must be enabled using :option:`--ciphers` option.
The desired PSK cipher suite may be black listed by
HTTP/2. To use those cipher suites with HTTP/2,
consider to use :option:`--no-http2-cipher-black-list` option.
But be aware its implications.
.. option:: --client-psk-secrets=<PATH>
Read PSK identity and secrets from <PATH>. This is used
for backend connection. The each line of input file is
formatted as <identity>:<hex-secret>, where <identity>
is PSK identity, and <hex-secret> is secret in hex. An
empty line, and line which starts with '#' are skipped.
The first identity and secret pair encountered is used.
The default enabled cipher list might not contain any
PSK cipher suite. In that case, desired PSK cipher
suites must be enabled using :option:`--client-ciphers` option.
The desired PSK cipher suite may be black listed by
HTTP/2. To use those cipher suites with HTTP/2,
consider to use :option:`--client-no-http2-cipher-black-list`
option. But be aware its implications.
HTTP/2 and SPDY
~~~~~~~~~~~~~~~
@@ -953,6 +1017,12 @@ Logging
Default: ``$remote_addr - - [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"``
.. option:: --accesslog-write-early
Write access log when response header fields are
received from backend rather than when request
transaction finishes.
.. option:: --errorlog-file=<PATH>
Set path to write error log. To reopen file, send USR1
@@ -1118,7 +1188,7 @@ HTTP
Change server response header field value to <NAME>.
Default: ``nghttpx nghttp2/1.16.1``
Default: ``nghttpx nghttp2/1.19.0``
.. option:: --no-server-rewrite
@@ -1137,6 +1207,33 @@ API
Default: ``16K``
DNS
~~~
.. option:: --dns-cache-timeout=<DURATION>
Set duration that cached DNS results remain valid. Note
that nghttpx caches the unsuccessful results as well.
Default: ``10s``
.. option:: --dns-lookup-timeout=<DURATION>
Set timeout that DNS server is given to respond to the
initial DNS query. For the 2nd and later queries,
server is given time based on this timeout, and it is
scaled linearly.
Default: ``5s``
.. option:: --dns-max-try=<N>
Set the number of DNS query before nghttpx gives up name
lookup.
Default: ``2``
Debug
~~~~~
@@ -1266,6 +1363,33 @@ FILES
:option:`--conf` option cannot be used in the configuration file and
will be ignored if specified.
Error log
Error log is written to stderr by default. It can be configured
using :option:`--errorlog-file`. The format of log message is as
follows:
<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
is in ISO 8601 format.
<master-pid>
It is a master process ID.
<current-pid>
It is a process ID which writes this log.
<thread-id>
It is a thread ID which writes this log. It would be unique
within <current-pid>.
<filename> and <line>
They are source file name, and line number which produce this log.
<msg>
It is a log message body.
SIGNALS
-------
@@ -1768,9 +1892,9 @@ 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
:option:`backend <--backend>` in request body while non numeric
hostname is allowed in command-line or configuration file is read
using :option:`--conf`.
: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`.
SEE ALSO
--------

View File

@@ -41,6 +41,33 @@ FILES
:option:`--conf` option cannot be used in the configuration file and
will be ignored if specified.
Error log
Error log is written to stderr by default. It can be configured
using :option:`--errorlog-file`. The format of log message is as
follows:
<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
is in ISO 8601 format.
<master-pid>
It is a master process ID.
<current-pid>
It is a process ID which writes this log.
<thread-id>
It is a thread ID which writes this log. It would be unique
within <current-pid>.
<filename> and <line>
They are source file name, and line number which produce this log.
<msg>
It is a log message body.
SIGNALS
-------
@@ -522,11 +549,11 @@ some cases where the error has occurred before reaching API endpoint
The following section describes available API endpoints.
PUT /api/v1beta1/backendconfig
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
POST /api/v1beta1/backendconfig
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This API replaces the current backend server settings with the
requested ones. The request method should be PUT, but POST is also
requested ones. The request method should be POST, but PUT is also
acceptable. The request body must be nghttpx configuration file
format. For configuration file format, see `FILES`_ section. The
line separator inside the request body must be single LF (0x0A).
@@ -543,9 +570,9 @@ 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
:option:`backend <--backend>` in request body while non numeric
hostname is allowed in command-line or configuration file is read
using :option:`--conf`.
: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`.
SEE ALSO
--------

View File

@@ -173,6 +173,11 @@ parsed as 64 bit signed integer. The sum of data length in the
following DATA frames must match with the number in "Content-Length"
header field if it is present (this does not include padding bytes).
RFC 7230 says that server must not send "Content-Length" in any
response with 1xx, and 204 status code. It also says that
"Content-Length" is not allowed in any response with 200 status code
to a CONNECT request. nghttp2 enforces them as well.
Any deviation results in stream error of type PROTOCOL_ERROR. If
error is found in PUSH_PROMISE frame, stream error is raised against
promised stream.

View File

@@ -22,16 +22,17 @@ unpacked:
.. code-block:: text
$ build/tools/make_standalone_toolchain.py \
--arch arm --api 16 --stl gnustl
--arch arm --api 16 --stl gnustl \
--install-dir $ANDROID_HOME/toolchain
The API level (``--api``) is not important here because we don't use
Android specific C/C++ API.
The dependent libraries, such as OpenSSL and libev should be built
with the toolchain and installed under ``$ANDROID_HOME/usr/local``.
We recommend to build these libraries as static library to make the
deployment easier. libxml2 support is currently disabled.
The dependent libraries, such as OpenSSL, libev, and c-ares should be
built with the toolchain and installed under
``$ANDROID_HOME/usr/local``. We recommend to build these libraries as
static library to make the deployment easier. libxml2 support is
currently disabled.
Although zlib comes with Android NDK, it seems not to be a part of
public API, so we have to built it for our own. That also provides us
@@ -96,6 +97,26 @@ patch, to configure libev, use the following script:
And run ``make install`` to build and install.
To configure c-ares, use the following script:
.. code-block:: sh
#!/bin/sh -e
if [ -z "$ANDROID_HOME" ]; then
echo 'No $ANDROID_HOME specified.'
exit 1
fi
PREFIX=$ANDROID_HOME/usr/local
TOOLCHAIN=$ANDROID_HOME/toolchain
PATH=$TOOLCHAIN/bin:$PATH
./configure \
--host=arm-linux-androideabi \
--build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \
--prefix=$PREFIX \
--disable-shared
To configure zlib, use the following script:
.. code-block:: sh

View File

@@ -343,10 +343,9 @@ requests, do this:
backend=serv1,3000;/;proto=h2
backend=serv1,3000;/ws/;proto=http/1.1
Note that the backends share the same pattern must have the same
backend protocol. The default backend protocol is HTTP/1.1.
The default backend protocol is HTTP/1.1.
TLS can be enabed per pattern basis:
TLS can be enabled per pattern basis:
.. code-block:: text
@@ -356,6 +355,96 @@ TLS can be enabed per pattern basis:
In the above case, connection to serv1 will be encrypted by TLS. On
the other hand, connection to serv2 will not be encrypted by TLS.
Dynamic hostname lookup
-----------------------
By default, nghttpx performs backend hostname lookup at start up, or
configuration reload, and keeps using them in its entire session. To
make nghttpx perform hostname lookup dynamically, use ``dns``
parameter in :option:`--backend` option, like so:
.. code-block:: text
backend=foo.example.com;;dns
nghttpx will cache resolved addresses for certain period of time. To
change this cache period, use :option:`--dns-cache-timeout`.
Enable PROXY protocol
---------------------
PROXY protocol can be enabled per frontend. In order to enable PROXY
protocol, use ``proxyproto`` parameter in :option:`--frontend` option,
like so:
.. code-block:: text
frontend=*,443;proxyproto
PSK cipher suites
-----------------
nghttpx supports pre-shared key (PSK) cipher suites for both frontend
and backend TLS connections. For frontend connection, use
:option:`--psk-secrets` option to specify a file which contains PSK
identity and secrets. The format of the file is
``<identity>:<hex-secret>``, where ``<identity>`` is PSK identity, and
``<hex-secret>`` is PSK secret in hex, like so:
.. code-block:: text
client1:9567800e065e078085c241d54a01c6c3f24b3bab71a606600f4c6ad2c134f3b9
client2:b1376c3f8f6dcf7c886c5bdcceecd1e6f1d708622b6ddd21bda26ebd0c0bca99
nghttpx server accepts any of the identity and secret pairs in the
file. The default cipher suite list does not contain PSK cipher
suites. In order to use PSK, PSK cipher suite must be enabled by
using :option:`--ciphers` option. The desired PSK cipher suite may be
listed in `HTTP/2 cipher black list
<https://tools.ietf.org/html/rfc7540#appendix-A>`_. In order to use
such PSK cipher suite with HTTP/2, disable HTTP/2 cipher black list by
using :option:`--no-http2-cipher-black-list` option. But you should
understand its implications.
At the time of writing, even if only PSK cipher suites are specified
in :option:`--ciphers` option, certificate and private key are still
required.
For backend connection, use :option:`--client-psk-secrets` option to
specify a file which contains single PSK identity and secret. The
format is the same as the file used by :option:`--psk-secrets`
described above, but only first identity and secret pair is solely
used, like so:
.. code-block:: text
client2:b1376c3f8f6dcf7c886c5bdcceecd1e6f1d708622b6ddd21bda26ebd0c0bca99
The default cipher suite list does not contain PSK cipher suites. In
order to use PSK, PSK cipher suite must be enabled by using
:option:`--client-ciphers` option. The desired PSK cipher suite may
be listed in `HTTP/2 cipher black list
<https://tools.ietf.org/html/rfc7540#appendix-A>`_. In order to use
such PSK cipher suite with HTTP/2, disable HTTP/2 cipher black list by
using :option:`--client-no-http2-cipher-black-list` option. But you
should understand its implications.
Migration from nghttpx v1.18.x or earlier
-----------------------------------------
As of nghttpx v1.19.0, :option:`--ciphers` option only changes cipher
list for frontend TLS connection. In order to change cipher list for
backend connection, use :option:`--client-ciphers` option.
Similarly, :option:`--no-http2-cipher-black-list` option only disables
HTTP/2 cipher black list for frontend connection. In order to disable
HTTP/2 cipher black list for backend connection, use
:option:`--client-no-http2-cipher-black-list` option.
``--accept-proxy-protocol`` option was deprecated. Instead, use
``proxyproto`` parameter in :option:`--frontend` option to enable
PROXY protocol support per frontend.
Migration from nghttpx v1.8.0 or earlier
----------------------------------------

View File

@@ -29,10 +29,6 @@ if(ENABLE_EXAMPLES)
add_executable(libevent-server libevent-server.c $<TARGET_OBJECTS:http-parser>)
add_executable(deflate deflate.c $<TARGET_OBJECTS:http-parser>)
if(ENABLE_TINY_NGHTTPD)
add_executable(tiny-nghttpd tiny-nghttpd.c $<TARGET_OBJECTS:http-parser>)
endif()
if(ENABLE_ASIO_LIB)
foreach(name asio-sv asio-sv2 asio-cl asio-cl2)
add_executable(${name} ${name}.cc $<TARGET_OBJECTS:http-parser>)

View File

@@ -51,14 +51,6 @@ libevent_server_SOURCES = libevent-server.c
deflate_SOURCES = deflate.c
if ENABLE_TINY_NGHTTPD
noinst_PROGRAMS += tiny-nghttpd
tiny_nghttpd_SOURCES = tiny-nghttpd.c
endif # ENABLE_TINY_NGHTTPD
if ENABLE_ASIO_LIB
noinst_PROGRAMS += asio-sv asio-sv2 asio-cl asio-cl2

View File

@@ -36,12 +36,25 @@
#include <iostream>
#include <string>
#include <thread>
#include <nghttp2/asio_http2_server.h>
using namespace nghttp2::asio_http2;
using namespace nghttp2::asio_http2::server;
namespace {
void run_forever(boost::asio::io_service &io_service, size_t num_threads) {
std::vector<std::thread> ts;
for (size_t i = 0; i < num_threads; ++i) {
ts.emplace_back([&io_service]() { io_service.run(); });
}
for (auto &t : ts) {
t.join();
}
}
} // namespace
int main(int argc, char *argv[]) {
try {
// Check command line arguments.
@@ -58,9 +71,9 @@ int main(int argc, char *argv[]) {
std::string port = argv[2];
std::size_t num_threads = std::stoi(argv[3]);
http2 server;
boost::asio::io_service io_service;
server.num_threads(num_threads);
http2 server(io_service);
server.handle("/", [](const request &req, const response &res) {
res.write_head(200, {{"foo", {"bar"}}});
@@ -136,11 +149,16 @@ int main(int argc, char *argv[]) {
if (server.listen_and_serve(ec, tls, addr, port)) {
std::cerr << "error: " << ec.message() << std::endl;
}
run_forever(io_service, num_threads);
} else {
if (server.listen_and_serve(ec, addr, port)) {
std::cerr << "error: " << ec.message() << std::endl;
}
run_forever(io_service, num_threads);
}
} catch (std::exception &e) {
std::cerr << "exception: " << e.what() << "\n";
}

View File

@@ -43,12 +43,25 @@
#endif // HAVE_FCNTL_H
#include <iostream>
#include <string>
#include <thread>
#include <nghttp2/asio_http2_server.h>
using namespace nghttp2::asio_http2;
using namespace nghttp2::asio_http2::server;
namespace {
void run_forever(boost::asio::io_service &io_service, size_t num_threads) {
std::vector<std::thread> ts;
for (size_t i = 0; i < num_threads; ++i) {
ts.emplace_back([&io_service]() { io_service.run(); });
}
for (auto &t : ts) {
t.join();
}
}
} // namespace
int main(int argc, char *argv[]) {
try {
// Check command line arguments.
@@ -65,9 +78,9 @@ int main(int argc, char *argv[]) {
std::size_t num_threads = std::stoi(argv[3]);
std::string docroot = argv[4];
http2 server;
boost::asio::io_service io_service;
server.num_threads(num_threads);
http2 server(io_service);
server.handle("/", [&docroot](const request &req, const response &res) {
auto path = percent_decode(req.uri().path);
@@ -112,10 +125,14 @@ int main(int argc, char *argv[]) {
if (server.listen_and_serve(ec, tls, addr, port)) {
std::cerr << "error: " << ec.message() << std::endl;
}
run_forever(io_service, num_threads);
} else {
if (server.listen_and_serve(ec, addr, port)) {
std::cerr << "error: " << ec.message() << std::endl;
}
run_forever(io_service, num_threads);
}
} catch (std::exception &e) {
std::cerr << "exception: " << e.what() << "\n";

View File

@@ -287,7 +287,7 @@ static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
int rv;
if (session_data->stream_data->stream_id == stream_id) {
fprintf(stderr, "Stream %d closed with error_code=%d\n", stream_id,
fprintf(stderr, "Stream %d closed with error_code=%u\n", stream_id,
error_code);
rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
if (rv != 0) {

File diff suppressed because it is too large Load Diff

View File

@@ -149,6 +149,15 @@ OPTIONS = [
"ecdh-curves",
"tls-sct-dir",
"backend-connect-timeout",
"dns-cache-timeout",
"dns-lookup-timeout",
"dns-max-try",
"frontend-keep-alive-timeout",
"psk-secrets",
"client-psk-secrets",
"client-no-http2-cipher-black-list",
"client-ciphers",
"accesslog-write-early",
]
LOGVARS = [

View File

@@ -796,6 +796,30 @@ func TestH1H2RespPhaseReturn(t *testing.T) {
}
}
// TestH1H2TE tests that "te: trailers" header is forwarded to HTTP/2
// backend server by stripping other encodings.
func TestH1H2TE(t *testing.T) {
st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
if got, want := r.Header.Get("te"), "trailers"; got != want {
t.Errorf("te: %v; want %v", got, want)
}
})
defer st.Close()
res, err := st.http1(requestParam{
name: "TestH1H2TE",
header: []hpack.HeaderField{
pair("te", "foo,trailers,bar"),
},
})
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)
}
}
// TestH1APIBackendconfig exercise backendconfig API endpoint routine
// for successful case.
func TestH1APIBackendconfig(t *testing.T) {

View File

@@ -1369,6 +1369,42 @@ func TestH2H1ProxyProtocolV1InvalidID(t *testing.T) {
}
}
// TestH2H1ExternalDNS tests that DNS resolution using external DNS
// with HTTP/1 backend works.
func TestH2H1ExternalDNS(t *testing.T) {
st := newServerTester([]string{"--external-dns"}, t, noopHandler)
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H1ExternalDNS",
})
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)
}
}
// TestH2H1DNS tests that DNS resolution without external DNS with
// HTTP/1 backend works.
func TestH2H1DNS(t *testing.T) {
st := newServerTester([]string{"--dns"}, t, noopHandler)
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H1DNS",
})
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)
}
}
// TestH2H1GracefulShutdown tests graceful shutdown.
func TestH2H1GracefulShutdown(t *testing.T) {
st := newServerTester(nil, t, noopHandler)
@@ -1845,6 +1881,42 @@ func TestH2H2RespPhaseReturn(t *testing.T) {
}
}
// TestH2H2ExternalDNS tests that DNS resolution using external DNS
// with HTTP/2 backend works.
func TestH2H2ExternalDNS(t *testing.T) {
st := newServerTester([]string{"--http2-bridge", "--external-dns"}, t, noopHandler)
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H2ExternalDNS",
})
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)
}
}
// TestH2H2DNS tests that DNS resolution without external DNS with
// HTTP/2 backend works.
func TestH2H2DNS(t *testing.T) {
st := newServerTester([]string{"--http2-bridge", "--dns"}, t, noopHandler)
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H2DNS",
})
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)
}
}
// TestH2APIBackendconfig exercise backendconfig API endpoint routine
// for successful case.
func TestH2APIBackendconfig(t *testing.T) {

View File

@@ -101,10 +101,20 @@ func newServerTesterInternal(src_args []string, t *testing.T, handler http.Handl
args := []string{}
backendTLS := false
dns := false
externalDNS := false
acceptProxyProtocol := false
for _, k := range src_args {
switch k {
case "--http2-bridge":
backendTLS = true
case "--dns":
dns = true
case "--external-dns":
dns = true
externalDNS = true
case "--accept-proxy-protocol":
acceptProxyProtocol = true
default:
args = append(args, k)
}
@@ -117,7 +127,7 @@ func newServerTesterInternal(src_args []string, t *testing.T, handler http.Handl
ts.TLS = new(tls.Config)
ts.TLS.NextProtos = append(ts.TLS.NextProtos, "h2")
ts.StartTLS()
args = append(args, "-k", "--backend-tls")
args = append(args, "-k")
} else {
ts.Start()
}
@@ -134,17 +144,36 @@ func newServerTesterInternal(src_args []string, t *testing.T, handler http.Handl
// URL.Host looks like "127.0.0.1:8080", but we want
// "127.0.0.1,8080"
b := "-b" + strings.Replace(backendURL.Host, ":", ",", -1)
if backendTLS {
b += ";;proto=h2;tls"
b := "-b"
if !externalDNS {
b += fmt.Sprintf("%v;", strings.Replace(backendURL.Host, ":", ",", -1))
} else {
sep := strings.LastIndex(backendURL.Host, ":")
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:])
}
noTLS := "no-tls"
if backendTLS {
b += ";proto=h2;tls"
}
if dns {
b += ";dns"
}
noTLS := ";no-tls"
if frontendTLS {
noTLS = ""
}
args = append(args, fmt.Sprintf("-f127.0.0.1,%v;%v", serverPort, noTLS), b,
var proxyProto string
if acceptProxyProtocol {
proxyProto = ";proxyproto"
}
args = append(args, fmt.Sprintf("-f127.0.0.1,%v%v%v", serverPort, noTLS, proxyProto), b,
"--errorlog-file="+logDir+"/log.txt", "-LINFO")
authority := fmt.Sprintf("127.0.0.1:%v", connectPort)

View File

@@ -26,8 +26,19 @@ set(NGHTTP2_SOURCES
nghttp2_debug.c
)
set(NGHTTP2_RES "")
if(WIN32)
configure_file(
version.rc.in
${CMAKE_CURRENT_BINARY_DIR}/version.rc
@ONLY)
set(NGHTTP2_RES ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
endif()
# Public shared library
add_library(nghttp2 SHARED ${NGHTTP2_SOURCES})
add_library(nghttp2 SHARED ${NGHTTP2_SOURCES} ${NGHTTP2_RES})
set_target_properties(nghttp2 PROPERTIES
COMPILE_FLAGS "${WARNCFLAGS}"
VERSION ${LT_VERSION} SOVERSION ${LT_SOVERSION}

View File

@@ -22,7 +22,7 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
SUBDIRS = includes
EXTRA_DIST = Makefile.msvc CMakeLists.txt
EXTRA_DIST = Makefile.msvc CMakeLists.txt version.rc.in
AM_CFLAGS = $(WARNCFLAGS) $(EXTRACFLAG)
AM_CPPFLAGS = -I$(srcdir)/includes -I$(builddir)/includes -DBUILDING_NGHTTP2 \

View File

@@ -3566,7 +3566,7 @@ NGHTTP2_EXTERN int nghttp2_session_upgrade2(nghttp2_session *session,
* Serializes the SETTINGS values |iv| in the |buf|. The size of the
* |buf| is specified by |buflen|. The number of entries in the |iv|
* array is given by |niv|. The required space in |buf| for the |niv|
* entries is ``8*niv`` bytes and if the given buffer is too small, an
* entries is ``6*niv`` bytes and if the given buffer is too small, an
* error is returned. This function is used mainly for creating a
* SETTINGS payload to be sent with the ``HTTP2-Settings`` header
* field in an HTTP Upgrade request. The data written in |buf| is NOT

View File

@@ -869,7 +869,9 @@ int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, const nghttp2_nv *nva,
p->name = nva[i].name;
p->namelen = nva[i].namelen;
} else {
memcpy(data, nva[i].name, nva[i].namelen);
if (nva[i].namelen) {
memcpy(data, nva[i].name, nva[i].namelen);
}
p->name = data;
p->namelen = nva[i].namelen;
data[p->namelen] = '\0';
@@ -881,7 +883,9 @@ int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, const nghttp2_nv *nva,
p->value = nva[i].value;
p->valuelen = nva[i].valuelen;
} else {
memcpy(data, nva[i].value, nva[i].valuelen);
if (nva[i].valuelen) {
memcpy(data, nva[i].value, nva[i].valuelen);
}
p->value = data;
p->valuelen = nva[i].valuelen;
data[p->valuelen] = '\0';

View File

@@ -2281,10 +2281,6 @@ ssize_t nghttp2_hd_decode_length(uint32_t *res, size_t *shift_ptr, int *fin,
return decode_length(res, shift_ptr, fin, initial, shift, in, last, prefix);
}
static size_t hd_get_num_table_entries(nghttp2_hd_context *context) {
return context->hd_table.len + NGHTTP2_STATIC_TABLE_LENGTH;
}
static const nghttp2_nv *hd_get_table_entry(nghttp2_hd_context *context,
size_t idx) {
if (idx == 0) {
@@ -2301,7 +2297,7 @@ static const nghttp2_nv *hd_get_table_entry(nghttp2_hd_context *context,
}
size_t nghttp2_hd_deflate_get_num_table_entries(nghttp2_hd_deflater *deflater) {
return hd_get_num_table_entries(&deflater->ctx);
return get_max_index(&deflater->ctx);
}
const nghttp2_nv *
@@ -2320,7 +2316,7 @@ nghttp2_hd_deflate_get_max_dynamic_table_size(nghttp2_hd_deflater *deflater) {
}
size_t nghttp2_hd_inflate_get_num_table_entries(nghttp2_hd_inflater *inflater) {
return hd_get_num_table_entries(&inflater->ctx);
return get_max_index(&inflater->ctx);
}
const nghttp2_nv *

View File

@@ -503,6 +503,10 @@ int nghttp2_check_header_value(const uint8_t *value, size_t len) {
}
uint8_t *nghttp2_cpymem(uint8_t *dest, const void *src, size_t len) {
if (len == 0) {
return dest;
}
memcpy(dest, src, len);
return dest + len;

View File

@@ -250,6 +250,25 @@ static int http_response_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
break;
}
case NGHTTP2_TOKEN_CONTENT_LENGTH: {
if (stream->status_code == 204) {
/* content-length header field in 204 response is prohibited by
RFC 7230. But some widely used servers send content-length:
0. Until they get fixed, we ignore it. */
if (stream->content_length != -1) {
/* Found multiple content-length field */
return NGHTTP2_ERR_HTTP_HEADER;
}
if (!lstrieq("0", nv->value->base, nv->value->len)) {
return NGHTTP2_ERR_HTTP_HEADER;
}
stream->content_length = 0;
return NGHTTP2_ERR_REMOVE_HTTP_HEADER;
}
if (stream->status_code / 100 == 1 ||
(stream->status_code == 200 &&
(stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT))) {
return NGHTTP2_ERR_HTTP_HEADER;
}
if (stream->content_length != -1) {
return NGHTTP2_ERR_HTTP_HEADER;
}

View File

@@ -46,7 +46,13 @@ typedef enum {
* Invalid HTTP header field was received but it can be treated as
* if it was not received because of compatibility reasons.
*/
NGHTTP2_ERR_IGN_HTTP_HEADER = -105
NGHTTP2_ERR_IGN_HTTP_HEADER = -105,
/*
* Invalid HTTP header field was received, and it is ignored.
* Unlike NGHTTP2_ERR_IGN_HTTP_HEADER, this does not invoke
* nghttp2_on_invalid_header_callback.
*/
NGHTTP2_ERR_REMOVE_HTTP_HEADER = -106
} nghttp2_internal_error;
#endif /* NGHTTP2_INT_H */

View File

@@ -28,6 +28,7 @@
#include <assert.h>
#include "nghttp2_mem.h"
#include "nghttp2_helper.h"
int nghttp2_rcbuf_new(nghttp2_rcbuf **rcbuf_ptr, size_t size,
nghttp2_mem *mem) {
@@ -58,10 +59,8 @@ int nghttp2_rcbuf_new2(nghttp2_rcbuf **rcbuf_ptr, const uint8_t *src,
return rv;
}
memcpy((*rcbuf_ptr)->base, src, srclen);
(*rcbuf_ptr)->len = srclen;
(*rcbuf_ptr)->base[srclen] = '\0';
*nghttp2_cpymem((*rcbuf_ptr)->base, src, srclen) = '\0';
return 0;
}

View File

@@ -1051,17 +1051,24 @@ nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session,
flags |= NGHTTP2_STREAM_FLAG_PUSH;
}
nghttp2_stream_init(stream, stream_id, flags, initial_state, pri_spec->weight,
(int32_t)session->remote_settings.initial_window_size,
(int32_t)session->local_settings.initial_window_size,
stream_user_data, mem);
if (stream_alloc) {
nghttp2_stream_init(stream, stream_id, flags, initial_state,
pri_spec->weight,
(int32_t)session->remote_settings.initial_window_size,
(int32_t)session->local_settings.initial_window_size,
stream_user_data, mem);
rv = nghttp2_map_insert(&session->streams, &stream->map_entry);
if (rv != 0) {
nghttp2_stream_free(stream);
nghttp2_mem_free(mem, stream);
return NULL;
}
} else {
stream->flags = flags;
stream->state = initial_state;
stream->weight = pri_spec->weight;
stream->stream_user_data = stream_user_data;
}
switch (initial_state) {

View File

@@ -365,7 +365,7 @@ int32_t nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags _U_,
return promised_stream_id;
}
int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags,
int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags _U_,
int32_t stream_id,
int32_t window_size_increment) {
int rv;
@@ -373,7 +373,6 @@ int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags,
if (window_size_increment == 0) {
return 0;
}
flags = 0;
if (stream_id == 0) {
rv = nghttp2_adjust_local_window_size(
&session->local_window_size, &session->recv_window_size,
@@ -404,14 +403,14 @@ int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags,
nghttp2_max(0, stream->consumed_size - window_size_increment);
}
return nghttp2_session_add_window_update(session, flags, stream_id,
return nghttp2_session_add_window_update(session, 0, stream_id,
window_size_increment);
}
return 0;
}
int nghttp2_session_set_local_window_size(nghttp2_session *session,
uint8_t flags, int32_t stream_id,
uint8_t flags _U_, int32_t stream_id,
int32_t window_size) {
int32_t window_size_increment;
nghttp2_stream *stream;
@@ -421,8 +420,6 @@ int nghttp2_session_set_local_window_size(nghttp2_session *session,
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
flags = 0;
if (stream_id == 0) {
window_size_increment = window_size - session->local_window_size;
@@ -472,7 +469,7 @@ int nghttp2_session_set_local_window_size(nghttp2_session *session,
}
if (window_size_increment > 0) {
return nghttp2_session_add_window_update(session, flags, stream_id,
return nghttp2_session_add_window_update(session, 0, stream_id,
window_size_increment);
}

40
lib/version.rc.in Normal file
View File

@@ -0,0 +1,40 @@
#include <winver.h>
VS_VERSION_INFO VERSIONINFO
FILEVERSION @PROJECT_VERSION_MAJOR@, @PROJECT_VERSION_MINOR@, @PROJECT_VERSION_PATCH@, 0
PRODUCTVERSION @PROJECT_VERSION_MAJOR@, @PROJECT_VERSION_MINOR@, @PROJECT_VERSION_PATCH@, 0
FILEFLAGSMASK 0x3fL
FILEOS 0x40004L
FILETYPE 0x2L
FILESUBTYPE 0x0L
#ifdef _DEBUG
#define VER_STR "@PROJECT_VERSION@.0 (MSVC debug)"
#define DBG "d"
FILEFLAGS 0x1L
#else
#define VER_STR "@PROJECT_VERSION@.0 (MSVC release)"
#define DBG ""
FILEFLAGS 0x0L
#endif
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "https://nghttp2.org/"
VALUE "FileDescription", "nghttp2; HTTP/2 C library"
VALUE "FileVersion", VER_STR
VALUE "InternalName", "nghttp2" DBG
VALUE "LegalCopyright", "The MIT License"
VALUE "LegalTrademarks", ""
VALUE "OriginalFilename", "nghttp2" DBG ".dll"
VALUE "ProductName", "NGHTTP2."
VALUE "ProductVersion", VER_STR
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END

View File

@@ -1,104 +0,0 @@
# ===========================================================================
# http://www.gnu.org/software/autoconf-archive/ax_have_epoll.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_HAVE_EPOLL([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
# AX_HAVE_EPOLL_PWAIT([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
#
# DESCRIPTION
#
# This macro determines whether the system supports the epoll I/O event
# interface. A neat usage example would be:
#
# AX_HAVE_EPOLL(
# [AX_CONFIG_FEATURE_ENABLE(epoll)],
# [AX_CONFIG_FEATURE_DISABLE(epoll)])
# AX_CONFIG_FEATURE(
# [epoll], [This platform supports epoll(7)],
# [HAVE_EPOLL], [This platform supports epoll(7).])
#
# The epoll interface was added to the Linux kernel in version 2.5.45, and
# the macro verifies that a kernel newer than this is installed. This
# check is somewhat unreliable if <linux/version.h> doesn't match the
# running kernel, but it is necessary regardless, because glibc comes with
# stubs for the epoll_create(), epoll_wait(), etc. that allow programs to
# compile and link even if the kernel is too old; the problem would then
# be detected only at runtime.
#
# Linux kernel version 2.6.19 adds the epoll_pwait() call in addition to
# epoll_wait(). The availability of that function can be tested with the
# second macro. Generally speaking, it is safe to assume that
# AX_HAVE_EPOLL would succeed if AX_HAVE_EPOLL_PWAIT has, but not the
# other way round.
#
# LICENSE
#
# Copyright (c) 2008 Peter Simons <simons@cryp.to>
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice
# and this notice are preserved. This file is offered as-is, without any
# warranty.
#serial 10
AC_DEFUN([AX_HAVE_EPOLL], [dnl
ax_have_epoll_cppflags="${CPPFLAGS}"
AC_CHECK_HEADER([linux/version.h], [CPPFLAGS="${CPPFLAGS} -DHAVE_LINUX_VERSION_H"])
AC_MSG_CHECKING([for Linux epoll(7) interface])
AC_CACHE_VAL([ax_cv_have_epoll], [dnl
AC_LINK_IFELSE([dnl
AC_LANG_PROGRAM([dnl
#include <sys/epoll.h>
#ifdef HAVE_LINUX_VERSION_H
# include <linux/version.h>
# if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,45)
# error linux kernel version is too old to have epoll
# endif
#endif
], [dnl
int fd, rc;
struct epoll_event ev;
fd = epoll_create(128);
rc = epoll_wait(fd, &ev, 1, 0);])],
[ax_cv_have_epoll=yes],
[ax_cv_have_epoll=no])])
CPPFLAGS="${ax_have_epoll_cppflags}"
AS_IF([test "${ax_cv_have_epoll}" = "yes"],
[AC_MSG_RESULT([yes])
$1],[AC_MSG_RESULT([no])
$2])
])dnl
AC_DEFUN([AX_HAVE_EPOLL_PWAIT], [dnl
ax_have_epoll_cppflags="${CPPFLAGS}"
AC_CHECK_HEADER([linux/version.h],
[CPPFLAGS="${CPPFLAGS} -DHAVE_LINUX_VERSION_H"])
AC_MSG_CHECKING([for Linux epoll(7) interface with signals extension])
AC_CACHE_VAL([ax_cv_have_epoll_pwait], [dnl
AC_LINK_IFELSE([dnl
AC_LANG_PROGRAM([dnl
#ifdef HAVE_LINUX_VERSION_H
# include <linux/version.h>
# if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
# error linux kernel version is too old to have epoll_pwait
# endif
#endif
#include <sys/epoll.h>
#include <signal.h>
], [dnl
int fd, rc;
struct epoll_event ev;
fd = epoll_create(128);
rc = epoll_wait(fd, &ev, 1, 0);
rc = epoll_pwait(fd, &ev, 1, 0, (sigset_t const *)(0));])],
[ax_cv_have_epoll_pwait=yes],
[ax_cv_have_epoll_pwait=no])])
CPPFLAGS="${ax_have_epoll_cppflags}"
AS_IF([test "${ax_cv_have_epoll_pwait}" = "yes"],
[AC_MSG_RESULT([yes])
$1],[AC_MSG_RESULT([no])
$2])
])dnl

View File

@@ -146,7 +146,7 @@ def send_and_receive_ocsp(respder_fn, cmd, cert_fn, issuer_fn, ocsp_uri,
'-noverify', '-respout', respder_fn
]
ver = openssl_version.lower()
if ver.startswith('openssl 1.') or ver.startswith('libressl '):
if ver.startswith('openssl 1.0.') or ver.startswith('libressl '):
args.extend(['-header', 'Host', ocsp_host])
resp = run_openssl(args, allow_tempfail=True)
return resp.decode('utf-8')

View File

@@ -19,6 +19,7 @@ include_directories(
${LIBXML2_INCLUDE_DIRS}
${LIBEV_INCLUDE_DIRS}
${OPENSSL_INCLUDE_DIRS}
${LIBCARES_INCLUDE_DIRS}
${JANSSON_INCLUDE_DIRS}
${ZLIB_INCLUDE_DIRS}
)
@@ -31,6 +32,7 @@ link_libraries(
${LIBXML2_LIBRARIES}
${LIBEV_LIBRARIES}
${OPENSSL_LIBRARIES}
${LIBCARES_LIBRARIES}
${JANSSON_LIBRARIES}
${ZLIB_LIBRARIES}
${APP_LIBRARIES}
@@ -112,6 +114,9 @@ if(ENABLE_APP)
shrpx_api_downstream_connection.cc
shrpx_health_monitor_downstream_connection.cc
shrpx_exec.cc
shrpx_dns_resolver.cc
shrpx_dual_dns_resolver.cc
shrpx_dns_tracker.cc
xsi_strerror.c
)
if(HAVE_SPDYLAY)
@@ -214,7 +219,6 @@ if(ENABLE_ASIO_LIB)
ssl.cc
timegm.c
asio_common.cc
asio_io_service_pool.cc
asio_server_http2.cc
asio_server_http2_impl.cc
asio_server.cc

View File

@@ -55,6 +55,21 @@ StringRef get_attr(const xmlChar **attrs, const StringRef &name) {
}
} // namespace
namespace {
ResourceType
get_resource_type_for_preload_as(const StringRef &attribute_value) {
if (util::strieq_l("image", attribute_value)) {
return REQ_IMG;
} else if (util::strieq_l("style", attribute_value)) {
return REQ_CSS;
} else if (util::strieq_l("script", attribute_value)) {
return REQ_UNBLOCK_JS;
} else {
return REQ_OTHERS;
}
}
} // namespace
namespace {
void add_link(ParserData *parser_data, const StringRef &uri,
ResourceType res_type) {
@@ -88,6 +103,13 @@ void start_element_func(void *user_data, const xmlChar *src_name,
add_link(parser_data, href_attr, REQ_OTHERS);
} else if (util::strieq_l("stylesheet", rel_attr)) {
add_link(parser_data, href_attr, REQ_CSS);
} else if (util::strieq_l("preload", rel_attr)) {
auto as_attr = get_attr(attrs, StringRef::from_lit("as"));
if (as_attr.empty()) {
return;
}
add_link(parser_data, href_attr,
get_resource_type_for_preload_as(as_attr));
}
} else if (util::strieq_l("img", name)) {
auto src_attr = get_attr(attrs, StringRef::from_lit("src"));

View File

@@ -557,7 +557,7 @@ Http2Handler::~Http2Handler() {
on_session_closed(this, session_id_);
nghttp2_session_del(session_);
if (ssl_) {
SSL_set_shutdown(ssl_, SSL_RECEIVED_SHUTDOWN);
SSL_set_shutdown(ssl_, SSL_get_shutdown(ssl_) | SSL_RECEIVED_SHUTDOWN);
ERR_clear_error();
SSL_shutdown(ssl_);
}
@@ -1921,13 +1921,18 @@ namespace {
FileEntry make_status_body(int status, uint16_t port) {
BlockAllocator balloc(1024, 1024);
auto status_string = http2::get_status_string(balloc, status);
auto status_string = http2::stringify_status(balloc, status);
auto reason_pharase = http2::get_reason_phrase(status);
std::string body;
body = "<html><head><title>";
body += status_string;
body += ' ';
body += reason_pharase;
body += "</title></head><body><h1>";
body += status_string;
body += ' ';
body += reason_pharase;
body += "</h1><hr><address>";
body += NGHTTPD_SERVER;
body += " at port ";

View File

@@ -41,9 +41,10 @@ AM_CPPFLAGS = \
-I$(top_srcdir)/src/includes \
-I$(top_srcdir)/third-party \
@LIBSPDYLAY_CFLAGS@ \
@XML_CPPFLAGS@ \
@LIBXML2_CFLAGS@ \
@LIBEV_CFLAGS@ \
@OPENSSL_CFLAGS@ \
@LIBCARES_CFLAGS@ \
@JANSSON_CFLAGS@ \
@ZLIB_CFLAGS@ \
@DEFS@
@@ -52,9 +53,10 @@ LDADD = $(top_builddir)/lib/libnghttp2.la \
$(top_builddir)/third-party/libhttp-parser.la \
@JEMALLOC_LIBS@ \
@LIBSPDYLAY_LIBS@ \
@XML_LIBS@ \
@LIBXML2_LIBS@ \
@LIBEV_LIBS@ \
@OPENSSL_LIBS@ \
@LIBCARES_LIBS@ \
@JANSSON_LIBS@ \
@ZLIB_LIBS@ \
@APPLDFLAGS@
@@ -139,6 +141,9 @@ NGHTTPX_SRCS = \
shrpx_health_monitor_downstream_connection.cc \
shrpx_health_monitor_downstream_connection.h \
shrpx_exec.cc shrpx_exec.h \
shrpx_dns_resolver.cc shrpx_dns_resolver.h \
shrpx_dual_dns_resolver.cc shrpx_dual_dns_resolver.h \
shrpx_dns_tracker.cc shrpx_dns_tracker.h \
buffer.h memchunk.h template.h allocator.h \
xsi_strerror.c xsi_strerror.h
@@ -238,7 +243,6 @@ libnghttp2_asio_la_SOURCES = \
ssl_compat.h \
timegm.c timegm.h \
asio_common.cc asio_common.h \
asio_io_service_pool.cc asio_io_service_pool.h \
asio_server_http2.cc \
asio_server_http2_impl.cc asio_server_http2_impl.h \
asio_server.cc asio_server.h \

View File

@@ -69,17 +69,17 @@ void session_impl::start_resolve(const std::string &host,
const std::string &service) {
deadline_.expires_from_now(connect_timeout_);
auto self = this->shared_from_this();
auto self = shared_from_this();
resolver_.async_resolve({host, service},
[this, self](const boost::system::error_code &ec,
[self](const boost::system::error_code &ec,
tcp::resolver::iterator endpoint_it) {
if (ec) {
not_connected(ec);
self->not_connected(ec);
return;
}
start_connect(endpoint_it);
self->start_connect(endpoint_it);
});
deadline_.async_wait(std::bind(&session_impl::handle_deadline, self));
@@ -597,38 +597,38 @@ void session_impl::do_read() {
auto self = this->shared_from_this();
read_socket([this, self](const boost::system::error_code &ec,
read_socket([self](const boost::system::error_code &ec,
std::size_t bytes_transferred) {
if (ec) {
if (!should_stop()) {
call_error_cb(ec);
if (!self->should_stop()) {
self->call_error_cb(ec);
}
stop();
self->stop();
return;
}
{
callback_guard cg(*this);
callback_guard cg(*self);
auto rv =
nghttp2_session_mem_recv(session_, rb_.data(), bytes_transferred);
nghttp2_session_mem_recv(self->session_, self->rb_.data(), bytes_transferred);
if (rv != static_cast<ssize_t>(bytes_transferred)) {
call_error_cb(make_error_code(
self->call_error_cb(make_error_code(
static_cast<nghttp2_error>(rv < 0 ? rv : NGHTTP2_ERR_PROTO)));
stop();
self->stop();
return;
}
}
do_write();
self->do_write();
if (should_stop()) {
stop();
if (self->should_stop()) {
self->stop();
return;
}
do_read();
self->do_read();
});
}
@@ -695,17 +695,17 @@ void session_impl::do_write() {
auto self = this->shared_from_this();
write_socket(
[this, self](const boost::system::error_code &ec, std::size_t n) {
[self](const boost::system::error_code &ec, std::size_t n) {
if (ec) {
call_error_cb(ec);
stop();
self->call_error_cb(ec);
self->stop();
return;
}
wblen_ = 0;
writing_ = false;
self->wblen_ = 0;
self->writing_ = false;
do_write();
self->do_write();
});
}

View File

@@ -37,19 +37,20 @@ session_tcp_impl::session_tcp_impl(
session_tcp_impl::~session_tcp_impl() {}
void session_tcp_impl::start_connect(tcp::resolver::iterator endpoint_it) {
auto self = shared_from_this();
boost::asio::async_connect(socket_, endpoint_it,
[this](const boost::system::error_code &ec,
[self](const boost::system::error_code &ec,
tcp::resolver::iterator endpoint_it) {
if (stopped()) {
if (self->stopped()) {
return;
}
if (ec) {
not_connected(ec);
self->not_connected(ec);
return;
}
connected(endpoint_it);
self->connected(endpoint_it);
});
}

View File

@@ -43,37 +43,38 @@ session_tls_impl::session_tls_impl(
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, [this](const boost::system::error_code &ec,
socket(), endpoint_it, [self](const boost::system::error_code &ec,
tcp::resolver::iterator endpoint_it) {
if (stopped()) {
if (self->stopped()) {
return;
}
if (ec) {
not_connected(ec);
self->not_connected(ec);
return;
}
socket_.async_handshake(
self->socket_.async_handshake(
boost::asio::ssl::stream_base::client,
[this, endpoint_it](const boost::system::error_code &ec) {
if (stopped()) {
[self, endpoint_it](const boost::system::error_code &ec) {
if (self->stopped()) {
return;
}
if (ec) {
not_connected(ec);
self->not_connected(ec);
return;
}
if (!tls_h2_negotiated(socket_)) {
not_connected(make_error_code(
if (!tls_h2_negotiated(self->socket_)) {
self->not_connected(make_error_code(
NGHTTP2_ASIO_ERR_TLS_NO_APP_PROTO_NEGOTIATED));
return;
}
connected(endpoint_it);
self->connected(endpoint_it);
});
});
}

View File

@@ -1,102 +0,0 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2014 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
// We wrote this code based on the original code which has the
// following license:
//
// io_service_pool.cpp
// ~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "asio_io_service_pool.h"
namespace nghttp2 {
namespace asio_http2 {
io_service_pool::io_service_pool(std::size_t pool_size) : next_io_service_(0) {
if (pool_size == 0) {
throw std::runtime_error("io_service_pool size is 0");
}
// Give all the io_services work to do so that their run() functions will not
// exit until they are explicitly stopped.
for (std::size_t i = 0; i < pool_size; ++i) {
auto io_service = std::make_shared<boost::asio::io_service>();
auto work = std::make_shared<boost::asio::io_service::work>(*io_service);
io_services_.push_back(io_service);
work_.push_back(work);
}
}
void io_service_pool::run(bool asynchronous) {
// Create a pool of threads to run all of the io_services.
for (std::size_t i = 0; i < io_services_.size(); ++i) {
futures_.push_back(std::async(std::launch::async,
(size_t(boost::asio::io_service::*)(void)) &
boost::asio::io_service::run,
io_services_[i]));
}
if (!asynchronous) {
join();
}
}
void io_service_pool::join() {
// Wait for all threads in the pool to exit.
for (auto &fut : futures_) {
fut.get();
}
}
void io_service_pool::stop() {
// Explicitly stop all io_services.
for (auto &iosv : io_services_) {
iosv->stop();
}
}
boost::asio::io_service &io_service_pool::get_io_service() {
// Use a round-robin scheme to choose the next io_service to use.
auto &io_service = *io_services_[next_io_service_];
++next_io_service_;
if (next_io_service_ == io_services_.size()) {
next_io_service_ = 0;
}
return io_service;
}
const std::vector<std::shared_ptr<boost::asio::io_service>> &
io_service_pool::io_services() const {
return io_services_;
}
} // namespace asio_http2
} // namespace nghttp2

View File

@@ -1,95 +0,0 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2014 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
// We wrote this code based on the original code which has the
// following license:
//
// io_service_pool.hpp
// ~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_IO_SERVICE_POOL_H
#define ASIO_IO_SERVICE_POOL_H
#include "nghttp2_config.h"
#include <vector>
#include <memory>
#include <future>
#include <boost/noncopyable.hpp>
#include <boost/thread.hpp>
#include <nghttp2/asio_http2.h>
namespace nghttp2 {
namespace asio_http2 {
/// A pool of io_service objects.
class io_service_pool : private boost::noncopyable {
public:
/// Construct the io_service pool.
explicit io_service_pool(std::size_t pool_size);
/// Run all io_service objects in the pool.
void run(bool asynchronous = false);
/// Stop all io_service objects in the pool.
void stop();
/// Join on all io_service objects in the pool.
void join();
/// Get an io_service to use.
boost::asio::io_service &get_io_service();
/// Get access to all io_service objects.
const std::vector<std::shared_ptr<boost::asio::io_service>> &
io_services() const;
private:
/// The pool of io_services.
std::vector<std::shared_ptr<boost::asio::io_service>> io_services_;
/// The work that keeps the io_services running.
std::vector<std::shared_ptr<boost::asio::io_service::work>> work_;
/// The next io_service to use for a connection.
std::size_t next_io_service_;
/// Futures to all the io_service objects
std::vector<std::future<std::size_t>> futures_;
};
} // namespace asio_http2
} // namespace nghttp2
#endif // ASIO_IO_SERVICE_POOL_H

View File

@@ -44,11 +44,11 @@ namespace nghttp2 {
namespace asio_http2 {
namespace server {
server::server(std::size_t io_service_pool_size,
const boost::posix_time::time_duration &tls_handshake_timeout,
server::server(boost::asio::io_service &service,
const boost::posix_time::time_duration &tls_handshake_timeout,
const boost::posix_time::time_duration &read_timeout)
: io_service_pool_(io_service_pool_size),
tls_handshake_timeout_(tls_handshake_timeout),
: service_(service),
tls_handshake_timeout_(tls_handshake_timeout),
read_timeout_(read_timeout) {}
boost::system::error_code
@@ -70,8 +70,6 @@ server::listen_and_serve(boost::system::error_code &ec,
}
}
io_service_pool_.run(asynchronous);
return ec;
}
@@ -81,7 +79,7 @@ boost::system::error_code server::bind_and_listen(boost::system::error_code &ec,
int backlog) {
// Open the acceptor with the option to reuse the address (i.e.
// SO_REUSEADDR).
tcp::resolver resolver(io_service_pool_.get_io_service());
tcp::resolver resolver(service_);
tcp::resolver::query query(address, port);
auto it = resolver.resolve(query, ec);
if (ec) {
@@ -90,7 +88,7 @@ boost::system::error_code server::bind_and_listen(boost::system::error_code &ec,
for (; it != tcp::resolver::iterator(); ++it) {
tcp::endpoint endpoint = *it;
auto acceptor = tcp::acceptor(io_service_pool_.get_io_service());
auto acceptor = tcp::acceptor(service_);
if (acceptor.open(endpoint.protocol(), ec)) {
continue;
@@ -126,7 +124,7 @@ void server::start_accept(boost::asio::ssl::context &tls_context,
tcp::acceptor &acceptor, serve_mux &mux) {
auto new_connection = std::make_shared<connection<ssl_socket>>(
mux, tls_handshake_timeout_, read_timeout_,
io_service_pool_.get_io_service(), tls_context);
service_, tls_context);
acceptor.async_accept(
new_connection->socket().lowest_layer(),
@@ -159,8 +157,7 @@ void server::start_accept(boost::asio::ssl::context &tls_context,
void server::start_accept(tcp::acceptor &acceptor, serve_mux &mux) {
auto new_connection = std::make_shared<connection<tcp::socket>>(
mux, tls_handshake_timeout_, read_timeout_,
io_service_pool_.get_io_service());
mux, tls_handshake_timeout_, read_timeout_, service_);
acceptor.async_accept(
new_connection->socket(), [this, &acceptor, &mux, new_connection](
@@ -170,25 +167,18 @@ void server::start_accept(tcp::acceptor &acceptor, serve_mux &mux) {
new_connection->start_read_deadline();
new_connection->start();
}
start_accept(acceptor, mux);
if (acceptor.is_open()) {
start_accept(acceptor, mux);
}
});
}
void server::stop() {
io_service_pool_.stop();
for (auto &acceptor : acceptors_) {
acceptor.close();
}
}
void server::join() { io_service_pool_.join(); }
const std::vector<std::shared_ptr<boost::asio::io_service>> &
server::io_services() const {
return io_service_pool_.io_services();
}
} // namespace server
} // namespace asio_http2
} // namespace nghttp2

View File

@@ -44,11 +44,10 @@
#include <memory>
#include <boost/noncopyable.hpp>
#include <boost/asio/io_service.hpp>
#include <nghttp2/asio_http2_server.h>
#include "asio_io_service_pool.h"
namespace nghttp2 {
namespace asio_http2 {
@@ -63,7 +62,7 @@ using ssl_socket = boost::asio::ssl::stream<tcp::socket>;
class server : private boost::noncopyable {
public:
explicit server(std::size_t io_service_pool_size,
explicit server(boost::asio::io_service &service,
const boost::posix_time::time_duration &tls_handshake_timeout,
const boost::posix_time::time_duration &read_timeout);
@@ -91,10 +90,7 @@ private:
const std::string &address,
const std::string &port,
int backlog);
/// The pool of io_service objects used to perform asynchronous
/// operations.
io_service_pool io_service_pool_;
boost::asio::io_service &service_;
/// Acceptor used to listen for incoming connections.
std::vector<tcp::acceptor> acceptors_;

View File

@@ -36,7 +36,7 @@ namespace asio_http2 {
namespace server {
http2::http2() : impl_(make_unique<http2_impl>()) {}
http2::http2(boost::asio::io_service &service) : impl_(make_unique<http2_impl>(service)) {}
http2::~http2() {}
@@ -65,8 +65,6 @@ boost::system::error_code http2::listen_and_serve(
return impl_->listen_and_serve(ec, &tls_context, address, port, asynchronous);
}
void http2::num_threads(size_t num_threads) { impl_->num_threads(num_threads); }
void http2::backlog(int backlog) { impl_->backlog(backlog); }
void http2::tls_handshake_timeout(const boost::posix_time::time_duration &t) {
@@ -83,13 +81,6 @@ bool http2::handle(std::string pattern, request_cb cb) {
void http2::stop() { impl_->stop(); }
void http2::join() { return impl_->join(); }
const std::vector<std::shared_ptr<boost::asio::io_service>> &
http2::io_services() const {
return impl_->io_services();
}
} // namespace server
} // namespace asio_http2

View File

@@ -249,7 +249,15 @@ http2_handler::http2_handler(boost::asio::io_service &io_service,
tstamp_cached_(time(nullptr)),
formatted_date_(util::http_date(tstamp_cached_)) {}
http2_handler::~http2_handler() { nghttp2_session_del(session_); }
http2_handler::~http2_handler() {
for (auto &p : streams_) {
auto &strm = p.second;
strm->response().impl().call_on_close(NGHTTP2_INTERNAL_ERROR);
close_stream(strm->get_stream_id());
}
nghttp2_session_del(session_);
}
const std::string &http2_handler::http_date() {
auto t = time(nullptr);

View File

@@ -37,8 +37,8 @@ namespace asio_http2 {
namespace server {
http2_impl::http2_impl()
: num_threads_(1),
http2_impl::http2_impl(boost::asio::io_service &service)
: service_(service),
backlog_(-1),
tls_handshake_timeout_(boost::posix_time::seconds(60)),
read_timeout_(boost::posix_time::seconds(60)) {}
@@ -47,13 +47,11 @@ boost::system::error_code http2_impl::listen_and_serve(
boost::system::error_code &ec, boost::asio::ssl::context *tls_context,
const std::string &address, const std::string &port, bool asynchronous) {
server_.reset(
new server(num_threads_, tls_handshake_timeout_, read_timeout_));
new server(service_, tls_handshake_timeout_, read_timeout_));
return server_->listen_and_serve(ec, tls_context, address, port, backlog_,
mux_, asynchronous);
}
void http2_impl::num_threads(size_t num_threads) { num_threads_ = num_threads; }
void http2_impl::backlog(int backlog) { backlog_ = backlog; }
void http2_impl::tls_handshake_timeout(
@@ -71,13 +69,6 @@ bool http2_impl::handle(std::string pattern, request_cb cb) {
void http2_impl::stop() { return server_->stop(); }
void http2_impl::join() { return server_->join(); }
const std::vector<std::shared_ptr<boost::asio::io_service>> &
http2_impl::io_services() const {
return server_->io_services();
}
} // namespace server
} // namespace asio_http2

View File

@@ -41,7 +41,7 @@ class server;
class http2_impl {
public:
http2_impl();
http2_impl(boost::asio::io_service &service);
boost::system::error_code listen_and_serve(
boost::system::error_code &ec, boost::asio::ssl::context *tls_context,
const std::string &address, const std::string &port, bool asynchronous);
@@ -51,13 +51,10 @@ public:
void read_timeout(const boost::posix_time::time_duration &t);
bool handle(std::string pattern, request_cb cb);
void stop();
void join();
const std::vector<std::shared_ptr<boost::asio::io_service>> &
io_services() const;
private:
std::unique_ptr<server> server_;
std::size_t num_threads_;
boost::asio::io_service &service_;
int backlog_;
serve_mux mux_;
boost::posix_time::time_duration tls_handshake_timeout_;

View File

@@ -36,11 +36,16 @@ std::string create_html(int status_code) {
BlockAllocator balloc(1024, 1024);
std::string res;
res.reserve(512);
auto status = ::nghttp2::http2::get_status_string(balloc, status_code);
auto status_string = ::nghttp2::http2::stringify_status(balloc, status_code);
auto reason_phrase = ::nghttp2::http2::get_reason_phrase(status_code);
res += R"(<!DOCTYPE html><html lang="en"><title>)";
res += status;
res += status_string;
res += ' ';
res += reason_phrase;
res += "</title><body><h1>";
res += status;
res += status_string;
res += ' ';
res += reason_phrase;
res += "</h1></body></html>";
return res;
}

View File

@@ -373,7 +373,7 @@ OPTIONS:
<< std::endl;
}
static struct option long_options[] = {
constexpr static struct option long_options[] = {
{"http1text", no_argument, nullptr, 't'},
{"table-size", required_argument, nullptr, 's'},
{"deflate-table-size", required_argument, nullptr, 'S'},

View File

@@ -79,7 +79,8 @@ bool recorded(const std::chrono::steady_clock::time_point &t) {
} // namespace
Config::Config()
: data_length(-1),
: ciphers(ssl::DEFAULT_CIPHER_LIST),
data_length(-1),
addrs(nullptr),
nreqs(1),
nclients(1),
@@ -268,9 +269,7 @@ void conn_timeout_cb(EV_P_ ev_timer *w, int revents) {
namespace {
bool check_stop_client_request_timeout(Client *client, ev_timer *w) {
auto nreq = client->req_todo - client->req_started;
if (nreq == 0 ||
if (client->req_left == 0 ||
client->streams.size() >= client->session->max_concurrent_streams()) {
// no more requests to make, stop timer
ev_timer_stop(client->worker->loop, w);
@@ -329,6 +328,8 @@ Client::Client(uint32_t id, Worker *worker, size_t req_todo)
reqidx(0),
state(CLIENT_IDLE),
req_todo(req_todo),
req_left(req_todo),
req_inflight(0),
req_started(0),
req_done(0),
id(id),
@@ -466,16 +467,13 @@ int Client::try_again_or_fail() {
if (new_connection_requested) {
new_connection_requested = false;
if (req_started < req_todo) {
if (req_left) {
// At the moment, we don't have a facility to re-start request
// already in in-flight. Make them fail.
auto req_abandoned = req_started - req_done;
worker->stats.req_failed += req_inflight;
worker->stats.req_error += req_inflight;
worker->stats.req_failed += req_abandoned;
worker->stats.req_error += req_abandoned;
worker->stats.req_done += req_abandoned;
req_done = req_started;
req_inflight = 0;
// Keep using current address
if (connect() == 0) {
@@ -509,7 +507,7 @@ void Client::disconnect() {
ev_io_stop(worker->loop, &wev);
ev_io_stop(worker->loop, &rev);
if (ssl) {
SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);
SSL_set_shutdown(ssl, SSL_get_shutdown(ssl) | SSL_RECEIVED_SHUTDOWN);
ERR_clear_error();
if (SSL_shutdown(ssl) != 1) {
@@ -527,16 +525,18 @@ void Client::disconnect() {
}
int Client::submit_request() {
++worker->stats.req_started;
if (session->submit_request() != 0) {
return -1;
}
++worker->stats.req_started;
--req_left;
++req_started;
++req_inflight;
// 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_started >= req_todo) {
if (worker->config->conn_active_timeout > 0. && req_left == 0) {
ev_timer_start(worker->loop, &conn_active_watcher);
}
@@ -544,40 +544,36 @@ int Client::submit_request() {
}
void Client::process_timedout_streams() {
for (auto &req_stat : worker->stats.req_stats) {
for (auto &p : streams) {
auto &req_stat = p.second.req_stat;
if (!req_stat.completed) {
req_stat.stream_close_time = std::chrono::steady_clock::now();
}
}
auto req_timed_out = req_todo - req_done;
worker->stats.req_timedout += req_timed_out;
worker->stats.req_timedout += req_inflight;
process_abandoned_streams();
}
void Client::process_abandoned_streams() {
auto req_abandoned = req_todo - req_done;
auto req_abandoned = req_inflight + req_left;
worker->stats.req_failed += req_abandoned;
worker->stats.req_error += req_abandoned;
worker->stats.req_done += req_abandoned;
req_done = req_todo;
req_inflight = 0;
req_left = 0;
}
void Client::process_request_failure() {
auto req_abandoned = req_todo - req_started;
worker->stats.req_failed += req_left;
worker->stats.req_error += req_left;
worker->stats.req_failed += req_abandoned;
worker->stats.req_error += req_abandoned;
worker->stats.req_done += req_abandoned;
req_left = 0;
req_done += req_abandoned;
if (req_done == req_todo) {
if (req_inflight == 0) {
terminate_session();
return;
}
}
@@ -595,7 +591,8 @@ void print_server_tmp_key(SSL *ssl) {
std::cout << "Server Temp Key: ";
switch (EVP_PKEY_id(key)) {
auto pkey_id = EVP_PKEY_id(key);
switch (pkey_id) {
case EVP_PKEY_RSA:
std::cout << "RSA " << EVP_PKEY_bits(key) << " bits" << std::endl;
break;
@@ -615,6 +612,10 @@ void print_server_tmp_key(SSL *ssl) {
<< std::endl;
break;
}
default:
std::cout << OBJ_nid2sn(pkey_id) << " " << EVP_PKEY_bits(key) << " bits"
<< std::endl;
break;
}
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
}
@@ -705,6 +706,9 @@ 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;
auto req_stat = get_req_stat(stream_id);
if (!req_stat) {
return;
@@ -735,22 +739,18 @@ void Client::on_stream_close(int32_t stream_id, bool success, bool final) {
}
++worker->stats.req_done;
++req_done;
worker->report_progress();
streams.erase(stream_id);
if (req_done == req_todo) {
if (req_left == 0 && req_inflight == 0) {
terminate_session();
return;
}
if (!config.timing_script && !final) {
if (req_started < req_todo) {
if (submit_request() != 0) {
process_request_failure();
}
return;
}
if (!config.timing_script && !final && req_left > 0 &&
submit_request() != 0) {
process_request_failure();
return;
}
}
@@ -865,8 +865,7 @@ int Client::connection_made() {
record_connect_time();
if (!config.timing_script) {
auto nreq =
std::min(req_todo - req_started, session->max_concurrent_streams());
auto nreq = std::min(req_left, session->max_concurrent_streams());
for (; nreq > 0; --nreq) {
if (submit_request() != 0) {
process_request_failure();
@@ -1697,6 +1696,8 @@ Options:
--ciphers=<SUITE>
Set allowed cipher list. The format of the string is
described in OpenSSL ciphers(1).
Default: )"
<< config.ciphers << R"(
-p, --no-tls-proto=<PROTOID>
Specify ALPN identifier of the protocol to be used when
accessing http URI without SSL/TLS.)";
@@ -1831,7 +1832,7 @@ int main(int argc, char **argv) {
bool nreqs_set_manually = false;
while (1) {
static int flag = 0;
static option long_options[] = {
constexpr static option long_options[] = {
{"requests", required_argument, nullptr, 'n'},
{"clients", required_argument, nullptr, 'c'},
{"data", required_argument, nullptr, 'd'},
@@ -2241,15 +2242,8 @@ 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);
const char *ciphers;
if (config.ciphers.empty()) {
ciphers = ssl::DEFAULT_CIPHER_LIST;
} else {
ciphers = config.ciphers.c_str();
}
if (SSL_CTX_set_cipher_list(ssl_ctx, ciphers) == 0) {
std::cerr << "SSL_CTX_set_cipher_list with " << ciphers
if (SSL_CTX_set_cipher_list(ssl_ctx, config.ciphers.c_str()) == 0) {
std::cerr << "SSL_CTX_set_cipher_list with " << config.ciphers
<< " failed: " << ERR_error_string(ERR_get_error(), nullptr)
<< std::endl;
exit(EXIT_FAILURE);

View File

@@ -292,6 +292,11 @@ struct Client {
ClientState state;
// The number of requests this client has to issue.
size_t req_todo;
// The number of requests left to issue
size_t req_left;
// The number of requests currently have started, but not abandoned
// or finished.
size_t req_inflight;
// The number of requests this client has issued so far.
size_t req_started;
// The number of requests this client has done so far.

View File

@@ -100,7 +100,7 @@ int htp_msg_completecb(http_parser *htp) {
http_parser_pause(htp, 1);
// Connection is going down. If we have still request to do,
// create new connection and keep on doing the job.
if (client->req_started < client->req_todo) {
if (client->req_left) {
client->try_new_connection();
}
@@ -146,7 +146,7 @@ int htp_body_cb(http_parser *htp, const char *data, size_t len) {
} // namespace
namespace {
http_parser_settings htp_hooks = {
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;

View File

@@ -113,7 +113,7 @@ ssize_t send_callback(spdylay_session *session, const uint8_t *data,
auto &wb = client->wb;
if (wb.rleft() >= BACKOFF_WRITE_BUFFER_THRES) {
return SPDYLAY_ERR_DEFERRED;
return SPDYLAY_ERR_WOULDBLOCK;
}
return wb.append(data, length);

View File

@@ -30,107 +30,107 @@ namespace nghttp2 {
namespace http2 {
StringRef get_status_string(BlockAllocator &balloc, unsigned int status_code) {
StringRef get_reason_phrase(unsigned int status_code) {
switch (status_code) {
case 100:
return StringRef::from_lit("100 Continue");
return StringRef::from_lit("Continue");
case 101:
return StringRef::from_lit("101 Switching Protocols");
return StringRef::from_lit("Switching Protocols");
case 200:
return StringRef::from_lit("200 OK");
return StringRef::from_lit("OK");
case 201:
return StringRef::from_lit("201 Created");
return StringRef::from_lit("Created");
case 202:
return StringRef::from_lit("202 Accepted");
return StringRef::from_lit("Accepted");
case 203:
return StringRef::from_lit("203 Non-Authoritative Information");
return StringRef::from_lit("Non-Authoritative Information");
case 204:
return StringRef::from_lit("204 No Content");
return StringRef::from_lit("No Content");
case 205:
return StringRef::from_lit("205 Reset Content");
return StringRef::from_lit("Reset Content");
case 206:
return StringRef::from_lit("206 Partial Content");
return StringRef::from_lit("Partial Content");
case 300:
return StringRef::from_lit("300 Multiple Choices");
return StringRef::from_lit("Multiple Choices");
case 301:
return StringRef::from_lit("301 Moved Permanently");
return StringRef::from_lit("Moved Permanently");
case 302:
return StringRef::from_lit("302 Found");
return StringRef::from_lit("Found");
case 303:
return StringRef::from_lit("303 See Other");
return StringRef::from_lit("See Other");
case 304:
return StringRef::from_lit("304 Not Modified");
return StringRef::from_lit("Not Modified");
case 305:
return StringRef::from_lit("305 Use Proxy");
// case 306: return StringRef::from_lit("306 (Unused)");
return StringRef::from_lit("Use Proxy");
// case 306: return StringRef::from_lit("(Unused)");
case 307:
return StringRef::from_lit("307 Temporary Redirect");
return StringRef::from_lit("Temporary Redirect");
case 308:
return StringRef::from_lit("308 Permanent Redirect");
return StringRef::from_lit("Permanent Redirect");
case 400:
return StringRef::from_lit("400 Bad Request");
return StringRef::from_lit("Bad Request");
case 401:
return StringRef::from_lit("401 Unauthorized");
return StringRef::from_lit("Unauthorized");
case 402:
return StringRef::from_lit("402 Payment Required");
return StringRef::from_lit("Payment Required");
case 403:
return StringRef::from_lit("403 Forbidden");
return StringRef::from_lit("Forbidden");
case 404:
return StringRef::from_lit("404 Not Found");
return StringRef::from_lit("Not Found");
case 405:
return StringRef::from_lit("405 Method Not Allowed");
return StringRef::from_lit("Method Not Allowed");
case 406:
return StringRef::from_lit("406 Not Acceptable");
return StringRef::from_lit("Not Acceptable");
case 407:
return StringRef::from_lit("407 Proxy Authentication Required");
return StringRef::from_lit("Proxy Authentication Required");
case 408:
return StringRef::from_lit("408 Request Timeout");
return StringRef::from_lit("Request Timeout");
case 409:
return StringRef::from_lit("409 Conflict");
return StringRef::from_lit("Conflict");
case 410:
return StringRef::from_lit("410 Gone");
return StringRef::from_lit("Gone");
case 411:
return StringRef::from_lit("411 Length Required");
return StringRef::from_lit("Length Required");
case 412:
return StringRef::from_lit("412 Precondition Failed");
return StringRef::from_lit("Precondition Failed");
case 413:
return StringRef::from_lit("413 Payload Too Large");
return StringRef::from_lit("Payload Too Large");
case 414:
return StringRef::from_lit("414 URI Too Long");
return StringRef::from_lit("URI Too Long");
case 415:
return StringRef::from_lit("415 Unsupported Media Type");
return StringRef::from_lit("Unsupported Media Type");
case 416:
return StringRef::from_lit("416 Requested Range Not Satisfiable");
return StringRef::from_lit("Requested Range Not Satisfiable");
case 417:
return StringRef::from_lit("417 Expectation Failed");
return StringRef::from_lit("Expectation Failed");
case 421:
return StringRef::from_lit("421 Misdirected Request");
return StringRef::from_lit("Misdirected Request");
case 426:
return StringRef::from_lit("426 Upgrade Required");
return StringRef::from_lit("Upgrade Required");
case 428:
return StringRef::from_lit("428 Precondition Required");
return StringRef::from_lit("Precondition Required");
case 429:
return StringRef::from_lit("429 Too Many Requests");
return StringRef::from_lit("Too Many Requests");
case 431:
return StringRef::from_lit("431 Request Header Fields Too Large");
return StringRef::from_lit("Request Header Fields Too Large");
case 451:
return StringRef::from_lit("451 Unavailable For Legal Reasons");
return StringRef::from_lit("Unavailable For Legal Reasons");
case 500:
return StringRef::from_lit("500 Internal Server Error");
return StringRef::from_lit("Internal Server Error");
case 501:
return StringRef::from_lit("501 Not Implemented");
return StringRef::from_lit("Not Implemented");
case 502:
return StringRef::from_lit("502 Bad Gateway");
return StringRef::from_lit("Bad Gateway");
case 503:
return StringRef::from_lit("503 Service Unavailable");
return StringRef::from_lit("Service Unavailable");
case 504:
return StringRef::from_lit("504 Gateway Timeout");
return StringRef::from_lit("Gateway Timeout");
case 505:
return StringRef::from_lit("505 HTTP Version Not Supported");
return StringRef::from_lit("HTTP Version Not Supported");
case 511:
return StringRef::from_lit("511 Network Authentication Required");
return StringRef::from_lit("Network Authentication Required");
default:
return util::make_string_ref_uint(balloc, status_code);
return StringRef{};
}
}
@@ -477,6 +477,11 @@ void dump_nv(FILE *out, const HeaderRefs &nva) {
fflush(out);
}
void erase_header(HeaderRef *hd) {
hd->name = StringRef{};
hd->token = -1;
}
StringRef rewrite_location_uri(BlockAllocator &balloc, const StringRef &uri,
const http_parser_url &u,
const StringRef &match_host,
@@ -1665,6 +1670,31 @@ StringRef copy_lower(BlockAllocator &balloc, const StringRef &src) {
return StringRef{iov.base, p};
}
bool contains_trailers(const StringRef &s) {
constexpr auto trailers = StringRef::from_lit("trailers");
for (auto p = std::begin(s), end = std::end(s);; ++p) {
p = std::find_if(p, end, [](char c) { return c != ' ' && c != '\t'; });
if (p == end || static_cast<size_t>(end - p) < trailers.size()) {
return false;
}
if (util::strieq(trailers, StringRef{p, p + trailers.size()})) {
// Make sure that there is no character other than white spaces
// before next "," or end of string.
p = std::find_if(p + trailers.size(), end,
[](char c) { return c != ' ' && c != '\t'; });
if (p == end || *p == ',') {
return true;
}
}
// Skip to next ",".
p = std::find_if(p, end, [](char c) { return c == ','; });
if (p == end) {
return false;
}
}
}
} // namespace http2
} // namespace nghttp2

View File

@@ -94,9 +94,9 @@ using HeaderRefs = std::vector<HeaderRef>;
namespace http2 {
// Returns string version of |status code| followed by reason
// string. (e.g., "404 Not Found").
StringRef get_status_string(BlockAllocator &balloc, unsigned int status_code);
// Returns reason-phrase for given |status code|. If there is no
// known reason-phrase for the given code, returns empty string.
StringRef get_reason_phrase(unsigned int status_code);
// Returns string version of |status_code|. (e.g., "404")
StringRef stringify_status(BlockAllocator &balloc, unsigned int status_code);
@@ -227,6 +227,9 @@ void dump_nv(FILE *out, const Headers &nva);
void dump_nv(FILE *out, const HeaderRefs &nva);
// Ereases header in |hd|.
void erase_header(HeaderRef *hd);
// Rewrites redirection URI which usually appears in location header
// field. The |uri| is the URI in the location header field. The |u|
// stores the result of parsed |uri|. The |request_authority| is the
@@ -384,6 +387,9 @@ int construct_push_component(BlockAllocator &balloc, StringRef &scheme,
// Copies |src| and return its lower-cased version.
StringRef copy_lower(BlockAllocator &balloc, const StringRef &src);
// Returns true if te header field value |s| contains "trailers".
bool contains_trailers(const StringRef &s);
} // namespace http2
} // namespace nghttp2

View File

@@ -962,4 +962,20 @@ void test_http2_construct_push_component(void) {
CU_ASSERT("/b/?q=a" == path);
}
void test_http2_contains_trailers(void) {
CU_ASSERT(!http2::contains_trailers(StringRef::from_lit("")));
CU_ASSERT(http2::contains_trailers(StringRef::from_lit("trailers")));
// Match must be case-insensitive.
CU_ASSERT(http2::contains_trailers(StringRef::from_lit("TRAILERS")));
CU_ASSERT(!http2::contains_trailers(StringRef::from_lit("trailer")));
CU_ASSERT(!http2::contains_trailers(StringRef::from_lit("trailers 3")));
CU_ASSERT(http2::contains_trailers(StringRef::from_lit("trailers,")));
CU_ASSERT(http2::contains_trailers(StringRef::from_lit("trailers,foo")));
CU_ASSERT(http2::contains_trailers(StringRef::from_lit("foo,trailers")));
CU_ASSERT(http2::contains_trailers(StringRef::from_lit("foo,trailers,bar")));
CU_ASSERT(
http2::contains_trailers(StringRef::from_lit("foo, trailers ,bar")));
CU_ASSERT(http2::contains_trailers(StringRef::from_lit(",trailers")));
}
} // namespace shrpx

View File

@@ -46,6 +46,7 @@ void test_http2_normalize_path(void);
void test_http2_rewrite_clean_path(void);
void test_http2_get_pure_path_component(void);
void test_http2_construct_push_component(void);
void test_http2_contains_trailers(void);
} // namespace shrpx

View File

@@ -132,7 +132,7 @@ class http2_impl;
class http2 {
public:
http2();
http2(boost::asio::io_service &service);
~http2();
http2(http2 &&other) noexcept;
@@ -190,10 +190,6 @@ public:
// equivalent .- and ..-free URL.
bool handle(std::string pattern, request_cb cb);
// Sets number of native threads to handle incoming HTTP request.
// It defaults to 1.
void num_threads(size_t num_threads);
// Sets the maximum length to which the queue of pending
// connections.
void backlog(int backlog);
@@ -207,13 +203,6 @@ public:
// Gracefully stop http2 server
void stop();
// Join on http2 server and wait for it to fully stop
void join();
// Get access to the io_service objects.
const std::vector<std::shared_ptr<boost::asio::io_service>> &
io_services() const;
private:
std::unique_ptr<http2_impl> impl_;
};

View File

@@ -251,7 +251,7 @@ OPTIONS:
;
}
static struct option long_options[] = {
constexpr static struct option long_options[] = {
{"dump-header-table", no_argument, nullptr, 'd'}, {nullptr, 0, nullptr, 0}};
int main(int argc, char **argv) {

View File

@@ -434,6 +434,102 @@ inline int limit_iovec(struct iovec *iov, int iovcnt, size_t max) {
return iovcnt;
}
// MemchunkBuffer is similar to Buffer, but it uses pooled Memchunk
// for its underlying buffer.
template <typename Memchunk> struct MemchunkBuffer {
MemchunkBuffer(Pool<Memchunk> *pool) : pool(pool), chunk(nullptr) {}
MemchunkBuffer(const MemchunkBuffer &) = delete;
MemchunkBuffer(MemchunkBuffer &&other) noexcept
: pool(other.pool), chunk(other.chunk) {
other.chunk = nullptr;
}
MemchunkBuffer &operator=(const MemchunkBuffer &) = delete;
MemchunkBuffer &operator=(MemchunkBuffer &&other) noexcept {
if (this == &other) {
return *this;
}
pool = other.pool;
chunk = other.chunk;
other.chunk = nullptr;
return *this;
}
~MemchunkBuffer() {
if (!pool || !chunk) {
return;
}
pool->recycle(chunk);
}
// Ensures that the underlying buffer is allocated.
void ensure_chunk() {
if (chunk) {
return;
}
chunk = pool->get();
}
// Releases the underlying buffer.
void release_chunk() {
if (!chunk) {
return;
}
pool->recycle(chunk);
chunk = nullptr;
}
// Returns true if the underlying buffer is allocated.
bool chunk_avail() const { return chunk != nullptr; }
// The functions below must be called after the underlying buffer is
// allocated (use ensure_chunk).
// MemchunkBuffer provides the same interface functions with Buffer.
// Since we has chunk as a member variable, pos and last are
// implemented as wrapper functions.
uint8_t *pos() const { return chunk->pos; }
uint8_t *last() const { return chunk->last; }
size_t rleft() const { return chunk->len(); }
size_t wleft() const { return chunk->left(); }
size_t write(const void *src, size_t count) {
count = std::min(count, wleft());
auto p = static_cast<const uint8_t *>(src);
chunk->last = std::copy_n(p, count, chunk->last);
return count;
}
size_t write(size_t count) {
count = std::min(count, wleft());
chunk->last += count;
return count;
}
size_t drain(size_t count) {
count = std::min(count, rleft());
chunk->pos += count;
return count;
}
size_t drain_reset(size_t count) {
count = std::min(count, rleft());
std::copy(chunk->pos + count, chunk->last, std::begin(chunk->buf));
chunk->last = std::begin(chunk->buf) + (chunk->last - (chunk->pos + count));
chunk->pos = std::begin(chunk->buf);
return count;
}
void reset() { chunk->reset(); }
uint8_t *begin() { return std::begin(chunk->buf); }
uint8_t &operator[](size_t n) { return chunk->buf[n]; }
const uint8_t &operator[](size_t n) const { return chunk->buf[n]; }
Pool<Memchunk> *pool;
Memchunk *chunk;
};
using DefaultMemchunkBuffer = MemchunkBuffer<Memchunk16K>;
} // namespace nghttp2
#endif // MEMCHUNK_H

View File

@@ -105,6 +105,7 @@ Config::Config()
window_bits(-1),
connection_window_bits(-1),
verbose(0),
port_override(0),
null_out(false),
remote_name(false),
get_assets(false),
@@ -156,7 +157,6 @@ Request::Request(const std::string &uri, const http_parser_url &u,
data_offset(0),
response_len(0),
inflater(nullptr),
html_parser(nullptr),
data_prd(data_prd),
header_buffer_size(0),
stream_id(-1),
@@ -167,10 +167,7 @@ Request::Request(const std::string &uri, const http_parser_url &u,
http2::init_hdidx(req_hdidx);
}
Request::~Request() {
nghttp2_gzip_inflate_del(inflater);
delete html_parser;
}
Request::~Request() { nghttp2_gzip_inflate_del(inflater); }
void Request::init_inflater() {
int rv;
@@ -178,7 +175,57 @@ void Request::init_inflater() {
assert(rv == 0);
}
void Request::init_html_parser() { html_parser = new HtmlParser(uri); }
StringRef Request::get_real_scheme() const {
return config.scheme_override.empty()
? util::get_uri_field(uri.c_str(), u, UF_SCHEMA)
: StringRef{config.scheme_override};
}
StringRef Request::get_real_host() const {
return config.host_override.empty()
? util::get_uri_field(uri.c_str(), u, UF_HOST)
: StringRef{config.host_override};
}
uint16_t Request::get_real_port() const {
auto scheme = get_real_scheme();
return config.host_override.empty()
? util::has_uri_field(u, UF_PORT) ? u.port
: scheme == "https" ? 443 : 80
: config.port_override == 0 ? scheme == "https" ? 443 : 80
: config.port_override;
}
void Request::init_html_parser() {
// We crawl HTML using overridden scheme, host, and port.
auto scheme = get_real_scheme();
auto host = get_real_host();
auto port = get_real_port();
auto ipv6_lit =
std::find(std::begin(host), std::end(host), ':') != std::end(host);
auto base_uri = scheme.str();
base_uri += "://";
if (ipv6_lit) {
base_uri += '[';
}
base_uri += host;
if (ipv6_lit) {
base_uri += ']';
}
if (!((scheme == "https" && port == 443) ||
(scheme == "http" && port == 80))) {
base_uri += ':';
base_uri += util::utos(port);
}
base_uri += util::get_uri_field(uri.c_str(), u, UF_PATH);
if (util::has_uri_field(u, UF_QUERY)) {
base_uri += '?';
base_uri += util::get_uri_field(uri.c_str(), u, UF_QUERY);
}
html_parser = make_unique<HtmlParser>(base_uri);
}
int Request::update_html_parser(const uint8_t *data, size_t len, int fin) {
if (!html_parser) {
@@ -371,7 +418,7 @@ int htp_msg_completecb(http_parser *htp) {
} // namespace
namespace {
http_parser_settings htp_hooks = {
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;
@@ -625,20 +672,11 @@ int HttpClient::initiate_connection() {
// If the user overrode the :authority or host header, use that
// value for the SNI extension
const char *host_string = nullptr;
auto i =
std::find_if(std::begin(config.headers), std::end(config.headers),
[](const Header &nv) {
return ":authority" == nv.name || "host" == nv.name;
});
if (i != std::end(config.headers)) {
host_string = (*i).value.c_str();
} else {
host_string = host.c_str();
}
const auto &host_string =
config.host_override.empty() ? host : config.host_override;
if (!util::numeric_host(host_string)) {
SSL_set_tlsext_host_name(ssl, host_string);
if (!util::numeric_host(host_string.c_str())) {
SSL_set_tlsext_host_name(ssl, host_string.c_str());
}
}
@@ -701,7 +739,7 @@ void HttpClient::disconnect() {
session = nullptr;
if (ssl) {
SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);
SSL_set_shutdown(ssl, SSL_get_shutdown(ssl) | SSL_RECEIVED_SHUTDOWN);
ERR_clear_error();
SSL_shutdown(ssl);
SSL_free(ssl);
@@ -1591,22 +1629,36 @@ void update_html_parser(HttpClient *client, Request *req, const uint8_t *data,
}
req->update_html_parser(data, len, fin);
auto scheme = req->get_real_scheme();
auto host = req->get_real_host();
auto port = req->get_real_port();
for (auto &p : req->html_parser->get_links()) {
auto uri = strip_fragment(p.first.c_str());
auto res_type = p.second;
http_parser_url u{};
if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) == 0 &&
util::fieldeq(uri.c_str(), u, req->uri.c_str(), req->u, UF_SCHEMA) &&
util::fieldeq(uri.c_str(), u, req->uri.c_str(), req->u, UF_HOST) &&
util::porteq(uri.c_str(), u, req->uri.c_str(), req->u)) {
// No POST data for assets
auto pri_spec = resolve_dep(res_type);
if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) {
continue;
}
if (client->add_request(uri, nullptr, 0, pri_spec, req->level + 1)) {
if (!util::fieldeq(uri.c_str(), u, UF_SCHEMA, scheme) ||
!util::fieldeq(uri.c_str(), u, UF_HOST, host)) {
continue;
}
submit_request(client, config.headers, client->reqvec.back().get());
}
auto link_port =
util::has_uri_field(u, UF_PORT) ? u.port : scheme == "https" ? 443 : 80;
if (port != link_port) {
continue;
}
// No POST data for assets
auto pri_spec = resolve_dep(res_type);
if (client->add_request(uri, nullptr, 0, pri_spec, req->level + 1)) {
submit_request(client, config.headers, client->reqvec.back().get());
}
}
req->html_parser->clear_links();
@@ -2168,24 +2220,6 @@ int client_select_next_proto_cb(SSL *ssl, unsigned char **out,
}
} // namespace
namespace {
// Recommended general purpose "Intermediate compatibility" cipher by
// mozilla.
//
// https://wiki.mozilla.org/Security/Server_Side_TLS
const char *const CIPHER_LIST =
"ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-"
"AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:"
"DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-"
"AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-"
"AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-"
"AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:"
"DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-"
"SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-"
"SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!"
"aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA";
} // namespace
namespace {
int communicate(
const std::string &scheme, const std::string &host, uint16_t port,
@@ -2212,7 +2246,7 @@ int communicate(
SSL_CTX_set_options(ssl_ctx, ssl_opts);
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
if (SSL_CTX_set_cipher_list(ssl_ctx, CIPHER_LIST) == 0) {
if (SSL_CTX_set_cipher_list(ssl_ctx, ssl::DEFAULT_CIPHER_LIST) == 0) {
std::cerr << "[ERROR] " << ERR_error_string(ERR_get_error(), nullptr)
<< std::endl;
result = -1;
@@ -2680,7 +2714,7 @@ int main(int argc, char **argv) {
bool color = false;
while (1) {
static int flag = 0;
static option long_options[] = {
constexpr static option long_options[] = {
{"verbose", no_argument, nullptr, 'v'},
{"null-out", no_argument, nullptr, 'n'},
{"remote-name", no_argument, nullptr, 'O'},
@@ -2950,6 +2984,41 @@ int main(int argc, char **argv) {
}
config.weight.insert(std::end(config.weight), argc - optind, weight_to_fill);
// Find scheme overridden by extra header fields.
auto scheme_it =
std::find_if(std::begin(config.headers), std::end(config.headers),
[](const Header &nv) { return nv.name == ":scheme"; });
if (scheme_it != std::end(config.headers)) {
config.scheme_override = (*scheme_it).value;
}
// Find host and port overridden by extra header fields.
auto authority_it =
std::find_if(std::begin(config.headers), std::end(config.headers),
[](const Header &nv) { return nv.name == ":authority"; });
if (authority_it == std::end(config.headers)) {
authority_it =
std::find_if(std::begin(config.headers), std::end(config.headers),
[](const Header &nv) { return nv.name == "host"; });
}
if (authority_it != std::end(config.headers)) {
// authority_it may looks like "host:port".
auto uri = "https://" + (*authority_it).value;
http_parser_url u{};
if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) {
std::cerr << "[ERROR] Could not parse authority in "
<< (*authority_it).name << ": " << (*authority_it).value
<< std::endl;
exit(EXIT_FAILURE);
}
config.host_override = util::get_uri_field(uri.c_str(), u, UF_HOST).str();
if (util::has_uri_field(u, UF_PORT)) {
config.port_override = u.port;
}
}
set_color_output(color || isatty(fileno(stdout)));
nghttp2_option_set_peer_max_concurrent_streams(

View File

@@ -69,6 +69,8 @@ struct Config {
std::string keyfile;
std::string datafile;
std::string harfile;
std::string scheme_override;
std::string host_override;
nghttp2_option *http2_option;
int64_t header_table_size;
int64_t min_header_table_size;
@@ -82,6 +84,7 @@ struct Config {
int window_bits;
int connection_window_bits;
int verbose;
uint16_t port_override;
bool null_out;
bool remote_name;
bool get_assets;
@@ -151,6 +154,15 @@ struct Request {
void record_response_start_time();
void record_response_end_time();
// Returns scheme taking into account overridden scheme.
StringRef get_real_scheme() const;
// Returns request host, without port, taking into account
// overridden host.
StringRef get_real_host() const;
// Returns request port, taking into account overridden host, port,
// and scheme.
uint16_t get_real_port() const;
Headers res_nva;
Headers req_nva;
std::string method;
@@ -164,7 +176,7 @@ struct Request {
// Number of bytes received from server
int64_t response_len;
nghttp2_gzip *inflater;
HtmlParser *html_parser;
std::unique_ptr<HtmlParser> html_parser;
const nghttp2_data_provider *data_prd;
size_t header_buffer_size;
int32_t stream_id;

View File

@@ -202,7 +202,7 @@ int main(int argc, char **argv) {
while (1) {
static int flag = 0;
static option long_options[] = {
constexpr static option long_options[] = {
{"address", required_argument, nullptr, 'a'},
{"daemon", no_argument, nullptr, 'D'},
{"htdocs", required_argument, nullptr, 'd'},

View File

@@ -102,6 +102,8 @@ int main(int argc, char *argv[]) {
shrpx::test_http2_get_pure_path_component) ||
!CU_add_test(pSuite, "http2_construct_push_component",
shrpx::test_http2_construct_push_component) ||
!CU_add_test(pSuite, "http2_contains_trailers",
shrpx::test_http2_contains_trailers) ||
!CU_add_test(pSuite, "downstream_field_store_append_last_header",
shrpx::test_downstream_field_store_append_last_header) ||
!CU_add_test(pSuite, "downstream_field_store_header",
@@ -179,6 +181,9 @@ int main(int argc, char *argv[]) {
!CU_add_test(pSuite, "util_random_alpha_digit",
shrpx::test_util_random_alpha_digit) ||
!CU_add_test(pSuite, "util_format_hex", shrpx::test_util_format_hex) ||
!CU_add_test(pSuite, "util_is_hex_string",
shrpx::test_util_is_hex_string) ||
!CU_add_test(pSuite, "util_decode_hex", shrpx::test_util_decode_hex) ||
!CU_add_test(pSuite, "gzip_inflate", test_nghttp2_gzip_inflate) ||
!CU_add_test(pSuite, "buffer_write", nghttp2::test_buffer_write) ||
!CU_add_test(pSuite, "pool_recycle", nghttp2::test_pool_recycle) ||

View File

@@ -411,7 +411,7 @@ void exec_binary() {
LOG(ERROR) << "Unblocking all signals failed: "
<< xsi_strerror(error, errbuf.data(), errbuf.size());
_Exit(EXIT_FAILURE);
nghttp2_Exit(EXIT_FAILURE);
}
auto exec_path =
@@ -419,7 +419,7 @@ void exec_binary() {
if (!exec_path) {
LOG(ERROR) << "Could not resolve the executable path";
_Exit(EXIT_FAILURE);
nghttp2_Exit(EXIT_FAILURE);
}
auto argv = make_unique<char *[]>(suconfig.argc + 1);
@@ -487,12 +487,12 @@ void exec_binary() {
}
// restores original stderr
util::restore_original_fds();
restore_original_fds();
if (execve(argv[0], argv.get(), envp.get()) == -1) {
auto error = errno;
LOG(ERROR) << "execve failed: errno=" << error;
_Exit(EXIT_FAILURE);
nghttp2_Exit(EXIT_FAILURE);
}
}
} // namespace
@@ -1170,7 +1170,7 @@ pid_t fork_worker_process(int &main_ipc_fd,
LOG(FATAL) << "Unblocking all signals failed: "
<< xsi_strerror(error, errbuf.data(), errbuf.size());
_Exit(EXIT_FAILURE);
nghttp2_Exit(EXIT_FAILURE);
}
close(ipc_fd[1]);
@@ -1179,13 +1179,13 @@ pid_t fork_worker_process(int &main_ipc_fd,
if (rv != 0) {
LOG(FATAL) << "Worker process returned error";
_Exit(EXIT_FAILURE);
nghttp2_Exit(EXIT_FAILURE);
}
LOG(NOTICE) << "Worker process shutting down momentarily";
// call exit(...) instead of _Exit to get leak sanitizer report
_Exit(EXIT_SUCCESS);
// call exit(...) instead of nghttp2_Exit to get leak sanitizer report
nghttp2_Exit(EXIT_SUCCESS);
}
// parent process
@@ -1354,6 +1354,9 @@ void fill_default_config(Config *config) {
}
tlsconf.session_timeout = std::chrono::hours(12);
tlsconf.ciphers = StringRef::from_lit(nghttp2::ssl::DEFAULT_CIPHER_LIST);
tlsconf.client.ciphers =
StringRef::from_lit(nghttp2::ssl::DEFAULT_CIPHER_LIST);
#if OPENSSL_1_1_API
tlsconf.ecdh_curves = StringRef::from_lit("X25519:P-256:P-384:P-521");
#else // !OPENSSL_1_1_API
@@ -1444,7 +1447,7 @@ void fill_default_config(Config *config) {
auto &listenerconf = connconf.listener;
{
// Default accept() backlog
listenerconf.backlog = 512;
listenerconf.backlog = 65536;
listenerconf.timeout.sleep = 30_s;
}
}
@@ -1461,6 +1464,9 @@ void fill_default_config(Config *config) {
// Write timeout for HTTP2/non-HTTP2 upstream connection
timeoutconf.write = 30_s;
// Keep alive timeout for HTTP/1 upstream connection
timeoutconf.idle_read = 1_min;
}
}
@@ -1486,6 +1492,14 @@ void fill_default_config(Config *config) {
auto &apiconf = config->api;
apiconf.max_request_body = 16_k;
auto &dnsconf = config->dns;
{
auto &timeoutconf = dnsconf.timeout;
timeoutconf.cache = 10_s;
timeoutconf.lookup = 5_s;
}
dnsconf.max_try = 2;
}
} // namespace
@@ -1587,13 +1601,13 @@ Connections:
Several parameters <PARAM> are accepted after <PATTERN>.
The parameters are delimited by ";". The available
parameters are: "proto=<PROTO>", "tls",
"sni=<SNI_HOST>", "fall=<N>", "rise=<N>", and
"affinity=<METHOD>". The parameter consists of keyword,
and optionally followed by "=" and value. For example,
the parameter "proto=h2" consists of the keyword "proto"
and value "h2". The parameter "tls" consists of the
keyword "tls" without value. Each parameter is
described as follows.
"sni=<SNI_HOST>", "fall=<N>", "rise=<N>",
"affinity=<METHOD>", "dns", and "frontend-tls". The
parameter consists of keyword, and optionally followed
by "=" and value. For example, the parameter "proto=h2"
consists of the keyword "proto" and value "h2". The
parameter "tls" consists of the keyword "tls" without
value. Each parameter is described as follows.
The backend application protocol can be specified using
optional "proto" parameter, and in the form of
@@ -1642,6 +1656,25 @@ Connections:
break if one of the backend gets unreachable, or backend
settings are reloaded or replaced by API.
By default, name resolution of backend host name is done
at start up, or reloading configuration. If "dns"
parameter is given, name resolution takes place
dynamically. This is useful if backend address changes
frequently. If "dns" is given, name resolution of
backend host name at start up, or reloading
configuration is skipped.
If "frontend-tls" parameter is used, the matched backend
requires frontend TLS connection. In other words, even
if pattern is matched, frontend connection is not TLS
protected, the request is forwarded to one of catch-all
backends. For this reason, catch-all backend cannot
have "frontend-tls" parameter. If at least one backend
has "frontend-tls" parameter, this feature is enabled
for all backend servers sharing the same <PATTERN>. It
is advised to set "frontend-tls" parameter to all
backends explicitly if this feature is desired.
Since ";" and ":" are used as delimiter, <PATTERN> must
not contain these characters. Since ";" has special
meaning in shell, the option value must be quoted.
@@ -1675,6 +1708,10 @@ Connections:
default. Any requests which come through this address
are replied with 200 HTTP status, without no body.
To accept PROXY protocol version 1 on frontend
connection, specify "proxyproto" parameter. This is
disabled by default.
Default: *,3000
--backlog=<N>
Set listen backlog size.
@@ -1699,8 +1736,6 @@ Connections:
timeouts when connecting and making CONNECT request can
be specified by --backend-read-timeout and
--backend-write-timeout options.
--accept-proxy-protocol
Accept PROXY protocol version 1 on frontend connection.
Performance:
-n, --workers=<N>
@@ -1812,6 +1847,11 @@ Timeout:
Specify write timeout for all frontend connections.
Default: )"
<< util::duration_str(config->conn.upstream.timeout.write) << R"(
--frontend-keep-alive-timeout=<DURATION>
Specify keep-alive timeout for frontend HTTP/1
connection.
Default: )"
<< util::duration_str(config->conn.upstream.timeout.idle_read) << R"(
--stream-read-timeout=<DURATION>
Specify read timeout for HTTP/2 and SPDY streams. 0
means no timeout.
@@ -1836,7 +1876,8 @@ Timeout:
Default: )"
<< util::duration_str(config->conn.downstream->timeout.connect) << R"(
--backend-keep-alive-timeout=<DURATION>
Specify keep-alive timeout for backend connection.
Specify keep-alive timeout for backend HTTP/1
connection.
Default: )"
<< util::duration_str(config->conn.downstream->timeout.idle_read) << R"(
--listener-disable-timeout=<DURATION>
@@ -1869,8 +1910,15 @@ Timeout:
SSL/TLS:
--ciphers=<SUITE>
Set allowed cipher list. The format of the string is
described in OpenSSL ciphers(1).
Set allowed cipher list for frontend connection. The
format of the string is described in OpenSSL ciphers(1).
Default: )"
<< config->tls.ciphers << R"(
--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"(
--ecdh-curves=<LIST>
Set supported curve list for frontend connections.
<LIST> is a colon separated list of curve NID or names
@@ -2064,9 +2112,15 @@ SSL/TLS:
Default: )"
<< util::duration_str(config->tls.dyn_rec.idle_timeout) << R"(
--no-http2-cipher-black-list
Allow black listed cipher suite on HTTP/2 connection.
See https://tools.ietf.org/html/rfc7540#appendix-A for
the complete HTTP/2 cipher suites black list.
Allow black listed cipher suite on frontend HTTP/2
connection. See
https://tools.ietf.org/html/rfc7540#appendix-A for the
complete HTTP/2 cipher suites black list.
--client-no-http2-cipher-black-list
Allow black listed cipher suite on backend HTTP/2
connection. See
https://tools.ietf.org/html/rfc7540#appendix-A for the
complete HTTP/2 cipher suites black list.
--tls-sct-dir=<DIR>
Specifies the directory where *.sct files exist. All
*.sct files in <DIR> are read, and sent as
@@ -2076,6 +2130,33 @@ SSL/TLS:
argument <CERT>, or certificate option in configuration
file. For additional certificates, use --subcert
option. This option requires OpenSSL >= 1.0.2.
--psk-secrets=<PATH>
Read list of PSK identity and secrets from <PATH>. This
is used for frontend connection. The each line of input
file is formatted as <identity>:<hex-secret>, where
<identity> is PSK identity, and <hex-secret> is secret
in hex. An empty line, and line which starts with '#'
are skipped. The default enabled cipher list might not
contain any PSK cipher suite. In that case, desired PSK
cipher suites must be enabled using --ciphers option.
The desired PSK cipher suite may be black listed by
HTTP/2. To use those cipher suites with HTTP/2,
consider to use --no-http2-cipher-black-list option.
But be aware its implications.
--client-psk-secrets=<PATH>
Read PSK identity and secrets from <PATH>. This is used
for backend connection. The each line of input file is
formatted as <identity>:<hex-secret>, where <identity>
is PSK identity, and <hex-secret> is secret in hex. An
empty line, and line which starts with '#' are skipped.
The first identity and secret pair encountered is used.
The default enabled cipher list might not contain any
PSK cipher suite. In that case, desired PSK cipher
suites must be enabled using --client-ciphers option.
The desired PSK cipher suite may be black listed by
HTTP/2. To use those cipher suites with HTTP/2,
consider to use --client-no-http2-cipher-black-list
option. But be aware its implications.
HTTP/2 and SPDY:
-c, --frontend-http2-max-concurrent-streams=<N>
@@ -2237,6 +2318,10 @@ Logging:
Default: )"
<< DEFAULT_ACCESSLOG_FORMAT << R"(
--accesslog-write-early
Write access log when response header fields are
received from backend rather than when request
transaction finishes.
--errorlog-file=<PATH>
Set path to write error log. To reopen file, send USR1
signal to nghttpx. stderr will be redirected to the
@@ -2368,6 +2453,25 @@ API:
Default: )"
<< util::utos_unit(config->api.max_request_body) << R"(
DNS:
--dns-cache-timeout=<DURATION>
Set duration that cached DNS results remain valid. Note
that nghttpx caches the unsuccessful results as well.
Default: )"
<< util::duration_str(config->dns.timeout.cache) << R"(
--dns-lookup-timeout=<DURATION>
Set timeout that DNS server is given to respond to the
initial DNS query. For the 2nd and later queries,
server is given time based on this timeout, and it is
scaled linearly.
Default: )"
<< util::duration_str(config->dns.timeout.lookup) << R"(
--dns-max-try=<N>
Set the number of DNS query before nghttpx gives up name
lookup.
Default: )"
<< config->dns.max_try << R"(
Debug:
--frontend-http2-dump-request-header=<PATH>
Dumps request headers received by HTTP/2 frontend to the
@@ -2789,7 +2893,7 @@ int main(int argc, char **argv) {
fill_default_config(mod_config());
// make copy of stderr
util::store_original_fds();
store_original_fds();
// First open log files with default configuration, so that we can
// log errors/warnings while reading configuration files.
@@ -2822,7 +2926,7 @@ int main(int argc, char **argv) {
while (1) {
static int flag = 0;
static option long_options[] = {
static constexpr option long_options[] = {
{SHRPX_OPT_DAEMON.c_str(), no_argument, nullptr, 'D'},
{SHRPX_OPT_LOG_LEVEL.c_str(), required_argument, nullptr, 'L'},
{SHRPX_OPT_BACKEND.c_str(), required_argument, nullptr, 'b'},
@@ -3027,6 +3131,17 @@ int main(int argc, char **argv) {
{SHRPX_OPT_TLS_SCT_DIR.c_str(), required_argument, &flag, 141},
{SHRPX_OPT_BACKEND_CONNECT_TIMEOUT.c_str(), required_argument, &flag,
142},
{SHRPX_OPT_DNS_CACHE_TIMEOUT.c_str(), required_argument, &flag, 143},
{SHRPX_OPT_DNS_LOOKUP_TIMEOUT.c_str(), required_argument, &flag, 144},
{SHRPX_OPT_DNS_MAX_TRY.c_str(), required_argument, &flag, 145},
{SHRPX_OPT_FRONTEND_KEEP_ALIVE_TIMEOUT.c_str(), required_argument,
&flag, 146},
{SHRPX_OPT_PSK_SECRETS.c_str(), required_argument, &flag, 147},
{SHRPX_OPT_CLIENT_PSK_SECRETS.c_str(), required_argument, &flag, 148},
{SHRPX_OPT_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST.c_str(), no_argument,
&flag, 149},
{SHRPX_OPT_CLIENT_CIPHERS.c_str(), required_argument, &flag, 150},
{SHRPX_OPT_ACCESSLOG_WRITE_EARLY.c_str(), no_argument, &flag, 151},
{nullptr, 0, nullptr, 0}};
int option_index = 0;
@@ -3700,6 +3815,45 @@ int main(int argc, char **argv) {
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_CONNECT_TIMEOUT,
StringRef{optarg});
break;
case 143:
// --dns-cache-timeout
cmdcfgs.emplace_back(SHRPX_OPT_DNS_CACHE_TIMEOUT, StringRef{optarg});
break;
case 144:
// --dns-lookup-timeou
cmdcfgs.emplace_back(SHRPX_OPT_DNS_LOOKUP_TIMEOUT, StringRef{optarg});
break;
case 145:
// --dns-max-try
cmdcfgs.emplace_back(SHRPX_OPT_DNS_MAX_TRY, StringRef{optarg});
break;
case 146:
// --frontend-keep-alive-timeout
cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_KEEP_ALIVE_TIMEOUT,
StringRef{optarg});
break;
case 147:
// --psk-secrets
cmdcfgs.emplace_back(SHRPX_OPT_PSK_SECRETS, StringRef{optarg});
break;
case 148:
// --client-psk-secrets
cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_PSK_SECRETS, StringRef{optarg});
break;
case 149:
// --client-no-http2-cipher-black-list
cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST,
StringRef::from_lit("yes"));
break;
case 150:
// --client-ciphers
cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_CIPHERS, StringRef{optarg});
break;
case 151:
// --accesslog-write-early
cmdcfgs.emplace_back(SHRPX_OPT_ACCESSLOG_WRITE_EARLY,
StringRef::from_lit("yes"));
break;
default:
break;
}

View File

@@ -36,13 +36,15 @@
#include <cassert>
#ifndef HAVE__EXIT
#define nghttp2_Exit(status) _exit(status)
#else // HAVE__EXIT
#define nghttp2_Exit(status) _Exit(status)
#endif // HAVE__EXIT
#include "shrpx_log.h"
#ifndef HAVE__EXIT
#define _Exit(status) _exit(status)
#endif // !HAVE__EXIT
#define DIE() _Exit(EXIT_FAILURE)
#define DIE() nghttp2_Exit(EXIT_FAILURE)
#if defined(HAVE_DECL_INITGROUPS) && !HAVE_DECL_INITGROUPS
inline int initgroups(const char *user, gid_t group) { return 0; }

View File

@@ -117,6 +117,7 @@ void writecb(struct ev_loop *loop, ev_io *w, int revents) {
int ClientHandler::noop() { return 0; }
int ClientHandler::read_clear() {
rb_.ensure_chunk();
for (;;) {
if (rb_.rleft() && on_read() != 0) {
return -1;
@@ -132,9 +133,12 @@ int ClientHandler::read_clear() {
return 0;
}
auto nread = conn_.read_clear(rb_.last, rb_.wleft());
auto nread = conn_.read_clear(rb_.last(), rb_.wleft());
if (nread == 0) {
if (rb_.rleft() == 0) {
rb_.release_chunk();
}
return 0;
}
@@ -209,6 +213,8 @@ int ClientHandler::tls_handshake() {
int ClientHandler::read_tls() {
ERR_clear_error();
rb_.ensure_chunk();
for (;;) {
// we should process buffered data first before we read EOF.
if (rb_.rleft() && on_read() != 0) {
@@ -225,9 +231,12 @@ int ClientHandler::read_tls() {
return 0;
}
auto nread = conn_.read_tls(rb_.last, rb_.wleft());
auto nread = conn_.read_tls(rb_.last(), rb_.wleft());
if (nread == 0) {
if (rb_.rleft() == 0) {
rb_.release_chunk();
}
return 0;
}
@@ -303,7 +312,7 @@ int ClientHandler::upstream_write() {
int ClientHandler::upstream_http2_connhd_read() {
auto nread = std::min(left_connhd_len_, rb_.rleft());
if (memcmp(NGHTTP2_CLIENT_MAGIC + NGHTTP2_CLIENT_MAGIC_LEN - left_connhd_len_,
rb_.pos, nread) != 0) {
rb_.pos(), nread) != 0) {
// There is no downgrade path here. Just drop the connection.
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "invalid client connection header";
@@ -332,7 +341,7 @@ int ClientHandler::upstream_http2_connhd_read() {
int ClientHandler::upstream_http1_connhd_read() {
auto nread = std::min(left_connhd_len_, rb_.rleft());
if (memcmp(NGHTTP2_CLIENT_MAGIC + NGHTTP2_CLIENT_MAGIC_LEN - left_connhd_len_,
rb_.pos, nread) != 0) {
rb_.pos(), nread) != 0) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "This is HTTP/1.1 connection, "
<< "but may be upgraded to HTTP/2 later.";
@@ -386,6 +395,7 @@ ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl,
// so the required space is 64 + 48 + 16 + 48 + 16 + 16 + 16 +
// 32 + 8 + 8 * 8 = 328.
balloc_(512, 512),
rb_(worker->get_mcpool()),
conn_(worker->get_loop(), fd, ssl, worker->get_mcpool(),
get_config()->conn.upstream.timeout.write,
get_config()->conn.upstream.timeout.read,
@@ -413,7 +423,8 @@ ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl,
auto config = get_config();
if (config->conn.upstream.accept_proxy_protocol) {
if (faddr_->accept_proxy_protocol ||
config->conn.upstream.accept_proxy_protocol) {
read_ = &ClientHandler::read_clear;
write_ = &ClientHandler::noop;
on_read_ = &ClientHandler::proxy_protocol_read;
@@ -646,9 +657,11 @@ int ClientHandler::do_read() { return read_(*this); }
int ClientHandler::do_write() { return write_(*this); }
int ClientHandler::on_read() {
auto rv = on_read_(*this);
if (rv != 0) {
return rv;
if (rb_.chunk_avail()) {
auto rv = on_read_(*this);
if (rv != 0) {
return rv;
}
}
conn_.handle_tls_pending_read();
return 0;
@@ -995,6 +1008,13 @@ ClientHandler::get_downstream_connection(Downstream *downstream) {
CLOG(INFO, this) << "Downstream address group_idx: " << group_idx;
}
if (groups[group_idx]->shared_addr->require_upstream_tls && !conn_.tls.ssl) {
CLOG(INFO, this) << "Downstream address group " << group_idx
<< " requires frontend TLS connection. Send request to "
"catch-all group.";
group_idx = catch_all;
}
auto &group = groups[group_idx];
auto &shared_addr = group->shared_addr;
@@ -1177,100 +1197,25 @@ void ClientHandler::start_immediate_shutdown() {
ev_timer_start(conn_.loop, &reneg_shutdown_timer_);
}
namespace {
// Construct absolute request URI from |Request|, mainly to log
// request URI for proxy request (HTTP/2 proxy or client proxy). This
// is mostly same routine found in
// HttpDownstreamConnection::push_request_headers(), but vastly
// simplified since we only care about absolute URI.
StringRef construct_absolute_request_uri(BlockAllocator &balloc,
const Request &req) {
if (req.authority.empty()) {
return req.path;
}
auto len = req.authority.size() + req.path.size();
if (req.scheme.empty()) {
len += str_size("http://");
} else {
len += req.scheme.size() + str_size("://");
}
auto iov = make_byte_ref(balloc, len + 1);
auto p = iov.base;
if (req.scheme.empty()) {
// We may have to log the request which lacks scheme (e.g.,
// http/1.1 with origin form).
p = util::copy_lit(p, "http://");
} else {
p = std::copy(std::begin(req.scheme), std::end(req.scheme), p);
p = util::copy_lit(p, "://");
}
p = std::copy(std::begin(req.authority), std::end(req.authority), p);
p = std::copy(std::begin(req.path), std::end(req.path), p);
*p = '\0';
return StringRef{iov.base, p};
}
} // namespace
void ClientHandler::write_accesslog(Downstream *downstream) {
nghttp2::ssl::TLSSessionInfo tls_info;
const auto &req = downstream->request();
const auto &resp = downstream->response();
auto &req = downstream->request();
auto &balloc = downstream->get_block_allocator();
auto config = get_config();
if (!req.tstamp) {
auto lgconf = log_config();
lgconf->update_tstamp(std::chrono::system_clock::now());
req.tstamp = lgconf->tstamp;
}
upstream_accesslog(
config->logging.access.format,
LogSpec{
downstream, downstream->get_addr(), ipaddr_,
http2::to_method_string(req.method),
req.method == HTTP_CONNECT
? StringRef(req.authority)
: config->http2_proxy
? StringRef(construct_absolute_request_uri(balloc, req))
: req.path.empty()
? req.method == HTTP_OPTIONS
? StringRef::from_lit("*")
: StringRef::from_lit("-")
: StringRef(req.path),
alpn_, nghttp2::ssl::get_tls_session_info(&tls_info, conn_.tls.ssl),
std::chrono::system_clock::now(), // time_now
downstream->get_request_start_time(), // request_start_time
downstream, ipaddr_, alpn_,
nghttp2::ssl::get_tls_session_info(&tls_info, conn_.tls.ssl),
std::chrono::high_resolution_clock::now(), // request_end_time
req.http_major, req.http_minor, resp.http_status,
downstream->response_sent_body_length, port_, faddr_->port,
config->pid,
});
}
void ClientHandler::write_accesslog(int major, int minor, unsigned int status,
int64_t body_bytes_sent) {
auto time_now = std::chrono::system_clock::now();
auto highres_now = std::chrono::high_resolution_clock::now();
nghttp2::ssl::TLSSessionInfo tls_info;
auto config = get_config();
upstream_accesslog(
config->logging.access.format,
LogSpec{
nullptr, nullptr, ipaddr_,
StringRef::from_lit("-"), // method
StringRef::from_lit("-"), // path,
alpn_, nghttp2::ssl::get_tls_session_info(&tls_info, conn_.tls.ssl),
time_now,
highres_now, // request_start_time TODO is
// there a better value?
highres_now, // request_end_time
major, minor, // major, minor
status, body_bytes_sent, port_, faddr_->port, config->pid,
port_, faddr_->port, config->pid,
});
}
@@ -1278,6 +1223,14 @@ ClientHandler::ReadBuf *ClientHandler::get_rb() { return &rb_; }
void ClientHandler::signal_write() { conn_.wlimit.startw(); }
void ClientHandler::signal_write_no_wait() {
// ev_feed_event works without starting watcher. But rate limiter
// requires active watcher. Without that, we might not send pending
// data. Also ClientHandler::write_tls requires it.
conn_.wlimit.startw();
ev_feed_event(conn_.loop, &conn_.wev, EV_WRITE);
}
RateLimit *ClientHandler::get_rlimit() { return &conn_.rlimit; }
RateLimit *ClientHandler::get_wlimit() { return &conn_.wlimit; }
@@ -1316,7 +1269,7 @@ ssize_t parse_proxy_line_port(const uint8_t *first, const uint8_t *last) {
int ClientHandler::on_proxy_protocol_finish() {
if (conn_.tls.ssl) {
conn_.tls.rbuf.append(rb_.pos, rb_.rleft());
conn_.tls.rbuf.append(rb_.pos(), rb_.rleft());
rb_.reset();
}
@@ -1337,7 +1290,7 @@ int ClientHandler::proxy_protocol_read() {
CLOG(INFO, this) << "PROXY-protocol: Started";
}
auto first = rb_.pos;
auto first = rb_.pos();
// NULL character really destroys functions which expects NULL
// terminated string. We won't expect it in PROXY protocol line, so
@@ -1346,12 +1299,12 @@ int ClientHandler::proxy_protocol_read() {
constexpr size_t MAX_PROXY_LINELEN = 107;
auto bufend = rb_.pos + std::min(MAX_PROXY_LINELEN, rb_.rleft());
auto bufend = rb_.pos() + std::min(MAX_PROXY_LINELEN, rb_.rleft());
auto end =
std::find_first_of(rb_.pos, bufend, std::begin(chrs), std::end(chrs));
std::find_first_of(rb_.pos(), bufend, std::begin(chrs), std::end(chrs));
if (end == bufend || *end == '\0' || end == rb_.pos || *(end - 1) != '\r') {
if (end == bufend || *end == '\0' || end == rb_.pos() || *(end - 1) != '\r') {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "PROXY-protocol-v1: No ending CR LF sequence found";
}
@@ -1362,14 +1315,14 @@ int ClientHandler::proxy_protocol_read() {
constexpr auto HEADER = StringRef::from_lit("PROXY ");
if (static_cast<size_t>(end - rb_.pos) < HEADER.size()) {
if (static_cast<size_t>(end - rb_.pos()) < HEADER.size()) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "PROXY-protocol-v1: PROXY version 1 ID not found";
}
return -1;
}
if (!util::streq(HEADER, StringRef{rb_.pos, HEADER.size()})) {
if (!util::streq(HEADER, StringRef{rb_.pos(), HEADER.size()})) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "PROXY-protocol-v1: Bad PROXY protocol version 1 ID";
}
@@ -1380,22 +1333,22 @@ int ClientHandler::proxy_protocol_read() {
int family;
if (rb_.pos[0] == 'T') {
if (end - rb_.pos < 5) {
if (rb_.pos()[0] == 'T') {
if (end - rb_.pos() < 5) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "PROXY-protocol-v1: INET protocol family not found";
}
return -1;
}
if (rb_.pos[1] != 'C' || rb_.pos[2] != 'P') {
if (rb_.pos()[1] != 'C' || rb_.pos()[2] != 'P') {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "PROXY-protocol-v1: Unknown INET protocol family";
}
return -1;
}
switch (rb_.pos[3]) {
switch (rb_.pos()[3]) {
case '4':
family = AF_INET;
break;
@@ -1411,26 +1364,26 @@ int ClientHandler::proxy_protocol_read() {
rb_.drain(5);
} else {
if (end - rb_.pos < 7) {
if (end - rb_.pos() < 7) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "PROXY-protocol-v1: INET protocol family not found";
}
return -1;
}
if (!util::streq_l("UNKNOWN", rb_.pos, 7)) {
if (!util::streq_l("UNKNOWN", rb_.pos(), 7)) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "PROXY-protocol-v1: Unknown INET protocol family";
}
return -1;
}
rb_.drain(end + 2 - rb_.pos);
rb_.drain(end + 2 - rb_.pos());
return on_proxy_protocol_finish();
}
// source address
auto token_end = std::find(rb_.pos, end, ' ');
auto token_end = std::find(rb_.pos(), end, ' ');
if (token_end == end) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "PROXY-protocol-v1: Source address not found";
@@ -1439,20 +1392,20 @@ int ClientHandler::proxy_protocol_read() {
}
*token_end = '\0';
if (!util::numeric_host(reinterpret_cast<const char *>(rb_.pos), family)) {
if (!util::numeric_host(reinterpret_cast<const char *>(rb_.pos()), family)) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "PROXY-protocol-v1: Invalid source address";
}
return -1;
}
auto src_addr = rb_.pos;
auto src_addrlen = token_end - rb_.pos;
auto src_addr = rb_.pos();
auto src_addrlen = token_end - rb_.pos();
rb_.drain(token_end - rb_.pos + 1);
rb_.drain(token_end - rb_.pos() + 1);
// destination address
token_end = std::find(rb_.pos, end, ' ');
token_end = std::find(rb_.pos(), end, ' ');
if (token_end == end) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "PROXY-protocol-v1: Destination address not found";
@@ -1461,7 +1414,7 @@ int ClientHandler::proxy_protocol_read() {
}
*token_end = '\0';
if (!util::numeric_host(reinterpret_cast<const char *>(rb_.pos), family)) {
if (!util::numeric_host(reinterpret_cast<const char *>(rb_.pos()), family)) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "PROXY-protocol-v1: Invalid destination address";
}
@@ -1470,26 +1423,26 @@ int ClientHandler::proxy_protocol_read() {
// Currently we don't use destination address
rb_.drain(token_end - rb_.pos + 1);
rb_.drain(token_end - rb_.pos() + 1);
// source port
auto n = parse_proxy_line_port(rb_.pos, end);
if (n <= 0 || *(rb_.pos + n) != ' ') {
auto n = parse_proxy_line_port(rb_.pos(), end);
if (n <= 0 || *(rb_.pos() + n) != ' ') {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "PROXY-protocol-v1: Invalid source port";
}
return -1;
}
rb_.pos[n] = '\0';
auto src_port = rb_.pos;
rb_.pos()[n] = '\0';
auto src_port = rb_.pos();
auto src_portlen = n;
rb_.drain(n + 1);
// destination port
n = parse_proxy_line_port(rb_.pos, end);
if (n <= 0 || rb_.pos + n != end) {
n = parse_proxy_line_port(rb_.pos(), end);
if (n <= 0 || rb_.pos() + n != end) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "PROXY-protocol-v1: Invalid destination port";
}
@@ -1498,14 +1451,14 @@ int ClientHandler::proxy_protocol_read() {
// Currently we don't use destination port
rb_.drain(end + 2 - rb_.pos);
rb_.drain(end + 2 - rb_.pos());
ipaddr_ =
make_string_ref(balloc_, StringRef{src_addr, src_addr + src_addrlen});
port_ = make_string_ref(balloc_, StringRef{src_port, src_port + src_portlen});
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "PROXY-protocol-v1: Finished, " << (rb_.pos - first)
CLOG(INFO, this) << "PROXY-protocol-v1: Finished, " << (rb_.pos() - first)
<< " bytes read";
}

View File

@@ -118,13 +118,9 @@ public:
// must not be nullptr.
void write_accesslog(Downstream *downstream);
// Writes upstream accesslog. This function is used if
// corresponding Downstream object is not available.
void write_accesslog(int major, int minor, unsigned int status,
int64_t body_bytes_sent);
Worker *get_worker() const;
using ReadBuf = Buffer<16_k>;
using ReadBuf = DefaultMemchunkBuffer;
ReadBuf *get_rb();
@@ -132,6 +128,8 @@ public:
RateLimit *get_wlimit();
void signal_write();
// Use this for HTTP/1 frontend since it produces better result.
void signal_write_no_wait();
ev_io *get_wev();
void setup_upstream_io_callback();
@@ -169,6 +167,7 @@ private:
// sure that the allocations must be bounded, and not proportional
// to the number of requests.
BlockAllocator balloc_;
DefaultMemchunkBuffer rb_;
Connection conn_;
ev_timer reneg_shutdown_timer_;
std::unique_ptr<Upstream> upstream_;
@@ -195,7 +194,6 @@ private:
bool should_close_after_write_;
// true if affinity_hash_ is computed
bool affinity_hash_computed_;
ReadBuf rb_;
};
} // namespace shrpx

View File

@@ -677,6 +677,7 @@ int parse_memcached_connection_params(MemcachedConnectionParams &out,
struct UpstreamParams {
int alt_mode;
bool tls;
bool proxyproto;
};
namespace {
@@ -705,6 +706,8 @@ int parse_upstream_params(UpstreamParams &out, const StringRef &src_params) {
return -1;
}
out.alt_mode = ALTMODE_HEALTHMON;
} else if (util::strieq_l("proxyproto", param)) {
out.proxyproto = true;
} else if (!param.empty()) {
LOG(ERROR) << "frontend: " << param << ": unknown keyword";
return -1;
@@ -728,6 +731,8 @@ struct DownstreamParams {
shrpx_proto proto;
shrpx_session_affinity affinity;
bool tls;
bool dns;
bool frontend_tls;
};
namespace {
@@ -801,6 +806,10 @@ int parse_downstream_params(DownstreamParams &out,
LOG(ERROR) << "backend: affinity: value must be either none or ip";
return -1;
}
} else if (util::strieq_l("dns", param)) {
out.dns = true;
} else if (util::strieq_l("frontend-tls", param)) {
out.frontend_tls = true;
} else if (!param.empty()) {
LOG(ERROR) << "backend: " << param << ": unknown keyword";
return -1;
@@ -841,11 +850,17 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
return -1;
}
if (addr.host_unix && params.dns) {
LOG(ERROR) << "backend: dns: cannot be used for UNIX domain socket";
return -1;
}
addr.fall = params.fall;
addr.rise = params.rise;
addr.proto = params.proto;
addr.tls = params.tls;
addr.sni = make_string_ref(downstreamconf.balloc, params.sni);
addr.dns = params.dns;
auto &routerconf = downstreamconf.router;
auto &router = routerconf.router;
@@ -887,6 +902,11 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
if (params.affinity != AFFINITY_NONE) {
g.affinity = params.affinity;
}
// If at least one backend requires frontend TLS connection,
// enable it for all backends sharing the same pattern.
if (params.frontend_tls) {
g.require_upstream_tls = true;
}
g.addrs.push_back(addr);
done = true;
break;
@@ -901,6 +921,7 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
auto &g = addr_groups.back();
g.addrs.push_back(addr);
g.affinity = params.affinity;
g.require_upstream_tls = params.frontend_tls;
if (pattern[0] == '*') {
// wildcard pattern
@@ -1187,6 +1208,134 @@ int read_tls_sct_from_dir(std::vector<uint8_t> &dst, const StringRef &opt,
}
} // namespace
#if !LIBRESSL_IN_USE
namespace {
// Reads PSK secrets from path, and parses each line. The result is
// directly stored into config->tls.psk_secrets. This function
// returns 0 if it succeeds, or -1.
int parse_psk_secrets(Config *config, const StringRef &path) {
auto &tlsconf = config->tls;
std::ifstream f(path.c_str(), std::ios::binary);
if (!f) {
LOG(ERROR) << SHRPX_OPT_PSK_SECRETS << ": could not open file " << path;
return -1;
}
size_t lineno = 0;
std::string line;
while (std::getline(f, line)) {
++lineno;
if (line.empty() || line[0] == '#') {
continue;
}
auto sep_it = std::find(std::begin(line), std::end(line), ':');
if (sep_it == std::end(line)) {
LOG(ERROR) << SHRPX_OPT_PSK_SECRETS
<< ": could not fine separator at line " << lineno;
return -1;
}
if (sep_it == std::begin(line)) {
LOG(ERROR) << SHRPX_OPT_PSK_SECRETS << ": empty identity at line "
<< lineno;
return -1;
}
if (sep_it + 1 == std::end(line)) {
LOG(ERROR) << SHRPX_OPT_PSK_SECRETS << ": empty secret at line "
<< lineno;
return -1;
}
if (!util::is_hex_string(StringRef{sep_it + 1, std::end(line)})) {
LOG(ERROR) << SHRPX_OPT_PSK_SECRETS
<< ": secret must be hex string at line " << lineno;
return -1;
}
auto identity =
make_string_ref(config->balloc, StringRef{std::begin(line), sep_it});
auto secret =
util::decode_hex(config->balloc, StringRef{sep_it + 1, std::end(line)});
auto rv = tlsconf.psk_secrets.emplace(identity, secret);
if (!rv.second) {
LOG(ERROR) << SHRPX_OPT_PSK_SECRETS
<< ": identity has already been registered at line " << lineno;
return -1;
}
}
return 0;
}
} // namespace
#endif // !LIBRESSL_IN_USE
#if !LIBRESSL_IN_USE
namespace {
// Reads PSK secrets from path, and parses each line. The result is
// directly stored into config->tls.client.psk. This function returns
// 0 if it succeeds, or -1.
int parse_client_psk_secrets(Config *config, const StringRef &path) {
auto &tlsconf = config->tls;
std::ifstream f(path.c_str(), std::ios::binary);
if (!f) {
LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS << ": could not open file "
<< path;
return -1;
}
size_t lineno = 0;
std::string line;
while (std::getline(f, line)) {
++lineno;
if (line.empty() || line[0] == '#') {
continue;
}
auto sep_it = std::find(std::begin(line), std::end(line), ':');
if (sep_it == std::end(line)) {
LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS
<< ": could not find separator at line " << lineno;
return -1;
}
if (sep_it == std::begin(line)) {
LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS << ": empty identity at line "
<< lineno;
return -1;
}
if (sep_it + 1 == std::end(line)) {
LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS << ": empty secret at line "
<< lineno;
return -1;
}
if (!util::is_hex_string(StringRef{sep_it + 1, std::end(line)})) {
LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS
<< ": secret must be hex string at line " << lineno;
return -1;
}
tlsconf.client.psk.identity =
make_string_ref(config->balloc, StringRef{std::begin(line), sep_it});
tlsconf.client.psk.secret =
util::decode_hex(config->balloc, StringRef{sep_it + 1, std::end(line)});
return 0;
}
return 0;
}
} // namespace
#endif // !LIBRESSL_IN_USE
// generated by gennghttpxfun.py
int option_lookup_token(const char *name, size_t namelen) {
switch (namelen) {
@@ -1354,6 +1503,9 @@ int option_lookup_token(const char *name, size_t namelen) {
if (util::strieq_l("ecdh-curve", name, 10)) {
return SHRPX_OPTID_ECDH_CURVES;
}
if (util::strieq_l("psk-secret", name, 10)) {
return SHRPX_OPTID_PSK_SECRETS;
}
break;
case 't':
if (util::strieq_l("write-burs", name, 10)) {
@@ -1361,6 +1513,9 @@ int option_lookup_token(const char *name, size_t namelen) {
}
break;
case 'y':
if (util::strieq_l("dns-max-tr", name, 10)) {
return SHRPX_OPTID_DNS_MAX_TRY;
}
if (util::strieq_l("http2-prox", name, 10)) {
return SHRPX_OPTID_HTTP2_PROXY;
}
@@ -1443,6 +1598,9 @@ int option_lookup_token(const char *name, size_t namelen) {
if (util::strieq_l("backend-no-tl", name, 13)) {
return SHRPX_OPTID_BACKEND_NO_TLS;
}
if (util::strieq_l("client-cipher", name, 13)) {
return SHRPX_OPTID_CLIENT_CIPHERS;
}
break;
case 't':
if (util::strieq_l("tls-proto-lis", name, 13)) {
@@ -1522,6 +1680,9 @@ int option_lookup_token(const char *name, size_t namelen) {
}
break;
case 't':
if (util::strieq_l("dns-cache-timeou", name, 16)) {
return SHRPX_OPTID_DNS_CACHE_TIMEOUT;
}
if (util::strieq_l("worker-read-burs", name, 16)) {
return SHRPX_OPTID_WORKER_READ_BURST;
}
@@ -1535,7 +1696,15 @@ int option_lookup_token(const char *name, size_t namelen) {
return SHRPX_OPTID_ADD_REQUEST_HEADER;
}
break;
case 's':
if (util::strieq_l("client-psk-secret", name, 17)) {
return SHRPX_OPTID_CLIENT_PSK_SECRETS;
}
break;
case 't':
if (util::strieq_l("dns-lookup-timeou", name, 17)) {
return SHRPX_OPTID_DNS_LOOKUP_TIMEOUT;
}
if (util::strieq_l("worker-write-burs", name, 17)) {
return SHRPX_OPTID_WORKER_WRITE_BURST;
}
@@ -1630,6 +1799,11 @@ int option_lookup_token(const char *name, size_t namelen) {
return SHRPX_OPTID_FRONTEND_READ_TIMEOUT;
}
break;
case 'y':
if (util::strieq_l("accesslog-write-earl", name, 20)) {
return SHRPX_OPTID_ACCESSLOG_WRITE_EARLY;
}
break;
}
break;
case 22:
@@ -1771,6 +1945,9 @@ int option_lookup_token(const char *name, size_t namelen) {
if (util::strieq_l("frontend-http2-read-timeou", name, 26)) {
return SHRPX_OPTID_FRONTEND_HTTP2_READ_TIMEOUT;
}
if (util::strieq_l("frontend-keep-alive-timeou", name, 26)) {
return SHRPX_OPTID_FRONTEND_KEEP_ALIVE_TIMEOUT;
}
break;
}
break;
@@ -1848,6 +2025,11 @@ int option_lookup_token(const char *name, size_t namelen) {
return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL;
}
break;
case 't':
if (util::strieq_l("client-no-http2-cipher-black-lis", name, 32)) {
return SHRPX_OPTID_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST;
}
break;
}
break;
case 34:
@@ -2070,6 +2252,7 @@ int parse_config(Config *config, int optid, const StringRef &opt,
addr.fd = -1;
addr.tls = params.tls;
addr.alt_mode = params.alt_mode;
addr.accept_proxy_protocol = params.proxyproto;
if (addr.alt_mode == ALTMODE_API) {
apiconf.enabled = true;
@@ -2862,6 +3045,8 @@ int parse_config(Config *config, int optid, const StringRef &opt,
#endif // !HAVE_MRUBY
return 0;
case SHRPX_OPTID_ACCEPT_PROXY_PROTOCOL:
LOG(WARN) << opt << ": deprecated. Use proxyproto keyword in "
<< SHRPX_OPT_FRONTEND << " instead.";
config->conn.upstream.accept_proxy_protocol = util::strieq_l("yes", optarg);
return 0;
@@ -3090,6 +3275,58 @@ int parse_config(Config *config, int optid, const StringRef &opt,
LOG(WARN) << opt << ": This option requires OpenSSL >= 1.0.2";
return 0;
#endif // !(!LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L)
case SHRPX_OPTID_DNS_CACHE_TIMEOUT:
return parse_duration(&config->dns.timeout.cache, opt, optarg);
case SHRPX_OPTID_DNS_LOOKUP_TIMEOUT:
return parse_duration(&config->dns.timeout.lookup, opt, optarg);
case SHRPX_OPTID_DNS_MAX_TRY: {
int n;
if (parse_uint(&n, opt, optarg) != 0) {
return -1;
}
if (n > 5) {
LOG(ERROR) << opt << ": must be smaller than or equal to 5";
return -1;
}
config->dns.max_try = n;
return 0;
}
case SHRPX_OPTID_FRONTEND_KEEP_ALIVE_TIMEOUT:
return parse_duration(&config->conn.upstream.timeout.idle_read, opt,
optarg);
case SHRPX_OPTID_PSK_SECRETS:
#if !LIBRESSL_IN_USE
return parse_psk_secrets(config, optarg);
#else // LIBRESSL_IN_USE
LOG(WARN)
<< opt
<< ": ignored because underlying TLS library does not support PSK";
return 0;
#endif // LIBRESSL_IN_USE
case SHRPX_OPTID_CLIENT_PSK_SECRETS:
#if !LIBRESSL_IN_USE
return parse_client_psk_secrets(config, optarg);
#else // LIBRESSL_IN_USE
LOG(WARN)
<< opt
<< ": ignored because underlying TLS library does not support PSK";
return 0;
#endif // LIBRESSL_IN_USE
case SHRPX_OPTID_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST:
config->tls.client.no_http2_cipher_black_list =
util::strieq_l("yes", optarg);
return 0;
case SHRPX_OPTID_CLIENT_CIPHERS:
config->tls.client.ciphers = make_string_ref(config->balloc, optarg);
return 0;
case SHRPX_OPTID_ACCESSLOG_WRITE_EARLY:
config->logging.access.write_early = util::strieq_l("yes", optarg);
return 0;
case SHRPX_OPTID_CONF:
LOG(WARN) << "conf: ignored";
@@ -3397,6 +3634,12 @@ int configure_downstream_group(Config *config, bool http2_proxy,
return -1;
}
if (addr_groups[catch_all_group].require_upstream_tls) {
LOG(FATAL)
<< "backend: Catch-all backend cannot have frontend-tls parameter";
return -1;
}
downstreamconf.addr_group_catch_all = catch_all_group;
if (LOG_ENABLED(INFO)) {
@@ -3442,24 +3685,38 @@ int configure_downstream_group(Config *config, bool http2_proxy,
auto hostport =
util::make_hostport(downstreamconf.balloc, addr.host, addr.port);
if (resolve_hostname(&addr.addr, addr.host.c_str(), addr.port,
downstreamconf.family, resolve_flags) == -1) {
LOG(FATAL) << "Resolving backend address failed: " << hostport;
return -1;
}
if (!addr.dns) {
if (resolve_hostname(&addr.addr, addr.host.c_str(), addr.port,
downstreamconf.family, resolve_flags) == -1) {
LOG(FATAL) << "Resolving backend address failed: " << hostport;
return -1;
}
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Resolved backend address: " << hostport << " -> "
<< util::to_numeric_addr(&addr.addr);
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Resolved backend address: " << hostport << " -> "
<< util::to_numeric_addr(&addr.addr);
}
} else {
LOG(INFO) << "Resolving backend address " << hostport
<< " takes place dynamically";
}
}
if (g.affinity == AFFINITY_IP) {
size_t idx = 0;
for (auto &addr : g.addrs) {
auto p = reinterpret_cast<uint8_t *>(&addr.addr.su);
rv = compute_affinity_hash(g.affinity_hash, idx,
StringRef{p, addr.addr.len});
StringRef key;
if (addr.dns) {
if (addr.host_unix) {
key = addr.host;
} else {
key = addr.hostport;
}
} else {
auto p = reinterpret_cast<uint8_t *>(&addr.addr.su);
key = StringRef{p, addr.addr.len};
}
rv = compute_affinity_hash(g.affinity_hash, idx, key);
if (rv != 0) {
return -1;
}

View File

@@ -312,6 +312,21 @@ constexpr auto SHRPX_OPT_ECDH_CURVES = StringRef::from_lit("ecdh-curves");
constexpr auto SHRPX_OPT_TLS_SCT_DIR = StringRef::from_lit("tls-sct-dir");
constexpr auto SHRPX_OPT_BACKEND_CONNECT_TIMEOUT =
StringRef::from_lit("backend-connect-timeout");
constexpr auto SHRPX_OPT_DNS_CACHE_TIMEOUT =
StringRef::from_lit("dns-cache-timeout");
constexpr auto SHRPX_OPT_DNS_LOOKUP_TIMEOUT =
StringRef::from_lit("dns-lookup-timeout");
constexpr auto SHRPX_OPT_DNS_MAX_TRY = StringRef::from_lit("dns-max-try");
constexpr auto SHRPX_OPT_FRONTEND_KEEP_ALIVE_TIMEOUT =
StringRef::from_lit("frontend-keep-alive-timeout");
constexpr auto SHRPX_OPT_PSK_SECRETS = StringRef::from_lit("psk-secrets");
constexpr auto SHRPX_OPT_CLIENT_PSK_SECRETS =
StringRef::from_lit("client-psk-secrets");
constexpr auto SHRPX_OPT_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST =
StringRef::from_lit("client-no-http2-cipher-black-list");
constexpr auto SHRPX_OPT_CLIENT_CIPHERS = StringRef::from_lit("client-ciphers");
constexpr auto SHRPX_OPT_ACCESSLOG_WRITE_EARLY =
StringRef::from_lit("accesslog-write-early");
constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8;
@@ -375,10 +390,13 @@ struct UpstreamAddr {
bool host_unix;
// true if TLS is enabled.
bool tls;
// true if client is supposed to send PROXY protocol v1 header.
bool accept_proxy_protocol;
int fd;
};
struct DownstreamAddrConfig {
// Resolved address if |dns| is false
Address addr;
// backend address. If |host_unix| is true, this is UNIX domain
// socket path. This must be NULL terminated string.
@@ -397,6 +415,8 @@ struct DownstreamAddrConfig {
// true if |host| contains UNIX domain socket path.
bool host_unix;
bool tls;
// true if dynamic DNS is enabled
bool dns;
};
// Mapping hash to idx which is an index into
@@ -410,7 +430,9 @@ struct AffinityHash {
struct DownstreamAddrGroupConfig {
DownstreamAddrGroupConfig(const StringRef &pattern)
: pattern(pattern), affinity(AFFINITY_NONE) {}
: pattern(pattern),
affinity(AFFINITY_NONE),
require_upstream_tls(false) {}
StringRef pattern;
std::vector<DownstreamAddrConfig> addrs;
@@ -419,6 +441,8 @@ struct DownstreamAddrGroupConfig {
std::vector<AffinityHash> affinity_hash;
// Session affinity
shrpx_session_affinity affinity;
// true if this group requires that client connection must be TLS.
bool require_upstream_tls;
};
struct TicketKey {
@@ -531,12 +555,23 @@ struct TLSConfig {
bool enabled;
} client_verify;
// Client private key and certificate used in backend connections.
// Client (backend connection) TLS configuration.
struct {
// Client PSK configuration
struct {
// identity must be NULL terminated string.
StringRef identity;
StringRef secret;
} psk;
StringRef private_key_file;
StringRef cert_file;
StringRef ciphers;
bool no_http2_cipher_black_list;
} client;
// PSK secrets. The key is identity, and the associated value is
// its secret.
std::map<StringRef, StringRef> psk_secrets;
// The list of additional TLS certificate pair
std::vector<TLSCertificate> subcerts;
std::vector<unsigned char> alpn_prefs;
@@ -657,6 +692,9 @@ struct LoggingConfig {
StringRef file;
// Send accesslog to syslog, ignoring accesslog_file.
bool syslog;
// Write accesslog when response headers are received from
// backend, rather than response body is received and sent.
bool write_early;
} access;
struct {
StringRef file;
@@ -758,12 +796,14 @@ struct ConnectionConfig {
ev_tstamp http2_read;
ev_tstamp read;
ev_tstamp write;
ev_tstamp idle_read;
} timeout;
struct {
RateLimitConfig read;
RateLimitConfig write;
} ratelimit;
size_t worker_connections;
// Deprecated. See UpstreamAddr.accept_proxy_protocol.
bool accept_proxy_protocol;
} upstream;
@@ -777,6 +817,16 @@ struct APIConfig {
bool enabled;
};
struct DNSConfig {
struct {
ev_tstamp cache;
ev_tstamp lookup;
} timeout;
// The number of tries name resolver makes before abandoning
// request.
size_t max_try;
};
struct Config {
Config()
: balloc(4096, 4096),
@@ -787,6 +837,7 @@ struct Config {
logging{},
conn{},
api{},
dns{},
num_worker{0},
padding{0},
rlimit_nofile{0},
@@ -815,6 +866,7 @@ struct Config {
LoggingConfig logging;
ConnectionConfig conn;
APIConfig api;
DNSConfig dns;
StringRef pid_file;
StringRef conf_path;
StringRef user;
@@ -845,6 +897,7 @@ enum {
SHRPX_OPTID_ACCESSLOG_FILE,
SHRPX_OPTID_ACCESSLOG_FORMAT,
SHRPX_OPTID_ACCESSLOG_SYSLOG,
SHRPX_OPTID_ACCESSLOG_WRITE_EARLY,
SHRPX_OPTID_ADD_FORWARDED,
SHRPX_OPTID_ADD_REQUEST_HEADER,
SHRPX_OPTID_ADD_RESPONSE_HEADER,
@@ -886,11 +939,17 @@ enum {
SHRPX_OPTID_CIPHERS,
SHRPX_OPTID_CLIENT,
SHRPX_OPTID_CLIENT_CERT_FILE,
SHRPX_OPTID_CLIENT_CIPHERS,
SHRPX_OPTID_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST,
SHRPX_OPTID_CLIENT_PRIVATE_KEY_FILE,
SHRPX_OPTID_CLIENT_PROXY,
SHRPX_OPTID_CLIENT_PSK_SECRETS,
SHRPX_OPTID_CONF,
SHRPX_OPTID_DAEMON,
SHRPX_OPTID_DH_PARAM_FILE,
SHRPX_OPTID_DNS_CACHE_TIMEOUT,
SHRPX_OPTID_DNS_LOOKUP_TIMEOUT,
SHRPX_OPTID_DNS_MAX_TRY,
SHRPX_OPTID_ECDH_CURVES,
SHRPX_OPTID_ERROR_PAGE,
SHRPX_OPTID_ERRORLOG_FILE,
@@ -914,6 +973,7 @@ enum {
SHRPX_OPTID_FRONTEND_HTTP2_SETTINGS_TIMEOUT,
SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS,
SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_SIZE,
SHRPX_OPTID_FRONTEND_KEEP_ALIVE_TIMEOUT,
SHRPX_OPTID_FRONTEND_NO_TLS,
SHRPX_OPTID_FRONTEND_READ_TIMEOUT,
SHRPX_OPTID_FRONTEND_WRITE_TIMEOUT,
@@ -945,6 +1005,7 @@ enum {
SHRPX_OPTID_PID_FILE,
SHRPX_OPTID_PRIVATE_KEY_FILE,
SHRPX_OPTID_PRIVATE_KEY_PASSWD_FILE,
SHRPX_OPTID_PSK_SECRETS,
SHRPX_OPTID_READ_BURST,
SHRPX_OPTID_READ_RATE,
SHRPX_OPTID_REQUEST_HEADER_FIELD_BUFFER,

View File

@@ -67,7 +67,9 @@ Connection::Connection(struct ev_loop *loop, int fd, SSL *ssl,
fd(fd),
tls_dyn_rec_warmup_threshold(tls_dyn_rec_warmup_threshold),
tls_dyn_rec_idle_timeout(tls_dyn_rec_idle_timeout),
proto(proto) {
proto(proto),
last_read(0.),
read_timeout(read_timeout) {
ev_io_init(&wev, writecb, fd, EV_WRITE);
ev_io_init(&rev, readcb, fd, EV_READ);
@@ -93,7 +95,8 @@ Connection::~Connection() { disconnect(); }
void Connection::disconnect() {
if (tls.ssl) {
SSL_set_shutdown(tls.ssl, SSL_RECEIVED_SHUTDOWN);
SSL_set_shutdown(tls.ssl,
SSL_get_shutdown(tls.ssl) | SSL_RECEIVED_SHUTDOWN);
ERR_clear_error();
if (tls.cached_session) {
@@ -138,7 +141,10 @@ void Connection::disconnect() {
void Connection::prepare_client_handshake() { SSL_set_connect_state(tls.ssl); }
void Connection::prepare_server_handshake() { SSL_set_accept_state(tls.ssl); }
void Connection::prepare_server_handshake() {
SSL_set_accept_state(tls.ssl);
tls.server_handshake = true;
}
// BIO implementation is inspired by openldap implementation:
// http://www.openldap.org/devel/cvsweb.cgi/~checkout~/libraries/libldap/tls_o.c
@@ -499,8 +505,14 @@ int Connection::write_tls_pending_handshake() {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "SSL/TLS handshake completed";
if (SSL_session_reused(tls.ssl)) {
LOG(INFO) << "SSL/TLS session reused";
nghttp2::ssl::TLSSessionInfo tls_info{};
if (nghttp2::ssl::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")
<< " session_id="
<< util::format_hex(tls_info.session_id,
tls_info.session_id_length);
}
}
@@ -527,7 +539,15 @@ int Connection::check_http2_requirement() {
}
return -1;
}
if (!get_config()->tls.no_http2_cipher_black_list &&
auto check_black_list = false;
if (tls.server_handshake) {
check_black_list = !get_config()->tls.no_http2_cipher_black_list;
} else {
check_black_list = !get_config()->tls.client.no_http2_cipher_black_list;
}
if (check_black_list &&
nghttp2::ssl::check_http2_cipher_black_list(tls.ssl)) {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "The negotiated cipher suite is in HTTP/2 cipher suite "
@@ -809,4 +829,27 @@ int Connection::get_tcp_hint(TCPHint *hint) const {
#endif // !defined(TCP_INFO) || !defined(TCP_NOTSENT_LOWAT)
}
void Connection::again_rt(ev_tstamp t) {
read_timeout = t;
rt.repeat = t;
ev_timer_again(loop, &rt);
last_read = ev_now(loop);
}
void Connection::again_rt() {
rt.repeat = read_timeout;
ev_timer_again(loop, &rt);
last_read = ev_now(loop);
}
bool Connection::expired_rt() {
auto delta = read_timeout - (ev_now(loop) - last_read);
if (delta < 1e-9) {
return true;
}
rt.repeat = delta;
ev_timer_again(loop, &rt);
return false;
}
} // namespace shrpx

View File

@@ -64,6 +64,8 @@ struct TLSConnection {
int handshake_state;
bool initial_handshake_done;
bool reneg_started;
// true if ssl is prepared to do handshake as server.
bool server_handshake;
};
struct TCPHint {
@@ -125,6 +127,17 @@ struct Connection {
int get_tcp_hint(TCPHint *hint) const;
// These functions are provided for read timer which is frequently
// restarted. We do a trick to make a bit more efficient than just
// calling ev_timer_again().
// Restarts read timer with timeout value |t|.
void again_rt(ev_tstamp t);
// Restarts read timer without chainging timeout.
void again_rt();
// Returns true if read timer expired.
bool expired_rt();
TLSConnection tls;
ev_io wev;
ev_io rev;
@@ -141,6 +154,11 @@ struct Connection {
// used in this object at the moment. The rest of the program may
// use this value when it is useful.
shrpx_proto proto;
// The point of time when last read is observed. Note: sinde we use
// |rt| as idle timer, the activity is not limited to read.
ev_tstamp last_read;
// Timeout for read timer |rt|.
ev_tstamp read_timeout;
};
// Creates BIO_method shared by all SSL objects. If nghttp2 is built

View File

@@ -499,7 +499,23 @@ void ConnectionHandler::cancel_ocsp_update() {
return;
}
kill(ocsp_.proc.pid, SIGTERM);
int rv;
rv = kill(ocsp_.proc.pid, SIGTERM);
if (rv != 0) {
auto error = errno;
LOG(ERROR) << "Could not send signal to OCSP query process: errno="
<< error;
}
while ((rv = waitpid(ocsp_.proc.pid, nullptr, 0)) == -1 && errno == EINTR)
;
if (rv == -1) {
auto error = errno;
LOG(ERROR) << "Error occurred while we were waiting for the completion of "
"OCSP query process: errno="
<< error;
}
}
// inspired by h2o_read_command function from h2o project:

351
src/shrpx_dns_resolver.cc Normal file
View File

@@ -0,0 +1,351 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2016 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "shrpx_dns_resolver.h"
#include <cstring>
#include <sys/time.h>
#include "shrpx_log.h"
#include "shrpx_connection.h"
#include "shrpx_config.h"
namespace shrpx {
namespace {
void sock_state_cb(void *data, int s, int read, int write) {
auto resolv = static_cast<DNSResolver *>(data);
if (resolv->get_status(nullptr) != DNS_STATUS_RUNNING) {
return;
}
if (read) {
resolv->start_rev(s);
} else {
resolv->stop_rev(s);
}
if (write) {
resolv->start_wev(s);
} else {
resolv->stop_wev(s);
}
}
} // namespace
namespace {
void host_cb(void *arg, int status, int timeouts, hostent *hostent) {
auto resolv = static_cast<DNSResolver *>(arg);
resolv->on_result(status, hostent);
}
} // namespace
namespace {
void process_result(DNSResolver *resolv) {
auto cb = resolv->get_complete_cb();
if (!cb) {
return;
}
Address result;
auto status = resolv->get_status(&result);
switch (status) {
case DNS_STATUS_OK:
case DNS_STATUS_ERROR:
cb(status, &result);
break;
}
// resolv may be deleted here.
}
} // namespace
namespace {
void readcb(struct ev_loop *loop, ev_io *w, int revents) {
auto resolv = static_cast<DNSResolver *>(w->data);
resolv->on_read(w->fd);
process_result(resolv);
}
} // namespace
namespace {
void writecb(struct ev_loop *loop, ev_io *w, int revents) {
auto resolv = static_cast<DNSResolver *>(w->data);
resolv->on_write(w->fd);
process_result(resolv);
}
} // namespace
namespace {
void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
auto resolv = static_cast<DNSResolver *>(w->data);
resolv->on_timeout();
process_result(resolv);
}
} // namespace
namespace {
void stop_ev(struct ev_loop *loop,
const std::vector<std::unique_ptr<ev_io>> &evs) {
for (auto &w : evs) {
ev_io_stop(loop, w.get());
}
}
} // namespace
DNSResolver::DNSResolver(struct ev_loop *loop)
: result_{},
loop_(loop),
channel_(nullptr),
family_(AF_UNSPEC),
status_(DNS_STATUS_IDLE) {
ev_timer_init(&timer_, timeoutcb, 0., 0.);
timer_.data = this;
}
DNSResolver::~DNSResolver() {
if (channel_) {
ares_destroy(channel_);
}
stop_ev(loop_, revs_);
stop_ev(loop_, wevs_);
ev_timer_stop(loop_, &timer_);
}
int DNSResolver::resolve(const StringRef &name, int family) {
if (status_ != DNS_STATUS_IDLE) {
return -1;
}
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Start resolving host " << name << " in IPv"
<< (family == AF_INET ? "4" : "6");
}
name_ = name;
family_ = family;
int rv;
auto &dnsconf = get_config()->dns;
ares_options opts{};
opts.sock_state_cb = sock_state_cb;
opts.sock_state_cb_data = this;
opts.timeout = static_cast<int>(dnsconf.timeout.lookup * 1000);
opts.tries = dnsconf.max_try;
auto optmask = ARES_OPT_SOCK_STATE_CB | ARES_OPT_TIMEOUTMS | ARES_OPT_TRIES;
ares_channel chan;
rv = ares_init_options(&chan, &opts, optmask);
if (rv != ARES_SUCCESS) {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "ares_init_options failed: " << ares_strerror(rv);
}
status_ = DNS_STATUS_ERROR;
return -1;
}
channel_ = chan;
status_ = DNS_STATUS_RUNNING;
ares_gethostbyname(channel_, name_.c_str(), family_, host_cb, this);
reset_timeout();
return 0;
}
int DNSResolver::on_read(int fd) { return handle_event(fd, ARES_SOCKET_BAD); }
int DNSResolver::on_write(int fd) { return handle_event(ARES_SOCKET_BAD, fd); }
int DNSResolver::on_timeout() {
return handle_event(ARES_SOCKET_BAD, ARES_SOCKET_BAD);
}
int DNSResolver::handle_event(int rfd, int wfd) {
if (status_ == DNS_STATUS_IDLE) {
return -1;
}
ares_process_fd(channel_, rfd, wfd);
switch (status_) {
case DNS_STATUS_RUNNING: {
reset_timeout();
return 0;
}
case DNS_STATUS_OK:
return 0;
case DNS_STATUS_ERROR:
return -1;
default:
// Unreachable
assert(0);
}
}
void DNSResolver::reset_timeout() {
if (status_ != DNS_STATUS_RUNNING) {
return;
}
timeval tvout;
auto tv = ares_timeout(channel_, nullptr, &tvout);
if (tv == nullptr) {
return;
}
// To avoid that timer_.repeat becomes 0, which makes ev_timer_again
// useless, add tiny fraction of time.
timer_.repeat = tv->tv_sec + tv->tv_usec / 1000000. + 1e-9;
ev_timer_again(loop_, &timer_);
}
int DNSResolver::get_status(Address *result) const {
if (status_ != DNS_STATUS_OK) {
return status_;
}
if (result) {
memcpy(result, &result_, sizeof(result_));
}
return status_;
}
namespace {
void start_ev(std::vector<std::unique_ptr<ev_io>> &evs, struct ev_loop *loop,
int fd, int event, IOCb cb, void *data) {
for (auto &w : evs) {
if (w->fd == fd) {
return;
}
}
for (auto &w : evs) {
if (w->fd == -1) {
ev_io_set(w.get(), fd, event);
ev_io_start(loop, w.get());
return;
}
}
auto w = make_unique<ev_io>();
ev_io_init(w.get(), cb, fd, event);
w->data = data;
ev_io_start(loop, w.get());
evs.emplace_back(std::move(w));
}
} // namespace
namespace {
void stop_ev(std::vector<std::unique_ptr<ev_io>> &evs, struct ev_loop *loop,
int fd, int event) {
for (auto &w : evs) {
if (w->fd == fd) {
ev_io_stop(loop, w.get());
ev_io_set(w.get(), -1, event);
return;
}
}
}
} // namespace
void DNSResolver::start_rev(int fd) {
start_ev(revs_, loop_, fd, EV_READ, readcb, this);
}
void DNSResolver::stop_rev(int fd) { stop_ev(revs_, loop_, fd, EV_READ); }
void DNSResolver::start_wev(int fd) {
start_ev(wevs_, loop_, fd, EV_WRITE, writecb, this);
}
void DNSResolver::stop_wev(int fd) { stop_ev(wevs_, loop_, fd, EV_WRITE); }
void DNSResolver::on_result(int status, hostent *hostent) {
stop_ev(loop_, revs_);
stop_ev(loop_, wevs_);
ev_timer_stop(loop_, &timer_);
if (status != ARES_SUCCESS) {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Name lookup for " << name_
<< " failed: " << ares_strerror(status);
}
status_ = DNS_STATUS_ERROR;
return;
}
auto ap = *hostent->h_addr_list;
if (!ap) {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Name lookup for " << name_ << "failed: no address returned";
}
status_ = DNS_STATUS_ERROR;
return;
}
switch (hostent->h_addrtype) {
case AF_INET:
status_ = DNS_STATUS_OK;
result_.len = sizeof(result_.su.in);
result_.su.in = {};
result_.su.in.sin_family = AF_INET;
#ifdef HAVE_SOCKADDR_IN_SIN_LEN
result_.su.in.sin_len = sizeof(result_.su.in);
#endif // HAVE_SOCKADDR_IN_SIN_LEN
memcpy(&result_.su.in.sin_addr, ap, sizeof(result_.su.in.sin_addr));
break;
case AF_INET6:
status_ = DNS_STATUS_OK;
result_.len = sizeof(result_.su.in6);
result_.su.in6 = {};
result_.su.in6.sin6_family = AF_INET6;
#ifdef HAVE_SOCKADDR_IN6_SIN6_LEN
result_.su.in6.sin6_len = sizeof(result_.su.in6);
#endif // HAVE_SOCKADDR_IN6_SIN6_LEN
memcpy(&result_.su.in6.sin6_addr, ap, sizeof(result_.su.in6.sin6_addr));
break;
default:
assert(0);
}
if (status_ == DNS_STATUS_OK) {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Name lookup succeeded: " << name_ << " -> "
<< util::numeric_name(&result_.su.sa, result_.len);
}
return;
}
status_ = DNS_STATUS_ERROR;
}
void DNSResolver::set_complete_cb(CompleteCb cb) {
completeCb_ = std::move(cb);
}
CompleteCb DNSResolver::get_complete_cb() const { return completeCb_; }
} // namespace shrpx

116
src/shrpx_dns_resolver.h Normal file
View File

@@ -0,0 +1,116 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2016 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef SHRPX_DNS_RESOLVER_H
#define SHRPX_DNS_RESOLVER_H
#include "shrpx.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <vector>
#include <ev.h>
#include <ares.h>
#include "template.h"
#include "network.h"
using namespace nghttp2;
namespace shrpx {
enum DNSResolverStatus {
// Resolver is in initial status
DNS_STATUS_IDLE,
// Resolver is currently resolving host name
DNS_STATUS_RUNNING,
// Resolver successfully resolved host name
DNS_STATUS_OK,
// Resolver failed to resolve host name
DNS_STATUS_ERROR,
};
// Callback function called when host name lookup is finished.
// |status| is either DNS_STATUS_OK, or DNS_STATUS_ERROR. If |status|
// is DNS_STATUS_OK, |result| points to the resolved address. Note
// that port portion of |result| is undefined, and must be initialized
// by application. This callback function is not called if name
// lookup finishes in DNSResolver::resolve() completely. In this
// case, application should call DNSResolver::get_status() to get
// current status and result. In other words, callback is called if
// get_status() returns DNS_STATUS_RUNNING.
using CompleteCb = std::function<void(int status, const Address *result)>;
// DNSResolver is asynchronous name resolver, backed by c-ares
// library.
class DNSResolver {
public:
DNSResolver(struct ev_loop *loop);
~DNSResolver();
// Starts resolving hostname |name|.
int resolve(const StringRef &name, int family);
// Returns status. If status_ is DNS_STATUS_SUCCESS && |result| is
// not nullptr, |*result| is filled.
int get_status(Address *result) const;
// Sets callback function when name lookup finishes. The callback
// function is called in a way that it can destroy this DNSResolver.
void set_complete_cb(CompleteCb cb);
CompleteCb get_complete_cb() const;
// Calls these functions when read/write event occurred respectively.
int on_read(int fd);
int on_write(int fd);
int on_timeout();
// Calls this function when DNS query finished.
void on_result(int staus, hostent *hostent);
void reset_timeout();
void start_rev(int fd);
void stop_rev(int fd);
void start_wev(int fd);
void stop_wev(int fd);
private:
int handle_event(int rfd, int wfd);
std::vector<std::unique_ptr<ev_io>> revs_, wevs_;
Address result_;
CompleteCb completeCb_;
ev_timer timer_;
StringRef name_;
struct ev_loop *loop_;
// ares_channel is pointer type
ares_channel channel_;
// AF_INET or AF_INET6. AF_INET for A record lookup, and AF_INET6
// for AAAA record lookup.
int family_;
int status_;
};
} // namespace shrpx
#endif // SHRPX_DNS_RESOLVER_H

325
src/shrpx_dns_tracker.cc Normal file
View File

@@ -0,0 +1,325 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2016 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "shrpx_dns_tracker.h"
#include "shrpx_config.h"
#include "util.h"
namespace shrpx {
namespace {
void gccb(struct ev_loop *loop, ev_timer *w, int revents) {
auto dns_tracker = static_cast<DNSTracker *>(w->data);
dns_tracker->gc();
}
} // namespace
DNSTracker::DNSTracker(struct ev_loop *loop) : loop_(loop) {
ev_timer_init(&gc_timer_, gccb, 0., 12_h);
gc_timer_.data = this;
}
DNSTracker::~DNSTracker() {
ev_timer_stop(loop_, &gc_timer_);
for (auto &p : ents_) {
auto &qlist = p.second.qlist;
while (!qlist.empty()) {
auto head = qlist.head;
qlist.remove(head);
head->status = DNS_STATUS_ERROR;
head->in_qlist = false;
// TODO Not sure we should call callback here, or it is even be
// safe to do that.
}
}
}
ResolverEntry DNSTracker::make_entry(std::unique_ptr<DualDNSResolver> resolv,
ImmutableString host, int status,
const Address *result) {
auto &dnsconf = get_config()->dns;
auto ent = ResolverEntry{};
ent.resolv = std::move(resolv);
ent.host = std::move(host);
ent.status = status;
switch (status) {
case DNS_STATUS_ERROR:
case DNS_STATUS_OK:
ent.expiry = ev_now(loop_) + dnsconf.timeout.cache;
break;
}
if (result) {
ent.result = *result;
}
return ent;
}
void DNSTracker::update_entry(ResolverEntry &ent,
std::unique_ptr<DualDNSResolver> resolv,
int status, const Address *result) {
auto &dnsconf = get_config()->dns;
ent.resolv = std::move(resolv);
ent.status = status;
switch (status) {
case DNS_STATUS_ERROR:
case DNS_STATUS_OK:
ent.expiry = ev_now(loop_) + dnsconf.timeout.cache;
break;
}
if (result) {
ent.result = *result;
}
}
int DNSTracker::resolve(Address *result, DNSQuery *dnsq) {
int rv;
auto it = ents_.find(dnsq->host);
if (it == std::end(ents_)) {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "DNS entry not found for " << dnsq->host;
}
auto resolv = make_unique<DualDNSResolver>(loop_);
auto host_copy =
ImmutableString{std::begin(dnsq->host), std::end(dnsq->host)};
auto host = StringRef{host_copy};
rv = resolv->resolve(host);
if (rv != 0) {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Name lookup failed for " << host;
}
ents_.emplace(host, make_entry(nullptr, std::move(host_copy),
DNS_STATUS_ERROR, nullptr));
start_gc_timer();
return DNS_STATUS_ERROR;
}
rv = resolv->get_status(result);
switch (rv) {
case DNS_STATUS_ERROR: {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Name lookup failed for " << host;
}
ents_.emplace(host, make_entry(nullptr, std::move(host_copy),
DNS_STATUS_ERROR, nullptr));
start_gc_timer();
return DNS_STATUS_ERROR;
}
case DNS_STATUS_OK: {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Name lookup succeeded: " << host << " -> "
<< util::numeric_name(&result->su.sa, result->len);
}
ents_.emplace(host, make_entry(nullptr, std::move(host_copy),
DNS_STATUS_OK, result));
start_gc_timer();
return DNS_STATUS_OK;
}
case DNS_STATUS_RUNNING: {
assert(rv == DNS_STATUS_RUNNING);
auto p = ents_.emplace(host,
make_entry(std::move(resolv), std::move(host_copy),
DNS_STATUS_RUNNING, nullptr));
start_gc_timer();
auto &ent = (*p.first).second;
add_to_qlist(ent, dnsq);
return DNS_STATUS_RUNNING;
}
default:
assert(0);
}
}
auto &ent = (*it).second;
if (ent.status != DNS_STATUS_RUNNING && ent.expiry < ev_now(loop_)) {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "DNS entry found for " << dnsq->host
<< ", but it has been expired";
}
auto resolv = make_unique<DualDNSResolver>(loop_);
auto host = StringRef{ent.host};
rv = resolv->resolve(host);
if (rv != 0) {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Name lookup failed for " << host;
}
update_entry(ent, nullptr, DNS_STATUS_ERROR, nullptr);
return DNS_STATUS_ERROR;
}
rv = resolv->get_status(result);
switch (rv) {
case DNS_STATUS_ERROR: {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Name lookup failed for " << host;
}
update_entry(ent, nullptr, DNS_STATUS_ERROR, nullptr);
return DNS_STATUS_ERROR;
}
case DNS_STATUS_OK: {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Name lookup succeeded: " << host << " -> "
<< util::numeric_name(&result->su.sa, result->len);
}
update_entry(ent, nullptr, DNS_STATUS_OK, result);
return DNS_STATUS_OK;
}
case DNS_STATUS_RUNNING: {
update_entry(ent, std::move(resolv), DNS_STATUS_RUNNING, nullptr);
add_to_qlist(ent, dnsq);
return DNS_STATUS_RUNNING;
}
default:
assert(0);
}
}
switch (ent.status) {
case DNS_STATUS_RUNNING:
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Waiting for name lookup complete for " << dnsq->host;
}
ent.qlist.append(dnsq);
dnsq->in_qlist = true;
return DNS_STATUS_RUNNING;
case DNS_STATUS_ERROR:
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Name lookup failed for " << dnsq->host << " (cached)";
}
return DNS_STATUS_ERROR;
case DNS_STATUS_OK:
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Name lookup succeeded (cached): " << dnsq->host << " -> "
<< util::numeric_name(&ent.result.su.sa, ent.result.len);
}
if (result) {
memcpy(result, &ent.result, sizeof(*result));
}
return DNS_STATUS_OK;
default:
assert(0);
}
}
void DNSTracker::add_to_qlist(ResolverEntry &ent, DNSQuery *dnsq) {
auto loop = loop_;
ent.resolv->set_complete_cb([&ent, loop](int status, const Address *result) {
auto &qlist = ent.qlist;
while (!qlist.empty()) {
auto head = qlist.head;
qlist.remove(head);
head->status = status;
head->in_qlist = false;
auto cb = head->cb;
cb(status, result);
}
auto &dnsconf = get_config()->dns;
ent.resolv.reset();
ent.status = status;
ent.expiry = ev_now(loop) + dnsconf.timeout.cache;
if (ent.status == DNS_STATUS_OK) {
ent.result = *result;
}
});
ent.qlist.append(dnsq);
dnsq->in_qlist = true;
}
void DNSTracker::cancel(DNSQuery *dnsq) {
if (!dnsq->in_qlist) {
return;
}
auto it = ents_.find(dnsq->host);
if (it == std::end(ents_)) {
return;
}
auto &ent = (*it).second;
ent.qlist.remove(dnsq);
dnsq->in_qlist = false;
}
void DNSTracker::start_gc_timer() {
if (ev_is_active(&gc_timer_)) {
return;
}
ev_timer_again(loop_, &gc_timer_);
}
void DNSTracker::gc() {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Starting removing expired DNS cache entries";
}
auto now = ev_now(loop_);
for (auto it = std::begin(ents_); it != std::end(ents_);) {
auto &ent = (*it).second;
if (ent.expiry >= now) {
++it;
continue;
}
it = ents_.erase(it);
}
if (ents_.empty()) {
ev_timer_stop(loop_, &gc_timer_);
}
}
} // namespace shrpx

116
src/shrpx_dns_tracker.h Normal file
View File

@@ -0,0 +1,116 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2016 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef SHRPX_DNS_TRACKER_H
#define SHRPX_DNS_TRACKER_H
#include "shrpx.h"
#include <map>
#include "shrpx_dual_dns_resolver.h"
using namespace nghttp2;
namespace shrpx {
struct DNSQuery {
DNSQuery(StringRef host, CompleteCb cb)
: host(std::move(host)),
cb(std::move(cb)),
dlnext(nullptr),
dlprev(nullptr),
status(DNS_STATUS_IDLE),
in_qlist(false) {}
// Host name we lookup for.
StringRef host;
// Callback function called when name lookup finished. This
// callback is not called if name lookup finishes within
// DNSTracker::resolve().
CompleteCb cb;
DNSQuery *dlnext, *dlprev;
int status;
// true if this object is in linked list ResolverEntry::qlist.
bool in_qlist;
};
struct ResolverEntry {
// Host name this entry lookups for.
ImmutableString host;
// DNS resolver. Only non-nullptr if status is DNS_STATUS_RUNNING.
std::unique_ptr<DualDNSResolver> resolv;
// DNSQuery interested in this name lookup result. The result is
// notified to them all.
DList<DNSQuery> qlist;
// Use the same enum with DNSResolverStatus
int status;
// result and its expiry time
Address result;
// time point when cached result expires.
ev_tstamp expiry;
};
class DNSTracker {
public:
DNSTracker(struct ev_loop *loop);
~DNSTracker();
// Lookups host name described in |dnsq|. If name lookup finishes
// within this function (either it came from /etc/hosts, host name
// is numeric, lookup result is cached, etc), it returns
// DNS_STATUS_OK or DNS_STATUS_ERROR. If lookup is successful,
// DNS_STATUS_OK is returned, and |result| is filled. If lookup
// failed, DNS_STATUS_ERROR is returned. If name lookup is being
// done background, it returns DNS_STATUS_RUNNING. Its completion
// is notified by calling dnsq->cb.
int resolve(Address *result, DNSQuery *dnsq);
// Cancels name lookup requested by |dnsq|.
void cancel(DNSQuery *dnsq);
// Removes expired entries from ents_.
void gc();
// Starts GC timer.
void start_gc_timer();
private:
ResolverEntry make_entry(std::unique_ptr<DualDNSResolver> resolv,
ImmutableString host, int status,
const Address *result);
void update_entry(ResolverEntry &ent, std::unique_ptr<DualDNSResolver> resolv,
int status, const Address *result);
void add_to_qlist(ResolverEntry &ent, DNSQuery *dnsq);
std::map<StringRef, ResolverEntry> ents_;
// Periodically iterates ents_, and removes expired entries to avoid
// excessive use of memory. Since only backend API can potentially
// increase memory consumption, interval could be very long.
ev_timer gc_timer_;
struct ev_loop *loop_;
};
} // namespace shrpx
#endif // SHRPX_DNS_TRACKER_H

View File

@@ -138,7 +138,8 @@ Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool,
chunked_response_(false),
expect_final_response_(false),
request_pending_(false),
request_header_sent_(false) {
request_header_sent_(false),
accesslog_written_(false) {
auto &timeoutconf = get_config()->http2.timeout;
@@ -350,10 +351,8 @@ void Downstream::crumble_request_cookie(std::vector<nghttp2_nv> &nva) {
}
namespace {
void add_header(bool &key_prev, size_t &sum, HeaderRefs &headers,
const StringRef &name, const StringRef &value, bool no_index,
int32_t token) {
key_prev = true;
void add_header(size_t &sum, HeaderRefs &headers, const StringRef &name,
const StringRef &value, bool no_index, int32_t token) {
sum += name.size() + value.size();
headers.emplace_back(name, value, no_index, token);
}
@@ -446,14 +445,14 @@ const HeaderRefs::value_type *FieldStore::header(const StringRef &name) const {
void FieldStore::add_header_token(const StringRef &name, const StringRef &value,
bool no_index, int32_t token) {
shrpx::add_header(header_key_prev_, buffer_size_, headers_, name, value,
no_index, token);
shrpx::add_header(buffer_size_, headers_, name, value, no_index, token);
}
void FieldStore::alloc_add_header_name(const StringRef &name) {
auto name_ref = alloc_header_name(balloc_, name);
auto token = http2::lookup_token(name_ref);
add_header_token(name_ref, StringRef{}, false, token);
header_key_prev_ = true;
}
void FieldStore::append_last_header_key(const char *data, size_t len) {
@@ -476,14 +475,14 @@ void FieldStore::add_trailer_token(const StringRef &name,
int32_t token) {
// Header size limit should be applied to all header and trailer
// fields combined.
shrpx::add_header(trailer_key_prev_, buffer_size_, trailers_, name, value,
no_index, token);
shrpx::add_header(buffer_size_, trailers_, name, value, no_index, token);
}
void FieldStore::alloc_add_trailer_name(const StringRef &name) {
auto name_ref = alloc_header_name(balloc_, name);
auto token = http2::lookup_token(name_ref);
add_trailer_token(name_ref, StringRef{}, false, token);
trailer_key_prev_ = true;
}
void FieldStore::append_last_trailer_key(const char *data, size_t len) {
@@ -908,7 +907,9 @@ void Downstream::disable_downstream_wtimer() {
disable_timer(loop, &downstream_wtimer_);
}
bool Downstream::accesslog_ready() const { return resp_.http_status > 0; }
bool Downstream::accesslog_ready() const {
return !accesslog_written_ && resp_.http_status > 0;
}
void Downstream::add_retry() { ++num_retry_; }
@@ -924,6 +925,10 @@ bool Downstream::get_request_pending() const { return request_pending_; }
void Downstream::set_request_header_sent(bool f) { request_header_sent_ = f; }
bool Downstream::get_request_header_sent() const {
return request_header_sent_;
}
bool Downstream::request_submission_ready() const {
return (request_state_ == Downstream::HEADER_COMPLETE ||
request_state_ == Downstream::MSG_COMPLETE) &&
@@ -983,4 +988,6 @@ void Downstream::set_addr(const DownstreamAddr *addr) { addr_ = addr; }
const DownstreamAddr *Downstream::get_addr() const { return addr_; }
void Downstream::set_accesslog_written(bool f) { accesslog_written_ = f; }
} // namespace shrpx

View File

@@ -76,7 +76,6 @@ public:
// Returns pointer to the header field with the name |name|. If
// multiple header have |name| as name, return last occurrence from
// the beginning. If no such header is found, returns nullptr.
// This function must be called after headers are indexed
const HeaderRefs::value_type *header(int32_t token) const;
HeaderRefs::value_type *header(int32_t token);
// Returns pointer to the header field with the name |name|. If no
@@ -153,6 +152,8 @@ struct Request {
}
FieldStore fs;
// Timestamp when all request header fields are received.
std::shared_ptr<Timestamp> tstamp;
// Request scheme. For HTTP/2, this is :scheme header field value.
// For HTTP/1.1, this is deduced from URI or connection.
StringRef scheme;
@@ -313,6 +314,7 @@ public:
void set_request_pending(bool f);
bool get_request_pending() const;
void set_request_header_sent(bool f);
bool get_request_header_sent() const;
// Returns true if request is ready to be submitted to downstream.
// When sending pending request, get_request_pending() should be
// checked too because this function may return true when
@@ -404,6 +406,8 @@ public:
const DownstreamAddr *get_addr() const;
void set_accesslog_written(bool f);
enum {
EVENT_ERROR = 0x1,
EVENT_TIMEOUT = 0x2,
@@ -487,6 +491,8 @@ private:
bool request_pending_;
// true if downstream request header is considered to be sent.
bool request_header_sent_;
// true if access.log has been written.
bool accesslog_written_;
};
} // namespace shrpx

View File

@@ -0,0 +1,87 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2016 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "shrpx_dual_dns_resolver.h"
namespace shrpx {
DualDNSResolver::DualDNSResolver(struct ev_loop *loop)
: resolv4_(loop), resolv6_(loop) {
auto cb = [this](int, const Address *) {
int rv;
Address result;
rv = this->get_status(&result);
switch (rv) {
case DNS_STATUS_ERROR:
case DNS_STATUS_OK:
break;
default:
return;
}
auto cb = this->get_complete_cb();
cb(rv, &result);
};
resolv4_.set_complete_cb(cb);
resolv6_.set_complete_cb(cb);
}
int DualDNSResolver::resolve(const StringRef &host) {
int rv4, rv6;
rv4 = resolv4_.resolve(host, AF_INET);
rv6 = resolv6_.resolve(host, AF_INET6);
if (rv4 != 0 && rv6 != 0) {
return -1;
}
return 0;
}
CompleteCb DualDNSResolver::get_complete_cb() const { return complete_cb_; }
void DualDNSResolver::set_complete_cb(CompleteCb cb) { complete_cb_ = cb; }
int DualDNSResolver::get_status(Address *result) const {
int rv4, rv6;
rv6 = resolv6_.get_status(result);
if (rv6 == DNS_STATUS_OK) {
return DNS_STATUS_OK;
}
rv4 = resolv4_.get_status(result);
if (rv4 == DNS_STATUS_OK) {
return DNS_STATUS_OK;
}
if (rv4 == DNS_STATUS_RUNNING || rv6 == DNS_STATUS_RUNNING) {
return DNS_STATUS_RUNNING;
}
if (rv4 == DNS_STATUS_ERROR || rv6 == DNS_STATUS_ERROR) {
return DNS_STATUS_ERROR;
}
return DNS_STATUS_IDLE;
}
} // namespace shrpx

View File

@@ -0,0 +1,63 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2016 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef SHRPX_DUAL_DNS_RESOLVER_H
#define SHRPX_DUAL_DNS_RESOLVER_H
#include "shrpx.h"
#include <ev.h>
#include "shrpx_dns_resolver.h"
using namespace nghttp2;
namespace shrpx {
// DualDNSResolver performs name resolution for both A and AAAA
// records at the same time. The first successful return (or if we
// have both successful results, prefer to AAAA) is chosen. This is
// wrapper around 2 DNSResolver inside. resolve(), get_status(), and
// how CompleteCb is called have the same semantics with DNSResolver.
class DualDNSResolver {
public:
DualDNSResolver(struct ev_loop *loop);
// Resolves |host|. |host| must be NULL-terminated string.
int resolve(const StringRef &host);
CompleteCb get_complete_cb() const;
void set_complete_cb(CompleteCb cb);
int get_status(Address *result) const;
private:
// For A record
DNSResolver resolv4_;
// For AAAA record
DNSResolver resolv6_;
CompleteCb complete_cb_;
};
} // namespace shrpx
#endif // SHRPX_DUAL_DNS_RESOLVER_H

View File

@@ -37,6 +37,7 @@ enum ErrorCode {
SHRPX_ERR_EOF = -101,
SHRPX_ERR_INPROGRESS = -102,
SHRPX_ERR_DCONN_CANCELED = -103,
SHRPX_ERR_RETRY = -104,
};
} // namespace shrpx

View File

@@ -83,7 +83,7 @@ int exec_read_command(Process &proc, char *const argv[]) {
auto error = errno;
LOG(FATAL) << "Unblocking all signals failed: errno=" << error;
_Exit(EXIT_FAILURE);
nghttp2_Exit(EXIT_FAILURE);
}
dup2(pfd[1], 1);
@@ -94,7 +94,7 @@ int exec_read_command(Process &proc, char *const argv[]) {
auto error = errno;
LOG(ERROR) << "Could not execute command: " << argv[0]
<< ", execve() faild, errno=" << error;
_Exit(EXIT_FAILURE);
nghttp2_Exit(EXIT_FAILURE);
}
// unreachable
}
@@ -111,7 +111,7 @@ int exec_read_command(Process &proc, char *const argv[]) {
auto error = errno;
LOG(FATAL) << "Restoring all signals failed: errno=" << error;
_Exit(EXIT_FAILURE);
nghttp2_Exit(EXIT_FAILURE);
}
if (pid == -1) {

View File

@@ -45,11 +45,14 @@ StringRef create_error_html(BlockAllocator &balloc, unsigned int http_status) {
}
}
auto status_string = http2::get_status_string(balloc, http_status);
auto status_string = http2::stringify_status(balloc, http_status);
auto reason_phrase = http2::get_reason_phrase(http_status);
return concat_string_ref(
balloc, StringRef::from_lit(R"(<!DOCTYPE html><html lang="en"><title>)"),
status_string, StringRef::from_lit("</title><body><h1>"), status_string,
status_string, StringRef::from_lit(" "), reason_phrase,
StringRef::from_lit("</title><body><h1>"), status_string,
StringRef::from_lit(" "), reason_phrase,
StringRef::from_lit("</h1><footer>"), httpconf.server_name,
StringRef::from_lit("</footer></body></html>"));
}

View File

@@ -402,7 +402,7 @@ int Http2DownstreamConnection::push_request_headers() {
// HTTP/1 upstream request can contain keyword other than
// "trailers". We just forward "trailers".
// TODO more strict handling required here.
if (te && util::strifind(te->value, StringRef::from_lit("trailers"))) {
if (te && http2::contains_trailers(te->value)) {
nva.push_back(http2::make_nv_ll("te", "trailers"));
}

View File

@@ -95,7 +95,7 @@ void settings_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
SSLOG(INFO, http2session) << "SETTINGS timeout";
}
downstream_failure(http2session->get_addr());
downstream_failure(http2session->get_addr(), http2session->get_raddr());
if (http2session->terminate_session(NGHTTP2_SETTINGS_TIMEOUT) != 0) {
delete http2session;
@@ -111,6 +111,10 @@ void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
auto conn = static_cast<Connection *>(w->data);
auto http2session = static_cast<Http2Session *>(conn->data);
if (w == &conn->rt && !conn->expired_rt()) {
return;
}
if (LOG_ENABLED(INFO)) {
SSLOG(INFO, http2session) << "Timeout";
}
@@ -198,6 +202,7 @@ Http2Session::Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx,
group_(group),
addr_(addr),
session_(nullptr),
raddr_(nullptr),
state_(DISCONNECTED),
connection_check_state_(CONNECTION_CHECK_NONE),
freelist_zone_(FREELIST_ZONE_NONE) {
@@ -240,6 +245,11 @@ int Http2Session::disconnect(bool hard) {
wb_.reset();
if (dns_query_) {
auto dns_tracker = worker_->get_dns_tracker();
dns_tracker->cancel(dns_query_.get());
}
conn_.rlimit.stopw();
conn_.wlimit.stopw();
@@ -298,12 +308,47 @@ int Http2Session::disconnect(bool hard) {
return 0;
}
int Http2Session::resolve_name() {
int rv;
auto dns_query = make_unique<DNSQuery>(
addr_->host, [this](int status, const Address *result) {
int rv;
if (status == DNS_STATUS_OK) {
*resolved_addr_ = *result;
util::set_port(*this->resolved_addr_, this->addr_->port);
}
rv = this->initiate_connection();
if (rv != 0) {
delete this;
}
});
resolved_addr_ = make_unique<Address>();
auto dns_tracker = worker_->get_dns_tracker();
rv = dns_tracker->resolve(resolved_addr_.get(), dns_query.get());
switch (rv) {
case DNS_STATUS_ERROR:
return -1;
case DNS_STATUS_RUNNING:
dns_query_ = std::move(dns_query);
state_ = RESOLVING_NAME;
return 0;
case DNS_STATUS_OK:
util::set_port(*resolved_addr_, addr_->port);
return 0;
default:
assert(0);
}
}
int Http2Session::initiate_connection() {
int rv = 0;
auto worker_blocker = worker_->get_connect_blocker();
if (state_ == DISCONNECTED) {
if (state_ == DISCONNECTED || state_ == RESOLVING_NAME) {
if (worker_blocker->blocked()) {
if (LOG_ENABLED(INFO)) {
SSLOG(INFO, this)
@@ -346,6 +391,8 @@ int Http2Session::initiate_connection() {
return -1;
}
raddr_ = &proxy.addr;
worker_blocker->on_success();
ev_io_set(&conn_.rev, conn_.fd, EV_READ);
@@ -370,36 +417,68 @@ int Http2Session::initiate_connection() {
return 0;
}
if (state_ == DISCONNECTED || state_ == PROXY_CONNECTED) {
if (state_ == DISCONNECTED || state_ == PROXY_CONNECTED ||
state_ == RESOLVING_NAME) {
if (LOG_ENABLED(INFO)) {
SSLOG(INFO, this) << "Connecting to downstream server";
if (state_ != RESOLVING_NAME) {
SSLOG(INFO, this) << "Connecting to downstream server";
}
}
if (addr_->tls) {
assert(ssl_ctx_);
auto ssl = ssl::create_ssl(ssl_ctx_);
if (!ssl) {
return -1;
if (state_ != RESOLVING_NAME) {
auto ssl = ssl::create_ssl(ssl_ctx_);
if (!ssl) {
return -1;
}
ssl::setup_downstream_http2_alpn(ssl);
conn_.set_ssl(ssl);
auto sni_name =
addr_->sni.empty() ? StringRef{addr_->host} : StringRef{addr_->sni};
if (!util::numeric_host(sni_name.c_str())) {
// TLS extensions: SNI. There is no documentation about the return
// code for this function (actually this is macro wrapping SSL_ctrl
// at the time of this writing).
SSL_set_tlsext_host_name(conn_.tls.ssl, sni_name.c_str());
}
auto tls_session = ssl::reuse_tls_session(addr_->tls_session_cache);
if (tls_session) {
SSL_set_session(conn_.tls.ssl, tls_session);
SSL_SESSION_free(tls_session);
}
}
ssl::setup_downstream_http2_alpn(ssl);
conn_.set_ssl(ssl);
auto sni_name =
addr_->sni.empty() ? StringRef{addr_->host} : StringRef{addr_->sni};
if (!util::numeric_host(sni_name.c_str())) {
// TLS extensions: SNI. There is no documentation about the return
// code for this function (actually this is macro wrapping SSL_ctrl
// at the time of this writing).
SSL_set_tlsext_host_name(conn_.tls.ssl, sni_name.c_str());
if (state_ == DISCONNECTED) {
if (addr_->dns) {
rv = resolve_name();
if (rv != 0) {
downstream_failure(addr_, nullptr);
return -1;
}
if (state_ == RESOLVING_NAME) {
return 0;
}
raddr_ = resolved_addr_.get();
} else {
raddr_ = &addr_->addr;
}
}
auto tls_session = ssl::reuse_tls_session(addr_->tls_session_cache);
if (tls_session) {
SSL_set_session(conn_.tls.ssl, tls_session);
SSL_SESSION_free(tls_session);
if (state_ == RESOLVING_NAME) {
if (dns_query_->status == DNS_STATUS_ERROR) {
downstream_failure(addr_, nullptr);
return -1;
}
assert(dns_query_->status == DNS_STATUS_OK);
state_ = DISCONNECTED;
dns_query_.reset();
raddr_ = resolved_addr_.get();
}
// If state_ == PROXY_CONNECTED, we has connected to the proxy
@@ -407,12 +486,11 @@ int Http2Session::initiate_connection() {
if (state_ == DISCONNECTED) {
assert(conn_.fd == -1);
conn_.fd =
util::create_nonblock_socket(addr_->addr.su.storage.ss_family);
conn_.fd = util::create_nonblock_socket(raddr_->su.storage.ss_family);
if (conn_.fd == -1) {
auto error = errno;
SSLOG(WARN, this)
<< "socket() failed; addr=" << util::to_numeric_addr(&addr_->addr)
<< "socket() failed; addr=" << util::to_numeric_addr(raddr_)
<< ", errno=" << error;
worker_blocker->on_failure();
@@ -423,15 +501,14 @@ int Http2Session::initiate_connection() {
rv = connect(conn_.fd,
// TODO maybe not thread-safe?
const_cast<sockaddr *>(&addr_->addr.su.sa),
addr_->addr.len);
const_cast<sockaddr *>(&raddr_->su.sa), raddr_->len);
if (rv != 0 && errno != EINPROGRESS) {
auto error = errno;
SSLOG(WARN, this) << "connect() failed; addr="
<< util::to_numeric_addr(&addr_->addr)
<< ", errno=" << error;
SSLOG(WARN, this)
<< "connect() failed; addr=" << util::to_numeric_addr(raddr_)
<< ", errno=" << error;
downstream_failure(addr_);
downstream_failure(addr_, raddr_);
return -1;
}
@@ -441,17 +518,44 @@ int Http2Session::initiate_connection() {
conn_.prepare_client_handshake();
} else {
if (state_ == DISCONNECTED) {
// Without TLS and proxy.
if (addr_->dns) {
rv = resolve_name();
if (rv != 0) {
downstream_failure(addr_, nullptr);
return -1;
}
if (state_ == RESOLVING_NAME) {
return 0;
}
raddr_ = resolved_addr_.get();
} else {
raddr_ = &addr_->addr;
}
}
if (state_ == RESOLVING_NAME) {
if (dns_query_->status == DNS_STATUS_ERROR) {
downstream_failure(addr_, nullptr);
return -1;
}
assert(dns_query_->status == DNS_STATUS_OK);
state_ = DISCONNECTED;
dns_query_.reset();
raddr_ = resolved_addr_.get();
}
if (state_ == DISCONNECTED) {
// Without TLS and proxy.
assert(conn_.fd == -1);
conn_.fd =
util::create_nonblock_socket(addr_->addr.su.storage.ss_family);
conn_.fd = util::create_nonblock_socket(raddr_->su.storage.ss_family);
if (conn_.fd == -1) {
auto error = errno;
SSLOG(WARN, this)
<< "socket() failed; addr=" << util::to_numeric_addr(&addr_->addr)
<< "socket() failed; addr=" << util::to_numeric_addr(raddr_)
<< ", errno=" << error;
worker_blocker->on_failure();
@@ -460,15 +564,15 @@ int Http2Session::initiate_connection() {
worker_blocker->on_success();
rv = connect(conn_.fd, const_cast<sockaddr *>(&addr_->addr.su.sa),
addr_->addr.len);
rv = connect(conn_.fd, const_cast<sockaddr *>(&raddr_->su.sa),
raddr_->len);
if (rv != 0 && errno != EINPROGRESS) {
auto error = errno;
SSLOG(WARN, this) << "connect() failed; addr="
<< util::to_numeric_addr(&addr_->addr)
<< ", errno=" << error;
SSLOG(WARN, this)
<< "connect() failed; addr=" << util::to_numeric_addr(raddr_)
<< ", errno=" << error;
downstream_failure(addr_);
downstream_failure(addr_, raddr_);
return -1;
}
@@ -477,26 +581,22 @@ int Http2Session::initiate_connection() {
}
}
write_ = &Http2Session::connected;
on_write_ = &Http2Session::downstream_write;
on_read_ = &Http2Session::downstream_read;
// We have been already connected when no TLS and proxy is used.
if (state_ != CONNECTED) {
state_ = CONNECTING;
conn_.wlimit.startw();
conn_.wt.repeat = downstreamconf.timeout.connect;
ev_timer_again(conn_.loop, &conn_.wt);
} else {
conn_.rlimit.startw();
if (addr_->num_dconn == 0) {
ev_timer_again(conn_.loop, &conn_.rt);
}
if (state_ == PROXY_CONNECTED) {
return connected();
}
write_ = &Http2Session::connected;
state_ = CONNECTING;
conn_.wlimit.startw();
conn_.wt.repeat = downstreamconf.timeout.connect;
ev_timer_again(conn_.loop, &conn_.wt);
return 0;
}
@@ -538,7 +638,7 @@ int htp_hdrs_completecb(http_parser *htp) {
} // namespace
namespace {
http_parser_settings htp_hooks = {
constexpr http_parser_settings htp_hooks = {
nullptr, // http_cb on_message_begin;
nullptr, // http_data_cb on_url;
nullptr, // http_data_cb on_status;
@@ -615,8 +715,6 @@ int Http2Session::downstream_connect_proxy() {
void Http2Session::add_downstream_connection(Http2DownstreamConnection *dconn) {
dconns_.append(dconn);
++addr_->num_dconn;
stop_read_timer();
}
void Http2Session::remove_downstream_connection(
@@ -625,10 +723,6 @@ void Http2Session::remove_downstream_connection(
dconns_.remove(dconn);
dconn->detach_stream_data();
if (addr_->num_dconn == 0) {
repeat_read_timer();
}
if (LOG_ENABLED(INFO)) {
SSLOG(INFO, this) << "Remove downstream";
}
@@ -996,11 +1090,15 @@ int on_response_headers(Http2Session *http2session, Downstream *downstream,
int rv;
auto upstream = downstream->get_upstream();
auto handler = upstream->get_client_handler();
const auto &req = downstream->request();
auto &resp = downstream->response();
auto &nva = resp.fs.headers();
auto config = get_config();
auto &loggingconf = config->logging;
downstream->set_expect_final_response(false);
auto status = resp.fs.header(http2::HD__STATUS);
@@ -1053,7 +1151,7 @@ int on_response_headers(Http2Session *http2session, Downstream *downstream,
// On upgrade sucess, both ends can send data
if (upstream->resume_read(SHRPX_NO_BUFFER, downstream, 0) != 0) {
// If resume_read fails, just drop connection. Not ideal.
delete upstream->get_client_handler();
delete handler;
return -1;
}
downstream->set_request_state(Downstream::HEADER_COMPLETE);
@@ -1090,6 +1188,11 @@ int on_response_headers(Http2Session *http2session, Downstream *downstream,
resp.headers_only = true;
}
if (loggingconf.access.write_early && downstream->accesslog_ready()) {
handler->write_accesslog(downstream);
downstream->set_accesslog_written(true);
}
rv = upstream->on_downstream_header_complete(downstream);
if (rv != 0) {
// Handling early return (in other words, response was hijacked by
@@ -1550,7 +1653,7 @@ int Http2Session::connection_made() {
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
if (!next_proto) {
downstream_failure(addr_);
downstream_failure(addr_, raddr_);
return -1;
}
@@ -1559,7 +1662,7 @@ int Http2Session::connection_made() {
SSLOG(INFO, this) << "Negotiated next protocol: " << proto;
}
if (!util::check_h2_is_selected(proto)) {
downstream_failure(addr_);
downstream_failure(addr_, raddr_);
return -1;
}
}
@@ -1608,24 +1711,9 @@ int Http2Session::connection_made() {
return -1;
}
auto must_terminate =
addr_->tls && !nghttp2::ssl::check_http2_requirement(conn_.tls.ssl);
reset_connection_check_timer(CONNCHK_TIMEOUT);
if (must_terminate) {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "TLSv1.2 was not negotiated. HTTP/2 must not be negotiated.";
}
rv = terminate_session(NGHTTP2_INADEQUATE_SECURITY);
if (rv != 0) {
return -1;
}
} else {
submit_pending_requests();
}
submit_pending_requests();
signal_write();
return 0;
@@ -1848,10 +1936,10 @@ int Http2Session::connected() {
auto sock_error = util::get_socket_error(conn_.fd);
if (sock_error != 0) {
SSLOG(WARN, this) << "Backend connect failed; addr="
<< util::to_numeric_addr(&addr_->addr)
<< util::to_numeric_addr(raddr_)
<< ": errno=" << sock_error;
downstream_failure(addr_);
downstream_failure(addr_, raddr_);
return -1;
}
@@ -1867,6 +1955,7 @@ int Http2Session::connected() {
ev_timer_again(conn_.loop, &conn_.wt);
conn_.rlimit.startw();
conn_.again_rt();
read_ = &Http2Session::read_clear;
write_ = &Http2Session::write_clear;
@@ -1891,6 +1980,8 @@ int Http2Session::connected() {
}
int Http2Session::read_clear() {
conn_.last_read = ev_now(conn_.loop);
std::array<uint8_t, 16_k> buf;
for (;;) {
@@ -1911,6 +2002,8 @@ int Http2Session::read_clear() {
}
int Http2Session::write_clear() {
conn_.last_read = ev_now(conn_.loop);
std::array<struct iovec, MAX_WR_IOVCNT> iov;
for (;;) {
@@ -1923,7 +2016,12 @@ int Http2Session::write_clear() {
}
if (nwrite < 0) {
return nwrite;
// We may have pending data in receive buffer which may
// contain part of response body. So keep reading. Invoke
// read event to get read(2) error just in case.
ev_feed_event(conn_.loop, &conn_.rev, EV_READ);
write_ = &Http2Session::write_void;
break;
}
wb_.drain(nwrite);
@@ -1945,7 +2043,7 @@ int Http2Session::write_clear() {
}
int Http2Session::tls_handshake() {
ev_timer_again(conn_.loop, &conn_.rt);
conn_.last_read = ev_now(conn_.loop);
ERR_clear_error();
@@ -1956,7 +2054,7 @@ int Http2Session::tls_handshake() {
}
if (rv < 0) {
downstream_failure(addr_);
downstream_failure(addr_, raddr_);
return rv;
}
@@ -1966,8 +2064,8 @@ int Http2Session::tls_handshake() {
}
if (!get_config()->tls.insecure &&
ssl::check_cert(conn_.tls.ssl, addr_) != 0) {
downstream_failure(addr_);
ssl::check_cert(conn_.tls.ssl, addr_, raddr_) != 0) {
downstream_failure(addr_, raddr_);
return -1;
}
@@ -1975,8 +2073,8 @@ int Http2Session::tls_handshake() {
if (!SSL_session_reused(conn_.tls.ssl)) {
auto tls_session = SSL_get0_session(conn_.tls.ssl);
if (tls_session) {
ssl::try_cache_tls_session(addr_->tls_session_cache, addr_->addr,
tls_session, ev_now(conn_.loop));
ssl::try_cache_tls_session(addr_->tls_session_cache, *raddr_, tls_session,
ev_now(conn_.loop));
}
}
@@ -1992,6 +2090,8 @@ int Http2Session::tls_handshake() {
}
int Http2Session::read_tls() {
conn_.last_read = ev_now(conn_.loop);
std::array<uint8_t, 16_k> buf;
ERR_clear_error();
@@ -2014,6 +2114,8 @@ int Http2Session::read_tls() {
}
int Http2Session::write_tls() {
conn_.last_read = ev_now(conn_.loop);
ERR_clear_error();
struct iovec iov;
@@ -2029,7 +2131,12 @@ int Http2Session::write_tls() {
}
if (nwrite < 0) {
return nwrite;
// We may have pending data in receive buffer which may
// contain part of response body. So keep reading. Invoke
// read event to get read(2) error just in case.
ev_feed_event(conn_.loop, &conn_.rev, EV_READ);
write_ = &Http2Session::write_void;
break;
}
wb_.drain(nwrite);
@@ -2052,6 +2159,11 @@ int Http2Session::write_tls() {
return 0;
}
int Http2Session::write_void() {
conn_.wlimit.stopw();
return 0;
}
bool Http2Session::should_hard_fail() const {
switch (state_) {
case PROXY_CONNECTING:
@@ -2259,9 +2371,9 @@ void Http2Session::on_timeout() {
}
case CONNECTING: {
SSLOG(WARN, this) << "Connect time out; addr="
<< util::to_numeric_addr(&addr_->addr);
<< util::to_numeric_addr(raddr_);
downstream_failure(addr_);
downstream_failure(addr_, raddr_);
break;
}
}
@@ -2281,10 +2393,6 @@ void Http2Session::check_retire() {
signal_write();
}
void Http2Session::repeat_read_timer() {
ev_timer_again(conn_.loop, &conn_.rt);
}
void Http2Session::stop_read_timer() { ev_timer_stop(conn_.loop, &conn_.rt); }
const Address *Http2Session::get_raddr() const { return raddr_; }
} // namespace shrpx

View File

@@ -50,6 +50,7 @@ class Http2DownstreamConnection;
class Worker;
struct DownstreamAddrGroup;
struct DownstreamAddr;
struct DNSQuery;
struct StreamData {
StreamData *dlnext, *dlprev;
@@ -81,6 +82,7 @@ public:
// associated ClientHandlers will be deleted.
int disconnect(bool hard = false);
int initiate_connection();
int resolve_name();
void add_downstream_connection(Http2DownstreamConnection *dconn);
void remove_downstream_connection(Http2DownstreamConnection *dconn);
@@ -112,6 +114,9 @@ public:
int tls_handshake();
int read_tls();
int write_tls();
// This is a special write function which just stop write event
// watcher.
int write_void();
int downstream_read_proxy(const uint8_t *data, size_t datalen);
int downstream_connect_proxy();
@@ -203,8 +208,8 @@ public:
// shutdown the connection.
void check_retire();
void repeat_read_timer();
void stop_read_timer();
// Returns address used to connect to backend. Could be nullptr.
const Address *get_raddr() const;
enum {
// Disconnected
@@ -221,6 +226,8 @@ public:
CONNECTED,
// Connection is started to fail
CONNECT_FAILING,
// Resolving host name
RESOLVING_NAME,
};
enum {
@@ -262,6 +269,13 @@ private:
// Address of remote endpoint
DownstreamAddr *addr_;
nghttp2_session *session_;
// Actual remote address used to contact backend. This is initially
// nullptr, and may point to either &addr_->addr,
// resolved_addr_.get(), or HTTP proxy's address structure.
const Address *raddr_;
// Resolved IP address if dns parameter is used
std::unique_ptr<Address> resolved_addr_;
std::unique_ptr<DNSQuery> dns_query_;
int state_;
int connection_check_state_;
int freelist_zone_;

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