Compare commits

..

97 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
83 changed files with 1685 additions and 982 deletions

View File

@@ -21,6 +21,7 @@ Anders Bakken
Andreas Pohl
Andy Davies
Ant Bryan
Benedikt Christoph Wolters
Bernard Spil
Brian Card
Brian Suh
@@ -83,6 +84,7 @@ 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.18.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 3)
set(LT_REVISION 4)
set(LT_AGE 12)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")

View File

@@ -99,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

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"

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.18.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, 3)
AC_SUBST(LT_REVISION, 4)
AC_SUBST(LT_AGE, 12)
major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"`

View File

@@ -8,7 +8,7 @@ _nghttpx()
_get_comp_words_by_ref cur prev
case $cur in
-*)
COMPREPLY=( $( compgen -W '--worker-read-rate --include --frontend-http2-dump-response-header --tls-ticket-key-file --verify-client-cacert --max-response-header-fields --backend-http2-window-size --frontend-keep-alive-timeout --backend-request-buffer --max-request-header-fields --fastopen --tls-ticket-key-memcached --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 --stream-read-timeout --backend-connect-timeout --forwarded-for --accesslog-syslog --dns-cache-timeout --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" "Jan 05, 2017" "1.18.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" "Jan 05, 2017" "1.18.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" "Jan 05, 2017" "1.18.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" "Jan 05, 2017" "1.18.1" "nghttp2"
.TH "NGHTTPX" "1" "Jan 25, 2017" "1.19.0" "nghttp2"
.SH NAME
nghttpx \- HTTP/2 proxy
.
@@ -218,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
@@ -225,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
@@ -253,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
@@ -529,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
@@ -820,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
@@ -836,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
@@ -1074,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
@@ -1255,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.18.1\fP
Default: \fBnghttpx nghttp2/1.19.0\fP
.UNINDENT
.INDENT 0.0
.TP

View File

@@ -202,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``
@@ -209,7 +213,7 @@ Connections
Set listen backlog size.
Default: ``512``
Default: ``65536``
.. option:: --backend-address-family=(auto|IPv4|IPv6)
@@ -235,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
~~~~~~~~~~~
@@ -487,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>
@@ -747,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>
@@ -762,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
~~~~~~~~~~~~~~~
@@ -969,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
@@ -1134,7 +1188,7 @@ HTTP
Change server response header field value to <NAME>.
Default: ``nghttpx nghttp2/1.18.1``
Default: ``nghttpx nghttp2/1.19.0``
.. option:: --no-server-rewrite

View File

@@ -549,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).

View File

@@ -22,7 +22,7 @@ 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

View File

@@ -370,6 +370,81 @@ parameter in :option:`--backend` option, like so:
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

@@ -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) {

View File

@@ -153,6 +153,11 @@ OPTIONS = [
"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

@@ -103,6 +103,7 @@ func newServerTesterInternal(src_args []string, t *testing.T, handler http.Handl
backendTLS := false
dns := false
externalDNS := false
acceptProxyProtocol := false
for _, k := range src_args {
switch k {
case "--http2-bridge":
@@ -112,6 +113,8 @@ func newServerTesterInternal(src_args []string, t *testing.T, handler http.Handl
case "--external-dns":
dns = true
externalDNS = true
case "--accept-proxy-protocol":
acceptProxyProtocol = true
default:
args = append(args, k)
}
@@ -160,12 +163,17 @@ func newServerTesterInternal(src_args []string, t *testing.T, handler http.Handl
b += ";dns"
}
noTLS := "no-tls"
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

@@ -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

@@ -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

@@ -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);
}

View File

@@ -219,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_);
}

View File

@@ -243,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

@@ -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

@@ -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

@@ -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

@@ -181,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;
}
}
@@ -1599,12 +1602,12 @@ Connections:
The parameters are delimited by ";". The available
parameters are: "proto=<PROTO>", "tls",
"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.
"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
@@ -1661,6 +1664,17 @@ Connections:
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.
@@ -1694,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.
@@ -1718,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>
@@ -1894,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
@@ -2089,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
@@ -2101,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>
@@ -2262,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
@@ -2833,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.
@@ -2866,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'},
@@ -3076,6 +3136,12 @@ int main(int argc, char **argv) {
{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;
@@ -3766,6 +3832,28 @@ int main(int argc, char **argv) {
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,
});
}
@@ -1324,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();
}
@@ -1345,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
@@ -1354,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";
}
@@ -1370,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";
}
@@ -1388,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;
@@ -1419,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";
@@ -1447,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";
@@ -1469,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";
}
@@ -1478,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";
}
@@ -1506,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();
@@ -171,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_;
@@ -197,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;
@@ -729,6 +732,7 @@ struct DownstreamParams {
shrpx_session_affinity affinity;
bool tls;
bool dns;
bool frontend_tls;
};
namespace {
@@ -804,6 +808,8 @@ int parse_downstream_params(DownstreamParams &out,
}
} 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;
@@ -896,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;
@@ -910,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
@@ -1196,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) {
@@ -1363,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)) {
@@ -1455,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)) {
@@ -1550,6 +1696,11 @@ 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;
@@ -1648,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:
@@ -1869,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:
@@ -2091,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;
@@ -2883,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;
@@ -3132,6 +3296,37 @@ int parse_config(Config *config, int optid, const StringRef &opt,
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";
@@ -3439,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)) {

View File

@@ -319,6 +319,14 @@ constexpr auto SHRPX_OPT_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;
@@ -382,6 +390,8 @@ 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;
};
@@ -420,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;
@@ -429,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 {
@@ -541,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;
@@ -667,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;
@@ -775,6 +803,7 @@ struct ConnectionConfig {
RateLimitConfig write;
} ratelimit;
size_t worker_connections;
// Deprecated. See UpstreamAddr.accept_proxy_protocol.
bool accept_proxy_protocol;
} upstream;
@@ -868,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,
@@ -909,8 +939,11 @@ 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,
@@ -972,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

@@ -95,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) {
@@ -140,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
@@ -501,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);
}
}
@@ -529,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 "

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 {

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;
@@ -906,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_; }
@@ -985,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

@@ -152,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;
@@ -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

@@ -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

@@ -638,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;
@@ -1090,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);
@@ -1147,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);
@@ -1184,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
@@ -1702,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;

View File

