mirror of
https://github.com/nghttp2/nghttp2.git
synced 2026-03-29 09:19:18 +08:00
Compare commits
97 Commits
v1.18.1
...
asio_threa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d1f4dafd73 | ||
|
|
13fc54c6a9 | ||
|
|
36a2023310 | ||
|
|
51e474c097 | ||
|
|
0df13452aa | ||
|
|
aad3e275d1 | ||
|
|
1649948e78 | ||
|
|
6d3e010ae7 | ||
|
|
7dddac081e | ||
|
|
588dd33241 | ||
|
|
2d9be885ec | ||
|
|
14ccb24be5 | ||
|
|
025ec85144 | ||
|
|
bd97886d8e | ||
|
|
0b1ddad62b | ||
|
|
540853bde8 | ||
|
|
c757f7d848 | ||
|
|
052f3a3871 | ||
|
|
2ae83e871b | ||
|
|
1cc08c0a51 | ||
|
|
16be89f9cc | ||
|
|
b72c5f104e | ||
|
|
7e6eb7e02a | ||
|
|
712b08e8ed | ||
|
|
fbf156d22d | ||
|
|
965a0e87e5 | ||
|
|
2b55ca1ce6 | ||
|
|
534c01896c | ||
|
|
c25197ac40 | ||
|
|
becae65257 | ||
|
|
ba9f2c3ae2 | ||
|
|
0387525b77 | ||
|
|
5311185333 | ||
|
|
2fc2a27ac1 | ||
|
|
db938afd66 | ||
|
|
89ddc47616 | ||
|
|
90b7849af1 | ||
|
|
3176e872b3 | ||
|
|
16206d5f67 | ||
|
|
0f33749790 | ||
|
|
5e7e4c0cc0 | ||
|
|
25503ad763 | ||
|
|
8f513fceca | ||
|
|
91af4ed70d | ||
|
|
685e926494 | ||
|
|
a2afd393ed | ||
|
|
4e9098bccf | ||
|
|
33aa327ef5 | ||
|
|
9067ff5eee | ||
|
|
efeede4192 | ||
|
|
6a8749873f | ||
|
|
b1b8308555 | ||
|
|
9b574a5a76 | ||
|
|
0567f1f038 | ||
|
|
4be5de1163 | ||
|
|
9db1c9467c | ||
|
|
3444b42d44 | ||
|
|
6595ae26ea | ||
|
|
41d8a3ac09 | ||
|
|
175001a8d9 | ||
|
|
7cf9e00283 | ||
|
|
8a3eb3f066 | ||
|
|
7e1a0d204b | ||
|
|
cbca2e35b5 | ||
|
|
fc9bdf024f | ||
|
|
3f97e6cd3a | ||
|
|
4fa150c494 | ||
|
|
e8b2508036 | ||
|
|
ac399e41ac | ||
|
|
95dd908834 | ||
|
|
9c7e54d9b5 | ||
|
|
3c03024881 | ||
|
|
36dfc0a56a | ||
|
|
55bf6cdb15 | ||
|
|
0abc220013 | ||
|
|
c28900990a | ||
|
|
5108193d7b | ||
|
|
79a24f5dd9 | ||
|
|
83c759572c | ||
|
|
1a07fb000b | ||
|
|
4aab15999d | ||
|
|
441982674f | ||
|
|
8256c6e070 | ||
|
|
ae87a44b94 | ||
|
|
87d1692e27 | ||
|
|
1d2f008656 | ||
|
|
b064d8a9ff | ||
|
|
528af200b6 | ||
|
|
c6827a7dac | ||
|
|
55ecb082ee | ||
|
|
5f2cf461e6 | ||
|
|
b313386988 | ||
|
|
3933280d29 | ||
|
|
2b6073900f | ||
|
|
d1ba43a69f | ||
|
|
a0779edec4 | ||
|
|
d70fefe72f |
2
AUTHORS
2
AUTHORS
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"`
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
.
|
||||
|
||||
@@ -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
|
||||
.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
----------------------------------------
|
||||
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 = [
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 *
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"));
|
||||
|
||||
@@ -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_);
|
||||
}
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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'},
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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_;
|
||||
};
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
169
src/nghttp.cc
169
src/nghttp.cc
@@ -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(
|
||||
|
||||
14
src/nghttp.h
14
src/nghttp.h
@@ -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;
|
||||
|
||||
@@ -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'},
|
||||
|
||||
@@ -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) ||
|
||||
|
||||
136
src/shrpx.cc
136
src/shrpx.cc
@@ -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;
|
||||
}
|
||||
|
||||
12
src/shrpx.h
12
src/shrpx.h
@@ -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; }
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 "
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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(' ');
|
||||
|
||||
355
src/shrpx_log.cc
355
src/shrpx_log.cc
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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,
|
||||
|
||||
116
src/shrpx_ssl.cc
116
src/shrpx_ssl.cc
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
16
src/ssl.cc
16
src/ssl.cc
@@ -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
|
||||
|
||||
16
src/ssl.h
16
src/ssl.h
@@ -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);
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
109
src/util.cc
109
src/util.cc
@@ -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
|
||||
|
||||
37
src/util.h
37
src/util.h
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user