Compare commits

...

99 Commits

Author SHA1 Message Date
Tatsuhiro Tsujikawa
6384375098 Update manual pages 2018-12-10 00:25:36 +09:00
Tatsuhiro Tsujikawa
27801e98ae Bump up version number to 1.35.1 2018-12-10 00:22:37 +09:00
Tatsuhiro Tsujikawa
60e020a85b nghttpx: Fix broken trailing slash handling
nghttpx allows a pattern with trailing slash to match a request path
without it.  Previously, under certain pattern registration, this does
not work.
2018-12-10 00:22:02 +09:00
Tatsuhiro Tsujikawa
e520469b3e Update manual pages 2018-11-23 10:07:33 +09:00
Tatsuhiro Tsujikawa
54067256eb Bump up version number to 1.35.0 2018-11-23 10:04:04 +09:00
Tatsuhiro Tsujikawa
c4d2eeeec7 Update AUTHORS 2018-11-23 10:01:20 +09:00
Tatsuhiro Tsujikawa
f51e696e4a asio: Add stop() to listen_and_serve doc 2018-11-18 17:30:35 +09:00
Tatsuhiro Tsujikawa
a433b132fc Merge pull request #1260 from nghttp2/h2load-non-final-response
h2load: Handle HTTP/1 non-final response
2018-11-15 17:32:15 +09:00
Tatsuhiro Tsujikawa
cf48a56d2e Merge pull request #1238 from jktjkt/cmake-fix-libevent-detection
cmake: Fix libevent version detection
2018-11-15 17:11:02 +09:00
Tatsuhiro Tsujikawa
6cad1b243b nghttpx: Write mruby send_info early 2018-11-15 10:17:47 +09:00
Tatsuhiro Tsujikawa
3c393dca58 nghttpx: Fix assertion failure on mruby send_info with HTTP/1 frontend 2018-11-15 10:17:41 +09:00
Tatsuhiro Tsujikawa
172924457f h2load: Handle HTTP/1 non-final response 2018-11-15 10:13:19 +09:00
Tatsuhiro Tsujikawa
f6644a92af make clang-format 2018-11-09 22:29:18 +09:00
Tatsuhiro Tsujikawa
48998f7239 Merge pull request #1222 from donny-dont/fix/declspec
Use __has_declspec_attribute for shared builds
2018-11-09 22:18:06 +09:00
Tatsuhiro Tsujikawa
15ff52f9fb Update README 2018-11-02 18:28:00 +09:00
Tatsuhiro Tsujikawa
6c03bb142b Upgrade travis toolchain 2018-11-02 17:57:16 +09:00
Tatsuhiro Tsujikawa
524b439221 Fix travis build failure 2018-11-02 17:56:53 +09:00
Tatsuhiro Tsujikawa
859bf2bc41 Update manual pages 2018-11-02 15:44:57 +09:00
Tatsuhiro Tsujikawa
b5619fb156 h2load: Clarify that time for connect includes TLS handshake 2018-11-02 15:43:35 +09:00
Tatsuhiro Tsujikawa
dcbe0c690f nghttpx: Simplify move ctor and operator 2018-11-02 15:40:53 +09:00
Tatsuhiro Tsujikawa
2996c28456 nghttpx: Cleanup 2018-11-02 15:16:36 +09:00
Tatsuhiro Tsujikawa
42e8ceb656 nghttpx: Convert API status code to enum class 2018-11-02 14:14:48 +09:00
Tatsuhiro Tsujikawa
1daf9ce8b7 nghttpx: Convert WorkerEventType to enum class 2018-11-02 14:14:48 +09:00
Tatsuhiro Tsujikawa
d68edf56c0 nghttpx: Convert MemcachedStatusCode to enum class 2018-11-02 14:14:48 +09:00
Tatsuhiro Tsujikawa
0c4e9fef29 nghttpx: Convert memcached op to enum class 2018-11-02 14:14:48 +09:00
Tatsuhiro Tsujikawa
571404c6e8 nghttpx: Convert MemcachedParseState to enum class 2018-11-02 14:14:48 +09:00
Tatsuhiro Tsujikawa
4d562b773b nghttpx: Convert LogFragmentType to enum class 2018-11-02 14:14:48 +09:00
Tatsuhiro Tsujikawa
e62258713e nghttpx: Convert connection check status to enum class 2018-11-02 14:14:48 +09:00
Tatsuhiro Tsujikawa
4bd075defd nghttpx: Convert Http2Session state to enum class 2018-11-02 14:14:48 +09:00
Tatsuhiro Tsujikawa
b46a324943 nghttpx: Convert FreelistZone to enum class 2018-10-17 14:19:58 +09:00
Tatsuhiro Tsujikawa
4bd44b9cdf nghttpx: Convert dispatch state to enum class 2018-10-17 14:19:58 +09:00
Tatsuhiro Tsujikawa
1b42110d4f nghttpx: Make Downstream state enum class 2018-10-17 14:19:58 +09:00
Tatsuhiro Tsujikawa
0735ec55f3 nghttpx: Convert shrpx_connect_proto to enum class 2018-10-17 14:19:58 +09:00
Tatsuhiro Tsujikawa
00554779e1 nghttpx: Convert DNSResolverStatus to enum class 2018-10-17 14:19:58 +09:00
Tatsuhiro Tsujikawa
0963f38935 nghttpx: Convert SerialEventType to enum class 2018-10-17 14:19:58 +09:00
Tatsuhiro Tsujikawa
1abfa3ca5f nghttpx: Make TLS handshake state enum class 2018-10-17 08:52:27 +09:00
Tatsuhiro Tsujikawa
f2159bc2c1 nghttpx: Convert UpstreamAltMode to enum class 2018-10-17 08:38:55 +09:00
Tatsuhiro Tsujikawa
b0eb68ee9e nghttpx: Convert shrpx_forwarded_node_type to enum class 2018-10-16 23:10:17 +09:00
Tatsuhiro Tsujikawa
e7b7b037f6 nghttpx: Convert shrpx_cookie_secure to enum class 2018-10-16 23:06:59 +09:00
Tatsuhiro Tsujikawa
5e4f434fd8 nghttpx: Convert shrpx_session_affinity to enum class 2018-10-16 23:03:17 +09:00
Tatsuhiro Tsujikawa
20ea964f2f nghttpx: Convert shrpx_proto to enum class 2018-10-16 22:59:34 +09:00
Tatsuhiro Tsujikawa
d105619bc3 src: Remove extra braces if possible 2018-10-15 23:46:33 +09:00
Tatsuhiro Tsujikawa
ec5729b1fa Use std::make_unique 2018-10-15 23:02:44 +09:00
Tatsuhiro Tsujikawa
6c9196953e Use C++14 2018-10-15 22:35:05 +09:00
Tatsuhiro Tsujikawa
46576178a3 Don't send Transfer-Encoding to pre-HTTP/1.1 clients 2018-10-14 22:57:54 +09:00
Tatsuhiro Tsujikawa
5e925f873e Update doc 2018-10-14 22:57:11 +09:00
Tatsuhiro Tsujikawa
153531d4d0 nghttpx: Use the same type as standard stream operator<< 2018-10-07 22:19:00 +09:00
Tatsuhiro Tsujikawa
f7287df03f Bump up version number to 1.35.0-DEV 2018-10-04 12:38:10 +09:00
Tatsuhiro Tsujikawa
2b085815b7 Update manual pages 2018-10-04 12:31:06 +09:00
Tatsuhiro Tsujikawa
986fa30264 Bump up version number to 1.34.0, LT revision to 31:1:17 2018-10-04 12:30:18 +09:00
Tatsuhiro Tsujikawa
7c8cb3a0ce nghttpx: Improve CONNECT response status handling 2018-10-04 12:04:15 +09:00
Tatsuhiro Tsujikawa
334c439ce0 Fix bug that regular CONNECT does not work 2018-10-04 12:02:46 +09:00
Jan Kundrát
6c17ed7e61 cmake: Fix libevent version detection
On my x86_64 Gentoo Linux, the CMake build won't find libevent because
Gentoo wraps include files via multilib-aware wrappers. This means that
the "real" include file lives in
/usr/include/x86_64-pc-linux-gnu/event2/event-config.h , and that
there's a proxy file at /usr/include/event2/event-config.h which check
the compile target's architecture and includes the real file from a
correct directory.

In other words, nghttp2's CMake FindLibevent.cmake reads a wrong file
and ends up not detecting the libevent's version.

This patch fixes it by simply using the version reported by pkg-config
as the libevent's version if the original method fails. I will be happy
to change this patch to always check version from pkg-config if you're
OK with that.

Signed-off-by: Jan Kundrát <jan.kundrat@cesnet.cz>
2018-10-03 17:38:17 +02:00
Tatsuhiro Tsujikawa
6700626c30 Rule out content-length in the successful response to CONNECT 2018-10-03 23:24:32 +09:00
Tatsuhiro Tsujikawa
15162addc4 Update manual pages 2018-10-02 01:34:32 +09:00
Tatsuhiro Tsujikawa
9327077741 Merge pull request #1235 from nghttp2/backend-conn-timeout
nghttpx: Add read/write-timeout parameters to backend option
2018-09-30 13:17:24 +09:00
Tatsuhiro Tsujikawa
aeb92bbbe2 nghttpx: Add read/write-timeout parameters to backend option 2018-09-30 12:32:43 +09:00
Tatsuhiro Tsujikawa
fc7489e044 nghttpx: Fix mruby parameter validation 2018-09-30 12:30:19 +09:00
Tatsuhiro Tsujikawa
87ac872fdc nghttpx: Update doc 2018-09-30 12:28:43 +09:00
Tatsuhiro Tsujikawa
c278adde7a nghttpx: Log error when mruby file cannot be opened 2018-09-30 12:23:01 +09:00
Tatsuhiro Tsujikawa
f94d720909 Merge pull request #1234 from nghttp2/nghttpx-rfc8441
nghttpx: Implement RFC 8441 Bootstrapping WebSocket with HTTP/2
2018-09-29 11:54:47 +09:00
Tatsuhiro Tsujikawa
9b9baa6bd9 Update doc 2018-09-29 11:46:11 +09:00
Tatsuhiro Tsujikawa
02566ee383 nghttpx: Update doc 2018-09-29 11:42:37 +09:00
Tatsuhiro Tsujikawa
3002f31b1f src: Add debug output for SETTINGS_ENABLE_CONNECT_PROTOCOL 2018-09-29 11:39:49 +09:00
Tatsuhiro Tsujikawa
d2a594a753 nghttpx: Implement RFC 8441 Bootstrapping WebSocket with HTTP/2 2018-09-29 11:35:41 +09:00
Tatsuhiro Tsujikawa
651e147711 Allow client sending :protocol optimistically 2018-09-28 00:12:02 +09:00
Tatsuhiro Tsujikawa
a42faf1cc2 nghttpx: Write TLS alert during handshake 2018-09-23 18:01:38 +09:00
Tatsuhiro Tsujikawa
4aac05e193 Merge pull request #1231 from nghttp2/ws-lib-only
Implement RFC 8441
2018-09-23 17:34:53 +09:00
Tatsuhiro Tsujikawa
b80dfaa8a0 Adjustment for RFC 8441 2018-09-23 11:22:30 +09:00
Tatsuhiro Tsujikawa
a19d8f5d31 Deal with :protocol pseudo header 2018-09-23 10:36:30 +09:00
Tatsuhiro Tsujikawa
33f6e90a56 Add NGHTTP2_TOKEN__PROTOCOL 2018-09-23 10:36:30 +09:00
Tatsuhiro Tsujikawa
ed7fabcbc2 Add SETTINGS_ENABLE_CONNECT_PROTOCOL 2018-09-23 10:36:30 +09:00
Tatsuhiro Tsujikawa
8753b6da14 Update doc 2018-09-17 16:12:15 +09:00
Tatsuhiro Tsujikawa
f2de733bdf Update neverbleed to fix OpenSSL 1.1.1 issues 2018-09-16 22:55:07 +09:00
Tatsuhiro Tsujikawa
88ff8c69a0 Update mruby 1.4.1 2018-09-16 22:54:09 +09:00
Tatsuhiro Tsujikawa
a63558a1eb nghttpx: Call OCSP_response_get1_basic only when OCSP status is successful 2018-09-16 22:19:27 +09:00
Tatsuhiro Tsujikawa
3575a1325e nghttpx: Fix crash with plain text HTTP 2018-09-15 12:16:23 +09:00
Tatsuhiro Tsujikawa
e2de2fee69 Update bash_completion 2018-09-15 11:15:22 +09:00
Tatsuhiro Tsujikawa
9f415979fb Update manual pages 2018-09-15 11:15:04 +09:00
Tatsuhiro Tsujikawa
4bfc0cd196 Merge pull request #1230 from nghttp2/nghttpx-faster-logging
nghttpx: Get rid of std::stringstream from Log
2018-09-14 23:13:03 +09:00
Tatsuhiro Tsujikawa
9c824b87fe nghttpx: Get rid of std::stringstream from Log 2018-09-14 22:58:48 +09:00
Tatsuhiro Tsujikawa
a1ea1696be Make VALID_HD_NAME_CHARS and VALID_HD_VALUE_CHARS const qualified 2018-09-13 23:50:31 +09:00
Tatsuhiro Tsujikawa
dfc0f248c6 Make static_table const qualified 2018-09-13 23:48:53 +09:00
Tatsuhiro Tsujikawa
ed7c9db2a6 nghttpx: Add mruby env.tls_handshake_finished 2018-09-09 22:59:35 +09:00
Tatsuhiro Tsujikawa
5b42815afb nghttpx: Strip incoming Early-Data header field by default 2018-09-09 22:37:22 +09:00
Tatsuhiro Tsujikawa
cfe7fa9a75 nghttpx: Add --tls13-ciphers and --tls-client-ciphers options 2018-09-09 16:35:47 +09:00
Tatsuhiro Tsujikawa
cb8a9d58fd src: Remove TLSv1.3 ciphers from DEFAULT_CIPHER_LIST
TLSv1.3 ciphers are treated differently from the ciphers for TLSv1.2
or earlier.
2018-09-09 15:53:04 +09:00
Tatsuhiro Tsujikawa
023b94480b Merge branch 'tls13-early-data' 2018-09-09 15:48:25 +09:00
Tatsuhiro Tsujikawa
9b03c64f68 nghttpx: Should postpone early data by default 2018-09-08 19:22:30 +09:00
Tatsuhiro Tsujikawa
b8eccec62d nghttpx: Disable OpenSSL anti-replay 2018-09-08 19:10:59 +09:00
Tatsuhiro Tsujikawa
9f21258720 Specify SSL_CTX_set_max_early_data and add an option to change max value 2018-09-08 17:59:28 +09:00
Tatsuhiro Tsujikawa
47f6012407 nghttpx: Add an option to postpone early data processing 2018-09-08 17:57:21 +09:00
Tatsuhiro Tsujikawa
770e44de4d Implement draft-ietf-httpbis-replay-02
nghttpx sends early-data header field when forwarding requests which
are received in TLSv1.3 early data, and the TLS handshake is still in
progress.
2018-09-08 17:54:35 +09:00
Tatsuhiro Tsujikawa
2ab319c137 Don't hide error code from openssl 2018-09-08 17:54:35 +09:00
Tatsuhiro Tsujikawa
3992302432 Remove SSL_ERROR_WANT_WRITE handling 2018-09-08 17:54:35 +09:00
Tatsuhiro Tsujikawa
b30f312a70 Honor SSL_read semantics 2018-09-08 17:54:35 +09:00
Tatsuhiro Tsujikawa
c5cdb78a95 nghttpx: Add TLSv1.3 0-RTT early data support 2018-09-08 17:54:35 +09:00
Don
d82811303b Use __has_declspec_attribute for shared builds 2018-09-05 10:01:50 -07:00
Tatsuhiro Tsujikawa
f79a58120e Bump up version number to 1.34.0 2018-09-02 15:55:08 +09:00
112 changed files with 3776 additions and 1440 deletions

3
.gitmodules vendored
View File

@@ -3,4 +3,5 @@
url = https://github.com/mruby/mruby
[submodule "third-party/neverbleed"]
path = third-party/neverbleed
url = https://github.com/h2o/neverbleed.git
url = https://github.com/tatsuhiro-t/neverbleed.git
branch = openssl111fix

View File

@@ -12,8 +12,10 @@ addons:
apt:
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-trusty-7
packages:
- g++-7
- g++-8
- clang-7
- autoconf
- automake
- autotools-dev
@@ -32,7 +34,8 @@ addons:
- cmake-data
before_install:
- $CC --version
- if [ "$CXX" = "g++" ]; then export CXX="g++-7" CC="gcc-7"; fi
- if [ "$CXX" = "g++" ]; then export CXX="g++-8" CC="gcc-8"; fi
- if [ "$CXX" = "clang++" ]; then export CXX="clang++-7" CC="clang-7"; fi
- $CC --version
- go version
- cmake --version

View File

@@ -36,6 +36,7 @@ Dave Reisner
David Beitey
David Weekly
Dmitriy Vetutnev
Don
Dylan Plecki
Etienne Cimon
Fabian Möller
@@ -44,6 +45,7 @@ Gabi Davar
Gitai
Google Inc.
Jacob Champion
Jan Kundrát
Jan-E
Janusz Dziemidowicz
Jay Satiro

View File

@@ -24,12 +24,12 @@
cmake_minimum_required(VERSION 3.0)
# XXX using 1.8.90 instead of 1.9.0-DEV
project(nghttp2 VERSION 1.33.0)
project(nghttp2 VERSION 1.35.1)
# See versioning rule:
# http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
set(LT_CURRENT 31)
set(LT_REVISION 0)
set(LT_REVISION 1)
set(LT_AGE 17)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
@@ -117,7 +117,7 @@ else()
endif()
include(ExtractValidFlags)
foreach(_cxx1x_flag -std=c++11 -std=c++0x)
foreach(_cxx1x_flag -std=c++14)
extract_valid_cxx_flags(_cxx1x_flag_supported ${_cxx1x_flag})
if(_cxx1x_flag_supported)
set(CXX1XCXXFLAGS ${_cxx1x_flag})

View File

@@ -145,8 +145,8 @@ minimizes the risk of private key leakage when serious bug like
Heartbleed is exploited. The neverbleed is disabled by default. To
enable it, use ``--with-neverbleed`` configure option.
In order to compile the source code, gcc >= 4.8.3 or clang >= 3.4 is
required.
In order to compile the source code, gcc >= 6.0 or clang >= 6.0 is
required. C++ source code requires C++14 language features.
.. note::
@@ -1332,7 +1332,7 @@ are:
* Boost::Thread
The server API is designed to build an HTTP/2 server very easily to utilize
C++11 anonymous functions and closures. The bare minimum example of
C++14 anonymous functions and closures. The bare minimum example of
an HTTP/2 server looks like this:
.. code-block:: cpp

View File

@@ -40,6 +40,9 @@ if(LIBEVENT_INCLUDE_DIR)
# Libevent 2.0
file(STRINGS "${LIBEVENT_INCLUDE_DIR}/event2/event-config.h"
LIBEVENT_VERSION REGEX "${_version_regex}")
if("${LIBEVENT_VERSION}" STREQUAL "")
set(LIBEVENT_VERSION ${PC_LIBEVENT_VERSION})
endif()
else()
# Libevent 1.4
file(STRINGS "${LIBEVENT_INCLUDE_DIR}/event-config.h"

View File

@@ -25,7 +25,7 @@ dnl Do not change user variables!
dnl http://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html
AC_PREREQ(2.61)
AC_INIT([nghttp2], [1.33.0], [t-tujikawa@users.sourceforge.net])
AC_INIT([nghttp2], [1.35.1], [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, 31)
AC_SUBST(LT_REVISION, 0)
AC_SUBST(LT_REVISION, 1)
AC_SUBST(LT_AGE, 17)
major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"`
@@ -180,7 +180,7 @@ fi
save_CXXFLAGS="$CXXFLAGS"
CXXFLAGS=
AX_CXX_COMPILE_STDCXX_11([noext], [optional])
AX_CXX_COMPILE_STDCXX([14], [noext], [optional])
CXX1XCXXFLAGS="$CXXFLAGS"
CXXFLAGS="$save_CXXFLAGS"

View File

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

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "H2LOAD" "1" "Sep 02, 2018" "1.33.0" "nghttp2"
.TH "H2LOAD" "1" "Dec 10, 2018" "1.35.1" "nghttp2"
.SH NAME
h2load \- HTTP/2 benchmarking tool
.
@@ -377,13 +377,16 @@ range (mean +/\- sd) against total number of successful requests.
.INDENT 7.0
.TP
.B min
The minimum time taken to connect to a server.
The minimum time taken to connect to a server including TLS
handshake.
.TP
.B max
The maximum time taken to connect to a server.
The maximum time taken to connect to a server including TLS
handshake.
.TP
.B mean
The mean time taken to connect to a server.
The mean time taken to connect to a server including TLS
handshake.
.TP
.B sd
The standard deviation of the time taken to connect to a server.

View File

@@ -313,11 +313,14 @@ time for request
time for connect
min
The minimum time taken to connect to a server.
The minimum time taken to connect to a server including TLS
handshake.
max
The maximum time taken to connect to a server.
The maximum time taken to connect to a server including TLS
handshake.
mean
The mean time taken to connect to a server.
The mean time taken to connect to a server including TLS
handshake.
sd
The standard deviation of the time taken to connect to a server.
+/- sd

View File

@@ -60,11 +60,14 @@ time for request
time for connect
min
The minimum time taken to connect to a server.
The minimum time taken to connect to a server including TLS
handshake.
max
The maximum time taken to connect to a server.
The maximum time taken to connect to a server including TLS
handshake.
mean
The mean time taken to connect to a server.
The mean time taken to connect to a server including TLS
handshake.
sd
The standard deviation of the time taken to connect to a server.
+/- sd

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "NGHTTP" "1" "Sep 02, 2018" "1.33.0" "nghttp2"
.TH "NGHTTP" "1" "Dec 10, 2018" "1.35.1" "nghttp2"
.SH NAME
nghttp \- HTTP/2 client
.

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "NGHTTPD" "1" "Sep 02, 2018" "1.33.0" "nghttp2"
.TH "NGHTTPD" "1" "Dec 10, 2018" "1.35.1" "nghttp2"
.SH NAME
nghttpd \- HTTP/2 server
.

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "NGHTTPX" "1" "Sep 02, 2018" "1.33.0" "nghttp2"
.TH "NGHTTPX" "1" "Dec 10, 2018" "1.35.1" "nghttp2"
.SH NAME
nghttpx \- HTTP/2 proxy
.
@@ -138,11 +138,13 @@ The parameters are delimited by ";". The available
parameters are: "proto=<PROTO>", "tls",
"sni=<SNI_HOST>", "fall=<N>", "rise=<N>",
"affinity=<METHOD>", "dns", "redirect\-if\-not\-tls",
"upgrade\-scheme", and "mruby=<PATH>". 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
"upgrade\-scheme", "mruby=<PATH>",
"read\-timeout=<DURATION>", and
"write\-timeout=<DURATION>". The parameter consists of
keyword, and optionally followed by "=" and value. For
example, the parameter "proto=h2" consists of the
keyword "proto" and value "h2". The parameter "tls"
consists of the keyword "tls" without value. Each
parameter is described as follows.
.sp
The backend application protocol can be specified using
@@ -241,6 +243,14 @@ script file which is invoked when this pattern is
matched. All backends which share the same pattern must
have the same mruby path.
.sp
"read\-timeout=<DURATION>" and "write\-timeout=<DURATION>"
parameters specify the read and write timeout of the
backend connection when this pattern is matched. All
backends which share the same pattern must have the same
timeouts. If these timeouts are entirely omitted for a
pattern, \fI\%\-\-backend\-read\-timeout\fP and
\fI\%\-\-backend\-write\-timeout\fP are used.
.sp
Since ";" and ":" are used as delimiter, <PATTERN> must
not contain these characters. Since ";" has special
meaning in shell, the option value must be quoted.
@@ -601,19 +611,43 @@ Default: \fB2m\fP
.B \-\-ciphers=<SUITE>
Set allowed cipher list for frontend connection. The
format of the string is described in OpenSSL ciphers(1).
This option sets cipher suites for TLSv1.2 or earlier.
Use \fI\%\-\-tls13\-ciphers\fP for TLSv1.3.
.sp
Default: \fBECDHE\-ECDSA\-AES256\-GCM\-SHA384:ECDHE\-RSA\-AES256\-GCM\-SHA384:ECDHE\-ECDSA\-CHACHA20\-POLY1305:ECDHE\-RSA\-CHACHA20\-POLY1305:ECDHE\-ECDSA\-AES128\-GCM\-SHA256:ECDHE\-RSA\-AES128\-GCM\-SHA256:ECDHE\-ECDSA\-AES256\-SHA384:ECDHE\-RSA\-AES256\-SHA384:ECDHE\-ECDSA\-AES128\-SHA256:ECDHE\-RSA\-AES128\-SHA256\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-tls13\-ciphers=<SUITE>
Set allowed cipher list for frontend connection. The
format of the string is described in OpenSSL ciphers(1).
This option sets cipher suites for TLSv1.3. Use
\fI\%\-\-ciphers\fP for TLSv1.2 or earlier.
.sp
Default: \fBTLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256\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).
This option sets cipher suites for TLSv1.2 or earlier.
Use \fI\%\-\-tls13\-client\-ciphers\fP for TLSv1.3.
.sp
Default: \fBECDHE\-ECDSA\-AES256\-GCM\-SHA384:ECDHE\-RSA\-AES256\-GCM\-SHA384:ECDHE\-ECDSA\-CHACHA20\-POLY1305:ECDHE\-RSA\-CHACHA20\-POLY1305:ECDHE\-ECDSA\-AES128\-GCM\-SHA256:ECDHE\-RSA\-AES128\-GCM\-SHA256:ECDHE\-ECDSA\-AES256\-SHA384:ECDHE\-RSA\-AES256\-SHA384:ECDHE\-ECDSA\-AES128\-SHA256:ECDHE\-RSA\-AES128\-SHA256\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-tls13\-client\-ciphers=<SUITE>
Set allowed cipher list for backend connection. The
format of the string is described in OpenSSL ciphers(1).
This option sets cipher suites for TLSv1.3. Use
\fI\%\-\-tls13\-client\-ciphers\fP for TLSv1.2 or earlier.
.sp
Default: \fBTLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-ecdh\-curves=<LIST>
Set supported curve list for frontend connections.
<LIST> is a colon separated list of curve NID or names
@@ -735,7 +769,7 @@ than TLSv1.2 is specified, make sure that the compatible
ciphers are included in \fI\%\-\-ciphers\fP option. The default
cipher list only includes ciphers compatible with
TLSv1.2 or above. The available versions are:
TLSv1.2, TLSv1.1, and TLSv1.0
TLSv1.3, TLSv1.2, TLSv1.1, and TLSv1.0
.sp
Default: \fBTLSv1.2\fP
.UNINDENT
@@ -748,9 +782,9 @@ done in case\-insensitive manner. The versions between
enabled. If the protocol list advertised by client does
not overlap this range, you will receive the error
message "unknown protocol". The available versions are:
TLSv1.2, TLSv1.1, and TLSv1.0
TLSv1.3, TLSv1.2, TLSv1.1, and TLSv1.0
.sp
Default: \fBTLSv1.2\fP
Default: \fBTLSv1.3\fP
.UNINDENT
.INDENT 0.0
.TP
@@ -1003,6 +1037,24 @@ 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
.INDENT 0.0
.TP
.B \-\-tls\-no\-postpone\-early\-data
By default, nghttpx postpones forwarding HTTP requests
sent in early data, including those sent in partially in
it, until TLS handshake finishes. If all backend server
recognizes "Early\-Data" header field, using this option
makes nghttpx not postpone forwarding request and get
full potential of 0\-RTT data.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-tls\-max\-early\-data=<SIZE>
Sets the maximum amount of 0\-RTT data that server
accepts.
.sp
Default: \fB16K\fP
.UNINDENT
.SS HTTP/2
.INDENT 0.0
.TP
@@ -1366,6 +1418,12 @@ is received, it is left unaltered.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-no\-strip\-incoming\-early\-data
Don\(aqt strip Early\-Data header field from inbound client
requests.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-no\-location\-rewrite
Don\(aqt rewrite location header field in default mode.
When \fI\%\-\-http2\-proxy\fP is used, location header field will
@@ -2105,6 +2163,15 @@ Return true if, and only if a SSL/TLS session is reused.
.B attribute [R] alpn
Return ALPN identifier negotiated in this connection.
.UNINDENT
.INDENT 7.0
.TP
.B attribute [R] tls_handshake_finished
Return true if SSL/TLS handshake has finished. If it returns
false in the request phase hook, the request is received in
TLSv1.3 early data (0\-RTT) and might be vulnerable to the
replay attack. nghttpx will send Early\-Data header field to
backend servers to indicate this.
.UNINDENT
.UNINDENT
.INDENT 0.0
.TP

View File

@@ -122,11 +122,13 @@ Connections
parameters are: "proto=<PROTO>", "tls",
"sni=<SNI_HOST>", "fall=<N>", "rise=<N>",
"affinity=<METHOD>", "dns", "redirect-if-not-tls",
"upgrade-scheme", and "mruby=<PATH>". 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
"upgrade-scheme", "mruby=<PATH>",
"read-timeout=<DURATION>", and
"write-timeout=<DURATION>". 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
@@ -225,6 +227,14 @@ Connections
matched. All backends which share the same pattern must
have the same mruby path.
"read-timeout=<DURATION>" and "write-timeout=<DURATION>"
parameters specify the read and write timeout of the
backend connection when this pattern is matched. All
backends which share the same pattern must have the same
timeouts. If these timeouts are entirely omitted for a
pattern, :option:`--backend-read-timeout` and
:option:`--backend-write-timeout` are used.
Since ";" and ":" are used as delimiter, <PATTERN> must
not contain these characters. Since ";" has special
meaning in shell, the option value must be quoted.
@@ -559,16 +569,38 @@ SSL/TLS
Set allowed cipher list for frontend connection. The
format of the string is described in OpenSSL ciphers(1).
This option sets cipher suites for TLSv1.2 or earlier.
Use :option:`--tls13-ciphers` for TLSv1.3.
Default: ``ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256``
.. option:: --tls13-ciphers=<SUITE>
Set allowed cipher list for frontend connection. The
format of the string is described in OpenSSL ciphers(1).
This option sets cipher suites for TLSv1.3. Use
:option:`--ciphers` for TLSv1.2 or earlier.
Default: ``TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256``
.. option:: --client-ciphers=<SUITE>
Set allowed cipher list for backend connection. The
format of the string is described in OpenSSL ciphers(1).
This option sets cipher suites for TLSv1.2 or earlier.
Use :option:`--tls13-client-ciphers` for TLSv1.3.
Default: ``ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256``
.. option:: --tls13-client-ciphers=<SUITE>
Set allowed cipher list for backend connection. The
format of the string is described in OpenSSL ciphers(1).
This option sets cipher suites for TLSv1.3. Use
:option:`--tls13-client-ciphers` for TLSv1.2 or earlier.
Default: ``TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256``
.. option:: --ecdh-curves=<LIST>
Set supported curve list for frontend connections.
@@ -679,7 +711,7 @@ SSL/TLS
ciphers are included in :option:`--ciphers` option. The default
cipher list only includes ciphers compatible with
TLSv1.2 or above. The available versions are:
TLSv1.2, TLSv1.1, and TLSv1.0
TLSv1.3, TLSv1.2, TLSv1.1, and TLSv1.0
Default: ``TLSv1.2``
@@ -691,9 +723,9 @@ SSL/TLS
enabled. If the protocol list advertised by client does
not overlap this range, you will receive the error
message "unknown protocol". The available versions are:
TLSv1.2, TLSv1.1, and TLSv1.0
TLSv1.3, TLSv1.2, TLSv1.1, and TLSv1.0
Default: ``TLSv1.2``
Default: ``TLSv1.3``
.. option:: --tls-ticket-key-file=<PATH>
@@ -921,6 +953,22 @@ SSL/TLS
consider to use :option:`--client-no-http2-cipher-black-list`
option. But be aware its implications.
.. option:: --tls-no-postpone-early-data
By default, nghttpx postpones forwarding HTTP requests
sent in early data, including those sent in partially in
it, until TLS handshake finishes. If all backend server
recognizes "Early-Data" header field, using this option
makes nghttpx not postpone forwarding request and get
full potential of 0-RTT data.
.. option:: --tls-max-early-data=<SIZE>
Sets the maximum amount of 0-RTT data that server
accepts.
Default: ``16K``
HTTP/2
~~~~~~
@@ -1237,6 +1285,11 @@ HTTP
Don't append to Via header field. If Via header field
is received, it is left unaltered.
.. option:: --no-strip-incoming-early-data
Don't strip Early-Data header field from inbound client
requests.
.. option:: --no-location-rewrite
Don't rewrite location header field in default mode.
@@ -1927,6 +1980,14 @@ respectively.
Return ALPN identifier negotiated in this connection.
.. rb:attr_reader:: tls_handshake_finished
Return true if SSL/TLS handshake has finished. If it returns
false in the request phase hook, the request is received in
TLSv1.3 early data (0-RTT) and might be vulnerable to the
replay attack. nghttpx will send Early-Data header field to
backend servers to indicate this.
.. rb:class:: Request
Object to represent request from client. The modification to

View File

@@ -437,6 +437,14 @@ respectively.
Return ALPN identifier negotiated in this connection.
.. rb:attr_reader:: tls_handshake_finished
Return true if SSL/TLS handshake has finished. If it returns
false in the request phase hook, the request is received in
TLSv1.3 early data (0-RTT) and might be vulnerable to the
replay attack. nghttpx will send Early-Data header field to
backend servers to indicate this.
.. rb:class:: Request
Object to represent request from client. The modification to

View File

@@ -110,9 +110,9 @@ HTTP Messaging
By default, nghttp2 library checks HTTP messaging rules described in
`HTTP/2 specification, section 8
<https://tools.ietf.org/html/draft-ietf-httpbis-http2-17#section-8>`_.
Everything described in that section is not validated however. We
briefly describe what the library does in this area. In the following
<https://tools.ietf.org/html/rfc7540#section-8>`_. Everything
described in that section is not validated however. We briefly
describe what the library does in this area. In the following
description, without loss of generality we omit CONTINUATION frame
since they must follow HEADERS frame and are processed atomically. In
other words, they are just one big HEADERS frame. To disable these
@@ -249,7 +249,7 @@ set to :type:`nghttp2_session_callbacks` using
`nghttp2_session_callbacks_set_pack_extension_callback()`.
For example, we will illustrate how to send `ALTSVC
<https://tools.ietf.org/html/draft-ietf-httpbis-alt-svc-14>`_ frame.
<https://tools.ietf.org/html/rfc7838>`_ frame.
.. code-block:: c

View File

@@ -471,6 +471,33 @@ 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.
TLSv1.3
-------
As of nghttpx v1.34.0, if it is built with OpenSSL 1.1.1 or later, it
supports TLSv1.3. 0-RTT data is supported, but by default its
processing is postponed until TLS handshake completes to mitigate
replay attack. This costs extra round trip and reduces effectiveness
of 0-RTT data. :option:`--tls-no-postpone-early-data` makes nghttpx
not wait for handshake to complete before forwarding request included
in 0-RTT to get full potential of 0-RTT data. In this case, nghttpx
adds ``Early-Data: 1`` header field when forwarding a request to a
backend server. All backend servers should recognize this header
field and understand that there is a risk for replay attack. See `RFC
8470 <https://tools.ietf.org/html/rfc8470>`_ for ``Early-Data`` header
field.
nghttpx disables anti replay protection provided by OpenSSL. The anti
replay protection of OpenSSL requires that a resumed request must hit
the same server which generates the session ticket. Therefore it
might not work nicely in a deployment where there are multiple nghttpx
instances sharing ticket encryption keys via memcached.
Because TLSv1.3 completely changes the semantics of cipher suite
naming scheme and structure, nghttpx provides the new option
:option:`--tls13-ciphers` and :option:`--tls13-client-ciphers` to
change preferred cipher list for TLSv1.3.
Migration from nghttpx v1.18.x or earlier
-----------------------------------------

View File

@@ -9,6 +9,7 @@ HEADERS = [
':scheme',
':status',
':host', # for spdy
':protocol',
'expect',
'host',
'if-modified-since',
@@ -31,6 +32,9 @@ HEADERS = [
"user-agent",
"date",
"content-type",
"early-data",
"sec-websocket-accept",
"sec-websocket-key",
# disallowed h1 headers
'connection',
'keep-alive',
@@ -40,4 +44,4 @@ HEADERS = [
]
if __name__ == '__main__':
gentokenlookup(HEADERS, 'HD')
gentokenlookup(HEADERS, 'HD_')

View File

@@ -67,6 +67,7 @@ HEADERS = [
('keep-alive',None),
('proxy-connection', None),
('upgrade', None),
(':protocol', None),
]
def to_enum_hd(k):

View File

@@ -50,4 +50,4 @@ if __name__ == '__main__':
continue
_, m, _ = line.split(',', 2)
methods.append(m.strip())
gentokenlookup(methods, 'HTTP')
gentokenlookup(methods, 'HTTP_')

View File

@@ -170,6 +170,11 @@ OPTIONS = [
"no-verify-ocsp",
"verify-client-tolerate-expired",
"ignore-per-pattern-mruby-error",
"tls-no-postpone-early-data",
"tls-max-early-data",
"tls13-ciphers",
"tls13-client-ciphers",
"no-strip-incoming-early-data",
]
LOGVARS = [
@@ -203,5 +208,5 @@ LOGVARS = [
]
if __name__ == '__main__':
gentokenlookup(OPTIONS, 'SHRPX_OPTID', value_type='char', comp_fun='util::strieq_l')
gentokenlookup(LOGVARS, 'SHRPX_LOGF', value_type='char', comp_fun='util::strieq_l', return_type='LogFragmentType', fail_value='SHRPX_LOGF_NONE')
gentokenlookup(OPTIONS, 'SHRPX_OPTID_', value_type='char', comp_fun='util::strieq_l')
gentokenlookup(LOGVARS, 'LogFragmentType::', value_type='char', comp_fun='util::strieq_l', return_type='LogFragmentType', fail_value='LogFragmentType::NONE')

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python
def to_enum_hd(k, prefix):
res = prefix + '_'
res = prefix
for c in k.upper():
if c == ':' or c == '-':
res += '_'
@@ -30,7 +30,7 @@ enum {'''
print '''\
{},'''.format(to_enum_hd(k, prefix))
print '''\
{}_MAXIDX,
{}MAXIDX,
}};'''.format(prefix)
def gen_index_header(tokens, prefix, value_type, comp_fun, return_type, fail_value):

View File

@@ -31,6 +31,11 @@
# define WIN32
#endif
/* Compatibility for non-Clang compilers */
#ifndef __has_declspec_attribute
# define __has_declspec_attribute(x) 0
#endif
#ifdef __cplusplus
extern "C" {
#endif
@@ -51,7 +56,8 @@ extern "C" {
#ifdef NGHTTP2_STATICLIB
# define NGHTTP2_EXTERN
#elif defined(WIN32)
#elif defined(WIN32) || (__has_declspec_attribute(dllexport) && \
__has_declspec_attribute(dllimport))
# ifdef BUILDING_NGHTTP2
# define NGHTTP2_EXTERN __declspec(dllexport)
# else /* !BUILDING_NGHTTP2 */
@@ -680,7 +686,12 @@ typedef enum {
/**
* SETTINGS_MAX_HEADER_LIST_SIZE
*/
NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE = 0x06
NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE = 0x06,
/**
* SETTINGS_ENABLE_CONNECT_PROTOCOL
* (`RFC 8441 <https://tools.ietf.org/html/rfc8441>`_)
*/
NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL = 0x08
} nghttp2_settings_id;
/* Note: If we add SETTINGS, update the capacity of
NGHTTP2_INBOUND_NUM_IV as well */

View File

@@ -1050,6 +1050,11 @@ int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv) {
break;
case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
break;
case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
if (iv[i].value != 0 && iv[i].value != 1) {
return 0;
}
break;
}
}
return 1;

View File

@@ -45,7 +45,7 @@
/* 3rd parameter is nghttp2_token value for header field name. We use
first enum value if same header names are repeated (e.g.,
:status). */
static nghttp2_hd_static_entry static_table[] = {
static const nghttp2_hd_static_entry static_table[] = {
MAKE_STATIC_ENT(":authority", "", 0, 3153725150u),
MAKE_STATIC_ENT(":method", "GET", 1, 695666056u),
MAKE_STATIC_ENT(":method", "POST", 1, 695666056u),
@@ -271,6 +271,15 @@ static int32_t lookup_token(const uint8_t *name, size_t namelen) {
break;
}
break;
case 9:
switch (name[8]) {
case 'l':
if (memeq(":protoco", name, 8)) {
return NGHTTP2_TOKEN__PROTOCOL;
}
break;
}
break;
case 10:
switch (name[9]) {
case 'e':
@@ -1159,7 +1168,7 @@ static search_result search_static_table(const nghttp2_nv *nv, int32_t token,
int name_only) {
search_result res = {token, 0};
int i;
nghttp2_hd_static_entry *ent;
const nghttp2_hd_static_entry *ent;
if (name_only) {
return res;
@@ -1184,7 +1193,7 @@ static search_result search_hd_table(nghttp2_hd_context *context,
int indexing_mode, nghttp2_hd_map *map,
uint32_t hash) {
search_result res = {-1, 0};
nghttp2_hd_entry *ent;
const nghttp2_hd_entry *ent;
int exact_match;
int name_only = indexing_mode == NGHTTP2_HD_NEVER_INDEXING;
@@ -1289,8 +1298,9 @@ nghttp2_hd_nv nghttp2_hd_table_get(nghttp2_hd_context *context, size_t idx) {
return hd_ringbuf_get(&context->hd_table, idx - NGHTTP2_STATIC_TABLE_LENGTH)
->nv;
} else {
nghttp2_hd_static_entry *ent = &static_table[idx];
nghttp2_hd_nv nv = {&ent->name, &ent->value, ent->token,
const nghttp2_hd_static_entry *ent = &static_table[idx];
nghttp2_hd_nv nv = {(nghttp2_rcbuf *)&ent->name,
(nghttp2_rcbuf *)&ent->value, ent->token,
NGHTTP2_NV_FLAG_NONE};
return nv;
}

View File

@@ -111,6 +111,7 @@ typedef enum {
NGHTTP2_TOKEN_KEEP_ALIVE,
NGHTTP2_TOKEN_PROXY_CONNECTION,
NGHTTP2_TOKEN_UPGRADE,
NGHTTP2_TOKEN__PROTOCOL,
} nghttp2_token;
struct nghttp2_hd_entry;

View File

@@ -340,7 +340,7 @@ const char *nghttp2_strerror(int error_code) {
}
/* Generated by gennmchartbl.py */
static int VALID_HD_NAME_CHARS[] = {
static const int VALID_HD_NAME_CHARS[] = {
0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */,
0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */,
0 /* BS */, 0 /* HT */, 0 /* LF */, 0 /* VT */,
@@ -428,7 +428,7 @@ int nghttp2_check_header_name(const uint8_t *name, size_t len) {
}
/* Generated by genvchartbl.py */
static int VALID_HD_VALUE_CHARS[] = {
static const int VALID_HD_VALUE_CHARS[] = {
0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */,
0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */,
0 /* BS */, 1 /* HT */, 0 /* LF */, 0 /* VT */,

View File

@@ -113,7 +113,7 @@ static int check_path(nghttp2_stream *stream) {
}
static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
int trailer) {
int trailer, int connect_protocol) {
if (nv->name->base[0] == ':') {
if (trailer ||
(stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
@@ -146,10 +146,6 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
return NGHTTP2_ERR_HTTP_HEADER;
}
stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
if (stream->http_flags &
(NGHTTP2_HTTP_FLAG__PATH | NGHTTP2_HTTP_FLAG__SCHEME)) {
return NGHTTP2_ERR_HTTP_HEADER;
}
}
break;
case 'S':
@@ -162,9 +158,6 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
}
break;
case NGHTTP2_TOKEN__PATH:
if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) {
return NGHTTP2_ERR_HTTP_HEADER;
}
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) {
return NGHTTP2_ERR_HTTP_HEADER;
}
@@ -175,9 +168,6 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
}
break;
case NGHTTP2_TOKEN__SCHEME:
if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) {
return NGHTTP2_ERR_HTTP_HEADER;
}
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) {
return NGHTTP2_ERR_HTTP_HEADER;
}
@@ -186,6 +176,15 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
stream->http_flags |= NGHTTP2_HTTP_FLAG_SCHEME_HTTP;
}
break;
case NGHTTP2_TOKEN__PROTOCOL:
if (!connect_protocol) {
return NGHTTP2_ERR_HTTP_HEADER;
}
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PROTOCOL)) {
return NGHTTP2_ERR_HTTP_HEADER;
}
break;
case NGHTTP2_TOKEN_HOST:
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG_HOST)) {
return NGHTTP2_ERR_HTTP_HEADER;
@@ -265,7 +264,7 @@ static int http_response_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
return NGHTTP2_ERR_REMOVE_HTTP_HEADER;
}
if (stream->status_code / 100 == 1 ||
(stream->status_code == 200 &&
(stream->status_code / 100 == 2 &&
(stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT))) {
return NGHTTP2_ERR_HTTP_HEADER;
}
@@ -458,7 +457,9 @@ int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
}
if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
return http_request_on_header(stream, nv, trailer);
return http_request_on_header(stream, nv, trailer,
session->server &&
session->pending_enable_connect_protocol);
}
return http_response_on_header(stream, nv, trailer);
@@ -466,8 +467,11 @@ int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
int nghttp2_http_on_request_headers(nghttp2_stream *stream,
nghttp2_frame *frame) {
if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) {
if ((stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0) {
if (!(stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) &&
(stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT)) {
if ((stream->http_flags &
(NGHTTP2_HTTP_FLAG__SCHEME | NGHTTP2_HTTP_FLAG__PATH)) ||
(stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0) {
return -1;
}
stream->content_length = -1;
@@ -478,6 +482,11 @@ int nghttp2_http_on_request_headers(nghttp2_stream *stream,
(NGHTTP2_HTTP_FLAG__AUTHORITY | NGHTTP2_HTTP_FLAG_HOST)) == 0) {
return -1;
}
if ((stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) &&
((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) == 0 ||
(stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0)) {
return -1;
}
if (!check_path(stream)) {
return -1;
}

View File

@@ -4361,6 +4361,9 @@ int nghttp2_session_update_local_settings(nghttp2_session *session,
case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
session->local_settings.max_header_list_size = iv[i].value;
break;
case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
session->local_settings.enable_connect_protocol = iv[i].value;
break;
}
}
@@ -4499,6 +4502,26 @@ int nghttp2_session_on_settings_received(nghttp2_session *session,
session->remote_settings.max_header_list_size = entry->value;
break;
case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
if (entry->value != 0 && entry->value != 1) {
return session_handle_invalid_connection(
session, frame, NGHTTP2_ERR_PROTO,
"SETTINGS: invalid SETTINGS_ENABLE_CONNECT_PROTOCOL");
}
if (!session->server &&
session->remote_settings.enable_connect_protocol &&
entry->value == 0) {
return session_handle_invalid_connection(
session, frame, NGHTTP2_ERR_PROTO,
"SETTINGS: server attempted to disable "
"SETTINGS_ENABLE_CONNECT_PROTOCOL");
}
session->remote_settings.enable_connect_protocol = entry->value;
break;
}
}
@@ -5250,6 +5273,7 @@ static void inbound_frame_set_settings_entry(nghttp2_inbound_frame *iframe) {
case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
break;
default:
DEBUGF("recv: unknown settings id=0x%02x\n", iv.settings_id);
@@ -7052,6 +7076,13 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags,
}
}
for (i = niv; i > 0; --i) {
if (iv[i - 1].settings_id == NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL) {
session->pending_enable_connect_protocol = (uint8_t)iv[i - 1].value;
break;
}
}
return 0;
}
@@ -7360,6 +7391,8 @@ uint32_t nghttp2_session_get_remote_settings(nghttp2_session *session,
return session->remote_settings.max_frame_size;
case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
return session->remote_settings.max_header_list_size;
case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
return session->remote_settings.enable_connect_protocol;
}
assert(0);
@@ -7381,6 +7414,8 @@ uint32_t nghttp2_session_get_local_settings(nghttp2_session *session,
return session->local_settings.max_frame_size;
case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
return session->local_settings.max_header_list_size;
case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
return session->local_settings.enable_connect_protocol;
}
assert(0);

View File

@@ -164,6 +164,7 @@ typedef struct {
uint32_t initial_window_size;
uint32_t max_frame_size;
uint32_t max_header_list_size;
uint32_t enable_connect_protocol;
} nghttp2_settings_storage;
typedef enum {
@@ -321,6 +322,9 @@ struct nghttp2_session {
/* Unacked local ENABLE_PUSH value. We use this to refuse
PUSH_PROMISE before SETTINGS ACK is received. */
uint8_t pending_enable_push;
/* Unacked local ENABLE_CONNECT_PROTOCOL value. We use this to
accept :protocol header field before SETTINGS_ACK is received. */
uint8_t pending_enable_connect_protocol;
/* Nonzero if the session is server side. */
uint8_t server;
/* Flags indicating GOAWAY is sent and/or received. The flags are

View File

@@ -130,7 +130,8 @@ typedef enum {
/* "http" or "https" scheme */
NGHTTP2_HTTP_FLAG_SCHEME_HTTP = 1 << 13,
/* set if final response is expected */
NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE = 1 << 14
NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE = 1 << 14,
NGHTTP2_HTTP_FLAG__PROTOCOL = 1 << 15,
} nghttp2_http_flag;
struct nghttp2_stream {

948
m4/ax_cxx_compile_stdcxx.m4 Normal file
View File

@@ -0,0 +1,948 @@
# ===========================================================================
# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional])
#
# DESCRIPTION
#
# Check for baseline language coverage in the compiler for the specified
# version of the C++ standard. If necessary, add switches to CXX and
# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard)
# or '14' (for the C++14 standard).
#
# The second argument, if specified, indicates whether you insist on an
# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.
# -std=c++11). If neither is specified, you get whatever works, with
# preference for an extended mode.
#
# The third argument, if specified 'mandatory' or if left unspecified,
# indicates that baseline support for the specified C++ standard is
# required and that the macro should error out if no mode with that
# support is found. If specified 'optional', then configuration proceeds
# regardless, after defining HAVE_CXX${VERSION} if and only if a
# supporting mode is found.
#
# LICENSE
#
# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
# Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
# Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
# Copyright (c) 2015 Paul Norman <penorman@mac.com>
# Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
# Copyright (c) 2016, 2018 Krzesimir Nowak <qdlacz@gmail.com>
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice
# and this notice are preserved. This file is offered as-is, without any
# warranty.
#serial 10
dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro
dnl (serial version number 13).
AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"],
[$1], [14], [ax_cxx_compile_alternatives="14 1y"],
[$1], [17], [ax_cxx_compile_alternatives="17 1z"],
[m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl
m4_if([$2], [], [],
[$2], [ext], [],
[$2], [noext], [],
[m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl
m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true],
[$3], [mandatory], [ax_cxx_compile_cxx$1_required=true],
[$3], [optional], [ax_cxx_compile_cxx$1_required=false],
[m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])])
AC_LANG_PUSH([C++])dnl
ac_success=no
m4_if([$2], [noext], [], [dnl
if test x$ac_success = xno; then
for alternative in ${ax_cxx_compile_alternatives}; do
switch="-std=gnu++${alternative}"
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
$cachevar,
[ac_save_CXX="$CXX"
CXX="$CXX $switch"
AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
[eval $cachevar=yes],
[eval $cachevar=no])
CXX="$ac_save_CXX"])
if eval test x\$$cachevar = xyes; then
CXX="$CXX $switch"
if test -n "$CXXCPP" ; then
CXXCPP="$CXXCPP $switch"
fi
ac_success=yes
break
fi
done
fi])
m4_if([$2], [ext], [], [dnl
if test x$ac_success = xno; then
dnl HP's aCC needs +std=c++11 according to:
dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf
dnl Cray's crayCC needs "-h std=c++11"
for alternative in ${ax_cxx_compile_alternatives}; do
for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
$cachevar,
[ac_save_CXX="$CXX"
CXX="$CXX $switch"
AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
[eval $cachevar=yes],
[eval $cachevar=no])
CXX="$ac_save_CXX"])
if eval test x\$$cachevar = xyes; then
CXX="$CXX $switch"
if test -n "$CXXCPP" ; then
CXXCPP="$CXXCPP $switch"
fi
ac_success=yes
break
fi
done
if test x$ac_success = xyes; then
break
fi
done
fi])
AC_LANG_POP([C++])
if test x$ax_cxx_compile_cxx$1_required = xtrue; then
if test x$ac_success = xno; then
AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.])
fi
fi
if test x$ac_success = xno; then
HAVE_CXX$1=0
AC_MSG_NOTICE([No compiler with C++$1 support was found])
else
HAVE_CXX$1=1
AC_DEFINE(HAVE_CXX$1,1,
[define if the compiler supports basic C++$1 syntax])
fi
AC_SUBST(HAVE_CXX$1)
])
dnl Test body for checking C++11 support
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11],
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
)
dnl Test body for checking C++14 support
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14],
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
)
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17],
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
_AX_CXX_COMPILE_STDCXX_testbody_new_in_17
)
dnl Tests for new features in C++11
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[
// If the compiler admits that it is not ready for C++11, why torture it?
// Hopefully, this will speed up the test.
#ifndef __cplusplus
#error "This is not a C++ compiler"
#elif __cplusplus < 201103L
#error "This is not a C++11 compiler"
#else
namespace cxx11
{
namespace test_static_assert
{
template <typename T>
struct check
{
static_assert(sizeof(int) <= sizeof(T), "not big enough");
};
}
namespace test_final_override
{
struct Base
{
virtual void f() {}
};
struct Derived : public Base
{
virtual void f() override {}
};
}
namespace test_double_right_angle_brackets
{
template < typename T >
struct check {};
typedef check<void> single_type;
typedef check<check<void>> double_type;
typedef check<check<check<void>>> triple_type;
typedef check<check<check<check<void>>>> quadruple_type;
}
namespace test_decltype
{
int
f()
{
int a = 1;
decltype(a) b = 2;
return a + b;
}
}
namespace test_type_deduction
{
template < typename T1, typename T2 >
struct is_same
{
static const bool value = false;
};
template < typename T >
struct is_same<T, T>
{
static const bool value = true;
};
template < typename T1, typename T2 >
auto
add(T1 a1, T2 a2) -> decltype(a1 + a2)
{
return a1 + a2;
}
int
test(const int c, volatile int v)
{
static_assert(is_same<int, decltype(0)>::value == true, "");
static_assert(is_same<int, decltype(c)>::value == false, "");
static_assert(is_same<int, decltype(v)>::value == false, "");
auto ac = c;
auto av = v;
auto sumi = ac + av + 'x';
auto sumf = ac + av + 1.0;
static_assert(is_same<int, decltype(ac)>::value == true, "");
static_assert(is_same<int, decltype(av)>::value == true, "");
static_assert(is_same<int, decltype(sumi)>::value == true, "");
static_assert(is_same<int, decltype(sumf)>::value == false, "");
static_assert(is_same<int, decltype(add(c, v))>::value == true, "");
return (sumf > 0.0) ? sumi : add(c, v);
}
}
namespace test_noexcept
{
int f() { return 0; }
int g() noexcept { return 0; }
static_assert(noexcept(f()) == false, "");
static_assert(noexcept(g()) == true, "");
}
namespace test_constexpr
{
template < typename CharT >
unsigned long constexpr
strlen_c_r(const CharT *const s, const unsigned long acc) noexcept
{
return *s ? strlen_c_r(s + 1, acc + 1) : acc;
}
template < typename CharT >
unsigned long constexpr
strlen_c(const CharT *const s) noexcept
{
return strlen_c_r(s, 0UL);
}
static_assert(strlen_c("") == 0UL, "");
static_assert(strlen_c("1") == 1UL, "");
static_assert(strlen_c("example") == 7UL, "");
static_assert(strlen_c("another\0example") == 7UL, "");
}
namespace test_rvalue_references
{
template < int N >
struct answer
{
static constexpr int value = N;
};
answer<1> f(int&) { return answer<1>(); }
answer<2> f(const int&) { return answer<2>(); }
answer<3> f(int&&) { return answer<3>(); }
void
test()
{
int i = 0;
const int c = 0;
static_assert(decltype(f(i))::value == 1, "");
static_assert(decltype(f(c))::value == 2, "");
static_assert(decltype(f(0))::value == 3, "");
}
}
namespace test_uniform_initialization
{
struct test
{
static const int zero {};
static const int one {1};
};
static_assert(test::zero == 0, "");
static_assert(test::one == 1, "");
}
namespace test_lambdas
{
void
test1()
{
auto lambda1 = [](){};
auto lambda2 = lambda1;
lambda1();
lambda2();
}
int
test2()
{
auto a = [](int i, int j){ return i + j; }(1, 2);
auto b = []() -> int { return '0'; }();
auto c = [=](){ return a + b; }();
auto d = [&](){ return c; }();
auto e = [a, &b](int x) mutable {
const auto identity = [](int y){ return y; };
for (auto i = 0; i < a; ++i)
a += b--;
return x + identity(a + b);
}(0);
return a + b + c + d + e;
}
int
test3()
{
const auto nullary = [](){ return 0; };
const auto unary = [](int x){ return x; };
using nullary_t = decltype(nullary);
using unary_t = decltype(unary);
const auto higher1st = [](nullary_t f){ return f(); };
const auto higher2nd = [unary](nullary_t f1){
return [unary, f1](unary_t f2){ return f2(unary(f1())); };
};
return higher1st(nullary) + higher2nd(nullary)(unary);
}
}
namespace test_variadic_templates
{
template <int...>
struct sum;
template <int N0, int... N1toN>
struct sum<N0, N1toN...>
{
static constexpr auto value = N0 + sum<N1toN...>::value;
};
template <>
struct sum<>
{
static constexpr auto value = 0;
};
static_assert(sum<>::value == 0, "");
static_assert(sum<1>::value == 1, "");
static_assert(sum<23>::value == 23, "");
static_assert(sum<1, 2>::value == 3, "");
static_assert(sum<5, 5, 11>::value == 21, "");
static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, "");
}
// http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae
// Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function
// because of this.
namespace test_template_alias_sfinae
{
struct foo {};
template<typename T>
using member = typename T::member_type;
template<typename T>
void func(...) {}
template<typename T>
void func(member<T>*) {}
void test();
void test() { func<foo>(0); }
}
} // namespace cxx11
#endif // __cplusplus >= 201103L
]])
dnl Tests for new features in C++14
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[
// If the compiler admits that it is not ready for C++14, why torture it?
// Hopefully, this will speed up the test.
#ifndef __cplusplus
#error "This is not a C++ compiler"
#elif __cplusplus < 201402L
#error "This is not a C++14 compiler"
#else
namespace cxx14
{
namespace test_polymorphic_lambdas
{
int
test()
{
const auto lambda = [](auto&&... args){
const auto istiny = [](auto x){
return (sizeof(x) == 1UL) ? 1 : 0;
};
const int aretiny[] = { istiny(args)... };
return aretiny[0];
};
return lambda(1, 1L, 1.0f, '1');
}
}
namespace test_binary_literals
{
constexpr auto ivii = 0b0000000000101010;
static_assert(ivii == 42, "wrong value");
}
namespace test_generalized_constexpr
{
template < typename CharT >
constexpr unsigned long
strlen_c(const CharT *const s) noexcept
{
auto length = 0UL;
for (auto p = s; *p; ++p)
++length;
return length;
}
static_assert(strlen_c("") == 0UL, "");
static_assert(strlen_c("x") == 1UL, "");
static_assert(strlen_c("test") == 4UL, "");
static_assert(strlen_c("another\0test") == 7UL, "");
}
namespace test_lambda_init_capture
{
int
test()
{
auto x = 0;
const auto lambda1 = [a = x](int b){ return a + b; };
const auto lambda2 = [a = lambda1(x)](){ return a; };
return lambda2();
}
}
namespace test_digit_separators
{
constexpr auto ten_million = 100'000'000;
static_assert(ten_million == 100000000, "");
}
namespace test_return_type_deduction
{
auto f(int& x) { return x; }
decltype(auto) g(int& x) { return x; }
template < typename T1, typename T2 >
struct is_same
{
static constexpr auto value = false;
};
template < typename T >
struct is_same<T, T>
{
static constexpr auto value = true;
};
int
test()
{
auto x = 0;
static_assert(is_same<int, decltype(f(x))>::value, "");
static_assert(is_same<int&, decltype(g(x))>::value, "");
return x;
}
}
} // namespace cxx14
#endif // __cplusplus >= 201402L
]])
dnl Tests for new features in C++17
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[
// If the compiler admits that it is not ready for C++17, why torture it?
// Hopefully, this will speed up the test.
#ifndef __cplusplus
#error "This is not a C++ compiler"
#elif __cplusplus < 201703L
#error "This is not a C++17 compiler"
#else
#include <initializer_list>
#include <utility>
#include <type_traits>
namespace cxx17
{
namespace test_constexpr_lambdas
{
constexpr int foo = [](){return 42;}();
}
namespace test::nested_namespace::definitions
{
}
namespace test_fold_expression
{
template<typename... Args>
int multiply(Args... args)
{
return (args * ... * 1);
}
template<typename... Args>
bool all(Args... args)
{
return (args && ...);
}
}
namespace test_extended_static_assert
{
static_assert (true);
}
namespace test_auto_brace_init_list
{
auto foo = {5};
auto bar {5};
static_assert(std::is_same<std::initializer_list<int>, decltype(foo)>::value);
static_assert(std::is_same<int, decltype(bar)>::value);
}
namespace test_typename_in_template_template_parameter
{
template<template<typename> typename X> struct D;
}
namespace test_fallthrough_nodiscard_maybe_unused_attributes
{
int f1()
{
return 42;
}
[[nodiscard]] int f2()
{
[[maybe_unused]] auto unused = f1();
switch (f1())
{
case 17:
f1();
[[fallthrough]];
case 42:
f1();
}
return f1();
}
}
namespace test_extended_aggregate_initialization
{
struct base1
{
int b1, b2 = 42;
};
struct base2
{
base2() {
b3 = 42;
}
int b3;
};
struct derived : base1, base2
{
int d;
};
derived d1 {{1, 2}, {}, 4}; // full initialization
derived d2 {{}, {}, 4}; // value-initialized bases
}
namespace test_general_range_based_for_loop
{
struct iter
{
int i;
int& operator* ()
{
return i;
}
const int& operator* () const
{
return i;
}
iter& operator++()
{
++i;
return *this;
}
};
struct sentinel
{
int i;
};
bool operator== (const iter& i, const sentinel& s)
{
return i.i == s.i;
}
bool operator!= (const iter& i, const sentinel& s)
{
return !(i == s);
}
struct range
{
iter begin() const
{
return {0};
}
sentinel end() const
{
return {5};
}
};
void f()
{
range r {};
for (auto i : r)
{
[[maybe_unused]] auto v = i;
}
}
}
namespace test_lambda_capture_asterisk_this_by_value
{
struct t
{
int i;
int foo()
{
return [*this]()
{
return i;
}();
}
};
}
namespace test_enum_class_construction
{
enum class byte : unsigned char
{};
byte foo {42};
}
namespace test_constexpr_if
{
template <bool cond>
int f ()
{
if constexpr(cond)
{
return 13;
}
else
{
return 42;
}
}
}
namespace test_selection_statement_with_initializer
{
int f()
{
return 13;
}
int f2()
{
if (auto i = f(); i > 0)
{
return 3;
}
switch (auto i = f(); i + 4)
{
case 17:
return 2;
default:
return 1;
}
}
}
namespace test_template_argument_deduction_for_class_templates
{
template <typename T1, typename T2>
struct pair
{
pair (T1 p1, T2 p2)
: m1 {p1},
m2 {p2}
{}
T1 m1;
T2 m2;
};
void f()
{
[[maybe_unused]] auto p = pair{13, 42u};
}
}
namespace test_non_type_auto_template_parameters
{
template <auto n>
struct B
{};
B<5> b1;
B<'a'> b2;
}
namespace test_structured_bindings
{
int arr[2] = { 1, 2 };
std::pair<int, int> pr = { 1, 2 };
auto f1() -> int(&)[2]
{
return arr;
}
auto f2() -> std::pair<int, int>&
{
return pr;
}
struct S
{
int x1 : 2;
volatile double y1;
};
S f3()
{
return {};
}
auto [ x1, y1 ] = f1();
auto& [ xr1, yr1 ] = f1();
auto [ x2, y2 ] = f2();
auto& [ xr2, yr2 ] = f2();
const auto [ x3, y3 ] = f3();
}
namespace test_exception_spec_type_system
{
struct Good {};
struct Bad {};
void g1() noexcept;
void g2();
template<typename T>
Bad
f(T*, T*);
template<typename T1, typename T2>
Good
f(T1*, T2*);
static_assert (std::is_same_v<Good, decltype(f(g1, g2))>);
}
namespace test_inline_variables
{
template<class T> void f(T)
{}
template<class T> inline T g(T)
{
return T{};
}
template<> inline void f<>(int)
{}
template<> int g<>(int)
{
return 5;
}
}
} // namespace cxx17
#endif // __cplusplus < 201703L
]])

View File

@@ -1,133 +0,0 @@
# ============================================================================
# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_11.html
# ============================================================================
#
# SYNOPSIS
#
# AX_CXX_COMPILE_STDCXX_11([ext|noext],[mandatory|optional])
#
# DESCRIPTION
#
# Check for baseline language coverage in the compiler for the C++11
# standard; if necessary, add switches to CXXFLAGS to enable support.
#
# The first argument, if specified, indicates whether you insist on an
# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.
# -std=c++11). If neither is specified, you get whatever works, with
# preference for an extended mode.
#
# The second argument, if specified 'mandatory' or if left unspecified,
# indicates that baseline C++11 support is required and that the macro
# should error out if no mode with that support is found. If specified
# 'optional', then configuration proceeds regardless, after defining
# HAVE_CXX11 if and only if a supporting mode is found.
#
# LICENSE
#
# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
# Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
# Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice
# and this notice are preserved. This file is offered as-is, without any
# warranty.
#serial 3
m4_define([_AX_CXX_COMPILE_STDCXX_11_testbody], [
template <typename T>
struct check
{
static_assert(sizeof(int) <= sizeof(T), "not big enough");
};
typedef check<check<bool>> right_angle_brackets;
int a;
decltype(a) b;
typedef check<int> check_type;
check_type c;
check_type&& cr = static_cast<check_type&&>(c);
auto d = a;
])
AC_DEFUN([AX_CXX_COMPILE_STDCXX_11], [dnl
m4_if([$1], [], [],
[$1], [ext], [],
[$1], [noext], [],
[m4_fatal([invalid argument `$1' to AX_CXX_COMPILE_STDCXX_11])])dnl
m4_if([$2], [], [ax_cxx_compile_cxx11_required=true],
[$2], [mandatory], [ax_cxx_compile_cxx11_required=true],
[$2], [optional], [ax_cxx_compile_cxx11_required=false],
[m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX_11])])dnl
AC_LANG_PUSH([C++])dnl
ac_success=no
AC_CACHE_CHECK(whether $CXX supports C++11 features by default,
ax_cv_cxx_compile_cxx11,
[AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])],
[ax_cv_cxx_compile_cxx11=yes],
[ax_cv_cxx_compile_cxx11=no])])
if test x$ax_cv_cxx_compile_cxx11 = xyes; then
ac_success=yes
fi
m4_if([$1], [noext], [], [dnl
if test x$ac_success = xno; then
for switch in -std=gnu++11 -std=gnu++0x; do
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx11_$switch])
AC_CACHE_CHECK(whether $CXX supports C++11 features with $switch,
$cachevar,
[ac_save_CXXFLAGS="$CXXFLAGS"
CXXFLAGS="$CXXFLAGS $switch"
AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])],
[eval $cachevar=yes],
[eval $cachevar=no])
CXXFLAGS="$ac_save_CXXFLAGS"])
if eval test x\$$cachevar = xyes; then
CXXFLAGS="$CXXFLAGS $switch"
ac_success=yes
break
fi
done
fi])
m4_if([$1], [ext], [], [dnl
if test x$ac_success = xno; then
for switch in -std=c++11 -std=c++0x; do
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx11_$switch])
AC_CACHE_CHECK(whether $CXX supports C++11 features with $switch,
$cachevar,
[ac_save_CXXFLAGS="$CXXFLAGS"
CXXFLAGS="$CXXFLAGS $switch"
AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])],
[eval $cachevar=yes],
[eval $cachevar=no])
CXXFLAGS="$ac_save_CXXFLAGS"])
if eval test x\$$cachevar = xyes; then
CXXFLAGS="$CXXFLAGS $switch"
ac_success=yes
break
fi
done
fi])
AC_LANG_POP([C++])
if test x$ax_cxx_compile_cxx11_required = xtrue; then
if test x$ac_success = xno; then
AC_MSG_ERROR([*** A compiler with support for C++11 language features is required.])
fi
else
if test x$ac_success = xno; then
HAVE_CXX11=0
AC_MSG_NOTICE([No compiler with C++11 support was found])
else
HAVE_CXX11=1
AC_DEFINE(HAVE_CXX11,1,
[define if the compiler supports basic C++11 syntax])
fi
AC_SUBST(HAVE_CXX11)
fi
])

View File

@@ -305,7 +305,7 @@ public:
}
}
auto handler =
make_unique<Http2Handler>(this, fd, ssl, get_next_session_id());
std::make_unique<Http2Handler>(this, fd, ssl, get_next_session_id());
if (!ssl) {
if (handler->connection_made() != 0) {
return;
@@ -358,11 +358,11 @@ public:
}
FileEntry *cache_fd(const std::string &path, const FileEntry &ent) {
#ifdef HAVE_STD_MAP_EMPLACE
auto rv = fd_cache_.emplace(path, make_unique<FileEntry>(ent));
auto rv = fd_cache_.emplace(path, std::make_unique<FileEntry>(ent));
#else // !HAVE_STD_MAP_EMPLACE
// for gcc-4.7
auto rv =
fd_cache_.insert(std::make_pair(path, make_unique<FileEntry>(ent)));
auto rv = fd_cache_.insert(
std::make_pair(path, std::make_unique<FileEntry>(ent)));
#endif // !HAVE_STD_MAP_EMPLACE
auto &res = (*rv).second;
res->it = rv;
@@ -1023,7 +1023,7 @@ int Http2Handler::submit_push_promise(Stream *stream,
return promised_stream_id;
}
auto promised_stream = make_unique<Stream>(this, promised_stream_id);
auto promised_stream = std::make_unique<Stream>(this, promised_stream_id);
auto &promised_header = promised_stream->header;
promised_header.method = StringRef::from_lit("GET");
@@ -1477,7 +1477,7 @@ int on_begin_headers_callback(nghttp2_session *session,
return 0;
}
auto stream = make_unique<Stream>(hd, frame->hd.stream_id);
auto stream = std::make_unique<Stream>(hd, frame->hd.stream_id);
add_stream_read_timeout(stream.get());
@@ -1832,10 +1832,10 @@ public:
if (config_->verbose) {
std::cerr << "spawning thread #" << i << std::endl;
}
auto worker = make_unique<Worker>();
auto worker = std::make_unique<Worker>();
auto loop = ev_loop_new(get_ev_loop_flags());
worker->sessions =
make_unique<Sessions>(sv, loop, config_, sessions_->get_ssl_ctx());
worker->sessions = std::make_unique<Sessions>(sv, loop, config_,
sessions_->get_ssl_ctx());
ev_async_init(&worker->w, worker_acceptcb);
worker->w.data = worker.get();
ev_async_start(loop, &worker->w);

View File

@@ -32,6 +32,7 @@
#endif // !_WIN32
#include <cassert>
#include <utility>
#include "template.h"
@@ -65,25 +66,19 @@ struct BlockAllocator {
~BlockAllocator() { reset(); }
BlockAllocator(BlockAllocator &&other) noexcept
: retain(other.retain),
head(other.head),
: retain{std::exchange(other.retain, nullptr)},
head{std::exchange(other.head, nullptr)},
block_size(other.block_size),
isolation_threshold(other.isolation_threshold) {
other.retain = nullptr;
other.head = nullptr;
}
isolation_threshold(other.isolation_threshold) {}
BlockAllocator &operator=(BlockAllocator &&other) noexcept {
reset();
retain = other.retain;
head = other.head;
retain = std::exchange(other.retain, nullptr);
head = std::exchange(other.head, nullptr);
block_size = other.block_size;
isolation_threshold = other.isolation_threshold;
other.retain = nullptr;
other.head = nullptr;
return *this;
}

View File

@@ -77,6 +77,8 @@ const char *strsettingsid(int32_t id) {
return "SETTINGS_MAX_FRAME_SIZE";
case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
return "SETTINGS_MAX_HEADER_LIST_SIZE";
case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
return "SETTINGS_ENABLE_CONNECT_PROTOCOL";
default:
return "UNKNOWN";
}

View File

@@ -34,7 +34,7 @@ namespace nghttp2 {
namespace asio_http2 {
namespace client {
request::request() : impl_(make_unique<request_impl>()) {}
request::request() : impl_(std::make_unique<request_impl>()) {}
request::~request() {}

View File

@@ -34,7 +34,7 @@ namespace nghttp2 {
namespace asio_http2 {
namespace client {
response::response() : impl_(make_unique<response_impl>()) {}
response::response() : impl_(std::make_unique<response_impl>()) {}
response::~response() {}

View File

@@ -473,7 +473,7 @@ stream *session_impl::create_push_stream(int32_t stream_id) {
}
std::unique_ptr<stream> session_impl::create_stream() {
return make_unique<stream>(this);
return std::make_unique<stream>(this);
}
const request *session_impl::submit(boost::system::error_code &ec,

View File

@@ -36,7 +36,7 @@ namespace asio_http2 {
namespace server {
http2::http2() : impl_(make_unique<http2_impl>()) {}
http2::http2() : impl_(std::make_unique<http2_impl>()) {}
http2::~http2() {}

View File

@@ -305,7 +305,8 @@ int http2_handler::start() {
}
stream *http2_handler::create_stream(int32_t stream_id) {
auto p = streams_.emplace(stream_id, make_unique<stream>(this, stream_id));
auto p =
streams_.emplace(stream_id, std::make_unique<stream>(this, stream_id));
assert(p.second);
return (*p.first).second.get();
}

View File

@@ -34,7 +34,7 @@ namespace nghttp2 {
namespace asio_http2 {
namespace server {
request::request() : impl_(make_unique<request_impl>()) {}
request::request() : impl_(std::make_unique<request_impl>()) {}
request::~request() {}

View File

@@ -34,7 +34,7 @@ namespace nghttp2 {
namespace asio_http2 {
namespace server {
response::response() : impl_(make_unique<response_impl>()) {}
response::response() : impl_(std::make_unique<response_impl>()) {}
response::~response() {}

View File

@@ -36,14 +36,17 @@ namespace nghttp2 {
namespace base64 {
namespace {
constexpr char B64_CHARS[] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/',
};
} // namespace
template <typename InputIt> std::string encode(InputIt first, InputIt last) {
static constexpr char CHAR_TABLE[] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/',
};
std::string res;
size_t len = last - first;
if (len == 0) {
@@ -57,29 +60,72 @@ template <typename InputIt> std::string encode(InputIt first, InputIt last) {
uint32_t n = static_cast<uint8_t>(*first++) << 16;
n += static_cast<uint8_t>(*first++) << 8;
n += static_cast<uint8_t>(*first++);
*p++ = CHAR_TABLE[n >> 18];
*p++ = CHAR_TABLE[(n >> 12) & 0x3fu];
*p++ = CHAR_TABLE[(n >> 6) & 0x3fu];
*p++ = CHAR_TABLE[n & 0x3fu];
*p++ = B64_CHARS[n >> 18];
*p++ = B64_CHARS[(n >> 12) & 0x3fu];
*p++ = B64_CHARS[(n >> 6) & 0x3fu];
*p++ = B64_CHARS[n & 0x3fu];
}
if (r == 2) {
uint32_t n = static_cast<uint8_t>(*first++) << 16;
n += static_cast<uint8_t>(*first++) << 8;
*p++ = CHAR_TABLE[n >> 18];
*p++ = CHAR_TABLE[(n >> 12) & 0x3fu];
*p++ = CHAR_TABLE[(n >> 6) & 0x3fu];
*p++ = B64_CHARS[n >> 18];
*p++ = B64_CHARS[(n >> 12) & 0x3fu];
*p++ = B64_CHARS[(n >> 6) & 0x3fu];
*p++ = '=';
} else if (r == 1) {
uint32_t n = static_cast<uint8_t>(*first++) << 16;
*p++ = CHAR_TABLE[n >> 18];
*p++ = CHAR_TABLE[(n >> 12) & 0x3fu];
*p++ = B64_CHARS[n >> 18];
*p++ = B64_CHARS[(n >> 12) & 0x3fu];
*p++ = '=';
*p++ = '=';
}
return res;
}
constexpr size_t encode_length(size_t n) { return (n + 2) / 3 * 4; }
template <typename InputIt, typename OutputIt>
OutputIt encode(InputIt first, InputIt last, OutputIt d_first) {
size_t len = last - first;
if (len == 0) {
return d_first;
}
auto r = len % 3;
auto j = last - r;
auto p = d_first;
while (first != j) {
uint32_t n = static_cast<uint8_t>(*first++) << 16;
n += static_cast<uint8_t>(*first++) << 8;
n += static_cast<uint8_t>(*first++);
*p++ = B64_CHARS[n >> 18];
*p++ = B64_CHARS[(n >> 12) & 0x3fu];
*p++ = B64_CHARS[(n >> 6) & 0x3fu];
*p++ = B64_CHARS[n & 0x3fu];
}
switch (r) {
case 2: {
uint32_t n = static_cast<uint8_t>(*first++) << 16;
n += static_cast<uint8_t>(*first++) << 8;
*p++ = B64_CHARS[n >> 18];
*p++ = B64_CHARS[(n >> 12) & 0x3fu];
*p++ = B64_CHARS[(n >> 6) & 0x3fu];
*p++ = '=';
break;
}
case 1: {
uint32_t n = static_cast<uint8_t>(*first++) << 16;
*p++ = B64_CHARS[n >> 18];
*p++ = B64_CHARS[(n >> 12) & 0x3fu];
*p++ = '=';
*p++ = '=';
break;
}
}
return p;
}
template <typename InputIt>
InputIt next_decode_input(InputIt first, InputIt last, const int *tbl) {
for (; first != last; ++first) {

View File

@@ -211,7 +211,7 @@ void rate_period_timeout_w_cb(struct ev_loop *loop, ev_timer *w, int revents) {
--worker->nreqs_rem;
}
auto client =
make_unique<Client>(worker->next_client_id++, worker, req_todo);
std::make_unique<Client>(worker->next_client_id++, worker, req_todo);
++worker->nconns_made;
@@ -869,9 +869,9 @@ int Client::connection_made() {
if (next_proto) {
auto proto = StringRef{next_proto, next_proto_len};
if (util::check_h2_is_selected(proto)) {
session = make_unique<Http2Session>(this);
session = std::make_unique<Http2Session>(this);
} else if (util::streq(NGHTTP2_H1_1, proto)) {
session = make_unique<Http1Session>(this);
session = std::make_unique<Http1Session>(this);
}
// Just assign next_proto to selected_proto anyway to show the
@@ -886,7 +886,7 @@ int Client::connection_made() {
std::cout
<< "Server does not support NPN/ALPN. Falling back to HTTP/1.1."
<< std::endl;
session = make_unique<Http1Session>(this);
session = std::make_unique<Http1Session>(this);
selected_proto = NGHTTP2_H1_1.str();
break;
}
@@ -910,11 +910,11 @@ int Client::connection_made() {
} else {
switch (config.no_tls_proto) {
case Config::PROTO_HTTP2:
session = make_unique<Http2Session>(this);
session = std::make_unique<Http2Session>(this);
selected_proto = NGHTTP2_CLEARTEXT_PROTO_VERSION_ID;
break;
case Config::PROTO_HTTP1_1:
session = make_unique<Http1Session>(this);
session = std::make_unique<Http1Session>(this);
selected_proto = NGHTTP2_H1_1.str();
break;
default:
@@ -1319,7 +1319,7 @@ void Worker::run() {
--nreqs_rem;
}
auto client = make_unique<Client>(next_client_id++, this, req_todo);
auto client = std::make_unique<Client>(next_client_id++, this, req_todo);
if (client->connect() != 0) {
std::cerr << "client could not connect to host" << std::endl;
client->fail();
@@ -1513,7 +1513,7 @@ process_time_stats(const std::vector<std::unique_ptr<Worker>> &workers) {
namespace {
void resolve_host() {
if (config.base_uri_unix) {
auto res = make_unique<addrinfo>();
auto res = std::make_unique<addrinfo>();
res->ai_family = config.unix_addr.sun_family;
res->ai_socktype = SOCK_STREAM;
res->ai_addrlen = sizeof(config.unix_addr);
@@ -1722,13 +1722,13 @@ std::unique_ptr<Worker> create_worker(uint32_t id, SSL_CTX *ssl_ctx,
}
if (config.is_rate_mode()) {
return make_unique<Worker>(id, ssl_ctx, nreqs, nclients, rate, max_samples,
&config);
return std::make_unique<Worker>(id, ssl_ctx, nreqs, nclients, rate,
max_samples, &config);
} else {
// Here rate is same as client because the rate_timeout callback
// will be called only once
return make_unique<Worker>(id, ssl_ctx, nreqs, nclients, nclients,
max_samples, &config);
return std::make_unique<Worker>(id, ssl_ctx, nreqs, nclients, nclients,
max_samples, &config);
}
}
} // namespace

View File

@@ -70,6 +70,11 @@ namespace {
int htp_statuscb(http_parser *htp, const char *at, size_t length) {
auto session = static_cast<Http1Session *>(htp->data);
auto client = session->get_client();
if (htp->status_code / 100 == 1) {
return 0;
}
client->on_status_code(session->stream_resp_counter_, htp->status_code);
return 0;
@@ -82,6 +87,10 @@ int htp_msg_completecb(http_parser *htp) {
auto session = static_cast<Http1Session *>(htp->data);
auto client = session->get_client();
if (htp->status_code / 100 == 1) {
return 0;
}
client->final = http_should_keep_alive(htp) == 0;
auto req_stat = client->get_req_stat(session->stream_resp_counter_);
@@ -133,6 +142,12 @@ int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) {
}
} // namespace
namespace {
int htp_hdrs_completecb(http_parser *htp) {
return !http2::expect_response_body(htp->status_code);
}
} // namespace
namespace {
int htp_body_cb(http_parser *htp, const char *data, size_t len) {
auto session = static_cast<Http1Session *>(htp->data);
@@ -147,14 +162,14 @@ int htp_body_cb(http_parser *htp, const char *data, size_t len) {
namespace {
constexpr http_parser_settings htp_hooks = {
htp_msg_begincb, // http_cb on_message_begin;
nullptr, // http_data_cb on_url;
htp_statuscb, // http_data_cb on_status;
htp_hdr_keycb, // http_data_cb on_header_field;
htp_hdr_valcb, // http_data_cb on_header_value;
nullptr, // http_cb on_headers_complete;
htp_body_cb, // http_data_cb on_body;
htp_msg_completecb // http_cb on_message_complete;
htp_msg_begincb, // http_cb on_message_begin;
nullptr, // http_data_cb on_url;
htp_statuscb, // http_data_cb on_status;
htp_hdr_keycb, // http_data_cb on_header_field;
htp_hdr_valcb, // http_data_cb on_header_value;
htp_hdrs_completecb, // http_cb on_headers_complete;
htp_body_cb, // http_data_cb on_body;
htp_msg_completecb // http_cb on_message_complete;
};
} // namespace

View File

@@ -107,6 +107,9 @@ StringRef get_reason_phrase(unsigned int status_code) {
return StringRef::from_lit("Expectation Failed");
case 421:
return StringRef::from_lit("Misdirected Request");
case 425:
// https://tools.ietf.org/html/rfc8470
return StringRef::from_lit("Too Early");
case 426:
return StringRef::from_lit("Upgrade Required");
case 428:
@@ -386,6 +389,21 @@ void copy_headers_to_nva_internal(std::vector<nghttp2_nv> &nva,
case HD_TRANSFER_ENCODING:
case HD_UPGRADE:
continue;
case HD_EARLY_DATA:
if (flags & HDOP_STRIP_EARLY_DATA) {
continue;
}
break;
case HD_SEC_WEBSOCKET_ACCEPT:
if (flags & HDOP_STRIP_SEC_WEBSOCKET_ACCEPT) {
continue;
}
break;
case HD_SEC_WEBSOCKET_KEY:
if (flags & HDOP_STRIP_SEC_WEBSOCKET_KEY) {
continue;
}
break;
case HD_FORWARDED:
if (flags & HDOP_STRIP_FORWARDED) {
continue;
@@ -480,6 +498,16 @@ void build_http1_headers_from_headers(DefaultMemchunks *buf,
case HD_SERVER:
case HD_UPGRADE:
continue;
case HD_EARLY_DATA:
if (flags & HDOP_STRIP_EARLY_DATA) {
continue;
}
break;
case HD_TRANSFER_ENCODING:
if (flags & HDOP_STRIP_TRANSFER_ENCODING) {
continue;
}
break;
case HD_FORWARDED:
if (flags & HDOP_STRIP_FORWARDED) {
continue;
@@ -821,10 +849,20 @@ int lookup_token(const uint8_t *name, size_t namelen) {
return HD_FORWARDED;
}
break;
case 'l':
if (util::streq_l(":protoco", name, 8)) {
return HD__PROTOCOL;
}
break;
}
break;
case 10:
switch (name[9]) {
case 'a':
if (util::streq_l("early-dat", name, 9)) {
return HD_EARLY_DATA;
}
break;
case 'e':
if (util::streq_l("keep-aliv", name, 9)) {
return HD_KEEP_ALIVE;
@@ -924,6 +962,20 @@ int lookup_token(const uint8_t *name, size_t namelen) {
return HD_X_FORWARDED_PROTO;
}
break;
case 'y':
if (util::streq_l("sec-websocket-ke", name, 16)) {
return HD_SEC_WEBSOCKET_KEY;
}
break;
}
break;
case 20:
switch (name[19]) {
case 't':
if (util::streq_l("sec-websocket-accep", name, 19)) {
return HD_SEC_WEBSOCKET_ACCEPT;
}
break;
}
break;
}
@@ -1313,7 +1365,8 @@ std::string path_join(const StringRef &base_path, const StringRef &base_query,
}
bool expect_response_body(int status_code) {
return status_code / 100 != 1 && status_code != 304 && status_code != 204;
return status_code == 101 ||
(status_code / 100 != 1 && status_code != 304 && status_code != 204);
}
bool expect_response_body(const std::string &method, int status_code) {
@@ -1811,6 +1864,25 @@ bool contains_trailers(const StringRef &s) {
}
}
StringRef make_websocket_accept_token(uint8_t *dest, const StringRef &key) {
static constexpr uint8_t magic[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
std::array<uint8_t, base64::encode_length(16) + str_size(magic)> s;
auto p = std::copy(std::begin(key), std::end(key), std::begin(s));
std::copy_n(magic, str_size(magic), p);
std::array<uint8_t, 20> h;
if (util::sha1(h.data(), StringRef{std::begin(s), std::end(s)}) != 0) {
return StringRef{};
}
auto end = base64::encode(std::begin(h), std::end(h), dest);
return StringRef{dest, end};
}
bool legacy_http1(int major, int minor) {
return major <= 0 || (major == 1 && minor == 0);
}
} // namespace http2
} // namespace nghttp2

View File

@@ -41,6 +41,7 @@
#include "memchunk.h"
#include "template.h"
#include "allocator.h"
#include "base64.h"
namespace nghttp2 {
@@ -203,9 +204,22 @@ enum HeaderBuildOp {
// Via header fields must be stripped. If this flag is not set, all
// Via header fields other than last one are added.
HDOP_STRIP_VIA = 1 << 3,
// Early-Data header fields must be stripped. If this flag is not
// set, all Early-Data header fields are added.
HDOP_STRIP_EARLY_DATA = 1 << 4,
// Strip above all header fields.
HDOP_STRIP_ALL = HDOP_STRIP_FORWARDED | HDOP_STRIP_X_FORWARDED_FOR |
HDOP_STRIP_X_FORWARDED_PROTO | HDOP_STRIP_VIA,
HDOP_STRIP_X_FORWARDED_PROTO | HDOP_STRIP_VIA |
HDOP_STRIP_EARLY_DATA,
// Sec-WebSocket-Accept header field must be stripped. If this flag
// is not set, all Sec-WebSocket-Accept header fields are added.
HDOP_STRIP_SEC_WEBSOCKET_ACCEPT = 1 << 5,
// Sec-WebSocket-Key header field must be stripped. If this flag is
// not set, all Sec-WebSocket-Key header fields are added.
HDOP_STRIP_SEC_WEBSOCKET_KEY = 1 << 6,
// Transfer-Encoding header field must be stripped. If this flag is
// not set, all Transfer-Encoding header fields are added.
HDOP_STRIP_TRANSFER_ENCODING = 1 << 7,
};
// Appends headers in |headers| to |nv|. |headers| must be indexed
@@ -293,6 +307,7 @@ enum {
HD__HOST,
HD__METHOD,
HD__PATH,
HD__PROTOCOL,
HD__SCHEME,
HD__STATUS,
HD_ACCEPT_ENCODING,
@@ -304,6 +319,7 @@ enum {
HD_CONTENT_TYPE,
HD_COOKIE,
HD_DATE,
HD_EARLY_DATA,
HD_EXPECT,
HD_FORWARDED,
HD_HOST,
@@ -313,6 +329,8 @@ enum {
HD_LINK,
HD_LOCATION,
HD_PROXY_CONNECTION,
HD_SEC_WEBSOCKET_ACCEPT,
HD_SEC_WEBSOCKET_KEY,
HD_SERVER,
HD_TE,
HD_TRAILER,
@@ -416,6 +434,16 @@ StringRef copy_lower(BlockAllocator &balloc, const StringRef &src);
// Returns true if te header field value |s| contains "trailers".
bool contains_trailers(const StringRef &s);
// Creates Sec-WebSocket-Accept value for |key|. The capacity of
// buffer pointed by |dest| must have at least 24 bytes (base64
// encoded length of 16 bytes data). It returns empty string in case
// of error.
StringRef make_websocket_accept_token(uint8_t *dest, const StringRef &key);
// Returns true if HTTP version represents pre-HTTP/1.1 (e.g.,
// HTTP/0.9 or HTTP/1.0).
bool legacy_http1(int major, int minor);
} // namespace http2
} // namespace nghttp2

View File

@@ -142,8 +142,8 @@ public:
// incoming requests in cleartext TCP connection. If |asynchronous|
// is false, this function blocks forever unless there is an error.
// If it is true, after server has started, this function returns
// immediately, and the caller should call join() to shutdown server
// gracefully.
// immediately, and the caller should call stop() and join() to
// shutdown server gracefully.
boost::system::error_code listen_and_serve(boost::system::error_code &ec,
const std::string &address,
const std::string &port,

View File

@@ -44,6 +44,7 @@ struct iovec {
#include <array>
#include <algorithm>
#include <string>
#include <utility>
#include "template.h"
@@ -111,11 +112,10 @@ template <typename Memchunk> struct Memchunks {
: pool(pool), head(nullptr), tail(nullptr), len(0) {}
Memchunks(const Memchunks &) = delete;
Memchunks(Memchunks &&other) noexcept
: pool(other.pool), head(other.head), tail(other.tail), len(other.len) {
// keep other.pool
other.head = other.tail = nullptr;
other.len = 0;
}
: pool{other.pool}, // keep other.pool
head{std::exchange(other.head, nullptr)},
tail{std::exchange(other.tail, nullptr)},
len{std::exchange(other.len, 0)} {}
Memchunks &operator=(const Memchunks &) = delete;
Memchunks &operator=(Memchunks &&other) noexcept {
if (this == &other) {
@@ -125,12 +125,9 @@ template <typename Memchunk> struct Memchunks {
reset();
pool = other.pool;
head = other.head;
tail = other.tail;
len = other.len;
other.head = other.tail = nullptr;
other.len = 0;
head = std::exchange(other.head, nullptr);
tail = std::exchange(other.tail, nullptr);
len = std::exchange(other.len, 0);
return *this;
}
@@ -335,14 +332,12 @@ template <typename Memchunk> struct PeekMemchunks {
peeking(true) {}
PeekMemchunks(const PeekMemchunks &) = delete;
PeekMemchunks(PeekMemchunks &&other) noexcept
: memchunks(std::move(other.memchunks)),
cur(other.cur),
cur_pos(other.cur_pos),
cur_last(other.cur_last),
len(other.len),
peeking(other.peeking) {
other.reset();
}
: memchunks{std::move(other.memchunks)},
cur{std::exchange(other.cur, nullptr)},
cur_pos{std::exchange(other.cur_pos, nullptr)},
cur_last{std::exchange(other.cur_last, nullptr)},
len{std::exchange(other.len, 0)},
peeking{std::exchange(other.peeking, true)} {}
PeekMemchunks &operator=(const PeekMemchunks &) = delete;
PeekMemchunks &operator=(PeekMemchunks &&other) noexcept {
if (this == &other) {
@@ -350,13 +345,11 @@ template <typename Memchunk> struct PeekMemchunks {
}
memchunks = std::move(other.memchunks);
cur = other.cur;
cur_pos = other.cur_pos;
cur_last = other.cur_last;
len = other.len;
peeking = other.peeking;
other.reset();
cur = std::exchange(other.cur, nullptr);
cur_pos = std::exchange(other.cur_pos, nullptr);
cur_last = std::exchange(other.cur_last, nullptr);
len = std::exchange(other.len, 0);
peeking = std::exchange(other.peeking, true);
return *this;
}

View File

@@ -224,10 +224,10 @@ void test_peek_memchunks_append(void) {
MemchunkPool16 pool;
PeekMemchunks16 pchunks(&pool);
std::array<uint8_t, 32> b{{
std::array<uint8_t, 32> b{
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1',
}},
},
d;
pchunks.append(b.data(), b.size());
@@ -259,10 +259,10 @@ void test_peek_memchunks_disable_peek_drain(void) {
MemchunkPool16 pool;
PeekMemchunks16 pchunks(&pool);
std::array<uint8_t, 32> b{{
std::array<uint8_t, 32> b{
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1',
}},
},
d;
pchunks.append(b.data(), b.size());
@@ -287,10 +287,10 @@ void test_peek_memchunks_disable_peek_no_drain(void) {
MemchunkPool16 pool;
PeekMemchunks16 pchunks(&pool);
std::array<uint8_t, 32> b{{
std::array<uint8_t, 32> b{
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1',
}},
},
d;
pchunks.append(b.data(), b.size());
@@ -315,10 +315,10 @@ void test_peek_memchunks_reset(void) {
MemchunkPool16 pool;
PeekMemchunks16 pchunks(&pool);
std::array<uint8_t, 32> b{{
std::array<uint8_t, 32> b{
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1',
}},
},
d;
pchunks.append(b.data(), b.size());

View File

@@ -233,7 +233,7 @@ void Request::init_html_parser() {
base_uri += util::get_uri_field(uri.c_str(), u, UF_QUERY);
}
html_parser = make_unique<HtmlParser>(base_uri);
html_parser = std::make_unique<HtmlParser>(base_uri);
}
int Request::update_html_parser(const uint8_t *data, size_t len, int fin) {
@@ -527,7 +527,7 @@ int submit_request(HttpClient *client, const Headers &headers, Request *req) {
req->req_nva = std::move(build_headers);
if (expect_continue) {
auto timer = make_unique<ContinueTimer>(client->loop, req);
auto timer = std::make_unique<ContinueTimer>(client->loop, req);
req->continue_timer = std::move(timer);
}
@@ -885,7 +885,7 @@ int HttpClient::connected() {
writefn = &HttpClient::write_clear;
if (need_upgrade()) {
htp = make_unique<http_parser>();
htp = std::make_unique<http_parser>();
http_parser_init(htp.get(), HTTP_RESPONSE);
htp->data = this;
@@ -1453,8 +1453,8 @@ bool HttpClient::add_request(const std::string &uri,
path_cache.insert(uri);
}
reqvec.push_back(
make_unique<Request>(uri, u, data_prd, data_length, pri_spec, level));
reqvec.push_back(std::make_unique<Request>(uri, u, data_prd, data_length,
pri_spec, level));
return true;
}
@@ -1854,7 +1854,7 @@ int on_begin_headers_callback(nghttp2_session *session,
nghttp2_priority_spec_default_init(&pri_spec);
auto req = make_unique<Request>("", u, nullptr, 0, pri_spec);
auto req = std::make_unique<Request>("", u, nullptr, 0, pri_spec);
req->stream_id = stream_id;
nghttp2_session_set_stream_user_data(session, stream_id, req.get());

View File

@@ -305,7 +305,7 @@ int save_pid() {
auto &pid_file = config->pid_file;
auto len = config->pid_file.size() + SUFFIX.size();
auto buf = make_unique<char[]>(len + 1);
auto buf = std::make_unique<char[]>(len + 1);
auto p = buf.get();
p = std::copy(std::begin(pid_file), std::end(pid_file), p);
@@ -438,7 +438,7 @@ void exec_binary() {
nghttp2_Exit(EXIT_FAILURE);
}
auto argv = make_unique<char *[]>(suconfig.argc + 1);
auto argv = std::make_unique<char *[]>(suconfig.argc + 1);
argv[0] = exec_path;
for (int i = 1; i < suconfig.argc; ++i) {
@@ -454,7 +454,8 @@ void exec_binary() {
auto &listenerconf = config->conn.listener;
// 2 for ENV_ORIG_PID and terminal nullptr.
auto envp = make_unique<char *[]>(envlen + listenerconf.addrs.size() + 2);
auto envp =
std::make_unique<char *[]>(envlen + listenerconf.addrs.size() + 2);
size_t envidx = 0;
std::vector<ImmutableString> fd_envs;
@@ -1354,7 +1355,7 @@ int event_loop() {
return -1;
}
worker_process_add(make_unique<WorkerProcess>(loop, pid, ipc_fd));
worker_process_add(std::make_unique<WorkerProcess>(loop, pid, ipc_fd));
// Write PID file when we are ready to accept connection from peer.
// This makes easier to write restart script for nghttpx. Because
@@ -1459,12 +1460,17 @@ void fill_default_config(Config *config) {
tlsconf.session_timeout = std::chrono::hours(12);
tlsconf.ciphers = StringRef::from_lit(nghttp2::tls::DEFAULT_CIPHER_LIST);
tlsconf.tls13_ciphers =
StringRef::from_lit(nghttp2::tls::DEFAULT_TLS13_CIPHER_LIST);
tlsconf.client.ciphers =
StringRef::from_lit(nghttp2::tls::DEFAULT_CIPHER_LIST);
tlsconf.client.tls13_ciphers =
StringRef::from_lit(nghttp2::tls::DEFAULT_TLS13_CIPHER_LIST);
tlsconf.min_proto_version =
tls::proto_version_from_string(DEFAULT_TLS_MIN_PROTO_VERSION);
tlsconf.max_proto_version =
tls::proto_version_from_string(DEFAULT_TLS_MAX_PROTO_VERSION);
tlsconf.max_early_data = 16_k;
#if OPENSSL_1_1_API || defined(OPENSSL_IS_BORINGSSL)
tlsconf.ecdh_curves = StringRef::from_lit("X25519:P-256:P-384:P-521");
#else // !OPENSSL_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
@@ -1482,6 +1488,7 @@ void fill_default_config(Config *config) {
httpconf.max_requests = std::numeric_limits<size_t>::max();
httpconf.xfp.add = true;
httpconf.xfp.strip_incoming = true;
httpconf.early_data.strip_incoming = true;
auto &http2conf = config->http2;
{
@@ -1730,11 +1737,13 @@ Connections:
parameters are: "proto=<PROTO>", "tls",
"sni=<SNI_HOST>", "fall=<N>", "rise=<N>",
"affinity=<METHOD>", "dns", "redirect-if-not-tls",
"upgrade-scheme", and "mruby=<PATH>". 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
"upgrade-scheme", "mruby=<PATH>",
"read-timeout=<DURATION>", and
"write-timeout=<DURATION>". 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
@@ -1833,6 +1842,14 @@ Connections:
matched. All backends which share the same pattern must
have the same mruby path.
"read-timeout=<DURATION>" and "write-timeout=<DURATION>"
parameters specify the read and write timeout of the
backend connection when this pattern is matched. All
backends which share the same pattern must have the same
timeouts. If these timeouts are entirely omitted for a
pattern, --backend-read-timeout and
--backend-write-timeout are used.
Since ";" and ":" are used as delimiter, <PATTERN> must
not contain these characters. Since ";" has special
meaning in shell, the option value must be quoted.
@@ -2080,13 +2097,31 @@ SSL/TLS:
--ciphers=<SUITE>
Set allowed cipher list for frontend connection. The
format of the string is described in OpenSSL ciphers(1).
This option sets cipher suites for TLSv1.2 or earlier.
Use --tls13-ciphers for TLSv1.3.
Default: )"
<< config->tls.ciphers << R"(
--tls13-ciphers=<SUITE>
Set allowed cipher list for frontend connection. The
format of the string is described in OpenSSL ciphers(1).
This option sets cipher suites for TLSv1.3. Use
--ciphers for TLSv1.2 or earlier.
Default: )"
<< config->tls.tls13_ciphers << R"(
--client-ciphers=<SUITE>
Set allowed cipher list for backend connection. The
format of the string is described in OpenSSL ciphers(1).
This option sets cipher suites for TLSv1.2 or earlier.
Use --tls13-client-ciphers for TLSv1.3.
Default: )"
<< config->tls.client.ciphers << R"(
--tls13-client-ciphers=<SUITE>
Set allowed cipher list for backend connection. The
format of the string is described in OpenSSL ciphers(1).
This option sets cipher suites for TLSv1.3. Use
--tls13-client-ciphers for TLSv1.2 or earlier.
Default: )"
<< config->tls.client.tls13_ciphers << R"(
--ecdh-curves=<LIST>
Set supported curve list for frontend connections.
<LIST> is a colon separated list of curve NID or names
@@ -2370,6 +2405,18 @@ SSL/TLS:
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.
--tls-no-postpone-early-data
By default, nghttpx postpones forwarding HTTP requests
sent in early data, including those sent in partially in
it, until TLS handshake finishes. If all backend server
recognizes "Early-Data" header field, using this option
makes nghttpx not postpone forwarding request and get
full potential of 0-RTT data.
--tls-max-early-data=<SIZE>
Sets the maximum amount of 0-RTT data that server
accepts.
Default: )"
<< util::utos_unit(config->tls.max_early_data) << R"(
HTTP/2:
-c, --frontend-http2-max-concurrent-streams=<N>
@@ -2609,6 +2656,9 @@ HTTP:
Default: obfuscated
--no-via Don't append to Via header field. If Via header field
is received, it is left unaltered.
--no-strip-incoming-early-data
Don't strip Early-Data header field from inbound client
requests.
--no-location-rewrite
Don't rewrite location header field in default mode.
When --http2-proxy is used, location header field will
@@ -3035,7 +3085,7 @@ int process_options(Config *config,
auto &fwdconf = config->http.forwarded;
if (fwdconf.by_node_type == FORWARDED_NODE_OBFUSCATED &&
if (fwdconf.by_node_type == ForwardedNode::OBFUSCATED &&
fwdconf.by_obfuscated.empty()) {
// 2 for '_' and terminal NULL
auto iov = make_byte_ref(config->balloc, SHRPX_OBFUSCATED_NODE_LENGTH + 2);
@@ -3091,7 +3141,7 @@ void reload_config(WorkerProcess *wp) {
LOG(NOTICE) << "Reloading configuration";
auto cur_config = mod_config();
auto new_config = make_unique<Config>();
auto new_config = std::make_unique<Config>();
fill_default_config(new_config.get());
@@ -3146,7 +3196,7 @@ void reload_config(WorkerProcess *wp) {
// We no longer use signals for this worker.
last_wp->shutdown_signal_watchers();
worker_process_add(make_unique<WorkerProcess>(loop, pid, ipc_fd));
worker_process_add(std::make_unique<WorkerProcess>(loop, pid, ipc_fd));
if (!get_config()->pid_file.empty()) {
save_pid();
@@ -3436,6 +3486,12 @@ int main(int argc, char **argv) {
160},
{SHRPX_OPT_IGNORE_PER_PATTERN_MRUBY_ERROR.c_str(), no_argument, &flag,
161},
{SHRPX_OPT_TLS_NO_POSTPONE_EARLY_DATA.c_str(), no_argument, &flag, 162},
{SHRPX_OPT_TLS_MAX_EARLY_DATA.c_str(), required_argument, &flag, 163},
{SHRPX_OPT_TLS13_CIPHERS.c_str(), required_argument, &flag, 164},
{SHRPX_OPT_TLS13_CLIENT_CIPHERS.c_str(), required_argument, &flag, 165},
{SHRPX_OPT_NO_STRIP_INCOMING_EARLY_DATA.c_str(), no_argument, &flag,
166},
{nullptr, 0, nullptr, 0}};
int option_index = 0;
@@ -4207,6 +4263,28 @@ int main(int argc, char **argv) {
cmdcfgs.emplace_back(SHRPX_OPT_IGNORE_PER_PATTERN_MRUBY_ERROR,
StringRef::from_lit("yes"));
break;
case 162:
// --tls-no-postpone-early-data
cmdcfgs.emplace_back(SHRPX_OPT_TLS_NO_POSTPONE_EARLY_DATA,
StringRef::from_lit("yes"));
break;
case 163:
// --tls-max-early-data
cmdcfgs.emplace_back(SHRPX_OPT_TLS_MAX_EARLY_DATA, StringRef{optarg});
break;
case 164:
// --tls13-ciphers
cmdcfgs.emplace_back(SHRPX_OPT_TLS13_CIPHERS, StringRef{optarg});
break;
case 165:
// --tls13-client-ciphers
cmdcfgs.emplace_back(SHRPX_OPT_TLS13_CLIENT_CIPHERS, StringRef{optarg});
break;
case 166:
// --no-strip-incoming-early-data
cmdcfgs.emplace_back(SHRPX_OPT_NO_STRIP_INCOMING_EARLY_DATA,
StringRef::from_lit("yes"));
break;
default:
break;
}

View File

@@ -41,7 +41,7 @@ namespace shrpx {
namespace {
// List of API endpoints
const std::array<APIEndpoint, 2> &apis() {
static const auto apis = new std::array<APIEndpoint, 2>{{
static const auto apis = new std::array<APIEndpoint, 2>{
APIEndpoint{
StringRef::from_lit("/api/v1beta1/backendconfig"),
true,
@@ -54,7 +54,7 @@ const std::array<APIEndpoint, 2> &apis() {
(1 << API_METHOD_GET),
&APIDownstreamConnection::handle_configrevision,
},
}};
};
return *apis;
}
@@ -95,15 +95,9 @@ void APIDownstreamConnection::detach_downstream(Downstream *downstream) {
downstream_ = nullptr;
}
// API status, which is independent from HTTP status code. But
// generally, 2xx code for API_SUCCESS, and otherwise API_FAILURE.
enum {
API_SUCCESS,
API_FAILURE,
};
int APIDownstreamConnection::send_reply(unsigned int http_status,
int api_status, const StringRef &data) {
APIStatusCode api_status,
const StringRef &data) {
shutdown_read_ = true;
auto upstream = downstream_->get_upstream();
@@ -117,10 +111,10 @@ int APIDownstreamConnection::send_reply(unsigned int http_status,
StringRef api_status_str;
switch (api_status) {
case API_SUCCESS:
case APIStatusCode::SUCCESS:
api_status_str = StringRef::from_lit("Success");
break;
case API_FAILURE:
case APIStatusCode::FAILURE:
api_status_str = StringRef::from_lit("Failure");
break;
default:
@@ -206,7 +200,7 @@ int APIDownstreamConnection::push_request_headers() {
api_ = lookup_api(path);
if (!api_) {
send_reply(404, API_FAILURE);
send_reply(404, APIStatusCode::FAILURE);
return 0;
}
@@ -238,7 +232,7 @@ int APIDownstreamConnection::push_request_headers() {
// This works with req.fs.content_length == -1
if (req.fs.content_length >
static_cast<int64_t>(get_config()->api.max_request_body)) {
send_reply(413, API_FAILURE);
send_reply(413, APIStatusCode::FAILURE);
return 0;
}
@@ -253,7 +247,7 @@ int APIDownstreamConnection::push_request_headers() {
fd_ = mkstemp(tempname);
#endif // !HAVE_MKOSTEMP
if (fd_ == -1) {
send_reply(500, API_FAILURE);
send_reply(500, APIStatusCode::FAILURE);
return 0;
}
@@ -303,7 +297,7 @@ int APIDownstreamConnection::error_method_not_allowed() {
resp.fs.add_header_token(StringRef::from_lit("allow"), StringRef{iov.base, p},
false, -1);
return send_reply(405, API_FAILURE);
return send_reply(405, APIStatusCode::FAILURE);
}
int APIDownstreamConnection::push_upload_data_chunk(const uint8_t *data,
@@ -316,7 +310,7 @@ int APIDownstreamConnection::push_upload_data_chunk(const uint8_t *data,
auto &apiconf = get_config()->api;
if (static_cast<size_t>(req.recv_body_length) > apiconf.max_request_body) {
send_reply(413, API_FAILURE);
send_reply(413, APIStatusCode::FAILURE);
return 0;
}
@@ -327,7 +321,7 @@ int APIDownstreamConnection::push_upload_data_chunk(const uint8_t *data,
if (nwrite == -1) {
auto error = errno;
LOG(ERROR) << "Could not write API request body: errno=" << error;
send_reply(500, API_FAILURE);
send_reply(500, APIStatusCode::FAILURE);
return 0;
}
@@ -351,14 +345,14 @@ int APIDownstreamConnection::handle_backendconfig() {
auto &req = downstream_->request();
if (req.recv_body_length == 0) {
send_reply(200, API_SUCCESS);
send_reply(200, APIStatusCode::SUCCESS);
return 0;
}
auto rp = mmap(nullptr, req.recv_body_length, PROT_READ, MAP_SHARED, fd_, 0);
if (rp == reinterpret_cast<void *>(-1)) {
send_reply(500, API_FAILURE);
send_reply(500, APIStatusCode::FAILURE);
}
auto unmapper = defer(munmap, rp, req.recv_body_length);
@@ -395,7 +389,7 @@ int APIDownstreamConnection::handle_backendconfig() {
auto eq = std::find(first, eol, '=');
if (eq == eol) {
send_reply(400, API_FAILURE);
send_reply(400, APIStatusCode::FAILURE);
return 0;
}
@@ -414,7 +408,7 @@ int APIDownstreamConnection::handle_backendconfig() {
if (parse_config(&new_config, optid, opt, optval, include_set,
pattern_addr_indexer) != 0) {
send_reply(400, API_FAILURE);
send_reply(400, APIStatusCode::FAILURE);
return 0;
}
@@ -424,7 +418,7 @@ int APIDownstreamConnection::handle_backendconfig() {
auto &tlsconf = config->tls;
if (configure_downstream_group(&new_config, config->http2_proxy, true,
tlsconf) != 0) {
send_reply(400, API_FAILURE);
send_reply(400, APIStatusCode::FAILURE);
return 0;
}
@@ -432,7 +426,7 @@ int APIDownstreamConnection::handle_backendconfig() {
conn_handler->send_replace_downstream(downstreamconf);
send_reply(200, API_SUCCESS);
send_reply(200, APIStatusCode::SUCCESS);
return 0;
}
@@ -451,7 +445,7 @@ int APIDownstreamConnection::handle_configrevision() {
util::make_string_ref_uint(balloc, config->config_revision),
StringRef::from_lit("}"));
send_reply(200, API_SUCCESS, data);
send_reply(200, APIStatusCode::SUCCESS, data);
return 0;
}

View File

@@ -43,6 +43,13 @@ enum APIMethod {
API_METHOD_MAX,
};
// API status code, which is independent from HTTP status code. But
// generally, 2xx code for SUCCESS, and otherwise FAILURE.
enum class APIStatusCode {
SUCCESS,
FAILURE,
};
class APIDownstreamConnection;
struct APIEndpoint {
@@ -83,7 +90,7 @@ public:
get_downstream_addr_group() const;
virtual DownstreamAddr *get_addr() const;
int send_reply(unsigned int http_status, int api_status,
int send_reply(unsigned int http_status, APIStatusCode api_status,
const StringRef &data = StringRef{});
int error_method_not_allowed();

View File

@@ -396,7 +396,7 @@ ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl,
get_config()->conn.upstream.ratelimit.write,
get_config()->conn.upstream.ratelimit.read, writecb, readcb,
timeoutcb, this, get_config()->tls.dyn_rec.warmup_threshold,
get_config()->tls.dyn_rec.idle_timeout, PROTO_NONE),
get_config()->tls.dyn_rec.idle_timeout, Proto::NONE),
ipaddr_(make_string_ref(balloc_, ipaddr)),
port_(make_string_ref(balloc_, port)),
faddr_(faddr),
@@ -430,7 +430,7 @@ ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl,
auto &fwdconf = config->http.forwarded;
if (fwdconf.params & FORWARDED_FOR) {
if (fwdconf.for_node_type == FORWARDED_NODE_OBFUSCATED) {
if (fwdconf.for_node_type == ForwardedNode::OBFUSCATED) {
// 1 for '_'
auto len = SHRPX_OBFUSCATED_NODE_LENGTH + 1;
// 1 for terminating NUL.
@@ -478,7 +478,7 @@ void ClientHandler::setup_upstream_io_callback() {
// For non-TLS version, first create HttpsUpstream. It may be
// upgraded to HTTP/2 through HTTP Upgrade or direct HTTP/2
// connection.
upstream_ = make_unique<HttpsUpstream>(this);
upstream_ = std::make_unique<HttpsUpstream>(this);
alpn_ = StringRef::from_lit("http/1.1");
read_ = &ClientHandler::read_clear;
write_ = &ClientHandler::write_clear;
@@ -584,7 +584,7 @@ int ClientHandler::validate_next_proto() {
if (util::check_h2_is_selected(proto)) {
on_read_ = &ClientHandler::upstream_http2_connhd_read;
auto http2_upstream = make_unique<Http2Upstream>(this);
auto http2_upstream = std::make_unique<Http2Upstream>(this);
upstream_ = std::move(http2_upstream);
alpn_ = make_string_ref(balloc_, proto);
@@ -600,7 +600,7 @@ int ClientHandler::validate_next_proto() {
}
if (proto == StringRef::from_lit("http/1.1")) {
upstream_ = make_unique<HttpsUpstream>(this);
upstream_ = std::make_unique<HttpsUpstream>(this);
alpn_ = StringRef::from_lit("http/1.1");
// At this point, input buffer is already filled with some bytes.
@@ -660,7 +660,7 @@ void ClientHandler::pool_downstream_connection(
auto &shared_addr = group->shared_addr;
if (shared_addr->affinity.type == AFFINITY_NONE) {
if (shared_addr->affinity.type == SessionAffinity::NONE) {
auto &dconn_pool = group->shared_addr->dconn_pool;
dconn_pool.add_downstream_connection(std::move(dconn));
@@ -769,7 +769,7 @@ Http2Session *ClientHandler::select_http2_session(
// First count the working backend addresses.
size_t min = 0;
for (const auto &addr : shared_addr->addrs) {
if (addr.proto != PROTO_HTTP2 || addr.connect_blocker->blocked()) {
if (addr.proto != Proto::HTTP2 || addr.connect_blocker->blocked()) {
continue;
}
@@ -825,7 +825,7 @@ Http2Session *ClientHandler::select_http2_session(
DownstreamAddr *selected_addr = nullptr;
for (auto &addr : shared_addr->addrs) {
if (addr.in_avail || addr.proto != PROTO_HTTP2 ||
if (addr.in_avail || addr.proto != Proto::HTTP2 ||
(addr.http2_extra_freelist.size() == 0 &&
addr.connect_blocker->blocked())) {
continue;
@@ -939,7 +939,7 @@ uint32_t ClientHandler::get_affinity_cookie(Downstream *downstream,
std::unique_ptr<DownstreamConnection>
ClientHandler::get_downstream_connection(int &err, Downstream *downstream,
shrpx_proto pref_proto) {
Proto pref_proto) {
size_t group_idx;
auto &downstreamconf = *worker_->get_downstream_config();
auto &routerconf = downstreamconf.router;
@@ -952,10 +952,12 @@ ClientHandler::get_downstream_connection(int &err, Downstream *downstream,
err = 0;
switch (faddr_->alt_mode) {
case ALTMODE_API:
return make_unique<APIDownstreamConnection>(worker_);
case ALTMODE_HEALTHMON:
return make_unique<HealthMonitorDownstreamConnection>();
case UpstreamAltMode::API:
return std::make_unique<APIDownstreamConnection>(worker_);
case UpstreamAltMode::HEALTHMON:
return std::make_unique<HealthMonitorDownstreamConnection>();
default:
break;
}
auto &balloc = downstream->get_block_allocator();
@@ -979,7 +981,7 @@ ClientHandler::get_downstream_connection(int &err, Downstream *downstream,
StringRef path;
// CONNECT method does not have path. But we requires path in
// host-path mapping. As workaround, we assume that path is "/".
if (req.method != HTTP_CONNECT) {
if (!req.regular_connect_method()) {
path = req.path;
}
@@ -1003,17 +1005,17 @@ ClientHandler::get_downstream_connection(int &err, Downstream *downstream,
auto &group = groups[group_idx];
auto &shared_addr = group->shared_addr;
if (shared_addr->affinity.type != AFFINITY_NONE) {
if (shared_addr->affinity.type != SessionAffinity::NONE) {
uint32_t hash;
switch (shared_addr->affinity.type) {
case AFFINITY_IP:
case SessionAffinity::IP:
if (!affinity_hash_computed_) {
affinity_hash_ = compute_affinity_from_ip(ipaddr_);
affinity_hash_computed_ = true;
}
hash = affinity_hash_;
break;
case AFFINITY_COOKIE:
case SessionAffinity::COOKIE:
hash = get_affinity_cookie(downstream, shared_addr->affinity.cookie.name);
break;
default:
@@ -1043,7 +1045,7 @@ ClientHandler::get_downstream_connection(int &err, Downstream *downstream,
}
addr = &shared_addr->addrs[shared_addr->affinity_hash[i].idx];
if (addr->connect_blocker->blocked() ||
(pref_proto != PROTO_NONE && pref_proto != addr->proto)) {
(pref_proto != Proto::NONE && pref_proto != addr->proto)) {
continue;
}
break;
@@ -1055,10 +1057,10 @@ ClientHandler::get_downstream_connection(int &err, Downstream *downstream,
aff_idx = i;
}
if (addr->proto == PROTO_HTTP2) {
if (addr->proto == Proto::HTTP2) {
auto http2session = select_http2_session_with_affinity(group, addr);
auto dconn = make_unique<Http2DownstreamConnection>(http2session);
auto dconn = std::make_unique<Http2DownstreamConnection>(http2session);
dconn->set_client_handler(this);
@@ -1069,8 +1071,8 @@ ClientHandler::get_downstream_connection(int &err, Downstream *downstream,
auto dconn = dconn_pool->pop_downstream_connection();
if (!dconn) {
dconn = make_unique<HttpDownstreamConnection>(group, aff_idx, conn_.loop,
worker_);
dconn = std::make_unique<HttpDownstreamConnection>(group, aff_idx,
conn_.loop, worker_);
}
dconn->set_client_handler(this);
@@ -1081,33 +1083,33 @@ ClientHandler::get_downstream_connection(int &err, Downstream *downstream,
auto http1_weight = shared_addr->http1_pri.weight;
auto http2_weight = shared_addr->http2_pri.weight;
auto proto = PROTO_NONE;
auto proto = Proto::NONE;
if (pref_proto == PROTO_HTTP1) {
if (pref_proto == Proto::HTTP1) {
if (http1_weight > 0) {
proto = PROTO_HTTP1;
proto = Proto::HTTP1;
}
} else if (pref_proto == PROTO_HTTP2) {
} else if (pref_proto == Proto::HTTP2) {
if (http2_weight > 0) {
proto = PROTO_HTTP2;
proto = Proto::HTTP2;
}
} else if (http1_weight > 0 && http2_weight > 0) {
// We only advance cycle if both weight has nonzero to keep its
// distance under WEIGHT_MAX.
if (pri_less(shared_addr->http1_pri, shared_addr->http2_pri)) {
proto = PROTO_HTTP1;
proto = Proto::HTTP1;
shared_addr->http1_pri.cycle = next_cycle(shared_addr->http1_pri);
} else {
proto = PROTO_HTTP2;
proto = Proto::HTTP2;
shared_addr->http2_pri.cycle = next_cycle(shared_addr->http2_pri);
}
} else if (http1_weight > 0) {
proto = PROTO_HTTP1;
proto = Proto::HTTP1;
} else if (http2_weight > 0) {
proto = PROTO_HTTP2;
proto = Proto::HTTP2;
}
if (proto == PROTO_NONE) {
if (proto == Proto::NONE) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "No working downstream address found";
}
@@ -1116,7 +1118,7 @@ ClientHandler::get_downstream_connection(int &err, Downstream *downstream,
return nullptr;
}
if (proto == PROTO_HTTP2) {
if (proto == Proto::HTTP2) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "Downstream connection pool is empty."
<< " Create new one";
@@ -1129,7 +1131,7 @@ ClientHandler::get_downstream_connection(int &err, Downstream *downstream,
return nullptr;
}
auto dconn = make_unique<Http2DownstreamConnection>(http2session);
auto dconn = std::make_unique<Http2DownstreamConnection>(http2session);
dconn->set_client_handler(this);
@@ -1152,8 +1154,8 @@ ClientHandler::get_downstream_connection(int &err, Downstream *downstream,
<< " Create new one";
}
dconn =
make_unique<HttpDownstreamConnection>(group, 0, conn_.loop, worker_);
dconn = std::make_unique<HttpDownstreamConnection>(group, 0, conn_.loop,
worker_);
}
dconn->set_client_handler(this);
@@ -1166,14 +1168,14 @@ MemchunkPool *ClientHandler::get_mcpool() { return worker_->get_mcpool(); }
SSL *ClientHandler::get_ssl() const { return conn_.tls.ssl; }
void ClientHandler::direct_http2_upgrade() {
upstream_ = make_unique<Http2Upstream>(this);
upstream_ = std::make_unique<Http2Upstream>(this);
alpn_ = StringRef::from_lit(NGHTTP2_CLEARTEXT_PROTO_VERSION_ID);
on_read_ = &ClientHandler::upstream_read;
write_ = &ClientHandler::write_clear;
}
int ClientHandler::perform_http2_upgrade(HttpsUpstream *http) {
auto upstream = make_unique<Http2Upstream>(this);
auto upstream = std::make_unique<Http2Upstream>(this);
auto output = upstream->get_response_buf();
@@ -1319,7 +1321,7 @@ int ClientHandler::proxy_protocol_read() {
// NULL character really destroys functions which expects NULL
// terminated string. We won't expect it in PROXY protocol line, so
// find it here.
auto chrs = std::array<char, 2>{{'\n', '\0'}};
auto chrs = std::array<char, 2>{'\n', '\0'};
constexpr size_t MAX_PROXY_LINELEN = 107;
@@ -1490,7 +1492,7 @@ int ClientHandler::proxy_protocol_read() {
auto &fwdconf = config->http.forwarded;
if ((fwdconf.params & FORWARDED_FOR) &&
fwdconf.for_node_type == FORWARDED_NODE_IP) {
fwdconf.for_node_type == ForwardedNode::IP) {
init_forwarded_for(family, ipaddr_);
}
@@ -1500,7 +1502,7 @@ int ClientHandler::proxy_protocol_read() {
StringRef ClientHandler::get_forwarded_by() const {
auto &fwdconf = get_config()->http.forwarded;
if (fwdconf.by_node_type == FORWARDED_NODE_OBFUSCATED) {
if (fwdconf.by_node_type == ForwardedNode::OBFUSCATED) {
return fwdconf.by_obfuscated;
}

View File

@@ -106,7 +106,7 @@ public:
// backend whose protocol is |pref_proto|.
std::unique_ptr<DownstreamConnection>
get_downstream_connection(int &err, Downstream *downstream,
shrpx_proto pref_proto = PROTO_NONE);
Proto pref_proto = Proto::NONE);
MemchunkPool *get_mcpool();
SSL *get_ssl() const;
// Call this function when HTTP/2 connection header is received at

View File

@@ -160,7 +160,7 @@ bool is_secure(const StringRef &filename) {
std::unique_ptr<TicketKeys>
read_tls_ticket_key_file(const std::vector<StringRef> &files,
const EVP_CIPHER *cipher, const EVP_MD *hmac) {
auto ticket_keys = make_unique<TicketKeys>();
auto ticket_keys = std::make_unique<TicketKeys>();
auto &keys = ticket_keys->keys;
keys.resize(files.size());
auto enc_keylen = EVP_CIPHER_key_length(cipher);
@@ -379,7 +379,7 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
switch (name[2]) {
case 'd':
if (util::strieq_l("pi", name, 2)) {
return SHRPX_LOGF_PID;
return LogFragmentType::PID;
}
break;
}
@@ -388,7 +388,7 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
switch (name[3]) {
case 'n':
if (util::strieq_l("alp", name, 3)) {
return SHRPX_LOGF_ALPN;
return LogFragmentType::ALPN;
}
break;
}
@@ -397,7 +397,7 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
switch (name[5]) {
case 's':
if (util::strieq_l("statu", name, 5)) {
return SHRPX_LOGF_STATUS;
return LogFragmentType::STATUS;
}
break;
}
@@ -406,12 +406,12 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
switch (name[6]) {
case 'i':
if (util::strieq_l("tls_sn", name, 6)) {
return SHRPX_LOGF_TLS_SNI;
return LogFragmentType::TLS_SNI;
}
break;
case 't':
if (util::strieq_l("reques", name, 6)) {
return SHRPX_LOGF_REQUEST;
return LogFragmentType::REQUEST;
}
break;
}
@@ -420,15 +420,15 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
switch (name[9]) {
case 'l':
if (util::strieq_l("time_loca", name, 9)) {
return SHRPX_LOGF_TIME_LOCAL;
return LogFragmentType::TIME_LOCAL;
}
break;
case 'r':
if (util::strieq_l("ssl_ciphe", name, 9)) {
return SHRPX_LOGF_SSL_CIPHER;
return LogFragmentType::SSL_CIPHER;
}
if (util::strieq_l("tls_ciphe", name, 9)) {
return SHRPX_LOGF_TLS_CIPHER;
return LogFragmentType::TLS_CIPHER;
}
break;
}
@@ -437,15 +437,15 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
switch (name[10]) {
case 'r':
if (util::strieq_l("remote_add", name, 10)) {
return SHRPX_LOGF_REMOTE_ADDR;
return LogFragmentType::REMOTE_ADDR;
}
break;
case 't':
if (util::strieq_l("remote_por", name, 10)) {
return SHRPX_LOGF_REMOTE_PORT;
return LogFragmentType::REMOTE_PORT;
}
if (util::strieq_l("server_por", name, 10)) {
return SHRPX_LOGF_SERVER_PORT;
return LogFragmentType::SERVER_PORT;
}
break;
}
@@ -454,28 +454,28 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
switch (name[11]) {
case '1':
if (util::strieq_l("time_iso860", name, 11)) {
return SHRPX_LOGF_TIME_ISO8601;
return LogFragmentType::TIME_ISO8601;
}
break;
case 'e':
if (util::strieq_l("request_tim", name, 11)) {
return SHRPX_LOGF_REQUEST_TIME;
return LogFragmentType::REQUEST_TIME;
}
break;
case 'l':
if (util::strieq_l("ssl_protoco", name, 11)) {
return SHRPX_LOGF_SSL_PROTOCOL;
return LogFragmentType::SSL_PROTOCOL;
}
if (util::strieq_l("tls_protoco", name, 11)) {
return SHRPX_LOGF_TLS_PROTOCOL;
return LogFragmentType::TLS_PROTOCOL;
}
break;
case 't':
if (util::strieq_l("backend_hos", name, 11)) {
return SHRPX_LOGF_BACKEND_HOST;
return LogFragmentType::BACKEND_HOST;
}
if (util::strieq_l("backend_por", name, 11)) {
return SHRPX_LOGF_BACKEND_PORT;
return LogFragmentType::BACKEND_PORT;
}
break;
}
@@ -484,10 +484,10 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
switch (name[13]) {
case 'd':
if (util::strieq_l("ssl_session_i", name, 13)) {
return SHRPX_LOGF_SSL_SESSION_ID;
return LogFragmentType::SSL_SESSION_ID;
}
if (util::strieq_l("tls_session_i", name, 13)) {
return SHRPX_LOGF_TLS_SESSION_ID;
return LogFragmentType::TLS_SESSION_ID;
}
break;
}
@@ -496,7 +496,7 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
switch (name[14]) {
case 't':
if (util::strieq_l("body_bytes_sen", name, 14)) {
return SHRPX_LOGF_BODY_BYTES_SENT;
return LogFragmentType::BODY_BYTES_SENT;
}
break;
}
@@ -505,7 +505,7 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
switch (name[16]) {
case 'l':
if (util::strieq_l("tls_client_seria", name, 16)) {
return SHRPX_LOGF_TLS_CLIENT_SERIAL;
return LogFragmentType::TLS_CLIENT_SERIAL;
}
break;
}
@@ -514,10 +514,10 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
switch (name[17]) {
case 'd':
if (util::strieq_l("ssl_session_reuse", name, 17)) {
return SHRPX_LOGF_SSL_SESSION_REUSED;
return LogFragmentType::SSL_SESSION_REUSED;
}
if (util::strieq_l("tls_session_reuse", name, 17)) {
return SHRPX_LOGF_TLS_SESSION_REUSED;
return LogFragmentType::TLS_SESSION_REUSED;
}
break;
}
@@ -526,7 +526,7 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
switch (name[21]) {
case 'e':
if (util::strieq_l("tls_client_issuer_nam", name, 21)) {
return SHRPX_LOGF_TLS_CLIENT_ISSUER_NAME;
return LogFragmentType::TLS_CLIENT_ISSUER_NAME;
}
break;
}
@@ -535,7 +535,7 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
switch (name[22]) {
case 'e':
if (util::strieq_l("tls_client_subject_nam", name, 22)) {
return SHRPX_LOGF_TLS_CLIENT_SUBJECT_NAME;
return LogFragmentType::TLS_CLIENT_SUBJECT_NAME;
}
break;
}
@@ -544,7 +544,7 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
switch (name[26]) {
case '1':
if (util::strieq_l("tls_client_fingerprint_sha", name, 26)) {
return SHRPX_LOGF_TLS_CLIENT_FINGERPRINT_SHA1;
return LogFragmentType::TLS_CLIENT_FINGERPRINT_SHA1;
}
break;
}
@@ -553,13 +553,13 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
switch (name[28]) {
case '6':
if (util::strieq_l("tls_client_fingerprint_sha25", name, 28)) {
return SHRPX_LOGF_TLS_CLIENT_FINGERPRINT_SHA256;
return LogFragmentType::TLS_CLIENT_FINGERPRINT_SHA256;
}
break;
}
break;
}
return SHRPX_LOGF_NONE;
return LogFragmentType::NONE;
}
} // namespace
@@ -613,16 +613,16 @@ std::vector<LogFragment> parse_log_format(BlockAllocator &balloc,
auto type = log_var_lookup_token(var_name, var_namelen);
if (type == SHRPX_LOGF_NONE) {
if (type == LogFragmentType::NONE) {
if (util::istarts_with_l(StringRef{var_name, var_namelen}, "http_")) {
if (util::streq_l("host", StringRef{var_name + str_size("http_"),
var_namelen - str_size("http_")})) {
// Special handling of host header field. We will use
// :authority header field if host header is missing. This
// is a typical case in HTTP/2.
type = SHRPX_LOGF_AUTHORITY;
type = LogFragmentType::AUTHORITY;
} else {
type = SHRPX_LOGF_HTTP;
type = LogFragmentType::HTTP;
value = var_name + str_size("http_");
}
} else {
@@ -634,7 +634,7 @@ std::vector<LogFragment> parse_log_format(BlockAllocator &balloc,
if (literal_start < var_start) {
res.emplace_back(
SHRPX_LOGF_LITERAL,
LogFragmentType::LITERAL,
make_string_ref(balloc, StringRef{literal_start, var_start}));
}
@@ -661,7 +661,7 @@ std::vector<LogFragment> parse_log_format(BlockAllocator &balloc,
}
if (literal_start != eop) {
res.emplace_back(SHRPX_LOGF_LITERAL,
res.emplace_back(LogFragmentType::LITERAL,
make_string_ref(balloc, StringRef{literal_start, eop}));
}
@@ -756,7 +756,7 @@ int parse_memcached_connection_params(MemcachedConnectionParams &out,
} // namespace
struct UpstreamParams {
int alt_mode;
UpstreamAltMode alt_mode;
bool tls;
bool sni_fwd;
bool proxyproto;
@@ -779,17 +779,19 @@ int parse_upstream_params(UpstreamParams &out, const StringRef &src_params) {
} else if (util::strieq_l("no-tls", param)) {
out.tls = false;
} else if (util::strieq_l("api", param)) {
if (out.alt_mode && out.alt_mode != ALTMODE_API) {
if (out.alt_mode != UpstreamAltMode::NONE &&
out.alt_mode != UpstreamAltMode::API) {
LOG(ERROR) << "frontend: api and healthmon are mutually exclusive";
return -1;
}
out.alt_mode = ALTMODE_API;
out.alt_mode = UpstreamAltMode::API;
} else if (util::strieq_l("healthmon", param)) {
if (out.alt_mode && out.alt_mode != ALTMODE_HEALTHMON) {
if (out.alt_mode != UpstreamAltMode::NONE &&
out.alt_mode != UpstreamAltMode::HEALTHMON) {
LOG(ERROR) << "frontend: api and healthmon are mutually exclusive";
return -1;
}
out.alt_mode = ALTMODE_HEALTHMON;
out.alt_mode = UpstreamAltMode::HEALTHMON;
} else if (util::strieq_l("proxyproto", param)) {
out.proxyproto = true;
} else if (!param.empty()) {
@@ -812,15 +814,33 @@ struct DownstreamParams {
StringRef sni;
StringRef mruby;
AffinityConfig affinity;
ev_tstamp read_timeout;
ev_tstamp write_timeout;
size_t fall;
size_t rise;
shrpx_proto proto;
Proto proto;
bool tls;
bool dns;
bool redirect_if_not_tls;
bool upgrade_scheme;
};
namespace {
// Parses |value| of parameter named |name| as duration. This
// function returns 0 if it succeeds and the parsed value is assigned
// to |dest|, or -1.
int parse_downstream_param_duration(ev_tstamp &dest, const StringRef &name,
const StringRef &value) {
auto t = util::parse_duration_with_unit(value);
if (t == std::numeric_limits<double>::infinity()) {
LOG(ERROR) << "backend: " << name << ": bad value: '" << value << "'";
return -1;
}
dest = t;
return 0;
}
} // namespace
namespace {
// Parses downstream configuration parameter |src_params|, and stores
// parsed results into |out|. This function returns 0 if it succeeds,
@@ -840,10 +860,10 @@ int parse_downstream_params(DownstreamParams &out,
}
if (util::streq_l("h2", std::begin(protostr), protostr.size())) {
out.proto = PROTO_HTTP2;
out.proto = Proto::HTTP2;
} else if (util::streq_l("http/1.1", std::begin(protostr),
protostr.size())) {
out.proto = PROTO_HTTP1;
out.proto = Proto::HTTP1;
} else {
LOG(ERROR) << "backend: proto: unknown protocol " << protostr;
return -1;
@@ -885,11 +905,11 @@ int parse_downstream_params(DownstreamParams &out,
} else if (util::istarts_with_l(param, "affinity=")) {
auto valstr = StringRef{first + str_size("affinity="), end};
if (util::strieq_l("none", valstr)) {
out.affinity.type = AFFINITY_NONE;
out.affinity.type = SessionAffinity::NONE;
} else if (util::strieq_l("ip", valstr)) {
out.affinity.type = AFFINITY_IP;
out.affinity.type = SessionAffinity::IP;
} else if (util::strieq_l("cookie", valstr)) {
out.affinity.type = AFFINITY_COOKIE;
out.affinity.type = SessionAffinity::COOKIE;
} else {
LOG(ERROR)
<< "backend: affinity: value must be one of none, ip, and cookie";
@@ -909,11 +929,11 @@ int parse_downstream_params(DownstreamParams &out,
} else if (util::istarts_with_l(param, "affinity-cookie-secure=")) {
auto valstr = StringRef{first + str_size("affinity-cookie-secure="), end};
if (util::strieq_l("auto", valstr)) {
out.affinity.cookie.secure = COOKIE_SECURE_AUTO;
out.affinity.cookie.secure = SessionAffinityCookieSecure::AUTO;
} else if (util::strieq_l("yes", valstr)) {
out.affinity.cookie.secure = COOKIE_SECURE_YES;
out.affinity.cookie.secure = SessionAffinityCookieSecure::YES;
} else if (util::strieq_l("no", valstr)) {
out.affinity.cookie.secure = COOKIE_SECURE_NO;
out.affinity.cookie.secure = SessionAffinityCookieSecure::NO;
} else {
LOG(ERROR) << "backend: affinity-cookie-secure: value must be one of "
"auto, yes, and no";
@@ -928,6 +948,18 @@ int parse_downstream_params(DownstreamParams &out,
} else if (util::istarts_with_l(param, "mruby=")) {
auto valstr = StringRef{first + str_size("mruby="), end};
out.mruby = valstr;
} else if (util::istarts_with_l(param, "read-timeout=")) {
if (parse_downstream_param_duration(
out.read_timeout, StringRef::from_lit("read-timeout"),
StringRef{first + str_size("read-timeout="), end}) == -1) {
return -1;
}
} else if (util::istarts_with_l(param, "write-timeout=")) {
if (parse_downstream_param_duration(
out.write_timeout, StringRef::from_lit("write-timeout"),
StringRef{first + str_size("write-timeout="), end}) == -1) {
return -1;
}
} else if (!param.empty()) {
LOG(ERROR) << "backend: " << param << ": unknown keyword";
return -1;
@@ -963,7 +995,7 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
auto &addr_groups = downstreamconf.addr_groups;
DownstreamParams params{};
params.proto = PROTO_HTTP1;
params.proto = Proto::HTTP1;
if (parse_downstream_params(params, src_params) != 0) {
return -1;
@@ -974,7 +1006,7 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
return -1;
}
if (params.affinity.type == AFFINITY_COOKIE &&
if (params.affinity.type == SessionAffinity::COOKIE &&
params.affinity.cookie.name.empty()) {
LOG(ERROR) << "backend: affinity-cookie-name is mandatory if "
"affinity=cookie is specified";
@@ -1026,10 +1058,10 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
auto &g = addr_groups[(*it).second];
// Last value wins if we have multiple different affinity
// value under one group.
if (params.affinity.type != AFFINITY_NONE) {
if (g.affinity.type == AFFINITY_NONE) {
if (params.affinity.type != SessionAffinity::NONE) {
if (g.affinity.type == SessionAffinity::NONE) {
g.affinity.type = params.affinity.type;
if (params.affinity.type == AFFINITY_COOKIE) {
if (params.affinity.type == SessionAffinity::COOKIE) {
g.affinity.cookie.name = make_string_ref(
downstreamconf.balloc, params.affinity.cookie.name);
if (!params.affinity.cookie.path.empty()) {
@@ -1053,15 +1085,40 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
g.redirect_if_not_tls = true;
}
// All backends in the same group must have the same mruby path.
// If some backend does not specify mruby file, and there is at
// least one backend with mruby file, it is used for all backend
// in the group.
if (g.mruby_file.empty()) {
g.mruby_file = make_string_ref(downstreamconf.balloc, params.mruby);
} else if (g.mruby_file != params.mruby) {
LOG(ERROR) << "backend: mruby: multiple different mruby file found in "
"a single group";
return -1;
// If some backends do not specify mruby file, and there is at
// least one backend with mruby file, it is used for all
// backends in the group.
if (!params.mruby.empty()) {
if (g.mruby_file.empty()) {
g.mruby_file = make_string_ref(downstreamconf.balloc, params.mruby);
} else if (g.mruby_file != params.mruby) {
LOG(ERROR) << "backend: mruby: multiple different mruby file found "
"in a single group";
return -1;
}
}
// All backends in the same group must have the same read/write
// timeout. If some backends do not specify read/write timeout,
// and there is at least one backend with read/write timeout, it
// is used for all backends in the group.
if (params.read_timeout > 1e-9) {
if (g.timeout.read < 1e-9) {
g.timeout.read = params.read_timeout;
} else if (fabs(g.timeout.read - params.read_timeout) > 1e-9) {
LOG(ERROR)
<< "backend: read-timeout: multiple different read-timeout "
"found in a single group";
return -1;
}
}
if (params.write_timeout > 1e-9) {
if (g.timeout.write < 1e-9) {
g.timeout.write = params.write_timeout;
} else if (fabs(g.timeout.write - params.write_timeout) > 1e-9) {
LOG(ERROR) << "backend: write-timeout: multiple different "
"write-timeout found in a single group";
return -1;
}
}
g.addrs.push_back(addr);
@@ -1074,7 +1131,7 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
auto &g = addr_groups.back();
g.addrs.push_back(addr);
g.affinity.type = params.affinity.type;
if (params.affinity.type == AFFINITY_COOKIE) {
if (params.affinity.type == SessionAffinity::COOKIE) {
g.affinity.cookie.name =
make_string_ref(downstreamconf.balloc, params.affinity.cookie.name);
if (!params.affinity.cookie.path.empty()) {
@@ -1085,6 +1142,8 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
}
g.redirect_if_not_tls = params.redirect_if_not_tls;
g.mruby_file = make_string_ref(downstreamconf.balloc, params.mruby);
g.timeout.read = params.read_timeout;
g.timeout.write = params.write_timeout;
if (pattern[0] == '*') {
// wildcard pattern
@@ -1138,27 +1197,27 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
} // namespace
namespace {
int parse_forwarded_node_type(const StringRef &optarg) {
ForwardedNode parse_forwarded_node_type(const StringRef &optarg) {
if (util::strieq_l("obfuscated", optarg)) {
return FORWARDED_NODE_OBFUSCATED;
return ForwardedNode::OBFUSCATED;
}
if (util::strieq_l("ip", optarg)) {
return FORWARDED_NODE_IP;
return ForwardedNode::IP;
}
if (optarg.size() < 2 || optarg[0] != '_') {
return -1;
return static_cast<ForwardedNode>(-1);
}
if (std::find_if_not(std::begin(optarg), std::end(optarg), [](char c) {
return util::is_alpha(c) || util::is_digit(c) || c == '.' || c == '_' ||
c == '-';
}) != std::end(optarg)) {
return -1;
return static_cast<ForwardedNode>(-1);
}
return FORWARDED_NODE_OBFUSCATED;
return ForwardedNode::OBFUSCATED;
}
} // namespace
@@ -1759,6 +1818,11 @@ int option_lookup_token(const char *name, size_t namelen) {
return SHRPX_OPTID_FORWARDED_FOR;
}
break;
case 's':
if (util::strieq_l("tls13-cipher", name, 12)) {
return SHRPX_OPTID_TLS13_CIPHERS;
}
break;
case 't':
if (util::strieq_l("verify-clien", name, 12)) {
return SHRPX_OPTID_VERIFY_CLIENT;
@@ -1883,6 +1947,11 @@ int option_lookup_token(const char *name, size_t namelen) {
break;
case 18:
switch (name[17]) {
case 'a':
if (util::strieq_l("tls-max-early-dat", name, 17)) {
return SHRPX_OPTID_TLS_MAX_EARLY_DATA;
}
break;
case 'r':
if (util::strieq_l("add-request-heade", name, 17)) {
return SHRPX_OPTID_ADD_REQUEST_HEADER;
@@ -1951,6 +2020,11 @@ int option_lookup_token(const char *name, size_t namelen) {
return SHRPX_OPTID_OCSP_UPDATE_INTERVAL;
}
break;
case 's':
if (util::strieq_l("tls13-client-cipher", name, 19)) {
return SHRPX_OPTID_TLS13_CLIENT_CIPHERS;
}
break;
case 't':
if (util::strieq_l("backend-read-timeou", name, 19)) {
return SHRPX_OPTID_BACKEND_READ_TIMEOUT;
@@ -2114,6 +2188,11 @@ int option_lookup_token(const char *name, size_t namelen) {
break;
case 26:
switch (name[25]) {
case 'a':
if (util::strieq_l("tls-no-postpone-early-dat", name, 25)) {
return SHRPX_OPTID_TLS_NO_POSTPONE_EARLY_DATA;
}
break;
case 'e':
if (util::strieq_l("frontend-http2-window-siz", name, 25)) {
return SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_SIZE;
@@ -2166,6 +2245,11 @@ int option_lookup_token(const char *name, size_t namelen) {
break;
case 28:
switch (name[27]) {
case 'a':
if (util::strieq_l("no-strip-incoming-early-dat", name, 27)) {
return SHRPX_OPTID_NO_STRIP_INCOMING_EARLY_DATA;
}
break;
case 'd':
if (util::strieq_l("tls-dyn-rec-warmup-threshol", name, 27)) {
return SHRPX_OPTID_TLS_DYN_REC_WARMUP_THRESHOLD;
@@ -2490,7 +2574,7 @@ int parse_config(Config *config, int optid, const StringRef &opt,
addr.alt_mode = params.alt_mode;
addr.accept_proxy_protocol = params.proxyproto;
if (addr.alt_mode == ALTMODE_API) {
if (addr.alt_mode == UpstreamAltMode::API) {
apiconf.enabled = true;
}
@@ -2822,6 +2906,10 @@ int parse_config(Config *config, int optid, const StringRef &opt,
case SHRPX_OPTID_CIPHERS:
config->tls.ciphers = make_string_ref(config->balloc, optarg);
return 0;
case SHRPX_OPTID_TLS13_CIPHERS:
config->tls.tls13_ciphers = make_string_ref(config->balloc, optarg);
return 0;
case SHRPX_OPTID_CLIENT:
LOG(ERROR) << opt
@@ -3301,7 +3389,7 @@ int parse_config(Config *config, int optid, const StringRef &opt,
case SHRPX_OPTID_FORWARDED_FOR: {
auto type = parse_forwarded_node_type(optarg);
if (type == -1 ||
if (type == static_cast<ForwardedNode>(-1) ||
(optid == SHRPX_OPTID_FORWARDED_FOR && optarg[0] == '_')) {
LOG(ERROR) << opt << ": unknown node type or illegal obfuscated string "
<< optarg;
@@ -3312,7 +3400,7 @@ int parse_config(Config *config, int optid, const StringRef &opt,
switch (optid) {
case SHRPX_OPTID_FORWARDED_BY:
fwdconf.by_node_type = static_cast<shrpx_forwarded_node_type>(type);
fwdconf.by_node_type = type;
if (optarg[0] == '_') {
fwdconf.by_obfuscated = make_string_ref(config->balloc, optarg);
} else {
@@ -3320,7 +3408,7 @@ int parse_config(Config *config, int optid, const StringRef &opt,
}
break;
case SHRPX_OPTID_FORWARDED_FOR:
fwdconf.for_node_type = static_cast<shrpx_forwarded_node_type>(type);
fwdconf.for_node_type = type;
break;
}
@@ -3537,6 +3625,10 @@ int parse_config(Config *config, int optid, const StringRef &opt,
case SHRPX_OPTID_CLIENT_CIPHERS:
config->tls.client.ciphers = make_string_ref(config->balloc, optarg);
return 0;
case SHRPX_OPTID_TLS13_CLIENT_CIPHERS:
config->tls.client.tls13_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);
@@ -3590,6 +3682,17 @@ int parse_config(Config *config, int optid, const StringRef &opt,
case SHRPX_OPTID_IGNORE_PER_PATTERN_MRUBY_ERROR:
config->ignore_per_pattern_mruby_error = util::strieq_l("yes", optarg);
return 0;
case SHRPX_OPTID_TLS_NO_POSTPONE_EARLY_DATA:
config->tls.no_postpone_early_data = util::strieq_l("yes", optarg);
return 0;
case SHRPX_OPTID_TLS_MAX_EARLY_DATA: {
return parse_uint_with_unit(&config->tls.max_early_data, opt, optarg);
}
case SHRPX_OPTID_NO_STRIP_INCOMING_EARLY_DATA:
config->http.early_data.strip_incoming = !util::strieq_l("yes", optarg);
return 0;
case SHRPX_OPTID_CONF:
LOG(WARN) << "conf: ignored";
@@ -3771,15 +3874,15 @@ int int_syslog_facility(const StringRef &strfacility) {
return -1;
}
StringRef strproto(shrpx_proto proto) {
StringRef strproto(Proto proto) {
switch (proto) {
case PROTO_NONE:
case Proto::NONE:
return StringRef::from_lit("none");
case PROTO_HTTP1:
case Proto::HTTP1:
return StringRef::from_lit("http/1.1");
case PROTO_HTTP2:
case Proto::HTTP2:
return StringRef::from_lit("h2");
case PROTO_MEMCACHED:
case Proto::MEMCACHED:
return StringRef::from_lit("memcached");
}
@@ -3842,7 +3945,7 @@ int configure_downstream_group(Config *config, bool http2_proxy,
DownstreamAddrConfig addr{};
addr.host = StringRef::from_lit(DEFAULT_DOWNSTREAM_HOST);
addr.port = DEFAULT_DOWNSTREAM_PORT;
addr.proto = PROTO_HTTP1;
addr.proto = Proto::HTTP1;
DownstreamAddrGroupConfig g(StringRef::from_lit("/"));
g.addrs.push_back(std::move(addr));
@@ -3975,7 +4078,7 @@ int configure_downstream_group(Config *config, bool http2_proxy,
}
}
if (g.affinity.type != AFFINITY_NONE) {
if (g.affinity.type != SessionAffinity::NONE) {
size_t idx = 0;
for (auto &addr : g.addrs) {
StringRef key;
@@ -4002,6 +4105,14 @@ int configure_downstream_group(Config *config, bool http2_proxy,
return lhs.hash < rhs.hash;
});
}
auto &timeout = g.timeout;
if (timeout.read < 1e-9) {
timeout.read = downstreamconf.timeout.read;
}
if (timeout.write < 1e-9) {
timeout.write = downstreamconf.timeout.write;
}
}
return 0;

View File

@@ -347,43 +347,57 @@ constexpr auto SHRPX_OPT_VERIFY_CLIENT_TOLERATE_EXPIRED =
StringRef::from_lit("verify-client-tolerate-expired");
constexpr auto SHRPX_OPT_IGNORE_PER_PATTERN_MRUBY_ERROR =
StringRef::from_lit("ignore-per-pattern-mruby-error");
constexpr auto SHRPX_OPT_TLS_NO_POSTPONE_EARLY_DATA =
StringRef::from_lit("tls-no-postpone-early-data");
constexpr auto SHRPX_OPT_TLS_MAX_EARLY_DATA =
StringRef::from_lit("tls-max-early-data");
constexpr auto SHRPX_OPT_TLS13_CIPHERS = StringRef::from_lit("tls13-ciphers");
constexpr auto SHRPX_OPT_TLS13_CLIENT_CIPHERS =
StringRef::from_lit("tls13-client-ciphers");
constexpr auto SHRPX_OPT_NO_STRIP_INCOMING_EARLY_DATA =
StringRef::from_lit("no-strip-incoming-early-data");
constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8;
constexpr char DEFAULT_DOWNSTREAM_HOST[] = "127.0.0.1";
constexpr int16_t DEFAULT_DOWNSTREAM_PORT = 80;
enum shrpx_proto { PROTO_NONE, PROTO_HTTP1, PROTO_HTTP2, PROTO_MEMCACHED };
enum shrpx_session_affinity {
// No session affinity
AFFINITY_NONE,
// Client IP affinity
AFFINITY_IP,
// Cookie based affinity
AFFINITY_COOKIE,
enum class Proto {
NONE,
HTTP1,
HTTP2,
MEMCACHED,
};
enum shrpx_cookie_secure {
enum class SessionAffinity {
// No session affinity
NONE,
// Client IP affinity
IP,
// Cookie based affinity
COOKIE,
};
enum class SessionAffinityCookieSecure {
// Secure attribute of session affinity cookie is determined by the
// request scheme.
COOKIE_SECURE_AUTO,
AUTO,
// Secure attribute of session affinity cookie is always set.
COOKIE_SECURE_YES,
YES,
// Secure attribute of session affinity cookie is always unset.
COOKIE_SECURE_NO,
NO,
};
struct AffinityConfig {
// Type of session affinity.
shrpx_session_affinity type;
SessionAffinity type;
struct {
// Name of a cookie to use.
StringRef name;
// Path which a cookie is applied to.
StringRef path;
// Secure attribute
shrpx_cookie_secure secure;
SessionAffinityCookieSecure secure;
} cookie;
};
@@ -395,9 +409,9 @@ enum shrpx_forwarded_param {
FORWARDED_PROTO = 0x8,
};
enum shrpx_forwarded_node_type {
FORWARDED_NODE_OBFUSCATED,
FORWARDED_NODE_IP,
enum class ForwardedNode {
OBFUSCATED,
IP,
};
struct AltSvc {
@@ -406,13 +420,13 @@ struct AltSvc {
uint16_t port;
};
enum UpstreamAltMode {
enum class UpstreamAltMode {
// No alternative mode
ALTMODE_NONE,
NONE,
// API processing mode
ALTMODE_API,
API,
// Health monitor mode
ALTMODE_HEALTHMON,
HEALTHMON,
};
struct UpstreamAddr {
@@ -430,7 +444,7 @@ struct UpstreamAddr {
// domain socket, this is 0.
int family;
// Alternate mode
int alt_mode;
UpstreamAltMode alt_mode;
// true if |host| contains UNIX domain socket path.
bool host_unix;
// true if TLS is enabled.
@@ -457,7 +471,7 @@ struct DownstreamAddrConfig {
size_t fall;
size_t rise;
// Application protocol used in this group
shrpx_proto proto;
Proto proto;
// backend port. 0 if |host_unix| is true.
uint16_t port;
// true if |host| contains UNIX domain socket path.
@@ -482,19 +496,26 @@ struct AffinityHash {
struct DownstreamAddrGroupConfig {
DownstreamAddrGroupConfig(const StringRef &pattern)
: pattern(pattern), affinity{AFFINITY_NONE}, redirect_if_not_tls(false) {}
: pattern(pattern),
affinity{SessionAffinity::NONE},
redirect_if_not_tls(false) {}
StringRef pattern;
StringRef mruby_file;
std::vector<DownstreamAddrConfig> addrs;
// Bunch of session affinity hash. Only used if affinity ==
// AFFINITY_IP.
// SessionAffinity::IP.
std::vector<AffinityHash> affinity_hash;
// Cookie based session affinity configuration.
AffinityConfig affinity;
// true if this group requires that client connection must be TLS,
// and the request must be redirected to https URI.
bool redirect_if_not_tls;
// Timeouts for backend connection.
struct {
ev_tstamp read;
ev_tstamp write;
} timeout;
};
struct TicketKey {
@@ -622,6 +643,7 @@ struct TLSConfig {
StringRef private_key_file;
StringRef cert_file;
StringRef ciphers;
StringRef tls13_ciphers;
bool no_http2_cipher_black_list;
} client;
@@ -648,14 +670,20 @@ struct TLSConfig {
StringRef cert_file;
StringRef dh_param_file;
StringRef ciphers;
StringRef tls13_ciphers;
StringRef ecdh_curves;
StringRef cacert;
// The maximum amount of 0-RTT data that server accepts.
uint32_t max_early_data;
// The minimum and maximum TLS version. These values are defined in
// OpenSSL header file.
int min_proto_version;
int max_proto_version;
bool insecure;
bool no_http2_cipher_black_list;
// true if forwarding requests included in TLS early data should not
// be postponed until TLS handshake finishes.
bool no_postpone_early_data;
};
// custom error page
@@ -676,10 +704,10 @@ struct HttpConfig {
uint32_t params;
// type of value recorded in "by" parameter of Forwarded header
// field.
shrpx_forwarded_node_type by_node_type;
ForwardedNode by_node_type;
// type of value recorded in "for" parameter of Forwarded header
// field.
shrpx_forwarded_node_type for_node_type;
ForwardedNode for_node_type;
bool strip_incoming;
} forwarded;
struct {
@@ -690,6 +718,9 @@ struct HttpConfig {
bool add;
bool strip_incoming;
} xfp;
struct {
bool strip_incoming;
} early_data;
std::vector<AltSvc> altsvcs;
std::vector<ErrorPage> error_pages;
HeaderRefs add_request_headers;
@@ -1086,6 +1117,7 @@ enum {
SHRPX_OPTID_NO_OCSP,
SHRPX_OPTID_NO_SERVER_PUSH,
SHRPX_OPTID_NO_SERVER_REWRITE,
SHRPX_OPTID_NO_STRIP_INCOMING_EARLY_DATA,
SHRPX_OPTID_NO_STRIP_INCOMING_X_FORWARDED_PROTO,
SHRPX_OPTID_NO_VERIFY_OCSP,
SHRPX_OPTID_NO_VIA,
@@ -1114,8 +1146,10 @@ enum {
SHRPX_OPTID_SYSLOG_FACILITY,
SHRPX_OPTID_TLS_DYN_REC_IDLE_TIMEOUT,
SHRPX_OPTID_TLS_DYN_REC_WARMUP_THRESHOLD,
SHRPX_OPTID_TLS_MAX_EARLY_DATA,
SHRPX_OPTID_TLS_MAX_PROTO_VERSION,
SHRPX_OPTID_TLS_MIN_PROTO_VERSION,
SHRPX_OPTID_TLS_NO_POSTPONE_EARLY_DATA,
SHRPX_OPTID_TLS_PROTO_LIST,
SHRPX_OPTID_TLS_SCT_DIR,
SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED,
@@ -1133,6 +1167,8 @@ enum {
SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_RETRY,
SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE,
SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_TLS,
SHRPX_OPTID_TLS13_CIPHERS,
SHRPX_OPTID_TLS13_CLIENT_CIPHERS,
SHRPX_OPTID_USER,
SHRPX_OPTID_VERIFY_CLIENT,
SHRPX_OPTID_VERIFY_CLIENT_CACERT,
@@ -1203,7 +1239,7 @@ read_tls_ticket_key_file(const std::vector<StringRef> &files,
const EVP_CIPHER *cipher, const EVP_MD *hmac);
// Returns string representation of |proto|.
StringRef strproto(shrpx_proto proto);
StringRef strproto(Proto proto);
int configure_downstream_group(Config *config, bool http2_proxy,
bool numeric_addr_only,

View File

@@ -79,84 +79,84 @@ void test_shrpx_config_parse_log_format(void) {
R"("${http_referer}" $http_host "$http_user_agent")"));
CU_ASSERT(16 == res.size());
CU_ASSERT(SHRPX_LOGF_REMOTE_ADDR == res[0].type);
CU_ASSERT(LogFragmentType::REMOTE_ADDR == res[0].type);
CU_ASSERT(SHRPX_LOGF_LITERAL == res[1].type);
CU_ASSERT(LogFragmentType::LITERAL == res[1].type);
CU_ASSERT(" - $remote_user [" == res[1].value);
CU_ASSERT(SHRPX_LOGF_TIME_LOCAL == res[2].type);
CU_ASSERT(LogFragmentType::TIME_LOCAL == res[2].type);
CU_ASSERT(SHRPX_LOGF_LITERAL == res[3].type);
CU_ASSERT(LogFragmentType::LITERAL == res[3].type);
CU_ASSERT("] \"" == res[3].value);
CU_ASSERT(SHRPX_LOGF_REQUEST == res[4].type);
CU_ASSERT(LogFragmentType::REQUEST == res[4].type);
CU_ASSERT(SHRPX_LOGF_LITERAL == res[5].type);
CU_ASSERT(LogFragmentType::LITERAL == res[5].type);
CU_ASSERT("\" " == res[5].value);
CU_ASSERT(SHRPX_LOGF_STATUS == res[6].type);
CU_ASSERT(LogFragmentType::STATUS == res[6].type);
CU_ASSERT(SHRPX_LOGF_LITERAL == res[7].type);
CU_ASSERT(LogFragmentType::LITERAL == res[7].type);
CU_ASSERT(" " == res[7].value);
CU_ASSERT(SHRPX_LOGF_BODY_BYTES_SENT == res[8].type);
CU_ASSERT(LogFragmentType::BODY_BYTES_SENT == res[8].type);
CU_ASSERT(SHRPX_LOGF_LITERAL == res[9].type);
CU_ASSERT(LogFragmentType::LITERAL == res[9].type);
CU_ASSERT(" \"" == res[9].value);
CU_ASSERT(SHRPX_LOGF_HTTP == res[10].type);
CU_ASSERT(LogFragmentType::HTTP == res[10].type);
CU_ASSERT("referer" == res[10].value);
CU_ASSERT(SHRPX_LOGF_LITERAL == res[11].type);
CU_ASSERT(LogFragmentType::LITERAL == res[11].type);
CU_ASSERT("\" " == res[11].value);
CU_ASSERT(SHRPX_LOGF_AUTHORITY == res[12].type);
CU_ASSERT(LogFragmentType::AUTHORITY == res[12].type);
CU_ASSERT(SHRPX_LOGF_LITERAL == res[13].type);
CU_ASSERT(LogFragmentType::LITERAL == res[13].type);
CU_ASSERT(" \"" == res[13].value);
CU_ASSERT(SHRPX_LOGF_HTTP == res[14].type);
CU_ASSERT(LogFragmentType::HTTP == res[14].type);
CU_ASSERT("user-agent" == res[14].value);
CU_ASSERT(SHRPX_LOGF_LITERAL == res[15].type);
CU_ASSERT(LogFragmentType::LITERAL == res[15].type);
CU_ASSERT("\"" == res[15].value);
res = parse_log_format(balloc, StringRef::from_lit("$"));
CU_ASSERT(1 == res.size());
CU_ASSERT(SHRPX_LOGF_LITERAL == res[0].type);
CU_ASSERT(LogFragmentType::LITERAL == res[0].type);
CU_ASSERT("$" == res[0].value);
res = parse_log_format(balloc, StringRef::from_lit("${"));
CU_ASSERT(1 == res.size());
CU_ASSERT(SHRPX_LOGF_LITERAL == res[0].type);
CU_ASSERT(LogFragmentType::LITERAL == res[0].type);
CU_ASSERT("${" == res[0].value);
res = parse_log_format(balloc, StringRef::from_lit("${a"));
CU_ASSERT(1 == res.size());
CU_ASSERT(SHRPX_LOGF_LITERAL == res[0].type);
CU_ASSERT(LogFragmentType::LITERAL == res[0].type);
CU_ASSERT("${a" == res[0].value);
res = parse_log_format(balloc, StringRef::from_lit("${a "));
CU_ASSERT(1 == res.size());
CU_ASSERT(SHRPX_LOGF_LITERAL == res[0].type);
CU_ASSERT(LogFragmentType::LITERAL == res[0].type);
CU_ASSERT("${a " == res[0].value);
res = parse_log_format(balloc, StringRef::from_lit("$$remote_addr"));
CU_ASSERT(2 == res.size());
CU_ASSERT(SHRPX_LOGF_LITERAL == res[0].type);
CU_ASSERT(LogFragmentType::LITERAL == res[0].type);
CU_ASSERT("$" == res[0].value);
CU_ASSERT(SHRPX_LOGF_REMOTE_ADDR == res[1].type);
CU_ASSERT(LogFragmentType::REMOTE_ADDR == res[1].type);
CU_ASSERT("" == res[1].value);
}

View File

@@ -59,8 +59,9 @@ Connection::Connection(struct ev_loop *loop, int fd, SSL *ssl,
const RateLimitConfig &read_limit, IOCb writecb,
IOCb readcb, TimerCb timeoutcb, void *data,
size_t tls_dyn_rec_warmup_threshold,
ev_tstamp tls_dyn_rec_idle_timeout, shrpx_proto proto)
: tls{DefaultMemchunks(mcpool), DefaultPeekMemchunks(mcpool)},
ev_tstamp tls_dyn_rec_idle_timeout, Proto proto)
: tls{DefaultMemchunks(mcpool), DefaultPeekMemchunks(mcpool),
DefaultMemchunks(mcpool)},
wlimit(loop, &wev, write_limit.rate, write_limit.burst),
rlimit(loop, &rev, read_limit.rate, read_limit.burst, this),
loop(loop),
@@ -120,10 +121,11 @@ void Connection::disconnect() {
tls.warmup_writelen = 0;
tls.last_writelen = 0;
tls.last_readlen = 0;
tls.handshake_state = 0;
tls.handshake_state = TLSHandshakeState::NORMAL;
tls.initial_handshake_done = false;
tls.reneg_started = false;
tls.sct_requested = false;
tls.early_data_finish = false;
}
if (fd != -1) {
@@ -141,7 +143,11 @@ void Connection::disconnect() {
wlimit.stopw();
}
void Connection::prepare_client_handshake() { SSL_set_connect_state(tls.ssl); }
void Connection::prepare_client_handshake() {
SSL_set_connect_state(tls.ssl);
// This prevents SSL_read_early_data from being called.
tls.early_data_finish = true;
}
void Connection::prepare_server_handshake() {
SSL_set_accept_state(tls.ssl);
@@ -327,8 +333,9 @@ int Connection::tls_handshake() {
wlimit.stopw();
ev_timer_stop(loop, &wt);
std::array<uint8_t, 16_k> buf;
if (ev_is_active(&rev)) {
std::array<uint8_t, 8_k> buf;
auto nread = read_clear(buf.data(), buf.size());
if (nread < 0) {
if (LOG_ENABLED(INFO)) {
@@ -347,9 +354,9 @@ int Connection::tls_handshake() {
}
switch (tls.handshake_state) {
case TLS_CONN_WAIT_FOR_SESSION_CACHE:
case TLSHandshakeState::WAIT_FOR_SESSION_CACHE:
return SHRPX_ERR_INPROGRESS;
case TLS_CONN_GOT_SESSION_CACHE: {
case TLSHandshakeState::GOT_SESSION_CACHE: {
// Use the same trick invented by @kazuho in h2o project.
// Discard all outgoing data.
@@ -373,17 +380,75 @@ int Connection::tls_handshake() {
SSL_set_accept_state(tls.ssl);
tls.handshake_state = TLS_CONN_NORMAL;
tls.handshake_state = TLSHandshakeState::NORMAL;
break;
}
case TLS_CONN_CANCEL_SESSION_CACHE:
tls.handshake_state = TLS_CONN_NORMAL;
case TLSHandshakeState::CANCEL_SESSION_CACHE:
tls.handshake_state = TLSHandshakeState::NORMAL;
break;
default:
break;
}
int rv;
ERR_clear_error();
auto rv = SSL_do_handshake(tls.ssl);
#if OPENSSL_1_1_1_API
if (!tls.server_handshake || tls.early_data_finish) {
rv = SSL_do_handshake(tls.ssl);
} else {
auto &tlsconf = get_config()->tls;
for (;;) {
size_t nread;
rv = SSL_read_early_data(tls.ssl, buf.data(), buf.size(), &nread);
if (rv == SSL_READ_EARLY_DATA_ERROR) {
// If we have early data, and server sends ServerHello, assume
// that handshake is completed in server side, and start
// processing request. If we don't exit handshake code here,
// server waits for EndOfEarlyData and Finished message from
// client, which voids the purpose of 0-RTT data. The left
// over of handshake is done through write_tls or read_tls.
if (tlsconf.no_postpone_early_data &&
(tls.handshake_state == TLSHandshakeState::WRITE_STARTED ||
tls.wbuf.rleft()) &&
tls.earlybuf.rleft()) {
rv = 1;
}
break;
}
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "tls: read early data " << nread << " bytes";
}
tls.earlybuf.append(buf.data(), nread);
if (rv == SSL_READ_EARLY_DATA_FINISH) {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "tls: read all early data; total "
<< tls.earlybuf.rleft() << " bytes";
}
tls.early_data_finish = true;
// The same reason stated above.
if (tlsconf.no_postpone_early_data &&
(tls.handshake_state == TLSHandshakeState::WRITE_STARTED ||
tls.wbuf.rleft()) &&
tls.earlybuf.rleft()) {
rv = 1;
} else {
ERR_clear_error();
rv = SSL_do_handshake(tls.ssl);
}
break;
}
}
}
#else // !OPENSSL_1_1_1_API
rv = SSL_do_handshake(tls.ssl);
#endif // !OPENSSL_1_1_1_API
if (rv <= 0) {
auto err = SSL_get_error(tls.ssl, rv);
@@ -398,12 +463,21 @@ int Connection::tls_handshake() {
break;
case SSL_ERROR_WANT_WRITE:
break;
case SSL_ERROR_SSL:
case SSL_ERROR_SSL: {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "tls: handshake libssl error: "
<< ERR_error_string(ERR_get_error(), nullptr);
}
struct iovec iov;
auto iovcnt = tls.wbuf.riovec(&iov, 1);
auto nwrite = writev_clear(&iov, iovcnt);
if (nwrite > 0) {
tls.wbuf.drain(nwrite);
}
return SHRPX_ERR_NETWORK;
}
default:
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "tls: handshake libssl error " << err;
@@ -412,7 +486,7 @@ int Connection::tls_handshake() {
}
}
if (tls.handshake_state == TLS_CONN_WAIT_FOR_SESSION_CACHE) {
if (tls.handshake_state == TLSHandshakeState::WAIT_FOR_SESSION_CACHE) {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "tls: handshake is still in progress";
}
@@ -424,8 +498,8 @@ int Connection::tls_handshake() {
// negotiated before sending finished message to the peer.
if (rv != 1 && tls.wbuf.rleft()) {
// First write indicates that resumption stuff has done.
if (tls.handshake_state != TLS_CONN_WRITE_STARTED) {
tls.handshake_state = TLS_CONN_WRITE_STARTED;
if (tls.handshake_state != TLSHandshakeState::WRITE_STARTED) {
tls.handshake_state = TLSHandshakeState::WRITE_STARTED;
// If peek has already disabled, this is noop.
tls.rbuf.disable_peek(true);
}
@@ -621,7 +695,21 @@ ssize_t Connection::write_tls(const void *data, size_t len) {
ERR_clear_error();
#if OPENSSL_1_1_1_API
int rv;
if (SSL_is_init_finished(tls.ssl)) {
rv = SSL_write(tls.ssl, data, len);
} else {
size_t nwrite;
rv = SSL_write_early_data(tls.ssl, data, len, &nwrite);
// Use the same semantics with SSL_write.
if (rv == 1) {
rv = nwrite;
}
}
#else // !OPENSSL_1_1_1_API
auto rv = SSL_write(tls.ssl, data, len);
#endif // !OPENSSL_1_1_1_API
if (rv <= 0) {
auto err = SSL_get_error(tls.ssl, rv);
@@ -656,6 +744,14 @@ ssize_t Connection::write_tls(const void *data, size_t len) {
}
ssize_t Connection::read_tls(void *data, size_t len) {
ERR_clear_error();
#if OPENSSL_1_1_1_API
if (tls.earlybuf.rleft()) {
return tls.earlybuf.remove(data, len);
}
#endif // OPENSSL_1_1_1_API
// SSL_read requires the same arguments (buf pointer and its
// length) on SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE.
// rlimit_.avail() or rlimit_.avail() may return different length
@@ -673,7 +769,46 @@ ssize_t Connection::read_tls(void *data, size_t len) {
tls.last_readlen = 0;
}
ERR_clear_error();
#if OPENSSL_1_1_1_API
if (!tls.early_data_finish) {
// TLSv1.3 handshake is still going on.
size_t nread;
auto rv = SSL_read_early_data(tls.ssl, data, len, &nread);
if (rv == SSL_READ_EARLY_DATA_ERROR) {
auto err = SSL_get_error(tls.ssl, rv);
switch (err) {
case SSL_ERROR_WANT_READ:
tls.last_readlen = len;
return 0;
case SSL_ERROR_SSL:
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "SSL_read: "
<< ERR_error_string(ERR_get_error(), nullptr);
}
return SHRPX_ERR_NETWORK;
default:
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "SSL_read: SSL_get_error returned " << err;
}
return SHRPX_ERR_NETWORK;
}
}
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "tls: read early data " << nread << " bytes";
}
if (rv == SSL_READ_EARLY_DATA_FINISH) {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "tls: read all early data";
}
tls.early_data_finish = true;
// We may have stopped write watcher in write_tls.
wlimit.startw();
}
return nread;
}
#endif // OPENSSL_1_1_1_API
auto rv = SSL_read(tls.ssl, data, len);

View File

@@ -45,17 +45,19 @@ namespace tls {
struct TLSSessionCache;
} // namespace tls
enum {
TLS_CONN_NORMAL,
TLS_CONN_WAIT_FOR_SESSION_CACHE,
TLS_CONN_GOT_SESSION_CACHE,
TLS_CONN_CANCEL_SESSION_CACHE,
TLS_CONN_WRITE_STARTED,
enum class TLSHandshakeState {
NORMAL,
WAIT_FOR_SESSION_CACHE,
GOT_SESSION_CACHE,
CANCEL_SESSION_CACHE,
WRITE_STARTED,
};
struct TLSConnection {
DefaultMemchunks wbuf;
DefaultPeekMemchunks rbuf;
// Stores TLSv1.3 early data.
DefaultMemchunks earlybuf;
SSL *ssl;
SSL_SESSION *cached_session;
MemcachedRequest *cached_session_lookup_req;
@@ -66,7 +68,7 @@ struct TLSConnection {
// required since these functions require the exact same parameters
// on non-blocking I/O.
size_t last_writelen, last_readlen;
int handshake_state;
TLSHandshakeState handshake_state;
bool initial_handshake_done;
bool reneg_started;
// true if ssl is prepared to do handshake as server.
@@ -74,6 +76,12 @@ struct TLSConnection {
// true if ssl is initialized as server, and client requested
// signed_certificate_timestamp extension.
bool sct_requested;
// true if TLSv1.3 early data has been completely received. Since
// SSL_read_early_data acts like SSL_do_handshake, this field may be
// true even if the negotiated TLS version is TLSv1.2 or earlier.
// This value is also true if this is client side connection for
// convenience.
bool early_data_finish;
};
struct TCPHint {
@@ -92,7 +100,7 @@ struct Connection {
const RateLimitConfig &write_limit,
const RateLimitConfig &read_limit, IOCb writecb, IOCb readcb,
TimerCb timeoutcb, void *data, size_t tls_dyn_rec_warmup_threshold,
ev_tstamp tls_dyn_rec_idle_timeout, shrpx_proto proto);
ev_tstamp tls_dyn_rec_idle_timeout, Proto proto);
~Connection();
void disconnect();
@@ -161,7 +169,7 @@ struct Connection {
// Application protocol used over the connection. This field is not
// used in this object at the moment. The rest of the program may
// use this value when it is useful.
shrpx_proto proto;
Proto proto;
// The point of time when last read is observed. Note: since we use
// |rt| as idle timer, the activity is not limited to read.
ev_tstamp last_read;

View File

@@ -183,7 +183,7 @@ void ConnectionHandler::set_ticket_keys_to_worker(
void ConnectionHandler::worker_reopen_log_files() {
WorkerEvent wev{};
wev.type = REOPEN_LOG;
wev.type = WorkerEventType::REOPEN_LOG;
for (auto &worker : workers_) {
worker->send(wev);
@@ -194,7 +194,7 @@ void ConnectionHandler::worker_replace_downstream(
std::shared_ptr<DownstreamConfig> downstreamconf) {
WorkerEvent wev{};
wev.type = REPLACE_DOWNSTREAM;
wev.type = WorkerEventType::REPLACE_DOWNSTREAM;
wev.downstreamconf = std::move(downstreamconf);
for (auto &worker : workers_) {
@@ -238,7 +238,7 @@ int ConnectionHandler::create_single_worker() {
}
}
single_worker_ = make_unique<Worker>(
single_worker_ = std::make_unique<Worker>(
loop_, sv_ssl_ctx, cl_ssl_ctx, session_cache_ssl_ctx, cert_tree_.get(),
ticket_keys_, this, config->conn.downstream);
#ifdef HAVE_MRUBY
@@ -299,7 +299,7 @@ int ConnectionHandler::create_worker_thread(size_t num) {
for (size_t i = 0; i < num; ++i) {
auto loop = ev_loop_new(config->ev_loop_flags);
auto worker = make_unique<Worker>(
auto worker = std::make_unique<Worker>(
loop, sv_ssl_ctx, cl_ssl_ctx, session_cache_ssl_ctx, cert_tree_.get(),
ticket_keys_, this, config->conn.downstream);
# ifdef HAVE_MRUBY
@@ -348,7 +348,7 @@ void ConnectionHandler::graceful_shutdown_worker() {
}
WorkerEvent wev{};
wev.type = GRACEFUL_SHUTDOWN;
wev.type = WorkerEventType::GRACEFUL_SHUTDOWN;
if (LOG_ENABLED(INFO)) {
LLOG(INFO, this) << "Sending graceful shutdown signal to worker";
@@ -407,7 +407,7 @@ int ConnectionHandler::handle_connection(int fd, sockaddr *addr, int addrlen,
Worker *worker;
if (faddr->alt_mode == ALTMODE_API) {
if (faddr->alt_mode == UpstreamAltMode::API) {
worker = workers_[0].get();
if (LOG_ENABLED(INFO)) {
@@ -432,7 +432,7 @@ int ConnectionHandler::handle_connection(int fd, sockaddr *addr, int addrlen,
}
WorkerEvent wev{};
wev.type = NEW_CONNECTION;
wev.type = WorkerEventType::NEW_CONNECTION;
wev.client_fd = fd;
memcpy(&wev.client_addr, addr, addrlen);
wev.client_addrlen = addrlen;
@@ -613,8 +613,8 @@ void ConnectionHandler::handle_ocsp_complete() {
auto status = WEXITSTATUS(rstatus);
if (ocsp_.error || !WIFEXITED(rstatus) || status != 0) {
LOG(WARN) << "ocsp query command for " << tls_ctx_data->cert_file
<< " failed: error=" << ocsp_.error << ", rstatus=" << std::hex
<< rstatus << std::dec << ", status=" << status;
<< " failed: error=" << ocsp_.error << ", rstatus=" << log::hex
<< rstatus << log::dec << ", status=" << status;
++ocsp_.next;
proceed_next_cert_ocsp();
return;
@@ -828,7 +828,7 @@ void ConnectionHandler::handle_serial_event() {
for (auto &sev : q) {
switch (sev.type) {
case SEV_REPLACE_DOWNSTREAM:
case SerialEventType::REPLACE_DOWNSTREAM:
// Mmake sure that none of worker uses
// get_config()->conn.downstream
mod_config()->conn.downstream = sev.downstreamconf;
@@ -841,6 +841,8 @@ void ConnectionHandler::handle_serial_event() {
worker_replace_downstream(sev.downstreamconf);
break;
default:
break;
}
}
@@ -848,7 +850,8 @@ void ConnectionHandler::handle_serial_event() {
void ConnectionHandler::send_replace_downstream(
const std::shared_ptr<DownstreamConfig> &downstreamconf) {
send_serial_event(SerialEvent(SEV_REPLACE_DOWNSTREAM, downstreamconf));
send_serial_event(
SerialEvent(SerialEventType::REPLACE_DOWNSTREAM, downstreamconf));
}
void ConnectionHandler::send_serial_event(SerialEvent ev) {

View File

@@ -84,17 +84,18 @@ struct OCSPUpdateContext {
};
// SerialEvent is an event sent from Worker thread.
enum SerialEventType {
SEV_NONE,
SEV_REPLACE_DOWNSTREAM,
enum class SerialEventType {
NONE,
REPLACE_DOWNSTREAM,
};
struct SerialEvent {
// ctor for event uses DownstreamConfig
SerialEvent(int type, const std::shared_ptr<DownstreamConfig> &downstreamconf)
SerialEvent(SerialEventType type,
const std::shared_ptr<DownstreamConfig> &downstreamconf)
: type(type), downstreamconf(downstreamconf) {}
int type;
SerialEventType type;
std::shared_ptr<DownstreamConfig> downstreamconf;
};
@@ -163,7 +164,8 @@ public:
void set_neverbleed(neverbleed_t *nb);
#endif // HAVE_NEVERBLEED
// Send SerialEvent SEV_REPLACE_DOWNSTREAM to this object.
// Send SerialEvent SerialEventType::REPLACE_DOWNSTREAM to this
// object.
void send_replace_downstream(
const std::shared_ptr<DownstreamConfig> &downstreamconf);
// Internal function to send |ev| to this object.

View File

@@ -37,7 +37,7 @@ namespace {
void sock_state_cb(void *data, int s, int read, int write) {
auto resolv = static_cast<DNSResolver *>(data);
if (resolv->get_status(nullptr) != DNS_STATUS_RUNNING) {
if (resolv->get_status(nullptr) != DNSResolverStatus::RUNNING) {
return;
}
@@ -70,10 +70,12 @@ void process_result(DNSResolver *resolv) {
Address result;
auto status = resolv->get_status(&result);
switch (status) {
case DNS_STATUS_OK:
case DNS_STATUS_ERROR:
case DNSResolverStatus::OK:
case DNSResolverStatus::ERROR:
cb(status, &result);
break;
default:
break;
}
// resolv may be deleted here.
}
@@ -117,7 +119,7 @@ DNSResolver::DNSResolver(struct ev_loop *loop)
loop_(loop),
channel_(nullptr),
family_(AF_UNSPEC),
status_(DNS_STATUS_IDLE) {
status_(DNSResolverStatus::IDLE) {
ev_timer_init(&timer_, timeoutcb, 0., 0.);
timer_.data = this;
}
@@ -134,7 +136,7 @@ DNSResolver::~DNSResolver() {
}
int DNSResolver::resolve(const StringRef &name, int family) {
if (status_ != DNS_STATUS_IDLE) {
if (status_ != DNSResolverStatus::IDLE) {
return -1;
}
@@ -164,12 +166,12 @@ int DNSResolver::resolve(const StringRef &name, int family) {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "ares_init_options failed: " << ares_strerror(rv);
}
status_ = DNS_STATUS_ERROR;
status_ = DNSResolverStatus::ERROR;
return -1;
}
channel_ = chan;
status_ = DNS_STATUS_RUNNING;
status_ = DNSResolverStatus::RUNNING;
ares_gethostbyname(channel_, name_.c_str(), family_, host_cb, this);
reset_timeout();
@@ -186,20 +188,19 @@ int DNSResolver::on_timeout() {
}
int DNSResolver::handle_event(int rfd, int wfd) {
if (status_ == DNS_STATUS_IDLE) {
if (status_ == DNSResolverStatus::IDLE) {
return -1;
}
ares_process_fd(channel_, rfd, wfd);
switch (status_) {
case DNS_STATUS_RUNNING: {
case DNSResolverStatus::RUNNING:
reset_timeout();
return 0;
}
case DNS_STATUS_OK:
case DNSResolverStatus::OK:
return 0;
case DNS_STATUS_ERROR:
case DNSResolverStatus::ERROR:
return -1;
default:
// Unreachable
@@ -209,7 +210,7 @@ int DNSResolver::handle_event(int rfd, int wfd) {
}
void DNSResolver::reset_timeout() {
if (status_ != DNS_STATUS_RUNNING) {
if (status_ != DNSResolverStatus::RUNNING) {
return;
}
timeval tvout;
@@ -223,8 +224,8 @@ void DNSResolver::reset_timeout() {
ev_timer_again(loop_, &timer_);
}
int DNSResolver::get_status(Address *result) const {
if (status_ != DNS_STATUS_OK) {
DNSResolverStatus DNSResolver::get_status(Address *result) const {
if (status_ != DNSResolverStatus::OK) {
return status_;
}
@@ -251,7 +252,7 @@ void start_ev(std::vector<std::unique_ptr<ev_io>> &evs, struct ev_loop *loop,
}
}
auto w = make_unique<ev_io>();
auto w = std::make_unique<ev_io>();
ev_io_init(w.get(), cb, fd, event);
w->data = data;
ev_io_start(loop, w.get());
@@ -294,7 +295,7 @@ void DNSResolver::on_result(int status, hostent *hostent) {
LOG(INFO) << "Name lookup for " << name_
<< " failed: " << ares_strerror(status);
}
status_ = DNS_STATUS_ERROR;
status_ = DNSResolverStatus::ERROR;
return;
}
@@ -303,13 +304,13 @@ void DNSResolver::on_result(int status, hostent *hostent) {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Name lookup for " << name_ << "failed: no address returned";
}
status_ = DNS_STATUS_ERROR;
status_ = DNSResolverStatus::ERROR;
return;
}
switch (hostent->h_addrtype) {
case AF_INET:
status_ = DNS_STATUS_OK;
status_ = DNSResolverStatus::OK;
result_.len = sizeof(result_.su.in);
result_.su.in = {};
result_.su.in.sin_family = AF_INET;
@@ -319,7 +320,7 @@ void DNSResolver::on_result(int status, hostent *hostent) {
memcpy(&result_.su.in.sin_addr, ap, sizeof(result_.su.in.sin_addr));
break;
case AF_INET6:
status_ = DNS_STATUS_OK;
status_ = DNSResolverStatus::OK;
result_.len = sizeof(result_.su.in6);
result_.su.in6 = {};
result_.su.in6.sin6_family = AF_INET6;
@@ -332,7 +333,7 @@ void DNSResolver::on_result(int status, hostent *hostent) {
assert(0);
}
if (status_ == DNS_STATUS_OK) {
if (status_ == DNSResolverStatus::OK) {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Name lookup succeeded: " << name_ << " -> "
<< util::numeric_name(&result_.su.sa, result_.len);
@@ -340,7 +341,7 @@ void DNSResolver::on_result(int status, hostent *hostent) {
return;
}
status_ = DNS_STATUS_ERROR;
status_ = DNSResolverStatus::ERROR;
}
void DNSResolver::set_complete_cb(CompleteCb cb) {

View File

@@ -42,27 +42,29 @@ using namespace nghttp2;
namespace shrpx {
enum DNSResolverStatus {
enum class DNSResolverStatus {
// Resolver is in initial status
DNS_STATUS_IDLE,
IDLE,
// Resolver is currently resolving host name
DNS_STATUS_RUNNING,
RUNNING,
// Resolver successfully resolved host name
DNS_STATUS_OK,
OK,
// Resolver failed to resolve host name
DNS_STATUS_ERROR,
ERROR,
};
// Callback function called when host name lookup is finished.
// |status| is either DNS_STATUS_OK, or DNS_STATUS_ERROR. If |status|
// is DNS_STATUS_OK, |result| points to the resolved address. Note
// that port portion of |result| is undefined, and must be initialized
// by application. This callback function is not called if name
// lookup finishes in DNSResolver::resolve() completely. In this
// case, application should call DNSResolver::get_status() to get
// current status and result. In other words, callback is called if
// get_status() returns DNS_STATUS_RUNNING.
using CompleteCb = std::function<void(int status, const Address *result)>;
// |status| is either DNSResolverStatus::OK, or
// DNSResolverStatus::ERROR. If |status| is DNSResolverStatus::OK,
// |result| points to the resolved address. Note that port portion of
// |result| is undefined, and must be initialized by application.
// This callback function is not called if name lookup finishes in
// DNSResolver::resolve() completely. In this case, application
// should call DNSResolver::get_status() to get current status and
// result. In other words, callback is called if get_status() returns
// DNSResolverStatus::RUNNING.
using CompleteCb =
std::function<void(DNSResolverStatus status, const Address *result)>;
// DNSResolver is asynchronous name resolver, backed by c-ares
// library.
@@ -73,9 +75,9 @@ public:
// Starts resolving hostname |name|.
int resolve(const StringRef &name, int family);
// Returns status. If status_ is DNS_STATUS_SUCCESS && |result| is
// not nullptr, |*result| is filled.
int get_status(Address *result) const;
// Returns status. If status_ is DNSResolverStatus::SUCCESS &&
// |result| is not nullptr, |*result| is filled.
DNSResolverStatus get_status(Address *result) const;
// Sets callback function when name lookup finishes. The callback
// function is called in a way that it can destroy this DNSResolver.
void set_complete_cb(CompleteCb cb);
@@ -108,7 +110,7 @@ private:
// AF_INET or AF_INET6. AF_INET for A record lookup, and AF_INET6
// for AAAA record lookup.
int family_;
int status_;
DNSResolverStatus status_;
};
} // namespace shrpx

View File

@@ -49,7 +49,7 @@ DNSTracker::~DNSTracker() {
while (!qlist.empty()) {
auto head = qlist.head;
qlist.remove(head);
head->status = DNS_STATUS_ERROR;
head->status = DNSResolverStatus::ERROR;
head->in_qlist = false;
// TODO Not sure we should call callback here, or it is even be
// safe to do that.
@@ -58,7 +58,8 @@ DNSTracker::~DNSTracker() {
}
ResolverEntry DNSTracker::make_entry(std::unique_ptr<DualDNSResolver> resolv,
ImmutableString host, int status,
ImmutableString host,
DNSResolverStatus status,
const Address *result) {
auto &dnsconf = get_config()->dns;
@@ -67,10 +68,12 @@ ResolverEntry DNSTracker::make_entry(std::unique_ptr<DualDNSResolver> resolv,
ent.host = std::move(host);
ent.status = status;
switch (status) {
case DNS_STATUS_ERROR:
case DNS_STATUS_OK:
case DNSResolverStatus::ERROR:
case DNSResolverStatus::OK:
ent.expiry = ev_now(loop_) + dnsconf.timeout.cache;
break;
default:
break;
}
if (result) {
ent.result = *result;
@@ -80,23 +83,25 @@ ResolverEntry DNSTracker::make_entry(std::unique_ptr<DualDNSResolver> resolv,
void DNSTracker::update_entry(ResolverEntry &ent,
std::unique_ptr<DualDNSResolver> resolv,
int status, const Address *result) {
DNSResolverStatus status, const Address *result) {
auto &dnsconf = get_config()->dns;
ent.resolv = std::move(resolv);
ent.status = status;
switch (status) {
case DNS_STATUS_ERROR:
case DNS_STATUS_OK:
case DNSResolverStatus::ERROR:
case DNSResolverStatus::OK:
ent.expiry = ev_now(loop_) + dnsconf.timeout.cache;
break;
default:
break;
}
if (result) {
ent.result = *result;
}
}
int DNSTracker::resolve(Address *result, DNSQuery *dnsq) {
DNSResolverStatus DNSTracker::resolve(Address *result, DNSQuery *dnsq) {
int rv;
auto it = ents_.find(dnsq->host);
@@ -106,7 +111,7 @@ int DNSTracker::resolve(Address *result, DNSQuery *dnsq) {
LOG(INFO) << "DNS entry not found for " << dnsq->host;
}
auto resolv = make_unique<DualDNSResolver>(loop_);
auto resolv = std::make_unique<DualDNSResolver>(loop_);
auto host_copy =
ImmutableString{std::begin(dnsq->host), std::end(dnsq->host)};
auto host = StringRef{host_copy};
@@ -118,46 +123,41 @@ int DNSTracker::resolve(Address *result, DNSQuery *dnsq) {
}
ents_.emplace(host, make_entry(nullptr, std::move(host_copy),
DNS_STATUS_ERROR, nullptr));
DNSResolverStatus::ERROR, nullptr));
start_gc_timer();
return DNS_STATUS_ERROR;
return DNSResolverStatus::ERROR;
}
rv = resolv->get_status(result);
switch (rv) {
case DNS_STATUS_ERROR: {
switch (resolv->get_status(result)) {
case DNSResolverStatus::ERROR:
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Name lookup failed for " << host;
}
ents_.emplace(host, make_entry(nullptr, std::move(host_copy),
DNS_STATUS_ERROR, nullptr));
DNSResolverStatus::ERROR, nullptr));
start_gc_timer();
return DNS_STATUS_ERROR;
}
case DNS_STATUS_OK: {
return DNSResolverStatus::ERROR;
case DNSResolverStatus::OK:
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Name lookup succeeded: " << host << " -> "
<< util::numeric_name(&result->su.sa, result->len);
}
ents_.emplace(host, make_entry(nullptr, std::move(host_copy),
DNS_STATUS_OK, result));
DNSResolverStatus::OK, result));
start_gc_timer();
return DNS_STATUS_OK;
}
case DNS_STATUS_RUNNING: {
assert(rv == DNS_STATUS_RUNNING);
return DNSResolverStatus::OK;
case DNSResolverStatus::RUNNING: {
auto p = ents_.emplace(host,
make_entry(std::move(resolv), std::move(host_copy),
DNS_STATUS_RUNNING, nullptr));
DNSResolverStatus::RUNNING, nullptr));
start_gc_timer();
@@ -165,7 +165,7 @@ int DNSTracker::resolve(Address *result, DNSQuery *dnsq) {
add_to_qlist(ent, dnsq);
return DNS_STATUS_RUNNING;
return DNSResolverStatus::RUNNING;
}
default:
assert(0);
@@ -174,13 +174,13 @@ int DNSTracker::resolve(Address *result, DNSQuery *dnsq) {
auto &ent = (*it).second;
if (ent.status != DNS_STATUS_RUNNING && ent.expiry < ev_now(loop_)) {
if (ent.status != DNSResolverStatus::RUNNING && ent.expiry < ev_now(loop_)) {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "DNS entry found for " << dnsq->host
<< ", but it has been expired";
}
auto resolv = make_unique<DualDNSResolver>(loop_);
auto resolv = std::make_unique<DualDNSResolver>(loop_);
auto host = StringRef{ent.host};
rv = resolv->resolve(host);
@@ -189,57 +189,53 @@ int DNSTracker::resolve(Address *result, DNSQuery *dnsq) {
LOG(INFO) << "Name lookup failed for " << host;
}
update_entry(ent, nullptr, DNS_STATUS_ERROR, nullptr);
update_entry(ent, nullptr, DNSResolverStatus::ERROR, nullptr);
return DNS_STATUS_ERROR;
return DNSResolverStatus::ERROR;
}
rv = resolv->get_status(result);
switch (rv) {
case DNS_STATUS_ERROR: {
switch (resolv->get_status(result)) {
case DNSResolverStatus::ERROR:
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Name lookup failed for " << host;
}
update_entry(ent, nullptr, DNS_STATUS_ERROR, nullptr);
update_entry(ent, nullptr, DNSResolverStatus::ERROR, nullptr);
return DNS_STATUS_ERROR;
}
case DNS_STATUS_OK: {
return DNSResolverStatus::ERROR;
case DNSResolverStatus::OK:
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Name lookup succeeded: " << host << " -> "
<< util::numeric_name(&result->su.sa, result->len);
}
update_entry(ent, nullptr, DNS_STATUS_OK, result);
update_entry(ent, nullptr, DNSResolverStatus::OK, result);
return DNS_STATUS_OK;
}
case DNS_STATUS_RUNNING: {
update_entry(ent, std::move(resolv), DNS_STATUS_RUNNING, nullptr);
return DNSResolverStatus::OK;
case DNSResolverStatus::RUNNING:
update_entry(ent, std::move(resolv), DNSResolverStatus::RUNNING, nullptr);
add_to_qlist(ent, dnsq);
return DNS_STATUS_RUNNING;
}
return DNSResolverStatus::RUNNING;
default:
assert(0);
}
}
switch (ent.status) {
case DNS_STATUS_RUNNING:
case DNSResolverStatus::RUNNING:
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Waiting for name lookup complete for " << dnsq->host;
}
ent.qlist.append(dnsq);
dnsq->in_qlist = true;
return DNS_STATUS_RUNNING;
case DNS_STATUS_ERROR:
return DNSResolverStatus::RUNNING;
case DNSResolverStatus::ERROR:
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Name lookup failed for " << dnsq->host << " (cached)";
}
return DNS_STATUS_ERROR;
case DNS_STATUS_OK:
return DNSResolverStatus::ERROR;
case DNSResolverStatus::OK:
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Name lookup succeeded (cached): " << dnsq->host << " -> "
<< util::numeric_name(&ent.result.su.sa, ent.result.len);
@@ -247,7 +243,7 @@ int DNSTracker::resolve(Address *result, DNSQuery *dnsq) {
if (result) {
memcpy(result, &ent.result, sizeof(*result));
}
return DNS_STATUS_OK;
return DNSResolverStatus::OK;
default:
assert(0);
abort();
@@ -256,26 +252,27 @@ int DNSTracker::resolve(Address *result, DNSQuery *dnsq) {
void DNSTracker::add_to_qlist(ResolverEntry &ent, DNSQuery *dnsq) {
auto loop = loop_;
ent.resolv->set_complete_cb([&ent, loop](int status, const Address *result) {
auto &qlist = ent.qlist;
while (!qlist.empty()) {
auto head = qlist.head;
qlist.remove(head);
head->status = status;
head->in_qlist = false;
auto cb = head->cb;
cb(status, result);
}
ent.resolv->set_complete_cb(
[&ent, loop](DNSResolverStatus status, const Address *result) {
auto &qlist = ent.qlist;
while (!qlist.empty()) {
auto head = qlist.head;
qlist.remove(head);
head->status = status;
head->in_qlist = false;
auto cb = head->cb;
cb(status, result);
}
auto &dnsconf = get_config()->dns;
auto &dnsconf = get_config()->dns;
ent.resolv.reset();
ent.status = status;
ent.expiry = ev_now(loop) + dnsconf.timeout.cache;
if (ent.status == DNS_STATUS_OK) {
ent.result = *result;
}
});
ent.resolv.reset();
ent.status = status;
ent.expiry = ev_now(loop) + dnsconf.timeout.cache;
if (ent.status == DNSResolverStatus::OK) {
ent.result = *result;
}
});
ent.qlist.append(dnsq);
dnsq->in_qlist = true;
}

View File

@@ -41,7 +41,7 @@ struct DNSQuery {
cb(std::move(cb)),
dlnext(nullptr),
dlprev(nullptr),
status(DNS_STATUS_IDLE),
status(DNSResolverStatus::IDLE),
in_qlist(false) {}
// Host name we lookup for.
@@ -51,7 +51,7 @@ struct DNSQuery {
// DNSTracker::resolve().
CompleteCb cb;
DNSQuery *dlnext, *dlprev;
int status;
DNSResolverStatus status;
// true if this object is in linked list ResolverEntry::qlist.
bool in_qlist;
};
@@ -59,13 +59,14 @@ struct DNSQuery {
struct ResolverEntry {
// Host name this entry lookups for.
ImmutableString host;
// DNS resolver. Only non-nullptr if status is DNS_STATUS_RUNNING.
// DNS resolver. Only non-nullptr if status is
// DNSResolverStatus::RUNNING.
std::unique_ptr<DualDNSResolver> resolv;
// DNSQuery interested in this name lookup result. The result is
// notified to them all.
DList<DNSQuery> qlist;
// Use the same enum with DNSResolverStatus
int status;
DNSResolverStatus status;
// result and its expiry time
Address result;
// time point when cached result expires.
@@ -80,12 +81,13 @@ public:
// Lookups host name described in |dnsq|. If name lookup finishes
// within this function (either it came from /etc/hosts, host name
// is numeric, lookup result is cached, etc), it returns
// DNS_STATUS_OK or DNS_STATUS_ERROR. If lookup is successful,
// DNS_STATUS_OK is returned, and |result| is filled. If lookup
// failed, DNS_STATUS_ERROR is returned. If name lookup is being
// done background, it returns DNS_STATUS_RUNNING. Its completion
// is notified by calling dnsq->cb.
int resolve(Address *result, DNSQuery *dnsq);
// DNSResolverStatus::OK or DNSResolverStatus::ERROR. If lookup is
// successful, DNSResolverStatus::OK is returned, and |result| is
// filled. If lookup failed, DNSResolverStatus::ERROR is returned.
// If name lookup is being done background, it returns
// DNSResolverStatus::RUNNING. Its completion is notified by
// calling dnsq->cb.
DNSResolverStatus resolve(Address *result, DNSQuery *dnsq);
// Cancels name lookup requested by |dnsq|.
void cancel(DNSQuery *dnsq);
// Removes expired entries from ents_.
@@ -95,11 +97,11 @@ public:
private:
ResolverEntry make_entry(std::unique_ptr<DualDNSResolver> resolv,
ImmutableString host, int status,
ImmutableString host, DNSResolverStatus status,
const Address *result);
void update_entry(ResolverEntry &ent, std::unique_ptr<DualDNSResolver> resolv,
int status, const Address *result);
DNSResolverStatus status, const Address *result);
void add_to_qlist(ResolverEntry &ent, DNSQuery *dnsq);

View File

@@ -133,9 +133,9 @@ Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool,
downstream_stream_id_(-1),
response_rst_stream_error_code_(NGHTTP2_NO_ERROR),
affinity_cookie_(0),
request_state_(INITIAL),
response_state_(INITIAL),
dispatch_state_(DISPATCH_NONE),
request_state_(DownstreamState::INITIAL),
response_state_(DownstreamState::INITIAL),
dispatch_state_(DispatchState::NONE),
upgraded_(false),
chunked_request_(false),
chunked_response_(false),
@@ -596,9 +596,11 @@ void Downstream::set_stream_id(int32_t stream_id) { stream_id_ = stream_id; }
int32_t Downstream::get_stream_id() const { return stream_id_; }
void Downstream::set_request_state(int state) { request_state_ = state; }
void Downstream::set_request_state(DownstreamState state) {
request_state_ = state;
}
int Downstream::get_request_state() const { return request_state_; }
DownstreamState Downstream::get_request_state() const { return request_state_; }
bool Downstream::get_chunked_request() const { return chunked_request_; }
@@ -610,7 +612,7 @@ bool Downstream::request_buf_full() {
auto worker = handler->get_worker();
// We don't check buffer size here for API endpoint.
if (faddr->alt_mode == ALTMODE_API) {
if (faddr->alt_mode == UpstreamAltMode::API) {
return false;
}
@@ -711,9 +713,13 @@ int Downstream::on_read() {
return dconn_->on_read();
}
void Downstream::set_response_state(int state) { response_state_ = state; }
void Downstream::set_response_state(DownstreamState state) {
response_state_ = state;
}
int Downstream::get_response_state() const { return response_state_; }
DownstreamState Downstream::get_response_state() const {
return response_state_;
}
DefaultMemchunks *Downstream::get_response_buf() { return &response_buf_; }
@@ -763,9 +769,35 @@ bool Downstream::validate_response_recv_body_length() const {
return true;
}
void Downstream::check_upgrade_fulfilled() {
void Downstream::check_upgrade_fulfilled_http2() {
// This handles nonzero req_.connect_proto and h1 frontend requests
// WebSocket upgrade.
upgraded_ = (req_.method == HTTP_CONNECT ||
req_.connect_proto == ConnectProto::WEBSOCKET) &&
resp_.http_status / 100 == 2;
}
void Downstream::check_upgrade_fulfilled_http1() {
if (req_.method == HTTP_CONNECT) {
upgraded_ = 200 <= resp_.http_status && resp_.http_status < 300;
if (req_.connect_proto == ConnectProto::WEBSOCKET) {
if (resp_.http_status != 101) {
return;
}
// This is done for HTTP/2 frontend only.
auto accept = resp_.fs.header(http2::HD_SEC_WEBSOCKET_ACCEPT);
if (!accept) {
return;
}
std::array<uint8_t, base64::encode_length(20)> accept_buf;
auto expected =
http2::make_websocket_accept_token(accept_buf.data(), ws_key_);
upgraded_ = expected != "" && expected == accept->value;
} else {
upgraded_ = resp_.http_status / 100 == 2;
}
return;
}
@@ -787,7 +819,7 @@ void Downstream::inspect_http2_request() {
void Downstream::inspect_http1_request() {
if (req_.method == HTTP_CONNECT) {
req_.upgrade_request = true;
} else {
} else if (req_.http_minor > 0) {
auto upgrade = req_.fs.header(http2::HD_UPGRADE);
if (upgrade) {
const auto &val = upgrade->value;
@@ -797,6 +829,12 @@ void Downstream::inspect_http1_request() {
req_.http2_upgrade_seen = true;
} else {
req_.upgrade_request = true;
// TODO Should we check Sec-WebSocket-Key, and
// Sec-WebSocket-Version as well?
if (util::strieq_l("websocket", val)) {
req_.connect_proto = ConnectProto::WEBSOCKET;
}
}
}
}
@@ -837,7 +875,7 @@ bool Downstream::get_upgraded() const { return upgraded_; }
bool Downstream::get_http2_upgrade_request() const {
return req_.http2_upgrade_seen && req_.fs.header(http2::HD_HTTP2_SETTINGS) &&
response_state_ == INITIAL;
response_state_ == DownstreamState::INITIAL;
}
StringRef Downstream::get_http2_settings() const {
@@ -1023,15 +1061,15 @@ bool Downstream::get_request_header_sent() const {
}
bool Downstream::request_submission_ready() const {
return (request_state_ == Downstream::HEADER_COMPLETE ||
request_state_ == Downstream::MSG_COMPLETE) &&
return (request_state_ == DownstreamState::HEADER_COMPLETE ||
request_state_ == DownstreamState::MSG_COMPLETE) &&
(request_pending_ || !request_header_sent_) &&
response_state_ == Downstream::INITIAL;
response_state_ == DownstreamState::INITIAL;
}
int Downstream::get_dispatch_state() const { return dispatch_state_; }
DispatchState Downstream::get_dispatch_state() const { return dispatch_state_; }
void Downstream::set_dispatch_state(int s) { dispatch_state_ = s; }
void Downstream::set_dispatch_state(DispatchState s) { dispatch_state_ = s; }
void Downstream::attach_blocked_link(BlockedLink *l) {
assert(!blocked_link_);
@@ -1050,8 +1088,8 @@ bool Downstream::can_detach_downstream_connection() const {
// We should check request and response buffer. If request buffer
// is not empty, then we might leave downstream connection in weird
// state, especially for HTTP/1.1
return dconn_ && response_state_ == Downstream::MSG_COMPLETE &&
request_state_ == Downstream::MSG_COMPLETE && !upgraded_ &&
return dconn_ && response_state_ == DownstreamState::MSG_COMPLETE &&
request_state_ == DownstreamState::MSG_COMPLETE && !upgraded_ &&
!resp_.connection_close && request_buf_.rleft() == 0;
}
@@ -1103,4 +1141,6 @@ bool Downstream::get_blocked_request_data_eof() const {
return blocked_request_data_eof_;
}
void Downstream::set_ws_key(const StringRef &key) { ws_key_ = key; }
} // namespace shrpx

View File

@@ -134,6 +134,12 @@ private:
bool trailer_key_prev_;
};
// Protocols allowed in HTTP/2 :protocol header field.
enum class ConnectProto {
NONE,
WEBSOCKET,
};
struct Request {
Request(BlockAllocator &balloc)
: fs(balloc, 16),
@@ -142,6 +148,7 @@ struct Request {
method(-1),
http_major(1),
http_minor(1),
connect_proto(ConnectProto::NONE),
upgrade_request(false),
http2_upgrade_seen(false),
connection_close(false),
@@ -153,6 +160,14 @@ struct Request {
unconsumed_body_length -= len;
}
bool regular_connect_method() const {
return method == HTTP_CONNECT && connect_proto == ConnectProto::NONE;
}
bool extended_connect_method() const {
return connect_proto != ConnectProto::NONE;
}
FieldStore fs;
// Timestamp when all request header fields are received.
std::shared_ptr<Timestamp> tstamp;
@@ -176,6 +191,10 @@ struct Request {
int method;
// HTTP major and minor version
int http_major, http_minor;
// connect_proto specified in HTTP/2 :protocol pseudo header field
// which enables extended CONNECT method. This field is also set if
// WebSocket upgrade is requested in h1 frontend for convenience.
ConnectProto connect_proto;
// Returns true if the request is HTTP upgrade (HTTP Upgrade or
// CONNECT method). Upgrade to HTTP/2 is excluded. For HTTP/2
// Upgrade, check get_http2_upgrade_request().
@@ -225,7 +244,7 @@ struct Response {
void resource_pushed(const StringRef &scheme, const StringRef &authority,
const StringRef &path) {
if (!pushed_resources) {
pushed_resources = make_unique<
pushed_resources = std::make_unique<
std::vector<std::tuple<StringRef, StringRef, StringRef>>>();
}
pushed_resources->emplace_back(scheme, authority, path);
@@ -257,6 +276,30 @@ struct Response {
bool headers_only;
};
enum class DownstreamState {
INITIAL,
HEADER_COMPLETE,
MSG_COMPLETE,
STREAM_CLOSED,
CONNECT_FAIL,
MSG_RESET,
// header contains invalid header field. We can safely send error
// response (502) to a client.
MSG_BAD_HEADER,
// header fields in HTTP/1 request exceed the configuration limit.
// This state is only transitioned from INITIAL state, and solely
// used to signal 431 status code to the client.
HTTP1_REQUEST_HEADER_TOO_LARGE,
};
enum class DispatchState {
NONE,
PENDING,
BLOCKED,
ACTIVE,
FAILURE,
};
class Downstream {
public:
Downstream(Upstream *upstream, MemchunkPool *mcpool, int32_t stream_id);
@@ -283,11 +326,14 @@ public:
// Returns true if output buffer is full. If underlying dconn_ is
// NULL, this function always returns false.
bool request_buf_full();
// Returns true if upgrade (HTTP Upgrade or CONNECT) is succeeded.
// This should not depend on inspect_http1_response().
void check_upgrade_fulfilled();
// Returns true if upgrade (HTTP Upgrade or CONNECT) is succeeded in
// h1 backend. This should not depend on inspect_http1_response().
void check_upgrade_fulfilled_http1();
// Returns true if upgrade (HTTP Upgrade or CONNECT) is succeeded in
// h2 backend.
void check_upgrade_fulfilled_http2();
// Returns true if the upgrade is succeeded as a result of the call
// check_upgrade_fulfilled(). HTTP/2 Upgrade is excluded.
// check_upgrade_fulfilled_http*(). HTTP/2 Upgrade is excluded.
bool get_upgraded() const;
// Inspects HTTP/2 request.
void inspect_http2_request();
@@ -327,24 +373,8 @@ public:
void set_request_downstream_host(const StringRef &host);
bool expect_response_body() const;
bool expect_response_trailer() const;
enum {
INITIAL,
HEADER_COMPLETE,
MSG_COMPLETE,
STREAM_CLOSED,
CONNECT_FAIL,
IDLE,
MSG_RESET,
// header contains invalid header field. We can safely send error
// response (502) to a client.
MSG_BAD_HEADER,
// header fields in HTTP/1 request exceed the configuration limit.
// This state is only transitioned from INITIAL state, and solely
// used to signal 431 status code to the client.
HTTP1_REQUEST_HEADER_TOO_LARGE,
};
void set_request_state(int state);
int get_request_state() const;
void set_request_state(DownstreamState state);
DownstreamState get_request_state() const;
DefaultMemchunks *get_request_buf();
void set_request_pending(bool f);
bool get_request_pending() const;
@@ -369,8 +399,8 @@ public:
bool get_chunked_response() const;
void set_chunked_response(bool f);
void set_response_state(int state);
int get_response_state() const;
void set_response_state(DownstreamState state);
DownstreamState get_response_state() const;
DefaultMemchunks *get_response_buf();
bool response_buf_full();
// Validates that received response body length and content-length
@@ -426,8 +456,8 @@ public:
// true if retry attempt should not be done.
bool no_more_retry() const;
int get_dispatch_state() const;
void set_dispatch_state(int s);
DispatchState get_dispatch_state() const;
void set_dispatch_state(DispatchState s);
void attach_blocked_link(BlockedLink *l);
BlockedLink *detach_blocked_link();
@@ -461,19 +491,13 @@ public:
// field, returns 0.
uint32_t get_affinity_cookie_to_send() const;
void set_ws_key(const StringRef &key);
enum {
EVENT_ERROR = 0x1,
EVENT_TIMEOUT = 0x2,
};
enum {
DISPATCH_NONE,
DISPATCH_PENDING,
DISPATCH_BLOCKED,
DISPATCH_ACTIVE,
DISPATCH_FAILURE,
};
Downstream *dlnext, *dlprev;
// the length of response body sent to upstream client
@@ -500,6 +524,10 @@ private:
DefaultMemchunks request_buf_;
DefaultMemchunks response_buf_;
// The Sec-WebSocket-Key field sent to the peer. This field is used
// if frontend uses RFC 8441 WebSocket bootstrapping via HTTP/2.
StringRef ws_key_;
ev_timer upstream_rtimer_;
ev_timer upstream_wtimer_;
@@ -529,11 +557,11 @@ private:
// An affinity cookie value.
uint32_t affinity_cookie_;
// request state
int request_state_;
DownstreamState request_state_;
// response state
int response_state_;
DownstreamState response_state_;
// only used by HTTP/2 upstream
int dispatch_state_;
DispatchState dispatch_state_;
// true if the connection is upgraded (HTTP Upgrade or CONNECT),
// excluding upgrade to HTTP/2.
bool upgraded_;

View File

@@ -49,12 +49,12 @@ DownstreamQueue::~DownstreamQueue() {
}
void DownstreamQueue::add_pending(std::unique_ptr<Downstream> downstream) {
downstream->set_dispatch_state(Downstream::DISPATCH_PENDING);
downstream->set_dispatch_state(DispatchState::PENDING);
downstreams_.append(downstream.release());
}
void DownstreamQueue::mark_failure(Downstream *downstream) {
downstream->set_dispatch_state(Downstream::DISPATCH_FAILURE);
downstream->set_dispatch_state(DispatchState::FAILURE);
}
DownstreamQueue::HostEntry &
@@ -87,13 +87,13 @@ void DownstreamQueue::mark_active(Downstream *downstream) {
auto &ent = find_host_entry(make_host_key(downstream));
++ent.num_active;
downstream->set_dispatch_state(Downstream::DISPATCH_ACTIVE);
downstream->set_dispatch_state(DispatchState::ACTIVE);
}
void DownstreamQueue::mark_blocked(Downstream *downstream) {
auto &ent = find_host_entry(make_host_key(downstream));
downstream->set_dispatch_state(Downstream::DISPATCH_BLOCKED);
downstream->set_dispatch_state(DispatchState::BLOCKED);
auto link = new BlockedLink{};
downstream->attach_blocked_link(link);
@@ -131,7 +131,7 @@ Downstream *DownstreamQueue::remove_and_get_blocked(Downstream *downstream,
auto host = make_host_key(downstream);
auto &ent = find_host_entry(host);
if (downstream->get_dispatch_state() == Downstream::DISPATCH_ACTIVE) {
if (downstream->get_dispatch_state() == DispatchState::ACTIVE) {
--ent.num_active;
} else {
// For those downstreams deleted while in blocked state

View File

@@ -67,7 +67,7 @@ public:
size_t num_active;
};
using HostEntryMap = std::map<StringRef, HostEntry, std::less<StringRef>>;
using HostEntryMap = std::map<StringRef, HostEntry>;
// conn_max_per_host == 0 means no limit for downstream connection.
DownstreamQueue(size_t conn_max_per_host = 0, bool unified_host = true);
@@ -88,10 +88,10 @@ public:
// |host|.
bool can_activate(const StringRef &host) const;
// Removes and frees |downstream| object. If |downstream| is in
// Downstream::DISPATCH_ACTIVE, and |next_blocked| is true, this
// function may return Downstream object with the same target host
// in Downstream::DISPATCH_BLOCKED if its connection is now not
// blocked by conn_max_per_host_ limit.
// DispatchState::ACTIVE, and |next_blocked| is true, this function
// may return Downstream object with the same target host in
// DispatchState::BLOCKED if its connection is now not blocked by
// conn_max_per_host_ limit.
Downstream *remove_and_get_blocked(Downstream *downstream,
bool next_blocked = true);
Downstream *get_downstreams() const;

View File

@@ -28,21 +28,20 @@ namespace shrpx {
DualDNSResolver::DualDNSResolver(struct ev_loop *loop)
: resolv4_(loop), resolv6_(loop) {
auto cb = [this](int, const Address *) {
int rv;
auto cb = [this](DNSResolverStatus, const Address *) {
Address result;
rv = this->get_status(&result);
switch (rv) {
case DNS_STATUS_ERROR:
case DNS_STATUS_OK:
auto status = this->get_status(&result);
switch (status) {
case DNSResolverStatus::ERROR:
case DNSResolverStatus::OK:
break;
default:
return;
}
auto cb = this->get_complete_cb();
cb(rv, &result);
cb(status, &result);
};
resolv4_.set_complete_cb(cb);
@@ -65,23 +64,22 @@ CompleteCb DualDNSResolver::get_complete_cb() const { return complete_cb_; }
void DualDNSResolver::set_complete_cb(CompleteCb cb) { complete_cb_ = cb; }
int DualDNSResolver::get_status(Address *result) const {
int rv4, rv6;
rv6 = resolv6_.get_status(result);
if (rv6 == DNS_STATUS_OK) {
return DNS_STATUS_OK;
DNSResolverStatus DualDNSResolver::get_status(Address *result) const {
auto rv6 = resolv6_.get_status(result);
if (rv6 == DNSResolverStatus::OK) {
return DNSResolverStatus::OK;
}
rv4 = resolv4_.get_status(result);
if (rv4 == DNS_STATUS_OK) {
return DNS_STATUS_OK;
auto rv4 = resolv4_.get_status(result);
if (rv4 == DNSResolverStatus::OK) {
return DNSResolverStatus::OK;
}
if (rv4 == DNS_STATUS_RUNNING || rv6 == DNS_STATUS_RUNNING) {
return DNS_STATUS_RUNNING;
if (rv4 == DNSResolverStatus::RUNNING || rv6 == DNSResolverStatus::RUNNING) {
return DNSResolverStatus::RUNNING;
}
if (rv4 == DNS_STATUS_ERROR || rv6 == DNS_STATUS_ERROR) {
return DNS_STATUS_ERROR;
if (rv4 == DNSResolverStatus::ERROR || rv6 == DNSResolverStatus::ERROR) {
return DNSResolverStatus::ERROR;
}
return DNS_STATUS_IDLE;
return DNSResolverStatus::IDLE;
}
} // namespace shrpx

View File

@@ -48,7 +48,7 @@ public:
int resolve(const StringRef &host);
CompleteCb get_complete_cb() const;
void set_complete_cb(CompleteCb cb);
int get_status(Address *result) const;
DNSResolverStatus get_status(Address *result) const;
private:
// For A record

View File

@@ -199,12 +199,12 @@ StringRef create_affinity_cookie(BlockAllocator &balloc, const StringRef &name,
return StringRef{iov.base, p};
}
bool require_cookie_secure_attribute(shrpx_cookie_secure secure,
bool require_cookie_secure_attribute(SessionAffinityCookieSecure secure,
const StringRef &scheme) {
switch (secure) {
case COOKIE_SECURE_AUTO:
case SessionAffinityCookieSecure::AUTO:
return scheme == "https";
case COOKIE_SECURE_YES:
case SessionAffinityCookieSecure::YES:
return true;
default:
return false;

View File

@@ -76,7 +76,7 @@ StringRef create_affinity_cookie(BlockAllocator &balloc, const StringRef &name,
// Returns true if |secure| indicates that Secure attribute should be
// set.
bool require_cookie_secure_attribute(shrpx_cookie_secure secure,
bool require_cookie_secure_attribute(SessionAffinityCookieSecure secure,
const StringRef &scheme);
} // namespace http

View File

@@ -41,6 +41,7 @@
#include "shrpx_log.h"
#include "http2.h"
#include "util.h"
#include "ssl_compat.h"
using namespace nghttp2;
@@ -61,16 +62,16 @@ Http2DownstreamConnection::~Http2DownstreamConnection() {
downstream_->disable_downstream_wtimer();
uint32_t error_code;
if (downstream_->get_request_state() == Downstream::STREAM_CLOSED &&
if (downstream_->get_request_state() == DownstreamState::STREAM_CLOSED &&
downstream_->get_upgraded()) {
// For upgraded connection, send NO_ERROR. Should we consider
// request states other than Downstream::STREAM_CLOSED ?
// request states other than DownstreamState::STREAM_CLOSED ?
error_code = NGHTTP2_NO_ERROR;
} else {
error_code = NGHTTP2_INTERNAL_ERROR;
}
if (http2session_->get_state() == Http2Session::CONNECTED &&
if (http2session_->get_state() == Http2SessionState::CONNECTED &&
downstream_->get_downstream_stream_id() != -1) {
submit_rst_stream(downstream_, error_code);
@@ -104,7 +105,7 @@ int Http2DownstreamConnection::attach_downstream(Downstream *downstream) {
auto &req = downstream_->request();
// HTTP/2 disables HTTP Upgrade.
if (req.method != HTTP_CONNECT) {
if (req.method != HTTP_CONNECT && req.connect_proto == ConnectProto::NONE) {
req.upgrade_request = false;
}
@@ -139,12 +140,12 @@ void Http2DownstreamConnection::detach_downstream(Downstream *downstream) {
int Http2DownstreamConnection::submit_rst_stream(Downstream *downstream,
uint32_t error_code) {
int rv = -1;
if (http2session_->get_state() == Http2Session::CONNECTED &&
if (http2session_->get_state() == Http2SessionState::CONNECTED &&
downstream->get_downstream_stream_id() != -1) {
switch (downstream->get_response_state()) {
case Downstream::MSG_RESET:
case Downstream::MSG_BAD_HEADER:
case Downstream::MSG_COMPLETE:
case DownstreamState::MSG_RESET:
case DownstreamState::MSG_BAD_HEADER:
case DownstreamState::MSG_COMPLETE:
break;
default:
if (LOG_ENABLED(INFO)) {
@@ -187,12 +188,12 @@ ssize_t http2_data_read_callback(nghttp2_session *session, int32_t stream_id,
*data_flags |= NGHTTP2_DATA_FLAG_NO_COPY;
if (input_empty &&
downstream->get_request_state() == Downstream::MSG_COMPLETE &&
downstream->get_request_state() == DownstreamState::MSG_COMPLETE &&
// If connection is upgraded, don't set EOF flag, since HTTP/1
// will set MSG_COMPLETE to request state after upgrade response
// header is seen.
(!req.upgrade_request ||
(downstream->get_response_state() == Downstream::HEADER_COMPLETE &&
(downstream->get_response_state() == DownstreamState::HEADER_COMPLETE &&
!downstream->get_upgraded()))) {
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
@@ -230,7 +231,7 @@ int Http2DownstreamConnection::push_request_headers() {
if (!downstream_) {
return 0;
}
if (!http2session_->can_push_request()) {
if (!http2session_->can_push_request(downstream_)) {
// The HTTP2 session to the backend has not been established or
// connection is now being checked. This function will be called
// again just after it is established.
@@ -243,6 +244,11 @@ int Http2DownstreamConnection::push_request_headers() {
const auto &req = downstream_->request();
if (req.connect_proto != ConnectProto::NONE &&
!http2session_->get_allow_connect_proto()) {
return -1;
}
auto &balloc = downstream_->get_block_allocator();
auto config = get_config();
@@ -250,7 +256,7 @@ int Http2DownstreamConnection::push_request_headers() {
auto &http2conf = config->http2;
auto no_host_rewrite = httpconf.no_host_rewrite || config->http2_proxy ||
req.method == HTTP_CONNECT;
req.regular_connect_method();
// http2session_ has already in CONNECTED state, so we can get
// addr_idx here.
@@ -271,24 +277,31 @@ int Http2DownstreamConnection::push_request_headers() {
num_cookies = downstream_->count_crumble_request_cookie();
}
// 9 means:
// 11 means:
// 1. :method
// 2. :scheme
// 3. :path
// 4. :authority (or host)
// 5. via (optional)
// 6. x-forwarded-for (optional)
// 7. x-forwarded-proto (optional)
// 8. te (optional)
// 9. forwarded (optional)
// 5. :protocol (optional)
// 6. via (optional)
// 7. x-forwarded-for (optional)
// 8. x-forwarded-proto (optional)
// 9. te (optional)
// 10. forwarded (optional)
// 11. early-data (optional)
auto nva = std::vector<nghttp2_nv>();
nva.reserve(req.fs.headers().size() + 9 + num_cookies +
nva.reserve(req.fs.headers().size() + 11 + num_cookies +
httpconf.add_request_headers.size());
nva.push_back(
http2::make_nv_ls_nocopy(":method", http2::to_method_string(req.method)));
if (req.connect_proto == ConnectProto::WEBSOCKET) {
nva.push_back(http2::make_nv_ll(":method", "CONNECT"));
nva.push_back(http2::make_nv_ll(":protocol", "websocket"));
} else {
nva.push_back(http2::make_nv_ls_nocopy(
":method", http2::to_method_string(req.method)));
}
if (req.method != HTTP_CONNECT) {
if (!req.regular_connect_method()) {
assert(!req.scheme.empty());
auto addr = http2session_->get_addr();
@@ -306,7 +319,7 @@ int Http2DownstreamConnection::push_request_headers() {
nva.push_back(http2::make_nv_ls_nocopy(":path", req.path));
}
if (!req.no_authority) {
if (!req.no_authority || req.connect_proto != ConnectProto::NONE) {
nva.push_back(http2::make_nv_ls_nocopy(":authority", authority));
} else {
nva.push_back(http2::make_nv_ls_nocopy("host", authority));
@@ -318,11 +331,14 @@ int Http2DownstreamConnection::push_request_headers() {
auto &fwdconf = httpconf.forwarded;
auto &xffconf = httpconf.xff;
auto &xfpconf = httpconf.xfp;
auto &earlydataconf = httpconf.early_data;
uint32_t build_flags =
(fwdconf.strip_incoming ? http2::HDOP_STRIP_FORWARDED : 0) |
(xffconf.strip_incoming ? http2::HDOP_STRIP_X_FORWARDED_FOR : 0) |
(xfpconf.strip_incoming ? http2::HDOP_STRIP_X_FORWARDED_PROTO : 0);
(xfpconf.strip_incoming ? http2::HDOP_STRIP_X_FORWARDED_PROTO : 0) |
(earlydataconf.strip_incoming ? http2::HDOP_STRIP_EARLY_DATA : 0) |
http2::HDOP_STRIP_SEC_WEBSOCKET_KEY;
http2::copy_headers_to_nva_nocopy(nva, req.fs.headers(), build_flags);
@@ -333,13 +349,21 @@ int Http2DownstreamConnection::push_request_headers() {
auto upstream = downstream_->get_upstream();
auto handler = upstream->get_client_handler();
#if OPENSSL_1_1_1_API
auto conn = handler->get_connection();
if (conn->tls.ssl && !SSL_is_init_finished(conn->tls.ssl)) {
nva.push_back(http2::make_nv_ll("early-data", "1"));
}
#endif // OPENSSL_1_1_1_API
auto fwd =
fwdconf.strip_incoming ? nullptr : req.fs.header(http2::HD_FORWARDED);
if (fwdconf.params) {
auto params = fwdconf.params;
if (config->http2_proxy || req.method == HTTP_CONNECT) {
if (config->http2_proxy || req.regular_connect_method()) {
params &= ~FORWARDED_PROTO;
}
@@ -380,7 +404,7 @@ int Http2DownstreamConnection::push_request_headers() {
nva.push_back(http2::make_nv_ls_nocopy("x-forwarded-for", xff->value));
}
if (!config->http2_proxy && req.method != HTTP_CONNECT) {
if (!config->http2_proxy && !req.regular_connect_method()) {
auto xfp = xfpconf.strip_incoming
? nullptr
: req.fs.header(http2::HD_X_FORWARDED_PROTO);
@@ -452,8 +476,8 @@ int Http2DownstreamConnection::push_request_headers() {
// Add body as long as transfer-encoding is given even if
// req.fs.content_length == 0 to forward trailer fields.
if (req.method == HTTP_CONNECT || transfer_encoding ||
req.fs.content_length > 0 || req.http2_expect_body) {
if (req.method == HTTP_CONNECT || req.connect_proto != ConnectProto::NONE ||
transfer_encoding || req.fs.content_length > 0 || req.http2_expect_body) {
// Request-body is expected.
data_prd = {{}, http2_data_read_callback};
data_prdptr = &data_prd;
@@ -510,7 +534,7 @@ int Http2DownstreamConnection::resume_read(IOCtrlReason reason,
size_t consumed) {
int rv;
if (http2session_->get_state() != Http2Session::CONNECTED) {
if (http2session_->get_state() != Http2SessionState::CONNECTED) {
return 0;
}

View File

@@ -69,7 +69,7 @@ void connchk_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
ev_timer_stop(loop, w);
switch (http2session->get_connection_check_state()) {
case Http2Session::CONNECTION_CHECK_STARTED:
case ConnectionCheck::STARTED:
// ping timeout; disconnect
if (LOG_ENABLED(INFO)) {
SSLOG(INFO, http2session) << "ping timeout";
@@ -82,8 +82,7 @@ void connchk_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
if (LOG_ENABLED(INFO)) {
SSLOG(INFO, http2session) << "connection check required";
}
http2session->set_connection_check_state(
Http2Session::CONNECTION_CHECK_REQUIRED);
http2session->set_connection_check_state(ConnectionCheck::REQUIRED);
}
}
} // namespace
@@ -186,10 +185,10 @@ Http2Session::Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx,
: dlnext(nullptr),
dlprev(nullptr),
conn_(loop, -1, nullptr, worker->get_mcpool(),
worker->get_downstream_config()->timeout.write,
worker->get_downstream_config()->timeout.read, {}, {}, writecb,
readcb, timeoutcb, this, get_config()->tls.dyn_rec.warmup_threshold,
get_config()->tls.dyn_rec.idle_timeout, PROTO_HTTP2),
group->shared_addr->timeout.write, group->shared_addr->timeout.read,
{}, {}, writecb, readcb, timeoutcb, this,
get_config()->tls.dyn_rec.warmup_threshold,
get_config()->tls.dyn_rec.idle_timeout, Proto::HTTP2),
wb_(worker->get_mcpool()),
worker_(worker),
ssl_ctx_(ssl_ctx),
@@ -197,9 +196,11 @@ Http2Session::Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx,
addr_(addr),
session_(nullptr),
raddr_(nullptr),
state_(DISCONNECTED),
connection_check_state_(CONNECTION_CHECK_NONE),
freelist_zone_(FREELIST_ZONE_NONE) {
state_(Http2SessionState::DISCONNECTED),
connection_check_state_(ConnectionCheck::NONE),
freelist_zone_(FreelistZone::NONE),
settings_recved_(false),
allow_connect_proto_(false) {
read_ = write_ = &Http2Session::noop;
on_read_ = &Http2Session::read_noop;
@@ -264,8 +265,8 @@ int Http2Session::disconnect(bool hard) {
proxy_htp_.reset();
}
connection_check_state_ = CONNECTION_CHECK_NONE;
state_ = DISCONNECTED;
connection_check_state_ = ConnectionCheck::NONE;
state_ = Http2SessionState::DISCONNECTED;
// When deleting Http2DownstreamConnection, it calls this object's
// remove_downstream_connection(). The multiple
@@ -303,13 +304,11 @@ int Http2Session::disconnect(bool hard) {
}
int Http2Session::resolve_name() {
int rv;
auto dns_query = make_unique<DNSQuery>(
addr_->host, [this](int status, const Address *result) {
auto dns_query = std::make_unique<DNSQuery>(
addr_->host, [this](DNSResolverStatus status, const Address *result) {
int rv;
if (status == DNS_STATUS_OK) {
if (status == DNSResolverStatus::OK) {
*resolved_addr_ = *result;
util::set_port(*this->resolved_addr_, this->addr_->port);
}
@@ -319,17 +318,16 @@ int Http2Session::resolve_name() {
delete this;
}
});
resolved_addr_ = make_unique<Address>();
resolved_addr_ = std::make_unique<Address>();
auto dns_tracker = worker_->get_dns_tracker();
rv = dns_tracker->resolve(resolved_addr_.get(), dns_query.get());
switch (rv) {
case DNS_STATUS_ERROR:
switch (dns_tracker->resolve(resolved_addr_.get(), dns_query.get())) {
case DNSResolverStatus::ERROR:
return -1;
case DNS_STATUS_RUNNING:
case DNSResolverStatus::RUNNING:
dns_query_ = std::move(dns_query);
state_ = RESOLVING_NAME;
state_ = Http2SessionState::RESOLVING_NAME;
return 0;
case DNS_STATUS_OK:
case DNSResolverStatus::OK:
util::set_port(*resolved_addr_, addr_->port);
return 0;
default:
@@ -343,7 +341,8 @@ int Http2Session::initiate_connection() {
auto worker_blocker = worker_->get_connect_blocker();
if (state_ == DISCONNECTED || state_ == RESOLVING_NAME) {
if (state_ == Http2SessionState::DISCONNECTED ||
state_ == Http2SessionState::RESOLVING_NAME) {
if (worker_blocker->blocked()) {
if (LOG_ENABLED(INFO)) {
SSLOG(INFO, this)
@@ -356,7 +355,7 @@ int Http2Session::initiate_connection() {
auto &downstreamconf = *get_config()->conn.downstream;
const auto &proxy = get_config()->downstream_http_proxy;
if (!proxy.host.empty() && state_ == DISCONNECTED) {
if (!proxy.host.empty() && state_ == Http2SessionState::DISCONNECTED) {
if (LOG_ENABLED(INFO)) {
SSLOG(INFO, this) << "Connecting to the proxy " << proxy.host << ":"
<< proxy.port;
@@ -403,26 +402,27 @@ int Http2Session::initiate_connection() {
on_read_ = &Http2Session::downstream_read_proxy;
on_write_ = &Http2Session::downstream_connect_proxy;
proxy_htp_ = make_unique<http_parser>();
proxy_htp_ = std::make_unique<http_parser>();
http_parser_init(proxy_htp_.get(), HTTP_RESPONSE);
proxy_htp_->data = this;
state_ = PROXY_CONNECTING;
state_ = Http2SessionState::PROXY_CONNECTING;
return 0;
}
if (state_ == DISCONNECTED || state_ == PROXY_CONNECTED ||
state_ == RESOLVING_NAME) {
if (state_ == Http2SessionState::DISCONNECTED ||
state_ == Http2SessionState::PROXY_CONNECTED ||
state_ == Http2SessionState::RESOLVING_NAME) {
if (LOG_ENABLED(INFO)) {
if (state_ != RESOLVING_NAME) {
if (state_ != Http2SessionState::RESOLVING_NAME) {
SSLOG(INFO, this) << "Connecting to downstream server";
}
}
if (addr_->tls) {
assert(ssl_ctx_);
if (state_ != RESOLVING_NAME) {
if (state_ != Http2SessionState::RESOLVING_NAME) {
auto ssl = tls::create_ssl(ssl_ctx_);
if (!ssl) {
return -1;
@@ -450,14 +450,14 @@ int Http2Session::initiate_connection() {
}
}
if (state_ == DISCONNECTED) {
if (state_ == Http2SessionState::DISCONNECTED) {
if (addr_->dns) {
rv = resolve_name();
if (rv != 0) {
downstream_failure(addr_, nullptr);
return -1;
}
if (state_ == RESOLVING_NAME) {
if (state_ == Http2SessionState::RESOLVING_NAME) {
return 0;
}
raddr_ = resolved_addr_.get();
@@ -466,20 +466,21 @@ int Http2Session::initiate_connection() {
}
}
if (state_ == RESOLVING_NAME) {
if (dns_query_->status == DNS_STATUS_ERROR) {
if (state_ == Http2SessionState::RESOLVING_NAME) {
if (dns_query_->status == DNSResolverStatus::ERROR) {
downstream_failure(addr_, nullptr);
return -1;
}
assert(dns_query_->status == DNS_STATUS_OK);
state_ = DISCONNECTED;
assert(dns_query_->status == DNSResolverStatus::OK);
state_ = Http2SessionState::DISCONNECTED;
dns_query_.reset();
raddr_ = resolved_addr_.get();
}
// If state_ == PROXY_CONNECTED, we has connected to the proxy
// using conn_.fd and tunnel has been established.
if (state_ == DISCONNECTED) {
// If state_ == Http2SessionState::PROXY_CONNECTED, we have
// connected to the proxy using conn_.fd and tunnel has been
// established.
if (state_ == Http2SessionState::DISCONNECTED) {
assert(conn_.fd == -1);
conn_.fd = util::create_nonblock_socket(raddr_->su.storage.ss_family);
@@ -514,7 +515,7 @@ int Http2Session::initiate_connection() {
conn_.prepare_client_handshake();
} else {
if (state_ == DISCONNECTED) {
if (state_ == Http2SessionState::DISCONNECTED) {
// Without TLS and proxy.
if (addr_->dns) {
rv = resolve_name();
@@ -522,7 +523,7 @@ int Http2Session::initiate_connection() {
downstream_failure(addr_, nullptr);
return -1;
}
if (state_ == RESOLVING_NAME) {
if (state_ == Http2SessionState::RESOLVING_NAME) {
return 0;
}
raddr_ = resolved_addr_.get();
@@ -531,18 +532,18 @@ int Http2Session::initiate_connection() {
}
}
if (state_ == RESOLVING_NAME) {
if (dns_query_->status == DNS_STATUS_ERROR) {
if (state_ == Http2SessionState::RESOLVING_NAME) {
if (dns_query_->status == DNSResolverStatus::ERROR) {
downstream_failure(addr_, nullptr);
return -1;
}
assert(dns_query_->status == DNS_STATUS_OK);
state_ = DISCONNECTED;
assert(dns_query_->status == DNSResolverStatus::OK);
state_ = Http2SessionState::DISCONNECTED;
dns_query_.reset();
raddr_ = resolved_addr_.get();
}
if (state_ == DISCONNECTED) {
if (state_ == Http2SessionState::DISCONNECTED) {
// Without TLS and proxy.
assert(conn_.fd == -1);
@@ -578,7 +579,7 @@ int Http2Session::initiate_connection() {
}
// We have been already connected when no TLS and proxy is used.
if (state_ == PROXY_CONNECTED) {
if (state_ == Http2SessionState::PROXY_CONNECTED) {
on_read_ = &Http2Session::read_noop;
on_write_ = &Http2Session::write_noop;
@@ -587,7 +588,7 @@ int Http2Session::initiate_connection() {
write_ = &Http2Session::connected;
state_ = CONNECTING;
state_ = Http2SessionState::CONNECTING;
conn_.wlimit.startw();
conn_.wt.repeat = downstreamconf.timeout.connect;
@@ -618,17 +619,17 @@ int htp_hdrs_completecb(http_parser *htp) {
http_parser_pause(htp, 1);
// We just check status code here
if (htp->status_code == 200) {
if (htp->status_code / 100 == 2) {
if (LOG_ENABLED(INFO)) {
SSLOG(INFO, http2session) << "Tunneling success";
}
http2session->set_state(Http2Session::PROXY_CONNECTED);
http2session->set_state(Http2SessionState::PROXY_CONNECTED);
return 0;
}
SSLOG(WARN, http2session) << "Tunneling failed: " << htp->status_code;
http2session->set_state(Http2Session::PROXY_FAILED);
http2session->set_state(Http2SessionState::PROXY_FAILED);
return 0;
}
@@ -657,14 +658,16 @@ int Http2Session::downstream_read_proxy(const uint8_t *data, size_t datalen) {
if (htperr == HPE_PAUSED) {
switch (state_) {
case Http2Session::PROXY_CONNECTED:
case Http2SessionState::PROXY_CONNECTED:
// Initiate SSL/TLS handshake through established tunnel.
if (initiate_connection() != 0) {
return -1;
}
return 0;
case Http2Session::PROXY_FAILED:
case Http2SessionState::PROXY_FAILED:
return -1;
default:
break;
}
// should not be here
assert(0);
@@ -724,7 +727,7 @@ void Http2Session::remove_downstream_connection(
SSLOG(INFO, this) << "Remove downstream";
}
if (freelist_zone_ == FREELIST_ZONE_NONE && !max_concurrency_reached()) {
if (freelist_zone_ == FreelistZone::NONE && !max_concurrency_reached()) {
if (LOG_ENABLED(INFO)) {
SSLOG(INFO, this) << "Append to http2_extra_freelist, addr=" << addr_
<< ", freelist.size="
@@ -746,8 +749,8 @@ void Http2Session::remove_stream_data(StreamData *sd) {
int Http2Session::submit_request(Http2DownstreamConnection *dconn,
const nghttp2_nv *nva, size_t nvlen,
const nghttp2_data_provider *data_prd) {
assert(state_ == CONNECTED);
auto sd = make_unique<StreamData>();
assert(state_ == Http2SessionState::CONNECTED);
auto sd = std::make_unique<StreamData>();
sd->dlnext = sd->dlprev = nullptr;
// TODO Specify nullptr to pri_spec for now
auto stream_id =
@@ -766,7 +769,7 @@ int Http2Session::submit_request(Http2DownstreamConnection *dconn,
}
int Http2Session::submit_rst_stream(int32_t stream_id, uint32_t error_code) {
assert(state_ == CONNECTED);
assert(state_ == Http2SessionState::CONNECTED);
if (LOG_ENABLED(INFO)) {
SSLOG(INFO, this) << "RST_STREAM stream_id=" << stream_id
<< " with error_code=" << error_code;
@@ -784,7 +787,7 @@ int Http2Session::submit_rst_stream(int32_t stream_id, uint32_t error_code) {
nghttp2_session *Http2Session::get_session() const { return session_; }
int Http2Session::resume_data(Http2DownstreamConnection *dconn) {
assert(state_ == CONNECTED);
assert(state_ == Http2SessionState::CONNECTED);
auto downstream = dconn->get_downstream();
int rv = nghttp2_session_resume_data(session_,
downstream->get_downstream_stream_id());
@@ -834,34 +837,34 @@ int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
auto upstream = downstream->get_upstream();
if (downstream->get_downstream_stream_id() % 2 == 0 &&
downstream->get_request_state() == Downstream::INITIAL) {
downstream->get_request_state() == DownstreamState::INITIAL) {
// Downstream is canceled in backend before it is submitted in
// frontend session.
// This will avoid to send RST_STREAM to backend
downstream->set_response_state(Downstream::MSG_RESET);
downstream->set_response_state(DownstreamState::MSG_RESET);
upstream->cancel_premature_downstream(downstream);
} else {
if (downstream->get_upgraded() &&
downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
if (downstream->get_upgraded() && downstream->get_response_state() ==
DownstreamState::HEADER_COMPLETE) {
// For tunneled connection, we have to submit RST_STREAM to
// upstream *after* whole response body is sent. We just set
// MSG_COMPLETE here. Upstream will take care of that.
downstream->get_upstream()->on_downstream_body_complete(downstream);
downstream->set_response_state(Downstream::MSG_COMPLETE);
downstream->set_response_state(DownstreamState::MSG_COMPLETE);
} else if (error_code == NGHTTP2_NO_ERROR) {
switch (downstream->get_response_state()) {
case Downstream::MSG_COMPLETE:
case Downstream::MSG_BAD_HEADER:
case DownstreamState::MSG_COMPLETE:
case DownstreamState::MSG_BAD_HEADER:
break;
default:
downstream->set_response_state(Downstream::MSG_RESET);
downstream->set_response_state(DownstreamState::MSG_RESET);
}
} else if (downstream->get_response_state() !=
Downstream::MSG_BAD_HEADER) {
downstream->set_response_state(Downstream::MSG_RESET);
DownstreamState::MSG_BAD_HEADER) {
downstream->set_response_state(DownstreamState::MSG_RESET);
}
if (downstream->get_response_state() == Downstream::MSG_RESET &&
if (downstream->get_response_state() == DownstreamState::MSG_RESET &&
downstream->get_response_rst_stream_error_code() ==
NGHTTP2_NO_ERROR) {
downstream->set_response_rst_stream_error_code(error_code);
@@ -1134,14 +1137,14 @@ int on_response_headers(Http2Session *http2session, Downstream *downstream,
if (rv != 0) {
http2session->submit_rst_stream(frame->hd.stream_id,
NGHTTP2_PROTOCOL_ERROR);
downstream->set_response_state(Downstream::MSG_RESET);
downstream->set_response_state(DownstreamState::MSG_RESET);
}
return 0;
}
downstream->set_response_state(Downstream::HEADER_COMPLETE);
downstream->check_upgrade_fulfilled();
downstream->set_response_state(DownstreamState::HEADER_COMPLETE);
downstream->check_upgrade_fulfilled_http2();
if (downstream->get_upgraded()) {
resp.connection_close = true;
@@ -1151,7 +1154,7 @@ int on_response_headers(Http2Session *http2session, Downstream *downstream,
delete handler;
return -1;
}
downstream->set_request_state(Downstream::HEADER_COMPLETE);
downstream->set_request_state(DownstreamState::HEADER_COMPLETE);
if (LOG_ENABLED(INFO)) {
SSLOG(INFO, http2session)
<< "HTTP upgrade success. stream_id=" << frame->hd.stream_id;
@@ -1171,7 +1174,7 @@ int on_response_headers(Http2Session *http2session, Downstream *downstream,
resp.connection_close = true;
} else {
// Otherwise, use chunked encoding to keep upstream connection
// open. In HTTP2, we are supporsed not to receive
// open. In HTTP2, we are supposed not to receive
// transfer-encoding.
resp.fs.add_header_token(StringRef::from_lit("transfer-encoding"),
StringRef::from_lit("chunked"), false,
@@ -1194,12 +1197,12 @@ int on_response_headers(Http2Session *http2session, Downstream *downstream,
if (rv != 0) {
// Handling early return (in other words, response was hijacked by
// mruby scripting).
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
http2session->submit_rst_stream(frame->hd.stream_id, NGHTTP2_CANCEL);
} else {
http2session->submit_rst_stream(frame->hd.stream_id,
NGHTTP2_INTERNAL_ERROR);
downstream->set_response_state(Downstream::MSG_RESET);
downstream->set_response_state(DownstreamState::MSG_RESET);
}
}
@@ -1226,20 +1229,21 @@ int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
if (rv != 0) {
http2session->submit_rst_stream(frame->hd.stream_id,
NGHTTP2_INTERNAL_ERROR);
downstream->set_response_state(Downstream::MSG_RESET);
downstream->set_response_state(DownstreamState::MSG_RESET);
} else if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
downstream->disable_downstream_rtimer();
if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
if (downstream->get_response_state() ==
DownstreamState::HEADER_COMPLETE) {
downstream->set_response_state(Downstream::MSG_COMPLETE);
downstream->set_response_state(DownstreamState::MSG_COMPLETE);
rv = upstream->on_downstream_body_complete(downstream);
if (rv != 0) {
downstream->set_response_state(Downstream::MSG_RESET);
downstream->set_response_state(DownstreamState::MSG_RESET);
}
}
}
@@ -1275,15 +1279,16 @@ int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
downstream->disable_downstream_rtimer();
if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
downstream->set_response_state(Downstream::MSG_COMPLETE);
if (downstream->get_response_state() ==
DownstreamState::HEADER_COMPLETE) {
downstream->set_response_state(DownstreamState::MSG_COMPLETE);
auto upstream = downstream->get_upstream();
rv = upstream->on_downstream_body_complete(downstream);
if (rv != 0) {
downstream->set_response_state(Downstream::MSG_RESET);
downstream->set_response_state(DownstreamState::MSG_RESET);
}
}
} else {
@@ -1308,6 +1313,7 @@ int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
}
case NGHTTP2_SETTINGS: {
if ((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) {
http2session->on_settings_received(frame);
return 0;
}
@@ -1442,7 +1448,7 @@ int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
downstream->set_response_state(Downstream::MSG_RESET);
downstream->set_response_state(DownstreamState::MSG_RESET);
}
call_downstream_readcb(http2session, downstream);
@@ -1536,8 +1542,8 @@ int on_frame_not_send_callback(nghttp2_session *session,
return 0;
}
// To avoid stream hanging around, flag Downstream::MSG_RESET.
downstream->set_response_state(Downstream::MSG_RESET);
// To avoid stream hanging around, flag DownstreamState::MSG_RESET.
downstream->set_response_state(DownstreamState::MSG_RESET);
call_downstream_readcb(http2session, downstream);
return 0;
@@ -1649,7 +1655,7 @@ nghttp2_session_callbacks *create_http2_downstream_callbacks() {
int Http2Session::connection_made() {
int rv;
state_ = Http2Session::CONNECTED;
state_ = Http2SessionState::CONNECTED;
on_write_ = &Http2Session::downstream_write;
on_read_ = &Http2Session::downstream_read;
@@ -1769,7 +1775,6 @@ int Http2Session::downstream_write() {
for (;;) {
const uint8_t *data;
auto datalen = nghttp2_session_mem_send(session_, &data);
if (datalen < 0) {
SSLOG(ERROR, this) << "nghttp2_session_mem_send() returned error: "
<< nghttp2_strerror(datalen);
@@ -1798,7 +1803,7 @@ int Http2Session::downstream_write() {
void Http2Session::signal_write() {
switch (state_) {
case Http2Session::DISCONNECTED:
case Http2SessionState::DISCONNECTED:
if (!ev_is_active(&initiate_connection_timer_)) {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Start connecting to backend server";
@@ -1810,9 +1815,11 @@ void Http2Session::signal_write() {
ev_feed_event(conn_.loop, &initiate_connection_timer_, 0);
}
break;
case Http2Session::CONNECTED:
case Http2SessionState::CONNECTED:
conn_.wlimit.startw();
break;
default:
break;
}
}
@@ -1822,9 +1829,9 @@ struct ev_loop *Http2Session::get_loop() const {
ev_io *Http2Session::get_wev() { return &conn_.wev; }
int Http2Session::get_state() const { return state_; }
Http2SessionState Http2Session::get_state() const { return state_; }
void Http2Session::set_state(int state) { state_ = state; }
void Http2Session::set_state(Http2SessionState state) { state_ = state; }
int Http2Session::terminate_session(uint32_t error_code) {
int rv;
@@ -1856,17 +1863,19 @@ int Http2Session::consume(int32_t stream_id, size_t len) {
return 0;
}
bool Http2Session::can_push_request() const {
return state_ == CONNECTED &&
connection_check_state_ == CONNECTION_CHECK_NONE;
bool Http2Session::can_push_request(const Downstream *downstream) const {
auto &req = downstream->request();
return state_ == Http2SessionState::CONNECTED &&
connection_check_state_ == ConnectionCheck::NONE &&
(req.connect_proto == ConnectProto::NONE || settings_recved_);
}
void Http2Session::start_checking_connection() {
if (state_ != CONNECTED ||
connection_check_state_ != CONNECTION_CHECK_REQUIRED) {
if (state_ != Http2SessionState::CONNECTED ||
connection_check_state_ != ConnectionCheck::REQUIRED) {
return;
}
connection_check_state_ = CONNECTION_CHECK_STARTED;
connection_check_state_ = ConnectionCheck::STARTED;
SSLOG(INFO, this) << "Start checking connection";
// If connection is down, we may get error when writing data. Issue
@@ -1885,7 +1894,7 @@ void Http2Session::reset_connection_check_timer(ev_tstamp t) {
}
void Http2Session::reset_connection_check_timer_if_not_checking() {
if (connection_check_state_ != CONNECTION_CHECK_NONE) {
if (connection_check_state_ != ConnectionCheck::NONE) {
return;
}
@@ -1895,7 +1904,7 @@ void Http2Session::reset_connection_check_timer_if_not_checking() {
void Http2Session::connection_alive() {
reset_connection_check_timer(CONNCHK_TIMEOUT);
if (connection_check_state_ == CONNECTION_CHECK_NONE) {
if (connection_check_state_ == ConnectionCheck::NONE) {
return;
}
@@ -1903,7 +1912,7 @@ void Http2Session::connection_alive() {
SSLOG(INFO, this) << "Connection alive";
}
connection_check_state_ = CONNECTION_CHECK_NONE;
connection_check_state_ = ConnectionCheck::NONE;
submit_pending_requests();
}
@@ -1917,6 +1926,11 @@ void Http2Session::submit_pending_requests() {
continue;
}
auto &req = downstream->request();
if (req.connect_proto != ConnectProto::NONE && !settings_recved_) {
continue;
}
auto upstream = downstream->get_upstream();
if (dconn->push_request_headers() != 0) {
@@ -1933,11 +1947,11 @@ void Http2Session::submit_pending_requests() {
}
}
void Http2Session::set_connection_check_state(int state) {
void Http2Session::set_connection_check_state(ConnectionCheck state) {
connection_check_state_ = state;
}
int Http2Session::get_connection_check_state() const {
ConnectionCheck Http2Session::get_connection_check_state() const {
return connection_check_state_;
}
@@ -1963,10 +1977,8 @@ int Http2Session::connected() {
SSLOG(INFO, this) << "Connection established";
}
auto &downstreamconf = *get_config()->conn.downstream;
// Reset timeout for write. Previously, we set timeout for connect.
conn_.wt.repeat = downstreamconf.timeout.write;
conn_.wt.repeat = group_->shared_addr->timeout.write;
ev_timer_again(conn_.loop, &conn_.wt);
conn_.rlimit.startw();
@@ -1975,7 +1987,7 @@ int Http2Session::connected() {
read_ = &Http2Session::read_clear;
write_ = &Http2Session::write_clear;
if (state_ == PROXY_CONNECTING) {
if (state_ == Http2SessionState::PROXY_CONNECTING) {
return do_write();
}
@@ -1987,7 +1999,7 @@ int Http2Session::connected() {
}
if (connection_made() != 0) {
state_ = CONNECT_FAILING;
state_ = Http2SessionState::CONNECT_FAILING;
return -1;
}
@@ -2089,7 +2101,7 @@ int Http2Session::tls_handshake() {
write_ = &Http2Session::write_tls;
if (connection_made() != 0) {
state_ = CONNECT_FAILING;
state_ = Http2SessionState::CONNECT_FAILING;
return -1;
}
@@ -2176,10 +2188,10 @@ int Http2Session::write_void() {
bool Http2Session::should_hard_fail() const {
switch (state_) {
case PROXY_CONNECTING:
case PROXY_FAILED:
case Http2SessionState::PROXY_CONNECTING:
case Http2SessionState::PROXY_FAILED:
return true;
case DISCONNECTED: {
case Http2SessionState::DISCONNECTED: {
const auto &proxy = get_config()->downstream_http_proxy;
return !proxy.host.empty();
}
@@ -2208,7 +2220,7 @@ int Http2Session::handle_downstream_push_promise(Downstream *downstream,
auto handler = upstream->get_client_handler();
auto promised_dconn = make_unique<Http2DownstreamConnection>(this);
auto promised_dconn = std::make_unique<Http2DownstreamConnection>(this);
promised_dconn->set_client_handler(handler);
auto ptr = promised_dconn.get();
@@ -2218,7 +2230,7 @@ int Http2Session::handle_downstream_push_promise(Downstream *downstream,
return -1;
}
auto promised_sd = make_unique<StreamData>();
auto promised_sd = std::make_unique<StreamData>();
nghttp2_session_set_stream_user_data(session_, promised_stream_id,
promised_sd.get());
@@ -2275,7 +2287,7 @@ int Http2Session::handle_downstream_push_promise_complete(
auto upstream = promised_downstream->get_upstream();
promised_downstream->set_request_state(Downstream::MSG_COMPLETE);
promised_downstream->set_request_state(DownstreamState::MSG_COMPLETE);
promised_downstream->set_request_header_sent(true);
if (upstream->on_downstream_push_promise_complete(downstream,
@@ -2307,7 +2319,7 @@ Http2Session::get_downstream_addr_group() const {
}
void Http2Session::add_to_avail_freelist() {
if (freelist_zone_ != FREELIST_ZONE_NONE) {
if (freelist_zone_ != FreelistZone::NONE) {
return;
}
@@ -2317,13 +2329,13 @@ void Http2Session::add_to_avail_freelist() {
<< group_->shared_addr->http2_avail_freelist.size();
}
freelist_zone_ = FREELIST_ZONE_AVAIL;
freelist_zone_ = FreelistZone::AVAIL;
group_->shared_addr->http2_avail_freelist.append(this);
addr_->in_avail = true;
}
void Http2Session::add_to_extra_freelist() {
if (freelist_zone_ != FREELIST_ZONE_NONE) {
if (freelist_zone_ != FreelistZone::NONE) {
return;
}
@@ -2333,15 +2345,15 @@ void Http2Session::add_to_extra_freelist() {
<< addr_->http2_extra_freelist.size();
}
freelist_zone_ = FREELIST_ZONE_EXTRA;
freelist_zone_ = FreelistZone::EXTRA;
addr_->http2_extra_freelist.append(this);
}
void Http2Session::remove_from_freelist() {
switch (freelist_zone_) {
case FREELIST_ZONE_NONE:
case FreelistZone::NONE:
return;
case FREELIST_ZONE_AVAIL:
case FreelistZone::AVAIL:
if (LOG_ENABLED(INFO)) {
SSLOG(INFO, this) << "Remove from http2_avail_freelist, group=" << group_
<< ", freelist.size="
@@ -2350,7 +2362,7 @@ void Http2Session::remove_from_freelist() {
group_->shared_addr->http2_avail_freelist.remove(this);
addr_->in_avail = false;
break;
case FREELIST_ZONE_EXTRA:
case FreelistZone::EXTRA:
if (LOG_ENABLED(INFO)) {
SSLOG(INFO, this) << "Remove from http2_extra_freelist, addr=" << addr_
<< ", freelist.size="
@@ -2358,34 +2370,35 @@ void Http2Session::remove_from_freelist() {
}
addr_->http2_extra_freelist.remove(this);
break;
case FREELIST_ZONE_GONE:
case FreelistZone::GONE:
return;
}
freelist_zone_ = FREELIST_ZONE_NONE;
freelist_zone_ = FreelistZone::NONE;
}
void Http2Session::exclude_from_scheduling() {
remove_from_freelist();
freelist_zone_ = FREELIST_ZONE_GONE;
freelist_zone_ = FreelistZone::GONE;
}
DefaultMemchunks *Http2Session::get_request_buf() { return &wb_; }
void Http2Session::on_timeout() {
switch (state_) {
case PROXY_CONNECTING: {
case Http2SessionState::PROXY_CONNECTING: {
auto worker_blocker = worker_->get_connect_blocker();
worker_blocker->on_failure();
break;
}
case CONNECTING: {
case Http2SessionState::CONNECTING:
SSLOG(WARN, this) << "Connect time out; addr="
<< util::to_numeric_addr(raddr_);
downstream_failure(addr_, raddr_);
break;
}
default:
break;
}
}
@@ -2405,4 +2418,28 @@ void Http2Session::check_retire() {
const Address *Http2Session::get_raddr() const { return raddr_; }
void Http2Session::on_settings_received(const nghttp2_frame *frame) {
// TODO This effectively disallows nghttpx to change its behaviour
// based on the 2nd SETTINGS.
if (settings_recved_) {
return;
}
settings_recved_ = true;
for (size_t i = 0; i < frame->settings.niv; ++i) {
auto &ent = frame->settings.iv[i];
if (ent.settings_id == NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL) {
allow_connect_proto_ = true;
break;
}
}
submit_pending_requests();
}
bool Http2Session::get_allow_connect_proto() const {
return allow_connect_proto_;
}
} // namespace shrpx

View File

@@ -58,18 +58,46 @@ struct StreamData {
Http2DownstreamConnection *dconn;
};
enum FreelistZone {
enum class FreelistZone {
// Http2Session object is not linked in any freelist.
FREELIST_ZONE_NONE,
NONE,
// Http2Session object is linked in group scope
// http2_avail_freelist.
FREELIST_ZONE_AVAIL,
AVAIL,
// Http2Session object is linked in address scope
// http2_extra_freelist.
FREELIST_ZONE_EXTRA,
EXTRA,
// Http2Session object is about to be deleted, and it does not
// belong to any linked list.
FREELIST_ZONE_GONE
GONE
};
enum class Http2SessionState {
// Disconnected
DISCONNECTED,
// Connecting proxy and making CONNECT request
PROXY_CONNECTING,
// Tunnel is established with proxy
PROXY_CONNECTED,
// Establishing tunnel is failed
PROXY_FAILED,
// Connecting to downstream and/or performing SSL/TLS handshake
CONNECTING,
// Connected to downstream
CONNECTED,
// Connection is started to fail
CONNECT_FAILING,
// Resolving host name
RESOLVING_NAME,
};
enum class ConnectionCheck {
// Connection checking is not required
NONE,
// Connection checking is required
REQUIRED,
// Connection checking has been started
STARTED,
};
class Http2Session {
@@ -135,8 +163,8 @@ public:
ev_io *get_wev();
int get_state() const;
void set_state(int state);
Http2SessionState get_state() const;
void set_state(Http2SessionState state);
void start_settings_timer();
void stop_settings_timer();
@@ -146,7 +174,7 @@ public:
int consume(int32_t stream_id, size_t len);
// Returns true if request can be issued on downstream connection.
bool can_push_request() const;
bool can_push_request(const Downstream *downstream) const;
// Initiates the connection checking if downstream connection has
// been established and connection checking is required.
void start_checking_connection();
@@ -159,8 +187,8 @@ public:
// reset_connection_check_timer() is called.
void connection_alive();
// Change connection check state.
void set_connection_check_state(int state);
int get_connection_check_state() const;
void set_connection_check_state(ConnectionCheck state);
ConnectionCheck get_connection_check_state() const;
bool should_hard_fail() const;
@@ -212,33 +240,11 @@ public:
// Returns address used to connect to backend. Could be nullptr.
const Address *get_raddr() const;
enum {
// Disconnected
DISCONNECTED,
// Connecting proxy and making CONNECT request
PROXY_CONNECTING,
// Tunnel is established with proxy
PROXY_CONNECTED,
// Establishing tunnel is failed
PROXY_FAILED,
// Connecting to downstream and/or performing SSL/TLS handshake
CONNECTING,
// Connected to downstream
CONNECTED,
// Connection is started to fail
CONNECT_FAILING,
// Resolving host name
RESOLVING_NAME,
};
// This is called when SETTINGS frame without ACK flag set is
// received.
void on_settings_received(const nghttp2_frame *frame);
enum {
// Connection checking is not required
CONNECTION_CHECK_NONE,
// Connection checking is required
CONNECTION_CHECK_REQUIRED,
// Connection checking has been started
CONNECTION_CHECK_STARTED
};
bool get_allow_connect_proto() const;
using ReadBuf = Buffer<8_k>;
@@ -249,7 +255,7 @@ private:
DefaultMemchunks wb_;
ev_timer settings_timer_;
// This timer has 2 purpose: when it first timeout, set
// connection_check_state_ = CONNECTION_CHECK_REQUIRED. After
// connection_check_state_ = ConnectionCheck::REQUIRED. After
// connection check has started, this timer is started again and
// traps PING ACK timeout.
ev_timer connchk_timer_;
@@ -277,9 +283,13 @@ private:
// Resolved IP address if dns parameter is used
std::unique_ptr<Address> resolved_addr_;
std::unique_ptr<DNSQuery> dns_query_;
int state_;
int connection_check_state_;
int freelist_zone_;
Http2SessionState state_;
ConnectionCheck connection_check_state_;
FreelistZone freelist_zone_;
// true if SETTINGS without ACK is received from peer.
bool settings_recved_;
// true if peer enables RFC 8441 CONNECT protocol.
bool allow_connect_proto_;
};
nghttp2_session_callbacks *create_http2_downstream_callbacks();

View File

@@ -77,7 +77,7 @@ int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
req.unconsumed_body_length = 0;
if (downstream->get_request_state() == Downstream::CONNECT_FAIL) {
if (downstream->get_request_state() == DownstreamState::CONNECT_FAIL) {
upstream->remove_downstream(downstream);
// downstream was deleted
@@ -89,7 +89,7 @@ int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
downstream->detach_downstream_connection();
}
downstream->set_request_state(Downstream::STREAM_CLOSED);
downstream->set_request_state(DownstreamState::STREAM_CLOSED);
// At this point, downstream read may be paused.
@@ -187,7 +187,7 @@ int on_header_callback2(nghttp2_session *session, const nghttp2_frame *frame,
if (req.fs.buffer_size() + namebuf.len + valuebuf.len >
httpconf.request_header_field_buffer ||
req.fs.num_fields() >= httpconf.max_request_header_fields) {
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
return 0;
}
@@ -278,8 +278,8 @@ int on_begin_headers_callback(nghttp2_session *session,
} // namespace
void Http2Upstream::on_start_request(const nghttp2_frame *frame) {
auto downstream = make_unique<Downstream>(this, handler_->get_mcpool(),
frame->hd.stream_id);
auto downstream = std::make_unique<Downstream>(this, handler_->get_mcpool(),
frame->hd.stream_id);
nghttp2_session_set_stream_user_data(session_, frame->hd.stream_id,
downstream.get());
@@ -312,7 +312,7 @@ int Http2Upstream::on_request_headers(Downstream *downstream,
auto &req = downstream->request();
req.tstamp = lgconf->tstamp;
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
return 0;
}
@@ -358,8 +358,8 @@ int Http2Upstream::on_request_headers(Downstream *downstream,
auto faddr = handler_->get_upstream_addr();
// For HTTP/2 proxy, we require :authority.
if (method_token != HTTP_CONNECT && config->http2_proxy && !faddr->alt_mode &&
!authority) {
if (method_token != HTTP_CONNECT && config->http2_proxy &&
faddr->alt_mode == UpstreamAltMode::NONE && !authority) {
rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR);
return 0;
}
@@ -383,7 +383,8 @@ int Http2Upstream::on_request_headers(Downstream *downstream,
if (method_token == HTTP_OPTIONS &&
path->value == StringRef::from_lit("*")) {
// Server-wide OPTIONS request. Path is empty.
} else if (config->http2_proxy && !faddr->alt_mode) {
} else if (config->http2_proxy &&
faddr->alt_mode == UpstreamAltMode::NONE) {
req.path = path->value;
} else {
req.path = http2::rewrite_clean_path(downstream->get_block_allocator(),
@@ -391,6 +392,17 @@ int Http2Upstream::on_request_headers(Downstream *downstream,
}
}
auto connect_proto = req.fs.header(http2::HD__PROTOCOL);
if (connect_proto) {
if (connect_proto->value != "websocket") {
if (error_reply(downstream, 400) != 0) {
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
return 0;
}
req.connect_proto = ConnectProto::WEBSOCKET;
}
if (!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) {
req.http2_expect_body = true;
} else if (req.fs.content_length == -1) {
@@ -401,7 +413,7 @@ int Http2Upstream::on_request_headers(Downstream *downstream,
downstream->inspect_http2_request();
downstream->set_request_state(Downstream::HEADER_COMPLETE);
downstream->set_request_state(DownstreamState::HEADER_COMPLETE);
#ifdef HAVE_MRUBY
auto upstream = downstream->get_upstream();
@@ -420,10 +432,10 @@ int Http2Upstream::on_request_headers(Downstream *downstream,
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
downstream->disable_upstream_rtimer();
downstream->set_request_state(Downstream::MSG_COMPLETE);
downstream->set_request_state(DownstreamState::MSG_COMPLETE);
}
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
return 0;
}
@@ -455,7 +467,7 @@ void Http2Upstream::initiate_downstream(Downstream *downstream) {
rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
}
downstream->set_request_state(Downstream::CONNECT_FAIL);
downstream->set_request_state(DownstreamState::CONNECT_FAIL);
downstream_queue_.mark_failure(downstream);
return;
@@ -471,7 +483,7 @@ void Http2Upstream::initiate_downstream(Downstream *downstream) {
rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
}
downstream->set_request_state(Downstream::CONNECT_FAIL);
downstream->set_request_state(DownstreamState::CONNECT_FAIL);
downstream_queue_.mark_failure(downstream);
@@ -492,7 +504,7 @@ void Http2Upstream::initiate_downstream(Downstream *downstream) {
return;
}
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
return;
}
}
@@ -544,12 +556,12 @@ int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
downstream->disable_upstream_rtimer();
if (downstream->end_upload_data() != 0) {
if (downstream->get_response_state() != Downstream::MSG_COMPLETE) {
if (downstream->get_response_state() != DownstreamState::MSG_COMPLETE) {
upstream->rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
}
}
downstream->set_request_state(Downstream::MSG_COMPLETE);
downstream->set_request_state(DownstreamState::MSG_COMPLETE);
}
return 0;
@@ -573,12 +585,12 @@ int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
downstream->disable_upstream_rtimer();
if (downstream->end_upload_data() != 0) {
if (downstream->get_response_state() != Downstream::MSG_COMPLETE) {
if (downstream->get_response_state() != DownstreamState::MSG_COMPLETE) {
upstream->rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
}
}
downstream->set_request_state(Downstream::MSG_COMPLETE);
downstream->set_request_state(DownstreamState::MSG_COMPLETE);
}
return 0;
@@ -625,7 +637,7 @@ int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
downstream->reset_upstream_rtimer();
if (downstream->push_upload_data_chunk(data, len) != 0) {
if (downstream->get_response_state() != Downstream::MSG_COMPLETE) {
if (downstream->get_response_state() != DownstreamState::MSG_COMPLETE) {
upstream->rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
}
@@ -694,7 +706,7 @@ int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame,
return 0;
}
auto promised_downstream = make_unique<Downstream>(
auto promised_downstream = std::make_unique<Downstream>(
upstream, handler->get_mcpool(), promised_stream_id);
auto &req = promised_downstream->request();
@@ -745,7 +757,7 @@ int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame,
promised_downstream->inspect_http2_request();
promised_downstream->set_request_state(Downstream::MSG_COMPLETE);
promised_downstream->set_request_state(DownstreamState::MSG_COMPLETE);
// a bit weird but start_downstream() expects that given
// downstream is in pending queue.
@@ -1015,29 +1027,36 @@ Http2Upstream::Http2Upstream(ClientHandler *handler)
auto faddr = handler_->get_upstream_addr();
rv = nghttp2_session_server_new2(
&session_, http2conf.upstream.callbacks, this,
faddr->alt_mode ? http2conf.upstream.alt_mode_option
: http2conf.upstream.option);
rv =
nghttp2_session_server_new2(&session_, http2conf.upstream.callbacks, this,
faddr->alt_mode != UpstreamAltMode::NONE
? http2conf.upstream.alt_mode_option
: http2conf.upstream.option);
assert(rv == 0);
flow_control_ = true;
// TODO Maybe call from outside?
std::array<nghttp2_settings_entry, 3> entry;
std::array<nghttp2_settings_entry, 4> entry;
size_t nentry = 2;
entry[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
entry[0].value = http2conf.upstream.max_concurrent_streams;
entry[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
if (faddr->alt_mode) {
if (faddr->alt_mode != UpstreamAltMode::NONE) {
entry[1].value = (1u << 31) - 1;
} else {
entry[1].value = http2conf.upstream.window_size;
}
if (!config->http2_proxy) {
entry[nentry].settings_id = NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL;
entry[nentry].value = 1;
++nentry;
}
if (http2conf.upstream.decoder_dynamic_table_size !=
NGHTTP2_DEFAULT_HEADER_TABLE_SIZE) {
entry[nentry].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
@@ -1053,7 +1072,7 @@ Http2Upstream::Http2Upstream(ClientHandler *handler)
}
auto window_size =
faddr->alt_mode
faddr->alt_mode != UpstreamAltMode::NONE
? std::numeric_limits<int32_t>::max()
: http2conf.upstream.optimize_window_size
? std::min(http2conf.upstream.connection_window_size,
@@ -1169,7 +1188,7 @@ int Http2Upstream::on_write() {
if (http2conf.upstream.optimize_window_size) {
auto faddr = handler_->get_upstream_addr();
if (!faddr->alt_mode) {
if (faddr->alt_mode == UpstreamAltMode::NONE) {
auto window_size = std::min(http2conf.upstream.connection_window_size,
static_cast<int32_t>(hint.rwin * 2));
@@ -1222,7 +1241,7 @@ ClientHandler *Http2Upstream::get_client_handler() const { return handler_; }
int Http2Upstream::downstream_read(DownstreamConnection *dconn) {
auto downstream = dconn->get_downstream();
if (downstream->get_response_state() == Downstream::MSG_RESET) {
if (downstream->get_response_state() == DownstreamState::MSG_RESET) {
// The downstream stream was reset (canceled). In this case,
// RST_STREAM to the upstream and delete downstream connection
// here. Deleting downstream will be taken place at
@@ -1233,7 +1252,8 @@ int Http2Upstream::downstream_read(DownstreamConnection *dconn) {
downstream->pop_downstream_connection();
// dconn was deleted
dconn = nullptr;
} else if (downstream->get_response_state() == Downstream::MSG_BAD_HEADER) {
} else if (downstream->get_response_state() ==
DownstreamState::MSG_BAD_HEADER) {
if (error_reply(downstream, 502) != 0) {
return -1;
}
@@ -1297,19 +1317,20 @@ int Http2Upstream::downstream_eof(DownstreamConnection *dconn) {
// dconn was deleted
dconn = nullptr;
// downstream wil be deleted in on_stream_close_callback.
if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
if (downstream->get_response_state() == DownstreamState::HEADER_COMPLETE) {
// Server may indicate the end of the request by EOF
if (LOG_ENABLED(INFO)) {
ULOG(INFO, this) << "Downstream body was ended by EOF";
}
downstream->set_response_state(Downstream::MSG_COMPLETE);
downstream->set_response_state(DownstreamState::MSG_COMPLETE);
// For tunneled connection, MSG_COMPLETE signals
// downstream_data_read_callback to send RST_STREAM after pending
// response body is sent. This is needed to ensure that RST_STREAM
// is sent after all pending data are sent.
on_downstream_body_complete(downstream);
} else if (downstream->get_response_state() != Downstream::MSG_COMPLETE) {
} else if (downstream->get_response_state() !=
DownstreamState::MSG_COMPLETE) {
// If stream was not closed, then we set MSG_COMPLETE and let
// on_stream_close_callback delete downstream.
if (error_reply(downstream, 502) != 0) {
@@ -1341,7 +1362,7 @@ int Http2Upstream::downstream_error(DownstreamConnection *dconn, int events) {
// dconn was deleted
dconn = nullptr;
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
// For SSL tunneling, we issue RST_STREAM. For other types of
// stream, we don't have to do anything since response was
// complete.
@@ -1349,7 +1370,7 @@ int Http2Upstream::downstream_error(DownstreamConnection *dconn, int events) {
rst_stream(downstream, NGHTTP2_NO_ERROR);
}
} else {
if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
if (downstream->get_response_state() == DownstreamState::HEADER_COMPLETE) {
if (downstream->get_upgraded()) {
on_downstream_body_complete(downstream);
} else {
@@ -1366,7 +1387,7 @@ int Http2Upstream::downstream_error(DownstreamConnection *dconn, int events) {
return -1;
}
}
downstream->set_response_state(Downstream::MSG_COMPLETE);
downstream->set_response_state(DownstreamState::MSG_COMPLETE);
}
handler_->signal_write();
// At this point, downstream may be deleted.
@@ -1433,7 +1454,7 @@ ssize_t downstream_data_read_callback(nghttp2_session *session,
*data_flags |= NGHTTP2_DATA_FLAG_NO_COPY;
if (body_empty &&
downstream->get_response_state() == Downstream::MSG_COMPLETE) {
downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
@@ -1530,7 +1551,7 @@ int Http2Upstream::send_reply(Downstream *downstream, const uint8_t *body,
buf->append(body, bodylen);
downstream->set_response_state(Downstream::MSG_COMPLETE);
downstream->set_response_state(DownstreamState::MSG_COMPLETE);
if (data_prd_ptr) {
downstream->reset_upstream_wtimer();
@@ -1550,7 +1571,7 @@ int Http2Upstream::error_reply(Downstream *downstream,
resp.http_status = status_code;
auto body = downstream->get_response_buf();
body->append(html);
downstream->set_response_state(Downstream::MSG_COMPLETE);
downstream->set_response_state(DownstreamState::MSG_COMPLETE);
nghttp2_data_provider data_prd;
data_prd.source.ptr = downstream;
@@ -1648,7 +1669,7 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
return -1;
}
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
return -1;
}
}
@@ -1664,7 +1685,7 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
return -1;
}
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
return -1;
}
}
@@ -1704,11 +1725,11 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
nva.reserve(resp.fs.headers().size() + 5 +
httpconf.add_response_headers.size());
auto response_status = http2::stringify_status(balloc, resp.http_status);
nva.push_back(http2::make_nv_ls_nocopy(":status", response_status));
if (downstream->get_non_final_response()) {
auto response_status = http2::stringify_status(balloc, resp.http_status);
nva.push_back(http2::make_nv_ls_nocopy(":status", response_status));
http2::copy_headers_to_nva_nocopy(nva, resp.fs.headers(),
http2::HDOP_STRIP_ALL);
@@ -1730,8 +1751,19 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
return 0;
}
http2::copy_headers_to_nva_nocopy(
nva, resp.fs.headers(), http2::HDOP_STRIP_ALL & ~http2::HDOP_STRIP_VIA);
auto striphd_flags = http2::HDOP_STRIP_ALL & ~http2::HDOP_STRIP_VIA;
StringRef response_status;
if (req.connect_proto == ConnectProto::WEBSOCKET && resp.http_status == 101) {
response_status = http2::stringify_status(balloc, 200);
striphd_flags |= http2::HDOP_STRIP_SEC_WEBSOCKET_ACCEPT;
} else {
response_status = http2::stringify_status(balloc, resp.http_status);
}
nva.push_back(http2::make_nv_ls_nocopy(":status", response_status));
http2::copy_headers_to_nva_nocopy(nva, resp.fs.headers(), striphd_flags);
if (!config->http2_proxy && !httpconf.no_server_rewrite) {
nva.push_back(http2::make_nv_ls_nocopy("server", httpconf.server_name));
@@ -1742,7 +1774,7 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
}
}
if (req.method != HTTP_CONNECT || !downstream->get_upgraded()) {
if (!req.regular_connect_method() || !downstream->get_upgraded()) {
auto affinity_cookie = downstream->get_affinity_cookie_to_send();
if (affinity_cookie) {
auto dconn = downstream->get_downstream_connection();
@@ -1916,7 +1948,7 @@ int Http2Upstream::on_downstream_abort_request_with_https_redirect(
int Http2Upstream::redirect_to_https(Downstream *downstream) {
auto &req = downstream->request();
if (req.method == HTTP_CONNECT || req.scheme != "http") {
if (req.regular_connect_method() || req.scheme != "http") {
return error_reply(downstream, 400);
}
@@ -1952,7 +1984,7 @@ int Http2Upstream::consume(int32_t stream_id, size_t len) {
auto faddr = handler_->get_upstream_addr();
if (faddr->alt_mode) {
if (faddr->alt_mode != UpstreamAltMode::NONE) {
return 0;
}
@@ -1993,7 +2025,7 @@ int Http2Upstream::on_timeout(Downstream *downstream) {
void Http2Upstream::on_handler_delete() {
for (auto d = downstream_queue_.get_downstreams(); d; d = d->dlnext) {
if (d->get_dispatch_state() == Downstream::DISPATCH_ACTIVE &&
if (d->get_dispatch_state() == DispatchState::ACTIVE &&
d->accesslog_ready()) {
handler_->write_accesslog(d);
}
@@ -2003,10 +2035,10 @@ void Http2Upstream::on_handler_delete() {
int Http2Upstream::on_downstream_reset(Downstream *downstream, bool no_retry) {
int rv;
if (downstream->get_dispatch_state() != Downstream::DISPATCH_ACTIVE) {
if (downstream->get_dispatch_state() != DispatchState::ACTIVE) {
// This is error condition when we failed push_request_headers()
// in initiate_downstream(). Otherwise, we have
// Downstream::DISPATCH_ACTIVE state, or we did not set
// DispatchState::ACTIVE state, or we did not set
// DownstreamConnection.
downstream->pop_downstream_connection();
handler_->signal_write();
@@ -2015,7 +2047,7 @@ int Http2Upstream::on_downstream_reset(Downstream *downstream, bool no_retry) {
}
if (!downstream->request_submission_ready()) {
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
// We have got all response body already. Send it off.
downstream->pop_downstream_connection();
return 0;
@@ -2267,7 +2299,7 @@ Http2Upstream::on_downstream_push_promise(Downstream *downstream,
// promised_stream_id is for backend HTTP/2 session, not for
// frontend.
auto promised_downstream =
make_unique<Downstream>(this, handler_->get_mcpool(), 0);
std::make_unique<Downstream>(this, handler_->get_mcpool(), 0);
auto &promised_req = promised_downstream->request();
promised_downstream->set_downstream_stream_id(promised_stream_id);

View File

@@ -39,6 +39,7 @@
#include "shrpx_log.h"
#include "http2.h"
#include "util.h"
#include "ssl_compat.h"
using namespace nghttp2;
@@ -91,7 +92,7 @@ void retry_downstream_connection(Downstream *downstream,
// request in request buffer.
auto ndconn = handler->get_downstream_connection(
rv, downstream,
downstream->get_request_header_sent() ? PROTO_HTTP1 : PROTO_NONE);
downstream->get_request_header_sent() ? Proto::HTTP1 : Proto::NONE);
if (ndconn) {
if (downstream->attach_downstream_connection(std::move(ndconn)) == 0 &&
downstream->push_request_headers() == 0) {
@@ -99,7 +100,7 @@ void retry_downstream_connection(Downstream *downstream,
}
}
downstream->set_request_state(Downstream::CONNECT_FAIL);
downstream->set_request_state(DownstreamState::CONNECT_FAIL);
if (rv == SHRPX_ERR_TLS_REQUIRED) {
rv = upstream->on_downstream_abort_request_with_https_redirect(downstream);
@@ -189,11 +190,10 @@ HttpDownstreamConnection::HttpDownstreamConnection(
const std::shared_ptr<DownstreamAddrGroup> &group, size_t initial_addr_idx,
struct ev_loop *loop, Worker *worker)
: conn_(loop, -1, nullptr, worker->get_mcpool(),
worker->get_downstream_config()->timeout.write,
worker->get_downstream_config()->timeout.read, {}, {}, connectcb,
readcb, connect_timeoutcb, this,
group->shared_addr->timeout.write, group->shared_addr->timeout.read,
{}, {}, connectcb, readcb, connect_timeoutcb, this,
get_config()->tls.dyn_rec.warmup_threshold,
get_config()->tls.dyn_rec.idle_timeout, PROTO_HTTP1),
get_config()->tls.dyn_rec.idle_timeout, Proto::HTTP1),
on_read_(&HttpDownstreamConnection::noop),
on_write_(&HttpDownstreamConnection::noop),
signal_write_(&HttpDownstreamConnection::noop),
@@ -259,7 +259,7 @@ int HttpDownstreamConnection::initiate_connection() {
// initial_addr_idx_.
size_t temp_idx = initial_addr_idx_;
auto &next_downstream = shared_addr->affinity.type == AFFINITY_NONE
auto &next_downstream = shared_addr->affinity.type == SessionAffinity::NONE
? shared_addr->next
: temp_idx;
auto end = next_downstream;
@@ -274,7 +274,7 @@ int HttpDownstreamConnection::initiate_connection() {
assert(addr->dns);
} else {
assert(addr_ == nullptr);
if (shared_addr->affinity.type == AFFINITY_NONE) {
if (shared_addr->affinity.type == SessionAffinity::NONE) {
addr = &addrs[next_downstream];
if (++next_downstream >= addrs.size()) {
next_downstream = 0;
@@ -286,7 +286,7 @@ int HttpDownstreamConnection::initiate_connection() {
}
}
if (addr->proto != PROTO_HTTP1) {
if (addr->proto != Proto::HTTP1) {
if (end == next_downstream) {
return SHRPX_ERR_NETWORK;
}
@@ -316,11 +316,12 @@ int HttpDownstreamConnection::initiate_connection() {
if (addr->dns) {
if (!check_dns_result) {
auto dns_query = make_unique<DNSQuery>(
addr->host, [this](int status, const Address *result) {
auto dns_query = std::make_unique<DNSQuery>(
addr->host,
[this](DNSResolverStatus status, const Address *result) {
int rv;
if (status == DNS_STATUS_OK) {
if (status == DNSResolverStatus::OK) {
*this->resolved_addr_ = *result;
}
@@ -335,33 +336,32 @@ int HttpDownstreamConnection::initiate_connection() {
auto dns_tracker = worker_->get_dns_tracker();
if (!resolved_addr_) {
resolved_addr_ = make_unique<Address>();
resolved_addr_ = std::make_unique<Address>();
}
rv = dns_tracker->resolve(resolved_addr_.get(), dns_query.get());
switch (rv) {
case DNS_STATUS_ERROR:
switch (dns_tracker->resolve(resolved_addr_.get(), dns_query.get())) {
case DNSResolverStatus::ERROR:
downstream_failure(addr, nullptr);
if (end == next_downstream) {
return SHRPX_ERR_NETWORK;
}
continue;
case DNS_STATUS_RUNNING:
case DNSResolverStatus::RUNNING:
dns_query_ = std::move(dns_query);
// Remember current addr
addr_ = addr;
return 0;
case DNS_STATUS_OK:
case DNSResolverStatus::OK:
break;
default:
assert(0);
}
} else {
switch (dns_query_->status) {
case DNS_STATUS_ERROR:
case DNSResolverStatus::ERROR:
dns_query_.reset();
downstream_failure(addr, nullptr);
continue;
case DNS_STATUS_OK:
case DNSResolverStatus::OK:
dns_query_.reset();
break;
default:
@@ -458,11 +458,11 @@ int HttpDownstreamConnection::initiate_connection() {
} else {
// we may set read timer cb to idle_timeoutcb. Reset again.
ev_set_cb(&conn_.rt, timeoutcb);
if (conn_.read_timeout < downstreamconf.timeout.read) {
conn_.read_timeout = downstreamconf.timeout.read;
if (conn_.read_timeout < group_->shared_addr->timeout.read) {
conn_.read_timeout = group_->shared_addr->timeout.read;
conn_.last_read = ev_now(conn_.loop);
} else {
conn_.again_rt(downstreamconf.timeout.read);
conn_.again_rt(group_->shared_addr->timeout.read);
}
ev_set_cb(&conn_.rev, readcb);
@@ -488,7 +488,7 @@ int HttpDownstreamConnection::push_request_headers() {
auto &balloc = downstream_->get_block_allocator();
auto connect_method = req.method == HTTP_CONNECT;
auto connect_method = req.regular_connect_method();
auto config = get_config();
auto &httpconf = config->http;
@@ -512,7 +512,8 @@ int HttpDownstreamConnection::push_request_headers() {
auto buf = downstream_->get_request_buf();
// Assume that method and request path do not contain \r\n.
auto meth = http2::to_method_string(req.method);
auto meth = http2::to_method_string(
req.connect_proto == ConnectProto::WEBSOCKET ? HTTP_GET : req.method);
buf->append(meth);
buf->append(' ');
@@ -539,11 +540,14 @@ int HttpDownstreamConnection::push_request_headers() {
auto &fwdconf = httpconf.forwarded;
auto &xffconf = httpconf.xff;
auto &xfpconf = httpconf.xfp;
auto &earlydataconf = httpconf.early_data;
uint32_t build_flags =
(fwdconf.strip_incoming ? http2::HDOP_STRIP_FORWARDED : 0) |
(xffconf.strip_incoming ? http2::HDOP_STRIP_X_FORWARDED_FOR : 0) |
(xfpconf.strip_incoming ? http2::HDOP_STRIP_X_FORWARDED_PROTO : 0);
(xfpconf.strip_incoming ? http2::HDOP_STRIP_X_FORWARDED_PROTO : 0) |
(earlydataconf.strip_incoming ? http2::HDOP_STRIP_EARLY_DATA : 0) |
(req.http_major == 2 ? http2::HDOP_STRIP_SEC_WEBSOCKET_KEY : 0);
http2::build_http1_headers_from_headers(buf, req.fs.headers(), build_flags);
@@ -556,16 +560,30 @@ int HttpDownstreamConnection::push_request_headers() {
// set transfer-encoding only when content-length is unknown and
// request body is expected.
if (!connect_method && req.http2_expect_body && req.fs.content_length == -1) {
if (req.method != HTTP_CONNECT && req.http2_expect_body &&
req.fs.content_length == -1) {
downstream_->set_chunked_request(true);
buf->append("Transfer-Encoding: chunked\r\n");
}
if (req.connection_close) {
buf->append("Connection: close\r\n");
}
if (req.connect_proto == ConnectProto::WEBSOCKET) {
if (req.http_major == 2) {
std::array<uint8_t, 16> nonce;
util::random_bytes(std::begin(nonce), std::end(nonce),
worker_->get_randgen());
auto iov = make_byte_ref(balloc, base64::encode_length(nonce.size()) + 1);
auto p = base64::encode(std::begin(nonce), std::end(nonce), iov.base);
*p = '\0';
auto key = StringRef{iov.base, p};
downstream_->set_ws_key(key);
if (!connect_method && req.upgrade_request) {
buf->append("Sec-Websocket-Key: ");
buf->append(key);
buf->append("\r\n");
}
buf->append("Upgrade: websocket\r\nConnection: Upgrade\r\n");
} else if (!connect_method && req.upgrade_request) {
auto connection = req.fs.header(http2::HD_CONNECTION);
if (connection) {
buf->append("Connection: ");
@@ -579,11 +597,21 @@ int HttpDownstreamConnection::push_request_headers() {
buf->append((*upgrade).value);
buf->append("\r\n");
}
} else if (req.connection_close) {
buf->append("Connection: close\r\n");
}
auto upstream = downstream_->get_upstream();
auto handler = upstream->get_client_handler();
#if OPENSSL_1_1_1_API
auto conn = handler->get_connection();
if (conn->tls.ssl && !SSL_is_init_finished(conn->tls.ssl)) {
buf->append("Early-Data: 1\r\n");
}
#endif // OPENSSL_1_1_1_API
auto fwd =
fwdconf.strip_incoming ? nullptr : req.fs.header(http2::HD_FORWARDED);
@@ -697,7 +725,8 @@ int HttpDownstreamConnection::push_request_headers() {
// Don't call signal_write() if we anticipate request body. We call
// signal_write() when we received request body chunk, and it
// enables us to send headers and data in one writev system call.
if (connect_method || downstream_->get_blocked_request_buf()->rleft() ||
if (req.method == HTTP_CONNECT ||
downstream_->get_blocked_request_buf()->rleft() ||
(!req.http2_expect_body && req.fs.content_length == 0)) {
signal_write();
}
@@ -781,7 +810,7 @@ void remove_from_pool(HttpDownstreamConnection *dconn) {
auto &group = dconn->get_downstream_addr_group();
auto &shared_addr = group->shared_addr;
if (shared_addr->affinity.type == AFFINITY_NONE) {
if (shared_addr->affinity.type == SessionAffinity::NONE) {
auto &dconn_pool =
dconn->get_downstream_addr_group()->shared_addr->dconn_pool;
dconn_pool.remove_downstream_connection(dconn);
@@ -872,7 +901,7 @@ namespace {
int htp_msg_begincb(http_parser *htp) {
auto downstream = static_cast<Downstream *>(htp->data);
if (downstream->get_response_state() != Downstream::INITIAL) {
if (downstream->get_response_state() != DownstreamState::INITIAL) {
return -1;
}
@@ -909,7 +938,7 @@ int htp_hdrs_completecb(http_parser *htp) {
// Server MUST NOT send Transfer-Encoding with a status code 1xx or
// 204. Also server MUST NOT send Transfer-Encoding with a status
// code 200 to a CONNECT request. Same holds true with
// code 2xx to a CONNECT request. Same holds true with
// Content-Length.
if (resp.http_status == 204) {
if (resp.fs.header(http2::HD_TRANSFER_ENCODING)) {
@@ -931,19 +960,19 @@ int htp_hdrs_completecb(http_parser *htp) {
return -1;
}
} else if (resp.http_status / 100 == 1 ||
(resp.http_status == 200 && req.method == HTTP_CONNECT)) {
(resp.http_status / 100 == 2 && req.method == HTTP_CONNECT)) {
if (resp.fs.header(http2::HD_CONTENT_LENGTH) ||
resp.fs.header(http2::HD_TRANSFER_ENCODING)) {
return -1;
}
} else if (resp.fs.parse_content_length() != 0) {
downstream->set_response_state(Downstream::MSG_BAD_HEADER);
downstream->set_response_state(DownstreamState::MSG_BAD_HEADER);
return -1;
}
// Check upgrade before processing non-final response, since if
// upgrade succeeded, 101 response is treated as final in nghttpx.
downstream->check_upgrade_fulfilled();
downstream->check_upgrade_fulfilled_http1();
if (downstream->get_non_final_response()) {
// Reset content-length because we reuse same Downstream for the
@@ -963,7 +992,7 @@ int htp_hdrs_completecb(http_parser *htp) {
}
resp.connection_close = !http_should_keep_alive(htp);
downstream->set_response_state(Downstream::HEADER_COMPLETE);
downstream->set_response_state(DownstreamState::HEADER_COMPLETE);
downstream->inspect_http1_response();
if (downstream->get_upgraded()) {
// content-length must be ignored for upgraded connection.
@@ -971,6 +1000,11 @@ int htp_hdrs_completecb(http_parser *htp) {
resp.connection_close = true;
// transfer-encoding not applied to upgraded connection
downstream->set_chunked_response(false);
} else if (http2::legacy_http1(req.http_major, req.http_minor)) {
if (resp.fs.content_length == -1) {
resp.connection_close = true;
}
downstream->set_chunked_response(false);
} else if (!downstream->expect_response_body()) {
downstream->set_chunked_response(false);
}
@@ -989,7 +1023,7 @@ int htp_hdrs_completecb(http_parser *htp) {
if (upstream->resume_read(SHRPX_NO_BUFFER, downstream, 0) != 0) {
return -1;
}
downstream->set_request_state(Downstream::HEADER_COMPLETE);
downstream->set_request_state(DownstreamState::HEADER_COMPLETE);
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "HTTP upgrade success. stream_id="
<< downstream->get_stream_id();
@@ -1052,7 +1086,7 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
return -1;
}
if (downstream->get_response_state() == Downstream::INITIAL) {
if (downstream->get_response_state() == DownstreamState::INITIAL) {
if (resp.fs.header_key_prev()) {
resp.fs.append_last_header_key(data, len);
} else {
@@ -1089,7 +1123,7 @@ int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) {
return -1;
}
if (downstream->get_response_state() == Downstream::INITIAL) {
if (downstream->get_response_state() == DownstreamState::INITIAL) {
resp.fs.append_last_header_value(data, len);
} else {
resp.fs.append_last_trailer_value(data, len);
@@ -1129,7 +1163,7 @@ int htp_msg_completecb(http_parser *htp) {
return 0;
}
downstream->set_response_state(Downstream::MSG_COMPLETE);
downstream->set_response_state(DownstreamState::MSG_COMPLETE);
// Block reading another response message from (broken?)
// server. This callback is not called if the connection is
// tunneled.
@@ -1401,7 +1435,7 @@ int HttpDownstreamConnection::process_input(const uint8_t *data,
if (htperr != HPE_OK) {
// Handling early return (in other words, response was hijacked by
// mruby scripting).
if (downstream_->get_response_state() == Downstream::MSG_COMPLETE) {
if (downstream_->get_response_state() == DownstreamState::MSG_COMPLETE) {
return SHRPX_ERR_DCONN_CANCELED;
}
@@ -1459,10 +1493,8 @@ int HttpDownstreamConnection::connected() {
DCLOG(INFO, this) << "Connected to downstream host";
}
auto &downstreamconf = *get_config()->conn.downstream;
// Reset timeout for write. Previously, we set timeout for connect.
conn_.wt.repeat = downstreamconf.timeout.write;
conn_.wt.repeat = group_->shared_addr->timeout.write;
ev_timer_again(conn_.loop, &conn_.wt);
conn_.rlimit.startw();

View File

@@ -44,6 +44,7 @@
#include "http2.h"
#include "util.h"
#include "template.h"
#include "base64.h"
using namespace nghttp2;
@@ -70,7 +71,8 @@ void HttpsUpstream::on_start_request() {
}
reset_current_header_length();
auto downstream = make_unique<Downstream>(this, handler_->get_mcpool(), 0);
auto downstream =
std::make_unique<Downstream>(this, handler_->get_mcpool(), 0);
attach_downstream(std::move(downstream));
@@ -109,8 +111,9 @@ int htp_uricb(http_parser *htp, const char *data, size_t len) {
ULOG(INFO, upstream) << "Too large URI size="
<< req.fs.buffer_size() + len;
}
assert(downstream->get_request_state() == Downstream::INITIAL);
downstream->set_request_state(Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE);
assert(downstream->get_request_state() == DownstreamState::INITIAL);
downstream->set_request_state(
DownstreamState::HTTP1_REQUEST_HEADER_TOO_LARGE);
return -1;
}
@@ -139,12 +142,13 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
ULOG(INFO, upstream) << "Too large header block size="
<< req.fs.buffer_size() + len;
}
if (downstream->get_request_state() == Downstream::INITIAL) {
downstream->set_request_state(Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE);
if (downstream->get_request_state() == DownstreamState::INITIAL) {
downstream->set_request_state(
DownstreamState::HTTP1_REQUEST_HEADER_TOO_LARGE);
}
return -1;
}
if (downstream->get_request_state() == Downstream::INITIAL) {
if (downstream->get_request_state() == DownstreamState::INITIAL) {
if (req.fs.header_key_prev()) {
req.fs.append_last_header_key(data, len);
} else {
@@ -154,7 +158,7 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
<< "Too many header field num=" << req.fs.num_fields() + 1;
}
downstream->set_request_state(
Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE);
DownstreamState::HTTP1_REQUEST_HEADER_TOO_LARGE);
return -1;
}
req.fs.alloc_add_header_name(StringRef{data, len});
@@ -190,12 +194,13 @@ int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) {
ULOG(INFO, upstream) << "Too large header block size="
<< req.fs.buffer_size() + len;
}
if (downstream->get_request_state() == Downstream::INITIAL) {
downstream->set_request_state(Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE);
if (downstream->get_request_state() == DownstreamState::INITIAL) {
downstream->set_request_state(
DownstreamState::HTTP1_REQUEST_HEADER_TOO_LARGE);
}
return -1;
}
if (downstream->get_request_state() == Downstream::INITIAL) {
if (downstream->get_request_state() == DownstreamState::INITIAL) {
req.fs.append_last_header_value(data, len);
} else {
req.fs.append_last_trailer_value(data, len);
@@ -395,7 +400,7 @@ int htp_hdrs_completecb(http_parser *htp) {
}
}
downstream->set_request_state(Downstream::HEADER_COMPLETE);
downstream->set_request_state(DownstreamState::HEADER_COMPLETE);
#ifdef HAVE_MRUBY
auto worker = handler->get_worker();
@@ -411,12 +416,13 @@ int htp_hdrs_completecb(http_parser *htp) {
// mruby hook may change method value
if (req.no_authority && config->http2_proxy && !faddr->alt_mode) {
if (req.no_authority && config->http2_proxy &&
faddr->alt_mode == UpstreamAltMode::NONE) {
// Request URI should be absolute-form for client proxy mode
return -1;
}
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
return 0;
}
@@ -426,7 +432,7 @@ int htp_hdrs_completecb(http_parser *htp) {
if (rv == SHRPX_ERR_TLS_REQUIRED) {
upstream->redirect_to_https(downstream);
}
downstream->set_request_state(Downstream::CONNECT_FAIL);
downstream->set_request_state(DownstreamState::CONNECT_FAIL);
return -1;
}
@@ -435,7 +441,7 @@ int htp_hdrs_completecb(http_parser *htp) {
auto dconn_ptr = dconn.get();
#endif // HAVE_MRUBY
if (downstream->attach_downstream_connection(std::move(dconn)) != 0) {
downstream->set_request_state(Downstream::CONNECT_FAIL);
downstream->set_request_state(DownstreamState::CONNECT_FAIL);
return -1;
}
@@ -450,7 +456,7 @@ int htp_hdrs_completecb(http_parser *htp) {
return -1;
}
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
return 0;
}
}
@@ -462,7 +468,7 @@ int htp_hdrs_completecb(http_parser *htp) {
return -1;
}
if (faddr->alt_mode) {
if (faddr->alt_mode != UpstreamAltMode::NONE) {
// Normally, we forward expect: 100-continue to backend server,
// and let them decide whether responds with 100 Continue or not.
// For alternative mode, we have no backend, so just send 100
@@ -491,7 +497,7 @@ int htp_bodycb(http_parser *htp, const char *data, size_t len) {
if (rv != 0) {
// Ignore error if response has been completed. We will end up in
// htp_msg_completecb, and request will end gracefully.
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
return 0;
}
@@ -510,10 +516,10 @@ int htp_msg_completecb(http_parser *htp) {
}
auto handler = upstream->get_client_handler();
auto downstream = upstream->get_downstream();
downstream->set_request_state(Downstream::MSG_COMPLETE);
downstream->set_request_state(DownstreamState::MSG_COMPLETE);
rv = downstream->end_upload_data();
if (rv != 0) {
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
// Here both response and request were completed. One of the
// reason why end_upload_data() failed is when we sent response
// in request phase hook. We only delete and proceed to the
@@ -593,8 +599,8 @@ int HttpsUpstream::on_read() {
if (downstream) {
// To avoid reading next pipelined request
switch (downstream->get_request_state()) {
case Downstream::INITIAL:
case Downstream::HEADER_COMPLETE:
case DownstreamState::INITIAL:
case DownstreamState::HEADER_COMPLETE:
break;
default:
return 0;
@@ -622,8 +628,8 @@ int HttpsUpstream::on_read() {
// We may pause parser in htp_msg_completecb when both side are
// completed. Signal write, so that we can run on_write().
if (downstream &&
downstream->get_request_state() == Downstream::MSG_COMPLETE &&
downstream->get_response_state() == Downstream::MSG_COMPLETE) {
downstream->get_request_state() == DownstreamState::MSG_COMPLETE &&
downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
handler_->signal_write();
}
return 0;
@@ -636,7 +642,8 @@ int HttpsUpstream::on_read() {
<< http_errno_description(htperr);
}
if (downstream && downstream->get_response_state() != Downstream::INITIAL) {
if (downstream &&
downstream->get_response_state() != DownstreamState::INITIAL) {
handler_->set_should_close_after_write(true);
handler_->signal_write();
return 0;
@@ -649,10 +656,10 @@ int HttpsUpstream::on_read() {
} else if (downstream) {
status_code = downstream->response().http_status;
if (status_code == 0) {
if (downstream->get_request_state() == Downstream::CONNECT_FAIL) {
if (downstream->get_request_state() == DownstreamState::CONNECT_FAIL) {
status_code = 502;
} else if (downstream->get_request_state() ==
Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE) {
DownstreamState::HTTP1_REQUEST_HEADER_TOO_LARGE) {
status_code = 431;
} else {
status_code = 400;
@@ -698,7 +705,7 @@ int HttpsUpstream::on_write() {
// We need to postpone detachment until all data are sent so that
// we can notify nghttp2 library all data consumed.
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
if (downstream->can_detach_downstream_connection()) {
// Keep-alive
downstream->detach_downstream_connection();
@@ -708,7 +715,7 @@ int HttpsUpstream::on_write() {
// dconn was deleted
}
// We need this if response ends before request.
if (downstream->get_request_state() == Downstream::MSG_COMPLETE) {
if (downstream->get_request_state() == DownstreamState::MSG_COMPLETE) {
delete_downstream();
if (handler_->get_should_close_after_write()) {
@@ -775,11 +782,11 @@ int HttpsUpstream::downstream_read(DownstreamConnection *dconn) {
return downstream_error(dconn, Downstream::EVENT_ERROR);
}
if (downstream->get_response_state() == Downstream::MSG_RESET) {
if (downstream->get_response_state() == DownstreamState::MSG_RESET) {
return -1;
}
if (downstream->get_response_state() == Downstream::MSG_BAD_HEADER) {
if (downstream->get_response_state() == DownstreamState::MSG_BAD_HEADER) {
error_reply(502);
downstream->pop_downstream_connection();
goto end;
@@ -817,23 +824,23 @@ int HttpsUpstream::downstream_eof(DownstreamConnection *dconn) {
DCLOG(INFO, dconn) << "EOF";
}
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
goto end;
}
if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
if (downstream->get_response_state() == DownstreamState::HEADER_COMPLETE) {
// Server may indicate the end of the request by EOF
if (LOG_ENABLED(INFO)) {
DCLOG(INFO, dconn) << "The end of the response body was indicated by "
<< "EOF";
}
on_downstream_body_complete(downstream);
downstream->set_response_state(Downstream::MSG_COMPLETE);
downstream->set_response_state(DownstreamState::MSG_COMPLETE);
downstream->pop_downstream_connection();
goto end;
}
if (downstream->get_response_state() == Downstream::INITIAL) {
if (downstream->get_response_state() == DownstreamState::INITIAL) {
// we did not send any response headers, so we can reply error
// message.
if (LOG_ENABLED(INFO)) {
@@ -862,7 +869,7 @@ int HttpsUpstream::downstream_error(DownstreamConnection *dconn, int events) {
DCLOG(INFO, dconn) << "Timeout";
}
}
if (downstream->get_response_state() != Downstream::INITIAL) {
if (downstream->get_response_state() != DownstreamState::INITIAL) {
return -1;
}
@@ -949,7 +956,7 @@ int HttpsUpstream::send_reply(Downstream *downstream, const uint8_t *body,
output->append(body, bodylen);
downstream->response_sent_body_length += bodylen;
downstream->set_response_state(Downstream::MSG_COMPLETE);
downstream->set_response_state(DownstreamState::MSG_COMPLETE);
return 0;
}
@@ -958,7 +965,8 @@ void HttpsUpstream::error_reply(unsigned int status_code) {
auto downstream = get_downstream();
if (!downstream) {
attach_downstream(make_unique<Downstream>(this, handler_->get_mcpool(), 1));
attach_downstream(
std::make_unique<Downstream>(this, handler_->get_mcpool(), 1));
downstream = get_downstream();
}
@@ -994,7 +1002,7 @@ void HttpsUpstream::error_reply(unsigned int status_code) {
output->append(html);
downstream->response_sent_body_length += html.size();
downstream->set_response_state(Downstream::MSG_COMPLETE);
downstream->set_response_state(DownstreamState::MSG_COMPLETE);
}
void HttpsUpstream::attach_downstream(std::unique_ptr<Downstream> downstream) {
@@ -1041,7 +1049,7 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
auto &resp = downstream->response();
auto &balloc = downstream->get_block_allocator();
auto dconn = downstream->get_downstream_connection();
assert(dconn);
// dconn might be nullptr if this is non-final response from mruby.
if (downstream->get_non_final_response() &&
!downstream->supports_non_final_response()) {
@@ -1051,6 +1059,7 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
#ifdef HAVE_MRUBY
if (!downstream->get_non_final_response()) {
assert(dconn);
const auto &group = dconn->get_downstream_addr_group();
if (group) {
const auto &dmruby_ctx = group->mruby_ctx;
@@ -1060,7 +1069,7 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
return -1;
}
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
return -1;
}
}
@@ -1073,7 +1082,7 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
return -1;
}
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
return -1;
}
}
@@ -1087,9 +1096,15 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
buf->append('.');
buf->append('0' + req.http_minor);
buf->append(' ');
buf->append(http2::stringify_status(balloc, resp.http_status));
buf->append(' ');
buf->append(http2::get_reason_phrase(resp.http_status));
if (req.connect_proto != ConnectProto::NONE && downstream->get_upgraded()) {
buf->append(http2::stringify_status(balloc, 101));
buf->append(' ');
buf->append(http2::get_reason_phrase(101));
} else {
buf->append(http2::stringify_status(balloc, resp.http_status));
buf->append(' ');
buf->append(http2::get_reason_phrase(resp.http_status));
}
buf->append("\r\n");
auto config = get_config();
@@ -1115,8 +1130,12 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
return 0;
}
http2::build_http1_headers_from_headers(
buf, resp.fs.headers(), http2::HDOP_STRIP_ALL & ~http2::HDOP_STRIP_VIA);
auto build_flags = (http2::HDOP_STRIP_ALL & ~http2::HDOP_STRIP_VIA) |
(!http2::legacy_http1(req.http_major, req.http_minor)
? 0
: http2::HDOP_STRIP_TRANSFER_ENCODING);
http2::build_http1_headers_from_headers(buf, resp.fs.headers(), build_flags);
auto worker = handler_->get_worker();
@@ -1139,18 +1158,35 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
}
if (!connect_method && downstream->get_upgraded()) {
auto connection = resp.fs.header(http2::HD_CONNECTION);
if (connection) {
buf->append("Connection: ");
buf->append((*connection).value);
if (req.connect_proto == ConnectProto::WEBSOCKET &&
resp.http_status / 100 == 2) {
buf->append("Upgrade: websocket\r\nConnection: Upgrade\r\n");
auto key = req.fs.header(http2::HD_SEC_WEBSOCKET_KEY);
if (!key || key->value.size() != base64::encode_length(16)) {
return -1;
}
std::array<uint8_t, base64::encode_length(20)> out;
auto accept = http2::make_websocket_accept_token(out.data(), key->value);
if (accept.empty()) {
return -1;
}
buf->append("Sec-WebSocket-Accept: ");
buf->append(accept);
buf->append("\r\n");
}
} else {
auto connection = resp.fs.header(http2::HD_CONNECTION);
if (connection) {
buf->append("Connection: ");
buf->append((*connection).value);
buf->append("\r\n");
}
auto upgrade = resp.fs.header(http2::HD_UPGRADE);
if (upgrade) {
buf->append("Upgrade: ");
buf->append((*upgrade).value);
buf->append("\r\n");
auto upgrade = resp.fs.header(http2::HD_UPGRADE);
if (upgrade) {
buf->append("Upgrade: ");
buf->append((*upgrade).value);
buf->append("\r\n");
}
}
}
@@ -1281,7 +1317,7 @@ int HttpsUpstream::on_downstream_body_complete(Downstream *downstream) {
if (req.connection_close || resp.connection_close ||
// To avoid to stall upload body
downstream->get_request_state() != Downstream::MSG_COMPLETE) {
downstream->get_request_state() != DownstreamState::MSG_COMPLETE) {
auto handler = get_client_handler();
handler->set_should_close_after_write(true);
}
@@ -1368,14 +1404,16 @@ int HttpsUpstream::on_downstream_reset(Downstream *downstream, bool no_retry) {
if (!downstream_->request_submission_ready()) {
switch (downstream_->get_response_state()) {
case Downstream::MSG_COMPLETE:
case DownstreamState::MSG_COMPLETE:
// We have got all response body already. Send it off.
return 0;
case Downstream::INITIAL:
case DownstreamState::INITIAL:
if (on_downstream_abort_request(downstream_.get(), 502) != 0) {
return -1;
}
return 0;
default:
break;
}
// Return error so that caller can delete handler
return -1;

View File

@@ -106,7 +106,7 @@ LiveCheck::LiveCheck(struct ev_loop *loop, SSL_CTX *ssl_ctx, Worker *worker,
worker->get_downstream_config()->timeout.write,
worker->get_downstream_config()->timeout.read, {}, {}, writecb,
readcb, timeoutcb, this, get_config()->tls.dyn_rec.warmup_threshold,
get_config()->tls.dyn_rec.idle_timeout, PROTO_NONE),
get_config()->tls.dyn_rec.idle_timeout, Proto::NONE),
wb_(worker->get_mcpool()),
gen_(gen),
read_(&LiveCheck::noop),
@@ -211,10 +211,10 @@ int LiveCheck::initiate_connection() {
}
switch (addr_->proto) {
case PROTO_HTTP1:
case Proto::HTTP1:
tls::setup_downstream_http1_alpn(ssl);
break;
case PROTO_HTTP2:
case Proto::HTTP2:
tls::setup_downstream_http2_alpn(ssl);
break;
default:
@@ -227,11 +227,11 @@ int LiveCheck::initiate_connection() {
if (addr_->dns) {
if (!dns_query_) {
auto dns_query = make_unique<DNSQuery>(
addr_->host, [this](int status, const Address *result) {
auto dns_query = std::make_unique<DNSQuery>(
addr_->host, [this](DNSResolverStatus status, const Address *result) {
int rv;
if (status == DNS_STATUS_OK) {
if (status == DNSResolverStatus::OK) {
*this->resolved_addr_ = *result;
}
rv = this->initiate_connection();
@@ -242,27 +242,26 @@ int LiveCheck::initiate_connection() {
auto dns_tracker = worker_->get_dns_tracker();
if (!resolved_addr_) {
resolved_addr_ = make_unique<Address>();
resolved_addr_ = std::make_unique<Address>();
}
rv = dns_tracker->resolve(resolved_addr_.get(), dns_query.get());
switch (rv) {
case DNS_STATUS_ERROR:
switch (dns_tracker->resolve(resolved_addr_.get(), dns_query.get())) {
case DNSResolverStatus::ERROR:
return -1;
case DNS_STATUS_RUNNING:
case DNSResolverStatus::RUNNING:
dns_query_ = std::move(dns_query);
return 0;
case DNS_STATUS_OK:
case DNSResolverStatus::OK:
break;
default:
assert(0);
}
} else {
switch (dns_query_->status) {
case DNS_STATUS_ERROR:
case DNSResolverStatus::ERROR:
dns_query_.reset();
return -1;
case DNS_STATUS_OK:
case DNSResolverStatus::OK:
dns_query_.reset();
break;
default:
@@ -359,7 +358,7 @@ int LiveCheck::connected() {
return do_write();
}
if (addr_->proto == PROTO_HTTP2) {
if (addr_->proto == Proto::HTTP2) {
// For HTTP/2, we try to read SETTINGS ACK from server to make
// sure it is really alive, and serving HTTP/2.
read_ = &LiveCheck::read_clear;
@@ -418,12 +417,12 @@ int LiveCheck::tls_handshake() {
auto proto = StringRef{next_proto, next_proto_len};
switch (addr_->proto) {
case PROTO_HTTP1:
case Proto::HTTP1:
if (proto.empty() || proto == StringRef::from_lit("http/1.1")) {
break;
}
return -1;
case PROTO_HTTP2:
case Proto::HTTP2:
if (util::check_h2_is_selected(proto)) {
// For HTTP/2, we try to read SETTINGS ACK from server to make
// sure it is really alive, and serving HTTP/2.

View File

@@ -74,6 +74,44 @@ constexpr const char *SEVERITY_COLOR[] = {
};
} // namespace
#ifndef NOTHREADS
# ifdef HAVE_THREAD_LOCAL
namespace {
thread_local LogBuffer logbuf_;
} // namespace
namespace {
LogBuffer *get_logbuf() { return &logbuf_; }
} // namespace
# else // !HAVE_THREAD_LOCAL
namespace {
pthread_key_t lckey;
pthread_once_t lckey_once = PTHREAD_ONCE_INIT;
} // namespace
namespace {
void make_key() { pthread_key_create(&lckey, NULL); }
} // namespace
LogBuffer *get_logbuf() {
pthread_once(&lckey_once, make_key);
auto buf = static_cast<LogBuffer *>(pthread_getspecific(lckey));
if (!buf) {
buf = new LogBuffer();
pthread_setspecific(lckey, buf);
}
return buf;
}
# endif // !HAVE_THREAD_LOCAL
#else // NOTHREADS
namespace {
LogBuffer *get_logbuf() {
static LogBuffer logbuf;
return &logbuf;
}
} // namespace
#endif // NOTHREADS
int Log::severity_thres_ = NOTICE;
void Log::set_severity_level(int severity) { severity_thres_ = severity; }
@@ -106,7 +144,15 @@ int severity_to_syslog_level(int severity) {
}
Log::Log(int severity, const char *filename, int linenum)
: filename_(filename), severity_(severity), linenum_(linenum) {}
: buf_(*get_logbuf()),
begin_(buf_.data()),
end_(begin_ + buf_.size()),
last_(begin_),
filename_(filename),
flags_(0),
severity_(severity),
linenum_(linenum),
full_(false) {}
Log::~Log() {
int rv;
@@ -127,12 +173,13 @@ Log::~Log() {
if (errorconf.syslog) {
if (severity_ == NOTICE) {
syslog(severity_to_syslog_level(severity_), "[%s] %s",
SEVERITY_STR[severity_].c_str(), stream_.str().c_str());
syslog(severity_to_syslog_level(severity_), "[%s] %.*s",
SEVERITY_STR[severity_].c_str(), static_cast<int>(rleft()),
begin_);
} else {
syslog(severity_to_syslog_level(severity_), "[%s] %s (%s:%d)",
SEVERITY_STR[severity_].c_str(), stream_.str().c_str(), filename_,
linenum_);
syslog(severity_to_syslog_level(severity_), "[%s] %.*s (%s:%d)",
SEVERITY_STR[severity_].c_str(), static_cast<int>(rleft()), begin_,
filename_, linenum_);
}
return;
@@ -145,11 +192,11 @@ 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",
rv = snprintf(buf, sizeof(buf), "%s %d %d %s %s%s%s (%s:%d) %.*s\n",
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());
filename_, linenum_, static_cast<int>(rleft()), begin_);
if (rv < 0) {
return;
@@ -161,6 +208,156 @@ Log::~Log() {
;
}
Log &Log::operator<<(const std::string &s) {
write_seq(std::begin(s), std::end(s));
return *this;
}
Log &Log::operator<<(const StringRef &s) {
write_seq(std::begin(s), std::end(s));
return *this;
}
Log &Log::operator<<(const char *s) {
write_seq(s, s + strlen(s));
return *this;
}
Log &Log::operator<<(const ImmutableString &s) {
write_seq(std::begin(s), std::end(s));
return *this;
}
Log &Log::operator<<(long long n) {
if (n >= 0) {
return *this << static_cast<uint64_t>(n);
}
if (flags_ & fmt_hex) {
write_hex(n);
return *this;
}
if (full_) {
return *this;
}
n *= -1;
size_t nlen = 0;
for (auto t = n; t; t /= 10, ++nlen)
;
if (wleft() < 1 /* sign */ + nlen) {
full_ = true;
return *this;
}
*last_++ = '-';
*last_ += nlen;
update_full();
auto p = last_ - 1;
for (; n; n /= 10) {
*p-- = (n % 10) + '0';
}
return *this;
}
Log &Log::operator<<(unsigned long long n) {
if (flags_ & fmt_hex) {
write_hex(n);
return *this;
}
if (full_) {
return *this;
}
if (n == 0) {
*last_++ = '0';
update_full();
return *this;
}
size_t nlen = 0;
for (auto t = n; t; t /= 10, ++nlen)
;
if (wleft() < nlen) {
full_ = true;
return *this;
}
last_ += nlen;
update_full();
auto p = last_ - 1;
for (; n; n /= 10) {
*p-- = (n % 10) + '0';
}
return *this;
}
Log &Log::operator<<(double n) {
if (full_) {
return *this;
}
auto left = wleft();
auto rv = snprintf(reinterpret_cast<char *>(last_), left, "%.9f", n);
if (rv > static_cast<int>(left)) {
full_ = true;
return *this;
}
last_ += rv;
update_full();
return *this;
}
Log &Log::operator<<(long double n) {
if (full_) {
return *this;
}
auto left = wleft();
auto rv = snprintf(reinterpret_cast<char *>(last_), left, "%.9Lf", n);
if (rv > static_cast<int>(left)) {
full_ = true;
return *this;
}
last_ += rv;
update_full();
return *this;
}
Log &Log::operator<<(bool n) {
if (full_) {
return *this;
}
*last_++ = n ? '1' : '0';
update_full();
return *this;
}
Log &Log::operator<<(const void *p) {
if (full_) {
return *this;
}
write_hex(reinterpret_cast<uintptr_t>(p));
return *this;
}
namespace log {
void hex(Log &log) { log.set_flags(Log::fmt_hex); };
void dec(Log &log) { log.set_flags(Log::fmt_dec); };
} // namespace log
namespace {
template <typename OutputIterator>
std::pair<OutputIterator, OutputIterator> copy(const char *src, size_t srclen,
@@ -412,19 +609,19 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv,
for (auto &lf : lfv) {
switch (lf.type) {
case SHRPX_LOGF_LITERAL:
case LogFragmentType::LITERAL:
std::tie(p, last) = copy(lf.value, p, last);
break;
case SHRPX_LOGF_REMOTE_ADDR:
case LogFragmentType::REMOTE_ADDR:
std::tie(p, last) = copy(lgsp.remote_addr, p, last);
break;
case SHRPX_LOGF_TIME_LOCAL:
case LogFragmentType::TIME_LOCAL:
std::tie(p, last) = copy(tstamp->time_local, p, last);
break;
case SHRPX_LOGF_TIME_ISO8601:
case LogFragmentType::TIME_ISO8601:
std::tie(p, last) = copy(tstamp->time_iso8601, p, last);
break;
case SHRPX_LOGF_REQUEST:
case LogFragmentType::REQUEST:
std::tie(p, last) = copy(method, p, last);
std::tie(p, last) = copy(' ', p, last);
std::tie(p, last) = copy_escape(path, p, last);
@@ -435,13 +632,13 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv,
std::tie(p, last) = copy(req.http_minor, p, last);
}
break;
case SHRPX_LOGF_STATUS:
case LogFragmentType::STATUS:
std::tie(p, last) = copy(resp.http_status, p, last);
break;
case SHRPX_LOGF_BODY_BYTES_SENT:
case LogFragmentType::BODY_BYTES_SENT:
std::tie(p, last) = copy(downstream->response_sent_body_length, p, last);
break;
case SHRPX_LOGF_HTTP: {
case LogFragmentType::HTTP: {
auto hd = req.fs.header(lf.value);
if (hd) {
std::tie(p, last) = copy_escape((*hd).value, p, last);
@@ -452,7 +649,7 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv,
break;
}
case SHRPX_LOGF_AUTHORITY:
case LogFragmentType::AUTHORITY:
if (!req.authority.empty()) {
std::tie(p, last) = copy(req.authority, p, last);
break;
@@ -461,13 +658,13 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv,
std::tie(p, last) = copy('-', p, last);
break;
case SHRPX_LOGF_REMOTE_PORT:
case LogFragmentType::REMOTE_PORT:
std::tie(p, last) = copy(lgsp.remote_port, p, last);
break;
case SHRPX_LOGF_SERVER_PORT:
case LogFragmentType::SERVER_PORT:
std::tie(p, last) = copy(lgsp.server_port, p, last);
break;
case SHRPX_LOGF_REQUEST_TIME: {
case LogFragmentType::REQUEST_TIME: {
auto t = std::chrono::duration_cast<std::chrono::milliseconds>(
lgsp.request_end_time - downstream->get_request_start_time())
.count();
@@ -481,20 +678,20 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv,
std::tie(p, last) = copy(frac, p, last);
break;
}
case SHRPX_LOGF_PID:
case LogFragmentType::PID:
std::tie(p, last) = copy(lgsp.pid, p, last);
break;
case SHRPX_LOGF_ALPN:
case LogFragmentType::ALPN:
std::tie(p, last) = copy_escape(lgsp.alpn, p, last);
break;
case SHRPX_LOGF_TLS_CIPHER:
case LogFragmentType::TLS_CIPHER:
if (!lgsp.ssl) {
std::tie(p, last) = copy('-', p, last);
break;
}
std::tie(p, last) = copy(SSL_get_cipher_name(lgsp.ssl), p, last);
break;
case SHRPX_LOGF_TLS_PROTOCOL:
case LogFragmentType::TLS_PROTOCOL:
if (!lgsp.ssl) {
std::tie(p, last) = copy('-', p, last);
break;
@@ -502,7 +699,7 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv,
std::tie(p, last) =
copy(nghttp2::tls::get_tls_protocol(lgsp.ssl), p, last);
break;
case SHRPX_LOGF_TLS_SESSION_ID: {
case LogFragmentType::TLS_SESSION_ID: {
auto session = SSL_get_session(lgsp.ssl);
if (!session) {
std::tie(p, last) = copy('-', p, last);
@@ -517,7 +714,7 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv,
std::tie(p, last) = copy_hex_low(session_id, session_id_length, p, last);
break;
}
case SHRPX_LOGF_TLS_SESSION_REUSED:
case LogFragmentType::TLS_SESSION_REUSED:
if (!lgsp.ssl) {
std::tie(p, last) = copy('-', p, last);
break;
@@ -525,15 +722,15 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv,
std::tie(p, last) =
copy(SSL_session_reused(lgsp.ssl) ? 'r' : '.', p, last);
break;
case SHRPX_LOGF_TLS_SNI:
case LogFragmentType::TLS_SNI:
if (lgsp.sni.empty()) {
std::tie(p, last) = copy('-', p, last);
break;
}
std::tie(p, last) = copy_escape(lgsp.sni, p, last);
break;
case SHRPX_LOGF_TLS_CLIENT_FINGERPRINT_SHA1:
case SHRPX_LOGF_TLS_CLIENT_FINGERPRINT_SHA256: {
case LogFragmentType::TLS_CLIENT_FINGERPRINT_SHA1:
case LogFragmentType::TLS_CLIENT_FINGERPRINT_SHA256: {
if (!lgsp.ssl) {
std::tie(p, last) = copy('-', p, last);
break;
@@ -546,8 +743,9 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv,
std::array<uint8_t, 32> buf;
auto len = tls::get_x509_fingerprint(
buf.data(), buf.size(), x,
lf.type == SHRPX_LOGF_TLS_CLIENT_FINGERPRINT_SHA256 ? EVP_sha256()
: EVP_sha1());
lf.type == LogFragmentType::TLS_CLIENT_FINGERPRINT_SHA256
? EVP_sha256()
: EVP_sha1());
X509_free(x);
if (len <= 0) {
std::tie(p, last) = copy('-', p, last);
@@ -556,8 +754,8 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv,
std::tie(p, last) = copy_hex_low(buf.data(), len, p, last);
break;
}
case SHRPX_LOGF_TLS_CLIENT_ISSUER_NAME:
case SHRPX_LOGF_TLS_CLIENT_SUBJECT_NAME: {
case LogFragmentType::TLS_CLIENT_ISSUER_NAME:
case LogFragmentType::TLS_CLIENT_SUBJECT_NAME: {
if (!lgsp.ssl) {
std::tie(p, last) = copy('-', p, last);
break;
@@ -567,7 +765,7 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv,
std::tie(p, last) = copy('-', p, last);
break;
}
auto name = lf.type == SHRPX_LOGF_TLS_CLIENT_ISSUER_NAME
auto name = lf.type == LogFragmentType::TLS_CLIENT_ISSUER_NAME
? tls::get_x509_issuer_name(balloc, x)
: tls::get_x509_subject_name(balloc, x);
X509_free(x);
@@ -578,7 +776,7 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv,
std::tie(p, last) = copy(name, p, last);
break;
}
case SHRPX_LOGF_TLS_CLIENT_SERIAL: {
case LogFragmentType::TLS_CLIENT_SERIAL: {
if (!lgsp.ssl) {
std::tie(p, last) = copy('-', p, last);
break;
@@ -597,21 +795,21 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv,
std::tie(p, last) = copy(sn, p, last);
break;
}
case SHRPX_LOGF_BACKEND_HOST:
case LogFragmentType::BACKEND_HOST:
if (!downstream_addr) {
std::tie(p, last) = copy('-', p, last);
break;
}
std::tie(p, last) = copy(downstream_addr->host, p, last);
break;
case SHRPX_LOGF_BACKEND_PORT:
case LogFragmentType::BACKEND_PORT:
if (!downstream_addr) {
std::tie(p, last) = copy('-', p, last);
break;
}
std::tie(p, last) = copy(downstream_addr->port, p, last);
break;
case SHRPX_LOGF_NONE:
case LogFragmentType::NONE:
break;
default:
break;
@@ -696,8 +894,9 @@ void log_chld(pid_t pid, int rstatus, const char *msg) {
LOG(NOTICE) << msg << ": [" << pid << "] exited "
<< (WIFEXITED(rstatus) ? "normally" : "abnormally")
<< " with status " << std::hex << rstatus << std::oct
<< "; exit status " << WEXITSTATUS(rstatus)
<< " with status " << log::hex << rstatus << log::dec
<< "; exit status "
<< (WIFEXITED(rstatus) ? WEXITSTATUS(rstatus) : 0)
<< (signalstr.empty() ? "" : signalstr.c_str());
}

View File

@@ -29,7 +29,6 @@
#include <sys/types.h>
#include <sstream>
#include <memory>
#include <vector>
#include <chrono>
@@ -38,6 +37,7 @@
#include "shrpx_log_config.h"
#include "tls.h"
#include "template.h"
#include "util.h"
using namespace nghttp2;
@@ -90,61 +90,163 @@ struct DownstreamAddr;
enum SeverityLevel { INFO, NOTICE, WARN, ERROR, FATAL };
using LogBuffer = std::array<uint8_t, 4_k>;
class Log {
public:
Log(int severity, const char *filename, int linenum);
~Log();
template <typename Type> Log &operator<<(Type s) {
stream_ << s;
Log &operator<<(const std::string &s);
Log &operator<<(const char *s);
Log &operator<<(const StringRef &s);
Log &operator<<(const ImmutableString &s);
Log &operator<<(short n) { return *this << static_cast<long long>(n); }
Log &operator<<(int n) { return *this << static_cast<long long>(n); }
Log &operator<<(long n) { return *this << static_cast<long long>(n); }
Log &operator<<(long long n);
Log &operator<<(unsigned short n) {
return *this << static_cast<unsigned long long>(n);
}
Log &operator<<(unsigned int n) {
return *this << static_cast<unsigned long long>(n);
}
Log &operator<<(unsigned long n) {
return *this << static_cast<unsigned long long>(n);
}
Log &operator<<(unsigned long long n);
Log &operator<<(float n) { return *this << static_cast<double>(n); }
Log &operator<<(double n);
Log &operator<<(long double n);
Log &operator<<(bool n);
Log &operator<<(const void *p);
template <typename T> Log &operator<<(const std::shared_ptr<T> &ptr) {
return *this << ptr.get();
}
Log &operator<<(void (*func)(Log &log)) {
func(*this);
return *this;
}
template <typename InputIt> void write_seq(InputIt first, InputIt last) {
if (full_) {
return;
}
auto d = std::distance(first, last);
auto n = std::min(wleft(), static_cast<size_t>(d));
last_ = std::copy(first, first + n, last_);
update_full();
}
template <typename T> void write_hex(T n) {
if (full_) {
return;
}
if (n == 0) {
if (wleft() < 4 /* for "0x00" */) {
full_ = true;
return;
}
*last_++ = '0';
*last_++ = 'x';
*last_++ = '0';
*last_++ = '0';
update_full();
return;
}
size_t nlen = 0;
for (auto t = n; t; t >>= 8, ++nlen)
;
nlen *= 2;
if (wleft() < 2 /* for "0x" */ + nlen) {
full_ = true;
return;
}
*last_++ = '0';
*last_++ = 'x';
last_ += nlen;
update_full();
auto p = last_ - 1;
for (; n; n >>= 8) {
uint8_t b = n & 0xff;
*p-- = util::LOWER_XDIGITS[b & 0xf];
*p-- = util::LOWER_XDIGITS[b >> 4];
}
}
static void set_severity_level(int severity);
static int set_severity_level_by_name(const StringRef &name);
static bool log_enabled(int severity) { return severity >= severity_thres_; }
enum {
fmt_dec = 0x00,
fmt_hex = 0x01,
};
void set_flags(int flags) { flags_ = flags; }
private:
std::stringstream stream_;
size_t rleft() { return last_ - begin_; }
size_t wleft() { return end_ - last_; }
void update_full() { full_ = last_ == end_; }
LogBuffer &buf_;
uint8_t *begin_;
uint8_t *end_;
uint8_t *last_;
const char *filename_;
uint32_t flags_;
int severity_;
int linenum_;
bool full_;
static int severity_thres_;
};
namespace log {
void hex(Log &log);
void dec(Log &log);
} // namespace log
#define TTY_HTTP_HD (log_config()->errorlog_tty ? "\033[1;34m" : "")
#define TTY_RST (log_config()->errorlog_tty ? "\033[0m" : "")
enum LogFragmentType {
SHRPX_LOGF_NONE,
SHRPX_LOGF_LITERAL,
SHRPX_LOGF_REMOTE_ADDR,
SHRPX_LOGF_TIME_LOCAL,
SHRPX_LOGF_TIME_ISO8601,
SHRPX_LOGF_REQUEST,
SHRPX_LOGF_STATUS,
SHRPX_LOGF_BODY_BYTES_SENT,
SHRPX_LOGF_HTTP,
SHRPX_LOGF_AUTHORITY,
SHRPX_LOGF_REMOTE_PORT,
SHRPX_LOGF_SERVER_PORT,
SHRPX_LOGF_REQUEST_TIME,
SHRPX_LOGF_PID,
SHRPX_LOGF_ALPN,
SHRPX_LOGF_TLS_CIPHER,
SHRPX_LOGF_SSL_CIPHER = SHRPX_LOGF_TLS_CIPHER,
SHRPX_LOGF_TLS_PROTOCOL,
SHRPX_LOGF_SSL_PROTOCOL = SHRPX_LOGF_TLS_PROTOCOL,
SHRPX_LOGF_TLS_SESSION_ID,
SHRPX_LOGF_SSL_SESSION_ID = SHRPX_LOGF_TLS_SESSION_ID,
SHRPX_LOGF_TLS_SESSION_REUSED,
SHRPX_LOGF_SSL_SESSION_REUSED = SHRPX_LOGF_TLS_SESSION_REUSED,
SHRPX_LOGF_TLS_SNI,
SHRPX_LOGF_TLS_CLIENT_FINGERPRINT_SHA1,
SHRPX_LOGF_TLS_CLIENT_FINGERPRINT_SHA256,
SHRPX_LOGF_TLS_CLIENT_ISSUER_NAME,
SHRPX_LOGF_TLS_CLIENT_SERIAL,
SHRPX_LOGF_TLS_CLIENT_SUBJECT_NAME,
SHRPX_LOGF_BACKEND_HOST,
SHRPX_LOGF_BACKEND_PORT,
enum class LogFragmentType {
NONE,
LITERAL,
REMOTE_ADDR,
TIME_LOCAL,
TIME_ISO8601,
REQUEST,
STATUS,
BODY_BYTES_SENT,
HTTP,
AUTHORITY,
REMOTE_PORT,
SERVER_PORT,
REQUEST_TIME,
PID,
ALPN,
TLS_CIPHER,
SSL_CIPHER = TLS_CIPHER,
TLS_PROTOCOL,
SSL_PROTOCOL = TLS_PROTOCOL,
TLS_SESSION_ID,
SSL_SESSION_ID = TLS_SESSION_ID,
TLS_SESSION_REUSED,
SSL_SESSION_REUSED = TLS_SESSION_REUSED,
TLS_SNI,
TLS_CLIENT_FINGERPRINT_SHA1,
TLS_CLIENT_FINGERPRINT_SHA256,
TLS_CLIENT_ISSUER_NAME,
TLS_CLIENT_SERIAL,
TLS_CLIENT_SUBJECT_NAME,
BACKEND_HOST,
BACKEND_PORT,
};
struct LogFragment {

View File

@@ -59,7 +59,7 @@ LogConfig::LogConfig()
#ifndef NOTHREADS
# ifdef HAVE_THREAD_LOCAL
namespace {
thread_local std::unique_ptr<LogConfig> config = make_unique<LogConfig>();
thread_local std::unique_ptr<LogConfig> config = std::make_unique<LogConfig>();
} // namespace
LogConfig *log_config() { return config.get(); }
@@ -88,7 +88,7 @@ void delete_log_config() { delete log_config(); }
# endif // !HAVE_THREAD_LOCAL
#else // NOTHREADS
namespace {
std::unique_ptr<LogConfig> config = make_unique<LogConfig>();
std::unique_ptr<LogConfig> config = std::make_unique<LogConfig>();
} // namespace
LogConfig *log_config() { return config.get(); }

View File

@@ -102,7 +102,7 @@ MemcachedConnection::MemcachedConnection(const Address *addr,
MemchunkPool *mcpool,
std::mt19937 &gen)
: conn_(loop, -1, nullptr, mcpool, write_timeout, read_timeout, {}, {},
connectcb, readcb, timeoutcb, this, 0, 0., PROTO_MEMCACHED),
connectcb, readcb, timeoutcb, this, 0, 0., Proto::MEMCACHED),
do_read_(&MemcachedConnection::noop),
do_write_(&MemcachedConnection::noop),
sni_name_(sni_name),
@@ -120,7 +120,8 @@ namespace {
void clear_request(std::deque<std::unique_ptr<MemcachedRequest>> &q) {
for (auto &req : q) {
if (req->cb) {
req->cb(req.get(), MemcachedResult(MEMCACHED_ERR_EXT_NETWORK_ERROR));
req->cb(req.get(),
MemcachedResult(MemcachedStatusCode::EXT_NETWORK_ERROR));
}
}
q.clear();
@@ -424,7 +425,7 @@ int MemcachedConnection::parse_packet() {
auto busy = false;
switch (parse_state_.state) {
case MEMCACHED_PARSE_HEADER24: {
case MemcachedParseState::HEADER24: {
if (recvbuf_.last - in < 24) {
recvbuf_.drain_reset(in - recvbuf_.pos);
return 0;
@@ -445,13 +446,14 @@ int MemcachedConnection::parse_packet() {
}
++in;
parse_state_.op = *in++;
parse_state_.op = static_cast<MemcachedOp>(*in++);
parse_state_.keylen = util::get_uint16(in);
in += 2;
parse_state_.extralen = *in++;
// skip 1 byte reserved data type
++in;
parse_state_.status_code = util::get_uint16(in);
parse_state_.status_code =
static_cast<MemcachedStatusCode>(util::get_uint16(in));
in += 2;
parse_state_.totalbody = util::get_uint32(in);
in += 4;
@@ -463,7 +465,8 @@ int MemcachedConnection::parse_packet() {
if (req->op != parse_state_.op) {
MCLOG(WARN, this)
<< "opcode in response does not match to the request: want "
<< static_cast<uint32_t>(req->op) << ", got " << parse_state_.op;
<< static_cast<uint32_t>(req->op) << ", got "
<< static_cast<uint32_t>(parse_state_.op);
return -1;
}
@@ -479,8 +482,9 @@ int MemcachedConnection::parse_packet() {
return -1;
}
if (parse_state_.op == MEMCACHED_OP_GET &&
parse_state_.status_code == 0 && parse_state_.extralen == 0) {
if (parse_state_.op == MemcachedOp::GET &&
parse_state_.status_code == MemcachedStatusCode::NO_ERROR &&
parse_state_.extralen == 0) {
MCLOG(WARN, this) << "response for GET does not have extra";
return -1;
}
@@ -494,17 +498,17 @@ int MemcachedConnection::parse_packet() {
}
if (parse_state_.extralen) {
parse_state_.state = MEMCACHED_PARSE_EXTRA;
parse_state_.state = MemcachedParseState::EXTRA;
parse_state_.read_left = parse_state_.extralen;
} else {
parse_state_.state = MEMCACHED_PARSE_VALUE;
parse_state_.state = MemcachedParseState::VALUE;
parse_state_.read_left = parse_state_.totalbody - parse_state_.keylen -
parse_state_.extralen;
}
busy = true;
break;
}
case MEMCACHED_PARSE_EXTRA: {
case MemcachedParseState::EXTRA: {
// We don't use extra for now. Just read and forget.
auto n = std::min(static_cast<size_t>(recvbuf_.last - in),
parse_state_.read_left);
@@ -515,7 +519,7 @@ int MemcachedConnection::parse_packet() {
recvbuf_.reset();
return 0;
}
parse_state_.state = MEMCACHED_PARSE_VALUE;
parse_state_.state = MemcachedParseState::VALUE;
// since we require keylen == 0, totalbody - extralen ==
// valuelen
parse_state_.read_left =
@@ -523,7 +527,7 @@ int MemcachedConnection::parse_packet() {
busy = true;
break;
}
case MEMCACHED_PARSE_VALUE: {
case MemcachedParseState::VALUE: {
auto n = std::min(static_cast<size_t>(recvbuf_.last - in),
parse_state_.read_left);
@@ -537,9 +541,9 @@ int MemcachedConnection::parse_packet() {
}
if (LOG_ENABLED(INFO)) {
if (parse_state_.status_code) {
MCLOG(INFO, this)
<< "response returned error status: " << parse_state_.status_code;
if (parse_state_.status_code != MemcachedStatusCode::NO_ERROR) {
MCLOG(INFO, this) << "response returned error status: "
<< static_cast<uint16_t>(parse_state_.status_code);
}
}
@@ -661,9 +665,9 @@ void MemcachedConnection::drain_send_queue(size_t nwrite) {
size_t MemcachedConnection::serialized_size(MemcachedRequest *req) {
switch (req->op) {
case MEMCACHED_OP_GET:
case MemcachedOp::GET:
return 24 + req->key.size();
case MEMCACHED_OP_ADD:
case MemcachedOp::ADD:
default:
return 24 + 8 + req->key.size() + req->value.size();
}
@@ -676,14 +680,14 @@ void MemcachedConnection::make_request(MemcachedSendbuf *sendbuf,
std::fill(std::begin(headbuf.buf), std::end(headbuf.buf), 0);
headbuf[0] = MEMCACHED_REQ_MAGIC;
headbuf[1] = req->op;
headbuf[1] = static_cast<uint8_t>(req->op);
switch (req->op) {
case MEMCACHED_OP_GET:
case MemcachedOp::GET:
util::put_uint16be(&headbuf[2], req->key.size());
util::put_uint32be(&headbuf[8], req->key.size());
headbuf.write(24);
break;
case MEMCACHED_OP_ADD:
case MemcachedOp::ADD:
util::put_uint16be(&headbuf[2], req->key.size());
headbuf[4] = 8;
util::put_uint32be(&headbuf[8], 8 + req->key.size() + req->value.size());

View File

@@ -43,15 +43,17 @@ using namespace nghttp2;
namespace shrpx {
struct MemcachedRequest;
enum class MemcachedOp : uint8_t;
enum class MemcachedStatusCode : uint16_t;
enum {
MEMCACHED_PARSE_HEADER24,
MEMCACHED_PARSE_EXTRA,
MEMCACHED_PARSE_VALUE,
enum class MemcachedParseState {
HEADER24,
EXTRA,
VALUE,
};
// Stores state when parsing response from memcached server
struct MemcachedParseState {
struct MemcachedParseContext {
// Buffer for value, dynamically allocated.
std::vector<uint8_t> value;
// cas in response
@@ -66,11 +68,11 @@ struct MemcachedParseState {
// Number of bytes left to read variable length field.
size_t read_left;
// Parser state; see enum above
int state;
MemcachedParseState state;
// status_code in response
int status_code;
MemcachedStatusCode status_code;
// op in response
int op;
MemcachedOp op;
};
struct MemcachedSendbuf {
@@ -138,7 +140,7 @@ private:
StringRef sni_name_;
tls::TLSSessionCache tls_session_cache_;
ConnectBlocker connect_blocker_;
MemcachedParseState parse_state_;
MemcachedParseContext parse_state_;
const Address *addr_;
SSL_CTX *ssl_ctx_;
// Sum of the bytes to be transmitted in sendbufv_.

View File

@@ -37,8 +37,8 @@ MemcachedDispatcher::MemcachedDispatcher(const Address *addr,
MemchunkPool *mcpool,
std::mt19937 &gen)
: loop_(loop),
mconn_(make_unique<MemcachedConnection>(addr, loop_, ssl_ctx, sni_name,
mcpool, gen)) {}
mconn_(std::make_unique<MemcachedConnection>(addr, loop_, ssl_ctx,
sni_name, mcpool, gen)) {}
MemcachedDispatcher::~MemcachedDispatcher() {}

View File

@@ -35,9 +35,9 @@
namespace shrpx {
enum {
MEMCACHED_OP_GET = 0x00,
MEMCACHED_OP_ADD = 0x02,
enum class MemcachedOp : uint8_t {
GET = 0x00,
ADD = 0x02,
};
struct MemcachedRequest;
@@ -50,7 +50,7 @@ struct MemcachedRequest {
std::vector<uint8_t> value;
MemcachedResultCallback cb;
uint32_t expiry;
int op;
MemcachedOp op;
bool canceled;
};

View File

@@ -31,18 +31,18 @@
namespace shrpx {
enum MemcachedStatusCode {
MEMCACHED_ERR_NO_ERROR,
MEMCACHED_ERR_EXT_NETWORK_ERROR = 0x1001,
enum class MemcachedStatusCode : uint16_t {
NO_ERROR,
EXT_NETWORK_ERROR = 0x1001,
};
struct MemcachedResult {
MemcachedResult(int status_code) : status_code(status_code) {}
MemcachedResult(int status_code, std::vector<uint8_t> value)
MemcachedResult(MemcachedStatusCode status_code) : status_code(status_code) {}
MemcachedResult(MemcachedStatusCode status_code, std::vector<uint8_t> value)
: value(std::move(value)), status_code(status_code) {}
std::vector<uint8_t> value;
int status_code;
MemcachedStatusCode status_code;
};
} // namespace shrpx

View File

@@ -82,7 +82,7 @@ int MRubyContext::run_app(Downstream *downstream, int phase) {
if (mrb_->exc) {
// If response has been committed, ignore error
if (downstream->get_response_state() != Downstream::MSG_COMPLETE) {
if (downstream->get_response_state() != DownstreamState::MSG_COMPLETE) {
rv = -1;
}
@@ -145,6 +145,7 @@ RProc *compile(mrb_state *mrb, const StringRef &filename) {
auto infile = fopen(filename.c_str(), "rb");
if (infile == nullptr) {
LOG(ERROR) << "Could not open mruby file " << filename;
return nullptr;
}
auto infile_d = defer(fclose, infile);
@@ -179,7 +180,8 @@ RProc *compile(mrb_state *mrb, const StringRef &filename) {
std::unique_ptr<MRubyContext> create_mruby_context(const StringRef &filename) {
if (filename.empty()) {
return make_unique<MRubyContext>(nullptr, mrb_nil_value(), mrb_nil_value());
return std::make_unique<MRubyContext>(nullptr, mrb_nil_value(),
mrb_nil_value());
}
auto mrb = mrb_open();
@@ -215,7 +217,7 @@ std::unique_ptr<MRubyContext> create_mruby_context(const StringRef &filename) {
mrb_gc_protect(mrb, env);
mrb_gc_protect(mrb, app);
return make_unique<MRubyContext>(mrb, std::move(app), std::move(env));
return std::make_unique<MRubyContext>(mrb, std::move(app), std::move(env));
}
mrb_sym intern_ptr(mrb_state *mrb, void *ptr) {

View File

@@ -397,6 +397,18 @@ mrb_value env_get_alpn(mrb_state *mrb, mrb_value self) {
}
} // namespace
namespace {
mrb_value env_get_tls_handshake_finished(mrb_state *mrb, mrb_value self) {
auto data = static_cast<MRubyAssocData *>(mrb->ud);
auto downstream = data->downstream;
auto upstream = downstream->get_upstream();
auto handler = upstream->get_client_handler();
auto conn = handler->get_connection();
return SSL_is_init_finished(conn->tls.ssl) ? mrb_true_value()
: mrb_false_value();
}
} // namespace
void init_env_class(mrb_state *mrb, RClass *module) {
auto env_class =
mrb_define_class_under(mrb, module, "Env", mrb->object_class);
@@ -439,6 +451,8 @@ void init_env_class(mrb_state *mrb, RClass *module) {
mrb_define_method(mrb, env_class, "tls_session_reused",
env_get_tls_session_reused, MRB_ARGS_NONE());
mrb_define_method(mrb, env_class, "alpn", env_get_alpn, MRB_ARGS_NONE());
mrb_define_method(mrb, env_class, "tls_handshake_finished",
env_get_tls_handshake_finished, MRB_ARGS_NONE());
}
} // namespace mruby

View File

@@ -254,7 +254,7 @@ mrb_value request_mod_header(mrb_state *mrb, mrb_value self, bool repl) {
}
if (mrb_array_p(values)) {
auto n = mrb_ary_len(mrb, values);
auto n = RARRAY_LEN(values);
for (int i = 0; i < n; ++i) {
auto value = mrb_ary_ref(mrb, values, i);
if (!mrb_string_p(value)) {

View File

@@ -146,7 +146,7 @@ mrb_value response_mod_header(mrb_state *mrb, mrb_value self, bool repl) {
}
if (mrb_array_p(values)) {
auto n = mrb_ary_len(mrb, values);
auto n = RARRAY_LEN(values);
for (int i = 0; i < n; ++i) {
auto value = mrb_ary_ref(mrb, values, i);
if (!mrb_string_p(value)) {
@@ -209,7 +209,7 @@ mrb_value response_return(mrb_state *mrb, mrb_value self) {
auto &balloc = downstream->get_block_allocator();
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
mrb_raise(mrb, E_RUNTIME_ERROR, "response has already been committed");
}
@@ -283,7 +283,7 @@ mrb_value response_send_info(mrb_state *mrb, mrb_value self) {
auto &resp = downstream->response();
int rv;
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
mrb_raise(mrb, E_RUNTIME_ERROR, "response has already been committed");
}
@@ -299,7 +299,7 @@ mrb_value response_send_info(mrb_state *mrb, mrb_value self) {
auto &balloc = downstream->get_block_allocator();
auto keys = mrb_hash_keys(mrb, hash);
auto keyslen = mrb_ary_len(mrb, keys);
auto keyslen = RARRAY_LEN(keys);
for (int i = 0; i < keyslen; ++i) {
auto key = mrb_ary_ref(mrb, keys, i);
@@ -322,7 +322,7 @@ mrb_value response_send_info(mrb_state *mrb, mrb_value self) {
auto token = http2::lookup_token(keyref.byte(), keyref.size());
if (mrb_array_p(values)) {
auto n = mrb_ary_len(mrb, values);
auto n = RARRAY_LEN(values);
for (int i = 0; i < n; ++i) {
auto value = mrb_ary_ref(mrb, values, i);
if (!mrb_string_p(value)) {
@@ -357,6 +357,10 @@ mrb_value response_send_info(mrb_state *mrb, mrb_value self) {
mrb_raise(mrb, E_RUNTIME_ERROR, "could not send non-final response");
}
auto handler = upstream->get_client_handler();
handler->signal_write();
return self;
}
} // namespace

View File

@@ -108,8 +108,9 @@ void RateLimit::stopw() {
}
void RateLimit::handle_tls_pending_read() {
if (!conn_ || !conn_->tls.ssl ||
(SSL_pending(conn_->tls.ssl) == 0 && conn_->tls.rbuf.rleft() == 0)) {
if (!conn_ || !conn_->tls.ssl || !conn_->tls.initial_handshake_done ||
(SSL_pending(conn_->tls.ssl) == 0 && conn_->tls.rbuf.rleft() == 0 &&
conn_->tls.earlybuf.rleft() == 0)) {
return;
}

View File

@@ -67,7 +67,7 @@ void Router::add_node(RNode *node, const char *pattern, size_t patlen,
ssize_t index, ssize_t wildcard_index) {
auto pat = make_string_ref(balloc_, StringRef{pattern, patlen});
auto new_node =
make_unique<RNode>(pat.c_str(), pat.size(), index, wildcard_index);
std::make_unique<RNode>(pat.c_str(), pat.size(), index, wildcard_index);
add_next_node(node, std::move(new_node));
}
@@ -131,8 +131,8 @@ size_t Router::add_route(const StringRef &pattern, size_t idx, bool wildcard) {
if (node->len > j) {
// node must be split into 2 nodes. new_node is now the child
// of node.
auto new_node = make_unique<RNode>(&node->s[j], node->len - j,
node->index, node->wildcard_index);
auto new_node = std::make_unique<RNode>(
&node->s[j], node->len - j, node->index, node->wildcard_index);
std::swap(node->next, new_node->next);
node->len = j;
@@ -220,9 +220,16 @@ const RNode *match_partial(bool *pattern_is_wildcard, const RNode *node,
return node;
}
// The last '/' handling, see below.
node = find_next_node(node, '/');
if (node != nullptr && node->index != -1 && node->len == 1) {
return node;
}
return nullptr;
}
// The last '/' handling, see below.
if (node->index != -1 && offset + n + 1 == node->len &&
node->s[node->len - 1] == '/') {
return node;
@@ -265,6 +272,13 @@ const RNode *match_partial(bool *pattern_is_wildcard, const RNode *node,
return node;
}
// The last '/' handling, see below.
node = find_next_node(node, '/');
if (node != nullptr && node->index != -1 && node->len == 1) {
*pattern_is_wildcard = false;
return node;
}
return found_node;
}

View File

@@ -45,6 +45,9 @@ void test_shrpx_router_match(void) {
{StringRef::from_lit("www.nghttp2.org/alpha/"), 4},
{StringRef::from_lit("/alpha"), 5},
{StringRef::from_lit("example.com/alpha/"), 6},
{StringRef::from_lit("nghttp2.org/alpha/bravo2/"), 7},
{StringRef::from_lit("www2.nghttp2.org/alpha/"), 8},
{StringRef::from_lit("www2.nghttp2.org/alpha2/"), 9},
};
Router router;
@@ -84,6 +87,13 @@ void test_shrpx_router_match(void) {
idx = router.match(StringRef::from_lit("nghttp2.org"),
StringRef::from_lit("/alpha/bravo"));
CU_ASSERT(3 == idx);
idx = router.match(StringRef::from_lit("www2.nghttp2.org"),
StringRef::from_lit("/alpha"));
CU_ASSERT(8 == idx);
idx = router.match(StringRef{}, StringRef::from_lit("/alpha"));
CU_ASSERT(5 == idx);

View File

@@ -110,13 +110,13 @@ int signal_set_handler(void (*handler)(int), Signals &&sigs) {
} // namespace
namespace {
constexpr auto master_proc_ign_signals = std::array<int, 1>{{SIGPIPE}};
constexpr auto master_proc_ign_signals = std::array<int, 1>{SIGPIPE};
} // namespace
namespace {
constexpr auto worker_proc_ign_signals =
std::array<int, 5>{{REOPEN_LOG_SIGNAL, EXEC_BINARY_SIGNAL,
GRACEFUL_SHUTDOWN_SIGNAL, RELOAD_SIGNAL, SIGPIPE}};
std::array<int, 5>{REOPEN_LOG_SIGNAL, EXEC_BINARY_SIGNAL,
GRACEFUL_SHUTDOWN_SIGNAL, RELOAD_SIGNAL, SIGPIPE};
} // namespace
int shrpx_signal_set_master_proc_ign_handler() {

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