mirror of
https://github.com/nghttp2/nghttp2.git
synced 2025-12-07 02:28:53 +08:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e6381b2b65 | ||
|
|
b6930e3d80 | ||
|
|
19d28365e9 | ||
|
|
a26eb08a89 | ||
|
|
8625a93bb0 | ||
|
|
51e4ca9f7e |
1
AUTHORS
1
AUTHORS
@@ -16,7 +16,6 @@ github issues [2].
|
||||
187j3x1
|
||||
Alek Storm
|
||||
Alex Nalivko
|
||||
Alexandros Konstantinakis-Karmis
|
||||
Alexis La Goutte
|
||||
Amir Pakdel
|
||||
Anders Bakken
|
||||
|
||||
@@ -24,13 +24,13 @@
|
||||
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
# XXX using 1.8.90 instead of 1.9.0-DEV
|
||||
project(nghttp2 VERSION 1.33.0)
|
||||
project(nghttp2 VERSION 1.32.90)
|
||||
|
||||
# See versioning rule:
|
||||
# http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
||||
set(LT_CURRENT 31)
|
||||
set(LT_REVISION 0)
|
||||
set(LT_AGE 17)
|
||||
set(LT_CURRENT 30)
|
||||
set(LT_REVISION 2)
|
||||
set(LT_AGE 16)
|
||||
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
|
||||
include(Version)
|
||||
|
||||
@@ -25,7 +25,7 @@ dnl Do not change user variables!
|
||||
dnl http://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html
|
||||
|
||||
AC_PREREQ(2.61)
|
||||
AC_INIT([nghttp2], [1.33.0], [t-tujikawa@users.sourceforge.net])
|
||||
AC_INIT([nghttp2], [1.33.0-DEV], [t-tujikawa@users.sourceforge.net])
|
||||
AC_CONFIG_AUX_DIR([.])
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
@@ -44,9 +44,9 @@ m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
|
||||
dnl See versioning rule:
|
||||
dnl http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
||||
AC_SUBST(LT_CURRENT, 31)
|
||||
AC_SUBST(LT_REVISION, 0)
|
||||
AC_SUBST(LT_AGE, 17)
|
||||
AC_SUBST(LT_CURRENT, 30)
|
||||
AC_SUBST(LT_REVISION, 2)
|
||||
AC_SUBST(LT_AGE, 16)
|
||||
|
||||
major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"`
|
||||
minor=`echo $PACKAGE_VERSION |cut -d. -f2 | sed -e "s/[^0-9]//g"`
|
||||
|
||||
@@ -8,7 +8,7 @@ _nghttpx()
|
||||
_get_comp_words_by_ref cur prev
|
||||
case $cur in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W '--worker-read-rate --include --frontend-http2-dump-response-header --tls-ticket-key-file --verify-client-cacert --max-response-header-fields --backend-http2-window-size --frontend-keep-alive-timeout --backend-request-buffer --max-request-header-fields --backend-connect-timeout --tls-max-proto-version --conf --dns-lookup-timeout --backend-http2-max-concurrent-streams --worker-write-burst --npn-list --dns-max-try --fetch-ocsp-response-file --no-via --tls-session-cache-memcached-cert-file --no-http2-cipher-black-list --mruby-file --add-forwarded --client-no-http2-cipher-black-list --stream-read-timeout --client-ciphers --ocsp-update-interval --forwarded-for --accesslog-syslog --dns-cache-timeout --frontend-http2-read-timeout --listener-disable-timeout --ciphers --client-psk-secrets --strip-incoming-x-forwarded-for --no-server-rewrite --private-key-passwd-file --backend-keep-alive-timeout --backend-http-proxy-uri --frontend-max-requests --rlimit-nofile --no-strip-incoming-x-forwarded-proto --tls-ticket-key-memcached-cert-file --no-verify-ocsp --forwarded-by --tls-session-cache-memcached-private-key-file --error-page --ocsp-startup --backend-write-timeout --tls-dyn-rec-warmup-threshold --tls-ticket-key-memcached-max-retry --frontend-http2-window-size --http2-no-cookie-crumbling --worker-read-burst --dh-param-file --accesslog-format --errorlog-syslog --redirect-https-port --request-header-field-buffer --api-max-request-body --frontend-http2-decoder-dynamic-table-size --errorlog-file --frontend-http2-max-concurrent-streams --psk-secrets --frontend-write-timeout --tls-ticket-key-cipher --read-burst --no-add-x-forwarded-proto --backend --server-name --insecure --backend-max-backoff --log-level --host-rewrite --tls-ticket-key-memcached-interval --frontend-http2-setting-timeout --frontend-http2-connection-window-size --worker-frontend-connections --syslog-facility --fastopen --no-location-rewrite --single-thread --tls-session-cache-memcached --no-ocsp --backend-response-buffer --tls-min-proto-version --workers --add-x-forwarded-for --no-server-push --worker-write-rate --add-request-header --backend-http2-settings-timeout --subcert --ignore-per-pattern-mruby-error --ecdh-curves --no-kqueue --help --frontend-frame-debug --tls-sct-dir --pid-file --frontend-http2-dump-request-header --daemon --write-rate --altsvc --backend-http2-decoder-dynamic-table-size --user --verify-client-tolerate-expired --frontend-read-timeout --tls-ticket-key-memcached-max-fail --backlog --write-burst --backend-connections-per-host --response-header-field-buffer --tls-ticket-key-memcached-address-family --padding --tls-session-cache-memcached-address-family --stream-write-timeout --cacert --tls-ticket-key-memcached-private-key-file --accesslog-write-early --backend-address-family --backend-http2-connection-window-size --version --add-response-header --backend-read-timeout --frontend-http2-optimize-window-size --frontend --accesslog-file --http2-proxy --backend-http2-encoder-dynamic-table-size --client-private-key-file --single-process --client-cert-file --tls-ticket-key-memcached --tls-dyn-rec-idle-timeout --frontend-http2-optimize-write-buffer-size --verify-client --frontend-http2-encoder-dynamic-table-size --read-rate --backend-connections-per-frontend --strip-incoming-forwarded ' -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W '--worker-read-rate --include --frontend-http2-dump-response-header --tls-ticket-key-file --verify-client-cacert --max-response-header-fields --backend-http2-window-size --frontend-keep-alive-timeout --backend-request-buffer --max-request-header-fields --backend-connect-timeout --tls-max-proto-version --conf --dns-lookup-timeout --backend-http2-max-concurrent-streams --worker-write-burst --npn-list --dns-max-try --fetch-ocsp-response-file --no-via --tls-session-cache-memcached-cert-file --no-http2-cipher-black-list --mruby-file --add-forwarded --client-no-http2-cipher-black-list --stream-read-timeout --client-ciphers --ocsp-update-interval --forwarded-for --accesslog-syslog --dns-cache-timeout --frontend-http2-read-timeout --listener-disable-timeout --ciphers --client-psk-secrets --strip-incoming-x-forwarded-for --no-server-rewrite --private-key-passwd-file --backend-keep-alive-timeout --backend-http-proxy-uri --frontend-max-requests --rlimit-nofile --no-strip-incoming-x-forwarded-proto --tls-ticket-key-memcached-cert-file --no-verify-ocsp --forwarded-by --tls-session-cache-memcached-private-key-file --error-page --ocsp-startup --backend-write-timeout --tls-dyn-rec-warmup-threshold --tls-ticket-key-memcached-max-retry --frontend-http2-window-size --http2-no-cookie-crumbling --worker-read-burst --dh-param-file --accesslog-format --errorlog-syslog --redirect-https-port --request-header-field-buffer --api-max-request-body --frontend-http2-decoder-dynamic-table-size --errorlog-file --frontend-http2-max-concurrent-streams --psk-secrets --frontend-write-timeout --tls-ticket-key-cipher --read-burst --no-add-x-forwarded-proto --backend --server-name --insecure --backend-max-backoff --log-level --host-rewrite --tls-ticket-key-memcached-interval --frontend-http2-setting-timeout --frontend-http2-connection-window-size --worker-frontend-connections --syslog-facility --fastopen --no-location-rewrite --single-thread --tls-session-cache-memcached --no-ocsp --backend-response-buffer --tls-min-proto-version --workers --add-x-forwarded-for --no-server-push --worker-write-rate --add-request-header --backend-http2-settings-timeout --subcert --ecdh-curves --no-kqueue --help --frontend-frame-debug --tls-sct-dir --pid-file --frontend-http2-dump-request-header --daemon --write-rate --altsvc --backend-http2-decoder-dynamic-table-size --user --verify-client-tolerate-expired --frontend-read-timeout --tls-ticket-key-memcached-max-fail --backlog --write-burst --backend-connections-per-host --response-header-field-buffer --tls-ticket-key-memcached-address-family --padding --tls-session-cache-memcached-address-family --stream-write-timeout --cacert --tls-ticket-key-memcached-private-key-file --accesslog-write-early --backend-address-family --backend-http2-connection-window-size --version --add-response-header --backend-read-timeout --frontend-http2-optimize-window-size --frontend --accesslog-file --http2-proxy --backend-http2-encoder-dynamic-table-size --client-private-key-file --single-process --client-cert-file --tls-ticket-key-memcached --tls-dyn-rec-idle-timeout --frontend-http2-optimize-write-buffer-size --verify-client --frontend-http2-encoder-dynamic-table-size --read-rate --backend-connections-per-frontend --strip-incoming-forwarded ' -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
_filedir
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "H2LOAD" "1" "Sep 02, 2018" "1.33.0" "nghttp2"
|
||||
.TH "H2LOAD" "1" "May 08, 2018" "1.32.0" "nghttp2"
|
||||
.SH NAME
|
||||
h2load \- HTTP/2 benchmarking tool
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTP" "1" "Sep 02, 2018" "1.33.0" "nghttp2"
|
||||
.TH "NGHTTP" "1" "May 08, 2018" "1.32.0" "nghttp2"
|
||||
.SH NAME
|
||||
nghttp \- HTTP/2 client
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTPD" "1" "Sep 02, 2018" "1.33.0" "nghttp2"
|
||||
.TH "NGHTTPD" "1" "May 08, 2018" "1.32.0" "nghttp2"
|
||||
.SH NAME
|
||||
nghttpd \- HTTP/2 server
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTPX" "1" "Sep 02, 2018" "1.33.0" "nghttp2"
|
||||
.TH "NGHTTPX" "1" "May 08, 2018" "1.32.0" "nghttp2"
|
||||
.SH NAME
|
||||
nghttpx \- HTTP/2 proxy
|
||||
.
|
||||
@@ -137,13 +137,12 @@ Several parameters <PARAM> are accepted after <PATTERN>.
|
||||
The parameters are delimited by ";". The available
|
||||
parameters are: "proto=<PROTO>", "tls",
|
||||
"sni=<SNI_HOST>", "fall=<N>", "rise=<N>",
|
||||
"affinity=<METHOD>", "dns", "redirect\-if\-not\-tls",
|
||||
"upgrade\-scheme", and "mruby=<PATH>". The parameter
|
||||
consists of keyword, and optionally followed by "=" and
|
||||
value. For example, the parameter "proto=h2" consists
|
||||
of the keyword "proto" and value "h2". The parameter
|
||||
"tls" consists of the keyword "tls" without value. Each
|
||||
parameter is described as follows.
|
||||
"affinity=<METHOD>", "dns", and "redirect\-if\-not\-tls".
|
||||
The parameter consists of keyword, and optionally
|
||||
followed by "=" and value. For example, the parameter
|
||||
"proto=h2" consists of the keyword "proto" and value
|
||||
"h2". The parameter "tls" consists of the keyword "tls"
|
||||
without value. Each parameter is described as follows.
|
||||
.sp
|
||||
The backend application protocol can be specified using
|
||||
optional "proto" parameter, and in the form of
|
||||
@@ -236,11 +235,6 @@ particular backend. This is a workaround for a backend
|
||||
server which requires "https" :scheme pseudo header
|
||||
field on TLS encrypted connection.
|
||||
.sp
|
||||
"mruby=<PATH>" parameter specifies a path to mruby
|
||||
script file which is invoked when this pattern is
|
||||
matched. All backends which share the same pattern must
|
||||
have the same mruby path.
|
||||
.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.
|
||||
@@ -1583,13 +1577,6 @@ signal handling feature is disabled.
|
||||
.B \-\-mruby\-file=<PATH>
|
||||
Set mruby script file
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-ignore\-per\-pattern\-mruby\-error
|
||||
Ignore mruby compile error for per\-pattern mruby script
|
||||
file. If error occurred, it is treated as if no mruby
|
||||
file were specified for the pattern.
|
||||
.UNINDENT
|
||||
.SS Misc
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -1947,28 +1934,9 @@ server. These hooks allows users to modify header fields, or common
|
||||
HTTP variables, like authority or request path, and even return custom
|
||||
response without forwarding request to backend servers.
|
||||
.sp
|
||||
There are 2 levels of mruby script invocations: global and
|
||||
per\-pattern. The global mruby script is set by \fI\%\-\-mruby\-file\fP
|
||||
option and is called for all requests. The per\-pattern mruby script
|
||||
is set by "mruby" parameter in \fI\%\-b\fP option. It is invoked for
|
||||
a request which matches the particular pattern. The order of hook
|
||||
invocation is: global request phase hook, per\-pattern request phase
|
||||
hook, per\-pattern response phase hook, and finally global response
|
||||
phase hook. If a hook returns a response, any later hooks are not
|
||||
invoked. The global request hook is invoked before the pattern
|
||||
matching is made and changing request path may affect the pattern
|
||||
matching.
|
||||
.sp
|
||||
Please note that request and response hooks of per\-pattern mruby
|
||||
script for a single request might not come from the same script. This
|
||||
might happen after a request hook is executed, backend failed for some
|
||||
reason, and at the same time, backend configuration is replaced by API
|
||||
request, and then the request uses new configuration on retry. The
|
||||
response hook from new configuration, if it is specified, will be
|
||||
invoked.
|
||||
.sp
|
||||
The all mruby script will be evaluated once per thread on startup, and
|
||||
it must instantiate object and evaluate it as the return value (e.g.,
|
||||
To specify mruby script file, use \fI\%\-\-mruby\-file\fP option. The
|
||||
script will be evaluated once per thread on startup, and it must
|
||||
instantiate object and evaluate it as the return value (e.g.,
|
||||
\fBApp.new\fP). This object is called app object. If app object
|
||||
defines \fBon_req\fP method, it is called with \fI\%Nghttpx::Env\fP
|
||||
object on request hook. Similarly, if app object defines \fBon_resp\fP
|
||||
@@ -2256,10 +2224,10 @@ to the backend, and response phase hook for this request will
|
||||
not be invoked. When this method is called in response phase
|
||||
hook, response from backend server is canceled and discarded.
|
||||
The status code and response header fields should be set
|
||||
before using this method. To set status code, use
|
||||
before using this method. To set status code, use :rb:meth To
|
||||
set response header fields, use
|
||||
\fI\%Nghttpx::Response#status\fP\&. If status code is not
|
||||
set, 200 is used. To set response header fields,
|
||||
\fI\%Nghttpx::Response#add_header\fP and
|
||||
set, 200 is used. \fI\%Nghttpx::Response#add_header\fP and
|
||||
\fI\%Nghttpx::Response#set_header\fP\&. When this method is
|
||||
invoked in response phase hook, the response headers are
|
||||
filled with the ones received from backend server. To send
|
||||
|
||||
@@ -121,13 +121,12 @@ Connections
|
||||
The parameters are delimited by ";". The available
|
||||
parameters are: "proto=<PROTO>", "tls",
|
||||
"sni=<SNI_HOST>", "fall=<N>", "rise=<N>",
|
||||
"affinity=<METHOD>", "dns", "redirect-if-not-tls",
|
||||
"upgrade-scheme", and "mruby=<PATH>". The parameter
|
||||
consists of keyword, and optionally followed by "=" and
|
||||
value. For example, the parameter "proto=h2" consists
|
||||
of the keyword "proto" and value "h2". The parameter
|
||||
"tls" consists of the keyword "tls" without value. Each
|
||||
parameter is described as follows.
|
||||
"affinity=<METHOD>", "dns", and "redirect-if-not-tls".
|
||||
The parameter consists of keyword, and optionally
|
||||
followed by "=" and value. For example, the parameter
|
||||
"proto=h2" consists of the keyword "proto" and value
|
||||
"h2". The parameter "tls" consists of the keyword "tls"
|
||||
without value. Each parameter is described as follows.
|
||||
|
||||
The backend application protocol can be specified using
|
||||
optional "proto" parameter, and in the form of
|
||||
@@ -220,11 +219,6 @@ Connections
|
||||
server which requires "https" :scheme pseudo header
|
||||
field on TLS encrypted connection.
|
||||
|
||||
"mruby=<PATH>" parameter specifies a path to mruby
|
||||
script file which is invoked when this pattern is
|
||||
matched. All backends which share the same pattern must
|
||||
have the same mruby path.
|
||||
|
||||
Since ";" and ":" are used as delimiter, <PATTERN> must
|
||||
not contain these characters. Since ";" has special
|
||||
meaning in shell, the option value must be quoted.
|
||||
@@ -1445,12 +1439,6 @@ Scripting
|
||||
|
||||
Set mruby script file
|
||||
|
||||
.. option:: --ignore-per-pattern-mruby-error
|
||||
|
||||
Ignore mruby compile error for per-pattern mruby script
|
||||
file. If error occurred, it is treated as if no mruby
|
||||
file were specified for the pattern.
|
||||
|
||||
|
||||
Misc
|
||||
~~~~
|
||||
@@ -1789,28 +1777,9 @@ server. These hooks allows users to modify header fields, or common
|
||||
HTTP variables, like authority or request path, and even return custom
|
||||
response without forwarding request to backend servers.
|
||||
|
||||
There are 2 levels of mruby script invocations: global and
|
||||
per-pattern. The global mruby script is set by :option:`--mruby-file`
|
||||
option and is called for all requests. The per-pattern mruby script
|
||||
is set by "mruby" parameter in :option:`-b` option. It is invoked for
|
||||
a request which matches the particular pattern. The order of hook
|
||||
invocation is: global request phase hook, per-pattern request phase
|
||||
hook, per-pattern response phase hook, and finally global response
|
||||
phase hook. If a hook returns a response, any later hooks are not
|
||||
invoked. The global request hook is invoked before the pattern
|
||||
matching is made and changing request path may affect the pattern
|
||||
matching.
|
||||
|
||||
Please note that request and response hooks of per-pattern mruby
|
||||
script for a single request might not come from the same script. This
|
||||
might happen after a request hook is executed, backend failed for some
|
||||
reason, and at the same time, backend configuration is replaced by API
|
||||
request, and then the request uses new configuration on retry. The
|
||||
response hook from new configuration, if it is specified, will be
|
||||
invoked.
|
||||
|
||||
The all mruby script will be evaluated once per thread on startup, and
|
||||
it must instantiate object and evaluate it as the return value (e.g.,
|
||||
To specify mruby script file, use :option:`--mruby-file` option. The
|
||||
script will be evaluated once per thread on startup, and it must
|
||||
instantiate object and evaluate it as the return value (e.g.,
|
||||
``App.new``). This object is called app object. If app object
|
||||
defines ``on_req`` method, it is called with :rb:class:`Nghttpx::Env`
|
||||
object on request hook. Similarly, if app object defines ``on_resp``
|
||||
@@ -2057,10 +2026,10 @@ respectively.
|
||||
not be invoked. When this method is called in response phase
|
||||
hook, response from backend server is canceled and discarded.
|
||||
The status code and response header fields should be set
|
||||
before using this method. To set status code, use
|
||||
before using this method. To set status code, use :rb:meth To
|
||||
set response header fields, use
|
||||
:rb:attr:`Nghttpx::Response#status`. If status code is not
|
||||
set, 200 is used. To set response header fields,
|
||||
:rb:meth:`Nghttpx::Response#add_header` and
|
||||
set, 200 is used. :rb:meth:`Nghttpx::Response#add_header` and
|
||||
:rb:meth:`Nghttpx::Response#set_header`. When this method is
|
||||
invoked in response phase hook, the response headers are
|
||||
filled with the ones received from backend server. To send
|
||||
|
||||
@@ -299,28 +299,9 @@ server. These hooks allows users to modify header fields, or common
|
||||
HTTP variables, like authority or request path, and even return custom
|
||||
response without forwarding request to backend servers.
|
||||
|
||||
There are 2 levels of mruby script invocations: global and
|
||||
per-pattern. The global mruby script is set by :option:`--mruby-file`
|
||||
option and is called for all requests. The per-pattern mruby script
|
||||
is set by "mruby" parameter in :option:`-b` option. It is invoked for
|
||||
a request which matches the particular pattern. The order of hook
|
||||
invocation is: global request phase hook, per-pattern request phase
|
||||
hook, per-pattern response phase hook, and finally global response
|
||||
phase hook. If a hook returns a response, any later hooks are not
|
||||
invoked. The global request hook is invoked before the pattern
|
||||
matching is made and changing request path may affect the pattern
|
||||
matching.
|
||||
|
||||
Please note that request and response hooks of per-pattern mruby
|
||||
script for a single request might not come from the same script. This
|
||||
might happen after a request hook is executed, backend failed for some
|
||||
reason, and at the same time, backend configuration is replaced by API
|
||||
request, and then the request uses new configuration on retry. The
|
||||
response hook from new configuration, if it is specified, will be
|
||||
invoked.
|
||||
|
||||
The all mruby script will be evaluated once per thread on startup, and
|
||||
it must instantiate object and evaluate it as the return value (e.g.,
|
||||
To specify mruby script file, use :option:`--mruby-file` option. The
|
||||
script will be evaluated once per thread on startup, and it must
|
||||
instantiate object and evaluate it as the return value (e.g.,
|
||||
``App.new``). This object is called app object. If app object
|
||||
defines ``on_req`` method, it is called with :rb:class:`Nghttpx::Env`
|
||||
object on request hook. Similarly, if app object defines ``on_resp``
|
||||
@@ -567,10 +548,10 @@ respectively.
|
||||
not be invoked. When this method is called in response phase
|
||||
hook, response from backend server is canceled and discarded.
|
||||
The status code and response header fields should be set
|
||||
before using this method. To set status code, use
|
||||
before using this method. To set status code, use :rb:meth To
|
||||
set response header fields, use
|
||||
:rb:attr:`Nghttpx::Response#status`. If status code is not
|
||||
set, 200 is used. To set response header fields,
|
||||
:rb:meth:`Nghttpx::Response#add_header` and
|
||||
set, 200 is used. :rb:meth:`Nghttpx::Response#add_header` and
|
||||
:rb:meth:`Nghttpx::Response#set_header`. When this method is
|
||||
invoked in response phase hook, the response headers are
|
||||
filled with the ones received from backend server. To send
|
||||
|
||||
@@ -9,6 +9,7 @@ HEADERS = [
|
||||
':scheme',
|
||||
':status',
|
||||
':host', # for spdy
|
||||
':protocol',
|
||||
'expect',
|
||||
'host',
|
||||
'if-modified-since',
|
||||
@@ -31,6 +32,7 @@ HEADERS = [
|
||||
"user-agent",
|
||||
"date",
|
||||
"content-type",
|
||||
"sec-websocket-accept",
|
||||
# disallowed h1 headers
|
||||
'connection',
|
||||
'keep-alive',
|
||||
|
||||
@@ -67,6 +67,7 @@ HEADERS = [
|
||||
('keep-alive',None),
|
||||
('proxy-connection', None),
|
||||
('upgrade', None),
|
||||
(':protocol', None),
|
||||
]
|
||||
|
||||
def to_enum_hd(k):
|
||||
|
||||
@@ -169,7 +169,6 @@ OPTIONS = [
|
||||
"ocsp-startup",
|
||||
"no-verify-ocsp",
|
||||
"verify-client-tolerate-expired",
|
||||
"ignore-per-pattern-mruby-error",
|
||||
]
|
||||
|
||||
LOGVARS = [
|
||||
|
||||
@@ -680,7 +680,12 @@ typedef enum {
|
||||
/**
|
||||
* SETTINGS_MAX_HEADER_LIST_SIZE
|
||||
*/
|
||||
NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE = 0x06
|
||||
NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE = 0x06,
|
||||
/**
|
||||
* SETTINGS_ENABLE_CONNECT_PROTOCOL
|
||||
* (https://tools.ietf.org/html/draft-ietf-httpbis-h2-websockets-00)
|
||||
*/
|
||||
NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL = 0x08
|
||||
} nghttp2_settings_id;
|
||||
/* Note: If we add SETTINGS, update the capacity of
|
||||
NGHTTP2_INBOUND_NUM_IV as well */
|
||||
@@ -3802,13 +3807,10 @@ nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec);
|
||||
* .. warning::
|
||||
*
|
||||
* This function returns assigned stream ID if it succeeds. But
|
||||
* that stream is not created yet. The application must not submit
|
||||
* that stream is not opened yet. The application must not submit
|
||||
* frame to that stream ID before
|
||||
* :type:`nghttp2_before_frame_send_callback` is called for this
|
||||
* frame. This means `nghttp2_session_get_stream_user_data()` does
|
||||
* not work before the callback. But
|
||||
* `nghttp2_session_set_stream_user_data()` handles this situation
|
||||
* specially, and it can set data to a stream during this period.
|
||||
* frame.
|
||||
*
|
||||
*/
|
||||
NGHTTP2_EXTERN int32_t nghttp2_submit_request(
|
||||
@@ -4613,8 +4615,7 @@ typedef struct {
|
||||
* :enum:`NGHTTP2_FLAG_NONE`.
|
||||
*
|
||||
* The |ov| points to the array of origins. The |nov| specifies the
|
||||
* number of origins included in |ov|. This function creates copies
|
||||
* of all elements in |ov|.
|
||||
* number of origins included in |ov|.
|
||||
*
|
||||
* The ORIGIN frame is only usable by a server. If this function is
|
||||
* invoked with client side session, this function returns
|
||||
|
||||
@@ -1050,6 +1050,11 @@ int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv) {
|
||||
break;
|
||||
case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
|
||||
break;
|
||||
case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
|
||||
if (iv[i].value != 0 && iv[i].value != 1) {
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
|
||||
@@ -271,6 +271,15 @@ static int32_t lookup_token(const uint8_t *name, size_t namelen) {
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 9:
|
||||
switch (name[8]) {
|
||||
case 'l':
|
||||
if (memeq(":protoco", name, 8)) {
|
||||
return NGHTTP2_TOKEN__PROTOCOL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 10:
|
||||
switch (name[9]) {
|
||||
case 'e':
|
||||
|
||||
@@ -111,6 +111,7 @@ typedef enum {
|
||||
NGHTTP2_TOKEN_KEEP_ALIVE,
|
||||
NGHTTP2_TOKEN_PROXY_CONNECTION,
|
||||
NGHTTP2_TOKEN_UPGRADE,
|
||||
NGHTTP2_TOKEN__PROTOCOL,
|
||||
} nghttp2_token;
|
||||
|
||||
struct nghttp2_hd_entry;
|
||||
|
||||
@@ -113,7 +113,7 @@ static int check_path(nghttp2_stream *stream) {
|
||||
}
|
||||
|
||||
static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
|
||||
int trailer) {
|
||||
int trailer, int connect_protocol) {
|
||||
if (nv->name->base[0] == ':') {
|
||||
if (trailer ||
|
||||
(stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
|
||||
@@ -146,10 +146,6 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
|
||||
if (stream->http_flags &
|
||||
(NGHTTP2_HTTP_FLAG__PATH | NGHTTP2_HTTP_FLAG__SCHEME)) {
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'S':
|
||||
@@ -162,9 +158,6 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
|
||||
}
|
||||
break;
|
||||
case NGHTTP2_TOKEN__PATH:
|
||||
if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) {
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) {
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
@@ -175,9 +168,6 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
|
||||
}
|
||||
break;
|
||||
case NGHTTP2_TOKEN__SCHEME:
|
||||
if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) {
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) {
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
@@ -186,6 +176,15 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
|
||||
stream->http_flags |= NGHTTP2_HTTP_FLAG_SCHEME_HTTP;
|
||||
}
|
||||
break;
|
||||
case NGHTTP2_TOKEN__PROTOCOL:
|
||||
if (!connect_protocol) {
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
|
||||
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PROTOCOL)) {
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
break;
|
||||
case NGHTTP2_TOKEN_HOST:
|
||||
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG_HOST)) {
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
@@ -458,16 +457,22 @@ int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
|
||||
}
|
||||
|
||||
if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
|
||||
return http_request_on_header(stream, nv, trailer);
|
||||
return http_request_on_header(
|
||||
stream, nv, trailer,
|
||||
session->server && session->local_settings.enable_connect_protocol);
|
||||
}
|
||||
|
||||
return http_response_on_header(stream, nv, trailer);
|
||||
}
|
||||
|
||||
int nghttp2_http_on_request_headers(nghttp2_stream *stream,
|
||||
nghttp2_frame *frame) {
|
||||
if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) {
|
||||
if ((stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0) {
|
||||
nghttp2_frame *frame,
|
||||
int connect_protocol) {
|
||||
if (!connect_protocol &&
|
||||
(stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT)) {
|
||||
if ((stream->http_flags &
|
||||
(NGHTTP2_HTTP_FLAG__SCHEME | NGHTTP2_HTTP_FLAG__PATH)) ||
|
||||
(stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0) {
|
||||
return -1;
|
||||
}
|
||||
stream->content_length = -1;
|
||||
@@ -478,6 +483,11 @@ int nghttp2_http_on_request_headers(nghttp2_stream *stream,
|
||||
(NGHTTP2_HTTP_FLAG__AUTHORITY | NGHTTP2_HTTP_FLAG_HOST)) == 0) {
|
||||
return -1;
|
||||
}
|
||||
if ((stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) &&
|
||||
(!connect_protocol ||
|
||||
(stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) == 0)) {
|
||||
return -1;
|
||||
}
|
||||
if (!check_path(stream)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -52,11 +52,13 @@ int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
|
||||
int trailer);
|
||||
|
||||
/*
|
||||
* This function is called when request header is received. This
|
||||
* This function is called when request header is received.
|
||||
* |connect_protocol| is nonzero if SETTINGS_ENABLE_CONNECT_PROTOCOL
|
||||
* is enabled by the local endpoint (which must be server). This
|
||||
* function performs validation and returns 0 if it succeeds, or -1.
|
||||
*/
|
||||
int nghttp2_http_on_request_headers(nghttp2_stream *stream,
|
||||
nghttp2_frame *frame);
|
||||
nghttp2_frame *frame, int connect_protocol);
|
||||
|
||||
/*
|
||||
* This function is called when response header is received. This
|
||||
|
||||
@@ -3746,13 +3746,17 @@ static int session_after_header_block_received(nghttp2_session *session) {
|
||||
subject_stream = nghttp2_session_get_stream(
|
||||
session, frame->push_promise.promised_stream_id);
|
||||
if (subject_stream) {
|
||||
rv = nghttp2_http_on_request_headers(subject_stream, frame);
|
||||
rv = nghttp2_http_on_request_headers(
|
||||
subject_stream, frame,
|
||||
session->server && session->local_settings.enable_connect_protocol);
|
||||
}
|
||||
} else {
|
||||
assert(frame->hd.type == NGHTTP2_HEADERS);
|
||||
switch (frame->headers.cat) {
|
||||
case NGHTTP2_HCAT_REQUEST:
|
||||
rv = nghttp2_http_on_request_headers(stream, frame);
|
||||
rv = nghttp2_http_on_request_headers(
|
||||
stream, frame,
|
||||
session->server && session->local_settings.enable_connect_protocol);
|
||||
break;
|
||||
case NGHTTP2_HCAT_RESPONSE:
|
||||
case NGHTTP2_HCAT_PUSH_RESPONSE:
|
||||
@@ -4361,6 +4365,9 @@ int nghttp2_session_update_local_settings(nghttp2_session *session,
|
||||
case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
|
||||
session->local_settings.max_header_list_size = iv[i].value;
|
||||
break;
|
||||
case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
|
||||
session->local_settings.enable_connect_protocol = iv[i].value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4499,6 +4506,26 @@ int nghttp2_session_on_settings_received(nghttp2_session *session,
|
||||
|
||||
session->remote_settings.max_header_list_size = entry->value;
|
||||
|
||||
break;
|
||||
case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
|
||||
|
||||
if (entry->value != 0 && entry->value != 1) {
|
||||
return session_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_ERR_PROTO,
|
||||
"SETTINGS: invalid SETTINGS_ENABLE_CONNECT_PROTOCOL");
|
||||
}
|
||||
|
||||
if (!session->server &&
|
||||
session->remote_settings.enable_connect_protocol &&
|
||||
entry->value == 0) {
|
||||
return session_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_ERR_PROTO,
|
||||
"SETTINGS: server attempted to disable "
|
||||
"SETTINGS_ENABLE_CONNECT_PROTOCOL");
|
||||
}
|
||||
|
||||
session->remote_settings.enable_connect_protocol = entry->value;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -5250,6 +5277,7 @@ static void inbound_frame_set_settings_entry(nghttp2_inbound_frame *iframe) {
|
||||
case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
|
||||
case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
|
||||
case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
|
||||
case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
|
||||
break;
|
||||
default:
|
||||
DEBUGF("recv: unknown settings id=0x%02x\n", iv.settings_id);
|
||||
@@ -7208,42 +7236,12 @@ int nghttp2_session_set_stream_user_data(nghttp2_session *session,
|
||||
int32_t stream_id,
|
||||
void *stream_user_data) {
|
||||
nghttp2_stream *stream;
|
||||
nghttp2_frame *frame;
|
||||
nghttp2_outbound_item *item;
|
||||
|
||||
stream = nghttp2_session_get_stream(session, stream_id);
|
||||
if (stream) {
|
||||
stream->stream_user_data = stream_user_data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (session->server || !nghttp2_session_is_my_stream_id(session, stream_id) ||
|
||||
!nghttp2_outbound_queue_top(&session->ob_syn)) {
|
||||
if (!stream) {
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
frame = &nghttp2_outbound_queue_top(&session->ob_syn)->frame;
|
||||
assert(frame->hd.type == NGHTTP2_HEADERS);
|
||||
|
||||
if (frame->hd.stream_id > stream_id ||
|
||||
(uint32_t)stream_id >= session->next_stream_id) {
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
for (item = session->ob_syn.head; item; item = item->qnext) {
|
||||
if (item->frame.hd.stream_id < stream_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item->frame.hd.stream_id > stream_id) {
|
||||
break;
|
||||
}
|
||||
|
||||
item->aux_data.headers.stream_user_data = stream_user_data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
stream->stream_user_data = stream_user_data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nghttp2_session_resume_data(nghttp2_session *session, int32_t stream_id) {
|
||||
@@ -7360,6 +7358,8 @@ uint32_t nghttp2_session_get_remote_settings(nghttp2_session *session,
|
||||
return session->remote_settings.max_frame_size;
|
||||
case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
|
||||
return session->remote_settings.max_header_list_size;
|
||||
case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
|
||||
return session->remote_settings.enable_connect_protocol;
|
||||
}
|
||||
|
||||
assert(0);
|
||||
@@ -7381,6 +7381,8 @@ uint32_t nghttp2_session_get_local_settings(nghttp2_session *session,
|
||||
return session->local_settings.max_frame_size;
|
||||
case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
|
||||
return session->local_settings.max_header_list_size;
|
||||
case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
|
||||
return session->local_settings.enable_connect_protocol;
|
||||
}
|
||||
|
||||
assert(0);
|
||||
|
||||
@@ -164,6 +164,7 @@ typedef struct {
|
||||
uint32_t initial_window_size;
|
||||
uint32_t max_frame_size;
|
||||
uint32_t max_header_list_size;
|
||||
uint32_t enable_connect_protocol;
|
||||
} nghttp2_settings_storage;
|
||||
|
||||
typedef enum {
|
||||
|
||||
@@ -130,7 +130,8 @@ typedef enum {
|
||||
/* "http" or "https" scheme */
|
||||
NGHTTP2_HTTP_FLAG_SCHEME_HTTP = 1 << 13,
|
||||
/* set if final response is expected */
|
||||
NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE = 1 << 14
|
||||
NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE = 1 << 14,
|
||||
NGHTTP2_HTTP_FLAG__PROTOCOL = 1 << 15,
|
||||
} nghttp2_http_flag;
|
||||
|
||||
struct nghttp2_stream {
|
||||
|
||||
@@ -44,15 +44,6 @@ session::session(boost::asio::io_service &io_service, const std::string &host,
|
||||
impl_->start_resolve(host, service);
|
||||
}
|
||||
|
||||
session::session(boost::asio::io_service &io_service,
|
||||
const boost::asio::ip::tcp::endpoint &local_endpoint,
|
||||
const std::string &host, const std::string &service)
|
||||
: impl_(std::make_shared<session_tcp_impl>(
|
||||
io_service, local_endpoint, host, service,
|
||||
boost::posix_time::seconds(60))) {
|
||||
impl_->start_resolve(host, service);
|
||||
}
|
||||
|
||||
session::session(boost::asio::io_service &io_service, const std::string &host,
|
||||
const std::string &service,
|
||||
const boost::posix_time::time_duration &connect_timeout)
|
||||
@@ -61,15 +52,6 @@ session::session(boost::asio::io_service &io_service, const std::string &host,
|
||||
impl_->start_resolve(host, service);
|
||||
}
|
||||
|
||||
session::session(boost::asio::io_service &io_service,
|
||||
const boost::asio::ip::tcp::endpoint &local_endpoint,
|
||||
const std::string &host, const std::string &service,
|
||||
const boost::posix_time::time_duration &connect_timeout)
|
||||
: impl_(std::make_shared<session_tcp_impl>(io_service, local_endpoint, host,
|
||||
service, connect_timeout)) {
|
||||
impl_->start_resolve(host, service);
|
||||
}
|
||||
|
||||
session::session(boost::asio::io_service &io_service,
|
||||
boost::asio::ssl::context &tls_ctx, const std::string &host,
|
||||
const std::string &service)
|
||||
|
||||
@@ -34,35 +34,24 @@ session_tcp_impl::session_tcp_impl(
|
||||
const boost::posix_time::time_duration &connect_timeout)
|
||||
: session_impl(io_service, connect_timeout), socket_(io_service) {}
|
||||
|
||||
session_tcp_impl::session_tcp_impl(
|
||||
boost::asio::io_service &io_service,
|
||||
const boost::asio::ip::tcp::endpoint &local_endpoint,
|
||||
const std::string &host, const std::string &service,
|
||||
const boost::posix_time::time_duration &connect_timeout)
|
||||
: session_impl(io_service, connect_timeout), socket_(io_service) {
|
||||
socket_.open(local_endpoint.protocol());
|
||||
boost::asio::socket_base::reuse_address option(true);
|
||||
socket_.set_option(option);
|
||||
socket_.bind(local_endpoint);
|
||||
}
|
||||
|
||||
session_tcp_impl::~session_tcp_impl() {}
|
||||
|
||||
void session_tcp_impl::start_connect(tcp::resolver::iterator endpoint_it) {
|
||||
auto self = shared_from_this();
|
||||
socket_.async_connect(
|
||||
*endpoint_it, [self, endpoint_it](const boost::system::error_code &ec) {
|
||||
if (self->stopped()) {
|
||||
return;
|
||||
}
|
||||
boost::asio::async_connect(socket_, endpoint_it,
|
||||
[self](const boost::system::error_code &ec,
|
||||
tcp::resolver::iterator endpoint_it) {
|
||||
if (self->stopped()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ec) {
|
||||
self->not_connected(ec);
|
||||
return;
|
||||
}
|
||||
if (ec) {
|
||||
self->not_connected(ec);
|
||||
return;
|
||||
}
|
||||
|
||||
self->connected(endpoint_it);
|
||||
});
|
||||
self->connected(endpoint_it);
|
||||
});
|
||||
}
|
||||
|
||||
tcp::socket &session_tcp_impl::socket() { return socket_; }
|
||||
|
||||
@@ -40,10 +40,6 @@ public:
|
||||
session_tcp_impl(boost::asio::io_service &io_service, const std::string &host,
|
||||
const std::string &service,
|
||||
const boost::posix_time::time_duration &connect_timeout);
|
||||
session_tcp_impl(boost::asio::io_service &io_service,
|
||||
const boost::asio::ip::tcp::endpoint &local_endpoint,
|
||||
const std::string &host, const std::string &service,
|
||||
const boost::posix_time::time_duration &connect_timeout);
|
||||
virtual ~session_tcp_impl();
|
||||
|
||||
virtual void start_connect(tcp::resolver::iterator endpoint_it);
|
||||
|
||||
22
src/http2.cc
22
src/http2.cc
@@ -438,6 +438,11 @@ void copy_headers_to_nva_internal(std::vector<nghttp2_nv> &nva,
|
||||
kv = &(*it_via);
|
||||
it_via = it;
|
||||
break;
|
||||
case HD_SEC_WEBSOCKET_ACCEPT:
|
||||
if (flags & HDOP_STRIP_SEC_WEBSOCKET_ACCEPT) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
nva.push_back(
|
||||
make_nv_internal(kv->name, kv->value, kv->no_index, nv_flags));
|
||||
@@ -821,6 +826,11 @@ int lookup_token(const uint8_t *name, size_t namelen) {
|
||||
return HD_FORWARDED;
|
||||
}
|
||||
break;
|
||||
case 'l':
|
||||
if (util::streq_l(":protoco", name, 8)) {
|
||||
return HD__PROTOCOL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 10:
|
||||
@@ -926,6 +936,15 @@ int lookup_token(const uint8_t *name, size_t namelen) {
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 20:
|
||||
switch (name[19]) {
|
||||
case 't':
|
||||
if (util::streq_l("sec-websocket-accep", name, 19)) {
|
||||
return HD_SEC_WEBSOCKET_ACCEPT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@@ -1313,7 +1332,8 @@ std::string path_join(const StringRef &base_path, const StringRef &base_query,
|
||||
}
|
||||
|
||||
bool expect_response_body(int status_code) {
|
||||
return status_code / 100 != 1 && status_code != 304 && status_code != 204;
|
||||
return status_code == 101 ||
|
||||
(status_code / 100 != 1 && status_code != 304 && status_code != 204);
|
||||
}
|
||||
|
||||
bool expect_response_body(const std::string &method, int status_code) {
|
||||
|
||||
@@ -206,6 +206,8 @@ enum HeaderBuildOp {
|
||||
// Strip above all header fields.
|
||||
HDOP_STRIP_ALL = HDOP_STRIP_FORWARDED | HDOP_STRIP_X_FORWARDED_FOR |
|
||||
HDOP_STRIP_X_FORWARDED_PROTO | HDOP_STRIP_VIA,
|
||||
// Sec-WebSocket-Accept header field must be stripped.
|
||||
HDOP_STRIP_SEC_WEBSOCKET_ACCEPT = 1 << 4,
|
||||
};
|
||||
|
||||
// Appends headers in |headers| to |nv|. |headers| must be indexed
|
||||
@@ -293,6 +295,7 @@ enum {
|
||||
HD__HOST,
|
||||
HD__METHOD,
|
||||
HD__PATH,
|
||||
HD__PROTOCOL,
|
||||
HD__SCHEME,
|
||||
HD__STATUS,
|
||||
HD_ACCEPT_ENCODING,
|
||||
@@ -313,6 +316,7 @@ enum {
|
||||
HD_LINK,
|
||||
HD_LOCATION,
|
||||
HD_PROXY_CONNECTION,
|
||||
HD_SEC_WEBSOCKET_ACCEPT,
|
||||
HD_SERVER,
|
||||
HD_TE,
|
||||
HD_TRAILER,
|
||||
|
||||
@@ -150,11 +150,6 @@ public:
|
||||
session(boost::asio::io_service &io_service, const std::string &host,
|
||||
const std::string &service);
|
||||
|
||||
// Same as previous but with pegged local endpoint
|
||||
session(boost::asio::io_service &io_service,
|
||||
const boost::asio::ip::tcp::endpoint &local_endpoint,
|
||||
const std::string &host, const std::string &service);
|
||||
|
||||
// Starts HTTP/2 session by connecting to |host| and |service|
|
||||
// (e.g., "80") using clear text TCP connection with given connect
|
||||
// timeout.
|
||||
@@ -162,12 +157,6 @@ public:
|
||||
const std::string &service,
|
||||
const boost::posix_time::time_duration &connect_timeout);
|
||||
|
||||
// Same as previous but with pegged local endpoint
|
||||
session(boost::asio::io_service &io_service,
|
||||
const boost::asio::ip::tcp::endpoint &local_endpoint,
|
||||
const std::string &host, const std::string &service,
|
||||
const boost::posix_time::time_duration &connect_timeout);
|
||||
|
||||
// Starts HTTP/2 session by connecting to |host| and |service|
|
||||
// (e.g., "443") using encrypted SSL/TLS connection with connect
|
||||
// timeout 60 seconds.
|
||||
|
||||
@@ -251,29 +251,6 @@ template <typename Memchunk> struct Memchunks {
|
||||
|
||||
return count - left;
|
||||
}
|
||||
size_t remove(Memchunks &dest) {
|
||||
assert(pool == dest.pool);
|
||||
|
||||
if (head == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto n = len;
|
||||
|
||||
if (dest.tail == nullptr) {
|
||||
dest.head = head;
|
||||
} else {
|
||||
dest.tail->next = head;
|
||||
}
|
||||
|
||||
dest.tail = tail;
|
||||
dest.len += len;
|
||||
|
||||
head = tail = nullptr;
|
||||
len = 0;
|
||||
|
||||
return n;
|
||||
}
|
||||
size_t drain(size_t count) {
|
||||
auto ndata = count;
|
||||
auto m = head;
|
||||
|
||||
29
src/shrpx.cc
29
src/shrpx.cc
@@ -1729,13 +1729,12 @@ Connections:
|
||||
The parameters are delimited by ";". The available
|
||||
parameters are: "proto=<PROTO>", "tls",
|
||||
"sni=<SNI_HOST>", "fall=<N>", "rise=<N>",
|
||||
"affinity=<METHOD>", "dns", "redirect-if-not-tls",
|
||||
"upgrade-scheme", and "mruby=<PATH>". The parameter
|
||||
consists of keyword, and optionally followed by "=" and
|
||||
value. For example, the parameter "proto=h2" consists
|
||||
of the keyword "proto" and value "h2". The parameter
|
||||
"tls" consists of the keyword "tls" without value. Each
|
||||
parameter is described as follows.
|
||||
"affinity=<METHOD>", "dns", and "redirect-if-not-tls".
|
||||
The parameter consists of keyword, and optionally
|
||||
followed by "=" and value. For example, the parameter
|
||||
"proto=h2" consists of the keyword "proto" and value
|
||||
"h2". The parameter "tls" consists of the keyword "tls"
|
||||
without value. Each parameter is described as follows.
|
||||
|
||||
The backend application protocol can be specified using
|
||||
optional "proto" parameter, and in the form of
|
||||
@@ -1828,11 +1827,6 @@ Connections:
|
||||
server which requires "https" :scheme pseudo header
|
||||
field on TLS encrypted connection.
|
||||
|
||||
"mruby=<PATH>" parameter specifies a path to mruby
|
||||
script file which is invoked when this pattern is
|
||||
matched. All backends which share the same pattern must
|
||||
have the same mruby path.
|
||||
|
||||
Since ";" and ":" are used as delimiter, <PATTERN> must
|
||||
not contain these characters. Since ";" has special
|
||||
meaning in shell, the option value must be quoted.
|
||||
@@ -2755,10 +2749,6 @@ Process:
|
||||
Scripting:
|
||||
--mruby-file=<PATH>
|
||||
Set mruby script file
|
||||
--ignore-per-pattern-mruby-error
|
||||
Ignore mruby compile error for per-pattern mruby script
|
||||
file. If error occurred, it is treated as if no mruby
|
||||
file were specified for the pattern.
|
||||
|
||||
Misc:
|
||||
--conf=<PATH>
|
||||
@@ -3434,8 +3424,6 @@ int main(int argc, char **argv) {
|
||||
{SHRPX_OPT_SINGLE_PROCESS.c_str(), no_argument, &flag, 159},
|
||||
{SHRPX_OPT_VERIFY_CLIENT_TOLERATE_EXPIRED.c_str(), no_argument, &flag,
|
||||
160},
|
||||
{SHRPX_OPT_IGNORE_PER_PATTERN_MRUBY_ERROR.c_str(), no_argument, &flag,
|
||||
161},
|
||||
{nullptr, 0, nullptr, 0}};
|
||||
|
||||
int option_index = 0;
|
||||
@@ -4202,11 +4190,6 @@ int main(int argc, char **argv) {
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_VERIFY_CLIENT_TOLERATE_EXPIRED,
|
||||
StringRef::from_lit("yes"));
|
||||
break;
|
||||
case 161:
|
||||
// --ignore-per-pattern-mruby-error
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_IGNORE_PER_PATTERN_MRUBY_ERROR,
|
||||
StringRef::from_lit("yes"));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -265,11 +265,6 @@ int APIDownstreamConnection::push_request_headers() {
|
||||
}
|
||||
}
|
||||
|
||||
downstream_->set_request_header_sent(true);
|
||||
auto src = downstream_->get_blocked_request_buf();
|
||||
auto dest = downstream_->get_request_buf();
|
||||
src->remove(*dest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -938,8 +938,7 @@ uint32_t ClientHandler::get_affinity_cookie(Downstream *downstream,
|
||||
}
|
||||
|
||||
std::unique_ptr<DownstreamConnection>
|
||||
ClientHandler::get_downstream_connection(int &err, Downstream *downstream,
|
||||
shrpx_proto pref_proto) {
|
||||
ClientHandler::get_downstream_connection(int &err, Downstream *downstream) {
|
||||
size_t group_idx;
|
||||
auto &downstreamconf = *worker_->get_downstream_config();
|
||||
auto &routerconf = downstreamconf.router;
|
||||
@@ -979,7 +978,7 @@ ClientHandler::get_downstream_connection(int &err, Downstream *downstream,
|
||||
StringRef path;
|
||||
// CONNECT method does not have path. But we requires path in
|
||||
// host-path mapping. As workaround, we assume that path is "/".
|
||||
if (req.method != HTTP_CONNECT) {
|
||||
if (!req.regular_connect_method()) {
|
||||
path = req.path;
|
||||
}
|
||||
|
||||
@@ -1042,8 +1041,7 @@ ClientHandler::get_downstream_connection(int &err, Downstream *downstream,
|
||||
i = 0;
|
||||
}
|
||||
addr = &shared_addr->addrs[shared_addr->affinity_hash[i].idx];
|
||||
if (addr->connect_blocker->blocked() ||
|
||||
(pref_proto != PROTO_NONE && pref_proto != addr->proto)) {
|
||||
if (addr->connect_blocker->blocked()) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
@@ -1083,15 +1081,7 @@ ClientHandler::get_downstream_connection(int &err, Downstream *downstream,
|
||||
|
||||
auto proto = PROTO_NONE;
|
||||
|
||||
if (pref_proto == PROTO_HTTP1) {
|
||||
if (http1_weight > 0) {
|
||||
proto = PROTO_HTTP1;
|
||||
}
|
||||
} else if (pref_proto == PROTO_HTTP2) {
|
||||
if (http2_weight > 0) {
|
||||
proto = PROTO_HTTP2;
|
||||
}
|
||||
} else if (http1_weight > 0 && http2_weight > 0) {
|
||||
if (http1_weight > 0 && http2_weight > 0) {
|
||||
// We only advance cycle if both weight has nonzero to keep its
|
||||
// distance under WEIGHT_MAX.
|
||||
if (pri_less(shared_addr->http1_pri, shared_addr->http2_pri)) {
|
||||
|
||||
@@ -102,11 +102,9 @@ public:
|
||||
// Returns DownstreamConnection object based on request path. This
|
||||
// function returns non-null DownstreamConnection, and assigns 0 to
|
||||
// |err| if it succeeds, or returns nullptr, and assigns negative
|
||||
// error code to |err|. If |pref_proto| is not PROTO_NONE, choose
|
||||
// backend whose protocol is |pref_proto|.
|
||||
// error code to |err|.
|
||||
std::unique_ptr<DownstreamConnection>
|
||||
get_downstream_connection(int &err, Downstream *downstream,
|
||||
shrpx_proto pref_proto = PROTO_NONE);
|
||||
get_downstream_connection(int &err, Downstream *downstream);
|
||||
MemchunkPool *get_mcpool();
|
||||
SSL *get_ssl() const;
|
||||
// Call this function when HTTP/2 connection header is received at
|
||||
|
||||
@@ -55,9 +55,6 @@
|
||||
#include "shrpx_log.h"
|
||||
#include "shrpx_tls.h"
|
||||
#include "shrpx_http.h"
|
||||
#ifdef HAVE_MRUBY
|
||||
# include "shrpx_mruby.h"
|
||||
#endif // HAVE_MRUBY
|
||||
#include "util.h"
|
||||
#include "base64.h"
|
||||
#include "ssl_compat.h"
|
||||
@@ -810,7 +807,6 @@ int parse_upstream_params(UpstreamParams &out, const StringRef &src_params) {
|
||||
|
||||
struct DownstreamParams {
|
||||
StringRef sni;
|
||||
StringRef mruby;
|
||||
AffinityConfig affinity;
|
||||
size_t fall;
|
||||
size_t rise;
|
||||
@@ -925,9 +921,6 @@ int parse_downstream_params(DownstreamParams &out,
|
||||
out.redirect_if_not_tls = true;
|
||||
} else if (util::strieq_l("upgrade-scheme", param)) {
|
||||
out.upgrade_scheme = true;
|
||||
} else if (util::istarts_with_l(param, "mruby=")) {
|
||||
auto valstr = StringRef{first + str_size("mruby="), end};
|
||||
out.mruby = valstr;
|
||||
} else if (!param.empty()) {
|
||||
LOG(ERROR) << "backend: " << param << ": unknown keyword";
|
||||
return -1;
|
||||
@@ -1052,18 +1045,6 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
|
||||
if (params.redirect_if_not_tls) {
|
||||
g.redirect_if_not_tls = true;
|
||||
}
|
||||
// All backends in the same group must have the same mruby path.
|
||||
// If some backend does not specify mruby file, and there is at
|
||||
// least one backend with mruby file, it is used for all backend
|
||||
// in the group.
|
||||
if (g.mruby_file.empty()) {
|
||||
g.mruby_file = make_string_ref(downstreamconf.balloc, params.mruby);
|
||||
} else if (g.mruby_file != params.mruby) {
|
||||
LOG(ERROR) << "backend: mruby: multiple different mruby file found in "
|
||||
"a single group";
|
||||
return -1;
|
||||
}
|
||||
|
||||
g.addrs.push_back(addr);
|
||||
continue;
|
||||
}
|
||||
@@ -1084,7 +1065,6 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
|
||||
g.affinity.cookie.secure = params.affinity.cookie.secure;
|
||||
}
|
||||
g.redirect_if_not_tls = params.redirect_if_not_tls;
|
||||
g.mruby_file = make_string_ref(downstreamconf.balloc, params.mruby);
|
||||
|
||||
if (pattern[0] == '*') {
|
||||
// wildcard pattern
|
||||
@@ -2199,9 +2179,6 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
if (util::strieq_l("ignore-per-pattern-mruby-erro", name, 29)) {
|
||||
return SHRPX_OPTID_IGNORE_PER_PATTERN_MRUBY_ERROR;
|
||||
}
|
||||
if (util::strieq_l("strip-incoming-x-forwarded-fo", name, 29)) {
|
||||
return SHRPX_OPTID_STRIP_INCOMING_X_FORWARDED_FOR;
|
||||
}
|
||||
@@ -3586,10 +3563,6 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
||||
case SHRPX_OPTID_VERIFY_CLIENT_TOLERATE_EXPIRED:
|
||||
config->tls.client_verify.tolerate_expired = util::strieq_l("yes", optarg);
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_IGNORE_PER_PATTERN_MRUBY_ERROR:
|
||||
config->ignore_per_pattern_mruby_error = util::strieq_l("yes", optarg);
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_CONF:
|
||||
LOG(WARN) << "conf: ignored";
|
||||
@@ -3881,33 +3854,8 @@ int configure_downstream_group(Config *config, bool http2_proxy,
|
||||
<< (addr.tls ? ", tls" : "");
|
||||
}
|
||||
}
|
||||
#ifdef HAVE_MRUBY
|
||||
// Try compile mruby script and catch compile error early.
|
||||
if (!g.mruby_file.empty()) {
|
||||
if (mruby::create_mruby_context(g.mruby_file) == nullptr) {
|
||||
LOG(config->ignore_per_pattern_mruby_error ? ERROR : FATAL)
|
||||
<< "backend: Could not compile mruby flie for pattern "
|
||||
<< g.pattern;
|
||||
if (!config->ignore_per_pattern_mruby_error) {
|
||||
return -1;
|
||||
}
|
||||
g.mruby_file = StringRef{};
|
||||
}
|
||||
}
|
||||
#endif // HAVE_MRUBY
|
||||
}
|
||||
|
||||
#ifdef HAVE_MRUBY
|
||||
// Try compile mruby script (--mruby-file) here to catch compile
|
||||
// error early.
|
||||
if (!config->mruby_file.empty()) {
|
||||
if (mruby::create_mruby_context(config->mruby_file) == nullptr) {
|
||||
LOG(FATAL) << "mruby-file: Could not compile mruby file";
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#endif // HAVE_MRUBY
|
||||
|
||||
if (catch_all_group == -1) {
|
||||
LOG(FATAL) << "backend: No catch-all backend address is configured";
|
||||
return -1;
|
||||
|
||||
@@ -345,8 +345,6 @@ constexpr auto SHRPX_OPT_OCSP_STARTUP = StringRef::from_lit("ocsp-startup");
|
||||
constexpr auto SHRPX_OPT_NO_VERIFY_OCSP = StringRef::from_lit("no-verify-ocsp");
|
||||
constexpr auto SHRPX_OPT_VERIFY_CLIENT_TOLERATE_EXPIRED =
|
||||
StringRef::from_lit("verify-client-tolerate-expired");
|
||||
constexpr auto SHRPX_OPT_IGNORE_PER_PATTERN_MRUBY_ERROR =
|
||||
StringRef::from_lit("ignore-per-pattern-mruby-error");
|
||||
|
||||
constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8;
|
||||
|
||||
@@ -485,7 +483,6 @@ struct DownstreamAddrGroupConfig {
|
||||
: pattern(pattern), affinity{AFFINITY_NONE}, redirect_if_not_tls(false) {}
|
||||
|
||||
StringRef pattern;
|
||||
StringRef mruby_file;
|
||||
std::vector<DownstreamAddrConfig> addrs;
|
||||
// Bunch of session affinity hash. Only used if affinity ==
|
||||
// AFFINITY_IP.
|
||||
@@ -918,7 +915,6 @@ struct Config {
|
||||
http2_proxy{false},
|
||||
single_process{false},
|
||||
single_thread{false},
|
||||
ignore_per_pattern_mruby_error{false},
|
||||
ev_loop_flags{0} {}
|
||||
~Config();
|
||||
|
||||
@@ -963,8 +959,6 @@ struct Config {
|
||||
// handling is omitted.
|
||||
bool single_process;
|
||||
bool single_thread;
|
||||
// Ignore mruby compile error for per-pattern mruby script.
|
||||
bool ignore_per_pattern_mruby_error;
|
||||
// flags passed to ev_default_loop() and ev_loop_new()
|
||||
int ev_loop_flags;
|
||||
};
|
||||
@@ -1069,7 +1063,6 @@ enum {
|
||||
SHRPX_OPTID_HTTP2_MAX_CONCURRENT_STREAMS,
|
||||
SHRPX_OPTID_HTTP2_NO_COOKIE_CRUMBLING,
|
||||
SHRPX_OPTID_HTTP2_PROXY,
|
||||
SHRPX_OPTID_IGNORE_PER_PATTERN_MRUBY_ERROR,
|
||||
SHRPX_OPTID_INCLUDE,
|
||||
SHRPX_OPTID_INSECURE,
|
||||
SHRPX_OPTID_LISTENER_DISABLE_TIMEOUT,
|
||||
|
||||
@@ -115,9 +115,6 @@ ConnectionHandler::ConnectionHandler(struct ev_loop *loop, std::mt19937 &gen)
|
||||
: gen_(gen),
|
||||
single_worker_(nullptr),
|
||||
loop_(loop),
|
||||
#ifdef HAVE_NEVERBLEED
|
||||
nb_(nullptr),
|
||||
#endif // HAVE_NEVERBLEED
|
||||
tls_ticket_key_memcached_get_retry_count_(0),
|
||||
tls_ticket_key_memcached_fail_count_(0),
|
||||
worker_round_robin_cnt_(get_config()->api.enabled ? 1 : 0),
|
||||
@@ -208,12 +205,12 @@ int ConnectionHandler::create_single_worker() {
|
||||
all_ssl_ctx_, indexed_ssl_ctx_, cert_tree_.get()
|
||||
#ifdef HAVE_NEVERBLEED
|
||||
,
|
||||
nb_
|
||||
nb_.get()
|
||||
#endif // HAVE_NEVERBLEED
|
||||
);
|
||||
auto cl_ssl_ctx = tls::setup_downstream_client_ssl_context(
|
||||
#ifdef HAVE_NEVERBLEED
|
||||
nb_
|
||||
nb_.get()
|
||||
#endif // HAVE_NEVERBLEED
|
||||
);
|
||||
|
||||
@@ -230,7 +227,7 @@ int ConnectionHandler::create_single_worker() {
|
||||
if (memcachedconf.tls) {
|
||||
session_cache_ssl_ctx = tls::create_ssl_client_context(
|
||||
#ifdef HAVE_NEVERBLEED
|
||||
nb_,
|
||||
nb_.get(),
|
||||
#endif // HAVE_NEVERBLEED
|
||||
tlsconf.cacert, memcachedconf.cert_file,
|
||||
memcachedconf.private_key_file, nullptr);
|
||||
@@ -259,12 +256,12 @@ int ConnectionHandler::create_worker_thread(size_t num) {
|
||||
all_ssl_ctx_, indexed_ssl_ctx_, cert_tree_.get()
|
||||
# ifdef HAVE_NEVERBLEED
|
||||
,
|
||||
nb_
|
||||
nb_.get()
|
||||
# endif // HAVE_NEVERBLEED
|
||||
);
|
||||
auto cl_ssl_ctx = tls::setup_downstream_client_ssl_context(
|
||||
# ifdef HAVE_NEVERBLEED
|
||||
nb_
|
||||
nb_.get()
|
||||
# endif // HAVE_NEVERBLEED
|
||||
);
|
||||
|
||||
@@ -288,7 +285,7 @@ int ConnectionHandler::create_worker_thread(size_t num) {
|
||||
if (memcachedconf.tls) {
|
||||
session_cache_ssl_ctx = tls::create_ssl_client_context(
|
||||
# ifdef HAVE_NEVERBLEED
|
||||
nb_,
|
||||
nb_.get(),
|
||||
# endif // HAVE_NEVERBLEED
|
||||
tlsconf.cacert, memcachedconf.cert_file,
|
||||
memcachedconf.private_key_file, nullptr);
|
||||
@@ -805,7 +802,7 @@ SSL_CTX *ConnectionHandler::create_tls_ticket_key_memcached_ssl_ctx() {
|
||||
|
||||
auto ssl_ctx = tls::create_ssl_client_context(
|
||||
#ifdef HAVE_NEVERBLEED
|
||||
nb_,
|
||||
nb_.get(),
|
||||
#endif // HAVE_NEVERBLEED
|
||||
tlsconf.cacert, memcachedconf.cert_file, memcachedconf.private_key_file,
|
||||
nullptr);
|
||||
@@ -816,7 +813,12 @@ SSL_CTX *ConnectionHandler::create_tls_ticket_key_memcached_ssl_ctx() {
|
||||
}
|
||||
|
||||
#ifdef HAVE_NEVERBLEED
|
||||
void ConnectionHandler::set_neverbleed(neverbleed_t *nb) { nb_ = nb; }
|
||||
void ConnectionHandler::set_neverbleed(std::unique_ptr<neverbleed_t> nb) {
|
||||
nb_ = std::move(nb);
|
||||
}
|
||||
|
||||
neverbleed_t *ConnectionHandler::get_neverbleed() const { return nb_.get(); }
|
||||
|
||||
#endif // HAVE_NEVERBLEED
|
||||
|
||||
void ConnectionHandler::handle_serial_event() {
|
||||
|
||||
@@ -160,7 +160,8 @@ public:
|
||||
const std::vector<SSL_CTX *> &get_indexed_ssl_ctx(size_t idx) const;
|
||||
|
||||
#ifdef HAVE_NEVERBLEED
|
||||
void set_neverbleed(neverbleed_t *nb);
|
||||
void set_neverbleed(std::unique_ptr<neverbleed_t> nb);
|
||||
neverbleed_t *get_neverbleed() const;
|
||||
#endif // HAVE_NEVERBLEED
|
||||
|
||||
// Send SerialEvent SEV_REPLACE_DOWNSTREAM to this object.
|
||||
@@ -209,7 +210,7 @@ private:
|
||||
struct ev_loop *loop_;
|
||||
std::vector<std::unique_ptr<AcceptHandler>> acceptors_;
|
||||
#ifdef HAVE_NEVERBLEED
|
||||
neverbleed_t *nb_;
|
||||
std::unique_ptr<neverbleed_t> nb_;
|
||||
#endif // HAVE_NEVERBLEED
|
||||
ev_timer disable_acceptor_timer_;
|
||||
ev_timer ocsp_timer_;
|
||||
|
||||
@@ -121,7 +121,6 @@ Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool,
|
||||
req_(balloc_),
|
||||
resp_(balloc_),
|
||||
request_start_time_(std::chrono::high_resolution_clock::now()),
|
||||
blocked_request_buf_(mcpool),
|
||||
request_buf_(mcpool),
|
||||
response_buf_(mcpool),
|
||||
upstream_(upstream),
|
||||
@@ -143,8 +142,7 @@ Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool,
|
||||
request_pending_(false),
|
||||
request_header_sent_(false),
|
||||
accesslog_written_(false),
|
||||
new_affinity_cookie_(false),
|
||||
blocked_request_data_eof_(false) {
|
||||
new_affinity_cookie_(false) {
|
||||
|
||||
auto &timeoutconf = get_config()->http2.timeout;
|
||||
|
||||
@@ -188,16 +186,6 @@ Downstream::~Downstream() {
|
||||
#endif // HAVE_MRUBY
|
||||
}
|
||||
|
||||
#ifdef HAVE_MRUBY
|
||||
if (dconn_) {
|
||||
const auto &group = dconn_->get_downstream_addr_group();
|
||||
if (group) {
|
||||
const auto &mruby_ctx = group->mruby_ctx;
|
||||
mruby_ctx->delete_downstream(this);
|
||||
}
|
||||
}
|
||||
#endif // HAVE_MRUBY
|
||||
|
||||
// DownstreamConnection may refer to this object. Delete it now
|
||||
// explicitly.
|
||||
dconn_.reset();
|
||||
@@ -227,14 +215,6 @@ void Downstream::detach_downstream_connection() {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef HAVE_MRUBY
|
||||
const auto &group = dconn_->get_downstream_addr_group();
|
||||
if (group) {
|
||||
const auto &mruby_ctx = group->mruby_ctx;
|
||||
mruby_ctx->delete_downstream(this);
|
||||
}
|
||||
#endif // HAVE_MRUBY
|
||||
|
||||
dconn_->detach_downstream(this);
|
||||
|
||||
auto handler = dconn_->get_client_handler();
|
||||
@@ -248,18 +228,6 @@ DownstreamConnection *Downstream::get_downstream_connection() {
|
||||
}
|
||||
|
||||
std::unique_ptr<DownstreamConnection> Downstream::pop_downstream_connection() {
|
||||
#ifdef HAVE_MRUBY
|
||||
if (!dconn_) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto &group = dconn_->get_downstream_addr_group();
|
||||
if (group) {
|
||||
const auto &mruby_ctx = group->mruby_ctx;
|
||||
mruby_ctx->delete_downstream(this);
|
||||
}
|
||||
#endif // HAVE_MRUBY
|
||||
|
||||
return std::unique_ptr<DownstreamConnection>(dconn_.release());
|
||||
}
|
||||
|
||||
@@ -616,8 +584,7 @@ bool Downstream::request_buf_full() {
|
||||
|
||||
if (dconn_) {
|
||||
auto &downstreamconf = *worker->get_downstream_config();
|
||||
return blocked_request_buf_.rleft() + request_buf_.rleft() >=
|
||||
downstreamconf.request_buffer_size;
|
||||
return request_buf_.rleft() >= downstreamconf.request_buffer_size;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -638,12 +605,6 @@ int Downstream::push_request_headers() {
|
||||
int Downstream::push_upload_data_chunk(const uint8_t *data, size_t datalen) {
|
||||
req_.recv_body_length += datalen;
|
||||
|
||||
if (!request_header_sent_) {
|
||||
blocked_request_buf_.append(data, datalen);
|
||||
req_.unconsumed_body_length += datalen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Assumes that request headers have already been pushed to output
|
||||
// buffer using push_request_headers().
|
||||
if (!dconn_) {
|
||||
@@ -660,10 +621,6 @@ int Downstream::push_upload_data_chunk(const uint8_t *data, size_t datalen) {
|
||||
}
|
||||
|
||||
int Downstream::end_upload_data() {
|
||||
if (!request_header_sent_) {
|
||||
blocked_request_data_eof_ = true;
|
||||
return 0;
|
||||
}
|
||||
if (!dconn_) {
|
||||
DLOG(INFO, this) << "dconn_ is NULL";
|
||||
return -1;
|
||||
@@ -765,7 +722,12 @@ bool Downstream::validate_response_recv_body_length() const {
|
||||
|
||||
void Downstream::check_upgrade_fulfilled() {
|
||||
if (req_.method == HTTP_CONNECT) {
|
||||
upgraded_ = 200 <= resp_.http_status && resp_.http_status < 300;
|
||||
if (req_.connect_proto) {
|
||||
// TODO For websocket, check Sec-WebSocket-Accept header field.
|
||||
upgraded_ = resp_.http_status == 101;
|
||||
} else {
|
||||
upgraded_ = 200 <= resp_.http_status && resp_.http_status < 300;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -1095,12 +1057,4 @@ uint32_t Downstream::get_affinity_cookie_to_send() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
DefaultMemchunks *Downstream::get_blocked_request_buf() {
|
||||
return &blocked_request_buf_;
|
||||
}
|
||||
|
||||
bool Downstream::get_blocked_request_data_eof() const {
|
||||
return blocked_request_data_eof_;
|
||||
}
|
||||
|
||||
} // namespace shrpx
|
||||
|
||||
@@ -134,6 +134,12 @@ private:
|
||||
bool trailer_key_prev_;
|
||||
};
|
||||
|
||||
// Protocols allowed in HTTP/2 :protocol header field.
|
||||
enum shrpx_connect_proto {
|
||||
CONNECT_PROTO_NONE,
|
||||
CONNECT_PROTO_WEBSOCKET,
|
||||
};
|
||||
|
||||
struct Request {
|
||||
Request(BlockAllocator &balloc)
|
||||
: fs(balloc, 16),
|
||||
@@ -142,6 +148,7 @@ struct Request {
|
||||
method(-1),
|
||||
http_major(1),
|
||||
http_minor(1),
|
||||
connect_proto(CONNECT_PROTO_NONE),
|
||||
upgrade_request(false),
|
||||
http2_upgrade_seen(false),
|
||||
connection_close(false),
|
||||
@@ -153,6 +160,14 @@ struct Request {
|
||||
unconsumed_body_length -= len;
|
||||
}
|
||||
|
||||
bool regular_connect_method() const {
|
||||
return method == HTTP_CONNECT && !connect_proto;
|
||||
}
|
||||
|
||||
bool extended_connect_method() const {
|
||||
return method == HTTP_CONNECT && connect_proto;
|
||||
}
|
||||
|
||||
FieldStore fs;
|
||||
// Timestamp when all request header fields are received.
|
||||
std::shared_ptr<Timestamp> tstamp;
|
||||
@@ -176,6 +191,9 @@ struct Request {
|
||||
int method;
|
||||
// HTTP major and minor version
|
||||
int http_major, http_minor;
|
||||
// connect_protocol specified in HTTP/2 :protocol pseudo header
|
||||
// field which enables extended CONNECT method.
|
||||
int connect_proto;
|
||||
// Returns true if the request is HTTP upgrade (HTTP Upgrade or
|
||||
// CONNECT method). Upgrade to HTTP/2 is excluded. For HTTP/2
|
||||
// Upgrade, check get_http2_upgrade_request().
|
||||
@@ -356,9 +374,6 @@ public:
|
||||
// get_request_pending() returns false.
|
||||
bool request_submission_ready() const;
|
||||
|
||||
DefaultMemchunks *get_blocked_request_buf();
|
||||
bool get_blocked_request_data_eof() const;
|
||||
|
||||
// downstream response API
|
||||
const Response &response() const { return resp_; }
|
||||
Response &response() { return resp_; }
|
||||
@@ -494,9 +509,6 @@ private:
|
||||
// or not.
|
||||
StringRef request_downstream_host_;
|
||||
|
||||
// Data arrived in frontend before sending header fields to backend
|
||||
// are stored in this buffer.
|
||||
DefaultMemchunks blocked_request_buf_;
|
||||
DefaultMemchunks request_buf_;
|
||||
DefaultMemchunks response_buf_;
|
||||
|
||||
@@ -553,9 +565,6 @@ private:
|
||||
bool accesslog_written_;
|
||||
// true if affinity cookie is generated for this request.
|
||||
bool new_affinity_cookie_;
|
||||
// true if eof is received from client before sending header fields
|
||||
// to backend.
|
||||
bool blocked_request_data_eof_;
|
||||
};
|
||||
|
||||
} // namespace shrpx
|
||||
|
||||
@@ -54,14 +54,7 @@ void HealthMonitorDownstreamConnection::detach_downstream(
|
||||
downstream_ = nullptr;
|
||||
}
|
||||
|
||||
int HealthMonitorDownstreamConnection::push_request_headers() {
|
||||
downstream_->set_request_header_sent(true);
|
||||
auto src = downstream_->get_blocked_request_buf();
|
||||
auto dest = downstream_->get_request_buf();
|
||||
src->remove(*dest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
int HealthMonitorDownstreamConnection::push_request_headers() { return 0; }
|
||||
|
||||
int HealthMonitorDownstreamConnection::push_upload_data_chunk(
|
||||
const uint8_t *data, size_t datalen) {
|
||||
|
||||
@@ -250,7 +250,7 @@ int Http2DownstreamConnection::push_request_headers() {
|
||||
auto &http2conf = config->http2;
|
||||
|
||||
auto no_host_rewrite = httpconf.no_host_rewrite || config->http2_proxy ||
|
||||
req.method == HTTP_CONNECT;
|
||||
req.regular_connect_method();
|
||||
|
||||
// http2session_ has already in CONNECTED state, so we can get
|
||||
// addr_idx here.
|
||||
@@ -288,7 +288,7 @@ int Http2DownstreamConnection::push_request_headers() {
|
||||
nva.push_back(
|
||||
http2::make_nv_ls_nocopy(":method", http2::to_method_string(req.method)));
|
||||
|
||||
if (req.method != HTTP_CONNECT) {
|
||||
if (!req.regular_connect_method()) {
|
||||
assert(!req.scheme.empty());
|
||||
|
||||
auto addr = http2session_->get_addr();
|
||||
@@ -339,7 +339,7 @@ int Http2DownstreamConnection::push_request_headers() {
|
||||
if (fwdconf.params) {
|
||||
auto params = fwdconf.params;
|
||||
|
||||
if (config->http2_proxy || req.method == HTTP_CONNECT) {
|
||||
if (config->http2_proxy || req.regular_connect_method()) {
|
||||
params &= ~FORWARDED_PROTO;
|
||||
}
|
||||
|
||||
@@ -380,7 +380,7 @@ int Http2DownstreamConnection::push_request_headers() {
|
||||
nva.push_back(http2::make_nv_ls_nocopy("x-forwarded-for", xff->value));
|
||||
}
|
||||
|
||||
if (!config->http2_proxy && req.method != HTTP_CONNECT) {
|
||||
if (!config->http2_proxy && !req.regular_connect_method()) {
|
||||
auto xfp = xfpconf.strip_incoming
|
||||
? nullptr
|
||||
: req.fs.header(http2::HD_X_FORWARDED_PROTO);
|
||||
|
||||
@@ -1468,15 +1468,6 @@ int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||
if (frame->hd.type == NGHTTP2_HEADERS &&
|
||||
frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
|
||||
downstream->set_request_header_sent(true);
|
||||
auto src = downstream->get_blocked_request_buf();
|
||||
if (src->rleft()) {
|
||||
auto dest = downstream->get_request_buf();
|
||||
src->remove(*dest);
|
||||
if (http2session->resume_data(sd->dconn) != 0) {
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
downstream->ensure_downstream_wtimer();
|
||||
}
|
||||
}
|
||||
|
||||
if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
|
||||
|
||||
@@ -391,6 +391,17 @@ int Http2Upstream::on_request_headers(Downstream *downstream,
|
||||
}
|
||||
}
|
||||
|
||||
auto connect_proto = req.fs.header(http2::HD__PROTOCOL);
|
||||
if (connect_proto) {
|
||||
if (connect_proto->value != "websocket") {
|
||||
if (error_reply(downstream, 400) != 0) {
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
req.connect_proto = CONNECT_PROTO_WEBSOCKET;
|
||||
}
|
||||
|
||||
if (!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) {
|
||||
req.http2_expect_body = true;
|
||||
} else if (req.fs.content_length == -1) {
|
||||
@@ -461,9 +472,6 @@ void Http2Upstream::initiate_downstream(Downstream *downstream) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef HAVE_MRUBY
|
||||
auto dconn_ptr = dconn.get();
|
||||
#endif // HAVE_MRUBY
|
||||
rv = downstream->attach_downstream_connection(std::move(dconn));
|
||||
if (rv != 0) {
|
||||
// downstream connection fails, send error page
|
||||
@@ -477,27 +485,6 @@ void Http2Upstream::initiate_downstream(Downstream *downstream) {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef HAVE_MRUBY
|
||||
const auto &group = dconn_ptr->get_downstream_addr_group();
|
||||
if (group) {
|
||||
const auto &mruby_ctx = group->mruby_ctx;
|
||||
if (mruby_ctx->run_on_request_proc(downstream) != 0) {
|
||||
if (error_reply(downstream, 500) != 0) {
|
||||
rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
|
||||
}
|
||||
|
||||
downstream_queue_.mark_failure(downstream);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif // HAVE_MRUBY
|
||||
|
||||
rv = downstream->push_request_headers();
|
||||
if (rv != 0) {
|
||||
|
||||
@@ -614,7 +601,7 @@ int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
||||
auto downstream = static_cast<Downstream *>(
|
||||
nghttp2_session_get_stream_user_data(session, stream_id));
|
||||
|
||||
if (!downstream) {
|
||||
if (!downstream || !downstream->get_downstream_connection()) {
|
||||
if (upstream->consume(stream_id, len) != 0) {
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
@@ -1025,7 +1012,7 @@ Http2Upstream::Http2Upstream(ClientHandler *handler)
|
||||
flow_control_ = true;
|
||||
|
||||
// TODO Maybe call from outside?
|
||||
std::array<nghttp2_settings_entry, 3> entry;
|
||||
std::array<nghttp2_settings_entry, 4> entry;
|
||||
size_t nentry = 2;
|
||||
|
||||
entry[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
|
||||
@@ -1038,6 +1025,12 @@ Http2Upstream::Http2Upstream(ClientHandler *handler)
|
||||
entry[1].value = http2conf.upstream.window_size;
|
||||
}
|
||||
|
||||
if (!config->http2_proxy) {
|
||||
entry[nentry].settings_id = NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL;
|
||||
entry[nentry].value = 1;
|
||||
++nentry;
|
||||
}
|
||||
|
||||
if (http2conf.upstream.decoder_dynamic_table_size !=
|
||||
NGHTTP2_DEFAULT_HEADER_TABLE_SIZE) {
|
||||
entry[nentry].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
|
||||
@@ -1635,24 +1628,6 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
|
||||
|
||||
#ifdef HAVE_MRUBY
|
||||
if (!downstream->get_non_final_response()) {
|
||||
auto dconn = downstream->get_downstream_connection();
|
||||
const auto &group = dconn->get_downstream_addr_group();
|
||||
if (group) {
|
||||
const auto &dmruby_ctx = group->mruby_ctx;
|
||||
|
||||
if (dmruby_ctx->run_on_response_proc(downstream) != 0) {
|
||||
if (error_reply(downstream, 500) != 0) {
|
||||
return -1;
|
||||
}
|
||||
// Returning -1 will signal deletion of dconn.
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
auto worker = handler_->get_worker();
|
||||
auto mruby_ctx = worker->get_mruby_context();
|
||||
|
||||
@@ -1704,11 +1679,11 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
|
||||
nva.reserve(resp.fs.headers().size() + 5 +
|
||||
httpconf.add_response_headers.size());
|
||||
|
||||
auto response_status = http2::stringify_status(balloc, resp.http_status);
|
||||
|
||||
nva.push_back(http2::make_nv_ls_nocopy(":status", response_status));
|
||||
|
||||
if (downstream->get_non_final_response()) {
|
||||
auto response_status = http2::stringify_status(balloc, resp.http_status);
|
||||
|
||||
nva.push_back(http2::make_nv_ls_nocopy(":status", response_status));
|
||||
|
||||
http2::copy_headers_to_nva_nocopy(nva, resp.fs.headers(),
|
||||
http2::HDOP_STRIP_ALL);
|
||||
|
||||
@@ -1730,8 +1705,19 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
http2::copy_headers_to_nva_nocopy(
|
||||
nva, resp.fs.headers(), http2::HDOP_STRIP_ALL & ~http2::HDOP_STRIP_VIA);
|
||||
auto striphd_flags = http2::HDOP_STRIP_ALL & ~http2::HDOP_STRIP_VIA;
|
||||
StringRef response_status;
|
||||
|
||||
if (req.connect_proto && resp.http_status == 101) {
|
||||
response_status = http2::stringify_status(balloc, 200);
|
||||
striphd_flags |= http2::HDOP_STRIP_SEC_WEBSOCKET_ACCEPT;
|
||||
} else {
|
||||
response_status = http2::stringify_status(balloc, resp.http_status);
|
||||
}
|
||||
|
||||
nva.push_back(http2::make_nv_ls_nocopy(":status", response_status));
|
||||
|
||||
http2::copy_headers_to_nva_nocopy(nva, resp.fs.headers(), striphd_flags);
|
||||
|
||||
if (!config->http2_proxy && !httpconf.no_server_rewrite) {
|
||||
nva.push_back(http2::make_nv_ls_nocopy("server", httpconf.server_name));
|
||||
@@ -1742,7 +1728,7 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
|
||||
}
|
||||
}
|
||||
|
||||
if (req.method != HTTP_CONNECT || !downstream->get_upgraded()) {
|
||||
if (!req.regular_connect_method() || !downstream->get_upgraded()) {
|
||||
auto affinity_cookie = downstream->get_affinity_cookie_to_send();
|
||||
if (affinity_cookie) {
|
||||
auto dconn = downstream->get_downstream_connection();
|
||||
@@ -1916,7 +1902,7 @@ int Http2Upstream::on_downstream_abort_request_with_https_redirect(
|
||||
|
||||
int Http2Upstream::redirect_to_https(Downstream *downstream) {
|
||||
auto &req = downstream->request();
|
||||
if (req.method == HTTP_CONNECT || req.scheme != "http") {
|
||||
if (req.regular_connect_method() || req.scheme != "http") {
|
||||
return error_reply(downstream, 400);
|
||||
}
|
||||
|
||||
|
||||
@@ -87,11 +87,7 @@ void retry_downstream_connection(Downstream *downstream,
|
||||
downstream->pop_downstream_connection();
|
||||
|
||||
int rv;
|
||||
// We have to use h1 backend for retry if we have already written h1
|
||||
// request in request buffer.
|
||||
auto ndconn = handler->get_downstream_connection(
|
||||
rv, downstream,
|
||||
downstream->get_request_header_sent() ? PROTO_HTTP1 : PROTO_NONE);
|
||||
auto ndconn = handler->get_downstream_connection(rv, downstream);
|
||||
if (ndconn) {
|
||||
if (downstream->attach_downstream_connection(std::move(ndconn)) == 0 &&
|
||||
downstream->push_request_headers() == 0) {
|
||||
@@ -488,7 +484,7 @@ int HttpDownstreamConnection::push_request_headers() {
|
||||
|
||||
auto &balloc = downstream_->get_block_allocator();
|
||||
|
||||
auto connect_method = req.method == HTTP_CONNECT;
|
||||
auto connect_method = req.regular_connect_method();
|
||||
|
||||
auto config = get_config();
|
||||
auto &httpconf = config->http;
|
||||
@@ -512,7 +508,8 @@ int HttpDownstreamConnection::push_request_headers() {
|
||||
auto buf = downstream_->get_request_buf();
|
||||
|
||||
// Assume that method and request path do not contain \r\n.
|
||||
auto meth = http2::to_method_string(req.method);
|
||||
auto meth = http2::to_method_string(
|
||||
req.connect_proto == CONNECT_PROTO_WEBSOCKET ? HTTP_GET : req.method);
|
||||
buf->append(meth);
|
||||
buf->append(' ');
|
||||
|
||||
@@ -556,7 +553,8 @@ int HttpDownstreamConnection::push_request_headers() {
|
||||
|
||||
// set transfer-encoding only when content-length is unknown and
|
||||
// request body is expected.
|
||||
if (!connect_method && req.http2_expect_body && req.fs.content_length == -1) {
|
||||
if (req.method != HTTP_CONNECT && req.http2_expect_body &&
|
||||
req.fs.content_length == -1) {
|
||||
downstream_->set_chunked_request(true);
|
||||
buf->append("Transfer-Encoding: chunked\r\n");
|
||||
}
|
||||
@@ -565,7 +563,11 @@ int HttpDownstreamConnection::push_request_headers() {
|
||||
buf->append("Connection: close\r\n");
|
||||
}
|
||||
|
||||
if (!connect_method && req.upgrade_request) {
|
||||
if (req.connect_proto == CONNECT_PROTO_WEBSOCKET) {
|
||||
// TODO Generate Sec-WebSocket-Key
|
||||
buf->append("Upgrade: websocket\r\nConnection: "
|
||||
"Upgrade\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n");
|
||||
} else if (!connect_method && req.upgrade_request) {
|
||||
auto connection = req.fs.header(http2::HD_CONNECTION);
|
||||
if (connection) {
|
||||
buf->append("Connection: ");
|
||||
@@ -697,37 +699,11 @@ int HttpDownstreamConnection::push_request_headers() {
|
||||
// Don't call signal_write() if we anticipate request body. We call
|
||||
// signal_write() when we received request body chunk, and it
|
||||
// enables us to send headers and data in one writev system call.
|
||||
if (connect_method || downstream_->get_blocked_request_buf()->rleft() ||
|
||||
if (connect_method || req.connect_proto ||
|
||||
(!req.http2_expect_body && req.fs.content_length == 0)) {
|
||||
signal_write();
|
||||
}
|
||||
|
||||
return process_blocked_request_buf();
|
||||
}
|
||||
|
||||
int HttpDownstreamConnection::process_blocked_request_buf() {
|
||||
auto src = downstream_->get_blocked_request_buf();
|
||||
|
||||
if (src->rleft()) {
|
||||
auto dest = downstream_->get_request_buf();
|
||||
auto chunked = downstream_->get_chunked_request();
|
||||
if (chunked) {
|
||||
auto chunk_size_hex = util::utox(src->rleft());
|
||||
dest->append(chunk_size_hex);
|
||||
dest->append("\r\n");
|
||||
}
|
||||
|
||||
src->remove(*dest);
|
||||
|
||||
if (chunked) {
|
||||
dest->append("\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (downstream_->get_blocked_request_data_eof()) {
|
||||
return end_upload_data();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -931,7 +907,7 @@ int htp_hdrs_completecb(http_parser *htp) {
|
||||
return -1;
|
||||
}
|
||||
} else if (resp.http_status / 100 == 1 ||
|
||||
(resp.http_status == 200 && req.method == HTTP_CONNECT)) {
|
||||
(resp.http_status == 200 && req.regular_connect_method())) {
|
||||
if (resp.fs.header(http2::HD_CONTENT_LENGTH) ||
|
||||
resp.fs.header(http2::HD_TRANSFER_ENCODING)) {
|
||||
return -1;
|
||||
|
||||
@@ -89,8 +89,6 @@ public:
|
||||
|
||||
int noop();
|
||||
|
||||
int process_blocked_request_buf();
|
||||
|
||||
private:
|
||||
Connection conn_;
|
||||
std::function<int(HttpDownstreamConnection &)> on_read_, on_write_,
|
||||
|
||||
@@ -431,31 +431,12 @@ int htp_hdrs_completecb(http_parser *htp) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef HAVE_MRUBY
|
||||
auto dconn_ptr = dconn.get();
|
||||
#endif // HAVE_MRUBY
|
||||
if (downstream->attach_downstream_connection(std::move(dconn)) != 0) {
|
||||
downstream->set_request_state(Downstream::CONNECT_FAIL);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef HAVE_MRUBY
|
||||
const auto &group = dconn_ptr->get_downstream_addr_group();
|
||||
if (group) {
|
||||
const auto &dmruby_ctx = group->mruby_ctx;
|
||||
|
||||
if (dmruby_ctx->run_on_request_proc(downstream) != 0) {
|
||||
resp.http_status = 500;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif // HAVE_MRUBY
|
||||
|
||||
rv = downstream->push_request_headers();
|
||||
|
||||
if (rv != 0) {
|
||||
@@ -1040,8 +1021,6 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
|
||||
const auto &req = downstream->request();
|
||||
auto &resp = downstream->response();
|
||||
auto &balloc = downstream->get_block_allocator();
|
||||
auto dconn = downstream->get_downstream_connection();
|
||||
assert(dconn);
|
||||
|
||||
if (downstream->get_non_final_response() &&
|
||||
!downstream->supports_non_final_response()) {
|
||||
@@ -1051,20 +1030,6 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
|
||||
|
||||
#ifdef HAVE_MRUBY
|
||||
if (!downstream->get_non_final_response()) {
|
||||
const auto &group = dconn->get_downstream_addr_group();
|
||||
if (group) {
|
||||
const auto &dmruby_ctx = group->mruby_ctx;
|
||||
|
||||
if (dmruby_ctx->run_on_response_proc(downstream) != 0) {
|
||||
error_reply(500);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
auto worker = handler_->get_worker();
|
||||
auto mruby_ctx = worker->get_mruby_context();
|
||||
|
||||
@@ -1185,6 +1150,8 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
|
||||
if (req.method != HTTP_CONNECT || !downstream->get_upgraded()) {
|
||||
auto affinity_cookie = downstream->get_affinity_cookie_to_send();
|
||||
if (affinity_cookie) {
|
||||
auto dconn = downstream->get_downstream_connection();
|
||||
assert(dconn);
|
||||
auto &group = dconn->get_downstream_addr_group();
|
||||
auto &shared_addr = group->shared_addr;
|
||||
auto &cookieconf = shared_addr->affinity.cookie;
|
||||
|
||||
@@ -68,10 +68,6 @@ void proc_wev_cb(struct ev_loop *loop, ev_timer *w, int revents) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DownstreamAddrGroup::DownstreamAddrGroup() : retired{false} {}
|
||||
|
||||
DownstreamAddrGroup::~DownstreamAddrGroup() {}
|
||||
|
||||
// DownstreamKey is used to index SharedDownstreamAddr in order to
|
||||
// find the same configuration.
|
||||
using DownstreamKey = std::tuple<
|
||||
@@ -181,12 +177,6 @@ void Worker::replace_downstream_config(
|
||||
std::vector<std::shared_ptr<DownstreamAddrGroup>>(groups.size());
|
||||
|
||||
std::map<DownstreamKey, size_t> addr_groups_indexer;
|
||||
#ifdef HAVE_MRUBY
|
||||
// TODO It is a bit less efficient because
|
||||
// mruby::create_mruby_context returns std::unique_ptr and we cannot
|
||||
// use std::make_shared.
|
||||
std::map<StringRef, std::shared_ptr<mruby::MRubyContext>> shared_mruby_ctxs;
|
||||
#endif // HAVE_MRUBY
|
||||
|
||||
for (size_t i = 0; i < groups.size(); ++i) {
|
||||
auto &src = groups[i];
|
||||
@@ -195,16 +185,6 @@ void Worker::replace_downstream_config(
|
||||
dst = std::make_shared<DownstreamAddrGroup>();
|
||||
dst->pattern =
|
||||
ImmutableString{std::begin(src.pattern), std::end(src.pattern)};
|
||||
#ifdef HAVE_MRUBY
|
||||
auto mruby_ctx_it = shared_mruby_ctxs.find(src.mruby_file);
|
||||
if (mruby_ctx_it == std::end(shared_mruby_ctxs)) {
|
||||
dst->mruby_ctx = mruby::create_mruby_context(src.mruby_file);
|
||||
assert(dst->mruby_ctx);
|
||||
shared_mruby_ctxs.emplace(src.mruby_file, dst->mruby_ctx);
|
||||
} else {
|
||||
dst->mruby_ctx = (*mruby_ctx_it).second;
|
||||
}
|
||||
#endif // HAVE_MRUBY
|
||||
|
||||
auto shared_addr = std::make_shared<SharedDownstreamAddr>();
|
||||
|
||||
|
||||
@@ -183,8 +183,7 @@ struct SharedDownstreamAddr {
|
||||
};
|
||||
|
||||
struct DownstreamAddrGroup {
|
||||
DownstreamAddrGroup();
|
||||
~DownstreamAddrGroup();
|
||||
DownstreamAddrGroup() : retired{false} {};
|
||||
|
||||
DownstreamAddrGroup(const DownstreamAddrGroup &) = delete;
|
||||
DownstreamAddrGroup(DownstreamAddrGroup &&) = delete;
|
||||
@@ -193,9 +192,6 @@ struct DownstreamAddrGroup {
|
||||
|
||||
ImmutableString pattern;
|
||||
std::shared_ptr<SharedDownstreamAddr> shared_addr;
|
||||
#ifdef HAVE_MRUBY
|
||||
std::shared_ptr<mruby::MRubyContext> mruby_ctx;
|
||||
#endif // HAVE_MRUBY
|
||||
// true if this group is no longer used for new request. If this is
|
||||
// true, the connection made using one of address in shared_addr
|
||||
// must not be pooled.
|
||||
|
||||
@@ -411,30 +411,35 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) {
|
||||
|
||||
auto gen = util::make_mt19937();
|
||||
|
||||
auto conn_handler = make_unique<ConnectionHandler>(loop, gen);
|
||||
ConnectionHandler conn_handler(loop, gen);
|
||||
|
||||
for (auto &addr : config->conn.listener.addrs) {
|
||||
conn_handler->add_acceptor(
|
||||
make_unique<AcceptHandler>(&addr, conn_handler.get()));
|
||||
conn_handler.add_acceptor(make_unique<AcceptHandler>(&addr, &conn_handler));
|
||||
}
|
||||
|
||||
#ifdef HAVE_NEVERBLEED
|
||||
std::array<char, NEVERBLEED_ERRBUF_SIZE> nb_errbuf;
|
||||
auto nb = make_unique<neverbleed_t>();
|
||||
if (neverbleed_init(nb.get(), nb_errbuf.data()) != 0) {
|
||||
LOG(FATAL) << "neverbleed_init failed: " << nb_errbuf.data();
|
||||
return -1;
|
||||
{
|
||||
std::array<char, NEVERBLEED_ERRBUF_SIZE> nb_errbuf;
|
||||
auto nb = make_unique<neverbleed_t>();
|
||||
if (neverbleed_init(nb.get(), nb_errbuf.data()) != 0) {
|
||||
LOG(FATAL) << "neverbleed_init failed: " << nb_errbuf.data();
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOG(NOTICE) << "neverbleed process [" << nb->daemon_pid << "] spawned";
|
||||
|
||||
conn_handler.set_neverbleed(std::move(nb));
|
||||
}
|
||||
|
||||
LOG(NOTICE) << "neverbleed process [" << nb->daemon_pid << "] spawned";
|
||||
|
||||
conn_handler->set_neverbleed(nb.get());
|
||||
auto nb = conn_handler.get_neverbleed();
|
||||
|
||||
ev_child nb_childev;
|
||||
if (nb) {
|
||||
ev_child_init(&nb_childev, nb_child_cb, nb->daemon_pid, 0);
|
||||
nb_childev.data = nullptr;
|
||||
ev_child_start(loop, &nb_childev);
|
||||
}
|
||||
|
||||
ev_child_init(&nb_childev, nb_child_cb, nb->daemon_pid, 0);
|
||||
nb_childev.data = nullptr;
|
||||
ev_child_start(loop, &nb_childev);
|
||||
#endif // HAVE_NEVERBLEED
|
||||
|
||||
MemchunkPool mcpool;
|
||||
@@ -448,17 +453,17 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) {
|
||||
SSL_CTX *ssl_ctx = nullptr;
|
||||
|
||||
if (memcachedconf.tls) {
|
||||
ssl_ctx = conn_handler->create_tls_ticket_key_memcached_ssl_ctx();
|
||||
ssl_ctx = conn_handler.create_tls_ticket_key_memcached_ssl_ctx();
|
||||
}
|
||||
|
||||
conn_handler->set_tls_ticket_key_memcached_dispatcher(
|
||||
conn_handler.set_tls_ticket_key_memcached_dispatcher(
|
||||
make_unique<MemcachedDispatcher>(
|
||||
&ticketconf.memcached.addr, loop, ssl_ctx,
|
||||
StringRef{memcachedconf.host}, &mcpool, gen));
|
||||
|
||||
ev_timer_init(&renew_ticket_key_timer, memcached_get_ticket_key_cb, 0.,
|
||||
0.);
|
||||
renew_ticket_key_timer.data = conn_handler.get();
|
||||
renew_ticket_key_timer.data = &conn_handler;
|
||||
// Get first ticket keys.
|
||||
memcached_get_ticket_key_cb(loop, &renew_ticket_key_timer, 0);
|
||||
} else {
|
||||
@@ -478,14 +483,14 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) {
|
||||
if (!ticket_keys) {
|
||||
LOG(WARN) << "Use internal session ticket key generator";
|
||||
} else {
|
||||
conn_handler->set_ticket_keys(std::move(ticket_keys));
|
||||
conn_handler.set_ticket_keys(std::move(ticket_keys));
|
||||
auto_tls_ticket_key = false;
|
||||
}
|
||||
}
|
||||
if (auto_tls_ticket_key) {
|
||||
// Generate new ticket key every 1hr.
|
||||
ev_timer_init(&renew_ticket_key_timer, renew_ticket_key_cb, 0., 1_h);
|
||||
renew_ticket_key_timer.data = conn_handler.get();
|
||||
renew_ticket_key_timer.data = &conn_handler;
|
||||
ev_timer_again(loop, &renew_ticket_key_timer);
|
||||
|
||||
// Generate first session ticket key before running workers.
|
||||
@@ -495,7 +500,7 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) {
|
||||
}
|
||||
|
||||
if (config->single_thread) {
|
||||
rv = conn_handler->create_single_worker();
|
||||
rv = conn_handler.create_single_worker();
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
@@ -513,7 +518,7 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) {
|
||||
}
|
||||
#endif // !NOTHREADS
|
||||
|
||||
rv = conn_handler->create_worker_thread(config->num_worker);
|
||||
rv = conn_handler.create_worker_thread(config->num_worker);
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
@@ -530,22 +535,22 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) {
|
||||
|
||||
drop_privileges(
|
||||
#ifdef HAVE_NEVERBLEED
|
||||
nb.get()
|
||||
nb
|
||||
#endif // HAVE_NEVERBLEED
|
||||
);
|
||||
|
||||
ev_io ipcev;
|
||||
ev_io_init(&ipcev, ipc_readcb, wpconf->ipc_fd, EV_READ);
|
||||
ipcev.data = conn_handler.get();
|
||||
ipcev.data = &conn_handler;
|
||||
ev_io_start(loop, &ipcev);
|
||||
|
||||
if (tls::upstream_tls_enabled(config->conn) && !config->tls.ocsp.disabled) {
|
||||
if (config->tls.ocsp.startup) {
|
||||
conn_handler->set_enable_acceptor_on_ocsp_completion(true);
|
||||
conn_handler->disable_acceptor();
|
||||
conn_handler.set_enable_acceptor_on_ocsp_completion(true);
|
||||
conn_handler.disable_acceptor();
|
||||
}
|
||||
|
||||
conn_handler->proceed_next_cert_ocsp();
|
||||
conn_handler.proceed_next_cert_ocsp();
|
||||
}
|
||||
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
@@ -554,29 +559,27 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) {
|
||||
|
||||
ev_run(loop, 0);
|
||||
|
||||
conn_handler->cancel_ocsp_update();
|
||||
|
||||
// Destroy SSL_CTX held in conn_handler before killing neverbleed
|
||||
// daemon. Otherwise priv_rsa_finish yields "write error" and
|
||||
// worker process aborts.
|
||||
conn_handler.reset();
|
||||
conn_handler.cancel_ocsp_update();
|
||||
|
||||
#ifdef HAVE_NEVERBLEED
|
||||
assert(nb->daemon_pid > 0);
|
||||
if (nb) {
|
||||
assert(nb->daemon_pid > 0);
|
||||
|
||||
rv = kill(nb->daemon_pid, SIGTERM);
|
||||
if (rv != 0) {
|
||||
auto error = errno;
|
||||
LOG(ERROR) << "Could not send signal to neverbleed daemon: errno=" << error;
|
||||
}
|
||||
rv = kill(nb->daemon_pid, SIGTERM);
|
||||
if (rv != 0) {
|
||||
auto error = errno;
|
||||
LOG(ERROR) << "Could not send signal to neverbleed daemon: errno="
|
||||
<< error;
|
||||
}
|
||||
|
||||
while ((rv = waitpid(nb->daemon_pid, nullptr, 0)) == -1 && errno == EINTR)
|
||||
;
|
||||
if (rv == -1) {
|
||||
auto error = errno;
|
||||
LOG(ERROR) << "Error occurred while we were waiting for the completion "
|
||||
"of neverbleed process: errno="
|
||||
<< error;
|
||||
while ((rv = waitpid(nb->daemon_pid, nullptr, 0)) == -1 && errno == EINTR)
|
||||
;
|
||||
if (rv == -1) {
|
||||
auto error = errno;
|
||||
LOG(ERROR) << "Error occurred while we were waiting for the completion "
|
||||
"of neverbleed process: errno="
|
||||
<< error;
|
||||
}
|
||||
}
|
||||
#endif // HAVE_NEVERBLEED
|
||||
|
||||
|
||||
26
src/util.h
26
src/util.h
@@ -370,12 +370,14 @@ template <typename T> std::string utos(T n) {
|
||||
res = "0";
|
||||
return res;
|
||||
}
|
||||
size_t nlen = 0;
|
||||
for (auto t = n; t; t /= 10, ++nlen)
|
||||
int i = 0;
|
||||
T t = n;
|
||||
for (; t; t /= 10, ++i)
|
||||
;
|
||||
res.resize(nlen);
|
||||
for (; n; n /= 10) {
|
||||
res[--nlen] = (n % 10) + '0';
|
||||
res.resize(i);
|
||||
--i;
|
||||
for (; n; --i, n /= 10) {
|
||||
res[i] = (n % 10) + '0';
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@@ -385,13 +387,15 @@ template <typename T, typename OutputIt> OutputIt utos(OutputIt dst, T n) {
|
||||
*dst++ = '0';
|
||||
return dst;
|
||||
}
|
||||
size_t nlen = 0;
|
||||
for (auto t = n; t; t /= 10, ++nlen)
|
||||
int i = 0;
|
||||
T t = n;
|
||||
for (; t; t /= 10, ++i)
|
||||
;
|
||||
auto p = dst + nlen;
|
||||
auto res = p;
|
||||
for (; n; n /= 10) {
|
||||
*--p = (n % 10) + '0';
|
||||
--i;
|
||||
auto p = dst + i;
|
||||
auto res = p + 1;
|
||||
for (; n; --i, n /= 10) {
|
||||
*p-- = (n % 10) + '0';
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -319,8 +319,6 @@ int main() {
|
||||
test_nghttp2_session_pause_data) ||
|
||||
!CU_add_test(pSuite, "session_no_closed_streams",
|
||||
test_nghttp2_session_no_closed_streams) ||
|
||||
!CU_add_test(pSuite, "session_set_stream_user_data",
|
||||
test_nghttp2_session_set_stream_user_data) ||
|
||||
!CU_add_test(pSuite, "http_mandatory_headers",
|
||||
test_nghttp2_http_mandatory_headers) ||
|
||||
!CU_add_test(pSuite, "http_content_length",
|
||||
|
||||
@@ -3480,6 +3480,29 @@ void test_nghttp2_session_on_settings_received(void) {
|
||||
CU_ASSERT(NGHTTP2_STREAM_CLOSING == stream1->state);
|
||||
|
||||
nghttp2_session_del(session);
|
||||
|
||||
/* It is invalid that peer disables ENABLE_CONNECT_PROTOCOL once it
|
||||
has been enabled. */
|
||||
nghttp2_session_client_new(&session, &callbacks, NULL);
|
||||
|
||||
session->remote_settings.enable_connect_protocol = 1;
|
||||
|
||||
iv[0].settings_id = NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL;
|
||||
iv[0].value = 0;
|
||||
|
||||
nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 1),
|
||||
1);
|
||||
|
||||
CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame, 0));
|
||||
|
||||
nghttp2_frame_settings_free(&frame.settings, mem);
|
||||
|
||||
item = nghttp2_session_get_next_ob_item(session);
|
||||
|
||||
CU_ASSERT(NULL != item);
|
||||
CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type);
|
||||
|
||||
nghttp2_session_del(session);
|
||||
}
|
||||
|
||||
void test_nghttp2_session_on_push_promise_received(void) {
|
||||
@@ -10696,39 +10719,6 @@ void test_nghttp2_session_no_closed_streams(void) {
|
||||
nghttp2_option_del(option);
|
||||
}
|
||||
|
||||
void test_nghttp2_session_set_stream_user_data(void) {
|
||||
nghttp2_session *session;
|
||||
nghttp2_session_callbacks callbacks;
|
||||
int32_t stream_id;
|
||||
int user_data1, user_data2;
|
||||
int rv;
|
||||
const uint8_t *datap;
|
||||
ssize_t datalen;
|
||||
|
||||
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
|
||||
|
||||
nghttp2_session_client_new(&session, &callbacks, NULL);
|
||||
|
||||
stream_id = nghttp2_submit_request(session, NULL, reqnv, ARRLEN(reqnv), NULL,
|
||||
&user_data1);
|
||||
|
||||
rv = nghttp2_session_set_stream_user_data(session, stream_id, &user_data2);
|
||||
|
||||
CU_ASSERT(0 == rv);
|
||||
|
||||
datalen = nghttp2_session_mem_send(session, &datap);
|
||||
|
||||
CU_ASSERT(datalen > 0);
|
||||
|
||||
CU_ASSERT(&user_data2 ==
|
||||
nghttp2_session_get_stream_user_data(session, stream_id));
|
||||
|
||||
CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT ==
|
||||
nghttp2_session_set_stream_user_data(session, 2, NULL));
|
||||
|
||||
nghttp2_session_del(session);
|
||||
}
|
||||
|
||||
static void check_nghttp2_http_recv_headers_fail(
|
||||
nghttp2_session *session, nghttp2_hd_deflater *deflater, int32_t stream_id,
|
||||
int stream_state, const nghttp2_nv *nva, size_t nvlen) {
|
||||
@@ -10884,6 +10874,17 @@ void test_nghttp2_http_mandatory_headers(void) {
|
||||
const nghttp2_nv asteriskoptions2_reqnv[] = {
|
||||
MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "localhost"),
|
||||
MAKE_NV(":method", "OPTIONS"), MAKE_NV(":path", "*")};
|
||||
const nghttp2_nv connectproto_reqnv[] = {
|
||||
MAKE_NV(":scheme", "https"), MAKE_NV(":path", "/"),
|
||||
MAKE_NV(":method", "CONNECT"), MAKE_NV(":authority", "localhost"),
|
||||
MAKE_NV(":protocol", "websocket")};
|
||||
const nghttp2_nv connectprotoget_reqnv[] = {
|
||||
MAKE_NV(":scheme", "https"), MAKE_NV(":path", "/"),
|
||||
MAKE_NV(":method", "GET"), MAKE_NV(":authority", "localhost"),
|
||||
MAKE_NV(":protocol", "websocket")};
|
||||
const nghttp2_nv connectprotonopath_reqnv[] = {
|
||||
MAKE_NV(":scheme", "https"), MAKE_NV(":method", "GET"),
|
||||
MAKE_NV(":authority", "localhost"), MAKE_NV(":protocol", "websocket")};
|
||||
|
||||
mem = nghttp2_mem_default();
|
||||
|
||||
@@ -11031,6 +11032,39 @@ void test_nghttp2_http_mandatory_headers(void) {
|
||||
asteriskoptions2_reqnv,
|
||||
ARRLEN(asteriskoptions2_reqnv));
|
||||
|
||||
/* :protocol is not allowed unless it is enabled by the local
|
||||
endpoint. */
|
||||
check_nghttp2_http_recv_headers_fail(session, &deflater, 27, -1,
|
||||
connectproto_reqnv,
|
||||
ARRLEN(connectproto_reqnv));
|
||||
|
||||
nghttp2_hd_deflate_free(&deflater);
|
||||
|
||||
nghttp2_session_del(session);
|
||||
|
||||
/* enable SETTINGS_CONNECT_PROTOCOL */
|
||||
nghttp2_session_server_new(&session, &callbacks, &ud);
|
||||
|
||||
session->local_settings.enable_connect_protocol = 1;
|
||||
|
||||
nghttp2_hd_deflate_init(&deflater, mem);
|
||||
|
||||
/* :protocol is allowed if SETTINGS_CONNECT_PROTOCOL is enabled by
|
||||
the local endpoint. */
|
||||
check_nghttp2_http_recv_headers_ok(session, &deflater, 1, -1,
|
||||
connectproto_reqnv,
|
||||
ARRLEN(connectproto_reqnv));
|
||||
|
||||
/* :protocol is only allowed with CONNECT method. */
|
||||
check_nghttp2_http_recv_headers_fail(session, &deflater, 3, -1,
|
||||
connectprotoget_reqnv,
|
||||
ARRLEN(connectprotoget_reqnv));
|
||||
|
||||
/* CONNECT method with :protocol requires :path. */
|
||||
check_nghttp2_http_recv_headers_fail(session, &deflater, 5, -1,
|
||||
connectprotonopath_reqnv,
|
||||
ARRLEN(connectprotonopath_reqnv));
|
||||
|
||||
nghttp2_hd_deflate_free(&deflater);
|
||||
|
||||
nghttp2_session_del(session);
|
||||
|
||||
@@ -158,7 +158,6 @@ void test_nghttp2_session_cancel_from_before_frame_send(void);
|
||||
void test_nghttp2_session_removed_closed_stream(void);
|
||||
void test_nghttp2_session_pause_data(void);
|
||||
void test_nghttp2_session_no_closed_streams(void);
|
||||
void test_nghttp2_session_set_stream_user_data(void);
|
||||
void test_nghttp2_http_mandatory_headers(void);
|
||||
void test_nghttp2_http_content_length(void);
|
||||
void test_nghttp2_http_content_length_mismatch(void);
|
||||
|
||||
Reference in New Issue
Block a user