@@ -296,11 +296,15 @@ int on_begin_headers_callback(nghttp2_session *session,
int Http2Upstream::on_request_headers(Downstream *downstream,
const nghttp2_frame *frame) {
auto lgconf = log_config();
lgconf->update_tstamp(std::chrono::system_clock::now());
auto &req = downstream->request();
req.tstamp = lgconf->tstamp;
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
return 0;
}
auto &req = downstream->request();
auto &nva = req.fs.headers();
if (LOG_ENABLED(INFO)) {
@@ -1055,7 +1059,7 @@ int Http2Upstream::on_read() {
auto rlimit = handler_->get_rlimit();
if (rb->rleft()) {
rv = nghttp2_session_mem_recv(session_, rb->pos, rb->rleft());
rv = nghttp2_session_mem_recv(session_, rb->pos(), rb->rleft());
if (rv < 0) {
if (rv != NGHTTP2_ERR_BAD_CLIENT_MAGIC) {
ULOG(ERROR, this) << "nghttp2_session_mem_recv() returned error: "
@@ -1489,7 +1493,7 @@ int Http2Upstream::error_reply(Downstream *downstream,
auto response_status = http2::stringify_status(balloc, status_code);
auto content_length = util::make_string_ref_uint(balloc, html.size());
auto date = make_string_ref(balloc, lgconf->time_http);
auto date = make_string_ref(balloc, lgconf->tstamp->time_http);
auto nva = std::array<nghttp2_nv, 5>{
{http2::make_nv_ls_nocopy(":status", response_status),

View File

@@ -189,9 +189,9 @@ HttpDownstreamConnection::HttpDownstreamConnection(
readcb, connect_timeoutcb, this,
get_config()->tls.dyn_rec.warmup_threshold,
get_config()->tls.dyn_rec.idle_timeout, PROTO_HTTP1),
do_read_(&HttpDownstreamConnection::noop),
do_write_(&HttpDownstreamConnection::noop),
do_signal_write_(&HttpDownstreamConnection::noop),
on_read_(&HttpDownstreamConnection::noop),
on_write_(&HttpDownstreamConnection::noop),
signal_write_(&HttpDownstreamConnection::noop),
worker_(worker),
ssl_ctx_(worker->get_cl_ssl_ctx()),
group_(group),
@@ -454,7 +454,7 @@ int HttpDownstreamConnection::initiate_connection() {
ev_set_cb(&conn_.rev, readcb);
do_write_ = &HttpDownstreamConnection::write_reuse_first;
on_write_ = &HttpDownstreamConnection::write_reuse_first;
reuse_first_write_done_ = false;
}
@@ -822,10 +822,14 @@ namespace {
int htp_hdrs_completecb(http_parser *htp) {
auto downstream = static_cast<Downstream *>(htp->data);
auto upstream = downstream->get_upstream();
auto handler = upstream->get_client_handler();
const auto &req = downstream->request();
auto &resp = downstream->response();
int rv;
auto config = get_config();
auto &loggingconf = config->logging;
resp.http_status = htp->status_code;
resp.http_major = htp->http_major;
resp.http_minor = htp->http_minor;
@@ -911,6 +915,11 @@ int htp_hdrs_completecb(http_parser *htp) {
downstream->set_chunked_response(false);
}
if (loggingconf.access.write_early && downstream->accesslog_ready()) {
handler->write_accesslog(downstream);
downstream->set_accesslog_written(true);
}
if (upstream->on_downstream_header_complete(downstream) != 0) {
return -1;
}
@@ -1070,7 +1079,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;
nullptr, // http_data_cb on_status;
@@ -1096,9 +1105,9 @@ int HttpDownstreamConnection::write_reuse_first() {
}
if (conn_.tls.ssl) {
do_write_ = &HttpDownstreamConnection::write_tls;
on_write_ = &HttpDownstreamConnection::write_tls;
} else {
do_write_ = &HttpDownstreamConnection::write_clear;
on_write_ = &HttpDownstreamConnection::write_clear;
}
reuse_first_write_done_ = true;
@@ -1158,7 +1167,7 @@ int HttpDownstreamConnection::write_clear() {
// 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);
do_write_ = &HttpDownstreamConnection::noop;
on_write_ = &HttpDownstreamConnection::noop;
reusable_ = false;
break;
}
@@ -1216,15 +1225,15 @@ int HttpDownstreamConnection::tls_handshake() {
auto &connect_blocker = addr_->connect_blocker;
do_signal_write_ = &HttpDownstreamConnection::actual_signal_write;
signal_write_ = &HttpDownstreamConnection::actual_signal_write;
connect_blocker->on_success();
ev_set_cb(&conn_.rt, timeoutcb);
ev_set_cb(&conn_.wt, timeoutcb);
do_read_ = &HttpDownstreamConnection::read_tls;
do_write_ = &HttpDownstreamConnection::write_tls;
on_read_ = &HttpDownstreamConnection::read_tls;
on_write_ = &HttpDownstreamConnection::write_tls;
// TODO Check negotiated ALPN
@@ -1287,7 +1296,7 @@ int HttpDownstreamConnection::write_tls() {
// 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);
do_write_ = &HttpDownstreamConnection::noop;
on_write_ = &HttpDownstreamConnection::noop;
reusable_ = false;
break;
}
@@ -1407,32 +1416,32 @@ int HttpDownstreamConnection::connected() {
ev_set_cb(&conn_.wev, writecb);
if (conn_.tls.ssl) {
do_read_ = &HttpDownstreamConnection::tls_handshake;
do_write_ = &HttpDownstreamConnection::tls_handshake;
on_read_ = &HttpDownstreamConnection::tls_handshake;
on_write_ = &HttpDownstreamConnection::tls_handshake;
return 0;
}
do_signal_write_ = &HttpDownstreamConnection::actual_signal_write;
signal_write_ = &HttpDownstreamConnection::actual_signal_write;
connect_blocker->on_success();
ev_set_cb(&conn_.rt, timeoutcb);
ev_set_cb(&conn_.wt, timeoutcb);
do_read_ = &HttpDownstreamConnection::read_clear;
do_write_ = &HttpDownstreamConnection::write_clear;
on_read_ = &HttpDownstreamConnection::read_clear;
on_write_ = &HttpDownstreamConnection::write_clear;
return 0;
}
int HttpDownstreamConnection::on_read() { return do_read_(*this); }
int HttpDownstreamConnection::on_read() { return on_read_(*this); }
int HttpDownstreamConnection::on_write() { return do_write_(*this); }
int HttpDownstreamConnection::on_write() { return on_write_(*this); }
void HttpDownstreamConnection::on_upstream_change(Upstream *upstream) {}
void HttpDownstreamConnection::signal_write() { do_signal_write_(*this); }
void HttpDownstreamConnection::signal_write() { signal_write_(*this); }
int HttpDownstreamConnection::actual_signal_write() {
ev_feed_event(conn_.loop, &conn_.wev, EV_WRITE);

View File

@@ -91,8 +91,8 @@ public:
private:
Connection conn_;
std::function<int(HttpDownstreamConnection &)> do_read_, do_write_,
do_signal_write_;
std::function<int(HttpDownstreamConnection &)> on_read_, on_write_,
signal_write_;
Worker *worker_;
// nullptr if TLS is not used.
SSL_CTX *ssl_ctx_;

View File

@@ -292,6 +292,10 @@ int htp_hdrs_completecb(http_parser *htp) {
auto downstream = upstream->get_downstream();
auto &req = downstream->request();
auto lgconf = log_config();
lgconf->update_tstamp(std::chrono::system_clock::now());
req.tstamp = lgconf->tstamp;
req.http_major = htp->http_major;
req.http_minor = htp->http_minor;
@@ -505,7 +509,7 @@ int htp_msg_completecb(http_parser *htp) {
} // namespace
namespace {
http_parser_settings htp_hooks = {
constexpr http_parser_settings htp_hooks = {
htp_msg_begin, // http_cb on_message_begin;
htp_uricb, // http_data_cb on_url;
nullptr, // http_data_cb on_status;
@@ -532,7 +536,7 @@ int HttpsUpstream::on_read() {
// callback chain called by http_parser_execute()
if (downstream && downstream->get_upgraded()) {
auto rv = downstream->push_upload_data_chunk(rb->pos, rb->rleft());
auto rv = downstream->push_upload_data_chunk(rb->pos(), rb->rleft());
if (rv != 0) {
return -1;
@@ -565,8 +569,9 @@ int HttpsUpstream::on_read() {
}
// http_parser_execute() does nothing once it entered error state.
auto nread = http_parser_execute(
&htp_, &htp_hooks, reinterpret_cast<const char *>(rb->pos), rb->rleft());
auto nread = http_parser_execute(&htp_, &htp_hooks,
reinterpret_cast<const char *>(rb->pos()),
rb->rleft());
rb->drain(nread);
rlimit->startw();
@@ -944,12 +949,13 @@ void HttpsUpstream::error_reply(unsigned int status_code) {
output->append("\r\nServer: ");
output->append(get_config()->http.server_name);
output->append("\r\nContent-Length: ");
auto cl = util::utos(html.size());
output->append(cl);
std::array<uint8_t, NGHTTP2_MAX_UINT64_DIGITS> intbuf;
output->append(StringRef{std::begin(intbuf),
util::utos(std::begin(intbuf), html.size())});
output->append("\r\nDate: ");
auto lgconf = log_config();
lgconf->update_tstamp(std::chrono::system_clock::now());
output->append(lgconf->time_http);
output->append(lgconf->tstamp->time_http);
output->append("\r\nContent-Type: text/html; "
"charset=UTF-8\r\nConnection: close\r\n\r\n");
output->append(html);
@@ -1021,11 +1027,14 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
auto connect_method = req.method == HTTP_CONNECT;
auto buf = downstream->get_response_buf();
std::array<uint8_t, NGHTTP2_MAX_UINT64_DIGITS> intbuf;
buf->append("HTTP/");
buf->append(util::utos(req.http_major));
buf->append(".");
buf->append(util::utos(req.http_minor));
buf->append(StringRef{std::begin(intbuf),
util::utos(std::begin(intbuf), req.http_major)});
buf->append('.');
buf->append(StringRef{std::begin(intbuf),
util::utos(std::begin(intbuf), req.http_minor)});
buf->append(' ');
buf->append(http2::stringify_status(balloc, resp.http_status));
buf->append(' ');

View File

@@ -33,7 +33,11 @@
#ifdef HAVE_INTTYPES_H
#include <inttypes.h>
#endif // HAVE_INTTYPES_H
#include <sys/types.h>
#include <sys/stat.h>
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif // HAVE_FCNTL_H
#include <sys/wait.h>
#include <cerrno>
@@ -54,14 +58,14 @@ using namespace nghttp2;
namespace shrpx {
namespace {
const StringRef SEVERITY_STR[] = {
constexpr StringRef SEVERITY_STR[] = {
StringRef::from_lit("INFO"), StringRef::from_lit("NOTICE"),
StringRef::from_lit("WARN"), StringRef::from_lit("ERROR"),
StringRef::from_lit("FATAL")};
} // namespace
namespace {
const char *SEVERITY_COLOR[] = {
constexpr const char *SEVERITY_COLOR[] = {
"\033[1;32m", // INFO
"\033[1;36m", // NOTICE
"\033[1;33m", // WARN
@@ -142,7 +146,7 @@ Log::~Log() {
// Error log format: <datetime> <master-pid> <current-pid>
// <thread-id> <level> (<filename>:<line>) <msg>
rv = snprintf(buf, sizeof(buf), "%s %d %d %s %s%s%s (%s:%d) %s\n",
lgconf->time_iso8601.c_str(), config->pid, lgconf->pid,
lgconf->tstamp->time_iso8601.c_str(), config->pid, lgconf->pid,
lgconf->thread_id.c_str(), tty ? SEVERITY_COLOR[severity_] : "",
SEVERITY_STR[severity_].c_str(), tty ? "\033[0m" : "",
filename_, linenum_, stream_.str().c_str());
@@ -159,74 +163,124 @@ Log::~Log() {
namespace {
template <typename OutputIterator>
std::pair<OutputIterator, size_t> copy(const char *src, size_t srclen,
size_t avail, OutputIterator oitr) {
auto nwrite = std::min(srclen, avail);
auto noitr = std::copy_n(src, nwrite, oitr);
return std::make_pair(noitr, avail - nwrite);
std::pair<OutputIterator, OutputIterator> copy(const char *src, size_t srclen,
OutputIterator d_first,
OutputIterator d_last) {
auto nwrite =
std::min(static_cast<size_t>(std::distance(d_first, d_last)), srclen);
return std::make_pair(std::copy_n(src, nwrite, d_first), d_last);
}
} // namespace
namespace {
template <typename OutputIterator>
std::pair<OutputIterator, size_t> copy(const char *src, size_t avail,
OutputIterator oitr) {
return copy(src, strlen(src), avail, oitr);
std::pair<OutputIterator, OutputIterator>
copy(const char *src, OutputIterator d_first, OutputIterator d_last) {
return copy(src, strlen(src), d_first, d_last);
}
} // namespace
namespace {
template <typename OutputIterator>
std::pair<OutputIterator, size_t> copy(const std::string &src, size_t avail,
OutputIterator oitr) {
return copy(src.c_str(), src.size(), avail, oitr);
}
} // namespace
namespace {
template <typename OutputIterator>
std::pair<OutputIterator, size_t> copy(const StringRef &src, size_t avail,
OutputIterator oitr) {
return copy(src.c_str(), src.size(), avail, oitr);
}
} // namespace
namespace {
template <typename OutputIterator>
std::pair<OutputIterator, size_t> copy(const ImmutableString &src, size_t avail,
OutputIterator oitr) {
return copy(src.c_str(), src.size(), avail, oitr);
std::pair<OutputIterator, OutputIterator>
copy(const StringRef &src, OutputIterator d_first, OutputIterator d_last) {
return copy(src.c_str(), src.size(), d_first, d_last);
}
} // namespace
namespace {
template <size_t N, typename OutputIterator>
std::pair<OutputIterator, size_t> copy_l(const char (&src)[N], size_t avail,
OutputIterator oitr) {
return copy(src, N - 1, avail, oitr);
std::pair<OutputIterator, OutputIterator>
copy_l(const char (&src)[N], OutputIterator d_first, OutputIterator d_last) {
return copy(src, N - 1, d_first, d_last);
}
} // namespace
namespace {
const char LOWER_XDIGITS[] = "0123456789abcdef";
template <typename OutputIterator>
std::pair<OutputIterator, OutputIterator> copy(char c, OutputIterator d_first,
OutputIterator d_last) {
if (d_first == d_last) {
return std::make_pair(d_last, d_last);
}
*d_first++ = c;
return std::make_pair(d_first, d_last);
}
} // namespace
namespace {
constexpr char LOWER_XDIGITS[] = "0123456789abcdef";
} // namespace
namespace {
template <typename OutputIterator>
std::pair<OutputIterator, size_t> copy_hex_low(const uint8_t *src,
size_t srclen, size_t avail,
OutputIterator oitr) {
auto nwrite = std::min(srclen * 2, avail) / 2;
for (auto i = 0u; i < nwrite; ++i) {
*oitr++ = LOWER_XDIGITS[src[i] >> 4];
*oitr++ = LOWER_XDIGITS[src[i] & 0xf];
std::pair<OutputIterator, OutputIterator>
copy_hex_low(const uint8_t *src, size_t srclen, OutputIterator d_first,
OutputIterator d_last) {
auto nwrite = std::min(static_cast<size_t>(std::distance(d_first, d_last)),
srclen * 2) /
2;
for (size_t i = 0; i < nwrite; ++i) {
*d_first++ = LOWER_XDIGITS[src[i] >> 4];
*d_first++ = LOWER_XDIGITS[src[i] & 0xf];
}
return std::make_pair(oitr, avail - nwrite);
return std::make_pair(d_first, d_last);
}
} // namespace
namespace {
template <typename OutputIterator, typename T>
std::pair<OutputIterator, OutputIterator> copy(T n, OutputIterator d_first,
OutputIterator d_last) {
if (static_cast<size_t>(std::distance(d_first, d_last)) <
NGHTTP2_MAX_UINT64_DIGITS) {
return std::make_pair(d_last, d_last);
}
return std::make_pair(util::utos(d_first, n), d_last);
}
} // namespace
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 upstream_accesslog(const std::vector<LogFragment> &lfv,
const LogSpec &lgsp) {
auto config = get_config();
auto lgconf = log_config();
auto &accessconf = get_config()->logging.access;
@@ -234,148 +288,150 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv,
return;
}
char buf[4_k];
std::array<char, 4_k> buf;
auto downstream = lgsp.downstream;
const Request *req = nullptr;
if (downstream) {
req = &downstream->request();
}
const auto &req = downstream->request();
const auto &resp = downstream->response();
const auto &tstamp = req.tstamp;
auto &balloc = downstream->get_block_allocator();
auto p = buf;
auto avail = sizeof(buf) - 2;
auto downstream_addr = downstream->get_addr();
auto method = http2::to_method_string(req.method);
auto path = req.method == HTTP_CONNECT
? req.authority
: config->http2_proxy
? construct_absolute_request_uri(balloc, req)
: req.path.empty()
? req.method == HTTP_OPTIONS
? StringRef::from_lit("*")
: StringRef::from_lit("-")
: req.path;
lgconf->update_tstamp(lgsp.time_now);
auto &time_local = lgconf->time_local;
auto &time_iso8601 = lgconf->time_iso8601;
auto p = std::begin(buf);
auto last = std::end(buf) - 2;
for (auto &lf : lfv) {
switch (lf.type) {
case SHRPX_LOGF_LITERAL:
std::tie(p, avail) = copy(lf.value, avail, p);
std::tie(p, last) = copy(lf.value, p, last);
break;
case SHRPX_LOGF_REMOTE_ADDR:
std::tie(p, avail) = copy(lgsp.remote_addr, avail, p);
std::tie(p, last) = copy(lgsp.remote_addr, p, last);
break;
case SHRPX_LOGF_TIME_LOCAL:
std::tie(p, avail) = copy(time_local, avail, p);
std::tie(p, last) = copy(tstamp->time_local, p, last);
break;
case SHRPX_LOGF_TIME_ISO8601:
std::tie(p, avail) = copy(time_iso8601, avail, p);
std::tie(p, last) = copy(tstamp->time_iso8601, p, last);
break;
case SHRPX_LOGF_REQUEST:
std::tie(p, avail) = copy(lgsp.method, avail, p);
std::tie(p, avail) = copy_l(" ", avail, p);
std::tie(p, avail) = copy(lgsp.path, avail, p);
std::tie(p, avail) = copy_l(" HTTP/", avail, p);
std::tie(p, avail) = copy(util::utos(lgsp.major), avail, p);
if (lgsp.major < 2) {
std::tie(p, avail) = copy_l(".", avail, p);
std::tie(p, avail) = copy(util::utos(lgsp.minor), avail, p);
std::tie(p, last) = copy(method, p, last);
std::tie(p, last) = copy(' ', p, last);
std::tie(p, last) = copy(path, p, last);
std::tie(p, last) = copy_l(" HTTP/", p, last);
std::tie(p, last) = copy(req.http_major, p, last);
if (req.http_major < 2) {
std::tie(p, last) = copy('.', p, last);
std::tie(p, last) = copy(req.http_minor, p, last);
}
break;
case SHRPX_LOGF_STATUS:
std::tie(p, avail) = copy(util::utos(lgsp.status), avail, p);
std::tie(p, last) = copy(resp.http_status, p, last);
break;
case SHRPX_LOGF_BODY_BYTES_SENT:
std::tie(p, avail) = copy(util::utos(lgsp.body_bytes_sent), avail, p);
std::tie(p, last) = copy(downstream->response_sent_body_length, p, last);
break;
case SHRPX_LOGF_HTTP:
if (req) {
auto hd = req->fs.header(lf.value);
if (hd) {
std::tie(p, avail) = copy((*hd).value, avail, p);
break;
}
case SHRPX_LOGF_HTTP: {
auto hd = req.fs.header(lf.value);
if (hd) {
std::tie(p, last) = copy((*hd).value, p, last);
break;
}
std::tie(p, avail) = copy_l("-", avail, p);
std::tie(p, last) = copy('-', p, last);
break;
}
case SHRPX_LOGF_AUTHORITY:
if (req) {
if (!req->authority.empty()) {
std::tie(p, avail) = copy(req->authority, avail, p);
break;
}
if (!req.authority.empty()) {
std::tie(p, last) = copy(req.authority, p, last);
break;
}
std::tie(p, avail) = copy_l("-", avail, p);
std::tie(p, last) = copy('-', p, last);
break;
case SHRPX_LOGF_REMOTE_PORT:
std::tie(p, avail) = copy(lgsp.remote_port, avail, p);
std::tie(p, last) = copy(lgsp.remote_port, p, last);
break;
case SHRPX_LOGF_SERVER_PORT:
std::tie(p, avail) = copy(util::utos(lgsp.server_port), avail, p);
std::tie(p, last) = copy(lgsp.server_port, p, last);
break;
case SHRPX_LOGF_REQUEST_TIME: {
auto t = std::chrono::duration_cast<std::chrono::milliseconds>(
lgsp.request_end_time - lgsp.request_start_time)
lgsp.request_end_time - downstream->get_request_start_time())
.count();
auto frac = util::utos(t % 1000);
auto sec = util::utos(t / 1000);
if (frac.size() < 3) {
frac = std::string(3 - frac.size(), '0') + frac;
std::tie(p, last) = copy(t / 1000, p, last);
std::tie(p, last) = copy('.', p, last);
auto frac = t % 1000;
if (frac < 100) {
auto n = frac < 10 ? 2 : 1;
std::tie(p, last) = copy("000", n, p, last);
}
sec += '.';
sec += frac;
std::tie(p, avail) = copy(sec, avail, p);
} break;
std::tie(p, last) = copy(frac, p, last);
break;
}
case SHRPX_LOGF_PID:
std::tie(p, avail) = copy(util::utos(lgsp.pid), avail, p);
std::tie(p, last) = copy(lgsp.pid, p, last);
break;
case SHRPX_LOGF_ALPN:
std::tie(p, avail) = copy(lgsp.alpn, avail, p);
std::tie(p, last) = copy(lgsp.alpn, p, last);
break;
case SHRPX_LOGF_SSL_CIPHER:
if (!lgsp.tls_info) {
std::tie(p, avail) = copy_l("-", avail, p);
std::tie(p, last) = copy('-', p, last);
break;
}
std::tie(p, avail) = copy(lgsp.tls_info->cipher, avail, p);
std::tie(p, last) = copy(lgsp.tls_info->cipher, p, last);
break;
case SHRPX_LOGF_SSL_PROTOCOL:
if (!lgsp.tls_info) {
std::tie(p, avail) = copy_l("-", avail, p);
std::tie(p, last) = copy('-', p, last);
break;
}
std::tie(p, avail) = copy(lgsp.tls_info->protocol, avail, p);
std::tie(p, last) = copy(lgsp.tls_info->protocol, p, last);
break;
case SHRPX_LOGF_SSL_SESSION_ID:
if (!lgsp.tls_info || lgsp.tls_info->session_id_length == 0) {
std::tie(p, avail) = copy_l("-", avail, p);
std::tie(p, last) = copy('-', p, last);
break;
}
std::tie(p, avail) =
copy_hex_low(lgsp.tls_info->session_id,
lgsp.tls_info->session_id_length, avail, p);
std::tie(p, last) = copy_hex_low(
lgsp.tls_info->session_id, lgsp.tls_info->session_id_length, p, last);
break;
case SHRPX_LOGF_SSL_SESSION_REUSED:
if (!lgsp.tls_info) {
std::tie(p, avail) = copy_l("-", avail, p);
std::tie(p, last) = copy('-', p, last);
break;
}
std::tie(p, avail) =
copy_l(lgsp.tls_info->session_reused ? "r" : ".", avail, p);
std::tie(p, last) =
copy(lgsp.tls_info->session_reused ? 'r' : '.', p, last);
break;
case SHRPX_LOGF_BACKEND_HOST:
if (!lgsp.downstream_addr) {
std::tie(p, avail) = copy_l("-", avail, p);
if (!downstream_addr) {
std::tie(p, last) = copy('-', p, last);
break;
}
std::tie(p, avail) = copy(lgsp.downstream_addr->host, avail, p);
std::tie(p, last) = copy(downstream_addr->host, p, last);
break;
case SHRPX_LOGF_BACKEND_PORT:
if (!lgsp.downstream_addr) {
std::tie(p, avail) = copy_l("-", avail, p);
if (!downstream_addr) {
std::tie(p, last) = copy('-', p, last);
break;
}
std::tie(p, avail) =
copy(util::utos(lgsp.downstream_addr->port), avail, p);
std::tie(p, last) = copy(downstream_addr->port, p, last);
break;
case SHRPX_LOGF_NONE:
break;
@@ -387,15 +443,16 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv,
*p = '\0';
if (accessconf.syslog) {
syslog(LOG_INFO, "%s", buf);
syslog(LOG_INFO, "%s", buf.data());
return;
}
*p++ = '\n';
auto nwrite = p - buf;
while (write(lgconf->accesslog_fd, buf, nwrite) == -1 && errno == EINTR)
auto nwrite = std::distance(std::begin(buf), p);
while (write(lgconf->accesslog_fd, buf.data(), nwrite) == -1 &&
errno == EINTR)
;
}
@@ -410,7 +467,7 @@ int reopen_log_files() {
auto &errorconf = config->logging.error;
if (!accessconf.syslog && !accessconf.file.empty()) {
new_accesslog_fd = util::open_log_file(accessconf.file.c_str());
new_accesslog_fd = open_log_file(accessconf.file.c_str());
if (new_accesslog_fd == -1) {
LOG(ERROR) << "Failed to open accesslog file " << accessconf.file;
@@ -419,7 +476,7 @@ int reopen_log_files() {
}
if (!errorconf.syslog && !errorconf.file.empty()) {
new_errorlog_fd = util::open_log_file(errorconf.file.c_str());
new_errorlog_fd = open_log_file(errorconf.file.c_str());
if (new_errorlog_fd == -1) {
if (lgconf->errorlog_fd != -1) {
@@ -433,8 +490,8 @@ int reopen_log_files() {
}
}
util::close_log_file(lgconf->accesslog_fd);
util::close_log_file(lgconf->errorlog_fd);
close_log_file(lgconf->accesslog_fd);
close_log_file(lgconf->errorlog_fd);
lgconf->accesslog_fd = new_accesslog_fd;
lgconf->errorlog_fd = new_errorlog_fd;
@@ -478,4 +535,60 @@ void redirect_stderr_to_errorlog() {
dup2(lgconf->errorlog_fd, STDERR_FILENO);
}
namespace {
int STDERR_COPY = -1;
int STDOUT_COPY = -1;
} // namespace
void store_original_fds() {
// consider dup'ing stdout too
STDERR_COPY = dup(STDERR_FILENO);
STDOUT_COPY = STDOUT_FILENO;
// no race here, since it is called early
util::make_socket_closeonexec(STDERR_COPY);
}
void restore_original_fds() { dup2(STDERR_COPY, STDERR_FILENO); }
void close_log_file(int &fd) {
if (fd != STDERR_COPY && fd != STDOUT_COPY && fd != -1) {
close(fd);
}
fd = -1;
}
int open_log_file(const char *path) {
if (strcmp(path, "/dev/stdout") == 0 ||
strcmp(path, "/proc/self/fd/1") == 0) {
return STDOUT_COPY;
}
if (strcmp(path, "/dev/stderr") == 0 ||
strcmp(path, "/proc/self/fd/2") == 0) {
return STDERR_COPY;
}
#if defined O_CLOEXEC
auto fd = open(path, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC,
S_IRUSR | S_IWUSR | S_IRGRP);
#else // !O_CLOEXEC
auto fd =
open(path, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP);
// We get race condition if execve is called at the same time.
if (fd != -1) {
util::make_socket_closeonexec(fd);
}
#endif // !O_CLOEXEC
if (fd == -1) {
return -1;
}
return fd;
}
} // namespace shrpx

View File

@@ -145,18 +145,10 @@ struct LogFragment {
struct LogSpec {
Downstream *downstream;
const DownstreamAddr *downstream_addr;
StringRef remote_addr;
StringRef method;
StringRef path;
StringRef alpn;
const nghttp2::ssl::TLSSessionInfo *tls_info;
std::chrono::system_clock::time_point time_now;
std::chrono::high_resolution_clock::time_point request_start_time;
std::chrono::high_resolution_clock::time_point request_end_time;
int major, minor;
unsigned int status;
int64_t body_bytes_sent;
StringRef remote_port;
uint16_t server_port;
pid_t pid;
@@ -173,6 +165,24 @@ void log_chld(pid_t pid, int rstatus, const char *msg);
void redirect_stderr_to_errorlog();
// Makes internal copy of stderr (and possibly stdout in the future),
// which is then used as pointer to /dev/stderr or /proc/self/fd/2
void store_original_fds();
// Restores the original stderr that was stored with copy_original_fds
// Used just before execv
void restore_original_fds();
// Closes |fd| which was returned by open_log_file (see below)
// and sets it to -1. In the case that |fd| points to stdout or
// stderr, or is -1, the descriptor is not closed (but still set to -1).
void close_log_file(int &fd);
// Opens |path| with O_APPEND enabled. If file does not exist, it is
// created first. This function returns file descriptor referring the
// opened file if it succeeds, or -1.
int open_log_file(const char *path);
} // namespace shrpx
#endif // SHRPX_LOG_H

View File

@@ -35,8 +35,19 @@ using namespace nghttp2;
namespace shrpx {
Timestamp::Timestamp(const std::chrono::system_clock::time_point &tp) {
time_local = util::format_common_log(time_local_buf.data(), tp);
time_iso8601 = util::format_iso8601(time_iso8601_buf.data(), tp);
time_http = util::format_http_date(time_http_buf.data(), tp);
}
LogConfig::LogConfig()
: pid(getpid()), accesslog_fd(-1), errorlog_fd(-1), errorlog_tty(false) {
: time_str_updated(std::chrono::system_clock::now()),
tstamp(std::make_shared<Timestamp>(time_str_updated)),
pid(getpid()),
accesslog_fd(-1),
errorlog_fd(-1),
errorlog_tty(false) {
auto tid = std::this_thread::get_id();
auto tid_hash =
util::hash32(StringRef{reinterpret_cast<uint8_t *>(&tid),
@@ -87,17 +98,15 @@ void delete_log_config() {}
void LogConfig::update_tstamp(
const std::chrono::system_clock::time_point &now) {
auto t0 = std::chrono::system_clock::to_time_t(time_str_updated_);
auto t0 = std::chrono::system_clock::to_time_t(time_str_updated);
auto t1 = std::chrono::system_clock::to_time_t(now);
if (t0 == t1) {
return;
}
time_str_updated_ = now;
time_str_updated = now;
time_local = util::format_common_log(time_local_buf.data(), now);
time_iso8601 = util::format_iso8601(time_iso8601_buf.data(), now);
time_http = util::format_http_date(time_http_buf.data(), now);
tstamp = std::make_shared<Timestamp>(now);
}
} // namespace shrpx

View File

@@ -37,15 +37,21 @@ using namespace nghttp2;
namespace shrpx {
struct LogConfig {
std::chrono::system_clock::time_point time_str_updated_;
struct Timestamp {
Timestamp(const std::chrono::system_clock::time_point &tp);
std::array<char, sizeof("03/Jul/2014:00:19:38 +0900")> time_local_buf;
std::array<char, sizeof("2014-11-15T12:58:24.741+09:00")> time_iso8601_buf;
std::array<char, sizeof("Mon, 10 Oct 2016 10:25:58 GMT")> time_http_buf;
std::string thread_id;
StringRef time_local;
StringRef time_iso8601;
StringRef time_http;
};
struct LogConfig {
std::chrono::system_clock::time_point time_str_updated;
std::shared_ptr<Timestamp> tstamp;
std::string thread_id;
pid_t pid;
int accesslog_fd;
int errorlog_fd;

View File

@@ -255,8 +255,8 @@ mrb_value response_return(mrb_state *mrb, mrb_value self) {
auto lgconf = log_config();
lgconf->update_tstamp(std::chrono::system_clock::now());
resp.fs.add_header_token(StringRef::from_lit("date"),
make_string_ref(balloc, lgconf->time_http), false,
http2::HD_DATE);
make_string_ref(balloc, lgconf->tstamp->time_http),
false, http2::HD_DATE);
}
auto upstream = downstream->get_upstream();

View File

@@ -105,7 +105,7 @@ ssize_t recv_callback(spdylay_session *session, uint8_t *buf, size_t len,
auto nread = std::min(rb->rleft(), len);
memcpy(buf, rb->pos, nread);
memcpy(buf, rb->pos(), nread);
rb->drain(nread);
rlimit->startw();
@@ -181,6 +181,10 @@ void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type,
auto &balloc = downstream->get_block_allocator();
auto lgconf = log_config();
lgconf->update_tstamp(std::chrono::system_clock::now());
req.tstamp = lgconf->tstamp;
downstream->reset_upstream_rtimer();
auto nv = frame->syn_stream.nv;
@@ -1006,7 +1010,7 @@ int SpdyUpstream::error_reply(Downstream *downstream,
"content-type", "text/html; charset=UTF-8",
"server", get_config()->http.server_name.c_str(),
"content-length", content_length.c_str(),
"date", lgconf->time_http.c_str(),
"date", lgconf->tstamp->time_http.c_str(),
nullptr};
rv = spdylay_submit_response(session_, downstream->get_stream_id(), nv,

View File

@@ -250,7 +250,7 @@ int tls_session_new_cb(SSL *ssl, SSL_SESSION *session) {
id = SSL_SESSION_get_id(session, &idlen);
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Memached: cache session, id=" << util::format_hex(id, idlen);
LOG(INFO) << "Memcached: cache session, id=" << util::format_hex(id, idlen);
}
auto req = make_unique<MemcachedRequest>();
@@ -525,6 +525,67 @@ int sct_parse_cb(SSL *ssl, unsigned int ext_type, const unsigned char *in,
} // namespace
#endif // !LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L
#if !LIBRESSL_IN_USE
namespace {
unsigned int psk_server_cb(SSL *ssl, const char *identity, unsigned char *psk,
unsigned int max_psk_len) {
auto config = get_config();
auto &tlsconf = config->tls;
auto it = tlsconf.psk_secrets.find(StringRef{identity});
if (it == std::end(tlsconf.psk_secrets)) {
return 0;
}
auto &secret = (*it).second;
if (secret.size() > max_psk_len) {
LOG(ERROR) << "The size of PSK secret is " << secret.size()
<< ", but the acceptable maximum size is" << max_psk_len;
return 0;
}
std::copy(std::begin(secret), std::end(secret), psk);
return static_cast<unsigned int>(secret.size());
}
} // namespace
#endif // !LIBRESSL_IN_USE
#if !LIBRESSL_IN_USE
namespace {
unsigned int psk_client_cb(SSL *ssl, const char *hint, char *identity_out,
unsigned int max_identity_len, unsigned char *psk,
unsigned int max_psk_len) {
auto config = get_config();
auto &tlsconf = config->tls;
auto &identity = tlsconf.client.psk.identity;
auto &secret = tlsconf.client.psk.secret;
if (identity.empty()) {
return 0;
}
if (identity.size() + 1 > max_identity_len) {
LOG(ERROR) << "The size of PSK identity is " << identity.size()
<< ", but the acceptable maximum size is " << max_identity_len;
return 0;
}
if (secret.size() > max_psk_len) {
LOG(ERROR) << "The size of PSK secret is " << secret.size()
<< ", but the acceptable maximum size is " << max_psk_len;
return 0;
}
*std::copy(std::begin(identity), std::end(identity), identity_out) = '\0';
std::copy(std::begin(secret), std::end(secret), psk);
return (unsigned int)secret.size();
}
} // namespace
#endif // !LIBRESSL_IN_USE
struct TLSProtocol {
StringRef name;
long int mask;
@@ -588,15 +649,8 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file,
SSL_CTX_set_timeout(ssl_ctx, tlsconf.session_timeout.count());
const char *ciphers;
if (!tlsconf.ciphers.empty()) {
ciphers = tlsconf.ciphers.c_str();
} else {
ciphers = nghttp2::ssl::DEFAULT_CIPHER_LIST;
}
if (SSL_CTX_set_cipher_list(ssl_ctx, ciphers) == 0) {
LOG(FATAL) << "SSL_CTX_set_cipher_list " << ciphers
if (SSL_CTX_set_cipher_list(ssl_ctx, tlsconf.ciphers.c_str()) == 0) {
LOG(FATAL) << "SSL_CTX_set_cipher_list " << tlsconf.ciphers
<< " failed: " << ERR_error_string(ERR_get_error(), nullptr);
DIE();
}
@@ -720,18 +774,24 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file,
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
#if !LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L
// SSL_extension_supported(TLS_EXT_SIGNED_CERTIFICATE_TIMESTAMP)
// returns 1, which means OpenSSL internally handles it. But
// OpenSSL handles signed_certificate_timestamp extension specially,
// and it lets custom handler to process the extension.
if (!sct_data.empty() &&
SSL_extension_supported(TLS_EXT_SIGNED_CERTIFICATE_TIMESTAMP) == 0) {
if (SSL_CTX_add_server_custom_ext(
ssl_ctx, TLS_EXT_SIGNED_CERTIFICATE_TIMESTAMP, sct_add_cb,
sct_free_cb, nullptr, sct_parse_cb, nullptr) != 1) {
LOG(FATAL) << "SSL_CTX_add_server_custom_ext failed: "
<< ERR_error_string(ERR_get_error(), nullptr);
DIE();
}
SSL_CTX_add_server_custom_ext(
ssl_ctx, TLS_EXT_SIGNED_CERTIFICATE_TIMESTAMP, sct_add_cb,
sct_free_cb, nullptr, sct_parse_cb, nullptr) != 1) {
LOG(FATAL) << "SSL_CTX_add_server_custom_ext failed: "
<< ERR_error_string(ERR_get_error(), nullptr);
DIE();
}
#endif // !LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L
#if !LIBRESSL_IN_USE
SSL_CTX_set_psk_server_callback(ssl_ctx, psk_server_cb);
#endif // !LIBRESSL_IN_USE
auto tls_ctx_data = new TLSContextData();
tls_ctx_data->cert_file = cert_file;
tls_ctx_data->sct_data = sct_data;
@@ -812,14 +872,8 @@ SSL_CTX *create_ssl_client_context(
SSL_CTX_set_options(ssl_ctx, ssl_opts | tlsconf.tls_proto_mask);
const char *ciphers;
if (!tlsconf.ciphers.empty()) {
ciphers = tlsconf.ciphers.c_str();
} else {
ciphers = nghttp2::ssl::DEFAULT_CIPHER_LIST;
}
if (SSL_CTX_set_cipher_list(ssl_ctx, ciphers) == 0) {
LOG(FATAL) << "SSL_CTX_set_cipher_list " << ciphers
if (SSL_CTX_set_cipher_list(ssl_ctx, tlsconf.client.ciphers.c_str()) == 0) {
LOG(FATAL) << "SSL_CTX_set_cipher_list " << tlsconf.client.ciphers
<< " failed: " << ERR_error_string(ERR_get_error(), nullptr);
DIE();
}
@@ -871,6 +925,10 @@ SSL_CTX *create_ssl_client_context(
#endif // HAVE_NEVERBLEED
}
#if !LIBRESSL_IN_USE
SSL_CTX_set_psk_client_callback(ssl_ctx, psk_client_cb);
#endif // !LIBRESSL_IN_USE
// NPN selection callback. This is required to set SSL_CTX because
// OpenSSL does not offer SSL_set_next_proto_select_cb.
SSL_CTX_set_next_proto_select_cb(ssl_ctx, next_proto_select_cb, nullptr);
@@ -1153,8 +1211,10 @@ int verify_hostname(X509 *cert, const StringRef &hostname,
int check_cert(SSL *ssl, const Address *addr, const StringRef &host) {
auto cert = SSL_get_peer_certificate(ssl);
if (!cert) {
LOG(ERROR) << "No certificate found";
return -1;
// By the protocol definition, TLS server always sends certificate
// if it has. If certificate cannot be retrieved, authentication
// without certificate is used, such as PSK.
return 0;
}
auto cert_deleter = defer(X509_free, cert);
auto verify_res = SSL_get_verify_result(ssl);

View File

@@ -76,7 +76,8 @@ bool match_shared_downstream_addr(
return false;
}
if (lhs->affinity != rhs->affinity) {
if (lhs->affinity != rhs->affinity ||
lhs->require_upstream_tls != rhs->require_upstream_tls) {
return false;
}
@@ -191,6 +192,7 @@ void Worker::replace_downstream_config(
shared_addr->addrs.resize(src.addrs.size());
shared_addr->affinity = src.affinity;
shared_addr->affinity_hash = src.affinity_hash;
shared_addr->require_upstream_tls = src.require_upstream_tls;
size_t num_http1 = 0;
size_t num_http2 = 0;

View File

@@ -136,7 +136,8 @@ struct SharedDownstreamAddr {
next{0},
http1_pri{},
http2_pri{},
affinity{AFFINITY_NONE} {}
affinity{AFFINITY_NONE},
require_upstream_tls{false} {}
SharedDownstreamAddr(const SharedDownstreamAddr &) = delete;
SharedDownstreamAddr(SharedDownstreamAddr &&) = delete;
@@ -149,7 +150,7 @@ struct SharedDownstreamAddr {
// AFFINITY_IP.
std::vector<AffinityHash> affinity_hash;
// List of Http2Session which is not fully utilized (i.e., the
// server advertized maximum concurrency is not reached). We will
// server advertised maximum concurrency is not reached). We will
// coalesce as much stream as possible in one Http2Session to fully
// utilize TCP connection.
//
@@ -171,6 +172,8 @@ struct SharedDownstreamAddr {
WeightedPri http2_pri;
// Session affinity
shrpx_session_affinity affinity;
// true if this group requires that client connection must be TLS.
bool require_upstream_tls;
};
struct DownstreamAddrGroup {

View File

@@ -161,7 +161,7 @@ void ipc_readcb(struct ev_loop *loop, ev_io *w, int revents) {
if (nread == 0) {
// IPC socket closed. Perform immediate shutdown.
LOG(FATAL) << "IPC socket is closed. Perform immediate shutdown.";
_Exit(EXIT_FAILURE);
nghttp2_Exit(EXIT_FAILURE);
}
for (ssize_t i = 0; i < nread; ++i) {
@@ -384,7 +384,7 @@ void nb_child_cb(struct ev_loop *loop, ev_child *w, int revents) {
LOG(FATAL) << "neverbleed process exitted; aborting now";
_Exit(EXIT_FAILURE);
nghttp2_Exit(EXIT_FAILURE);
}
} // namespace
#endif // HAVE_NEVERBLEED

View File

@@ -38,22 +38,6 @@ namespace nghttp2 {
namespace ssl {
// Recommended general purpose "Intermediate compatibility" cipher
// suites by mozilla.
//
// https://wiki.mozilla.org/Security/Server_Side_TLS
const char *const DEFAULT_CIPHER_LIST =
"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";
#if OPENSSL_1_1_API
// CRYPTO_LOCK is deprecated as of OpenSSL 1.1.0

View File

@@ -45,7 +45,21 @@ public:
LibsslGlobalLock &operator=(const LibsslGlobalLock &) = delete;
};
extern const char *const DEFAULT_CIPHER_LIST;
// Recommended general purpose "Intermediate compatibility" cipher
// suites by mozilla.
//
// https://wiki.mozilla.org/Security/Server_Side_TLS
constexpr char DEFAULT_CIPHER_LIST[] =
"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";
const char *get_tls_protocol(SSL *ssl);

View File

@@ -417,7 +417,7 @@ public:
explicit StringRef(const char *s) : base(s), len(strlen(s)) {}
constexpr StringRef(const char *s, size_t n) : base(s), len(n) {}
template <typename CharT>
StringRef(const CharT *s, size_t n)
constexpr StringRef(const CharT *s, size_t n)
: base(reinterpret_cast<const char *>(s)), len(n) {}
template <typename InputIt>
StringRef(InputIt first, InputIt last)

View File

@@ -205,9 +205,10 @@ Iterator cpydig(Iterator d, uint32_t n, size_t len) {
} // namespace
namespace {
const char *MONTH[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
const char *DAY_OF_WEEK[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
constexpr const char *MONTH[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
constexpr const char *DAY_OF_WEEK[] = {"Sun", "Mon", "Tue", "Wed",
"Thu", "Fri", "Sat"};
} // namespace
std::string http_date(time_t t) {
@@ -369,7 +370,7 @@ char upcase(char c) {
}
namespace {
const char LOWER_XDIGITS[] = "0123456789abcdef";
constexpr char LOWER_XDIGITS[] = "0123456789abcdef";
} // namespace
std::string format_hex(const unsigned char *s, size_t len) {
@@ -472,7 +473,7 @@ int levenshtein(const char *a, int alen, const char *b, int blen, int swapcost,
}
} // namespace
void show_candidates(const char *unkopt, option *options) {
void show_candidates(const char *unkopt, const option *options) {
for (; *unkopt == '-'; ++unkopt)
;
if (*unkopt == '\0') {
@@ -554,20 +555,16 @@ bool fieldeq(const char *uri1, const http_parser_url &u1, const char *uri2,
bool fieldeq(const char *uri, const http_parser_url &u,
http_parser_url_fields field, const char *t) {
return fieldeq(uri, u, field, StringRef{t});
}
bool fieldeq(const char *uri, const http_parser_url &u,
http_parser_url_fields field, const StringRef &t) {
if (!has_uri_field(u, field)) {
if (!t[0]) {
return true;
} else {
return false;
}
} else if (!t[0]) {
return false;
return t.empty();
}
int i, len = u.field_data[field].len;
const char *p = uri + u.field_data[field].off;
for (i = 0; i < len && t[i] && p[i] == t[i]; ++i)
;
return i == len && !t[i];
auto &f = u.field_data[field];
return StringRef{uri + f.off, f.len} == t;
}
StringRef get_uri_field(const char *uri, const http_parser_url &u,
@@ -677,60 +674,6 @@ void set_port(Address &addr, uint16_t port) {
}
}
static int STDERR_COPY = -1;
static int STDOUT_COPY = -1;
void store_original_fds() {
// consider dup'ing stdout too
STDERR_COPY = dup(STDERR_FILENO);
STDOUT_COPY = STDOUT_FILENO;
// no race here, since it is called early
make_socket_closeonexec(STDERR_COPY);
}
void restore_original_fds() { dup2(STDERR_COPY, STDERR_FILENO); }
void close_log_file(int &fd) {
if (fd != STDERR_COPY && fd != STDOUT_COPY && fd != -1) {
close(fd);
}
fd = -1;
}
int open_log_file(const char *path) {
if (strcmp(path, "/dev/stdout") == 0 ||
strcmp(path, "/proc/self/fd/1") == 0) {
return STDOUT_COPY;
}
if (strcmp(path, "/dev/stderr") == 0 ||
strcmp(path, "/proc/self/fd/2") == 0) {
return STDERR_COPY;
}
#if defined O_CLOEXEC
auto fd = open(path, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC,
S_IRUSR | S_IWUSR | S_IRGRP);
#else // !O_CLOEXEC
auto fd =
open(path, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP);
// We get race condition if execve is called at the same time.
if (fd != -1) {
make_socket_closeonexec(fd);
}
#endif // !O_CLOEXEC
if (fd == -1) {
return -1;
}
return fd;
}
std::string ascii_dump(const uint8_t *data, size_t len) {
std::string res;
@@ -1465,6 +1408,30 @@ int sha256(uint8_t *res, const StringRef &s) {
return 0;
}
bool is_hex_string(const StringRef &s) {
if (s.size() % 2) {
return false;
}
for (auto c : s) {
if (!is_hex_digit(c)) {
return false;
}
}
return true;
}
StringRef decode_hex(BlockAllocator &balloc, const StringRef &s) {
auto iov = make_byte_ref(balloc, s.size() + 1);
auto p = iov.base;
for (auto it = std::begin(s); it != std::end(s); it += 2) {
*p++ = (hex_to_uint(*it) << 4) | hex_to_uint(*(it + 1));
}
*p = '\0';
return StringRef{iov.base, p};
}
} // namespace util
} // namespace nghttp2

View File

@@ -70,6 +70,8 @@ constexpr auto NGHTTP2_H2_14 = StringRef::from_lit("h2-14");
constexpr auto NGHTTP2_H1_1_ALPN = StringRef::from_lit("\x8http/1.1");
constexpr auto NGHTTP2_H1_1 = StringRef::from_lit("http/1.1");
constexpr size_t NGHTTP2_MAX_UINT64_DIGITS = str_size("18446744073709551615");
namespace util {
inline bool is_alpha(const char c) {
@@ -82,6 +84,9 @@ inline bool is_hex_digit(const char c) {
return is_digit(c) || ('A' <= c && c <= 'F') || ('a' <= c && c <= 'f');
}
// Returns true if |s| is hex string.
bool is_hex_string(const StringRef &s);
bool in_rfc3986_unreserved_chars(const char c);
bool in_rfc3986_sub_delims(const char c);
@@ -147,6 +152,11 @@ template <size_t N> std::string format_hex(const std::array<uint8_t, N> &s) {
StringRef format_hex(BlockAllocator &balloc, const StringRef &s);
// decode_hex decodes hex string |s|, returns the decoded byte string.
// This function assumes |s| is hex string, that is is_hex_string(s)
// == true.
StringRef decode_hex(BlockAllocator &balloc, const StringRef &s);
// Returns given time |t| from epoch in HTTP Date format (e.g., Mon,
// 10 Oct 2016 10:25:58 GMT).
std::string http_date(time_t t);
@@ -176,7 +186,7 @@ time_t parse_http_date(const StringRef &s);
char upcase(char c);
inline char lowcase(char c) {
static unsigned char tbl[] = {
constexpr static unsigned char tbl[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
@@ -374,7 +384,7 @@ template <typename T, typename OutputIt> OutputIt utos(OutputIt dst, T n) {
template <typename T>
StringRef make_string_ref_uint(BlockAllocator &balloc, T n) {
auto iov = make_byte_ref(balloc, str_size("18446744073709551615") + 1);
auto iov = make_byte_ref(balloc, NGHTTP2_MAX_UINT64_DIGITS + 1);
auto p = iov.base;
p = util::utos(p, n);
*p = '\0';
@@ -443,7 +453,7 @@ void to_token68(std::string &base64str);
StringRef to_base64(BlockAllocator &balloc, const StringRef &token68str);
void show_candidates(const char *unkopt, option *options);
void show_candidates(const char *unkopt, const option *options);
bool has_uri_field(const http_parser_url &u, http_parser_url_fields field);
@@ -453,6 +463,9 @@ bool fieldeq(const char *uri1, const http_parser_url &u1, const char *uri2,
bool fieldeq(const char *uri, const http_parser_url &u,
http_parser_url_fields field, const char *t);
bool fieldeq(const char *uri, const http_parser_url &u,
http_parser_url_fields field, const StringRef &t);
StringRef get_uri_field(const char *uri, const http_parser_url &u,
http_parser_url_fields field);
@@ -481,24 +494,6 @@ std::string to_numeric_addr(const Address *addr);
// Sets |port| to |addr|.
void set_port(Address &addr, uint16_t port);
// Makes internal copy of stderr (and possibly stdout in the future),
// which is then used as pointer to /dev/stderr or /proc/self/fd/2
void store_original_fds();
// Restores the original stderr that was stored with copy_original_fds
// Used just before execv
void restore_original_fds();
// Closes |fd| which was returned by open_log_file (see below)
// and sets it to -1. In the case that |fd| points to stdout or
// stderr, or is -1, the descriptor is not closed (but still set to -1).
void close_log_file(int &fd);
// Opens |path| with O_APPEND enabled. If file does not exist, it is
// created first. This function returns file descriptor referring the
// opened file if it succeeds, or -1.
int open_log_file(const char *path);
// Returns ASCII dump of |data| of length |len|. Only ASCII printable
// characters are preserved. Other characters are replaced with ".".
std::string ascii_dump(const uint8_t *data, size_t len);

View File

@@ -593,4 +593,20 @@ void test_util_format_hex(void) {
CU_ASSERT("" == util::format_hex(balloc, StringRef::from_lit("")));
}
void test_util_is_hex_string(void) {
CU_ASSERT(util::is_hex_string(StringRef{}));
CU_ASSERT(util::is_hex_string(StringRef::from_lit("0123456789abcdef")));
CU_ASSERT(util::is_hex_string(StringRef::from_lit("0123456789ABCDEF")));
CU_ASSERT(!util::is_hex_string(StringRef::from_lit("000")));
CU_ASSERT(!util::is_hex_string(StringRef::from_lit("XX")));
}
void test_util_decode_hex(void) {
BlockAllocator balloc(4096, 4096);
CU_ASSERT("\x0f\xf0" ==
util::decode_hex(balloc, StringRef::from_lit("0ff0")));
CU_ASSERT("" == util::decode_hex(balloc, StringRef{}));
}
} // namespace shrpx

View File

@@ -64,6 +64,8 @@ void test_util_make_hostport(void);
void test_util_strifind(void);
void test_util_random_alpha_digit(void);
void test_util_format_hex(void);
void test_util_is_hex_string(void);
void test_util_decode_hex(void);
} // namespace shrpx