Compare commits

..

6 Commits
v1.33.0 ... ws

Author SHA1 Message Date
Tatsuhiro Tsujikawa
e6381b2b65 nghttpx: Fix uninitialized member variable 2018-06-23 11:25:39 +09:00
Tatsuhiro Tsujikawa
b6930e3d80 nghttpx: Disable connect protocol if forward proxy is enabled 2018-06-23 11:25:39 +09:00
Tatsuhiro Tsujikawa
19d28365e9 nghttpx: Add experimental websocket with extended CONNECT 2018-06-23 11:25:39 +09:00
Tatsuhiro Tsujikawa
a26eb08a89 Deal with :protocol pseudo header 2018-06-23 11:23:50 +09:00
Tatsuhiro Tsujikawa
8625a93bb0 Add NGHTTP2_TOKEN__PROTOCOL 2018-06-23 11:23:50 +09:00
Tatsuhiro Tsujikawa
51e4ca9f7e Add SETTINGS_ENABLE_CONNECT_PROTOCOL 2018-06-23 11:23:50 +09:00
53 changed files with 419 additions and 713 deletions

View File

@@ -16,7 +16,6 @@ github issues [2].
187j3x1
Alek Storm
Alex Nalivko
Alexandros Konstantinakis-Karmis
Alexis La Goutte
Amir Pakdel
Anders Bakken

View File

@@ -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)

View File

@@ -25,7 +25,7 @@ dnl Do not change user variables!
dnl http://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html
AC_PREREQ(2.61)
AC_INIT([nghttp2], [1.33.0], [t-tujikawa@users.sourceforge.net])
AC_INIT([nghttp2], [1.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"`

View File

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

View File

@@ -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
.

View File

@@ -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
.

View File

@@ -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
.

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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',

View File

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

View File

@@ -169,7 +169,6 @@ OPTIONS = [
"ocsp-startup",
"no-verify-ocsp",
"verify-client-tolerate-expired",
"ignore-per-pattern-mruby-error",
]
LOGVARS = [

View File

@@ -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

View File

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

View File

@@ -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':

View File

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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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);

View File

@@ -164,6 +164,7 @@ typedef struct {
uint32_t initial_window_size;
uint32_t max_frame_size;
uint32_t max_header_list_size;
uint32_t enable_connect_protocol;
} nghttp2_settings_storage;
typedef enum {

View File

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

View File

@@ -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)

View File

@@ -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_; }

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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,

View File

@@ -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.

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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)) {

View File

@@ -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

View File

@@ -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;

View File

@@ -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,

View File

@@ -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() {

View File

@@ -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_;

View File

@@ -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

View File

@@ -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

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -89,8 +89,6 @@ public:
int noop();
int process_blocked_request_buf();
private:
Connection conn_;
std::function<int(HttpDownstreamConnection &)> on_read_, on_write_,

View File

@@ -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;

View File

@@ -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>();

View File

@@ -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.

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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",

View File

@@ -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);

View File

@@ -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);