mirror of
https://github.com/nghttp2/nghttp2.git
synced 2025-12-07 18:48:54 +08:00
Compare commits
99 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6384375098 | ||
|
|
27801e98ae | ||
|
|
60e020a85b | ||
|
|
e520469b3e | ||
|
|
54067256eb | ||
|
|
c4d2eeeec7 | ||
|
|
f51e696e4a | ||
|
|
a433b132fc | ||
|
|
cf48a56d2e | ||
|
|
6cad1b243b | ||
|
|
3c393dca58 | ||
|
|
172924457f | ||
|
|
f6644a92af | ||
|
|
48998f7239 | ||
|
|
15ff52f9fb | ||
|
|
6c03bb142b | ||
|
|
524b439221 | ||
|
|
859bf2bc41 | ||
|
|
b5619fb156 | ||
|
|
dcbe0c690f | ||
|
|
2996c28456 | ||
|
|
42e8ceb656 | ||
|
|
1daf9ce8b7 | ||
|
|
d68edf56c0 | ||
|
|
0c4e9fef29 | ||
|
|
571404c6e8 | ||
|
|
4d562b773b | ||
|
|
e62258713e | ||
|
|
4bd075defd | ||
|
|
b46a324943 | ||
|
|
4bd44b9cdf | ||
|
|
1b42110d4f | ||
|
|
0735ec55f3 | ||
|
|
00554779e1 | ||
|
|
0963f38935 | ||
|
|
1abfa3ca5f | ||
|
|
f2159bc2c1 | ||
|
|
b0eb68ee9e | ||
|
|
e7b7b037f6 | ||
|
|
5e4f434fd8 | ||
|
|
20ea964f2f | ||
|
|
d105619bc3 | ||
|
|
ec5729b1fa | ||
|
|
6c9196953e | ||
|
|
46576178a3 | ||
|
|
5e925f873e | ||
|
|
153531d4d0 | ||
|
|
f7287df03f | ||
|
|
2b085815b7 | ||
|
|
986fa30264 | ||
|
|
7c8cb3a0ce | ||
|
|
334c439ce0 | ||
|
|
6c17ed7e61 | ||
|
|
6700626c30 | ||
|
|
15162addc4 | ||
|
|
9327077741 | ||
|
|
aeb92bbbe2 | ||
|
|
fc7489e044 | ||
|
|
87ac872fdc | ||
|
|
c278adde7a | ||
|
|
f94d720909 | ||
|
|
9b9baa6bd9 | ||
|
|
02566ee383 | ||
|
|
3002f31b1f | ||
|
|
d2a594a753 | ||
|
|
651e147711 | ||
|
|
a42faf1cc2 | ||
|
|
4aac05e193 | ||
|
|
b80dfaa8a0 | ||
|
|
a19d8f5d31 | ||
|
|
33f6e90a56 | ||
|
|
ed7fabcbc2 | ||
|
|
8753b6da14 | ||
|
|
f2de733bdf | ||
|
|
88ff8c69a0 | ||
|
|
a63558a1eb | ||
|
|
3575a1325e | ||
|
|
e2de2fee69 | ||
|
|
9f415979fb | ||
|
|
4bfc0cd196 | ||
|
|
9c824b87fe | ||
|
|
a1ea1696be | ||
|
|
dfc0f248c6 | ||
|
|
ed7c9db2a6 | ||
|
|
5b42815afb | ||
|
|
cfe7fa9a75 | ||
|
|
cb8a9d58fd | ||
|
|
023b94480b | ||
|
|
9b03c64f68 | ||
|
|
b8eccec62d | ||
|
|
9f21258720 | ||
|
|
47f6012407 | ||
|
|
770e44de4d | ||
|
|
2ab319c137 | ||
|
|
3992302432 | ||
|
|
b30f312a70 | ||
|
|
c5cdb78a95 | ||
|
|
d82811303b | ||
|
|
f79a58120e |
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
2
AUTHORS
2
AUTHORS
@@ -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
|
||||
|
||||
@@ -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})
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
11
doc/h2load.1
11
doc/h2load.1
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
.
|
||||
|
||||
@@ -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
|
||||
.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
-----------------------------------------
|
||||
|
||||
|
||||
@@ -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_')
|
||||
|
||||
@@ -67,6 +67,7 @@ HEADERS = [
|
||||
('keep-alive',None),
|
||||
('proxy-connection', None),
|
||||
('upgrade', None),
|
||||
(':protocol', None),
|
||||
]
|
||||
|
||||
def to_enum_hd(k):
|
||||
|
||||
@@ -50,4 +50,4 @@ if __name__ == '__main__':
|
||||
continue
|
||||
_, m, _ = line.split(',', 2)
|
||||
methods.append(m.strip())
|
||||
gentokenlookup(methods, 'HTTP')
|
||||
gentokenlookup(methods, 'HTTP_')
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
948
m4/ax_cxx_compile_stdcxx.m4
Normal 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
|
||||
|
||||
]])
|
||||
@@ -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
|
||||
])
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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() {}
|
||||
|
||||
|
||||
@@ -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() {}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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() {}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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() {}
|
||||
|
||||
|
||||
@@ -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() {}
|
||||
|
||||
|
||||
78
src/base64.h
78
src/base64.h
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
74
src/http2.cc
74
src/http2.cc
@@ -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
|
||||
|
||||
30
src/http2.h
30
src/http2.h
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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());
|
||||
|
||||
102
src/shrpx.cc
102
src/shrpx.cc
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
279
src/shrpx_log.cc
279
src/shrpx_log.cc
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
174
src/shrpx_log.h
174
src/shrpx_log.h
@@ -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 {
|
||||
|
||||
@@ -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(); }
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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_.
|
||||
|
||||
@@ -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() {}
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user