Merge pull request #2 from tatsuhiro-t/master
pulling changes from master
This commit is contained in:
commit
6cf772da6a
|
@ -23,6 +23,11 @@
|
||||||
SUBDIRS = lib third-party src examples python tests integration-tests \
|
SUBDIRS = lib third-party src examples python tests integration-tests \
|
||||||
doc contrib script
|
doc contrib script
|
||||||
|
|
||||||
|
# Now with python setuptools, make uninstall will leave many files we
|
||||||
|
# cannot easily remove (e.g., easy-install.pth). Disable it for
|
||||||
|
# distcheck rule.
|
||||||
|
AM_DISTCHECK_CONFIGURE_FLAGS = --disable-python-bindings
|
||||||
|
|
||||||
ACLOCAL_AMFLAGS = -I m4
|
ACLOCAL_AMFLAGS = -I m4
|
||||||
|
|
||||||
dist_doc_DATA = README.rst
|
dist_doc_DATA = README.rst
|
||||||
|
|
|
@ -25,7 +25,7 @@ dnl Do not change user variables!
|
||||||
dnl http://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html
|
dnl http://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html
|
||||||
|
|
||||||
AC_PREREQ(2.61)
|
AC_PREREQ(2.61)
|
||||||
AC_INIT([nghttp2], [1.0.6-DEV], [t-tujikawa@users.sourceforge.net])
|
AC_INIT([nghttp2], [1.1.2-DEV], [t-tujikawa@users.sourceforge.net])
|
||||||
AC_USE_SYSTEM_EXTENSIONS
|
AC_USE_SYSTEM_EXTENSIONS
|
||||||
|
|
||||||
LT_PREREQ([2.2.6])
|
LT_PREREQ([2.2.6])
|
||||||
|
@ -48,7 +48,7 @@ AC_CONFIG_HEADERS([config.h])
|
||||||
dnl See versioning rule:
|
dnl See versioning rule:
|
||||||
dnl http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
dnl http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
||||||
AC_SUBST(LT_CURRENT, 14)
|
AC_SUBST(LT_CURRENT, 14)
|
||||||
AC_SUBST(LT_REVISION, 5)
|
AC_SUBST(LT_REVISION, 6)
|
||||||
AC_SUBST(LT_AGE, 0)
|
AC_SUBST(LT_AGE, 0)
|
||||||
|
|
||||||
major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"`
|
major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"`
|
||||||
|
|
|
@ -8,7 +8,7 @@ _h2load()
|
||||||
_get_comp_words_by_ref cur prev
|
_get_comp_words_by_ref cur prev
|
||||||
case $cur in
|
case $cur in
|
||||||
-*)
|
-*)
|
||||||
COMPREPLY=( $( compgen -W '--threads --connection-window-bits --input-file --help --requests --data --verbose --version --window-bits --clients --no-tls-proto --header --max-concurrent-streams ' -- "$cur" ) )
|
COMPREPLY=( $( compgen -W '--threads --connection-window-bits --input-file --help --requests --data --verbose --ciphers --window-bits --clients --no-tls-proto --version --header --max-concurrent-streams ' -- "$cur" ) )
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
_filedir
|
_filedir
|
||||||
|
|
|
@ -8,7 +8,7 @@ _nghttp()
|
||||||
_get_comp_words_by_ref cur prev
|
_get_comp_words_by_ref cur prev
|
||||||
case $cur in
|
case $cur in
|
||||||
-*)
|
-*)
|
||||||
COMPREPLY=( $( compgen -W '--no-push --verbose --no-dep --get-assets --har --header-table-size --multiply --padding --hexdump --continuation --connection-window-bits --peer-max-concurrent-streams --timeout --data --no-content-length --version --color --cert --upgrade --remote-name --trailer --weight --help --key --null-out --window-bits --stat --header ' -- "$cur" ) )
|
COMPREPLY=( $( compgen -W '--no-push --verbose --no-dep --get-assets --har --header-table-size --multiply --padding --hexdump --max-concurrent-streams --continuation --connection-window-bits --peer-max-concurrent-streams --timeout --data --no-content-length --version --color --cert --upgrade --remote-name --trailer --weight --help --key --null-out --window-bits --stat --header ' -- "$cur" ) )
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
_filedir
|
_filedir
|
||||||
|
|
|
@ -8,7 +8,7 @@ _nghttpx()
|
||||||
_get_comp_words_by_ref cur prev
|
_get_comp_words_by_ref cur prev
|
||||||
case $cur in
|
case $cur in
|
||||||
-*)
|
-*)
|
||||||
COMPREPLY=( $( compgen -W '--worker-read-rate --frontend-no-tls --frontend-http2-dump-response-header --backend-http1-connections-per-frontend --tls-ticket-key-file --verify-client-cacert --backend-request-buffer --backend-http2-connection-window-bits --conf --worker-write-burst --npn-list --fetch-ocsp-response-file --stream-read-timeout --accesslog-syslog --frontend-http2-read-timeout --listener-disable-timeout --frontend-http2-connection-window-bits --ciphers --strip-incoming-x-forwarded-for --daemon --backend-keep-alive-timeout --backend-http-proxy-uri --backend-http1-connections-per-host --rlimit-nofile --no-via --ocsp-update-interval --backend-write-timeout --client --http2-no-cookie-crumbling --worker-read-burst --client-proxy --http2-bridge --accesslog-format --errorlog-syslog --errorlog-file --http2-max-concurrent-streams --frontend-write-timeout --read-burst --backend-ipv4 --backend-ipv6 --backend --insecure --log-level --tls-proto-list --backend-http2-connections-per-worker --dh-param-file --worker-frontend-connections --header-field-buffer --no-server-push --no-location-rewrite --no-ocsp --backend-response-buffer --workers --frontend-http2-window-bits --no-host-rewrite --worker-write-rate --add-request-header --backend-tls-sni-field --subcert --help --frontend-frame-debug --pid-file --frontend-http2-dump-request-header --private-key-passwd-file --write-rate --altsvc --user --add-x-forwarded-for --syslog-facility --frontend-read-timeout --backlog --write-burst --backend-http2-window-bits --padding --stream-write-timeout --cacert --version --verify-client --backend-read-timeout --frontend --accesslog-file --http2-proxy --max-header-fields --backend-no-tls --client-private-key-file --client-cert-file --add-response-header --read-rate ' -- "$cur" ) )
|
COMPREPLY=( $( compgen -W '--worker-read-rate --frontend-no-tls --frontend-http2-dump-response-header --backend-http1-connections-per-frontend --tls-ticket-key-file --verify-client-cacert --include --backend-request-buffer --backend-http2-connection-window-bits --conf --worker-write-burst --npn-list --fetch-ocsp-response-file --stream-read-timeout --accesslog-syslog --frontend-http2-read-timeout --listener-disable-timeout --frontend-http2-connection-window-bits --ciphers --strip-incoming-x-forwarded-for --daemon --backend-keep-alive-timeout --backend-http-proxy-uri --backend-http1-connections-per-host --rlimit-nofile --no-via --ocsp-update-interval --backend-write-timeout --client --http2-no-cookie-crumbling --worker-read-burst --client-proxy --http2-bridge --accesslog-format --errorlog-syslog --errorlog-file --http2-max-concurrent-streams --frontend-write-timeout --read-burst --backend-ipv4 --backend-ipv6 --backend --insecure --log-level --tls-proto-list --backend-http2-connections-per-worker --dh-param-file --worker-frontend-connections --header-field-buffer --no-server-push --no-location-rewrite --no-ocsp --backend-response-buffer --workers --frontend-http2-window-bits --no-host-rewrite --worker-write-rate --add-request-header --backend-tls-sni-field --subcert --help --frontend-frame-debug --pid-file --frontend-http2-dump-request-header --private-key-passwd-file --write-rate --altsvc --user --add-x-forwarded-for --syslog-facility --frontend-read-timeout --backlog --write-burst --backend-http2-window-bits --padding --stream-write-timeout --cacert --version --verify-client --backend-read-timeout --frontend --accesslog-file --http2-proxy --max-header-fields --backend-no-tls --client-private-key-file --client-cert-file --add-response-header --read-rate ' -- "$cur" ) )
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
_filedir
|
_filedir
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
.\" Man page generated from reStructuredText.
|
.\" Man page generated from reStructuredText.
|
||||||
.
|
.
|
||||||
.TH "H2LOAD" "1" "June 27, 2015" "1.0.5" "nghttp2"
|
.TH "H2LOAD" "1" "July 15, 2015" "1.1.1" "nghttp2"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
h2load \- HTTP/2 benchmarking tool
|
h2load \- HTTP/2 benchmarking tool
|
||||||
.
|
.
|
||||||
|
@ -113,6 +113,12 @@ Add/Override a header to the requests.
|
||||||
.UNINDENT
|
.UNINDENT
|
||||||
.INDENT 0.0
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
|
.B \-\-ciphers=<SUITE>
|
||||||
|
Set allowed cipher list. The format of the string is
|
||||||
|
described in OpenSSL ciphers(1).
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
|
.TP
|
||||||
.B \-p, \-\-no\-tls\-proto=<PROTOID>
|
.B \-p, \-\-no\-tls\-proto=<PROTOID>
|
||||||
Specify ALPN identifier of the protocol to be used when
|
Specify ALPN identifier of the protocol to be used when
|
||||||
accessing http URI without SSL/TLS.
|
accessing http URI without SSL/TLS.
|
||||||
|
|
|
@ -84,6 +84,11 @@ OPTIONS
|
||||||
|
|
||||||
Add/Override a header to the requests.
|
Add/Override a header to the requests.
|
||||||
|
|
||||||
|
.. option:: --ciphers=<SUITE>
|
||||||
|
|
||||||
|
Set allowed cipher list. The format of the string is
|
||||||
|
described in OpenSSL ciphers(1).
|
||||||
|
|
||||||
.. option:: -p, --no-tls-proto=<PROTOID>
|
.. option:: -p, --no-tls-proto=<PROTOID>
|
||||||
|
|
||||||
Specify ALPN identifier of the protocol to be used when
|
Specify ALPN identifier of the protocol to be used when
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
.\" Man page generated from reStructuredText.
|
.\" Man page generated from reStructuredText.
|
||||||
.
|
.
|
||||||
.TH "NGHTTP" "1" "June 27, 2015" "1.0.5" "nghttp2"
|
.TH "NGHTTP" "1" "July 15, 2015" "1.1.1" "nghttp2"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
nghttp \- HTTP/2 experimental client
|
nghttp \- HTTP/2 experimental client
|
||||||
.
|
.
|
||||||
|
@ -205,6 +205,12 @@ Disable server push.
|
||||||
.UNINDENT
|
.UNINDENT
|
||||||
.INDENT 0.0
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
|
.B \-\-max\-concurrent\-streams=<N>
|
||||||
|
The number of concurrent pushed streams this client
|
||||||
|
accepts.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
|
.TP
|
||||||
.B \-\-version
|
.B \-\-version
|
||||||
Display version information and exit.
|
Display version information and exit.
|
||||||
.UNINDENT
|
.UNINDENT
|
||||||
|
|
|
@ -158,6 +158,11 @@ OPTIONS
|
||||||
|
|
||||||
Disable server push.
|
Disable server push.
|
||||||
|
|
||||||
|
.. option:: --max-concurrent-streams=<N>
|
||||||
|
|
||||||
|
The number of concurrent pushed streams this client
|
||||||
|
accepts.
|
||||||
|
|
||||||
.. option:: --version
|
.. option:: --version
|
||||||
|
|
||||||
Display version information and exit.
|
Display version information and exit.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
.\" Man page generated from reStructuredText.
|
.\" Man page generated from reStructuredText.
|
||||||
.
|
.
|
||||||
.TH "NGHTTPD" "1" "June 27, 2015" "1.0.5" "nghttp2"
|
.TH "NGHTTPD" "1" "July 15, 2015" "1.1.1" "nghttp2"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
nghttpd \- HTTP/2 experimental server
|
nghttpd \- HTTP/2 experimental server
|
||||||
.
|
.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
.\" Man page generated from reStructuredText.
|
.\" Man page generated from reStructuredText.
|
||||||
.
|
.
|
||||||
.TH "NGHTTPX" "1" "June 27, 2015" "1.0.5" "nghttp2"
|
.TH "NGHTTPX" "1" "July 15, 2015" "1.1.1" "nghttp2"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
nghttpx \- HTTP/2 experimental proxy
|
nghttpx \- HTTP/2 experimental proxy
|
||||||
.
|
.
|
||||||
|
@ -55,17 +55,69 @@ The options are categorized into several groups.
|
||||||
.SS Connections
|
.SS Connections
|
||||||
.INDENT 0.0
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
.B \-b, \-\-backend=<HOST,PORT>
|
.B \-b, \-\-backend=(<HOST>,<PORT>|unix:<PATH>)[;<PATTERN>[:...]]
|
||||||
Set backend host and port. The multiple backend
|
Set backend host and port. The multiple backend
|
||||||
addresses are accepted by repeating this option. UNIX
|
addresses are accepted by repeating this option. UNIX
|
||||||
domain socket can be specified by prefixing path name
|
domain socket can be specified by prefixing path name
|
||||||
with "unix:" (e.g., unix:/var/run/backend.sock)
|
with "unix:" (e.g., unix:/var/run/backend.sock).
|
||||||
|
.sp
|
||||||
|
Optionally, if <PATTERN>s are given, the backend address
|
||||||
|
is only used if request matches the pattern. If \fI\%\-s\fP or
|
||||||
|
\fI\%\-p\fP is used, <PATTERN>s are ignored. The pattern
|
||||||
|
matching is closely designed to ServeMux in net/http
|
||||||
|
package of Go programming language. <PATTERN> consists
|
||||||
|
of path, host + path or just host. The path must start
|
||||||
|
with "\fI/\fP". If it ends with "\fI/\fP", it matches all request
|
||||||
|
path in its subtree. To deal with the request to the
|
||||||
|
directory without trailing slash, the path which ends
|
||||||
|
with "\fI/\fP" also matches the request path which only lacks
|
||||||
|
trailing \(aq\fI/\fP\(aq (e.g., path "\fI/foo/\fP" matches request path
|
||||||
|
"\fI/foo\fP"). If it does not end with "\fI/\fP", it performs exact
|
||||||
|
match against the request path. If host is given, it
|
||||||
|
performs exact match against the request host. If host
|
||||||
|
alone is given, "\fI/\fP" is appended to it, so that it
|
||||||
|
matches all request paths under the host (e.g.,
|
||||||
|
specifying "nghttp2.org" equals to "nghttp2.org/").
|
||||||
|
.sp
|
||||||
|
Patterns with host take precedence over patterns with
|
||||||
|
just path. Then, longer patterns take precedence over
|
||||||
|
shorter ones, breaking a tie by the order of the
|
||||||
|
appearance in the configuration.
|
||||||
|
.sp
|
||||||
|
If <PATTERN> is omitted, "\fI/\fP" is used as pattern, which
|
||||||
|
matches all request paths (catch\-all pattern). The
|
||||||
|
catch\-all backend must be given.
|
||||||
|
.sp
|
||||||
|
When doing a match, nghttpx made some normalization to
|
||||||
|
pattern, request host and path. For host part, they are
|
||||||
|
converted to lower case. For path part, percent\-encoded
|
||||||
|
unreserved characters defined in RFC 3986 are decoded,
|
||||||
|
and any dot\-segments (".." and ".") are resolved and
|
||||||
|
removed.
|
||||||
|
.sp
|
||||||
|
For example, \fI\%\-b\fP\(aq127.0.0.1,8080;nghttp2.org/httpbin/\(aq
|
||||||
|
matches the request host "nghttp2.org" and the request
|
||||||
|
path "\fI/httpbin/get\fP", but does not match the request host
|
||||||
|
"nghttp2.org" and the request path "\fI/index.html\fP".
|
||||||
|
.sp
|
||||||
|
The multiple <PATTERN>s can be specified, delimiting
|
||||||
|
them by ":". Specifying
|
||||||
|
\fI\%\-b\fP\(aq127.0.0.1,8080;nghttp2.org:www.nghttp2.org\(aq has the
|
||||||
|
same effect to specify \fI\%\-b\fP\(aq127.0.0.1,8080;nghttp2.org\(aq
|
||||||
|
and \fI\%\-b\fP\(aq127.0.0.1,8080;www.nghttp2.org\(aq.
|
||||||
|
.sp
|
||||||
|
The backend addresses sharing same <PATTERN> are grouped
|
||||||
|
together forming load balancing group.
|
||||||
|
.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.
|
||||||
.sp
|
.sp
|
||||||
Default: \fB127.0.0.1,80\fP
|
Default: \fB127.0.0.1,80\fP
|
||||||
.UNINDENT
|
.UNINDENT
|
||||||
.INDENT 0.0
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
.B \-f, \-\-frontend=<HOST,PORT>
|
.B \-f, \-\-frontend=(<HOST>,<PORT>|unix:<PATH>)
|
||||||
Set frontend host and port. If <HOST> is \(aq*\(aq, it
|
Set frontend host and port. If <HOST> is \(aq*\(aq, it
|
||||||
assumes all addresses including both IPv4 and IPv6.
|
assumes all addresses including both IPv4 and IPv6.
|
||||||
UNIX domain socket can be specified by prefixing path
|
UNIX domain socket can be specified by prefixing path
|
||||||
|
@ -195,9 +247,14 @@ Default: \fB0\fP
|
||||||
.INDENT 0.0
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
.B \-\-backend\-http2\-connections\-per\-worker=<N>
|
.B \-\-backend\-http2\-connections\-per\-worker=<N>
|
||||||
Set maximum number of HTTP/2 connections per worker.
|
Set maximum number of backend HTTP/2 physical
|
||||||
The default value is 0, which means the number of
|
connections per worker. If pattern is used in \fI\%\-b\fP
|
||||||
backend addresses specified by \fI\%\-b\fP option.
|
option, this limit is applied to each pattern group (in
|
||||||
|
other words, each pattern group can have maximum <N>
|
||||||
|
HTTP/2 connections). The default value is 0, which
|
||||||
|
means that the value is adjusted to the number of
|
||||||
|
backend addresses. If pattern is used, this adjustment
|
||||||
|
is done for each pattern group.
|
||||||
.UNINDENT
|
.UNINDENT
|
||||||
.INDENT 0.0
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
|
@ -626,8 +683,20 @@ $pid: PID of the running process.
|
||||||
$alpn: ALPN identifier of the protocol which generates
|
$alpn: ALPN identifier of the protocol which generates
|
||||||
the response. For HTTP/1, ALPN is always http/1.1,
|
the response. For HTTP/1, ALPN is always http/1.1,
|
||||||
regardless of minor version.
|
regardless of minor version.
|
||||||
|
.IP \(bu 2
|
||||||
|
$ssl_cipher: cipher used for SSL/TLS connection.
|
||||||
|
.IP \(bu 2
|
||||||
|
$ssl_protocol: protocol for SSL/TLS connection.
|
||||||
|
.IP \(bu 2
|
||||||
|
$ssl_session_id: session ID for SSL/TLS connection.
|
||||||
|
.IP \(bu 2
|
||||||
|
$ssl_session_reused: "r" if SSL/TLS session was
|
||||||
|
reused. Otherwise, "."
|
||||||
.UNINDENT
|
.UNINDENT
|
||||||
.sp
|
.sp
|
||||||
|
The variable can be enclosed by "{" and "}" for
|
||||||
|
disambiguation (e.g., ${remote_addr}).
|
||||||
|
.sp
|
||||||
Default: \fB$remote_addr \- \- [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"\fP
|
Default: \fB$remote_addr \- \- [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"\fP
|
||||||
.UNINDENT
|
.UNINDENT
|
||||||
.INDENT 0.0
|
.INDENT 0.0
|
||||||
|
@ -786,6 +855,14 @@ Default: \fB/etc/nghttpx/nghttpx.conf\fP
|
||||||
.UNINDENT
|
.UNINDENT
|
||||||
.INDENT 0.0
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
|
.B \-\-include=<PATH>
|
||||||
|
Load additional configurations from <PATH>. File <PATH>
|
||||||
|
is read when configuration parser encountered this
|
||||||
|
option. This option can be used multiple times, or even
|
||||||
|
recursively.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
|
.TP
|
||||||
.B \-v, \-\-version
|
.B \-v, \-\-version
|
||||||
Print version and exit.
|
Print version and exit.
|
||||||
.UNINDENT
|
.UNINDENT
|
||||||
|
|
|
@ -37,16 +37,69 @@ The options are categorized into several groups.
|
||||||
Connections
|
Connections
|
||||||
~~~~~~~~~~~
|
~~~~~~~~~~~
|
||||||
|
|
||||||
.. option:: -b, --backend=<HOST,PORT>
|
.. option:: -b, --backend=(<HOST>,<PORT>|unix:<PATH>)[;<PATTERN>[:...]]
|
||||||
|
|
||||||
Set backend host and port. The multiple backend
|
Set backend host and port. The multiple backend
|
||||||
addresses are accepted by repeating this option. UNIX
|
addresses are accepted by repeating this option. UNIX
|
||||||
domain socket can be specified by prefixing path name
|
domain socket can be specified by prefixing path name
|
||||||
with "unix:" (e.g., unix:/var/run/backend.sock)
|
with "unix:" (e.g., unix:/var/run/backend.sock).
|
||||||
|
|
||||||
|
Optionally, if <PATTERN>s are given, the backend address
|
||||||
|
is only used if request matches the pattern. If :option:`-s` or
|
||||||
|
:option:`-p` is used, <PATTERN>s are ignored. The pattern
|
||||||
|
matching is closely designed to ServeMux in net/http
|
||||||
|
package of Go programming language. <PATTERN> consists
|
||||||
|
of path, host + path or just host. The path must start
|
||||||
|
with "*/*". If it ends with "*/*", it matches all request
|
||||||
|
path in its subtree. To deal with the request to the
|
||||||
|
directory without trailing slash, the path which ends
|
||||||
|
with "*/*" also matches the request path which only lacks
|
||||||
|
trailing '*/*' (e.g., path "*/foo/*" matches request path
|
||||||
|
"*/foo*"). If it does not end with "*/*", it performs exact
|
||||||
|
match against the request path. If host is given, it
|
||||||
|
performs exact match against the request host. If host
|
||||||
|
alone is given, "*/*" is appended to it, so that it
|
||||||
|
matches all request paths under the host (e.g.,
|
||||||
|
specifying "nghttp2.org" equals to "nghttp2.org/").
|
||||||
|
|
||||||
|
Patterns with host take precedence over patterns with
|
||||||
|
just path. Then, longer patterns take precedence over
|
||||||
|
shorter ones, breaking a tie by the order of the
|
||||||
|
appearance in the configuration.
|
||||||
|
|
||||||
|
If <PATTERN> is omitted, "*/*" is used as pattern, which
|
||||||
|
matches all request paths (catch-all pattern). The
|
||||||
|
catch-all backend must be given.
|
||||||
|
|
||||||
|
When doing a match, nghttpx made some normalization to
|
||||||
|
pattern, request host and path. For host part, they are
|
||||||
|
converted to lower case. For path part, percent-encoded
|
||||||
|
unreserved characters defined in RFC 3986 are decoded,
|
||||||
|
and any dot-segments (".." and ".") are resolved and
|
||||||
|
removed.
|
||||||
|
|
||||||
|
For example, :option:`-b`\'127.0.0.1,8080;nghttp2.org/httpbin/'
|
||||||
|
matches the request host "nghttp2.org" and the request
|
||||||
|
path "*/httpbin/get*", but does not match the request host
|
||||||
|
"nghttp2.org" and the request path "*/index.html*".
|
||||||
|
|
||||||
|
The multiple <PATTERN>s can be specified, delimiting
|
||||||
|
them by ":". Specifying
|
||||||
|
:option:`-b`\'127.0.0.1,8080;nghttp2.org:www.nghttp2.org' has the
|
||||||
|
same effect to specify :option:`-b`\'127.0.0.1,8080;nghttp2.org'
|
||||||
|
and :option:`-b`\'127.0.0.1,8080;www.nghttp2.org'.
|
||||||
|
|
||||||
|
The backend addresses sharing same <PATTERN> are grouped
|
||||||
|
together forming load balancing group.
|
||||||
|
|
||||||
|
Since ";" and ":" are used as delimiter, <PATTERN> must
|
||||||
|
not contain these characters. Since ";" has special
|
||||||
|
meaning in shell, the option value must be quoted.
|
||||||
|
|
||||||
|
|
||||||
Default: ``127.0.0.1,80``
|
Default: ``127.0.0.1,80``
|
||||||
|
|
||||||
.. option:: -f, --frontend=<HOST,PORT>
|
.. option:: -f, --frontend=(<HOST>,<PORT>|unix:<PATH>)
|
||||||
|
|
||||||
Set frontend host and port. If <HOST> is '\*', it
|
Set frontend host and port. If <HOST> is '\*', it
|
||||||
assumes all addresses including both IPv4 and IPv6.
|
assumes all addresses including both IPv4 and IPv6.
|
||||||
|
@ -165,9 +218,14 @@ Performance
|
||||||
|
|
||||||
.. option:: --backend-http2-connections-per-worker=<N>
|
.. option:: --backend-http2-connections-per-worker=<N>
|
||||||
|
|
||||||
Set maximum number of HTTP/2 connections per worker.
|
Set maximum number of backend HTTP/2 physical
|
||||||
The default value is 0, which means the number of
|
connections per worker. If pattern is used in :option:`-b`
|
||||||
backend addresses specified by :option:`-b` option.
|
option, this limit is applied to each pattern group (in
|
||||||
|
other words, each pattern group can have maximum <N>
|
||||||
|
HTTP/2 connections). The default value is 0, which
|
||||||
|
means that the value is adjusted to the number of
|
||||||
|
backend addresses. If pattern is used, this adjustment
|
||||||
|
is done for each pattern group.
|
||||||
|
|
||||||
.. option:: --backend-http1-connections-per-host=<N>
|
.. option:: --backend-http1-connections-per-host=<N>
|
||||||
|
|
||||||
|
@ -550,6 +608,14 @@ Logging
|
||||||
* $alpn: ALPN identifier of the protocol which generates
|
* $alpn: ALPN identifier of the protocol which generates
|
||||||
the response. For HTTP/1, ALPN is always http/1.1,
|
the response. For HTTP/1, ALPN is always http/1.1,
|
||||||
regardless of minor version.
|
regardless of minor version.
|
||||||
|
* $ssl_cipher: cipher used for SSL/TLS connection.
|
||||||
|
* $ssl_protocol: protocol for SSL/TLS connection.
|
||||||
|
* $ssl_session_id: session ID for SSL/TLS connection.
|
||||||
|
* $ssl_session_reused: "r" if SSL/TLS session was
|
||||||
|
reused. Otherwise, "."
|
||||||
|
|
||||||
|
The variable can be enclosed by "{" and "}" for
|
||||||
|
disambiguation (e.g., ${remote_addr}).
|
||||||
|
|
||||||
|
|
||||||
Default: ``$remote_addr - - [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"``
|
Default: ``$remote_addr - - [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"``
|
||||||
|
@ -700,6 +766,13 @@ Misc
|
||||||
|
|
||||||
Default: ``/etc/nghttpx/nghttpx.conf``
|
Default: ``/etc/nghttpx/nghttpx.conf``
|
||||||
|
|
||||||
|
.. option:: --include=<PATH>
|
||||||
|
|
||||||
|
Load additional configurations from <PATH>. File <PATH>
|
||||||
|
is read when configuration parser encountered this
|
||||||
|
option. This option can be used multiple times, or even
|
||||||
|
recursively.
|
||||||
|
|
||||||
.. option:: -v, --version
|
.. option:: -v, --version
|
||||||
|
|
||||||
Print version and exit.
|
Print version and exit.
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
from gentokenlookup import gentokenlookup
|
||||||
|
|
||||||
|
OPTIONS = [
|
||||||
|
"private-key-file",
|
||||||
|
"private-key-passwd-file",
|
||||||
|
"certificate-file",
|
||||||
|
"dh-param-file",
|
||||||
|
"subcert",
|
||||||
|
"backend",
|
||||||
|
"frontend",
|
||||||
|
"workers",
|
||||||
|
"http2-max-concurrent-streams",
|
||||||
|
"log-level",
|
||||||
|
"daemon",
|
||||||
|
"http2-proxy",
|
||||||
|
"http2-bridge",
|
||||||
|
"client-proxy",
|
||||||
|
"add-x-forwarded-for",
|
||||||
|
"strip-incoming-x-forwarded-for",
|
||||||
|
"no-via",
|
||||||
|
"frontend-http2-read-timeout",
|
||||||
|
"frontend-read-timeout",
|
||||||
|
"frontend-write-timeout",
|
||||||
|
"backend-read-timeout",
|
||||||
|
"backend-write-timeout",
|
||||||
|
"stream-read-timeout",
|
||||||
|
"stream-write-timeout",
|
||||||
|
"accesslog-file",
|
||||||
|
"accesslog-syslog",
|
||||||
|
"accesslog-format",
|
||||||
|
"errorlog-file",
|
||||||
|
"errorlog-syslog",
|
||||||
|
"backend-keep-alive-timeout",
|
||||||
|
"frontend-http2-window-bits",
|
||||||
|
"backend-http2-window-bits",
|
||||||
|
"frontend-http2-connection-window-bits",
|
||||||
|
"backend-http2-connection-window-bits",
|
||||||
|
"frontend-no-tls",
|
||||||
|
"backend-no-tls",
|
||||||
|
"backend-tls-sni-field",
|
||||||
|
"pid-file",
|
||||||
|
"user",
|
||||||
|
"syslog-facility",
|
||||||
|
"backlog",
|
||||||
|
"ciphers",
|
||||||
|
"client",
|
||||||
|
"insecure",
|
||||||
|
"cacert",
|
||||||
|
"backend-ipv4",
|
||||||
|
"backend-ipv6",
|
||||||
|
"backend-http-proxy-uri",
|
||||||
|
"read-rate",
|
||||||
|
"read-burst",
|
||||||
|
"write-rate",
|
||||||
|
"write-burst",
|
||||||
|
"worker-read-rate",
|
||||||
|
"worker-read-burst",
|
||||||
|
"worker-write-rate",
|
||||||
|
"worker-write-burst",
|
||||||
|
"npn-list",
|
||||||
|
"tls-proto-list",
|
||||||
|
"verify-client",
|
||||||
|
"verify-client-cacert",
|
||||||
|
"client-private-key-file",
|
||||||
|
"client-cert-file",
|
||||||
|
"frontend-http2-dump-request-header",
|
||||||
|
"frontend-http2-dump-response-header",
|
||||||
|
"http2-no-cookie-crumbling",
|
||||||
|
"frontend-frame-debug",
|
||||||
|
"padding",
|
||||||
|
"altsvc",
|
||||||
|
"add-request-header",
|
||||||
|
"add-response-header",
|
||||||
|
"worker-frontend-connections",
|
||||||
|
"no-location-rewrite",
|
||||||
|
"no-host-rewrite",
|
||||||
|
"backend-http1-connections-per-host",
|
||||||
|
"backend-http1-connections-per-frontend",
|
||||||
|
"listener-disable-timeout",
|
||||||
|
"tls-ticket-key-file",
|
||||||
|
"rlimit-nofile",
|
||||||
|
"backend-request-buffer",
|
||||||
|
"backend-response-buffer",
|
||||||
|
"no-server-push",
|
||||||
|
"backend-http2-connections-per-worker",
|
||||||
|
"fetch-ocsp-response-file",
|
||||||
|
"ocsp-update-interval",
|
||||||
|
"no-ocsp",
|
||||||
|
"header-field-buffer",
|
||||||
|
"max-header-fields",
|
||||||
|
"include",
|
||||||
|
"conf",
|
||||||
|
]
|
||||||
|
|
||||||
|
LOGVARS = [
|
||||||
|
"remote_addr",
|
||||||
|
"time_local",
|
||||||
|
"time_iso8601",
|
||||||
|
"request",
|
||||||
|
"status",
|
||||||
|
"body_bytes_sent",
|
||||||
|
"remote_port",
|
||||||
|
"server_port",
|
||||||
|
"request_time",
|
||||||
|
"pid",
|
||||||
|
"alpn",
|
||||||
|
"ssl_cipher",
|
||||||
|
"ssl_protocol",
|
||||||
|
"ssl_session_id",
|
||||||
|
"ssl_session_reused",
|
||||||
|
]
|
||||||
|
|
||||||
|
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')
|
|
@ -33,10 +33,10 @@ enum {'''
|
||||||
{}_MAXIDX,
|
{}_MAXIDX,
|
||||||
}};'''.format(prefix)
|
}};'''.format(prefix)
|
||||||
|
|
||||||
def gen_index_header(tokens, prefix):
|
def gen_index_header(tokens, prefix, value_type, comp_fun, return_type, fail_value):
|
||||||
print '''\
|
print '''\
|
||||||
int lookup_token(const uint8_t *name, size_t namelen) {
|
{} lookup_token(const {} *name, size_t namelen) {{
|
||||||
switch (namelen) {'''
|
switch (namelen) {{'''.format(return_type, value_type)
|
||||||
b = build_header(tokens)
|
b = build_header(tokens)
|
||||||
for size in sorted(b.keys()):
|
for size in sorted(b.keys()):
|
||||||
ents = b[size]
|
ents = b[size]
|
||||||
|
@ -50,20 +50,20 @@ int lookup_token(const uint8_t *name, size_t namelen) {
|
||||||
case '{}':'''.format(c)
|
case '{}':'''.format(c)
|
||||||
for k in headers:
|
for k in headers:
|
||||||
print '''\
|
print '''\
|
||||||
if (util::streq_l("{}", name, {})) {{
|
if ({}("{}", name, {})) {{
|
||||||
return {};
|
return {};
|
||||||
}}'''.format(k[:-1], size - 1, to_enum_hd(k, prefix))
|
}}'''.format(comp_fun, k[:-1], size - 1, to_enum_hd(k, prefix))
|
||||||
print '''\
|
print '''\
|
||||||
break;'''
|
break;'''
|
||||||
print '''\
|
print '''\
|
||||||
}
|
}
|
||||||
break;'''
|
break;'''
|
||||||
print '''\
|
print '''\
|
||||||
}
|
}}
|
||||||
return -1;
|
return {};
|
||||||
}'''
|
}}'''.format(fail_value)
|
||||||
|
|
||||||
def gentokenlookup(tokens, prefix):
|
def gentokenlookup(tokens, prefix, value_type='uint8_t', comp_fun='util::streq_l', return_type='int', fail_value='-1'):
|
||||||
gen_enum(tokens, prefix)
|
gen_enum(tokens, prefix)
|
||||||
print ''
|
print ''
|
||||||
gen_index_header(tokens, prefix)
|
gen_index_header(tokens, prefix, value_type, comp_fun, return_type, fail_value)
|
||||||
|
|
|
@ -22,7 +22,6 @@
|
||||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
from setuptools import setup, Extension
|
from setuptools import setup, Extension
|
||||||
from Cython.Build import cythonize
|
|
||||||
|
|
||||||
LIBS = ['nghttp2']
|
LIBS = ['nghttp2']
|
||||||
|
|
||||||
|
@ -34,13 +33,13 @@ setup(
|
||||||
author_email = 'tatsuhiro.t@gmail.com',
|
author_email = 'tatsuhiro.t@gmail.com',
|
||||||
url = 'https://nghttp2.org/',
|
url = 'https://nghttp2.org/',
|
||||||
keywords = [],
|
keywords = [],
|
||||||
ext_modules = cythonize([Extension("nghttp2",
|
ext_modules = [Extension("nghttp2",
|
||||||
["nghttp2.pyx"],
|
["nghttp2.c"],
|
||||||
include_dirs=['@top_srcdir@/lib',
|
include_dirs=['@top_srcdir@/lib',
|
||||||
'@top_srcdir@/lib/includes',
|
'@top_srcdir@/lib/includes',
|
||||||
'@top_builddir@/lib/includes'],
|
'@top_builddir@/lib/includes'],
|
||||||
library_dirs=['@top_builddir@/lib/.libs',
|
library_dirs=['@top_builddir@/lib/.libs',
|
||||||
'@top_builddir@'],
|
'@top_builddir@'],
|
||||||
libraries=LIBS)]),
|
libraries=LIBS)],
|
||||||
long_description='TBD'
|
long_description='TBD'
|
||||||
)
|
)
|
||||||
|
|
|
@ -207,10 +207,10 @@ libnghttp2_asio_la_LDFLAGS = -no-undefined -version-info 1:0:0
|
||||||
libnghttp2_asio_la_LIBADD = \
|
libnghttp2_asio_la_LIBADD = \
|
||||||
$(top_builddir)/lib/libnghttp2.la \
|
$(top_builddir)/lib/libnghttp2.la \
|
||||||
$(top_builddir)/third-party/libhttp-parser.la \
|
$(top_builddir)/third-party/libhttp-parser.la \
|
||||||
|
@OPENSSL_LIBS@ \
|
||||||
${BOOST_LDFLAGS} \
|
${BOOST_LDFLAGS} \
|
||||||
${BOOST_ASIO_LIB} \
|
${BOOST_ASIO_LIB} \
|
||||||
${BOOST_THREAD_LIB} \
|
${BOOST_THREAD_LIB} \
|
||||||
${BOOST_SYSTEM_LIB} \
|
${BOOST_SYSTEM_LIB}
|
||||||
@OPENSSL_LIBS@
|
|
||||||
|
|
||||||
endif # ENABLE_ASIO_LIB
|
endif # ENABLE_ASIO_LIB
|
||||||
|
|
51
src/http2.h
51
src/http2.h
|
@ -37,6 +37,8 @@
|
||||||
|
|
||||||
#include "http-parser/http_parser.h"
|
#include "http-parser/http_parser.h"
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
namespace nghttp2 {
|
namespace nghttp2 {
|
||||||
|
|
||||||
struct Header {
|
struct Header {
|
||||||
|
@ -298,6 +300,55 @@ int lookup_method_token(const std::string &name);
|
||||||
|
|
||||||
const char *to_method_string(int method_token);
|
const char *to_method_string(int method_token);
|
||||||
|
|
||||||
|
template <typename InputIt>
|
||||||
|
std::string normalize_path(InputIt first, InputIt last) {
|
||||||
|
// First, decode %XX for unreserved characters, then do
|
||||||
|
// http2::join_path
|
||||||
|
std::string result;
|
||||||
|
// We won't find %XX if length is less than 3.
|
||||||
|
if (last - first < 3) {
|
||||||
|
result.assign(first, last);
|
||||||
|
} else {
|
||||||
|
for (; first < last - 2;) {
|
||||||
|
if (*first == '%') {
|
||||||
|
if (util::isHexDigit(*(first + 1)) && util::isHexDigit(*(first + 2))) {
|
||||||
|
auto c = (util::hex_to_uint(*(first + 1)) << 4) +
|
||||||
|
util::hex_to_uint(*(first + 2));
|
||||||
|
if (util::inRFC3986UnreservedChars(c)) {
|
||||||
|
result += c;
|
||||||
|
first += 3;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
result += '%';
|
||||||
|
result += util::upcase(*(first + 1));
|
||||||
|
result += util::upcase(*(first + 2));
|
||||||
|
first += 3;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result += *first++;
|
||||||
|
}
|
||||||
|
result.append(first, last);
|
||||||
|
}
|
||||||
|
return path_join(nullptr, 0, nullptr, 0, result.c_str(), result.size(),
|
||||||
|
nullptr, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InputIt>
|
||||||
|
std::string rewrite_clean_path(InputIt first, InputIt last) {
|
||||||
|
if (first == last || *first != '/') {
|
||||||
|
return std::string(first, last);
|
||||||
|
}
|
||||||
|
// probably, not necessary most of the case, but just in case.
|
||||||
|
auto fragment = std::find(first, last, '#');
|
||||||
|
auto query = std::find(first, fragment, '?');
|
||||||
|
auto path = normalize_path(first, query);
|
||||||
|
if (query != fragment) {
|
||||||
|
path.append(query, fragment);
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace http2
|
} // namespace http2
|
||||||
|
|
||||||
} // namespace nghttp2
|
} // namespace nghttp2
|
||||||
|
|
|
@ -829,4 +829,56 @@ void test_http2_path_join(void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_http2_normalize_path(void) {
|
||||||
|
std::string src;
|
||||||
|
|
||||||
|
src = "/alpha/bravo/../charlie";
|
||||||
|
CU_ASSERT("/alpha/charlie" ==
|
||||||
|
http2::normalize_path(std::begin(src), std::end(src)));
|
||||||
|
|
||||||
|
src = "/a%6c%70%68%61";
|
||||||
|
CU_ASSERT("/alpha" == http2::normalize_path(std::begin(src), std::end(src)));
|
||||||
|
|
||||||
|
src = "/alpha%2f%3a";
|
||||||
|
CU_ASSERT("/alpha%2F%3A" ==
|
||||||
|
http2::normalize_path(std::begin(src), std::end(src)));
|
||||||
|
|
||||||
|
src = "%2f";
|
||||||
|
CU_ASSERT("/%2F" == http2::normalize_path(std::begin(src), std::end(src)));
|
||||||
|
|
||||||
|
src = "%f";
|
||||||
|
CU_ASSERT("/%f" == http2::normalize_path(std::begin(src), std::end(src)));
|
||||||
|
|
||||||
|
src = "%";
|
||||||
|
CU_ASSERT("/%" == http2::normalize_path(std::begin(src), std::end(src)));
|
||||||
|
|
||||||
|
src = "";
|
||||||
|
CU_ASSERT("/" == http2::normalize_path(std::begin(src), std::end(src)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_http2_rewrite_clean_path(void) {
|
||||||
|
std::string src;
|
||||||
|
|
||||||
|
// unreserved characters
|
||||||
|
src = "/alpha/%62ravo/";
|
||||||
|
CU_ASSERT("/alpha/bravo/" ==
|
||||||
|
http2::rewrite_clean_path(std::begin(src), std::end(src)));
|
||||||
|
|
||||||
|
// percent-encoding is converted to upper case.
|
||||||
|
src = "/delta%3a";
|
||||||
|
CU_ASSERT("/delta%3A" ==
|
||||||
|
http2::rewrite_clean_path(std::begin(src), std::end(src)));
|
||||||
|
|
||||||
|
// path component is normalized before mathcing
|
||||||
|
src = "/alpha/charlie/%2e././bravo/delta/..";
|
||||||
|
CU_ASSERT("/alpha/bravo/" ==
|
||||||
|
http2::rewrite_clean_path(std::begin(src), std::end(src)));
|
||||||
|
|
||||||
|
src = "alpha%3a";
|
||||||
|
CU_ASSERT(src == http2::rewrite_clean_path(std::begin(src), std::end(src)));
|
||||||
|
|
||||||
|
src = "";
|
||||||
|
CU_ASSERT(src == http2::rewrite_clean_path(std::begin(src), std::end(src)));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace shrpx
|
} // namespace shrpx
|
||||||
|
|
|
@ -45,6 +45,8 @@ void test_http2_http2_header_allowed(void);
|
||||||
void test_http2_mandatory_request_headers_presence(void);
|
void test_http2_mandatory_request_headers_presence(void);
|
||||||
void test_http2_parse_link_header(void);
|
void test_http2_parse_link_header(void);
|
||||||
void test_http2_path_join(void);
|
void test_http2_path_join(void);
|
||||||
|
void test_http2_normalize_path(void);
|
||||||
|
void test_http2_rewrite_clean_path(void);
|
||||||
|
|
||||||
} // namespace shrpx
|
} // namespace shrpx
|
||||||
|
|
||||||
|
|
|
@ -1379,7 +1379,8 @@ void HttpClient::output_har(FILE *outfile) {
|
||||||
|
|
||||||
auto pushed = req->stream_id % 2 == 0;
|
auto pushed = req->stream_id % 2 == 0;
|
||||||
|
|
||||||
json_object_set_new(entry, "comment", json_string(pushed ? "Pushed Object" : ""));
|
json_object_set_new(entry, "comment",
|
||||||
|
json_string(pushed ? "Pushed Object" : ""));
|
||||||
|
|
||||||
auto request = json_object();
|
auto request = json_object();
|
||||||
json_object_set_new(entry, "request", request);
|
json_object_set_new(entry, "request", request);
|
||||||
|
@ -1463,7 +1464,8 @@ void HttpClient::output_har(FILE *outfile) {
|
||||||
json_object_set_new(timings, "receive", json_real(receive_delta));
|
json_object_set_new(timings, "receive", json_real(receive_delta));
|
||||||
|
|
||||||
json_object_set_new(entry, "pageref", json_string(PAGE_ID));
|
json_object_set_new(entry, "pageref", json_string(PAGE_ID));
|
||||||
json_object_set_new(entry, "connection", json_string(util::utos(req->stream_id).c_str()));
|
json_object_set_new(entry, "connection",
|
||||||
|
json_string(util::utos(req->stream_id).c_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
json_dumpf(root, outfile, JSON_PRESERVE_ORDER | JSON_INDENT(2));
|
json_dumpf(root, outfile, JSON_PRESERVE_ORDER | JSON_INDENT(2));
|
||||||
|
|
|
@ -96,6 +96,10 @@ int main(int argc, char *argv[]) {
|
||||||
!CU_add_test(pSuite, "http2_parse_link_header",
|
!CU_add_test(pSuite, "http2_parse_link_header",
|
||||||
shrpx::test_http2_parse_link_header) ||
|
shrpx::test_http2_parse_link_header) ||
|
||||||
!CU_add_test(pSuite, "http2_path_join", shrpx::test_http2_path_join) ||
|
!CU_add_test(pSuite, "http2_path_join", shrpx::test_http2_path_join) ||
|
||||||
|
!CU_add_test(pSuite, "http2_normalize_path",
|
||||||
|
shrpx::test_http2_normalize_path) ||
|
||||||
|
!CU_add_test(pSuite, "http2_rewrite_clean_path",
|
||||||
|
shrpx::test_http2_rewrite_clean_path) ||
|
||||||
!CU_add_test(pSuite, "downstream_index_request_headers",
|
!CU_add_test(pSuite, "downstream_index_request_headers",
|
||||||
shrpx::test_downstream_index_request_headers) ||
|
shrpx::test_downstream_index_request_headers) ||
|
||||||
!CU_add_test(pSuite, "downstream_index_response_headers",
|
!CU_add_test(pSuite, "downstream_index_response_headers",
|
||||||
|
@ -118,6 +122,8 @@ int main(int argc, char *argv[]) {
|
||||||
shrpx::test_shrpx_config_parse_log_format) ||
|
shrpx::test_shrpx_config_parse_log_format) ||
|
||||||
!CU_add_test(pSuite, "config_read_tls_ticket_key_file",
|
!CU_add_test(pSuite, "config_read_tls_ticket_key_file",
|
||||||
shrpx::test_shrpx_config_read_tls_ticket_key_file) ||
|
shrpx::test_shrpx_config_read_tls_ticket_key_file) ||
|
||||||
|
!CU_add_test(pSuite, "config_match_downstream_addr_group",
|
||||||
|
shrpx::test_shrpx_config_match_downstream_addr_group) ||
|
||||||
!CU_add_test(pSuite, "util_streq", shrpx::test_util_streq) ||
|
!CU_add_test(pSuite, "util_streq", shrpx::test_util_streq) ||
|
||||||
!CU_add_test(pSuite, "util_strieq", shrpx::test_util_strieq) ||
|
!CU_add_test(pSuite, "util_strieq", shrpx::test_util_strieq) ||
|
||||||
!CU_add_test(pSuite, "util_inp_strlower",
|
!CU_add_test(pSuite, "util_inp_strlower",
|
||||||
|
|
155
src/shrpx.cc
155
src/shrpx.cc
|
@ -966,6 +966,7 @@ void fill_default_config() {
|
||||||
mod_config()->no_ocsp = false;
|
mod_config()->no_ocsp = false;
|
||||||
mod_config()->header_field_buffer = 64_k;
|
mod_config()->header_field_buffer = 64_k;
|
||||||
mod_config()->max_header_fields = 100;
|
mod_config()->max_header_fields = 100;
|
||||||
|
mod_config()->downstream_addr_group_catch_all = 0;
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
@ -997,14 +998,67 @@ Options:
|
||||||
The options are categorized into several groups.
|
The options are categorized into several groups.
|
||||||
|
|
||||||
Connections:
|
Connections:
|
||||||
-b, --backend=<HOST,PORT>
|
-b, --backend=(<HOST>,<PORT>|unix:<PATH>)[;<PATTERN>[:...]]
|
||||||
Set backend host and port. The multiple backend
|
Set backend host and port. The multiple backend
|
||||||
addresses are accepted by repeating this option. UNIX
|
addresses are accepted by repeating this option. UNIX
|
||||||
domain socket can be specified by prefixing path name
|
domain socket can be specified by prefixing path name
|
||||||
with "unix:" (e.g., unix:/var/run/backend.sock)
|
with "unix:" (e.g., unix:/var/run/backend.sock).
|
||||||
|
|
||||||
|
Optionally, if <PATTERN>s are given, the backend address
|
||||||
|
is only used if request matches the pattern. If -s or
|
||||||
|
-p is used, <PATTERN>s are ignored. The pattern
|
||||||
|
matching is closely designed to ServeMux in net/http
|
||||||
|
package of Go programming language. <PATTERN> consists
|
||||||
|
of path, host + path or just host. The path must start
|
||||||
|
with "/". If it ends with "/", it matches all request
|
||||||
|
path in its subtree. To deal with the request to the
|
||||||
|
directory without trailing slash, the path which ends
|
||||||
|
with "/" also matches the request path which only lacks
|
||||||
|
trailing '/' (e.g., path "/foo/" matches request path
|
||||||
|
"/foo"). If it does not end with "/", it performs exact
|
||||||
|
match against the request path. If host is given, it
|
||||||
|
performs exact match against the request host. If host
|
||||||
|
alone is given, "/" is appended to it, so that it
|
||||||
|
matches all request paths under the host (e.g.,
|
||||||
|
specifying "nghttp2.org" equals to "nghttp2.org/").
|
||||||
|
|
||||||
|
Patterns with host take precedence over patterns with
|
||||||
|
just path. Then, longer patterns take precedence over
|
||||||
|
shorter ones, breaking a tie by the order of the
|
||||||
|
appearance in the configuration.
|
||||||
|
|
||||||
|
If <PATTERN> is omitted, "/" is used as pattern, which
|
||||||
|
matches all request paths (catch-all pattern). The
|
||||||
|
catch-all backend must be given.
|
||||||
|
|
||||||
|
When doing a match, nghttpx made some normalization to
|
||||||
|
pattern, request host and path. For host part, they are
|
||||||
|
converted to lower case. For path part, percent-encoded
|
||||||
|
unreserved characters defined in RFC 3986 are decoded,
|
||||||
|
and any dot-segments (".." and ".") are resolved and
|
||||||
|
removed.
|
||||||
|
|
||||||
|
For example, -b'127.0.0.1,8080;nghttp2.org/httpbin/'
|
||||||
|
matches the request host "nghttp2.org" and the request
|
||||||
|
path "/httpbin/get", but does not match the request host
|
||||||
|
"nghttp2.org" and the request path "/index.html".
|
||||||
|
|
||||||
|
The multiple <PATTERN>s can be specified, delimiting
|
||||||
|
them by ":". Specifying
|
||||||
|
-b'127.0.0.1,8080;nghttp2.org:www.nghttp2.org' has the
|
||||||
|
same effect to specify -b'127.0.0.1,8080;nghttp2.org'
|
||||||
|
and -b'127.0.0.1,8080;www.nghttp2.org'.
|
||||||
|
|
||||||
|
The backend addresses sharing same <PATTERN> are grouped
|
||||||
|
together forming load balancing group.
|
||||||
|
|
||||||
|
Since ";" and ":" are used as delimiter, <PATTERN> must
|
||||||
|
not contain these characters. Since ";" has special
|
||||||
|
meaning in shell, the option value must be quoted.
|
||||||
|
|
||||||
Default: )" << DEFAULT_DOWNSTREAM_HOST << ","
|
Default: )" << DEFAULT_DOWNSTREAM_HOST << ","
|
||||||
<< DEFAULT_DOWNSTREAM_PORT << R"(
|
<< DEFAULT_DOWNSTREAM_PORT << R"(
|
||||||
-f, --frontend=<HOST,PORT>
|
-f, --frontend=(<HOST>,<PORT>|unix:<PATH>)
|
||||||
Set frontend host and port. If <HOST> is '*', it
|
Set frontend host and port. If <HOST> is '*', it
|
||||||
assumes all addresses including both IPv4 and IPv6.
|
assumes all addresses including both IPv4 and IPv6.
|
||||||
UNIX domain socket can be specified by prefixing path
|
UNIX domain socket can be specified by prefixing path
|
||||||
|
@ -1079,9 +1133,14 @@ Performance:
|
||||||
accepts. Setting 0 means unlimited.
|
accepts. Setting 0 means unlimited.
|
||||||
Default: )" << get_config()->worker_frontend_connections << R"(
|
Default: )" << get_config()->worker_frontend_connections << R"(
|
||||||
--backend-http2-connections-per-worker=<N>
|
--backend-http2-connections-per-worker=<N>
|
||||||
Set maximum number of HTTP/2 connections per worker.
|
Set maximum number of backend HTTP/2 physical
|
||||||
The default value is 0, which means the number of
|
connections per worker. If pattern is used in -b
|
||||||
backend addresses specified by -b option.
|
option, this limit is applied to each pattern group (in
|
||||||
|
other words, each pattern group can have maximum <N>
|
||||||
|
HTTP/2 connections). The default value is 0, which
|
||||||
|
means that the value is adjusted to the number of
|
||||||
|
backend addresses. If pattern is used, this adjustment
|
||||||
|
is done for each pattern group.
|
||||||
--backend-http1-connections-per-host=<N>
|
--backend-http1-connections-per-host=<N>
|
||||||
Set maximum number of backend concurrent HTTP/1
|
Set maximum number of backend concurrent HTTP/1
|
||||||
connections per origin host. This option is meaningful
|
connections per origin host. This option is meaningful
|
||||||
|
@ -1351,6 +1410,9 @@ Logging:
|
||||||
* $ssl_session_reused: "r" if SSL/TLS session was
|
* $ssl_session_reused: "r" if SSL/TLS session was
|
||||||
reused. Otherwise, "."
|
reused. Otherwise, "."
|
||||||
|
|
||||||
|
The variable can be enclosed by "{" and "}" for
|
||||||
|
disambiguation (e.g., ${remote_addr}).
|
||||||
|
|
||||||
Default: )" << DEFAULT_ACCESSLOG_FORMAT << R"(
|
Default: )" << DEFAULT_ACCESSLOG_FORMAT << R"(
|
||||||
--errorlog-file=<PATH>
|
--errorlog-file=<PATH>
|
||||||
Set path to write error log. To reopen file, send USR1
|
Set path to write error log. To reopen file, send USR1
|
||||||
|
@ -1446,6 +1508,11 @@ Misc:
|
||||||
--conf=<PATH>
|
--conf=<PATH>
|
||||||
Load configuration from <PATH>.
|
Load configuration from <PATH>.
|
||||||
Default: )" << get_config()->conf_path.get() << R"(
|
Default: )" << get_config()->conf_path.get() << R"(
|
||||||
|
--include=<PATH>
|
||||||
|
Load additional configurations from <PATH>. File <PATH>
|
||||||
|
is read when configuration parser encountered this
|
||||||
|
option. This option can be used multiple times, or even
|
||||||
|
recursively.
|
||||||
-v, --version
|
-v, --version
|
||||||
Print version and exit.
|
Print version and exit.
|
||||||
-h, --help Print this help and exit.
|
-h, --help Print this help and exit.
|
||||||
|
@ -1592,6 +1659,7 @@ int main(int argc, char **argv) {
|
||||||
{SHRPX_OPT_HEADER_FIELD_BUFFER, required_argument, &flag, 80},
|
{SHRPX_OPT_HEADER_FIELD_BUFFER, required_argument, &flag, 80},
|
||||||
{SHRPX_OPT_MAX_HEADER_FIELDS, required_argument, &flag, 81},
|
{SHRPX_OPT_MAX_HEADER_FIELDS, required_argument, &flag, 81},
|
||||||
{SHRPX_OPT_ADD_REQUEST_HEADER, required_argument, &flag, 82},
|
{SHRPX_OPT_ADD_REQUEST_HEADER, required_argument, &flag, 82},
|
||||||
|
{SHRPX_OPT_INCLUDE, required_argument, &flag, 83},
|
||||||
{nullptr, 0, nullptr, 0}};
|
{nullptr, 0, nullptr, 0}};
|
||||||
|
|
||||||
int option_index = 0;
|
int option_index = 0;
|
||||||
|
@ -1954,6 +2022,10 @@ int main(int argc, char **argv) {
|
||||||
// --add-request-header
|
// --add-request-header
|
||||||
cmdcfgs.emplace_back(SHRPX_OPT_ADD_REQUEST_HEADER, optarg);
|
cmdcfgs.emplace_back(SHRPX_OPT_ADD_REQUEST_HEADER, optarg);
|
||||||
break;
|
break;
|
||||||
|
case 83:
|
||||||
|
// --include
|
||||||
|
cmdcfgs.emplace_back(SHRPX_OPT_INCLUDE, optarg);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1964,11 +2036,13 @@ int main(int argc, char **argv) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conf_exists(get_config()->conf_path.get())) {
|
if (conf_exists(get_config()->conf_path.get())) {
|
||||||
if (load_config(get_config()->conf_path.get()) == -1) {
|
std::set<std::string> include_set;
|
||||||
|
if (load_config(get_config()->conf_path.get(), include_set) == -1) {
|
||||||
LOG(FATAL) << "Failed to load configuration from "
|
LOG(FATAL) << "Failed to load configuration from "
|
||||||
<< get_config()->conf_path.get();
|
<< get_config()->conf_path.get();
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
assert(include_set.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argc - optind >= 2) {
|
if (argc - optind >= 2) {
|
||||||
|
@ -1980,13 +2054,20 @@ int main(int argc, char **argv) {
|
||||||
// parsing option values.
|
// parsing option values.
|
||||||
reopen_log_files();
|
reopen_log_files();
|
||||||
|
|
||||||
|
{
|
||||||
|
std::set<std::string> include_set;
|
||||||
|
|
||||||
for (size_t i = 0, len = cmdcfgs.size(); i < len; ++i) {
|
for (size_t i = 0, len = cmdcfgs.size(); i < len; ++i) {
|
||||||
if (parse_config(cmdcfgs[i].first, cmdcfgs[i].second) == -1) {
|
if (parse_config(cmdcfgs[i].first, cmdcfgs[i].second, include_set) ==
|
||||||
|
-1) {
|
||||||
LOG(FATAL) << "Failed to parse command-line argument.";
|
LOG(FATAL) << "Failed to parse command-line argument.";
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(include_set.empty());
|
||||||
|
}
|
||||||
|
|
||||||
if (get_config()->accesslog_syslog || get_config()->errorlog_syslog) {
|
if (get_config()->accesslog_syslog || get_config()->errorlog_syslog) {
|
||||||
openlog("nghttpx", LOG_NDELAY | LOG_NOWAIT | LOG_PID,
|
openlog("nghttpx", LOG_NDELAY | LOG_NOWAIT | LOG_PID,
|
||||||
get_config()->syslog_facility);
|
get_config()->syslog_facility);
|
||||||
|
@ -2118,19 +2199,59 @@ int main(int argc, char **argv) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get_config()->downstream_addrs.empty()) {
|
if (get_config()->downstream_addr_groups.empty()) {
|
||||||
DownstreamAddr addr;
|
DownstreamAddr addr;
|
||||||
addr.host = strcopy(DEFAULT_DOWNSTREAM_HOST);
|
addr.host = strcopy(DEFAULT_DOWNSTREAM_HOST);
|
||||||
addr.port = DEFAULT_DOWNSTREAM_PORT;
|
addr.port = DEFAULT_DOWNSTREAM_PORT;
|
||||||
|
|
||||||
mod_config()->downstream_addrs.push_back(std::move(addr));
|
DownstreamAddrGroup g("/");
|
||||||
|
g.addrs.push_back(std::move(addr));
|
||||||
|
mod_config()->downstream_addr_groups.push_back(std::move(g));
|
||||||
|
} else if (get_config()->http2_proxy || get_config()->client_proxy) {
|
||||||
|
// We don't support host mapping in these cases. Move all
|
||||||
|
// non-catch-all patterns to catch-all pattern.
|
||||||
|
DownstreamAddrGroup catch_all("/");
|
||||||
|
for (auto &g : mod_config()->downstream_addr_groups) {
|
||||||
|
std::move(std::begin(g.addrs), std::end(g.addrs),
|
||||||
|
std::back_inserter(catch_all.addrs));
|
||||||
|
}
|
||||||
|
std::vector<DownstreamAddrGroup>().swap(
|
||||||
|
mod_config()->downstream_addr_groups);
|
||||||
|
mod_config()->downstream_addr_groups.push_back(std::move(catch_all));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LOG_ENABLED(INFO)) {
|
if (LOG_ENABLED(INFO)) {
|
||||||
LOG(INFO) << "Resolving backend address";
|
LOG(INFO) << "Resolving backend address";
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &addr : mod_config()->downstream_addrs) {
|
ssize_t catch_all_group = -1;
|
||||||
|
for (size_t i = 0; i < mod_config()->downstream_addr_groups.size(); ++i) {
|
||||||
|
auto &g = mod_config()->downstream_addr_groups[i];
|
||||||
|
if (g.pattern == "/") {
|
||||||
|
catch_all_group = i;
|
||||||
|
}
|
||||||
|
if (LOG_ENABLED(INFO)) {
|
||||||
|
LOG(INFO) << "Host-path pattern: group " << i << ": '" << g.pattern
|
||||||
|
<< "'";
|
||||||
|
for (auto &addr : g.addrs) {
|
||||||
|
LOG(INFO) << "group " << i << " -> " << addr.host.get()
|
||||||
|
<< (addr.host_unix ? "" : ":" + util::utos(addr.port));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (catch_all_group == -1) {
|
||||||
|
LOG(FATAL) << "-b: No catch-all backend address is configured";
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
mod_config()->downstream_addr_group_catch_all = catch_all_group;
|
||||||
|
|
||||||
|
if (LOG_ENABLED(INFO)) {
|
||||||
|
LOG(INFO) << "Catch-all pattern is group " << catch_all_group;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &g : mod_config()->downstream_addr_groups) {
|
||||||
|
for (auto &addr : g.addrs) {
|
||||||
|
|
||||||
if (addr.host_unix) {
|
if (addr.host_unix) {
|
||||||
// for AF_UNIX socket, we use "localhost" as host for backend
|
// for AF_UNIX socket, we use "localhost" as host for backend
|
||||||
|
@ -2163,12 +2284,13 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
if (resolve_hostname(
|
if (resolve_hostname(
|
||||||
&addr.addr, &addr.addrlen, addr.host.get(), addr.port,
|
&addr.addr, &addr.addrlen, addr.host.get(), addr.port,
|
||||||
get_config()->backend_ipv4
|
get_config()->backend_ipv4 ? AF_INET : (get_config()->backend_ipv6
|
||||||
? AF_INET
|
? AF_INET6
|
||||||
: (get_config()->backend_ipv6 ? AF_INET6 : AF_UNSPEC)) == -1) {
|
: AF_UNSPEC)) == -1) {
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (get_config()->downstream_http_proxy_host) {
|
if (get_config()->downstream_http_proxy_host) {
|
||||||
if (LOG_ENABLED(INFO)) {
|
if (LOG_ENABLED(INFO)) {
|
||||||
|
@ -2183,11 +2305,6 @@ int main(int argc, char **argv) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get_config()->http2_downstream_connections_per_worker == 0) {
|
|
||||||
mod_config()->http2_downstream_connections_per_worker =
|
|
||||||
get_config()->downstream_addrs.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (get_config()->rlimit_nofile) {
|
if (get_config()->rlimit_nofile) {
|
||||||
struct rlimit lim = {static_cast<rlim_t>(get_config()->rlimit_nofile),
|
struct rlimit lim = {static_cast<rlim_t>(get_config()->rlimit_nofile),
|
||||||
static_cast<rlim_t>(get_config()->rlimit_nofile)};
|
static_cast<rlim_t>(get_config()->rlimit_nofile)};
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
#include "shrpx_worker.h"
|
#include "shrpx_worker.h"
|
||||||
#include "shrpx_downstream_connection_pool.h"
|
#include "shrpx_downstream_connection_pool.h"
|
||||||
#include "shrpx_downstream.h"
|
#include "shrpx_downstream.h"
|
||||||
|
#include "shrpx_http2_session.h"
|
||||||
#ifdef HAVE_SPDYLAY
|
#ifdef HAVE_SPDYLAY
|
||||||
#include "shrpx_spdy_upstream.h"
|
#include "shrpx_spdy_upstream.h"
|
||||||
#endif // HAVE_SPDYLAY
|
#endif // HAVE_SPDYLAY
|
||||||
|
@ -360,8 +361,12 @@ ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl,
|
||||||
get_config()->upstream_read_timeout, get_config()->write_rate,
|
get_config()->upstream_read_timeout, get_config()->write_rate,
|
||||||
get_config()->write_burst, get_config()->read_rate,
|
get_config()->write_burst, get_config()->read_rate,
|
||||||
get_config()->read_burst, writecb, readcb, timeoutcb, this),
|
get_config()->read_burst, writecb, readcb, timeoutcb, this),
|
||||||
|
pinned_http2sessions_(
|
||||||
|
get_config()->downstream_proto == PROTO_HTTP2
|
||||||
|
? make_unique<std::vector<ssize_t>>(
|
||||||
|
get_config()->downstream_addr_groups.size(), -1)
|
||||||
|
: nullptr),
|
||||||
ipaddr_(ipaddr), port_(port), worker_(worker),
|
ipaddr_(ipaddr), port_(port), worker_(worker),
|
||||||
http2session_(worker_->next_http2_session()),
|
|
||||||
left_connhd_len_(NGHTTP2_CLIENT_MAGIC_LEN),
|
left_connhd_len_(NGHTTP2_CLIENT_MAGIC_LEN),
|
||||||
should_close_after_write_(false) {
|
should_close_after_write_(false) {
|
||||||
|
|
||||||
|
@ -588,7 +593,8 @@ void ClientHandler::pool_downstream_connection(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (LOG_ENABLED(INFO)) {
|
if (LOG_ENABLED(INFO)) {
|
||||||
CLOG(INFO, this) << "Pooling downstream connection DCONN:" << dconn.get();
|
CLOG(INFO, this) << "Pooling downstream connection DCONN:" << dconn.get()
|
||||||
|
<< " in group " << dconn->get_group();
|
||||||
}
|
}
|
||||||
dconn->set_client_handler(nullptr);
|
dconn->set_client_handler(nullptr);
|
||||||
auto dconn_pool = worker_->get_dconn_pool();
|
auto dconn_pool = worker_->get_dconn_pool();
|
||||||
|
@ -605,9 +611,43 @@ void ClientHandler::remove_downstream_connection(DownstreamConnection *dconn) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<DownstreamConnection>
|
std::unique_ptr<DownstreamConnection>
|
||||||
ClientHandler::get_downstream_connection() {
|
ClientHandler::get_downstream_connection(Downstream *downstream) {
|
||||||
|
size_t group;
|
||||||
|
auto &groups = get_config()->downstream_addr_groups;
|
||||||
|
auto catch_all = get_config()->downstream_addr_group_catch_all;
|
||||||
|
|
||||||
|
// Fast path. If we have one group, it must be catch-all group.
|
||||||
|
// HTTP/2 and client proxy modes fall in this case.
|
||||||
|
if (groups.size() == 1) {
|
||||||
|
group = 0;
|
||||||
|
} else if (downstream->get_request_method() == HTTP_CONNECT) {
|
||||||
|
// We don't know how to treat CONNECT request in host-path
|
||||||
|
// mapping. It most likely appears in proxy scenario. Since we
|
||||||
|
// have dealt with proxy case already, just use catch-all group.
|
||||||
|
group = catch_all;
|
||||||
|
} else {
|
||||||
|
if (!downstream->get_request_http2_authority().empty()) {
|
||||||
|
group = match_downstream_addr_group(
|
||||||
|
downstream->get_request_http2_authority(),
|
||||||
|
downstream->get_request_path(), groups, catch_all);
|
||||||
|
} else {
|
||||||
|
auto h = downstream->get_request_header(http2::HD_HOST);
|
||||||
|
if (h) {
|
||||||
|
group = match_downstream_addr_group(
|
||||||
|
h->value, downstream->get_request_path(), groups, catch_all);
|
||||||
|
} else {
|
||||||
|
group = match_downstream_addr_group("", downstream->get_request_path(),
|
||||||
|
groups, catch_all);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LOG_ENABLED(INFO)) {
|
||||||
|
CLOG(INFO, this) << "Downstream address group: " << group;
|
||||||
|
}
|
||||||
|
|
||||||
auto dconn_pool = worker_->get_dconn_pool();
|
auto dconn_pool = worker_->get_dconn_pool();
|
||||||
auto dconn = dconn_pool->pop_downstream_connection();
|
auto dconn = dconn_pool->pop_downstream_connection(group);
|
||||||
|
|
||||||
if (!dconn) {
|
if (!dconn) {
|
||||||
if (LOG_ENABLED(INFO)) {
|
if (LOG_ENABLED(INFO)) {
|
||||||
|
@ -617,10 +657,20 @@ ClientHandler::get_downstream_connection() {
|
||||||
|
|
||||||
auto dconn_pool = worker_->get_dconn_pool();
|
auto dconn_pool = worker_->get_dconn_pool();
|
||||||
|
|
||||||
if (http2session_) {
|
if (get_config()->downstream_proto == PROTO_HTTP2) {
|
||||||
dconn = make_unique<Http2DownstreamConnection>(dconn_pool, http2session_);
|
Http2Session *http2session;
|
||||||
|
auto &pinned = (*pinned_http2sessions_)[group];
|
||||||
|
if (pinned == -1) {
|
||||||
|
http2session = worker_->next_http2_session(group);
|
||||||
|
pinned = http2session->get_index();
|
||||||
} else {
|
} else {
|
||||||
dconn = make_unique<HttpDownstreamConnection>(dconn_pool, conn_.loop);
|
auto dgrp = worker_->get_dgrp(group);
|
||||||
|
http2session = dgrp->http2sessions[pinned].get();
|
||||||
|
}
|
||||||
|
dconn = make_unique<Http2DownstreamConnection>(dconn_pool, http2session);
|
||||||
|
} else {
|
||||||
|
dconn =
|
||||||
|
make_unique<HttpDownstreamConnection>(dconn_pool, group, conn_.loop);
|
||||||
}
|
}
|
||||||
dconn->set_client_handler(this);
|
dconn->set_client_handler(this);
|
||||||
return dconn;
|
return dconn;
|
||||||
|
|
|
@ -44,7 +44,6 @@ namespace shrpx {
|
||||||
|
|
||||||
class Upstream;
|
class Upstream;
|
||||||
class DownstreamConnection;
|
class DownstreamConnection;
|
||||||
class Http2Session;
|
|
||||||
class HttpsUpstream;
|
class HttpsUpstream;
|
||||||
class ConnectBlocker;
|
class ConnectBlocker;
|
||||||
class DownstreamConnectionPool;
|
class DownstreamConnectionPool;
|
||||||
|
@ -92,7 +91,8 @@ public:
|
||||||
|
|
||||||
void pool_downstream_connection(std::unique_ptr<DownstreamConnection> dconn);
|
void pool_downstream_connection(std::unique_ptr<DownstreamConnection> dconn);
|
||||||
void remove_downstream_connection(DownstreamConnection *dconn);
|
void remove_downstream_connection(DownstreamConnection *dconn);
|
||||||
std::unique_ptr<DownstreamConnection> get_downstream_connection();
|
std::unique_ptr<DownstreamConnection>
|
||||||
|
get_downstream_connection(Downstream *downstream);
|
||||||
MemchunkPool *get_mcpool();
|
MemchunkPool *get_mcpool();
|
||||||
SSL *get_ssl() const;
|
SSL *get_ssl() const;
|
||||||
ConnectBlocker *get_connect_blocker() const;
|
ConnectBlocker *get_connect_blocker() const;
|
||||||
|
@ -134,6 +134,7 @@ private:
|
||||||
Connection conn_;
|
Connection conn_;
|
||||||
ev_timer reneg_shutdown_timer_;
|
ev_timer reneg_shutdown_timer_;
|
||||||
std::unique_ptr<Upstream> upstream_;
|
std::unique_ptr<Upstream> upstream_;
|
||||||
|
std::unique_ptr<std::vector<ssize_t>> pinned_http2sessions_;
|
||||||
std::string ipaddr_;
|
std::string ipaddr_;
|
||||||
std::string port_;
|
std::string port_;
|
||||||
// The ALPN identifier negotiated for this connection.
|
// The ALPN identifier negotiated for this connection.
|
||||||
|
@ -141,7 +142,6 @@ private:
|
||||||
std::function<int(ClientHandler &)> read_, write_;
|
std::function<int(ClientHandler &)> read_, write_;
|
||||||
std::function<int(ClientHandler &)> on_read_, on_write_;
|
std::function<int(ClientHandler &)> on_read_, on_write_;
|
||||||
Worker *worker_;
|
Worker *worker_;
|
||||||
Http2Session *http2session_;
|
|
||||||
// The number of bytes of HTTP/2 client connection header to read
|
// The number of bytes of HTTP/2 client connection header to read
|
||||||
size_t left_connhd_len_;
|
size_t left_connhd_len_;
|
||||||
bool should_close_after_write_;
|
bool should_close_after_write_;
|
||||||
|
|
1490
src/shrpx_config.cc
1490
src/shrpx_config.cc
File diff suppressed because it is too large
Load Diff
|
@ -42,6 +42,7 @@
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
#include <openssl/ssl.h>
|
#include <openssl/ssl.h>
|
||||||
|
|
||||||
|
@ -49,6 +50,10 @@
|
||||||
|
|
||||||
#include <nghttp2/nghttp2.h>
|
#include <nghttp2/nghttp2.h>
|
||||||
|
|
||||||
|
#include "template.h"
|
||||||
|
|
||||||
|
using namespace nghttp2;
|
||||||
|
|
||||||
namespace shrpx {
|
namespace shrpx {
|
||||||
|
|
||||||
struct LogFragment;
|
struct LogFragment;
|
||||||
|
@ -165,6 +170,7 @@ constexpr char SHRPX_OPT_OCSP_UPDATE_INTERVAL[] = "ocsp-update-interval";
|
||||||
constexpr char SHRPX_OPT_NO_OCSP[] = "no-ocsp";
|
constexpr char SHRPX_OPT_NO_OCSP[] = "no-ocsp";
|
||||||
constexpr char SHRPX_OPT_HEADER_FIELD_BUFFER[] = "header-field-buffer";
|
constexpr char SHRPX_OPT_HEADER_FIELD_BUFFER[] = "header-field-buffer";
|
||||||
constexpr char SHRPX_OPT_MAX_HEADER_FIELDS[] = "max-header-fields";
|
constexpr char SHRPX_OPT_MAX_HEADER_FIELDS[] = "max-header-fields";
|
||||||
|
constexpr char SHRPX_OPT_INCLUDE[] = "include";
|
||||||
|
|
||||||
union sockaddr_union {
|
union sockaddr_union {
|
||||||
sockaddr_storage storage;
|
sockaddr_storage storage;
|
||||||
|
@ -194,6 +200,11 @@ struct AltSvc {
|
||||||
|
|
||||||
struct DownstreamAddr {
|
struct DownstreamAddr {
|
||||||
DownstreamAddr() : addr{{0}}, addrlen(0), port(0), host_unix(false) {}
|
DownstreamAddr() : addr{{0}}, addrlen(0), port(0), host_unix(false) {}
|
||||||
|
DownstreamAddr(const DownstreamAddr &other);
|
||||||
|
DownstreamAddr(DownstreamAddr &&) = default;
|
||||||
|
DownstreamAddr &operator=(const DownstreamAddr &other);
|
||||||
|
DownstreamAddr &operator=(DownstreamAddr &&other) = default;
|
||||||
|
|
||||||
sockaddr_union addr;
|
sockaddr_union addr;
|
||||||
// backend address. If |host_unix| is true, this is UNIX domain
|
// backend address. If |host_unix| is true, this is UNIX domain
|
||||||
// socket path.
|
// socket path.
|
||||||
|
@ -206,6 +217,12 @@ struct DownstreamAddr {
|
||||||
bool host_unix;
|
bool host_unix;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct DownstreamAddrGroup {
|
||||||
|
DownstreamAddrGroup(std::string pattern) : pattern(std::move(pattern)) {}
|
||||||
|
std::string pattern;
|
||||||
|
std::vector<DownstreamAddr> addrs;
|
||||||
|
};
|
||||||
|
|
||||||
struct TicketKey {
|
struct TicketKey {
|
||||||
uint8_t name[16];
|
uint8_t name[16];
|
||||||
uint8_t aes_key[16];
|
uint8_t aes_key[16];
|
||||||
|
@ -225,7 +242,7 @@ struct Config {
|
||||||
std::vector<std::pair<std::string, std::string>> add_response_headers;
|
std::vector<std::pair<std::string, std::string>> add_response_headers;
|
||||||
std::vector<unsigned char> alpn_prefs;
|
std::vector<unsigned char> alpn_prefs;
|
||||||
std::vector<LogFragment> accesslog_format;
|
std::vector<LogFragment> accesslog_format;
|
||||||
std::vector<DownstreamAddr> downstream_addrs;
|
std::vector<DownstreamAddrGroup> downstream_addr_groups;
|
||||||
std::vector<std::string> tls_ticket_key_files;
|
std::vector<std::string> tls_ticket_key_files;
|
||||||
// binary form of http proxy host and port
|
// binary form of http proxy host and port
|
||||||
sockaddr_union downstream_http_proxy_addr;
|
sockaddr_union downstream_http_proxy_addr;
|
||||||
|
@ -311,6 +328,8 @@ struct Config {
|
||||||
size_t downstream_response_buffer_size;
|
size_t downstream_response_buffer_size;
|
||||||
size_t header_field_buffer;
|
size_t header_field_buffer;
|
||||||
size_t max_header_fields;
|
size_t max_header_fields;
|
||||||
|
// The index of catch-all group in downstream_addr_groups.
|
||||||
|
size_t downstream_addr_group_catch_all;
|
||||||
// Bit mask to disable SSL/TLS protocol versions. This will be
|
// Bit mask to disable SSL/TLS protocol versions. This will be
|
||||||
// passed to SSL_CTX_set_options().
|
// passed to SSL_CTX_set_options().
|
||||||
long int tls_proto_mask;
|
long int tls_proto_mask;
|
||||||
|
@ -365,26 +384,28 @@ void create_config();
|
||||||
|
|
||||||
// Parses option name |opt| and value |optarg|. The results are
|
// Parses option name |opt| and value |optarg|. The results are
|
||||||
// stored into statically allocated Config object. This function
|
// stored into statically allocated Config object. This function
|
||||||
// returns 0 if it succeeds, or -1.
|
// returns 0 if it succeeds, or -1. The |included_set| contains the
|
||||||
int parse_config(const char *opt, const char *optarg);
|
// all paths already included while processing this configuration, to
|
||||||
|
// avoid loop in --include option.
|
||||||
|
int parse_config(const char *opt, const char *optarg,
|
||||||
|
std::set<std::string> &included_set);
|
||||||
|
|
||||||
// Loads configurations from |filename| and stores them in statically
|
// Loads configurations from |filename| and stores them in statically
|
||||||
// allocated Config object. This function returns 0 if it succeeds, or
|
// allocated Config object. This function returns 0 if it succeeds, or
|
||||||
// -1.
|
// -1. See parse_config() for |include_set|.
|
||||||
int load_config(const char *filename);
|
int load_config(const char *filename, std::set<std::string> &include_set);
|
||||||
|
|
||||||
// Read passwd from |filename|
|
// Read passwd from |filename|
|
||||||
std::string read_passwd_from_file(const char *filename);
|
std::string read_passwd_from_file(const char *filename);
|
||||||
|
|
||||||
// Parses comma delimited strings in |s| and returns the array of
|
// Parses delimited strings in |s| and returns the array of pointers,
|
||||||
// pointers, each element points to the each substring in |s|. The
|
// each element points to the each substring in |s|. The delimiter is
|
||||||
// |s| must be comma delimited list of strings. The strings must be
|
// given by |delim. The |s| must be comma delimited list of strings.
|
||||||
// delimited by a single comma and any white spaces around it are
|
// The strings must be delimited by a single comma and any white
|
||||||
// treated as a part of protocol strings. This function may modify
|
// spaces around it are treated as a part of protocol strings. This
|
||||||
// |s| and the caller must leave it as is after this call. This
|
|
||||||
// function copies |s| and first element in the return value points to
|
// function copies |s| and first element in the return value points to
|
||||||
// it. It is caller's responsibility to deallocate its memory.
|
// it. It is caller's responsibility to deallocate its memory.
|
||||||
std::vector<char *> parse_config_str_list(const char *s);
|
std::vector<char *> parse_config_str_list(const char *s, char delim = ',');
|
||||||
|
|
||||||
// Clears all elements of |list|, which is returned by
|
// Clears all elements of |list|, which is returned by
|
||||||
// parse_config_str_list(). If list is not empty, list[0] is freed by
|
// parse_config_str_list(). If list is not empty, list[0] is freed by
|
||||||
|
@ -399,15 +420,23 @@ std::pair<std::string, std::string> parse_header(const char *optarg);
|
||||||
|
|
||||||
std::vector<LogFragment> parse_log_format(const char *optarg);
|
std::vector<LogFragment> parse_log_format(const char *optarg);
|
||||||
|
|
||||||
// Returns a copy of NULL-terminated string |val|.
|
// Returns a copy of NULL-terminated string [first, last).
|
||||||
std::unique_ptr<char[]> strcopy(const char *val);
|
template <typename InputIt>
|
||||||
|
std::unique_ptr<char[]> strcopy(InputIt first, InputIt last) {
|
||||||
|
auto res = make_unique<char[]>(last - first + 1);
|
||||||
|
*std::copy(first, last, res.get()) = '\0';
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
// Returns a copy of string |val| of length |n|. The returned string
|
// Returns a copy of NULL-terminated string |val|.
|
||||||
// will be NULL-terminated.
|
inline std::unique_ptr<char[]> strcopy(const char *val) {
|
||||||
std::unique_ptr<char[]> strcopy(const char *val, size_t n);
|
return strcopy(val, val + strlen(val));
|
||||||
|
}
|
||||||
|
|
||||||
// Returns a copy of val.c_str().
|
// Returns a copy of val.c_str().
|
||||||
std::unique_ptr<char[]> strcopy(const std::string &val);
|
inline std::unique_ptr<char[]> strcopy(const std::string &val) {
|
||||||
|
return strcopy(std::begin(val), std::end(val));
|
||||||
|
}
|
||||||
|
|
||||||
// Returns string for syslog |facility|.
|
// Returns string for syslog |facility|.
|
||||||
const char *str_syslog_facility(int facility);
|
const char *str_syslog_facility(int facility);
|
||||||
|
@ -423,6 +452,16 @@ FILE *open_file_for_write(const char *filename);
|
||||||
std::unique_ptr<TicketKeys>
|
std::unique_ptr<TicketKeys>
|
||||||
read_tls_ticket_key_file(const std::vector<std::string> &files);
|
read_tls_ticket_key_file(const std::vector<std::string> &files);
|
||||||
|
|
||||||
|
// Selects group based on request's |hostport| and |path|. |hostport|
|
||||||
|
// is the value taken from :authority or host header field, and may
|
||||||
|
// contain port. The |path| may contain query part. We require the
|
||||||
|
// catch-all pattern in place, so this function always selects one
|
||||||
|
// group. The catch-all group index is given in |catch_all|. All
|
||||||
|
// patterns are given in |groups|.
|
||||||
|
size_t match_downstream_addr_group(
|
||||||
|
const std::string &hostport, const std::string &path,
|
||||||
|
const std::vector<DownstreamAddrGroup> &groups, size_t catch_all);
|
||||||
|
|
||||||
} // namespace shrpx
|
} // namespace shrpx
|
||||||
|
|
||||||
#endif // SHRPX_CONFIG_H
|
#endif // SHRPX_CONFIG_H
|
||||||
|
|
|
@ -48,7 +48,7 @@ void test_shrpx_config_parse_config_str_list(void) {
|
||||||
CU_ASSERT(0 == strcmp("", res[1]));
|
CU_ASSERT(0 == strcmp("", res[1]));
|
||||||
clear_config_str_list(res);
|
clear_config_str_list(res);
|
||||||
|
|
||||||
res = parse_config_str_list(",a,,");
|
res = parse_config_str_list(":a::", ':');
|
||||||
CU_ASSERT(4 == res.size());
|
CU_ASSERT(4 == res.size());
|
||||||
CU_ASSERT(0 == strcmp("", res[0]));
|
CU_ASSERT(0 == strcmp("", res[0]));
|
||||||
CU_ASSERT(0 == strcmp("a", res[1]));
|
CU_ASSERT(0 == strcmp("a", res[1]));
|
||||||
|
@ -95,9 +95,9 @@ void test_shrpx_config_parse_header(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_shrpx_config_parse_log_format(void) {
|
void test_shrpx_config_parse_log_format(void) {
|
||||||
auto res = parse_log_format("$remote_addr - $remote_user [$time_local] "
|
auto res = parse_log_format(R"($remote_addr - $remote_user [$time_local] )"
|
||||||
"\"$request\" $status $body_bytes_sent "
|
R"("$request" $status $body_bytes_sent )"
|
||||||
"\"$http_referer\" \"$http_user_agent\"");
|
R"("${http_referer}" "$http_user_agent")");
|
||||||
CU_ASSERT(14 == res.size());
|
CU_ASSERT(14 == res.size());
|
||||||
|
|
||||||
CU_ASSERT(SHRPX_LOGF_REMOTE_ADDR == res[0].type);
|
CU_ASSERT(SHRPX_LOGF_REMOTE_ADDR == res[0].type);
|
||||||
|
@ -136,6 +136,44 @@ void test_shrpx_config_parse_log_format(void) {
|
||||||
|
|
||||||
CU_ASSERT(SHRPX_LOGF_LITERAL == res[13].type);
|
CU_ASSERT(SHRPX_LOGF_LITERAL == res[13].type);
|
||||||
CU_ASSERT(0 == strcmp("\"", res[13].value.get()));
|
CU_ASSERT(0 == strcmp("\"", res[13].value.get()));
|
||||||
|
|
||||||
|
res = parse_log_format("$");
|
||||||
|
|
||||||
|
CU_ASSERT(1 == res.size());
|
||||||
|
|
||||||
|
CU_ASSERT(SHRPX_LOGF_LITERAL == res[0].type);
|
||||||
|
CU_ASSERT(0 == strcmp("$", res[0].value.get()));
|
||||||
|
|
||||||
|
res = parse_log_format("${");
|
||||||
|
|
||||||
|
CU_ASSERT(1 == res.size());
|
||||||
|
|
||||||
|
CU_ASSERT(SHRPX_LOGF_LITERAL == res[0].type);
|
||||||
|
CU_ASSERT(0 == strcmp("${", res[0].value.get()));
|
||||||
|
|
||||||
|
res = parse_log_format("${a");
|
||||||
|
|
||||||
|
CU_ASSERT(1 == res.size());
|
||||||
|
|
||||||
|
CU_ASSERT(SHRPX_LOGF_LITERAL == res[0].type);
|
||||||
|
CU_ASSERT(0 == strcmp("${a", res[0].value.get()));
|
||||||
|
|
||||||
|
res = parse_log_format("${a ");
|
||||||
|
|
||||||
|
CU_ASSERT(1 == res.size());
|
||||||
|
|
||||||
|
CU_ASSERT(SHRPX_LOGF_LITERAL == res[0].type);
|
||||||
|
CU_ASSERT(0 == strcmp("${a ", res[0].value.get()));
|
||||||
|
|
||||||
|
res = parse_log_format("$$remote_addr");
|
||||||
|
|
||||||
|
CU_ASSERT(2 == res.size());
|
||||||
|
|
||||||
|
CU_ASSERT(SHRPX_LOGF_LITERAL == res[0].type);
|
||||||
|
CU_ASSERT(0 == strcmp("$", res[0].value.get()));
|
||||||
|
|
||||||
|
CU_ASSERT(SHRPX_LOGF_REMOTE_ADDR == res[1].type);
|
||||||
|
CU_ASSERT(nullptr == res[1].value.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_shrpx_config_read_tls_ticket_key_file(void) {
|
void test_shrpx_config_read_tls_ticket_key_file(void) {
|
||||||
|
@ -172,4 +210,64 @@ void test_shrpx_config_read_tls_ticket_key_file(void) {
|
||||||
memcmp("a..............b", key->hmac_key, sizeof(key->hmac_key)));
|
memcmp("a..............b", key->hmac_key, sizeof(key->hmac_key)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_shrpx_config_match_downstream_addr_group(void) {
|
||||||
|
auto groups = std::vector<DownstreamAddrGroup>{
|
||||||
|
{"nghttp2.org/"},
|
||||||
|
{"nghttp2.org/alpha/bravo/"},
|
||||||
|
{"nghttp2.org/alpha/charlie"},
|
||||||
|
{"nghttp2.org/delta%3A"},
|
||||||
|
{"www.nghttp2.org/"},
|
||||||
|
{"[::1]/"},
|
||||||
|
};
|
||||||
|
|
||||||
|
CU_ASSERT(0 == match_downstream_addr_group("nghttp2.org", "/", groups, 255));
|
||||||
|
|
||||||
|
// port is removed
|
||||||
|
CU_ASSERT(0 ==
|
||||||
|
match_downstream_addr_group("nghttp2.org:8080", "/", groups, 255));
|
||||||
|
|
||||||
|
// host is case-insensitive
|
||||||
|
CU_ASSERT(4 == match_downstream_addr_group("WWW.nghttp2.org", "/alpha",
|
||||||
|
groups, 255));
|
||||||
|
|
||||||
|
CU_ASSERT(1 == match_downstream_addr_group("nghttp2.org", "/alpha/bravo/",
|
||||||
|
groups, 255));
|
||||||
|
|
||||||
|
// /alpha/bravo also matches /alpha/bravo/
|
||||||
|
CU_ASSERT(1 == match_downstream_addr_group("nghttp2.org", "/alpha/bravo",
|
||||||
|
groups, 255));
|
||||||
|
|
||||||
|
// path part is case-sensitive
|
||||||
|
CU_ASSERT(0 == match_downstream_addr_group("nghttp2.org", "/Alpha/bravo",
|
||||||
|
groups, 255));
|
||||||
|
|
||||||
|
CU_ASSERT(1 == match_downstream_addr_group(
|
||||||
|
"nghttp2.org", "/alpha/bravo/charlie", groups, 255));
|
||||||
|
|
||||||
|
CU_ASSERT(2 == match_downstream_addr_group("nghttp2.org", "/alpha/charlie",
|
||||||
|
groups, 255));
|
||||||
|
|
||||||
|
// pattern which does not end with '/' must match its entirely. So
|
||||||
|
// this matches to group 0, not group 2.
|
||||||
|
CU_ASSERT(0 == match_downstream_addr_group("nghttp2.org", "/alpha/charlie/",
|
||||||
|
groups, 255));
|
||||||
|
|
||||||
|
CU_ASSERT(255 ==
|
||||||
|
match_downstream_addr_group("example.org", "/", groups, 255));
|
||||||
|
|
||||||
|
CU_ASSERT(255 == match_downstream_addr_group("", "/", groups, 255));
|
||||||
|
|
||||||
|
CU_ASSERT(255 == match_downstream_addr_group("", "alpha", groups, 255));
|
||||||
|
|
||||||
|
CU_ASSERT(255 == match_downstream_addr_group("foo/bar", "/", groups, 255));
|
||||||
|
|
||||||
|
// If path is "*", only match with host + "/".
|
||||||
|
CU_ASSERT(0 == match_downstream_addr_group("nghttp2.org", "*", groups, 255));
|
||||||
|
|
||||||
|
CU_ASSERT(5 == match_downstream_addr_group("[::1]", "/", groups, 255));
|
||||||
|
CU_ASSERT(5 == match_downstream_addr_group("[::1]:8080", "/", groups, 255));
|
||||||
|
CU_ASSERT(255 == match_downstream_addr_group("[::1", "/", groups, 255));
|
||||||
|
CU_ASSERT(255 == match_downstream_addr_group("[::1]8000", "/", groups, 255));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace shrpx
|
} // namespace shrpx
|
||||||
|
|
|
@ -35,6 +35,7 @@ void test_shrpx_config_parse_config_str_list(void);
|
||||||
void test_shrpx_config_parse_header(void);
|
void test_shrpx_config_parse_header(void);
|
||||||
void test_shrpx_config_parse_log_format(void);
|
void test_shrpx_config_parse_log_format(void);
|
||||||
void test_shrpx_config_read_tls_ticket_key_file(void);
|
void test_shrpx_config_read_tls_ticket_key_file(void);
|
||||||
|
void test_shrpx_config_match_downstream_addr_group(void);
|
||||||
|
|
||||||
} // namespace shrpx
|
} // namespace shrpx
|
||||||
|
|
||||||
|
|
|
@ -152,10 +152,6 @@ Downstream::~Downstream() {
|
||||||
DLOG(INFO, this) << "Deleting";
|
DLOG(INFO, this) << "Deleting";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (blocked_link_) {
|
|
||||||
detach_blocked_link(blocked_link_);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check nullptr for unittest
|
// check nullptr for unittest
|
||||||
if (upstream_) {
|
if (upstream_) {
|
||||||
auto loop = upstream_->get_client_handler()->get_loop();
|
auto loop = upstream_->get_client_handler()->get_loop();
|
||||||
|
@ -166,6 +162,10 @@ Downstream::~Downstream() {
|
||||||
ev_timer_stop(loop, &downstream_wtimer_);
|
ev_timer_stop(loop, &downstream_wtimer_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DownstreamConnection may refer to this object. Delete it now
|
||||||
|
// explicitly.
|
||||||
|
dconn_.reset();
|
||||||
|
|
||||||
if (LOG_ENABLED(INFO)) {
|
if (LOG_ENABLED(INFO)) {
|
||||||
DLOG(INFO, this) << "Deleted";
|
DLOG(INFO, this) << "Deleted";
|
||||||
}
|
}
|
||||||
|
@ -195,8 +195,6 @@ void Downstream::detach_downstream_connection() {
|
||||||
std::unique_ptr<DownstreamConnection>(dconn_.release()));
|
std::unique_ptr<DownstreamConnection>(dconn_.release()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Downstream::release_downstream_connection() { dconn_.release(); }
|
|
||||||
|
|
||||||
DownstreamConnection *Downstream::get_downstream_connection() {
|
DownstreamConnection *Downstream::get_downstream_connection() {
|
||||||
return dconn_.get();
|
return dconn_.get();
|
||||||
}
|
}
|
||||||
|
@ -1200,12 +1198,10 @@ void Downstream::attach_blocked_link(BlockedLink *l) {
|
||||||
blocked_link_ = l;
|
blocked_link_ = l;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Downstream::detach_blocked_link(BlockedLink *l) {
|
BlockedLink *Downstream::detach_blocked_link() {
|
||||||
assert(blocked_link_);
|
auto link = blocked_link_;
|
||||||
assert(l->downstream == this);
|
|
||||||
|
|
||||||
l->downstream = nullptr;
|
|
||||||
blocked_link_ = nullptr;
|
blocked_link_ = nullptr;
|
||||||
|
return link;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Downstream::add_request_headers_sum(size_t amount) {
|
void Downstream::add_request_headers_sum(size_t amount) {
|
||||||
|
|
|
@ -69,8 +69,6 @@ public:
|
||||||
|
|
||||||
int attach_downstream_connection(std::unique_ptr<DownstreamConnection> dconn);
|
int attach_downstream_connection(std::unique_ptr<DownstreamConnection> dconn);
|
||||||
void detach_downstream_connection();
|
void detach_downstream_connection();
|
||||||
// Releases dconn_, without freeing it.
|
|
||||||
void release_downstream_connection();
|
|
||||||
DownstreamConnection *get_downstream_connection();
|
DownstreamConnection *get_downstream_connection();
|
||||||
// Returns dconn_ and nullifies dconn_.
|
// Returns dconn_ and nullifies dconn_.
|
||||||
std::unique_ptr<DownstreamConnection> pop_downstream_connection();
|
std::unique_ptr<DownstreamConnection> pop_downstream_connection();
|
||||||
|
@ -332,7 +330,7 @@ public:
|
||||||
void set_dispatch_state(int s);
|
void set_dispatch_state(int s);
|
||||||
|
|
||||||
void attach_blocked_link(BlockedLink *l);
|
void attach_blocked_link(BlockedLink *l);
|
||||||
void detach_blocked_link(BlockedLink *l);
|
BlockedLink *detach_blocked_link();
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
EVENT_ERROR = 0x1,
|
EVENT_ERROR = 0x1,
|
||||||
|
|
|
@ -57,6 +57,7 @@ public:
|
||||||
|
|
||||||
virtual void on_upstream_change(Upstream *uptream) = 0;
|
virtual void on_upstream_change(Upstream *uptream) = 0;
|
||||||
virtual int on_priority_change(int32_t pri) = 0;
|
virtual int on_priority_change(int32_t pri) = 0;
|
||||||
|
virtual size_t get_group() const = 0;
|
||||||
|
|
||||||
// true if this object is poolable.
|
// true if this object is poolable.
|
||||||
virtual bool poolable() const = 0;
|
virtual bool poolable() const = 0;
|
||||||
|
|
|
@ -27,33 +27,42 @@
|
||||||
|
|
||||||
namespace shrpx {
|
namespace shrpx {
|
||||||
|
|
||||||
DownstreamConnectionPool::DownstreamConnectionPool() {}
|
DownstreamConnectionPool::DownstreamConnectionPool(size_t num_groups)
|
||||||
|
: gpool_(num_groups) {}
|
||||||
|
|
||||||
DownstreamConnectionPool::~DownstreamConnectionPool() {
|
DownstreamConnectionPool::~DownstreamConnectionPool() {
|
||||||
for (auto dconn : pool_) {
|
for (auto &pool : gpool_) {
|
||||||
|
for (auto dconn : pool) {
|
||||||
delete dconn;
|
delete dconn;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void DownstreamConnectionPool::add_downstream_connection(
|
void DownstreamConnectionPool::add_downstream_connection(
|
||||||
std::unique_ptr<DownstreamConnection> dconn) {
|
std::unique_ptr<DownstreamConnection> dconn) {
|
||||||
pool_.insert(dconn.release());
|
auto group = dconn->get_group();
|
||||||
|
assert(gpool_.size() > group);
|
||||||
|
gpool_[group].insert(dconn.release());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<DownstreamConnection>
|
std::unique_ptr<DownstreamConnection>
|
||||||
DownstreamConnectionPool::pop_downstream_connection() {
|
DownstreamConnectionPool::pop_downstream_connection(size_t group) {
|
||||||
if (pool_.empty()) {
|
assert(gpool_.size() > group);
|
||||||
|
auto &pool = gpool_[group];
|
||||||
|
if (pool.empty()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto dconn = std::unique_ptr<DownstreamConnection>(*std::begin(pool_));
|
auto dconn = std::unique_ptr<DownstreamConnection>(*std::begin(pool));
|
||||||
pool_.erase(std::begin(pool_));
|
pool.erase(std::begin(pool));
|
||||||
return dconn;
|
return dconn;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DownstreamConnectionPool::remove_downstream_connection(
|
void DownstreamConnectionPool::remove_downstream_connection(
|
||||||
DownstreamConnection *dconn) {
|
DownstreamConnection *dconn) {
|
||||||
pool_.erase(dconn);
|
auto group = dconn->get_group();
|
||||||
|
assert(gpool_.size() > group);
|
||||||
|
gpool_[group].erase(dconn);
|
||||||
delete dconn;
|
delete dconn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,15 +36,15 @@ class DownstreamConnection;
|
||||||
|
|
||||||
class DownstreamConnectionPool {
|
class DownstreamConnectionPool {
|
||||||
public:
|
public:
|
||||||
DownstreamConnectionPool();
|
DownstreamConnectionPool(size_t num_groups);
|
||||||
~DownstreamConnectionPool();
|
~DownstreamConnectionPool();
|
||||||
|
|
||||||
void add_downstream_connection(std::unique_ptr<DownstreamConnection> dconn);
|
void add_downstream_connection(std::unique_ptr<DownstreamConnection> dconn);
|
||||||
std::unique_ptr<DownstreamConnection> pop_downstream_connection();
|
std::unique_ptr<DownstreamConnection> pop_downstream_connection(size_t group);
|
||||||
void remove_downstream_connection(DownstreamConnection *dconn);
|
void remove_downstream_connection(DownstreamConnection *dconn);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::set<DownstreamConnection *> pool_;
|
std::vector<std::set<DownstreamConnection *>> gpool_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace shrpx
|
} // namespace shrpx
|
||||||
|
|
|
@ -124,17 +124,21 @@ Downstream *DownstreamQueue::remove_and_get_blocked(Downstream *downstream) {
|
||||||
// Delete downstream when this function returns.
|
// Delete downstream when this function returns.
|
||||||
auto delptr = std::unique_ptr<Downstream>(downstream);
|
auto delptr = std::unique_ptr<Downstream>(downstream);
|
||||||
|
|
||||||
if (downstream->get_dispatch_state() != Downstream::DISPATCH_ACTIVE) {
|
|
||||||
assert(downstream->get_dispatch_state() != Downstream::DISPATCH_NONE);
|
|
||||||
downstreams_.remove(downstream);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
downstreams_.remove(downstream);
|
downstreams_.remove(downstream);
|
||||||
|
|
||||||
auto &host = make_host_key(downstream);
|
auto &host = make_host_key(downstream);
|
||||||
auto &ent = find_host_entry(host);
|
auto &ent = find_host_entry(host);
|
||||||
|
|
||||||
|
if (downstream->get_dispatch_state() == Downstream::DISPATCH_ACTIVE) {
|
||||||
--ent.num_active;
|
--ent.num_active;
|
||||||
|
} else {
|
||||||
|
// For those downstreams deleted while in blocked state
|
||||||
|
auto link = downstream->detach_blocked_link();
|
||||||
|
if (link) {
|
||||||
|
ent.blocked.remove(link);
|
||||||
|
delete link;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (remove_host_entry_if_empty(ent, host_entries_, host)) {
|
if (remove_host_entry_if_empty(ent, host_entries_, host)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -144,22 +148,21 @@ Downstream *DownstreamQueue::remove_and_get_blocked(Downstream *downstream) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto link = ent.blocked.head; link;) {
|
auto link = ent.blocked.head;
|
||||||
auto next = link->dlnext;
|
|
||||||
if (!link->downstream) {
|
if (!link) {
|
||||||
ent.blocked.remove(link);
|
return nullptr;
|
||||||
link = next;
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto next_downstream = link->downstream;
|
auto next_downstream = link->downstream;
|
||||||
next_downstream->detach_blocked_link(link);
|
auto link2 = next_downstream->detach_blocked_link();
|
||||||
|
assert(link2 == link);
|
||||||
ent.blocked.remove(link);
|
ent.blocked.remove(link);
|
||||||
delete link;
|
delete link;
|
||||||
remove_host_entry_if_empty(ent, host_entries_, host);
|
remove_host_entry_if_empty(ent, host_entries_, host);
|
||||||
|
|
||||||
return next_downstream;
|
return next_downstream;
|
||||||
}
|
}
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
Downstream *DownstreamQueue::get_downstreams() const {
|
Downstream *DownstreamQueue::get_downstreams() const {
|
||||||
return downstreams_.head;
|
return downstreams_.head;
|
||||||
|
|
|
@ -86,11 +86,7 @@ Http2DownstreamConnection::~Http2DownstreamConnection() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
http2session_->remove_downstream_connection(this);
|
http2session_->remove_downstream_connection(this);
|
||||||
// Downstream and DownstreamConnection may be deleted
|
|
||||||
// asynchronously.
|
|
||||||
if (downstream_) {
|
|
||||||
downstream_->release_downstream_connection();
|
|
||||||
}
|
|
||||||
if (LOG_ENABLED(INFO)) {
|
if (LOG_ENABLED(INFO)) {
|
||||||
DCLOG(INFO, this) << "Deleted";
|
DCLOG(INFO, this) << "Deleted";
|
||||||
}
|
}
|
||||||
|
@ -263,8 +259,11 @@ int Http2DownstreamConnection::push_request_headers() {
|
||||||
// http2session_ has already in CONNECTED state, so we can get
|
// http2session_ has already in CONNECTED state, so we can get
|
||||||
// addr_idx here.
|
// addr_idx here.
|
||||||
auto addr_idx = http2session_->get_addr_idx();
|
auto addr_idx = http2session_->get_addr_idx();
|
||||||
auto downstream_hostport =
|
auto group = http2session_->get_group();
|
||||||
get_config()->downstream_addrs[addr_idx].hostport.get();
|
auto downstream_hostport = get_config()
|
||||||
|
->downstream_addr_groups[group]
|
||||||
|
.addrs[addr_idx]
|
||||||
|
.hostport.get();
|
||||||
|
|
||||||
const char *authority = nullptr, *host = nullptr;
|
const char *authority = nullptr, *host = nullptr;
|
||||||
if (!no_host_rewrite) {
|
if (!no_host_rewrite) {
|
||||||
|
@ -557,4 +556,10 @@ int Http2DownstreamConnection::on_timeout() {
|
||||||
return submit_rst_stream(downstream_, NGHTTP2_NO_ERROR);
|
return submit_rst_stream(downstream_, NGHTTP2_NO_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t Http2DownstreamConnection::get_group() const {
|
||||||
|
// HTTP/2 backend connections are managed by Http2Session object,
|
||||||
|
// and it stores group index.
|
||||||
|
return http2session_->get_group();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace shrpx
|
} // namespace shrpx
|
||||||
|
|
|
@ -61,6 +61,7 @@ public:
|
||||||
|
|
||||||
virtual void on_upstream_change(Upstream *upstream) {}
|
virtual void on_upstream_change(Upstream *upstream) {}
|
||||||
virtual int on_priority_change(int32_t pri);
|
virtual int on_priority_change(int32_t pri);
|
||||||
|
virtual size_t get_group() const;
|
||||||
|
|
||||||
// This object is not poolable because we dont' have facility to
|
// This object is not poolable because we dont' have facility to
|
||||||
// migrate to another Http2Session object.
|
// migrate to another Http2Session object.
|
||||||
|
|
|
@ -142,13 +142,14 @@ void writecb(struct ev_loop *loop, ev_io *w, int revents) {
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
Http2Session::Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx,
|
Http2Session::Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx,
|
||||||
ConnectBlocker *connect_blocker, Worker *worker)
|
ConnectBlocker *connect_blocker, Worker *worker,
|
||||||
|
size_t group, size_t idx)
|
||||||
: conn_(loop, -1, nullptr, get_config()->downstream_write_timeout,
|
: conn_(loop, -1, nullptr, get_config()->downstream_write_timeout,
|
||||||
get_config()->downstream_read_timeout, 0, 0, 0, 0, writecb, readcb,
|
get_config()->downstream_read_timeout, 0, 0, 0, 0, writecb, readcb,
|
||||||
timeoutcb, this),
|
timeoutcb, this),
|
||||||
worker_(worker), connect_blocker_(connect_blocker), ssl_ctx_(ssl_ctx),
|
worker_(worker), connect_blocker_(connect_blocker), ssl_ctx_(ssl_ctx),
|
||||||
session_(nullptr), data_pending_(nullptr), data_pendinglen_(0),
|
session_(nullptr), data_pending_(nullptr), data_pendinglen_(0),
|
||||||
addr_idx_(0), state_(DISCONNECTED),
|
addr_idx_(0), group_(group), index_(idx), state_(DISCONNECTED),
|
||||||
connection_check_state_(CONNECTION_CHECK_NONE), flow_control_(false) {
|
connection_check_state_(CONNECTION_CHECK_NONE), flow_control_(false) {
|
||||||
|
|
||||||
read_ = write_ = &Http2Session::noop;
|
read_ = write_ = &Http2Session::noop;
|
||||||
|
@ -233,11 +234,17 @@ int Http2Session::disconnect(bool hard) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Http2Session::check_cert() { return ssl::check_cert(conn_.tls.ssl); }
|
int Http2Session::check_cert() {
|
||||||
|
return ssl::check_cert(
|
||||||
|
conn_.tls.ssl,
|
||||||
|
&get_config()->downstream_addr_groups[group_].addrs[addr_idx_]);
|
||||||
|
}
|
||||||
|
|
||||||
int Http2Session::initiate_connection() {
|
int Http2Session::initiate_connection() {
|
||||||
int rv = 0;
|
int rv = 0;
|
||||||
|
|
||||||
|
auto &addrs = get_config()->downstream_addr_groups[group_].addrs;
|
||||||
|
|
||||||
if (state_ == DISCONNECTED) {
|
if (state_ == DISCONNECTED) {
|
||||||
if (connect_blocker_->blocked()) {
|
if (connect_blocker_->blocked()) {
|
||||||
if (LOG_ENABLED(INFO)) {
|
if (LOG_ENABLED(INFO)) {
|
||||||
|
@ -247,18 +254,19 @@ int Http2Session::initiate_connection() {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto worker_stat = worker_->get_worker_stat();
|
auto &next_downstream = worker_->get_dgrp(group_)->next;
|
||||||
addr_idx_ = worker_stat->next_downstream;
|
addr_idx_ = next_downstream;
|
||||||
++worker_stat->next_downstream;
|
if (++next_downstream >= addrs.size()) {
|
||||||
worker_stat->next_downstream %= get_config()->downstream_addrs.size();
|
next_downstream = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (LOG_ENABLED(INFO)) {
|
if (LOG_ENABLED(INFO)) {
|
||||||
SSLOG(INFO, this) << "Using downstream address idx=" << addr_idx_
|
SSLOG(INFO, this) << "Using downstream address idx=" << addr_idx_
|
||||||
<< " out of " << get_config()->downstream_addrs.size();
|
<< " out of " << addrs.size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto &downstream_addr = get_config()->downstream_addrs[addr_idx_];
|
auto &downstream_addr = addrs[addr_idx_];
|
||||||
|
|
||||||
if (get_config()->downstream_http_proxy_host && state_ == DISCONNECTED) {
|
if (get_config()->downstream_http_proxy_host && state_ == DISCONNECTED) {
|
||||||
if (LOG_ENABLED(INFO)) {
|
if (LOG_ENABLED(INFO)) {
|
||||||
|
@ -503,7 +511,8 @@ int Http2Session::downstream_connect_proxy() {
|
||||||
if (LOG_ENABLED(INFO)) {
|
if (LOG_ENABLED(INFO)) {
|
||||||
SSLOG(INFO, this) << "Connected to the proxy";
|
SSLOG(INFO, this) << "Connected to the proxy";
|
||||||
}
|
}
|
||||||
auto &downstream_addr = get_config()->downstream_addrs[addr_idx_];
|
auto &downstream_addr =
|
||||||
|
get_config()->downstream_addr_groups[group_].addrs[addr_idx_];
|
||||||
|
|
||||||
std::string req = "CONNECT ";
|
std::string req = "CONNECT ";
|
||||||
req += downstream_addr.hostport.get();
|
req += downstream_addr.hostport.get();
|
||||||
|
@ -1744,4 +1753,8 @@ bool Http2Session::should_hard_fail() const {
|
||||||
|
|
||||||
size_t Http2Session::get_addr_idx() const { return addr_idx_; }
|
size_t Http2Session::get_addr_idx() const { return addr_idx_; }
|
||||||
|
|
||||||
|
size_t Http2Session::get_group() const { return group_; }
|
||||||
|
|
||||||
|
size_t Http2Session::get_index() const { return index_; }
|
||||||
|
|
||||||
} // namespace shrpx
|
} // namespace shrpx
|
||||||
|
|
|
@ -58,7 +58,8 @@ struct StreamData {
|
||||||
class Http2Session {
|
class Http2Session {
|
||||||
public:
|
public:
|
||||||
Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx,
|
Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx,
|
||||||
ConnectBlocker *connect_blocker, Worker *worker);
|
ConnectBlocker *connect_blocker, Worker *worker, size_t group,
|
||||||
|
size_t idx);
|
||||||
~Http2Session();
|
~Http2Session();
|
||||||
|
|
||||||
int check_cert();
|
int check_cert();
|
||||||
|
@ -151,6 +152,10 @@ public:
|
||||||
|
|
||||||
size_t get_addr_idx() const;
|
size_t get_addr_idx() const;
|
||||||
|
|
||||||
|
size_t get_group() const;
|
||||||
|
|
||||||
|
size_t get_index() const;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
// Disconnected
|
// Disconnected
|
||||||
DISCONNECTED,
|
DISCONNECTED,
|
||||||
|
@ -203,6 +208,10 @@ private:
|
||||||
size_t data_pendinglen_;
|
size_t data_pendinglen_;
|
||||||
// index of get_config()->downstream_addrs this object uses
|
// index of get_config()->downstream_addrs this object uses
|
||||||
size_t addr_idx_;
|
size_t addr_idx_;
|
||||||
|
size_t group_;
|
||||||
|
// index inside group, this is used to pin frontend to certain
|
||||||
|
// HTTP/2 backend for better throughput.
|
||||||
|
size_t index_;
|
||||||
int state_;
|
int state_;
|
||||||
int connection_check_state_;
|
int connection_check_state_;
|
||||||
bool flow_control_;
|
bool flow_control_;
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
#include "shrpx_config.h"
|
#include "shrpx_config.h"
|
||||||
#include "shrpx_http.h"
|
#include "shrpx_http.h"
|
||||||
#include "shrpx_worker.h"
|
#include "shrpx_worker.h"
|
||||||
|
#include "shrpx_http2_session.h"
|
||||||
#include "http2.h"
|
#include "http2.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "base64.h"
|
#include "base64.h"
|
||||||
|
@ -300,7 +301,15 @@ int Http2Upstream::on_request_headers(Downstream *downstream,
|
||||||
downstream->set_request_method(method_token);
|
downstream->set_request_method(method_token);
|
||||||
downstream->set_request_http2_scheme(http2::value_to_str(scheme));
|
downstream->set_request_http2_scheme(http2::value_to_str(scheme));
|
||||||
downstream->set_request_http2_authority(http2::value_to_str(authority));
|
downstream->set_request_http2_authority(http2::value_to_str(authority));
|
||||||
|
if (path) {
|
||||||
|
if (get_config()->http2_proxy || get_config()->client_proxy) {
|
||||||
downstream->set_request_path(http2::value_to_str(path));
|
downstream->set_request_path(http2::value_to_str(path));
|
||||||
|
} else {
|
||||||
|
auto &value = path->value;
|
||||||
|
downstream->set_request_path(
|
||||||
|
http2::rewrite_clean_path(std::begin(value), std::end(value)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) {
|
if (!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) {
|
||||||
downstream->set_request_http2_expect_body(true);
|
downstream->set_request_http2_expect_body(true);
|
||||||
|
@ -334,7 +343,7 @@ void Http2Upstream::initiate_downstream(Downstream *downstream) {
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
rv = downstream->attach_downstream_connection(
|
rv = downstream->attach_downstream_connection(
|
||||||
handler_->get_downstream_connection());
|
handler_->get_downstream_connection(downstream));
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
// downstream connection fails, send error page
|
// downstream connection fails, send error page
|
||||||
if (error_reply(downstream, 503) != 0) {
|
if (error_reply(downstream, 503) != 0) {
|
||||||
|
@ -541,7 +550,8 @@ int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||||
{nv.value, nv.value + nv.valuelen});
|
{nv.value, nv.value + nv.valuelen});
|
||||||
break;
|
break;
|
||||||
case http2::HD__PATH:
|
case http2::HD__PATH:
|
||||||
downstream->set_request_path({nv.value, nv.value + nv.valuelen});
|
downstream->set_request_path(
|
||||||
|
http2::rewrite_clean_path(nv.value, nv.value + nv.valuelen));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
downstream->add_request_header(nv.name, nv.namelen, nv.value, nv.valuelen,
|
downstream->add_request_header(nv.name, nv.namelen, nv.value, nv.valuelen,
|
||||||
|
@ -874,16 +884,6 @@ ClientHandler *Http2Upstream::get_client_handler() const { return handler_; }
|
||||||
int Http2Upstream::downstream_read(DownstreamConnection *dconn) {
|
int Http2Upstream::downstream_read(DownstreamConnection *dconn) {
|
||||||
auto downstream = dconn->get_downstream();
|
auto downstream = dconn->get_downstream();
|
||||||
|
|
||||||
if (downstream->get_request_state() == Downstream::STREAM_CLOSED) {
|
|
||||||
// If upstream HTTP2 stream was closed, we just close downstream,
|
|
||||||
// because there is no consumer now. Downstream connection is also
|
|
||||||
// closed in this case.
|
|
||||||
remove_downstream(downstream);
|
|
||||||
// downstream was deleted
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (downstream->get_response_state() == Downstream::MSG_RESET) {
|
if (downstream->get_response_state() == Downstream::MSG_RESET) {
|
||||||
// The downstream stream was reset (canceled). In this case,
|
// The downstream stream was reset (canceled). In this case,
|
||||||
// RST_STREAM to the upstream and delete downstream connection
|
// RST_STREAM to the upstream and delete downstream connection
|
||||||
|
@ -949,14 +949,6 @@ int Http2Upstream::downstream_eof(DownstreamConnection *dconn) {
|
||||||
if (LOG_ENABLED(INFO)) {
|
if (LOG_ENABLED(INFO)) {
|
||||||
DCLOG(INFO, dconn) << "EOF. stream_id=" << downstream->get_stream_id();
|
DCLOG(INFO, dconn) << "EOF. stream_id=" << downstream->get_stream_id();
|
||||||
}
|
}
|
||||||
if (downstream->get_request_state() == Downstream::STREAM_CLOSED) {
|
|
||||||
// If stream was closed already, we don't need to send reply at
|
|
||||||
// the first place. We can delete downstream.
|
|
||||||
remove_downstream(downstream);
|
|
||||||
// downstream was deleted
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete downstream connection. If we don't delete it here, it will
|
// Delete downstream connection. If we don't delete it here, it will
|
||||||
// be pooled in on_stream_close_callback.
|
// be pooled in on_stream_close_callback.
|
||||||
|
@ -1002,13 +994,6 @@ int Http2Upstream::downstream_error(DownstreamConnection *dconn, int events) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (downstream->get_request_state() == Downstream::STREAM_CLOSED) {
|
|
||||||
remove_downstream(downstream);
|
|
||||||
// downstream was deleted
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete downstream connection. If we don't delete it here, it will
|
// Delete downstream connection. If we don't delete it here, it will
|
||||||
// be pooled in on_stream_close_callback.
|
// be pooled in on_stream_close_callback.
|
||||||
downstream->pop_downstream_connection();
|
downstream->pop_downstream_connection();
|
||||||
|
@ -1476,7 +1461,7 @@ int Http2Upstream::on_downstream_reset(bool no_retry) {
|
||||||
// downstream connection.
|
// downstream connection.
|
||||||
|
|
||||||
rv = downstream->attach_downstream_connection(
|
rv = downstream->attach_downstream_connection(
|
||||||
handler_->get_downstream_connection());
|
handler_->get_downstream_connection(downstream));
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
#include "shrpx_connect_blocker.h"
|
#include "shrpx_connect_blocker.h"
|
||||||
#include "shrpx_downstream_connection_pool.h"
|
#include "shrpx_downstream_connection_pool.h"
|
||||||
#include "shrpx_worker.h"
|
#include "shrpx_worker.h"
|
||||||
|
#include "shrpx_http2_session.h"
|
||||||
#include "http2.h"
|
#include "http2.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
|
@ -109,21 +110,15 @@ void connectcb(struct ev_loop *loop, ev_io *w, int revents) {
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
HttpDownstreamConnection::HttpDownstreamConnection(
|
HttpDownstreamConnection::HttpDownstreamConnection(
|
||||||
DownstreamConnectionPool *dconn_pool, struct ev_loop *loop)
|
DownstreamConnectionPool *dconn_pool, size_t group, struct ev_loop *loop)
|
||||||
: DownstreamConnection(dconn_pool),
|
: DownstreamConnection(dconn_pool),
|
||||||
conn_(loop, -1, nullptr, get_config()->downstream_write_timeout,
|
conn_(loop, -1, nullptr, get_config()->downstream_write_timeout,
|
||||||
get_config()->downstream_read_timeout, 0, 0, 0, 0, connectcb,
|
get_config()->downstream_read_timeout, 0, 0, 0, 0, connectcb,
|
||||||
readcb, timeoutcb, this),
|
readcb, timeoutcb, this),
|
||||||
ioctrl_(&conn_.rlimit), response_htp_{0}, addr_idx_(0),
|
ioctrl_(&conn_.rlimit), response_htp_{0}, group_(group), addr_idx_(0),
|
||||||
connected_(false) {}
|
connected_(false) {}
|
||||||
|
|
||||||
HttpDownstreamConnection::~HttpDownstreamConnection() {
|
HttpDownstreamConnection::~HttpDownstreamConnection() {}
|
||||||
// Downstream and DownstreamConnection may be deleted
|
|
||||||
// asynchronously.
|
|
||||||
if (downstream_) {
|
|
||||||
downstream_->release_downstream_connection();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int HttpDownstreamConnection::attach_downstream(Downstream *downstream) {
|
int HttpDownstreamConnection::attach_downstream(Downstream *downstream) {
|
||||||
if (LOG_ENABLED(INFO)) {
|
if (LOG_ENABLED(INFO)) {
|
||||||
|
@ -142,15 +137,17 @@ int HttpDownstreamConnection::attach_downstream(Downstream *downstream) {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto worker = client_handler_->get_worker();
|
auto worker = client_handler_->get_worker();
|
||||||
auto worker_stat = worker->get_worker_stat();
|
auto &next_downstream = worker->get_dgrp(group_)->next;
|
||||||
auto end = worker_stat->next_downstream;
|
auto end = next_downstream;
|
||||||
|
auto &addrs = get_config()->downstream_addr_groups[group_].addrs;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
auto i = worker_stat->next_downstream;
|
auto &addr = addrs[next_downstream];
|
||||||
++worker_stat->next_downstream;
|
auto i = next_downstream;
|
||||||
worker_stat->next_downstream %= get_config()->downstream_addrs.size();
|
if (++next_downstream >= addrs.size()) {
|
||||||
|
next_downstream = 0;
|
||||||
|
}
|
||||||
|
|
||||||
conn_.fd = util::create_nonblock_socket(
|
conn_.fd = util::create_nonblock_socket(addr.addr.storage.ss_family);
|
||||||
get_config()->downstream_addrs[i].addr.storage.ss_family);
|
|
||||||
|
|
||||||
if (conn_.fd == -1) {
|
if (conn_.fd == -1) {
|
||||||
auto error = errno;
|
auto error = errno;
|
||||||
|
@ -162,8 +159,7 @@ int HttpDownstreamConnection::attach_downstream(Downstream *downstream) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int rv;
|
int rv;
|
||||||
rv = connect(conn_.fd, &get_config()->downstream_addrs[i].addr.sa,
|
rv = connect(conn_.fd, &addr.addr.sa, addr.addrlen);
|
||||||
get_config()->downstream_addrs[i].addrlen);
|
|
||||||
if (rv != 0 && errno != EINPROGRESS) {
|
if (rv != 0 && errno != EINPROGRESS) {
|
||||||
auto error = errno;
|
auto error = errno;
|
||||||
DCLOG(WARN, this) << "connect() failed; errno=" << error;
|
DCLOG(WARN, this) << "connect() failed; errno=" << error;
|
||||||
|
@ -172,7 +168,7 @@ int HttpDownstreamConnection::attach_downstream(Downstream *downstream) {
|
||||||
close(conn_.fd);
|
close(conn_.fd);
|
||||||
conn_.fd = -1;
|
conn_.fd = -1;
|
||||||
|
|
||||||
if (end == worker_stat->next_downstream) {
|
if (end == next_downstream) {
|
||||||
return SHRPX_ERR_NETWORK;
|
return SHRPX_ERR_NETWORK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,6 +199,8 @@ int HttpDownstreamConnection::attach_downstream(Downstream *downstream) {
|
||||||
ev_set_cb(&conn_.rev, readcb);
|
ev_set_cb(&conn_.rev, readcb);
|
||||||
|
|
||||||
conn_.rt.repeat = get_config()->downstream_read_timeout;
|
conn_.rt.repeat = get_config()->downstream_read_timeout;
|
||||||
|
// we may set read timer cb to idle_timeoutcb. Reset again.
|
||||||
|
ev_set_cb(&conn_.rt, timeoutcb);
|
||||||
ev_timer_again(conn_.loop, &conn_.rt);
|
ev_timer_again(conn_.loop, &conn_.rt);
|
||||||
// TODO we should have timeout for connection establishment
|
// TODO we should have timeout for connection establishment
|
||||||
ev_timer_again(conn_.loop, &conn_.wt);
|
ev_timer_again(conn_.loop, &conn_.wt);
|
||||||
|
@ -212,8 +210,10 @@ int HttpDownstreamConnection::attach_downstream(Downstream *downstream) {
|
||||||
|
|
||||||
int HttpDownstreamConnection::push_request_headers() {
|
int HttpDownstreamConnection::push_request_headers() {
|
||||||
const char *authority = nullptr, *host = nullptr;
|
const char *authority = nullptr, *host = nullptr;
|
||||||
auto downstream_hostport =
|
auto downstream_hostport = get_config()
|
||||||
get_config()->downstream_addrs[addr_idx_].hostport.get();
|
->downstream_addr_groups[group_]
|
||||||
|
.addrs[addr_idx_]
|
||||||
|
.hostport.get();
|
||||||
auto connect_method = downstream_->get_request_method() == HTTP_CONNECT;
|
auto connect_method = downstream_->get_request_method() == HTTP_CONNECT;
|
||||||
|
|
||||||
if (!get_config()->no_host_rewrite && !get_config()->http2_proxy &&
|
if (!get_config()->no_host_rewrite && !get_config()->http2_proxy &&
|
||||||
|
@ -860,6 +860,8 @@ int HttpDownstreamConnection::on_connect() {
|
||||||
DLOG(INFO, this) << "downstream connect failed";
|
DLOG(INFO, this) << "downstream connect failed";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
downstream_->set_request_state(Downstream::CONNECT_FAIL);
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -877,4 +879,6 @@ void HttpDownstreamConnection::on_upstream_change(Upstream *upstream) {}
|
||||||
|
|
||||||
void HttpDownstreamConnection::signal_write() { conn_.wlimit.startw(); }
|
void HttpDownstreamConnection::signal_write() { conn_.wlimit.startw(); }
|
||||||
|
|
||||||
|
size_t HttpDownstreamConnection::get_group() const { return group_; }
|
||||||
|
|
||||||
} // namespace shrpx
|
} // namespace shrpx
|
||||||
|
|
|
@ -39,7 +39,7 @@ class DownstreamConnectionPool;
|
||||||
|
|
||||||
class HttpDownstreamConnection : public DownstreamConnection {
|
class HttpDownstreamConnection : public DownstreamConnection {
|
||||||
public:
|
public:
|
||||||
HttpDownstreamConnection(DownstreamConnectionPool *dconn_pool,
|
HttpDownstreamConnection(DownstreamConnectionPool *dconn_pool, size_t group,
|
||||||
struct ev_loop *loop);
|
struct ev_loop *loop);
|
||||||
virtual ~HttpDownstreamConnection();
|
virtual ~HttpDownstreamConnection();
|
||||||
virtual int attach_downstream(Downstream *downstream);
|
virtual int attach_downstream(Downstream *downstream);
|
||||||
|
@ -58,6 +58,7 @@ public:
|
||||||
|
|
||||||
virtual void on_upstream_change(Upstream *upstream);
|
virtual void on_upstream_change(Upstream *upstream);
|
||||||
virtual int on_priority_change(int32_t pri) { return 0; }
|
virtual int on_priority_change(int32_t pri) { return 0; }
|
||||||
|
virtual size_t get_group() const;
|
||||||
|
|
||||||
virtual bool poolable() const { return true; }
|
virtual bool poolable() const { return true; }
|
||||||
|
|
||||||
|
@ -68,6 +69,7 @@ private:
|
||||||
Connection conn_;
|
Connection conn_;
|
||||||
IOControl ioctrl_;
|
IOControl ioctrl_;
|
||||||
http_parser response_htp_;
|
http_parser response_htp_;
|
||||||
|
size_t group_;
|
||||||
// index of get_config()->downstream_addrs this object is using
|
// index of get_config()->downstream_addrs this object is using
|
||||||
size_t addr_idx_;
|
size_t addr_idx_;
|
||||||
bool connected_;
|
bool connected_;
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
#include "shrpx_error.h"
|
#include "shrpx_error.h"
|
||||||
#include "shrpx_log_config.h"
|
#include "shrpx_log_config.h"
|
||||||
#include "shrpx_worker.h"
|
#include "shrpx_worker.h"
|
||||||
|
#include "shrpx_http2_session.h"
|
||||||
#include "http2.h"
|
#include "http2.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "template.h"
|
#include "template.h"
|
||||||
|
@ -218,7 +219,12 @@ void rewrite_request_host_path_from_uri(Downstream *downstream, const char *uri,
|
||||||
path += '?';
|
path += '?';
|
||||||
path.append(uri + fdata.off, fdata.len);
|
path.append(uri + fdata.off, fdata.len);
|
||||||
}
|
}
|
||||||
|
if (get_config()->http2_proxy || get_config()->client_proxy) {
|
||||||
downstream->set_request_path(std::move(path));
|
downstream->set_request_path(std::move(path));
|
||||||
|
} else {
|
||||||
|
downstream->set_request_path(
|
||||||
|
http2::rewrite_clean_path(std::begin(path), std::end(path)));
|
||||||
|
}
|
||||||
|
|
||||||
std::string scheme;
|
std::string scheme;
|
||||||
http2::copy_url_component(scheme, &u, UF_SCHEMA, uri);
|
http2::copy_url_component(scheme, &u, UF_SCHEMA, uri);
|
||||||
|
@ -286,6 +292,9 @@ int htp_hdrs_completecb(http_parser *htp) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
downstream->set_request_path(
|
||||||
|
http2::rewrite_clean_path(std::begin(uri), std::end(uri)));
|
||||||
|
|
||||||
if (upstream->get_client_handler()->get_ssl()) {
|
if (upstream->get_client_handler()->get_ssl()) {
|
||||||
downstream->set_request_http2_scheme("https");
|
downstream->set_request_http2_scheme("https");
|
||||||
} else {
|
} else {
|
||||||
|
@ -297,7 +306,7 @@ int htp_hdrs_completecb(http_parser *htp) {
|
||||||
}
|
}
|
||||||
|
|
||||||
rv = downstream->attach_downstream_connection(
|
rv = downstream->attach_downstream_connection(
|
||||||
upstream->get_client_handler()->get_downstream_connection());
|
upstream->get_client_handler()->get_downstream_connection(downstream));
|
||||||
|
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
downstream->set_request_state(Downstream::CONNECT_FAIL);
|
downstream->set_request_state(Downstream::CONNECT_FAIL);
|
||||||
|
@ -993,7 +1002,7 @@ int HttpsUpstream::on_downstream_reset(bool no_retry) {
|
||||||
}
|
}
|
||||||
|
|
||||||
rv = downstream_->attach_downstream_connection(
|
rv = downstream_->attach_downstream_connection(
|
||||||
handler_->get_downstream_connection());
|
handler_->get_downstream_connection(downstream_.get()));
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,6 +123,8 @@ enum LogFragmentType {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct LogFragment {
|
struct LogFragment {
|
||||||
|
LogFragment(LogFragmentType type, std::unique_ptr<char[]> value = nullptr)
|
||||||
|
: type(type), value(std::move(value)) {}
|
||||||
LogFragmentType type;
|
LogFragmentType type;
|
||||||
std::unique_ptr<char[]> value;
|
std::unique_ptr<char[]> value;
|
||||||
};
|
};
|
||||||
|
|
|
@ -229,7 +229,12 @@ void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type,
|
||||||
} else {
|
} else {
|
||||||
downstream->set_request_http2_scheme(scheme->value);
|
downstream->set_request_http2_scheme(scheme->value);
|
||||||
downstream->set_request_http2_authority(host->value);
|
downstream->set_request_http2_authority(host->value);
|
||||||
|
if (get_config()->http2_proxy || get_config()->client_proxy) {
|
||||||
downstream->set_request_path(path->value);
|
downstream->set_request_path(path->value);
|
||||||
|
} else {
|
||||||
|
downstream->set_request_path(http2::rewrite_clean_path(
|
||||||
|
std::begin(path->value), std::end(path->value)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(frame->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_FIN)) {
|
if (!(frame->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_FIN)) {
|
||||||
|
@ -271,7 +276,7 @@ void SpdyUpstream::start_downstream(Downstream *downstream) {
|
||||||
|
|
||||||
void SpdyUpstream::initiate_downstream(Downstream *downstream) {
|
void SpdyUpstream::initiate_downstream(Downstream *downstream) {
|
||||||
int rv = downstream->attach_downstream_connection(
|
int rv = downstream->attach_downstream_connection(
|
||||||
handler_->get_downstream_connection());
|
handler_->get_downstream_connection(downstream));
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
// If downstream connection fails, issue RST_STREAM.
|
// If downstream connection fails, issue RST_STREAM.
|
||||||
rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);
|
rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);
|
||||||
|
@ -555,16 +560,6 @@ ClientHandler *SpdyUpstream::get_client_handler() const { return handler_; }
|
||||||
int SpdyUpstream::downstream_read(DownstreamConnection *dconn) {
|
int SpdyUpstream::downstream_read(DownstreamConnection *dconn) {
|
||||||
auto downstream = dconn->get_downstream();
|
auto downstream = dconn->get_downstream();
|
||||||
|
|
||||||
if (downstream->get_request_state() == Downstream::STREAM_CLOSED) {
|
|
||||||
// If upstream SPDY stream was closed, we just close downstream,
|
|
||||||
// because there is no consumer now. Downstream connection is also
|
|
||||||
// closed in this case.
|
|
||||||
remove_downstream(downstream);
|
|
||||||
// downstrea was deleted
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (downstream->get_response_state() == Downstream::MSG_RESET) {
|
if (downstream->get_response_state() == Downstream::MSG_RESET) {
|
||||||
// The downstream stream was reset (canceled). In this case,
|
// The downstream stream was reset (canceled). In this case,
|
||||||
// RST_STREAM to the upstream and delete downstream connection
|
// RST_STREAM to the upstream and delete downstream connection
|
||||||
|
@ -628,14 +623,6 @@ int SpdyUpstream::downstream_eof(DownstreamConnection *dconn) {
|
||||||
if (LOG_ENABLED(INFO)) {
|
if (LOG_ENABLED(INFO)) {
|
||||||
DCLOG(INFO, dconn) << "EOF. stream_id=" << downstream->get_stream_id();
|
DCLOG(INFO, dconn) << "EOF. stream_id=" << downstream->get_stream_id();
|
||||||
}
|
}
|
||||||
if (downstream->get_request_state() == Downstream::STREAM_CLOSED) {
|
|
||||||
// If stream was closed already, we don't need to send reply at
|
|
||||||
// the first place. We can delete downstream.
|
|
||||||
remove_downstream(downstream);
|
|
||||||
// downstream was deleted
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete downstream connection. If we don't delete it here, it will
|
// Delete downstream connection. If we don't delete it here, it will
|
||||||
// be pooled in on_stream_close_callback.
|
// be pooled in on_stream_close_callback.
|
||||||
|
@ -681,13 +668,6 @@ int SpdyUpstream::downstream_error(DownstreamConnection *dconn, int events) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (downstream->get_request_state() == Downstream::STREAM_CLOSED) {
|
|
||||||
remove_downstream(downstream);
|
|
||||||
// downstream was deleted
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete downstream connection. If we don't delete it here, it will
|
// Delete downstream connection. If we don't delete it here, it will
|
||||||
// be pooled in on_stream_close_callback.
|
// be pooled in on_stream_close_callback.
|
||||||
downstream->pop_downstream_connection();
|
downstream->pop_downstream_connection();
|
||||||
|
@ -1104,7 +1084,7 @@ int SpdyUpstream::on_downstream_reset(bool no_retry) {
|
||||||
// downstream connection.
|
// downstream connection.
|
||||||
|
|
||||||
rv = downstream->attach_downstream_connection(
|
rv = downstream->attach_downstream_connection(
|
||||||
handler_->get_downstream_connection());
|
handler_->get_downstream_connection(downstream));
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,7 @@
|
||||||
#include "shrpx_config.h"
|
#include "shrpx_config.h"
|
||||||
#include "shrpx_worker.h"
|
#include "shrpx_worker.h"
|
||||||
#include "shrpx_downstream_connection_pool.h"
|
#include "shrpx_downstream_connection_pool.h"
|
||||||
|
#include "shrpx_http2_session.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "ssl.h"
|
#include "ssl.h"
|
||||||
#include "template.h"
|
#include "template.h"
|
||||||
|
@ -743,7 +744,7 @@ void get_altnames(X509 *cert, std::vector<std::string> &dns_names,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int check_cert(SSL *ssl) {
|
int check_cert(SSL *ssl, const DownstreamAddr *addr) {
|
||||||
auto cert = SSL_get_peer_certificate(ssl);
|
auto cert = SSL_get_peer_certificate(ssl);
|
||||||
if (!cert) {
|
if (!cert) {
|
||||||
LOG(ERROR) << "No certificate found";
|
LOG(ERROR) << "No certificate found";
|
||||||
|
@ -760,9 +761,7 @@ int check_cert(SSL *ssl) {
|
||||||
std::vector<std::string> dns_names;
|
std::vector<std::string> dns_names;
|
||||||
std::vector<std::string> ip_addrs;
|
std::vector<std::string> ip_addrs;
|
||||||
get_altnames(cert, dns_names, ip_addrs, common_name);
|
get_altnames(cert, dns_names, ip_addrs, common_name);
|
||||||
if (verify_hostname(get_config()->downstream_addrs[0].host.get(),
|
if (verify_hostname(addr->host.get(), &addr->addr, addr->addrlen, dns_names,
|
||||||
&get_config()->downstream_addrs[0].addr,
|
|
||||||
get_config()->downstream_addrs[0].addrlen, dns_names,
|
|
||||||
ip_addrs, common_name) != 0) {
|
ip_addrs, common_name) != 0) {
|
||||||
LOG(ERROR) << "Certificate verification failed: hostname does not match";
|
LOG(ERROR) << "Certificate verification failed: hostname does not match";
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
@ -40,6 +40,7 @@ namespace shrpx {
|
||||||
class ClientHandler;
|
class ClientHandler;
|
||||||
class Worker;
|
class Worker;
|
||||||
class DownstreamConnectionPool;
|
class DownstreamConnectionPool;
|
||||||
|
struct DownstreamAddr;
|
||||||
|
|
||||||
namespace ssl {
|
namespace ssl {
|
||||||
|
|
||||||
|
@ -68,7 +69,7 @@ ClientHandler *accept_connection(Worker *worker, int fd, sockaddr *addr,
|
||||||
// Check peer's certificate against first downstream address in
|
// Check peer's certificate against first downstream address in
|
||||||
// Config::downstream_addrs. We only consider first downstream since
|
// Config::downstream_addrs. We only consider first downstream since
|
||||||
// we use this function for HTTP/2 downstream link only.
|
// we use this function for HTTP/2 downstream link only.
|
||||||
int check_cert(SSL *ssl);
|
int check_cert(SSL *ssl, const DownstreamAddr *addr);
|
||||||
|
|
||||||
// Retrieves DNS and IP address in subjectAltNames and commonName from
|
// Retrieves DNS and IP address in subjectAltNames and commonName from
|
||||||
// the |cert|.
|
// the |cert|.
|
||||||
|
|
|
@ -61,8 +61,11 @@ void mcpool_clear_cb(struct ev_loop *loop, ev_timer *w, int revents) {
|
||||||
Worker::Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx,
|
Worker::Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx,
|
||||||
ssl::CertLookupTree *cert_tree,
|
ssl::CertLookupTree *cert_tree,
|
||||||
const std::shared_ptr<TicketKeys> &ticket_keys)
|
const std::shared_ptr<TicketKeys> &ticket_keys)
|
||||||
: next_http2session_(0), loop_(loop), sv_ssl_ctx_(sv_ssl_ctx),
|
: dconn_pool_(get_config()->downstream_addr_groups.size()),
|
||||||
cl_ssl_ctx_(cl_ssl_ctx), cert_tree_(cert_tree), ticket_keys_(ticket_keys),
|
worker_stat_(get_config()->downstream_addr_groups.size()),
|
||||||
|
dgrps_(get_config()->downstream_addr_groups.size()), loop_(loop),
|
||||||
|
sv_ssl_ctx_(sv_ssl_ctx), cl_ssl_ctx_(cl_ssl_ctx), cert_tree_(cert_tree),
|
||||||
|
ticket_keys_(ticket_keys),
|
||||||
connect_blocker_(make_unique<ConnectBlocker>(loop_)),
|
connect_blocker_(make_unique<ConnectBlocker>(loop_)),
|
||||||
graceful_shutdown_(false) {
|
graceful_shutdown_(false) {
|
||||||
ev_async_init(&w_, eventcb);
|
ev_async_init(&w_, eventcb);
|
||||||
|
@ -74,9 +77,17 @@ Worker::Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx,
|
||||||
|
|
||||||
if (get_config()->downstream_proto == PROTO_HTTP2) {
|
if (get_config()->downstream_proto == PROTO_HTTP2) {
|
||||||
auto n = get_config()->http2_downstream_connections_per_worker;
|
auto n = get_config()->http2_downstream_connections_per_worker;
|
||||||
for (; n > 0; --n) {
|
size_t group = 0;
|
||||||
http2sessions_.push_back(make_unique<Http2Session>(
|
for (auto &dgrp : dgrps_) {
|
||||||
loop_, cl_ssl_ctx, connect_blocker_.get(), this));
|
auto m = n;
|
||||||
|
if (m == 0) {
|
||||||
|
m = get_config()->downstream_addr_groups[group].addrs.size();
|
||||||
|
}
|
||||||
|
for (size_t idx = 0; idx < m; ++idx) {
|
||||||
|
dgrp.http2sessions.push_back(make_unique<Http2Session>(
|
||||||
|
loop_, cl_ssl_ctx, connect_blocker_.get(), this, group, idx));
|
||||||
|
}
|
||||||
|
++group;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -207,15 +218,17 @@ WorkerStat *Worker::get_worker_stat() { return &worker_stat_; }
|
||||||
|
|
||||||
DownstreamConnectionPool *Worker::get_dconn_pool() { return &dconn_pool_; }
|
DownstreamConnectionPool *Worker::get_dconn_pool() { return &dconn_pool_; }
|
||||||
|
|
||||||
Http2Session *Worker::next_http2_session() {
|
Http2Session *Worker::next_http2_session(size_t group) {
|
||||||
if (http2sessions_.empty()) {
|
auto &dgrp = dgrps_[group];
|
||||||
|
auto &http2sessions = dgrp.http2sessions;
|
||||||
|
if (http2sessions.empty()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto res = http2sessions_[next_http2session_].get();
|
auto res = http2sessions[dgrp.next_http2session].get();
|
||||||
++next_http2session_;
|
++dgrp.next_http2session;
|
||||||
if (next_http2session_ >= http2sessions_.size()) {
|
if (dgrp.next_http2session >= http2sessions.size()) {
|
||||||
next_http2session_ = 0;
|
dgrp.next_http2session = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
|
@ -239,4 +252,9 @@ bool Worker::get_graceful_shutdown() const { return graceful_shutdown_; }
|
||||||
|
|
||||||
MemchunkPool *Worker::get_mcpool() { return &mcpool_; }
|
MemchunkPool *Worker::get_mcpool() { return &mcpool_; }
|
||||||
|
|
||||||
|
DownstreamGroup *Worker::get_dgrp(size_t group) {
|
||||||
|
assert(group < dgrps_.size());
|
||||||
|
return &dgrps_[group];
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace shrpx
|
} // namespace shrpx
|
||||||
|
|
|
@ -54,14 +54,21 @@ namespace ssl {
|
||||||
class CertLookupTree;
|
class CertLookupTree;
|
||||||
} // namespace ssl
|
} // namespace ssl
|
||||||
|
|
||||||
|
struct DownstreamGroup {
|
||||||
|
DownstreamGroup() : next_http2session(0), next(0) {}
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<Http2Session>> http2sessions;
|
||||||
|
// Next index in http2sessions.
|
||||||
|
size_t next_http2session;
|
||||||
|
// Next downstream address index corresponding to
|
||||||
|
// Config::downstream_addr_groups[].
|
||||||
|
size_t next;
|
||||||
|
};
|
||||||
|
|
||||||
struct WorkerStat {
|
struct WorkerStat {
|
||||||
WorkerStat() : num_connections(0), next_downstream(0) {}
|
WorkerStat(size_t num_groups) : num_connections(0) {}
|
||||||
|
|
||||||
size_t num_connections;
|
size_t num_connections;
|
||||||
// Next downstream index in Config::downstream_addrs. For HTTP/2
|
|
||||||
// downstream connections, this is always 0. For HTTP/1, this is
|
|
||||||
// used as load balancing.
|
|
||||||
size_t next_downstream;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum WorkerEventType {
|
enum WorkerEventType {
|
||||||
|
@ -97,7 +104,7 @@ public:
|
||||||
void set_ticket_keys(std::shared_ptr<TicketKeys> ticket_keys);
|
void set_ticket_keys(std::shared_ptr<TicketKeys> ticket_keys);
|
||||||
WorkerStat *get_worker_stat();
|
WorkerStat *get_worker_stat();
|
||||||
DownstreamConnectionPool *get_dconn_pool();
|
DownstreamConnectionPool *get_dconn_pool();
|
||||||
Http2Session *next_http2_session();
|
Http2Session *next_http2_session(size_t group);
|
||||||
ConnectBlocker *get_connect_blocker() const;
|
ConnectBlocker *get_connect_blocker() const;
|
||||||
struct ev_loop *get_loop() const;
|
struct ev_loop *get_loop() const;
|
||||||
SSL_CTX *get_sv_ssl_ctx() const;
|
SSL_CTX *get_sv_ssl_ctx() const;
|
||||||
|
@ -109,9 +116,9 @@ public:
|
||||||
MemchunkPool *get_mcpool();
|
MemchunkPool *get_mcpool();
|
||||||
void schedule_clear_mcpool();
|
void schedule_clear_mcpool();
|
||||||
|
|
||||||
|
DownstreamGroup *get_dgrp(size_t group);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<std::unique_ptr<Http2Session>> http2sessions_;
|
|
||||||
size_t next_http2session_;
|
|
||||||
#ifndef NOTHREADS
|
#ifndef NOTHREADS
|
||||||
std::future<void> fut_;
|
std::future<void> fut_;
|
||||||
#endif // NOTHREADS
|
#endif // NOTHREADS
|
||||||
|
@ -122,6 +129,7 @@ private:
|
||||||
MemchunkPool mcpool_;
|
MemchunkPool mcpool_;
|
||||||
DownstreamConnectionPool dconn_pool_;
|
DownstreamConnectionPool dconn_pool_;
|
||||||
WorkerStat worker_stat_;
|
WorkerStat worker_stat_;
|
||||||
|
std::vector<DownstreamGroup> dgrps_;
|
||||||
struct ev_loop *loop_;
|
struct ev_loop *loop_;
|
||||||
|
|
||||||
// Following fields are shared across threads if
|
// Following fields are shared across threads if
|
||||||
|
|
|
@ -34,7 +34,7 @@ static int count_leap_year(int y) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Based on the algorithm of Python 2.7 calendar.timegm. */
|
/* Based on the algorithm of Python 2.7 calendar.timegm. */
|
||||||
time_t timegm(struct tm *tm) {
|
time_t nghttp2_timegm(struct tm *tm) {
|
||||||
int days;
|
int days;
|
||||||
int num_leap_year;
|
int num_leap_year;
|
||||||
int64_t t;
|
int64_t t;
|
||||||
|
|
|
@ -37,7 +37,7 @@ extern "C" {
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#endif // HAVE_TIME_H
|
#endif // HAVE_TIME_H
|
||||||
|
|
||||||
time_t timegm(struct tm *tm);
|
time_t nghttp2_timegm(struct tm *tm);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
@ -278,7 +278,7 @@ std::string common_log_date(time_t t) {
|
||||||
#ifdef HAVE_STRUCT_TM_TM_GMTOFF
|
#ifdef HAVE_STRUCT_TM_TM_GMTOFF
|
||||||
auto gmtoff = tms.tm_gmtoff;
|
auto gmtoff = tms.tm_gmtoff;
|
||||||
#else // !HAVE_STRUCT_TM_TM_GMTOFF
|
#else // !HAVE_STRUCT_TM_TM_GMTOFF
|
||||||
auto gmtoff = timegm(&tms) - t;
|
auto gmtoff = nghttp2_timegm(&tms) - t;
|
||||||
#endif // !HAVE_STRUCT_TM_TM_GMTOFF
|
#endif // !HAVE_STRUCT_TM_TM_GMTOFF
|
||||||
if (gmtoff >= 0) {
|
if (gmtoff >= 0) {
|
||||||
*p++ = '+';
|
*p++ = '+';
|
||||||
|
@ -326,7 +326,7 @@ std::string iso8601_date(int64_t ms) {
|
||||||
#ifdef HAVE_STRUCT_TM_TM_GMTOFF
|
#ifdef HAVE_STRUCT_TM_TM_GMTOFF
|
||||||
auto gmtoff = tms.tm_gmtoff;
|
auto gmtoff = tms.tm_gmtoff;
|
||||||
#else // !HAVE_STRUCT_TM_TM_GMTOFF
|
#else // !HAVE_STRUCT_TM_TM_GMTOFF
|
||||||
auto gmtoff = timegm(&tms) - sec;
|
auto gmtoff = nghttp2_timegm(&tms) - sec;
|
||||||
#endif // !HAVE_STRUCT_TM_TM_GMTOFF
|
#endif // !HAVE_STRUCT_TM_TM_GMTOFF
|
||||||
if (gmtoff == 0) {
|
if (gmtoff == 0) {
|
||||||
*p++ = 'Z';
|
*p++ = 'Z';
|
||||||
|
@ -354,7 +354,7 @@ time_t parse_http_date(const std::string &s) {
|
||||||
if (r == 0) {
|
if (r == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return timegm(&tm);
|
return nghttp2_timegm(&tm);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
|
@ -386,9 +386,13 @@ bool streq_l(const char (&a)[N], InputIt b, size_t blen) {
|
||||||
|
|
||||||
bool strifind(const char *a, const char *b);
|
bool strifind(const char *a, const char *b);
|
||||||
|
|
||||||
|
template <typename InputIt> void inp_strlower(InputIt first, InputIt last) {
|
||||||
|
std::transform(first, last, first, lowcase);
|
||||||
|
}
|
||||||
|
|
||||||
// Lowercase |s| in place.
|
// Lowercase |s| in place.
|
||||||
inline void inp_strlower(std::string &s) {
|
inline void inp_strlower(std::string &s) {
|
||||||
std::transform(std::begin(s), std::end(s), std::begin(s), lowcase);
|
inp_strlower(std::begin(s), std::end(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns string representation of |n| with 2 fractional digits.
|
// Returns string representation of |n| with 2 fractional digits.
|
||||||
|
|
Loading…
Reference in New Issue