Merge branch 'master' into simple-extensions

This commit is contained in:
Tatsuhiro Tsujikawa 2016-02-24 23:21:03 +09:00
commit 34bf153653
97 changed files with 2920 additions and 1774 deletions

81
AUTHORS
View File

@ -1 +1,80 @@
Tatsuhiro Tsujikawa <t-tujikawa at users dot sourceforge dot net> nghttp2 project was started as a fork of spdylay project [1]. Both
projects were started by Tatsuhiro Tsujikawa, who is still the main
author of these projects. Meanwhile, we have many contributions, and
we are not here without them. We sincerely thank you to all who made
a contribution. Here is the all individuals/organizations who
contributed to nghttp2 and spdylay project at which we forked. These
names are retrieved from git commit log. If you have made a
contribution, but you are missing in the list, please let us know via
github issues [2].
[1] https://github.com/tatsuhiro-t/spdylay
[2] https://github.com/tatsuhiro-t/nghttp2/issues
--------
187j3x1
Alek Storm
Alex Nalivko
Alexis La Goutte
Anders Bakken
Andreas Pohl
Andy Davies
Ant Bryan
Bernard Spil
Brian Card
Daniel Stenberg
Dave Reisner
David Beitey
David Weekly
Etienne Cimon
Fabian Möller
Fabian Wiesel
Gabi Davar
Janusz Dziemidowicz
Jay Satiro
Jim Morrison
José F. Calcerrada
Kamil Dudka
Kazuho Oku
Kenny (kang-yen) Peng
Kenny Peng
Kit Chan
Kyle Schomp
Lucas Pardue
MATSUMOTO Ryosuke
Mike Frysinger
Nicholas Hurley
Nora Shoemaker
Peeyush Aggarwal
Peter Wu
Piotr Sikora
Raul Gutierrez Segales
Remo E
Reza Tavakoli
Ross Smith II
Scott Mitchell
Stefan Eissing
Stephen Ludin
Sunpoet Po-Chuan Hsieh
Svante Signell
Syohei YOSHIDA
Tatsuhiko Kubo
Tatsuhiro Tsujikawa
Tom Harwood
Tomasz Buchert
Vernon Tang
Viacheslav Biriukov
Viktor Szépe
Xiaoguang Sun
Zhuoyun Wei
acesso
ayanamist
bxshi
es
fangdingjun
kumagi
mod-h2-dev
moparisthebest
snnn
yuuki-kodama

View File

@ -1,6 +1,7 @@
The MIT License The MIT License
Copyright (c) 2012, 2014, 2015, 2016 Tatsuhiro Tsujikawa Copyright (c) 2012, 2014, 2015, 2016 Tatsuhiro Tsujikawa
Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the

View File

@ -58,9 +58,9 @@ To build the documentation, you need to install:
* sphinx (http://sphinx-doc.org/) * sphinx (http://sphinx-doc.org/)
To build and run the application programs (``nghttp``, ``nghttpd`` and To build and run the application programs (``nghttp``, ``nghttpd``,
``nghttpx``) in the ``src`` directory, the following packages are ``nghttpx`` and ``h2load``) in the ``src`` directory, the following packages
required: are required:
* OpenSSL >= 1.0.1 * OpenSSL >= 1.0.1
* libev >= 4.15 * libev >= 4.15
@ -110,8 +110,9 @@ If you are using Ubuntu 14.04 LTS (trusty) or Debian 7.0 (wheezy) and above run
zlib1g-dev libcunit1-dev libssl-dev libxml2-dev libev-dev libevent-dev libjansson-dev \ zlib1g-dev libcunit1-dev libssl-dev libxml2-dev libev-dev libevent-dev libjansson-dev \
libjemalloc-dev cython python3-dev python-setuptools libjemalloc-dev cython python3-dev python-setuptools
spdylay is not packaged in Ubuntu, so you need to build it yourself: From Ubuntu 15.10, spdylay has been available as a package named
http://tatsuhiro-t.github.io/spdylay/ `libspdylay-dev`. For the earlier Ubuntu release, you need to build
it yourself: http://tatsuhiro-t.github.io/spdylay/
To enable mruby support for nghttpx, `mruby To enable mruby support for nghttpx, `mruby
<https://github.com/mruby/mruby>`_ is required. We need to build <https://github.com/mruby/mruby>`_ is required. We need to build
@ -160,6 +161,17 @@ To compile the source code, gcc >= 4.8.3 or clang >= 3.4 is required.
them from crashing. A patch is welcome to make multi threading work them from crashing. A patch is welcome to make multi threading work
on Mac OS X platform. on Mac OS X platform.
.. note::
To compile the associated applications (nghttp, nghttpd, nghttpx
and h2load), you must use the ``--enable-app`` configure option and
ensure that the specified requirements above are met. Normally,
configure script checks required dependencies to build these
applications, and enable ``--enable-app`` automatically, so you
don't have to use it explicitly. But if you found that
applications were not built, then using ``--enable-app`` may find
that cause, such as the missing dependency.
Notes for building on Windows (Mingw/Cygwin) Notes for building on Windows (Mingw/Cygwin)
-------------------------------------------- --------------------------------------------
@ -639,7 +651,9 @@ push.
<https://istlsfastyet.com/#server-performance>`_ in TLS, such as <https://istlsfastyet.com/#server-performance>`_ in TLS, such as
session IDs, session tickets (with automatic key rotation), OCSP session IDs, session tickets (with automatic key rotation), OCSP
stapling, dynamic record sizing, ALPN/NPN, forward secrecy and SPDY & stapling, dynamic record sizing, ALPN/NPN, forward secrecy and SPDY &
HTTP/2. HTTP/2. ``nghttpx`` also offers the functionality to share session
cache and ticket keys among multiple ``nghttpx`` instances via
memcached.
``nghttpx`` has several operational modes: ``nghttpx`` has several operational modes:
@ -661,7 +675,9 @@ The default mode, ``--http2-proxy`` and ``--http2-bridge`` modes use
SSL/TLS in the frontend connection by default. To disable SSL/TLS, SSL/TLS in the frontend connection by default. To disable SSL/TLS,
use the ``--frontend-no-tls`` option. If that option is used, SPDY is use the ``--frontend-no-tls`` option. If that option is used, SPDY is
disabled in the frontend and incoming HTTP/1.1 connections can be disabled in the frontend and incoming HTTP/1.1 connections can be
upgraded to HTTP/2 through HTTP Upgrade. upgraded to HTTP/2 through HTTP Upgrade. In these modes, HTTP/1
backend connections are cleartext by default. To enable TLS, use
``--backend-http1-tls`` opiton.
The ``--http2-bridge``, ``--client`` and ``--client-proxy`` modes use The ``--http2-bridge``, ``--client`` and ``--client-proxy`` modes use
SSL/TLS in the backend connection by default. To disable SSL/TLS, use SSL/TLS in the backend connection by default. To disable SSL/TLS, use

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 dnl http://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html
AC_PREREQ(2.61) AC_PREREQ(2.61)
AC_INIT([nghttp2], [1.7.1-DEV], [t-tujikawa@users.sourceforge.net]) AC_INIT([nghttp2], [1.8.0-DEV], [t-tujikawa@users.sourceforge.net])
AC_CONFIG_AUX_DIR([.]) AC_CONFIG_AUX_DIR([.])
AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_HEADERS([config.h]) AC_CONFIG_HEADERS([config.h])
@ -76,7 +76,7 @@ AC_ARG_ENABLE([threads],
AC_ARG_ENABLE([app], AC_ARG_ENABLE([app],
[AS_HELP_STRING([--enable-app], [AS_HELP_STRING([--enable-app],
[Build applications (nghttp, nghttpd and nghttpx) [default=check]])], [Build applications (nghttp, nghttpd, nghttpx and h2load) [default=check]])],
[request_app=$enableval], [request_app=check]) [request_app=$enableval], [request_app=check])
AC_ARG_ENABLE([hpack-tools], AC_ARG_ENABLE([hpack-tools],
@ -185,8 +185,8 @@ if test "x$GCC" = "xyes" -o "x$CC" = "xclang" ; then
AC_DEFINE([_U_], [__attribute__((unused))], [Hint to the compiler that a function parameters is not used]) AC_DEFINE([_U_], [__attribute__((unused))], [Hint to the compiler that a function parameters is not used])
AC_DEFINE([NGHTTP2_NORETURN], [__attribute__((noreturn))], [Hint to the compiler that a function never return]) AC_DEFINE([NGHTTP2_NORETURN], [__attribute__((noreturn))], [Hint to the compiler that a function never return])
else else
AC_DEFINE([_U_], , [Hint to the compiler that a function parameters is not use AC_DEFINE([NGHTTP2_NORETURN], , [Hint to the compiler that a function never return]) AC_DEFINE([_U_], , [Hint to the compiler that a function parameter is not used])
d]) AC_DEFINE([NGHTTP2_NORETURN], , [Hint to the compiler that a function never return])
fi fi
save_CXXFLAGS="$CXXFLAGS" save_CXXFLAGS="$CXXFLAGS"
@ -352,7 +352,10 @@ fi
# libxml2 (for src/nghttp) # libxml2 (for src/nghttp)
have_libxml2=no have_libxml2=no
if test "x${request_libxml2}" != "xno"; then if test "x${request_libxml2}" != "xno"; then
AM_PATH_XML2(2.7.7, [have_libxml2=yes], [have_libxml2=no]) m4_ifdef([AM_PATH_XML2],
[AM_PATH_XML2(2.7.7, [have_libxml2=yes], [have_libxml2=no])],
[AC_MSG_WARN([configure was created without libxml2 detection macro; libxml2 detection is disabled])])
if test "x${have_libxml2}" = "xyes"; then if test "x${have_libxml2}" = "xyes"; then
AC_DEFINE([HAVE_LIBXML2], [1], [Define to 1 if you have `libxml2` library.]) AC_DEFINE([HAVE_LIBXML2], [1], [Define to 1 if you have `libxml2` library.])
fi fi
@ -651,8 +654,13 @@ AC_CHECK_FUNC([timerfd_create],
# For cygwin: we can link initgroups, so AC_CHECK_FUNCS succeeds, but # For cygwin: we can link initgroups, so AC_CHECK_FUNCS succeeds, but
# cygwin disables initgroups due to feature test macro magic with our # cygwin disables initgroups due to feature test macro magic with our
# configuration. # configuration. FreeBSD declares initgroups() in unistd.h.
AC_CHECK_DECLS([initgroups], [], [], [[#include <grp.h>]]) AC_CHECK_DECLS([initgroups], [], [], [[
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <grp.h>
]])
# Checks for epoll availability, primarily for examples/tiny-nghttpd # Checks for epoll availability, primarily for examples/tiny-nghttpd
AX_HAVE_EPOLL([have_epoll=yes], [have_epoll=no]) AX_HAVE_EPOLL([have_epoll=yes], [have_epoll=no])

View File

@ -150,15 +150,18 @@ APIDOCS= \
nghttp2_submit_window_update.rst \ nghttp2_submit_window_update.rst \
nghttp2_version.rst nghttp2_version.rst
EXTRA_DIST = \ RST_FILES = \
mkapiref.py \
README.rst \ README.rst \
programmers-guide.rst \ programmers-guide.rst \
$(APIDOCS) \
nghttp.1.rst \ nghttp.1.rst \
nghttpd.1.rst \ nghttpd.1.rst \
nghttpx.1.rst \ nghttpx.1.rst \
h2load.1.rst \ h2load.1.rst
EXTRA_DIST = \
mkapiref.py \
$(RST_FILES) \
$(APIDOCS) \
sources/index.rst \ sources/index.rst \
sources/tutorial-client.rst \ sources/tutorial-client.rst \
sources/tutorial-server.rst \ sources/tutorial-server.rst \
@ -232,7 +235,8 @@ help:
apiref.rst: \ apiref.rst: \
$(top_builddir)/lib/includes/nghttp2/nghttp2ver.h \ $(top_builddir)/lib/includes/nghttp2/nghttp2ver.h \
$(top_builddir)/lib/includes/nghttp2/nghttp2.h $(top_srcdir)/lib/includes/nghttp2/nghttp2.h
for i in $(RST_FILES); do [ -e $(builddir)/$$i ] || cp $(srcdir)/$$i $(builddir); done
$(PYTHON) $(top_srcdir)/doc/mkapiref.py \ $(PYTHON) $(top_srcdir)/doc/mkapiref.py \
apiref.rst macros.rst enums.rst types.rst . $^ apiref.rst macros.rst enums.rst types.rst . $^

View File

@ -8,7 +8,7 @@ _nghttpd()
_get_comp_words_by_ref cur prev _get_comp_words_by_ref cur prev
case $cur in case $cur in
-*) -*)
COMPREPLY=( $( compgen -W '--htdocs --verbose --daemon --echo-upload --error-gzip --push --header-table-size --padding --hexdump --max-concurrent-streams --no-tls --mime-types-file --no-content-length --workers --version --color --early-response --dh-param-file --trailer --address --verify-client --help ' -- "$cur" ) ) COMPREPLY=( $( compgen -W '--htdocs --verbose --daemon --echo-upload --error-gzip --push --header-table-size --padding --hexdump --max-concurrent-streams --no-tls --connection-window-bits --mime-types-file --no-content-length --workers --version --color --early-response --dh-param-file --trailer --address --window-bits --verify-client --help ' -- "$cur" ) )
;; ;;
*) *)
_filedir _filedir

View File

@ -8,7 +8,7 @@ _nghttpx()
_get_comp_words_by_ref cur prev _get_comp_words_by_ref cur prev
case $cur in case $cur in
-*) -*)
COMPREPLY=( $( compgen -W '--worker-read-rate --frontend-no-tls --frontend-http2-dump-response-header --backend-http1-connections-per-frontend --tls-ticket-key-file --verify-client-cacert --include --backend-request-buffer --backend-http2-connection-window-bits --conf --worker-write-burst --npn-list --fetch-ocsp-response-file --mruby-file --stream-read-timeout --tls-ticket-key-memcached --forwarded-for --accesslog-syslog --frontend-http2-read-timeout --listener-disable-timeout --frontend-http2-connection-window-bits --ciphers --strip-incoming-x-forwarded-for --private-key-passwd-file --backend-keep-alive-timeout --backend-http-proxy-uri --backend-http1-connections-per-host --rlimit-nofile --tls-dyn-rec-warmup-threshold --no-via --ocsp-update-interval --backend-write-timeout --client --tls-ticket-key-memcached-max-retry --http2-no-cookie-crumbling --worker-read-burst --client-proxy --http2-bridge --accesslog-format --errorlog-syslog --errorlog-file --http2-max-concurrent-streams --frontend-write-timeout --tls-ticket-key-cipher --read-burst --backend-ipv4 --backend-ipv6 --backend --insecure --log-level --host-rewrite --tls-proto-list --backend-http2-connections-per-worker --tls-ticket-key-memcached-interval --dh-param-file --worker-frontend-connections --syslog-facility --fastopen --no-location-rewrite --tls-session-cache-memcached --no-ocsp --backend-response-buffer --workers --add-forwarded --frontend-http2-window-bits --worker-write-rate --add-request-header --backend-tls-sni-field --subcert --help --frontend-frame-debug --pid-file --frontend-http2-dump-request-header --daemon --write-rate --altsvc --user --add-x-forwarded-for --header-field-buffer --frontend-read-timeout --tls-ticket-key-memcached-max-fail --backlog --write-burst --no-server-push --backend-http2-window-bits --padding --stream-write-timeout --cacert --forwarded-by --version --add-response-header --backend-read-timeout --frontend --accesslog-file --http2-proxy --max-header-fields --backend-no-tls --client-private-key-file --client-cert-file --accept-proxy-protocol --tls-dyn-rec-idle-timeout --verify-client --read-rate --strip-incoming-forwarded ' -- "$cur" ) ) COMPREPLY=( $( compgen -W '--worker-read-rate --frontend-no-tls --frontend-http2-dump-response-header --backend-http1-connections-per-frontend --tls-ticket-key-file --verify-client-cacert --include --max-response-header-fields --backend-request-buffer --max-request-header-fields --backend-http2-connection-window-bits --backend-tls-session-cache-per-worker --conf --worker-write-burst --npn-list --fetch-ocsp-response-file --no-http2-cipher-black-list --mruby-file --stream-read-timeout --tls-ticket-key-memcached --forwarded-for --accesslog-syslog --frontend-http2-read-timeout --listener-disable-timeout --frontend-http2-connection-window-bits --ciphers --strip-incoming-x-forwarded-for --private-key-passwd-file --backend-keep-alive-timeout --backend-http-proxy-uri --backend-http1-connections-per-host --rlimit-nofile --tls-dyn-rec-warmup-threshold --no-via --ocsp-update-interval --backend-write-timeout --client --tls-ticket-key-memcached-max-retry --http2-no-cookie-crumbling --worker-read-burst --client-proxy --http2-bridge --accesslog-format --errorlog-syslog --request-header-field-buffer --errorlog-file --http2-max-concurrent-streams --frontend-write-timeout --tls-ticket-key-cipher --read-burst --backend-ipv4 --backend-ipv6 --backend --insecure --log-level --host-rewrite --tls-proto-list --backend-http2-connections-per-worker --tls-ticket-key-memcached-interval --dh-param-file --worker-frontend-connections --backend-http1-tls --syslog-facility --fastopen --no-location-rewrite --tls-session-cache-memcached --no-ocsp --backend-response-buffer --workers --add-forwarded --frontend-http2-window-bits --worker-write-rate --add-request-header --backend-tls-sni-field --subcert --help --frontend-frame-debug --pid-file --frontend-http2-dump-request-header --daemon --write-rate --altsvc --user --add-x-forwarded-for --frontend-read-timeout --tls-ticket-key-memcached-max-fail --backlog --write-burst --no-server-push --backend-http2-window-bits --response-header-field-buffer --padding --stream-write-timeout --cacert --forwarded-by --version --add-response-header --backend-read-timeout --frontend --accesslog-file --http2-proxy --backend-no-tls --client-private-key-file --client-cert-file --accept-proxy-protocol --tls-dyn-rec-idle-timeout --verify-client --read-rate --strip-incoming-forwarded ' -- "$cur" ) )
;; ;;
*) *)
_filedir _filedir

View File

@ -41,7 +41,7 @@ import sys, os
# documentation root, use os.path.abspath to make it absolute, like shown here. # documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.')) #sys.path.insert(0, os.path.abspath('.'))
sys.path.append(os.path.abspath('_exts')) sys.path.append(os.path.abspath('@top_srcdir@/doc/_exts'))
# -- General configuration ----------------------------------------------------- # -- General configuration -----------------------------------------------------

View File

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText. .\" Man page generated from reStructuredText.
. .
.TH "H2LOAD" "1" "January 25, 2016" "1.7.0" "nghttp2" .TH "H2LOAD" "1" "February 14, 2016" "1.8.0-DEV" "nghttp2"
.SH NAME .SH NAME
h2load \- HTTP/2 benchmarking tool h2load \- HTTP/2 benchmarking tool
. .

View File

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText. .\" Man page generated from reStructuredText.
. .
.TH "NGHTTP" "1" "January 25, 2016" "1.7.0" "nghttp2" .TH "NGHTTP" "1" "February 14, 2016" "1.8.0-DEV" "nghttp2"
.SH NAME .SH NAME
nghttp \- HTTP/2 client nghttp \- HTTP/2 client
. .

View File

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText. .\" Man page generated from reStructuredText.
. .
.TH "NGHTTPD" "1" "January 25, 2016" "1.7.0" "nghttp2" .TH "NGHTTPD" "1" "February 14, 2016" "1.8.0-DEV" "nghttp2"
.SH NAME .SH NAME
nghttpd \- HTTP/2 server nghttpd \- HTTP/2 server
. .
@ -139,6 +139,17 @@ Make error response gzipped.
.UNINDENT .UNINDENT
.INDENT 0.0 .INDENT 0.0
.TP .TP
.B \-w, \-\-window\-bits=<N>
Sets the stream level initial window size to 2**<N>\-1.
.UNINDENT
.INDENT 0.0
.TP
.B \-W, \-\-connection\-window\-bits=<N>
Sets the connection level initial window size to
2**<N>\-1.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-dh\-param\-file=<PATH> .B \-\-dh\-param\-file=<PATH>
Path to file that contains DH parameters in PEM format. Path to file that contains DH parameters in PEM format.
Without this option, DHE cipher suites are not Without this option, DHE cipher suites are not

View File

@ -104,6 +104,15 @@ OPTIONS
Make error response gzipped. Make error response gzipped.
.. option:: -w, --window-bits=<N>
Sets the stream level initial window size to 2\*\*<N>-1.
.. option:: -W, --connection-window-bits=<N>
Sets the connection level initial window size to
2\*\*<N>-1.
.. option:: --dh-param-file=<PATH> .. option:: --dh-param-file=<PATH>
Path to file that contains DH parameters in PEM format. Path to file that contains DH parameters in PEM format.

View File

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText. .\" Man page generated from reStructuredText.
. .
.TH "NGHTTPX" "1" "January 25, 2016" "1.7.0" "nghttp2" .TH "NGHTTPX" "1" "February 14, 2016" "1.8.0-DEV" "nghttp2"
.SH NAME .SH NAME
nghttpx \- HTTP/2 proxy nghttpx \- HTTP/2 proxy
. .
@ -121,7 +121,9 @@ Default: \fB127.0.0.1,80\fP
Set frontend host and port. If <HOST> is \(aq*\(aq, it Set frontend host and port. If <HOST> is \(aq*\(aq, it
assumes all addresses including both IPv4 and IPv6. assumes all addresses including both IPv4 and IPv6.
UNIX domain socket can be specified by prefixing path UNIX domain socket can be specified by prefixing path
name with "unix:" (e.g., unix:/var/run/nghttpx.sock) name with "unix:" (e.g., unix:/var/run/nghttpx.sock).
This option can be used multiple times to listen to
multiple addresses.
.sp .sp
Default: \fB*,3000\fP Default: \fB*,3000\fP
.UNINDENT .UNINDENT
@ -134,13 +136,13 @@ Default: \fB512\fP
.UNINDENT .UNINDENT
.INDENT 0.0 .INDENT 0.0
.TP .TP
.B \-\-backend\-ipv4 .B \-\-backend\-address\-family=(auto|IPv4|IPv6)
Resolve backend hostname to IPv4 address only. Specify address family of backend connections. If
.UNINDENT "auto" is given, both IPv4 and IPv6 are considered. If
.INDENT 0.0 "IPv4" is given, only IPv4 address is considered. If
.TP "IPv6" is given, only IPv6 address is considered.
.B \-\-backend\-ipv6 .sp
Resolve backend hostname to IPv6 address only. Default: \fBauto\fP
.UNINDENT .UNINDENT
.INDENT 0.0 .INDENT 0.0
.TP .TP
@ -163,6 +165,22 @@ be specified by \fI\%\-\-backend\-read\-timeout\fP and
.B \-\-accept\-proxy\-protocol .B \-\-accept\-proxy\-protocol
Accept PROXY protocol version 1 on frontend connection. Accept PROXY protocol version 1 on frontend connection.
.UNINDENT .UNINDENT
.INDENT 0.0
.TP
.B \-\-backend\-no\-tls
Disable SSL/TLS on backend connections. For HTTP/2
backend connections, TLS is enabled by default. For
HTTP/1 backend connections, TLS is disabled by default,
and can be enabled by \fI\%\-\-backend\-http1\-tls\fP option. If
both \fI\%\-\-backend\-no\-tls\fP and \fI\%\-\-backend\-http1\-tls\fP options
are used, \fI\%\-\-backend\-no\-tls\fP has the precedence.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-backend\-http1\-tls
Enable SSL/TLS on backend HTTP/1 connections. See also
\fI\%\-\-backend\-no\-tls\fP option.
.UNINDENT
.SS Performance .SS Performance
.INDENT 0.0 .INDENT 0.0
.TP .TP
@ -396,19 +414,17 @@ described in OpenSSL ciphers(1).
.INDENT 0.0 .INDENT 0.0
.TP .TP
.B \-k, \-\-insecure .B \-k, \-\-insecure
Don\(aqt verify backend server\(aqs certificate if \fI\%\-p\fP, Don\(aqt verify backend server\(aqs certificate if TLS is
\fI\%\-\-client\fP or \fI\%\-\-http2\-bridge\fP are given and enabled for backend connections.
\fI\%\-\-backend\-no\-tls\fP is not given.
.UNINDENT .UNINDENT
.INDENT 0.0 .INDENT 0.0
.TP .TP
.B \-\-cacert=<PATH> .B \-\-cacert=<PATH>
Set path to trusted CA certificate file if \fI\%\-p\fP, \fI\%\-\-client\fP Set path to trusted CA certificate file used in backend
or \fI\%\-\-http2\-bridge\fP are given and \fI\%\-\-backend\-no\-tls\fP is not TLS connections. The file must be in PEM format. It
given. The file must be in PEM format. It can contain can contain multiple certificates. If the linked
multiple certificates. If the linked OpenSSL is OpenSSL is configured to load system wide certificates,
configured to load system wide certificates, they are they are loaded at startup regardless of this option.
loaded at startup regardless of this option.
.UNINDENT .UNINDENT
.INDENT 0.0 .INDENT 0.0
.TP .TP
@ -518,16 +534,27 @@ required.
.INDENT 0.0 .INDENT 0.0
.TP .TP
.B \-\-tls\-ticket\-key\-memcached=<HOST>,<PORT> .B \-\-tls\-ticket\-key\-memcached=<HOST>,<PORT>
Specify address of memcached server to store session Specify address of memcached server to get TLS ticket
cache. This enables shared TLS ticket key between keys for session resumption. This enables shared TLS
multiple nghttpx instances. nghttpx does not set TLS ticket key between multiple nghttpx instances. nghttpx
ticket key to memcached. The external ticket key does not set TLS ticket key to memcached. The external
generator is required. nghttpx just gets TLS ticket ticket key generator is required. nghttpx just gets TLS
keys from memcached, and use them, possibly replacing ticket keys from memcached, and use them, possibly
current set of keys. It is up to extern TLS ticket key replacing current set of keys. It is up to extern TLS
generator to rotate keys frequently. See "TLS SESSION ticket key generator to rotate keys frequently. See
TICKET RESUMPTION" section in manual page to know the "TLS SESSION TICKET RESUMPTION" section in manual page
data format in memcached entry. to know the data format in memcached entry.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-tls\-ticket\-key\-memcached\-address\-family=(auto|IPv4|IPv6)
Specify address family of memcached connections to get
TLS ticket keys. If "auto" is given, both IPv4 and IPv6
are considered. If "IPv4" is given, only IPv4 address
is considered. If "IPv6" is given, only IPv6 address is
considered.
.sp
Default: \fBauto\fP
.UNINDENT .UNINDENT
.INDENT 0.0 .INDENT 0.0
.TP .TP
@ -565,6 +592,24 @@ aes\-128\-cbc is used.
.UNINDENT .UNINDENT
.INDENT 0.0 .INDENT 0.0
.TP .TP
.B \-\-tls\-ticket\-key\-memcached\-tls
Enable SSL/TLS on memcached connections to get TLS
ticket keys.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-tls\-ticket\-key\-memcached\-cert\-file=<PATH>
Path to client certificate for memcached connections to
get TLS ticket keys.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-tls\-ticket\-key\-memcached\-private\-key\-file=<PATH>
Path to client private key for memcached connections to
get TLS ticket keys.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-fetch\-ocsp\-response\-file=<PATH> .B \-\-fetch\-ocsp\-response\-file=<PATH>
Path to fetch\-ocsp\-response script file. It should be Path to fetch\-ocsp\-response script file. It should be
absolute path. absolute path.
@ -592,6 +637,35 @@ multiple nghttpx instances.
.UNINDENT .UNINDENT
.INDENT 0.0 .INDENT 0.0
.TP .TP
.B \-\-tls\-session\-cache\-memcached\-address\-family=(auto|IPv4|IPv6)
Specify address family of memcached connections to store
session cache. If "auto" is given, both IPv4 and IPv6
are considered. If "IPv4" is given, only IPv4 address
is considered. If "IPv6" is given, only IPv6 address is
considered.
.sp
Default: \fBauto\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-tls\-session\-cache\-memcached\-tls
Enable SSL/TLS on memcached connections to store session
cache.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-tls\-session\-cache\-memcached\-cert\-file=<PATH>
Path to client certificate for memcached connections to
store session cache.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-tls\-session\-cache\-memcached\-private\-key\-file=<PATH>
Path to client private key for memcached connections to
store session cache.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-tls\-dyn\-rec\-warmup\-threshold=<SIZE> .B \-\-tls\-dyn\-rec\-warmup\-threshold=<SIZE>
Specify the threshold size for TLS dynamic record size Specify the threshold size for TLS dynamic record size
behaviour. During a TLS session, after the threshold behaviour. During a TLS session, after the threshold
@ -616,6 +690,21 @@ TLS HTTP/2 backends.
.sp .sp
Default: \fB1s\fP Default: \fB1s\fP
.UNINDENT .UNINDENT
.INDENT 0.0
.TP
.B \-\-no\-http2\-cipher\-black\-list
Allow black listed cipher suite on HTTP/2 connection.
See \fI\%https://tools.ietf.org/html/rfc7540#appendix\-A\fP for
the complete HTTP/2 cipher suites black list.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-backend\-tls\-session\-cache\-per\-worker=<N>
Set the maximum number of backend TLS session cache
stored per worker.
.sp
Default: \fB10000\fP
.UNINDENT
.SS HTTP/2 and SPDY .SS HTTP/2 and SPDY
.INDENT 0.0 .INDENT 0.0
.TP .TP
@ -666,11 +755,6 @@ Default: \fB16\fP
.UNINDENT .UNINDENT
.INDENT 0.0 .INDENT 0.0
.TP .TP
.B \-\-backend\-no\-tls
Disable SSL/TLS on backend connections.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-http2\-no\-cookie\-crumbling .B \-\-http2\-no\-cookie\-crumbling
Don\(aqt crumble cookie header field. Don\(aqt crumble cookie header field.
.UNINDENT .UNINDENT
@ -868,11 +952,12 @@ Specify the parameter value sent out with "by" parameter
of Forwarded header field. If "obfuscated" is given, of Forwarded header field. If "obfuscated" is given,
the string is randomly generated at startup. If "ip" is the string is randomly generated at startup. If "ip" is
given, the interface address of the connection, given, the interface address of the connection,
including port number, is sent with "by" parameter. including port number, is sent with "by" parameter. In
User can also specify the static obfuscated string. The case of UNIX domain socket, "localhost" is used instead
limitation is that it must start with "_", and only of address and port. User can also specify the static
consists of character set [A\-Za\-z0\-9._\-], as described obfuscated string. The limitation is that it must start
in RFC 7239. with "_", and only consists of character set
[A\-Za\-z0\-9._\-], as described in RFC 7239.
.sp .sp
Default: \fBobfuscated\fP Default: \fBobfuscated\fP
.UNINDENT .UNINDENT
@ -884,7 +969,8 @@ parameter of Forwarded header field. If "obfuscated" is
given, the string is randomly generated for each client given, the string is randomly generated for each client
connection. If "ip" is given, the remote client address connection. If "ip" is given, the remote client address
of the connection, without port number, is sent with of the connection, without port number, is sent with
"for" parameter. "for" parameter. In case of UNIX domain socket,
"localhost" is used instead of address.
.sp .sp
Default: \fBobfuscated\fP Default: \fBobfuscated\fP
.UNINDENT .UNINDENT
@ -940,22 +1026,42 @@ Example: \fI\%\-\-add\-response\-header\fP="foo: bar"
.UNINDENT .UNINDENT
.INDENT 0.0 .INDENT 0.0
.TP .TP
.B \-\-header\-field\-buffer=<SIZE> .B \-\-request\-header\-field\-buffer=<SIZE>
Set maximum buffer size for incoming HTTP request header Set maximum buffer size for incoming HTTP request header
field list. This is the sum of header name and value in field list. This is the sum of header name and value in
bytes. bytes. If trailer fields exist, they are counted
towards this number.
.sp .sp
Default: \fB64K\fP Default: \fB64K\fP
.UNINDENT .UNINDENT
.INDENT 0.0 .INDENT 0.0
.TP .TP
.B \-\-max\-header\-fields=<N> .B \-\-max\-request\-header\-fields=<N>
Set maximum number of incoming HTTP request header Set maximum number of incoming HTTP request header
fields, which appear in one request or response header fields. If trailer fields exist, they are counted
field list. towards this number.
.sp .sp
Default: \fB100\fP Default: \fB100\fP
.UNINDENT .UNINDENT
.INDENT 0.0
.TP
.B \-\-response\-header\-field\-buffer=<SIZE>
Set maximum buffer size for incoming HTTP response
header field list. This is the sum of header name and
value in bytes. If trailer fields exist, they are
counted towards this number.
.sp
Default: \fB64K\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-max\-response\-header\-fields=<N>
Set maximum number of incoming HTTP response header
fields. If trailer fields exist, they are counted
towards this number.
.sp
Default: \fB500\fP
.UNINDENT
.SS Debug .SS Debug
.INDENT 0.0 .INDENT 0.0
.TP .TP
@ -1203,6 +1309,10 @@ insert serialized session data to memcached with
\fBnghttpx:tls\-session\-cache:\fP + lowercased hex string of session ID \fBnghttpx:tls\-session\-cache:\fP + lowercased hex string of session ID
as a memcached entry key, with expiry time 12 hours. Session timeout as a memcached entry key, with expiry time 12 hours. Session timeout
is set to 12 hours. is set to 12 hours.
.sp
By default, connections to memcached server are not encrypted. To
enable encryption, use \fI\%\-\-tls\-session\-cache\-memcached\-tls\fP
option.
.SS TLS SESSION TICKET RESUMPTION .SS TLS SESSION TICKET RESUMPTION
.sp .sp
By default, session ticket is shared by all worker threads. The By default, session ticket is shared by all worker threads. The
@ -1247,6 +1357,10 @@ used, LEN must be 48. If
keys. The key appeared first is used as encryption key. All the keys. The key appeared first is used as encryption key. All the
remaining keys are used as decryption only. remaining keys are used as decryption only.
.sp .sp
By default, connections to memcached server are not encrypted. To
enable encryption, use \fI\%\-\-tls\-ticket\-key\-memcached\-tls\fP
option.
.sp
If \fI\%\-\-tls\-ticket\-key\-file\fP is given, encryption key is read If \fI\%\-\-tls\-ticket\-key\-file\fP is given, encryption key is read
from the given file. In this case, nghttpx does not rotate key from the given file. In this case, nghttpx does not rotate key
automatically. To rotate key, one has to restart nghttpx (see automatically. To rotate key, one has to restart nghttpx (see

View File

@ -104,7 +104,9 @@ Connections
Set frontend host and port. If <HOST> is '\*', it Set frontend host and port. If <HOST> is '\*', it
assumes all addresses including both IPv4 and IPv6. assumes all addresses including both IPv4 and IPv6.
UNIX domain socket can be specified by prefixing path UNIX domain socket can be specified by prefixing path
name with "unix:" (e.g., unix:/var/run/nghttpx.sock) name with "unix:" (e.g., unix:/var/run/nghttpx.sock).
This option can be used multiple times to listen to
multiple addresses.
Default: ``*,3000`` Default: ``*,3000``
@ -114,13 +116,14 @@ Connections
Default: ``512`` Default: ``512``
.. option:: --backend-ipv4 .. option:: --backend-address-family=(auto|IPv4|IPv6)
Resolve backend hostname to IPv4 address only. Specify address family of backend connections. If
"auto" is given, both IPv4 and IPv6 are considered. If
"IPv4" is given, only IPv4 address is considered. If
"IPv6" is given, only IPv6 address is considered.
.. option:: --backend-ipv6 Default: ``auto``
Resolve backend hostname to IPv6 address only.
.. option:: --backend-http-proxy-uri=<URI> .. option:: --backend-http-proxy-uri=<URI>
@ -141,6 +144,20 @@ Connections
Accept PROXY protocol version 1 on frontend connection. Accept PROXY protocol version 1 on frontend connection.
.. option:: --backend-no-tls
Disable SSL/TLS on backend connections. For HTTP/2
backend connections, TLS is enabled by default. For
HTTP/1 backend connections, TLS is disabled by default,
and can be enabled by :option:`--backend-http1-tls` option. If
both :option:`--backend-no-tls` and :option:`\--backend-http1-tls` options
are used, :option:`--backend-no-tls` has the precedence.
.. option:: --backend-http1-tls
Enable SSL/TLS on backend HTTP/1 connections. See also
:option:`--backend-no-tls` option.
Performance Performance
~~~~~~~~~~~ ~~~~~~~~~~~
@ -354,18 +371,16 @@ SSL/TLS
.. option:: -k, --insecure .. option:: -k, --insecure
Don't verify backend server's certificate if :option:`-p`\, Don't verify backend server's certificate if TLS is
:option:`--client` or :option:`\--http2-bridge` are given and enabled for backend connections.
:option:`--backend-no-tls` is not given.
.. option:: --cacert=<PATH> .. option:: --cacert=<PATH>
Set path to trusted CA certificate file if :option:`-p`\, :option:`--client` Set path to trusted CA certificate file used in backend
or :option:`--http2-bridge` are given and :option:`\--backend-no-tls` is not TLS connections. The file must be in PEM format. It
given. The file must be in PEM format. It can contain can contain multiple certificates. If the linked
multiple certificates. If the linked OpenSSL is OpenSSL is configured to load system wide certificates,
configured to load system wide certificates, they are they are loaded at startup regardless of this option.
loaded at startup regardless of this option.
.. option:: --private-key-passwd-file=<PATH> .. option:: --private-key-passwd-file=<PATH>
@ -463,16 +478,26 @@ SSL/TLS
.. option:: --tls-ticket-key-memcached=<HOST>,<PORT> .. option:: --tls-ticket-key-memcached=<HOST>,<PORT>
Specify address of memcached server to store session Specify address of memcached server to get TLS ticket
cache. This enables shared TLS ticket key between keys for session resumption. This enables shared TLS
multiple nghttpx instances. nghttpx does not set TLS ticket key between multiple nghttpx instances. nghttpx
ticket key to memcached. The external ticket key does not set TLS ticket key to memcached. The external
generator is required. nghttpx just gets TLS ticket ticket key generator is required. nghttpx just gets TLS
keys from memcached, and use them, possibly replacing ticket keys from memcached, and use them, possibly
current set of keys. It is up to extern TLS ticket key replacing current set of keys. It is up to extern TLS
generator to rotate keys frequently. See "TLS SESSION ticket key generator to rotate keys frequently. See
TICKET RESUMPTION" section in manual page to know the "TLS SESSION TICKET RESUMPTION" section in manual page
data format in memcached entry. to know the data format in memcached entry.
.. option:: --tls-ticket-key-memcached-address-family=(auto|IPv4|IPv6)
Specify address family of memcached connections to get
TLS ticket keys. If "auto" is given, both IPv4 and IPv6
are considered. If "IPv4" is given, only IPv4 address
is considered. If "IPv6" is given, only IPv6 address is
considered.
Default: ``auto``
.. option:: --tls-ticket-key-memcached-interval=<DURATION> .. option:: --tls-ticket-key-memcached-interval=<DURATION>
@ -504,6 +529,21 @@ SSL/TLS
either aes-128-cbc or aes-256-cbc. By default, either aes-128-cbc or aes-256-cbc. By default,
aes-128-cbc is used. aes-128-cbc is used.
.. option:: --tls-ticket-key-memcached-tls
Enable SSL/TLS on memcached connections to get TLS
ticket keys.
.. option:: --tls-ticket-key-memcached-cert-file=<PATH>
Path to client certificate for memcached connections to
get TLS ticket keys.
.. option:: --tls-ticket-key-memcached-private-key-file=<PATH>
Path to client private key for memcached connections to
get TLS ticket keys.
.. option:: --fetch-ocsp-response-file=<PATH> .. option:: --fetch-ocsp-response-file=<PATH>
Path to fetch-ocsp-response script file. It should be Path to fetch-ocsp-response script file. It should be
@ -527,6 +567,31 @@ SSL/TLS
cache. This enables shared session cache between cache. This enables shared session cache between
multiple nghttpx instances. multiple nghttpx instances.
.. option:: --tls-session-cache-memcached-address-family=(auto|IPv4|IPv6)
Specify address family of memcached connections to store
session cache. If "auto" is given, both IPv4 and IPv6
are considered. If "IPv4" is given, only IPv4 address
is considered. If "IPv6" is given, only IPv6 address is
considered.
Default: ``auto``
.. option:: --tls-session-cache-memcached-tls
Enable SSL/TLS on memcached connections to store session
cache.
.. option:: --tls-session-cache-memcached-cert-file=<PATH>
Path to client certificate for memcached connections to
store session cache.
.. option:: --tls-session-cache-memcached-private-key-file=<PATH>
Path to client private key for memcached connections to
store session cache.
.. option:: --tls-dyn-rec-warmup-threshold=<SIZE> .. option:: --tls-dyn-rec-warmup-threshold=<SIZE>
Specify the threshold size for TLS dynamic record size Specify the threshold size for TLS dynamic record size
@ -551,6 +616,19 @@ SSL/TLS
Default: ``1s`` Default: ``1s``
.. option:: --no-http2-cipher-black-list
Allow black listed cipher suite on HTTP/2 connection.
See https://tools.ietf.org/html/rfc7540#appendix-A for
the complete HTTP/2 cipher suites black list.
.. option:: --backend-tls-session-cache-per-worker=<N>
Set the maximum number of backend TLS session cache
stored per worker.
Default: ``10000``
HTTP/2 and SPDY HTTP/2 and SPDY
~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~
@ -596,10 +674,6 @@ HTTP/2 and SPDY
Default: ``16`` Default: ``16``
.. option:: --backend-no-tls
Disable SSL/TLS on backend connections.
.. option:: --http2-no-cookie-crumbling .. option:: --http2-no-cookie-crumbling
Don't crumble cookie header field. Don't crumble cookie header field.
@ -773,11 +847,12 @@ HTTP
of Forwarded header field. If "obfuscated" is given, of Forwarded header field. If "obfuscated" is given,
the string is randomly generated at startup. If "ip" is the string is randomly generated at startup. If "ip" is
given, the interface address of the connection, given, the interface address of the connection,
including port number, is sent with "by" parameter. including port number, is sent with "by" parameter. In
User can also specify the static obfuscated string. The case of UNIX domain socket, "localhost" is used instead
limitation is that it must start with "_", and only of address and port. User can also specify the static
consists of character set [A-Za-z0-9._-], as described obfuscated string. The limitation is that it must start
in RFC 7239. with "_", and only consists of character set
[A-Za-z0-9._-], as described in RFC 7239.
Default: ``obfuscated`` Default: ``obfuscated``
@ -788,7 +863,8 @@ HTTP
given, the string is randomly generated for each client given, the string is randomly generated for each client
connection. If "ip" is given, the remote client address connection. If "ip" is given, the remote client address
of the connection, without port number, is sent with of the connection, without port number, is sent with
"for" parameter. "for" parameter. In case of UNIX domain socket,
"localhost" is used instead of address.
Default: ``obfuscated`` Default: ``obfuscated``
@ -836,22 +912,40 @@ HTTP
used several times to specify multiple header fields. used several times to specify multiple header fields.
Example: :option:`--add-response-header`\="foo: bar" Example: :option:`--add-response-header`\="foo: bar"
.. option:: --header-field-buffer=<SIZE> .. option:: --request-header-field-buffer=<SIZE>
Set maximum buffer size for incoming HTTP request header Set maximum buffer size for incoming HTTP request header
field list. This is the sum of header name and value in field list. This is the sum of header name and value in
bytes. bytes. If trailer fields exist, they are counted
towards this number.
Default: ``64K`` Default: ``64K``
.. option:: --max-header-fields=<N> .. option:: --max-request-header-fields=<N>
Set maximum number of incoming HTTP request header Set maximum number of incoming HTTP request header
fields, which appear in one request or response header fields. If trailer fields exist, they are counted
field list. towards this number.
Default: ``100`` Default: ``100``
.. option:: --response-header-field-buffer=<SIZE>
Set maximum buffer size for incoming HTTP response
header field list. This is the sum of header name and
value in bytes. If trailer fields exist, they are
counted towards this number.
Default: ``64K``
.. option:: --max-response-header-fields=<N>
Set maximum number of incoming HTTP response header
fields. If trailer fields exist, they are counted
towards this number.
Default: ``500``
Debug Debug
~~~~~ ~~~~~
@ -1091,6 +1185,10 @@ insert serialized session data to memcached with
as a memcached entry key, with expiry time 12 hours. Session timeout as a memcached entry key, with expiry time 12 hours. Session timeout
is set to 12 hours. is set to 12 hours.
By default, connections to memcached server are not encrypted. To
enable encryption, use :option:`--tls-session-cache-memcached-tls`
option.
TLS SESSION TICKET RESUMPTION TLS SESSION TICKET RESUMPTION
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -1130,6 +1228,10 @@ used, LEN must be 48. If
keys. The key appeared first is used as encryption key. All the keys. The key appeared first is used as encryption key. All the
remaining keys are used as decryption only. remaining keys are used as decryption only.
By default, connections to memcached server are not encrypted. To
enable encryption, use :option:`--tls-ticket-key-memcached-tls`
option.
If :option:`--tls-ticket-key-file` is given, encryption key is read If :option:`--tls-ticket-key-file` is given, encryption key is read
from the given file. In this case, nghttpx does not rotate key from the given file. In this case, nghttpx does not rotate key
automatically. To rotate key, one has to restart nghttpx (see automatically. To rotate key, one has to restart nghttpx (see
@ -1372,6 +1474,12 @@ addresses:
App.new App.new
NOTES
-----
1. nghttpx - HTTP/2 proxy - HOW-TO
https://nghttp2.org/documentation/nghttpx-howto.html
SEE ALSO SEE ALSO
-------- --------

View File

@ -150,6 +150,10 @@ insert serialized session data to memcached with
as a memcached entry key, with expiry time 12 hours. Session timeout as a memcached entry key, with expiry time 12 hours. Session timeout
is set to 12 hours. is set to 12 hours.
By default, connections to memcached server are not encrypted. To
enable encryption, use :option:`--tls-session-cache-memcached-tls`
option.
TLS SESSION TICKET RESUMPTION TLS SESSION TICKET RESUMPTION
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -189,6 +193,10 @@ used, LEN must be 48. If
keys. The key appeared first is used as encryption key. All the keys. The key appeared first is used as encryption key. All the
remaining keys are used as decryption only. remaining keys are used as decryption only.
By default, connections to memcached server are not encrypted. To
enable encryption, use :option:`--tls-ticket-key-memcached-tls`
option.
If :option:`--tls-ticket-key-file` is given, encryption key is read If :option:`--tls-ticket-key-file` is given, encryption key is read
from the given file. In this case, nghttpx does not rotate key from the given file. In this case, nghttpx does not rotate key
automatically. To rotate key, one has to restart nghttpx (see automatically. To rotate key, one has to restart nghttpx (see

View File

@ -1,6 +1,62 @@
Programmers' Guide Programmers' Guide
================== ==================
Architecture
------------
The most notable point in nghttp2 library architecture is it does not
perform any I/O. nghttp2 only performs HTTP/2 protocol stuff based on
input byte strings. It will calls callback functions set by
applications while processing input. The output of nghttp2 is just
byte string. An application is responsible to send these output to
the remote peer. The callback functions may be called while producing
output.
Not doing I/O makes embedding nghttp2 library in the existing code
base very easy. Usually, the existing applications have its own I/O
event loops. It is very hard to use nghttp2 in that situation if
nghttp2 does its own I/O. It also makes light weight language wrapper
for nghttp2 easy with the same reason. The down side is that an
application author has to write more code to write complete
application using nghttp2. This is especially true for simple "toy"
application. For the real applications, however, this is not the
case. This is because you probably want to support HTTP/1 which
nghttp2 does not provide, and to do that, you will need to write your
own HTTP/1 stack or use existing third-party library, and bind them
together with nghttp2 and I/O event loop. In this point, not
performing I/O in nghttp2 has more point than doing it.
The primary object that an application uses is :type:`nghttp2_session`
object, which is opaque struct and its details are hidden in order to
ensure the upgrading its internal architecture without breaking the
backward compatibility. An application can set callbacks to
:type:`nghttp2_session` object through the dedicated object and
functions, and it also interacts with it via many API function calls.
An application can create as many :type:`nghttp2_session` object as it
wants. But single :type:`nghttp2_session` object must be used by a
single thread at the same time. This is not so hard to enforce since
most event-based architecture applicatons use is single thread per
core, and handling one connection I/O is done by single thread.
To feed input to :type:`nghttp2_session` object, one can use
`nghttp2_session_recv()` or `nghttp2_session_mem_recv()` functions.
They behave similarly, and the difference is that
`nghttp2_session_recv()` will use :type:`nghttp2_read_callback` to get
input. On the other hand, `nghttp2_session_mem_recv()` will take
input as its parameter. If in doubt, use `nghttp2_session_mem_recv()`
since it is simpler, and could be faster since it avoids calling
callback function.
To get output from :type:`nghttp2_session` object, one can use
`nghttp2_session_send()` or `nghttp2_session_mem_send()`. The
difference between them is that the former uses
:type:`nghttp2_send_callback` to pass output to an application. On
the other hand, the latter returns the output to the caller. If in
doubt, use `nghttp2_session_mem_send()` since it is simpler. But
`nghttp2_session_send()` might be easier to use if the output buffer
an application has is fixed sized.
Includes Includes
-------- --------

View File

@ -1,33 +1,43 @@
.. program:: h2load
h2load - HTTP/2 benchmarking tool - HOW-TO h2load - HTTP/2 benchmarking tool - HOW-TO
========================================== ==========================================
h2load is benchmarking tool for HTTP/2 and HTTP/1.1. If built with :doc:`h2load.1` is benchmarking tool for HTTP/2 and HTTP/1.1. If
spdylay (http://tatsuhiro-t.github.io/spdylay/) library, it also built with spdylay (http://tatsuhiro-t.github.io/spdylay/) library, it
supports SPDY protocol. It supports SSL/TLS and clear text for all also supports SPDY protocol. It supports SSL/TLS and clear text for
supported protocols. all supported protocols.
Compiling from source
---------------------
h2load is compiled alongside nghttp2 and requires that the
``--enable-apps`` flag is passed to ``./configure`` and `required
dependencies <https://github.com/tatsuhiro-t/nghttp2#requirements>`_
are available during compilation. For details on compiling, see
`nghttp2: Building from Git
<https://github.com/tatsuhiro-t/nghttp2#building-from-git>`_.
Basic Usage Basic Usage
----------- -----------
In order to set benchmark settings, specify following 3 options. In order to set benchmark settings, specify following 3 options.
``-n`` :option:`-n`
The number of total requests. Default: 1 The number of total requests. Default: 1
``-c`` :option:`-c`
The number of concurrent clients. Default: 1 The number of concurrent clients. Default: 1
``-m`` :option:`-m`
The max concurrent streams to issue per client. The max concurrent streams to issue per client. Default: 1
If ``auto`` is given, the number of given URIs is used.
Default: ``auto``
For SSL/TLS connection, the protocol will be negotiated via ALPN/NPN. For SSL/TLS connection, the protocol will be negotiated via ALPN/NPN.
You can set specific protocols in ``--npn-list`` option. For You can set specific protocols in :option:`--npn-list` option. For
cleartext connection, the default protocol is HTTP/2. To change the cleartext connection, the default protocol is HTTP/2. To change the
protocol in cleartext connection, use ``--no-tls-proto`` option. For protocol in cleartext connection, use :option:`--no-tls-proto` option.
convenience, ``--h1`` option forces HTTP/1.1 for both cleartext and For convenience, :option:`--h1` option forces HTTP/1.1 for both
SSL/TLS connections. cleartext and SSL/TLS connections.
Here is a command-line to perform benchmark to URI \https://localhost Here is a command-line to perform benchmark to URI \https://localhost
using total 100000 requests, 100 concurrent clients and 10 max using total 100000 requests, 100 concurrent clients and 10 max
@ -62,11 +72,11 @@ benchmarking results. By default, h2load uses large enough flow
control window, which effectively disables flow control. To adjust control window, which effectively disables flow control. To adjust
receiver flow control window size, there are following options: receiver flow control window size, there are following options:
``-w`` :option:`-w`
Sets the stream level initial window size to Sets the stream level initial window size to
(2**<N>)-1. For SPDY, 2**<N> is used instead. (2**<N>)-1. For SPDY, 2**<N> is used instead.
``-W`` :option:`-W`
Sets the connection level initial window size to Sets the connection level initial window size to
(2**<N>)-1. For SPDY, if <N> is strictly less (2**<N>)-1. For SPDY, if <N> is strictly less
than 16, this option is ignored. Otherwise than 16, this option is ignored. Otherwise
@ -76,17 +86,17 @@ Multi-Threading
--------------- ---------------
Sometimes benchmarking client itself becomes a bottleneck. To remedy Sometimes benchmarking client itself becomes a bottleneck. To remedy
this situation, use ``-t`` option to specify the number of native this situation, use :option:`-t` option to specify the number of native
thread to use. thread to use.
``-t`` :option:`-t`
The number of native threads. Default: 1 The number of native threads. Default: 1
Selecting protocol for clear text Selecting protocol for clear text
--------------------------------- ---------------------------------
By default, if \http:// URI is given, HTTP/2 protocol is used. To By default, if \http:// URI is given, HTTP/2 protocol is used. To
change the protocol to use for clear text, use ``-p`` option. change the protocol to use for clear text, use :option:`-p` option.
Multiple URIs Multiple URIs
------------- -------------
@ -97,3 +107,12 @@ If multiple URIs are specified, they are used in round robin manner.
Please note that h2load uses scheme, host and port in the first URI Please note that h2load uses scheme, host and port in the first URI
and ignores those parts in the rest of the URIs. and ignores those parts in the rest of the URIs.
UNIX domain socket
------------------
To request against UNIX domain socket, use :option:`--base-uri`, and
specify ``unix:`` followed by the path to UNIX domain socket. For
example, if UNIX domain socket is ``/tmp/nghttpx.sock``, use
``--base-uri=unix:/tmp/nghttpx.sock``. h2load uses scheme, host and
port in the first URI in command-line or input file.

View File

@ -1,30 +1,33 @@
.. program:: nghttpx
nghttpx - HTTP/2 proxy - HOW-TO nghttpx - HTTP/2 proxy - HOW-TO
=============================== ===============================
nghttpx is a proxy translating protocols between HTTP/2 and other :doc:`nghttpx.1` is a proxy translating protocols between HTTP/2 and
protocols (e.g., HTTP/1, SPDY). It operates in several modes and each other protocols (e.g., HTTP/1, SPDY). It operates in several modes
mode may require additional programs to work with. This article and each mode may require additional programs to work with. This
describes each operation mode and explains the intended use-cases. It article describes each operation mode and explains the intended
also covers some useful options later. use-cases. It also covers some useful options later.
Default mode Default mode
------------ ------------
If nghttpx is invoked without any ``-s``, ``-p`` and ``--client``, it If nghttpx is invoked without any :option:`--http2-proxy`,
operates in default mode. In this mode, nghttpx frontend listens for :option:`--client`, and :option:`--client-proxy`, it operates in
HTTP/2 requests and translates them to HTTP/1 requests. Thus it works default mode. In this mode, nghttpx frontend listens for HTTP/2
as reverse proxy (gateway) for HTTP/2 clients to HTTP/1 web server. requests and translates them to HTTP/1 requests. Thus it works as
This is also known as "HTTP/2 router". HTTP/1 requests are also reverse proxy (gateway) for HTTP/2 clients to HTTP/1 web server. This
supported in frontend as a fallback. If nghttpx is linked with is also known as "HTTP/2 router". HTTP/1 requests are also supported
spdylay library and frontend connection is SSL/TLS, the frontend also in frontend as a fallback. If nghttpx is linked with spdylay library
supports SPDY protocol. and frontend connection is SSL/TLS, the frontend also supports SPDY
protocol.
By default, this mode's frontend connection is encrypted using By default, this mode's frontend connection is encrypted using
SSL/TLS. So server's private key and certificate must be supplied to SSL/TLS. So server's private key and certificate must be supplied to
the command line (or through configuration file). In this case, the the command line (or through configuration file). In this case, the
frontend protocol selection will be done via ALPN or NPN. frontend protocol selection will be done via ALPN or NPN.
With ``--frontend-no-tls`` option, user can turn off SSL/TLS in With :option:`--frontend-no-tls` option, user can turn off SSL/TLS in
frontend connection. In this case, SPDY protocol is not available frontend connection. In this case, SPDY protocol is not available
even if spdylay library is liked to nghttpx. HTTP/2 and HTTP/1 are even if spdylay library is liked to nghttpx. HTTP/2 and HTTP/1 are
available on the frontend and a HTTP/1 connection can be upgraded to available on the frontend and a HTTP/1 connection can be upgraded to
@ -32,8 +35,9 @@ HTTP/2 using HTTP Upgrade. Starting HTTP/2 connection by sending
HTTP/2 connection preface is also supported. HTTP/2 connection preface is also supported.
By default, backend HTTP/1 connections are not encrypted. To enable By default, backend HTTP/1 connections are not encrypted. To enable
TLS on HTTP/1 backend connections, use ``--backend-http1-tls`` option. TLS on HTTP/1 backend connections, use :option:`--backend-http1-tls`
This applies to all mode whose backend connections are HTTP/1. option. This applies to all mode whose backend connections are
HTTP/1.
The backend is supposed to be HTTP/1 Web server. For example, to make The backend is supposed to be HTTP/1 Web server. For example, to make
nghttpx listen to encrypted HTTP/2 requests at port 8443, and a nghttpx listen to encrypted HTTP/2 requests at port 8443, and a
@ -50,19 +54,19 @@ example, you can send GET request to the server using nghttp::
HTTP/2 proxy mode HTTP/2 proxy mode
----------------- -----------------
If nghttpx is invoked with ``-s`` option, it operates in HTTP/2 proxy If nghttpx is invoked with :option:`--http2-proxy` (or its shorthand
mode. The supported protocols in frontend and backend connections are :option:`-s`) option, it operates in HTTP/2 proxy mode. The supported
the same in `default mode`_. The difference is that this mode acts protocols in frontend and backend connections are the same in `default
like forward proxy and assumes the backend is HTTP/1 proxy server mode`_. The difference is that this mode acts like forward proxy and
(e.g., squid, traffic server). So HTTP/1 request must include assumes the backend is HTTP/1 proxy server (e.g., squid, traffic
absolute URI in request line. server). So HTTP/1 request must include absolute URI in request line.
By default, frontend connection is encrypted. So this mode is also By default, frontend connection is encrypted. So this mode is also
called secure proxy. If nghttpx is linked with spdylay, it supports called secure proxy. If nghttpx is linked with spdylay, it supports
SPDY protocols and it works as so called SPDY proxy. SPDY protocols and it works as so called SPDY proxy.
With ``--frontend-no-tls`` option, SSL/TLS is turned off in frontend With :option:`--frontend-no-tls` option, SSL/TLS is turned off in
connection, so the connection gets insecure. frontend connection, so the connection gets insecure.
The backend must be HTTP/1 proxy server. nghttpx supports multiple The backend must be HTTP/1 proxy server. nghttpx supports multiple
backend server addresses. It translates incoming requests to HTTP/1 backend server addresses. It translates incoming requests to HTTP/1
@ -96,7 +100,9 @@ Chromium require valid certificate for secure proxy.
For Firefox, open Preference window and select Advanced then click For Firefox, open Preference window and select Advanced then click
Network tab. Clicking Connection Settings button will show the Network tab. Clicking Connection Settings button will show the
dialog. Select "Automatic proxy configuration URL" and enter the path dialog. Select "Automatic proxy configuration URL" and enter the path
to proxy.pac file, something like this:: to proxy.pac file, something like this:
.. code-block:: text
file:///path/to/proxy.pac file:///path/to/proxy.pac
@ -112,25 +118,27 @@ configuration items to edit::
CONFIG proxy.config.url_remap.remap_required INT 0 CONFIG proxy.config.url_remap.remap_required INT 0
Consult Traffic server `documentation Consult Traffic server `documentation
<https://docs.trafficserver.apache.org/en/latest/admin/forward-proxy.en.html>`_ <http://trafficserver.readthedocs.org/en/latest/admin-guide/configuration/transparent-forward-proxying.en.html>`_
to know how to configure traffic server as forward proxy and its to know how to configure traffic server as forward proxy and its
security implications. security implications.
Client mode Client mode
----------- -----------
If nghttpx is invoked with ``--client`` option, it operates in client If nghttpx is invoked with :option:`--client` option, it operates in
mode. In this mode, nghttpx listens for plain, unencrypted HTTP/2 and client mode. In this mode, nghttpx listens for plain, unencrypted
HTTP/1 requests and translates them to encrypted HTTP/2 requests to HTTP/2 and HTTP/1 requests and translates them to encrypted HTTP/2
the backend. User cannot enable SSL/TLS in frontend connection. requests to the backend. User cannot enable SSL/TLS in frontend
connection.
HTTP/1 frontend connection can be upgraded to HTTP/2 using HTTP HTTP/1 frontend connection can be upgraded to HTTP/2 using HTTP
Upgrade. To disable SSL/TLS in backend connection, use Upgrade. To disable SSL/TLS in backend connection, use
``--backend-no-tls`` option. :option:`--backend-no-tls` option.
By default, the number of backend HTTP/2 connections per worker By default, the number of backend HTTP/2 connections per worker
(thread) is determined by number of ``-b`` option. To adjust this (thread) is determined by number of :option:`--backend` option. To
value, use ``--backend-http2-connections-per-worker`` option. adjust this value, use
:option:`--backend-http2-connections-per-worker` option.
The backend server is supporsed to be a HTTP/2 web server (e.g., The backend server is supporsed to be a HTTP/2 web server (e.g.,
nghttpd). The one use-case of this mode is utilize existing HTTP/1 nghttpd). The one use-case of this mode is utilize existing HTTP/1
@ -142,9 +150,10 @@ mode to access to that web server::
.. note:: .. note::
You may need ``-k`` option if HTTP/2 server enables SSL/TLS and You may need :option:`--insecure` (or its shorthand :option:`-k`)
its certificate is self-signed. But please note that it is option if HTTP/2 server enables SSL/TLS and its certificate is
insecure. self-signed. But please note that it is insecure, and you should
know what you are doing.
Then you can use curl to access HTTP/2 server via nghttpx:: Then you can use curl to access HTTP/2 server via nghttpx::
@ -153,18 +162,19 @@ Then you can use curl to access HTTP/2 server via nghttpx::
Client proxy mode Client proxy mode
----------------- -----------------
If nghttpx is invoked with ``-p`` option, it operates in client proxy If nghttpx is invoked with :option:`--client-proxy` (or its shorthand
mode. This mode behaves like `client mode`_, but it works like :option:`-p`) option, it operates in client proxy mode. This mode
forward proxy. So HTTP/1 request must include absolute URI in request behaves like `client mode`_, but it works like forward proxy. So
line. HTTP/1 request must include absolute URI in request line.
HTTP/1 frontend connection can be upgraded to HTTP/2 using HTTP HTTP/1 frontend connection can be upgraded to HTTP/2 using HTTP
Upgrade. To disable SSL/TLS in backend connection, use Upgrade. To disable SSL/TLS in backend connection, use
``--backend-no-tls`` option. :option:`--backend-no-tls` option.
By default, the number of backend HTTP/2 connections per worker By default, the number of backend HTTP/2 connections per worker
(thread) is determined by number of ``-b`` option. To adjust this (thread) is determined by number of :option:`--backend` option. To
value, use ``--backend-http2-connections-per-worker`` option. adjust this value, use
:option:`--backend-http2-connections-per-worker` option.
The backend server must be a HTTP/2 proxy. You can use nghttpx in The backend server must be a HTTP/2 proxy. You can use nghttpx in
`HTTP/2 proxy mode`_ as backend server. The one use-case of this mode `HTTP/2 proxy mode`_ as backend server. The one use-case of this mode
@ -182,8 +192,9 @@ that server, invoke nghttpx like this::
.. note:: .. note::
You may need ``-k`` option if HTTP/2 server's certificate is You may need :option:`--insecure` (or its shorthand :option:`-k`)
self-signed. But please note that it is insecure. option if HTTP/2 server's certificate is self-signed. But please
note that it is insecure, and you should know what you are doing.
Then you can use curl to issue HTTP request via HTTP/2 proxy:: Then you can use curl to issue HTTP request via HTTP/2 proxy::
@ -195,23 +206,24 @@ proxy.
HTTP/2 bridge mode HTTP/2 bridge mode
------------------ ------------------
If nghttpx is invoked with ``--http2-bridge`` option, it operates in If nghttpx is invoked with :option:`--http2-bridge` option, it
HTTP/2 bridge mode. The supported protocols in frontend connections operates in HTTP/2 bridge mode. The supported protocols in frontend
are the same in `default mode`_. The protocol in backend is HTTP/2 connections are the same in `default mode`_. The protocol in backend
only. is HTTP/2 only.
With ``--frontend-no-tls`` option, SSL/TLS is turned off in frontend With :option:`--frontend-no-tls` option, SSL/TLS is turned off in
connection, so the connection gets insecure. To disable SSL/TLS in frontend connection, so the connection gets insecure. To disable
backend connection, use ``--backend-no-tls`` option. SSL/TLS in backend connection, use :option:`--backend-no-tls` option.
By default, the number of backend HTTP/2 connections per worker By default, the number of backend HTTP/2 connections per worker
(thread) is determined by number of ``-b`` option. To adjust this (thread) is determined by number of :option:`--backend` option. To
value, use ``--backend-http2-connections-per-worker`` option. adjust this value, use
:option:`--backend-http2-connections-per-worker` option.
The backend server is supporsed to be a HTTP/2 web server or HTTP/2 The backend server is supporsed to be a HTTP/2 web server or HTTP/2
proxy. If backend server is HTTP/2 proxy, use proxy. If backend server is HTTP/2 proxy, use
``--no-location-rewrite`` and ``--no-host-rewrite`` options to disable :option:`--no-location-rewrite` option to disable rewriting
rewriting location, host and :authority header field. ``Location`` header field.
The use-case of this mode is aggregate the incoming connections to one The use-case of this mode is aggregate the incoming connections to one
HTTP/2 connection. One backend HTTP/2 connection is created per HTTP/2 connection. One backend HTTP/2 connection is created per
@ -222,26 +234,48 @@ Disable SSL/TLS
In `default mode`_, `HTTP/2 proxy mode`_ and `HTTP/2 bridge mode`_, In `default mode`_, `HTTP/2 proxy mode`_ and `HTTP/2 bridge mode`_,
frontend connections are encrypted with SSL/TLS by default. To turn frontend connections are encrypted with SSL/TLS by default. To turn
off SSL/TLS, use ``--frontend-no-tls`` option. If this option is off SSL/TLS, use :option:`--frontend-no-tls` option. If this option
used, the private key and certificate are not required to run nghttpx. is used, the private key and certificate are not required to run
nghttpx.
In `client mode`_, `client proxy mode`_ and `HTTP/2 bridge mode`_, In `client mode`_, `client proxy mode`_ and `HTTP/2 bridge mode`_,
backend connections are encrypted with SSL/TLS by default. To turn backend connections are encrypted with SSL/TLS by default. To turn
off SSL/TLS, use ``--backend-no-tls`` option. off SSL/TLS, use :option:`--backend-no-tls` option.
Enable SSL/TLS on HTTP/1 backend
--------------------------------
In all modes which use HTTP/1 as backend protocol, backend HTTP/1
connection is not encrypted by default. To enable encryption, use
:option:`--backend-http1-tls` option.
Enable SSL/TLS on memcached connection
--------------------------------------
By default, memcached connection is not encrypted. To enable
encryption, use :option:`--tls-ticket-key-memcached-tls` for TLS
ticket key, and use :option:`--tls-session-cache-memcached-tls` for
TLS session cache.
Specifying additional server certificates
-----------------------------------------
nghttpx accepts additional server private key and certificate pairs
using :option:`--subcert` option. It can be used multiple times.
Specifying additional CA certificate Specifying additional CA certificate
------------------------------------ ------------------------------------
By default, nghttpx tries to read CA certificate from system. But By default, nghttpx tries to read CA certificate from system. But
depending on the system you use, this may fail or is not supported. depending on the system you use, this may fail or is not supported.
To specify CA certificate manually, use ``--cacert`` option. The To specify CA certificate manually, use :option:`--cacert` option.
specified file must be PEM format and can contain multiple The specified file must be PEM format and can contain multiple
certificates. certificates.
By default, nghttpx validates server's certificate. If you want to By default, nghttpx validates server's certificate. If you want to
turn off this validation, knowing this is really insecure and what you turn off this validation, knowing this is really insecure and what you
are doing, you can use ``-k`` option to disable certificate are doing, you can use :option:`--insecure` option to disable
validation. certificate validation.
Read/write rate limit Read/write rate limit
--------------------- ---------------------
@ -250,9 +284,9 @@ nghttpx supports transfer rate limiting on frontend connections. You
can do rate limit per frontend connection for reading and writing can do rate limit per frontend connection for reading and writing
individually. individually.
To perform rate limit for reading, use ``--read-rate`` and To perform rate limit for reading, use :option:`--read-rate` and
``--read-burst`` options. For writing, use ``--write-rate`` and :option:`--read-burst` options. For writing, use
``--write-burst``. :option:`--write-rate` and :option:`--write-burst`.
Please note that rate limit is performed on top of TCP and nothing to Please note that rate limit is performed on top of TCP and nothing to
do with HTTP/2 flow control. do with HTTP/2 flow control.
@ -294,14 +328,64 @@ Re-opening log files
When rotating log files, it is desirable to re-open log files after When rotating log files, it is desirable to re-open log files after
log rotation daemon renamed existing log files. To tell nghttpx to log rotation daemon renamed existing log files. To tell nghttpx to
re-open log files, send USR1 signal to nghttpx process. It will re-open log files, send USR1 signal to nghttpx process. It will
re-open files specified by ``--accesslog-file`` and re-open files specified by :option:`--accesslog-file` and
``--errorlog-file`` options. :option:`--errorlog-file` options.
Multiple backend addresses Multiple backend addresses
-------------------------- --------------------------
nghttpx supports multiple backend addresses. To specify them, just nghttpx supports multiple backend addresses. To specify them, just
use ``-b`` option repeatedly. For example, to use backend1:8080 and use :option:`--backend` (or its shorthand :option:`-b`) option
backend2:8080, use command-line like this: ``-bbackend1,8080 repeatedly. For example, to use ``192.168.0.10:8080`` and
-bbackend2,8080``. For HTTP/2 backend, see also ``192.168.0.11:8080``, use command-line like this:
``--backend-http2-connections-per-worker`` option. ``-b192.168.0.10,8080 -b192.168.0.11,8080``. In configuration file,
this looks like:
.. code-block:: text
backend=192.168.0.10,8080
backend=192.168.0.11,8008
nghttpx can route request to different backend according to request
host and path. For example, to route request destined to host
``doc.example.com`` to backend server ``docserv:3000``, you can write
like so:
.. code-block:: text
backend=docserv,3000;doc.example.com/
When you write this option in command-line, you should enclose
argument with single or double quotes, since the character ``;`` has a
special meaning in shell.
To route, request to request path whose prefix is ``/foo`` to backend
server ``[::1]:8080``, you can write like so:
.. code-block:: text
backend=::1,8080;/foo
Of course, you can specify both host and request path at the same
time.
One important thing you have to remember is that we have to specify
default routing pattern for so called "catch all" pattern. To write
"catch all" pattern, just specify backend server address, without
pattern.
Usually, host is the value of ``Host`` header field. In HTTP/2, the
value of ``:authority`` pseudo header field is used.
When you write multiple backend addresses sharing the same routing
pattern, they are used as load balancing. For example, to use 2
servers ``serv1:3000`` and ``serv2:3000`` for request host
``example.com`` and path ``/myservice``, you can write like so:
.. code-block:: text
backend=serv1,3000;example.com/myservice
backend=serv2,3000;example.com/myservice
For HTTP/2 backend, see also
:option:`--backend-http2-connections-per-worker` option.

View File

@ -289,8 +289,6 @@ static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
return 0; return 0;
} }
#define MAX_OUTLEN 4096
/* /*
* The implementation of nghttp2_on_data_chunk_recv_callback type. We * The implementation of nghttp2_on_data_chunk_recv_callback type. We
* use this function to print the received response body. * use this function to print the received response body.

View File

@ -295,7 +295,7 @@ static size_t http_date(char *buf, time_t t) {
static char date[29]; static char date[29];
static size_t datelen; static size_t datelen;
static void update_date() { datelen = http_date(date, time(NULL)); } static void update_date(void) { datelen = http_date(date, time(NULL)); }
static size_t utos(char *buf, size_t len, uint64_t n) { static size_t utos(char *buf, size_t len, uint64_t n) {
size_t nwrite = 0; size_t nwrite = 0;

View File

@ -62,11 +62,67 @@ HEADERS = [
('vary', 58), ('vary', 58),
('via', 59), ('via', 59),
('www-authenticate', 60), ('www-authenticate', 60),
('te', None), ('accept-ch', None),
('accept-datetime', None),
('accept-features', None),
('accept-patch', None),
('access-control-allow-credentials', None),
('access-control-allow-headers', None),
('access-control-allow-methods', None),
('access-control-expose-headers', None),
('access-control-max-age', None),
('access-control-request-headers', None),
('access-control-request-method', None),
('alt-svc', None),
('alternates', None),
('connection', None), ('connection', None),
('content-md5', None),
('content-security-policy', None),
('content-security-policy-report-only', None),
('dnt', None),
('forwarded', None),
('front-end-https', None),
('keep-alive', None), ('keep-alive', None),
('last-event-id', None),
('negotiate', None),
('origin', None),
('p3p', None),
('pragma', None),
('proxy-connection', None), ('proxy-connection', None),
('public-key-pins', None),
('sec-websocket-extensions', None),
('sec-websocket-key', None),
('sec-websocket-origin', None),
('sec-websocket-protocol', None),
('sec-websocket-version', None),
('set-cookie2', None),
('status', None),
('tcn', None),
('te', None),
('trailer', None),
('tsv', None),
('upgrade', None), ('upgrade', None),
('upgrade-insecure-requests', None),
('variant-vary', None),
('warning', None),
('x-api-version', None),
('x-att-deviceid', None),
('x-cache', None),
('x-cache-lookup', None),
('x-content-duration', None),
('x-content-security-policy', None),
('x-content-type-options', None),
('x-dnsprefetch-control', None),
('x-forwarded-for', None),
('x-forwarded-host', None),
('x-forwarded-proto', None),
('x-frame-options', None),
('x-powered-by', None),
('x-requested-with', None),
('x-ua-compatible', None),
('x-wap-profile', None),
('x-webkit-csp', None),
('x-xss-protection', None),
] ]
def to_enum_hd(k): def to_enum_hd(k):

View File

@ -92,6 +92,7 @@ OPTIONS = [
"tls-ticket-key-cipher", "tls-ticket-key-cipher",
"host-rewrite", "host-rewrite",
"tls-session-cache-memcached", "tls-session-cache-memcached",
"tls-session-cache-memcached-tls",
"tls-ticket-key-memcached", "tls-ticket-key-memcached",
"tls-ticket-key-memcached-interval", "tls-ticket-key-memcached-interval",
"tls-ticket-key-memcached-max-retry", "tls-ticket-key-memcached-max-retry",
@ -114,7 +115,15 @@ OPTIONS = [
"max-header-fields", "max-header-fields",
"no-http2-cipher-black-list", "no-http2-cipher-black-list",
"backend-http1-tls", "backend-http1-tls",
"backend-tls-session-cache-per-worker" "backend-tls-session-cache-per-worker",
"tls-session-cache-memcached-cert-file",
"tls-session-cache-memcached-private-key-file",
"tls-session-cache-memcached-address-family",
"tls-ticket-key-memcached-tls",
"tls-ticket-key-memcached-cert-file",
"tls-ticket-key-memcached-private-key-file",
"tls-ticket-key-memcached-address-family",
"backend-address-family"
] ]
LOGVARS = [ LOGVARS = [

View File

@ -21,11 +21,14 @@
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
EXTRA_DIST = \ GO_FILES = \
nghttpx_http1_test.go \ nghttpx_http1_test.go \
nghttpx_http2_test.go \ nghttpx_http2_test.go \
nghttpx_spdy_test.go \ nghttpx_spdy_test.go \
server_tester.go \ server_tester.go
EXTRA_DIST = \
$(GO_FILES) \
server.key \ server.key \
server.crt \ server.crt \
alt-server.key \ alt-server.key \
@ -43,4 +46,5 @@ itprep-local:
go get -d -v golang.org/x/net/websocket go get -d -v golang.org/x/net/websocket
it-local: it-local:
for i in $(GO_FILES); do [ -e $(builddir)/$$i ] || cp $(srcdir)/$$i $(builddir); done
sh setenv go test -v sh setenv go test -v

View File

@ -2,4 +2,5 @@ package nghttp2
const ( const (
buildDir = "@top_builddir@" buildDir = "@top_builddir@"
sourceDir = "@top_srcdir@"
) )

View File

@ -29,7 +29,8 @@ import (
const ( const (
serverBin = buildDir + "/src/nghttpx" serverBin = buildDir + "/src/nghttpx"
serverPort = 3009 serverPort = 3009
testDir = buildDir + "/integration-tests" testDir = sourceDir + "/integration-tests"
logDir = buildDir + "/integration-tests"
) )
func pair(name, value string) hpack.HeaderField { func pair(name, value string) hpack.HeaderField {
@ -124,7 +125,7 @@ func newServerTesterInternal(args []string, t *testing.T, handler http.Handler,
// "127.0.0.1,8080" // "127.0.0.1,8080"
b := "-b" + strings.Replace(backendURL.Host, ":", ",", -1) b := "-b" + strings.Replace(backendURL.Host, ":", ",", -1)
args = append(args, fmt.Sprintf("-f127.0.0.1,%v", serverPort), b, args = append(args, fmt.Sprintf("-f127.0.0.1,%v", serverPort), b,
"--errorlog-file="+testDir+"/log.txt", "-LINFO") "--errorlog-file="+logDir+"/log.txt", "-LINFO")
authority := fmt.Sprintf("127.0.0.1:%v", serverPort) authority := fmt.Sprintf("127.0.0.1:%v", serverPort)

View File

@ -1612,6 +1612,14 @@ typedef int (*nghttp2_on_begin_headers_callback)(nghttp2_session *session,
* *
* To set this callback to :type:`nghttp2_session_callbacks`, use * To set this callback to :type:`nghttp2_session_callbacks`, use
* `nghttp2_session_callbacks_set_on_header_callback()`. * `nghttp2_session_callbacks_set_on_header_callback()`.
*
* .. warning::
*
* Application should properly limit the total buffer size to store
* incoming header fields. Without it, peer may send large number
* of header fields or large header fields to cause out of memory in
* local endpoint. Due to how HPACK works, peer can do this
* effectively without using much memory on their own.
*/ */
typedef int (*nghttp2_on_header_callback)(nghttp2_session *session, typedef int (*nghttp2_on_header_callback)(nghttp2_session *session,
const nghttp2_frame *frame, const nghttp2_frame *frame,

View File

@ -137,6 +137,26 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
return NGHTTP2_TOKEN_AGE; return NGHTTP2_TOKEN_AGE;
} }
break; break;
case 'n':
if (lstreq("tc", name, 2)) {
return NGHTTP2_TOKEN_TCN;
}
break;
case 'p':
if (lstreq("p3", name, 2)) {
return NGHTTP2_TOKEN_P3P;
}
break;
case 't':
if (lstreq("dn", name, 2)) {
return NGHTTP2_TOKEN_DNT;
}
break;
case 'v':
if (lstreq("ts", name, 2)) {
return NGHTTP2_TOKEN_TSV;
}
break;
} }
break; break;
case 4: case 4:
@ -197,16 +217,31 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
break; break;
case 6: case 6:
switch (name[5]) { switch (name[5]) {
case 'a':
if (lstreq("pragm", name, 5)) {
return NGHTTP2_TOKEN_PRAGMA;
}
break;
case 'e': case 'e':
if (lstreq("cooki", name, 5)) { if (lstreq("cooki", name, 5)) {
return NGHTTP2_TOKEN_COOKIE; return NGHTTP2_TOKEN_COOKIE;
} }
break; break;
case 'n':
if (lstreq("origi", name, 5)) {
return NGHTTP2_TOKEN_ORIGIN;
}
break;
case 'r': case 'r':
if (lstreq("serve", name, 5)) { if (lstreq("serve", name, 5)) {
return NGHTTP2_TOKEN_SERVER; return NGHTTP2_TOKEN_SERVER;
} }
break; break;
case 's':
if (lstreq("statu", name, 5)) {
return NGHTTP2_TOKEN_STATUS;
}
break;
case 't': case 't':
if (lstreq("accep", name, 5)) { if (lstreq("accep", name, 5)) {
return NGHTTP2_TOKEN_ACCEPT; return NGHTTP2_TOKEN_ACCEPT;
@ -219,6 +254,11 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
break; break;
case 7: case 7:
switch (name[6]) { switch (name[6]) {
case 'c':
if (lstreq("alt-sv", name, 6)) {
return NGHTTP2_TOKEN_ALT_SVC;
}
break;
case 'd': case 'd':
if (lstreq(":metho", name, 6)) { if (lstreq(":metho", name, 6)) {
return NGHTTP2_TOKEN__METHOD; return NGHTTP2_TOKEN__METHOD;
@ -237,6 +277,14 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
if (lstreq("upgrad", name, 6)) { if (lstreq("upgrad", name, 6)) {
return NGHTTP2_TOKEN_UPGRADE; return NGHTTP2_TOKEN_UPGRADE;
} }
if (lstreq("x-cach", name, 6)) {
return NGHTTP2_TOKEN_X_CACHE;
}
break;
case 'g':
if (lstreq("warnin", name, 6)) {
return NGHTTP2_TOKEN_WARNING;
}
break; break;
case 'h': case 'h':
if (lstreq("refres", name, 6)) { if (lstreq("refres", name, 6)) {
@ -247,6 +295,9 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
if (lstreq("refere", name, 6)) { if (lstreq("refere", name, 6)) {
return NGHTTP2_TOKEN_REFERER; return NGHTTP2_TOKEN_REFERER;
} }
if (lstreq("traile", name, 6)) {
return NGHTTP2_TOKEN_TRAILER;
}
break; break;
case 's': case 's':
if (lstreq(":statu", name, 6)) { if (lstreq(":statu", name, 6)) {
@ -295,6 +346,25 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
break; break;
} }
break; break;
case 9:
switch (name[8]) {
case 'd':
if (lstreq("forwarde", name, 8)) {
return NGHTTP2_TOKEN_FORWARDED;
}
break;
case 'e':
if (lstreq("negotiat", name, 8)) {
return NGHTTP2_TOKEN_NEGOTIATE;
}
break;
case 'h':
if (lstreq("accept-c", name, 8)) {
return NGHTTP2_TOKEN_ACCEPT_CH;
}
break;
}
break;
case 10: case 10:
switch (name[9]) { switch (name[9]) {
case 'e': case 'e':
@ -310,6 +380,11 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
return NGHTTP2_TOKEN_CONNECTION; return NGHTTP2_TOKEN_CONNECTION;
} }
break; break;
case 's':
if (lstreq("alternate", name, 9)) {
return NGHTTP2_TOKEN_ALTERNATES;
}
break;
case 't': case 't':
if (lstreq("user-agen", name, 9)) { if (lstreq("user-agen", name, 9)) {
return NGHTTP2_TOKEN_USER_AGENT; return NGHTTP2_TOKEN_USER_AGENT;
@ -324,6 +399,16 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
break; break;
case 11: case 11:
switch (name[10]) { switch (name[10]) {
case '2':
if (lstreq("set-cookie", name, 10)) {
return NGHTTP2_TOKEN_SET_COOKIE2;
}
break;
case '5':
if (lstreq("content-md", name, 10)) {
return NGHTTP2_TOKEN_CONTENT_MD5;
}
break;
case 'r': case 'r':
if (lstreq("retry-afte", name, 10)) { if (lstreq("retry-afte", name, 10)) {
return NGHTTP2_TOKEN_RETRY_AFTER; return NGHTTP2_TOKEN_RETRY_AFTER;
@ -338,16 +423,37 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
return NGHTTP2_TOKEN_CONTENT_TYPE; return NGHTTP2_TOKEN_CONTENT_TYPE;
} }
break; break;
case 'h':
if (lstreq("accept-patc", name, 11)) {
return NGHTTP2_TOKEN_ACCEPT_PATCH;
}
break;
case 'p':
if (lstreq("x-webkit-cs", name, 11)) {
return NGHTTP2_TOKEN_X_WEBKIT_CSP;
}
break;
case 's': case 's':
if (lstreq("max-forward", name, 11)) { if (lstreq("max-forward", name, 11)) {
return NGHTTP2_TOKEN_MAX_FORWARDS; return NGHTTP2_TOKEN_MAX_FORWARDS;
} }
break; break;
case 'y':
if (lstreq("variant-var", name, 11)) {
return NGHTTP2_TOKEN_VARIANT_VARY;
}
if (lstreq("x-powered-b", name, 11)) {
return NGHTTP2_TOKEN_X_POWERED_BY;
}
break;
} }
break; break;
case 13: case 13:
switch (name[12]) { switch (name[12]) {
case 'd': case 'd':
if (lstreq("last-event-i", name, 12)) {
return NGHTTP2_TOKEN_LAST_EVENT_ID;
}
if (lstreq("last-modifie", name, 12)) { if (lstreq("last-modifie", name, 12)) {
return NGHTTP2_TOKEN_LAST_MODIFIED; return NGHTTP2_TOKEN_LAST_MODIFIED;
} }
@ -356,6 +462,9 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
if (lstreq("content-rang", name, 12)) { if (lstreq("content-rang", name, 12)) {
return NGHTTP2_TOKEN_CONTENT_RANGE; return NGHTTP2_TOKEN_CONTENT_RANGE;
} }
if (lstreq("x-wap-profil", name, 12)) {
return NGHTTP2_TOKEN_X_WAP_PROFILE;
}
break; break;
case 'h': case 'h':
if (lstreq("if-none-matc", name, 12)) { if (lstreq("if-none-matc", name, 12)) {
@ -371,6 +480,9 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
if (lstreq("authorizatio", name, 12)) { if (lstreq("authorizatio", name, 12)) {
return NGHTTP2_TOKEN_AUTHORIZATION; return NGHTTP2_TOKEN_AUTHORIZATION;
} }
if (lstreq("x-api-versio", name, 12)) {
return NGHTTP2_TOKEN_X_API_VERSION;
}
break; break;
case 's': case 's':
if (lstreq("accept-range", name, 12)) { if (lstreq("accept-range", name, 12)) {
@ -381,11 +493,21 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
break; break;
case 14: case 14:
switch (name[13]) { switch (name[13]) {
case 'd':
if (lstreq("x-att-devicei", name, 13)) {
return NGHTTP2_TOKEN_X_ATT_DEVICEID;
}
break;
case 'h': case 'h':
if (lstreq("content-lengt", name, 13)) { if (lstreq("content-lengt", name, 13)) {
return NGHTTP2_TOKEN_CONTENT_LENGTH; return NGHTTP2_TOKEN_CONTENT_LENGTH;
} }
break; break;
case 'p':
if (lstreq("x-cache-looku", name, 13)) {
return NGHTTP2_TOKEN_X_CACHE_LOOKUP;
}
break;
case 't': case 't':
if (lstreq("accept-charse", name, 13)) { if (lstreq("accept-charse", name, 13)) {
return NGHTTP2_TOKEN_ACCEPT_CHARSET; return NGHTTP2_TOKEN_ACCEPT_CHARSET;
@ -396,15 +518,40 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
case 15: case 15:
switch (name[14]) { switch (name[14]) {
case 'e': case 'e':
if (lstreq("accept-datetim", name, 14)) {
return NGHTTP2_TOKEN_ACCEPT_DATETIME;
}
if (lstreq("accept-languag", name, 14)) { if (lstreq("accept-languag", name, 14)) {
return NGHTTP2_TOKEN_ACCEPT_LANGUAGE; return NGHTTP2_TOKEN_ACCEPT_LANGUAGE;
} }
if (lstreq("x-ua-compatibl", name, 14)) {
return NGHTTP2_TOKEN_X_UA_COMPATIBLE;
}
break; break;
case 'g': case 'g':
if (lstreq("accept-encodin", name, 14)) { if (lstreq("accept-encodin", name, 14)) {
return NGHTTP2_TOKEN_ACCEPT_ENCODING; return NGHTTP2_TOKEN_ACCEPT_ENCODING;
} }
break; break;
case 'r':
if (lstreq("x-forwarded-fo", name, 14)) {
return NGHTTP2_TOKEN_X_FORWARDED_FOR;
}
break;
case 's':
if (lstreq("accept-feature", name, 14)) {
return NGHTTP2_TOKEN_ACCEPT_FEATURES;
}
if (lstreq("front-end-http", name, 14)) {
return NGHTTP2_TOKEN_FRONT_END_HTTPS;
}
if (lstreq("public-key-pin", name, 14)) {
return NGHTTP2_TOKEN_PUBLIC_KEY_PINS;
}
if (lstreq("x-frame-option", name, 14)) {
return NGHTTP2_TOKEN_X_FRAME_OPTIONS;
}
break;
} }
break; break;
case 16: case 16:
@ -422,6 +569,11 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
return NGHTTP2_TOKEN_CONTENT_ENCODING; return NGHTTP2_TOKEN_CONTENT_ENCODING;
} }
break; break;
case 'h':
if (lstreq("x-requested-wit", name, 15)) {
return NGHTTP2_TOKEN_X_REQUESTED_WITH;
}
break;
case 'n': case 'n':
if (lstreq("content-locatio", name, 15)) { if (lstreq("content-locatio", name, 15)) {
return NGHTTP2_TOKEN_CONTENT_LOCATION; return NGHTTP2_TOKEN_CONTENT_LOCATION;
@ -429,6 +581,14 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
if (lstreq("proxy-connectio", name, 15)) { if (lstreq("proxy-connectio", name, 15)) {
return NGHTTP2_TOKEN_PROXY_CONNECTION; return NGHTTP2_TOKEN_PROXY_CONNECTION;
} }
if (lstreq("x-xss-protectio", name, 15)) {
return NGHTTP2_TOKEN_X_XSS_PROTECTION;
}
break;
case 't':
if (lstreq("x-forwarded-hos", name, 15)) {
return NGHTTP2_TOKEN_X_FORWARDED_HOST;
}
break; break;
} }
break; break;
@ -444,6 +604,16 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
return NGHTTP2_TOKEN_TRANSFER_ENCODING; return NGHTTP2_TOKEN_TRANSFER_ENCODING;
} }
break; break;
case 'o':
if (lstreq("x-forwarded-prot", name, 16)) {
return NGHTTP2_TOKEN_X_FORWARDED_PROTO;
}
break;
case 'y':
if (lstreq("sec-websocket-ke", name, 16)) {
return NGHTTP2_TOKEN_SEC_WEBSOCKET_KEY;
}
break;
} }
break; break;
case 18: case 18:
@ -453,6 +623,11 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
return NGHTTP2_TOKEN_PROXY_AUTHENTICATE; return NGHTTP2_TOKEN_PROXY_AUTHENTICATE;
} }
break; break;
case 'n':
if (lstreq("x-content-duratio", name, 17)) {
return NGHTTP2_TOKEN_X_CONTENT_DURATION;
}
break;
} }
break; break;
case 19: case 19:
@ -472,12 +647,80 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
break; break;
} }
break; break;
case 20:
switch (name[19]) {
case 'n':
if (lstreq("sec-websocket-origi", name, 19)) {
return NGHTTP2_TOKEN_SEC_WEBSOCKET_ORIGIN;
}
break;
}
break;
case 21:
switch (name[20]) {
case 'l':
if (lstreq("x-dnsprefetch-contro", name, 20)) {
return NGHTTP2_TOKEN_X_DNSPREFETCH_CONTROL;
}
break;
case 'n':
if (lstreq("sec-websocket-versio", name, 20)) {
return NGHTTP2_TOKEN_SEC_WEBSOCKET_VERSION;
}
break;
}
break;
case 22:
switch (name[21]) {
case 'e':
if (lstreq("access-control-max-ag", name, 21)) {
return NGHTTP2_TOKEN_ACCESS_CONTROL_MAX_AGE;
}
break;
case 'l':
if (lstreq("sec-websocket-protoco", name, 21)) {
return NGHTTP2_TOKEN_SEC_WEBSOCKET_PROTOCOL;
}
break;
case 's':
if (lstreq("x-content-type-option", name, 21)) {
return NGHTTP2_TOKEN_X_CONTENT_TYPE_OPTIONS;
}
break;
}
break;
case 23:
switch (name[22]) {
case 'y':
if (lstreq("content-security-polic", name, 22)) {
return NGHTTP2_TOKEN_CONTENT_SECURITY_POLICY;
}
break;
}
break;
case 24:
switch (name[23]) {
case 's':
if (lstreq("sec-websocket-extension", name, 23)) {
return NGHTTP2_TOKEN_SEC_WEBSOCKET_EXTENSIONS;
}
break;
}
break;
case 25: case 25:
switch (name[24]) { switch (name[24]) {
case 's':
if (lstreq("upgrade-insecure-request", name, 24)) {
return NGHTTP2_TOKEN_UPGRADE_INSECURE_REQUESTS;
}
break;
case 'y': case 'y':
if (lstreq("strict-transport-securit", name, 24)) { if (lstreq("strict-transport-securit", name, 24)) {
return NGHTTP2_TOKEN_STRICT_TRANSPORT_SECURITY; return NGHTTP2_TOKEN_STRICT_TRANSPORT_SECURITY;
} }
if (lstreq("x-content-security-polic", name, 24)) {
return NGHTTP2_TOKEN_X_CONTENT_SECURITY_POLICY;
}
break; break;
} }
break; break;
@ -490,6 +733,59 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
break; break;
} }
break; break;
case 28:
switch (name[27]) {
case 's':
if (lstreq("access-control-allow-header", name, 27)) {
return NGHTTP2_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS;
}
if (lstreq("access-control-allow-method", name, 27)) {
return NGHTTP2_TOKEN_ACCESS_CONTROL_ALLOW_METHODS;
}
break;
}
break;
case 29:
switch (name[28]) {
case 'd':
if (lstreq("access-control-request-metho", name, 28)) {
return NGHTTP2_TOKEN_ACCESS_CONTROL_REQUEST_METHOD;
}
break;
case 's':
if (lstreq("access-control-expose-header", name, 28)) {
return NGHTTP2_TOKEN_ACCESS_CONTROL_EXPOSE_HEADERS;
}
break;
}
break;
case 30:
switch (name[29]) {
case 's':
if (lstreq("access-control-request-header", name, 29)) {
return NGHTTP2_TOKEN_ACCESS_CONTROL_REQUEST_HEADERS;
}
break;
}
break;
case 32:
switch (name[31]) {
case 's':
if (lstreq("access-control-allow-credential", name, 31)) {
return NGHTTP2_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS;
}
break;
}
break;
case 35:
switch (name[34]) {
case 'y':
if (lstreq("content-security-policy-report-onl", name, 34)) {
return NGHTTP2_TOKEN_CONTENT_SECURITY_POLICY_REPORT_ONLY;
}
break;
}
break;
} }
return -1; return -1;
} }
@ -617,8 +913,8 @@ static nghttp2_hd_entry *hd_map_find(nghttp2_hd_map *map, int *exact_match,
*exact_match = 0; *exact_match = 0;
for (p = map->table[hash & (HD_MAP_SIZE - 1)]; p; p = p->next) { for (p = map->table[hash & (HD_MAP_SIZE - 1)]; p; p = p->next) {
if (hash != p->hash || token != p->token || if (token != p->token ||
(token == -1 && !name_eq(&p->nv, nv))) { (token == -1 && (hash != p->hash || !name_eq(&p->nv, nv)))) {
continue; continue;
} }
if (!res) { if (!res) {
@ -1444,7 +1740,7 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs,
int indexing_mode; int indexing_mode;
int token; int token;
nghttp2_mem *mem; nghttp2_mem *mem;
uint32_t hash; uint32_t hash = 0;
DEBUGF(fprintf(stderr, "deflatehd: deflating %.*s: %.*s\n", (int)nv->namelen, DEBUGF(fprintf(stderr, "deflatehd: deflating %.*s: %.*s\n", (int)nv->namelen,
nv->name, (int)nv->valuelen, nv->value)); nv->name, (int)nv->valuelen, nv->value));
@ -1452,9 +1748,9 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs,
mem = deflater->ctx.mem; mem = deflater->ctx.mem;
token = lookup_token(nv->name, nv->namelen); token = lookup_token(nv->name, nv->namelen);
if (token == -1 || token > NGHTTP2_TOKEN_WWW_AUTHENTICATE) { if (token == -1) {
hash = name_hash(nv); hash = name_hash(nv);
} else { } else if (token <= NGHTTP2_TOKEN_WWW_AUTHENTICATE) {
hash = static_table[token].hash; hash = static_table[token].hash;
} }

View File

@ -105,11 +105,67 @@ typedef enum {
NGHTTP2_TOKEN_VARY = 58, NGHTTP2_TOKEN_VARY = 58,
NGHTTP2_TOKEN_VIA = 59, NGHTTP2_TOKEN_VIA = 59,
NGHTTP2_TOKEN_WWW_AUTHENTICATE = 60, NGHTTP2_TOKEN_WWW_AUTHENTICATE = 60,
NGHTTP2_TOKEN_TE, NGHTTP2_TOKEN_ACCEPT_CH,
NGHTTP2_TOKEN_ACCEPT_DATETIME,
NGHTTP2_TOKEN_ACCEPT_FEATURES,
NGHTTP2_TOKEN_ACCEPT_PATCH,
NGHTTP2_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS,
NGHTTP2_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS,
NGHTTP2_TOKEN_ACCESS_CONTROL_ALLOW_METHODS,
NGHTTP2_TOKEN_ACCESS_CONTROL_EXPOSE_HEADERS,
NGHTTP2_TOKEN_ACCESS_CONTROL_MAX_AGE,
NGHTTP2_TOKEN_ACCESS_CONTROL_REQUEST_HEADERS,
NGHTTP2_TOKEN_ACCESS_CONTROL_REQUEST_METHOD,
NGHTTP2_TOKEN_ALT_SVC,
NGHTTP2_TOKEN_ALTERNATES,
NGHTTP2_TOKEN_CONNECTION, NGHTTP2_TOKEN_CONNECTION,
NGHTTP2_TOKEN_CONTENT_MD5,
NGHTTP2_TOKEN_CONTENT_SECURITY_POLICY,
NGHTTP2_TOKEN_CONTENT_SECURITY_POLICY_REPORT_ONLY,
NGHTTP2_TOKEN_DNT,
NGHTTP2_TOKEN_FORWARDED,
NGHTTP2_TOKEN_FRONT_END_HTTPS,
NGHTTP2_TOKEN_KEEP_ALIVE, NGHTTP2_TOKEN_KEEP_ALIVE,
NGHTTP2_TOKEN_LAST_EVENT_ID,
NGHTTP2_TOKEN_NEGOTIATE,
NGHTTP2_TOKEN_ORIGIN,
NGHTTP2_TOKEN_P3P,
NGHTTP2_TOKEN_PRAGMA,
NGHTTP2_TOKEN_PROXY_CONNECTION, NGHTTP2_TOKEN_PROXY_CONNECTION,
NGHTTP2_TOKEN_UPGRADE NGHTTP2_TOKEN_PUBLIC_KEY_PINS,
NGHTTP2_TOKEN_SEC_WEBSOCKET_EXTENSIONS,
NGHTTP2_TOKEN_SEC_WEBSOCKET_KEY,
NGHTTP2_TOKEN_SEC_WEBSOCKET_ORIGIN,
NGHTTP2_TOKEN_SEC_WEBSOCKET_PROTOCOL,
NGHTTP2_TOKEN_SEC_WEBSOCKET_VERSION,
NGHTTP2_TOKEN_SET_COOKIE2,
NGHTTP2_TOKEN_STATUS,
NGHTTP2_TOKEN_TCN,
NGHTTP2_TOKEN_TE,
NGHTTP2_TOKEN_TRAILER,
NGHTTP2_TOKEN_TSV,
NGHTTP2_TOKEN_UPGRADE,
NGHTTP2_TOKEN_UPGRADE_INSECURE_REQUESTS,
NGHTTP2_TOKEN_VARIANT_VARY,
NGHTTP2_TOKEN_WARNING,
NGHTTP2_TOKEN_X_API_VERSION,
NGHTTP2_TOKEN_X_ATT_DEVICEID,
NGHTTP2_TOKEN_X_CACHE,
NGHTTP2_TOKEN_X_CACHE_LOOKUP,
NGHTTP2_TOKEN_X_CONTENT_DURATION,
NGHTTP2_TOKEN_X_CONTENT_SECURITY_POLICY,
NGHTTP2_TOKEN_X_CONTENT_TYPE_OPTIONS,
NGHTTP2_TOKEN_X_DNSPREFETCH_CONTROL,
NGHTTP2_TOKEN_X_FORWARDED_FOR,
NGHTTP2_TOKEN_X_FORWARDED_HOST,
NGHTTP2_TOKEN_X_FORWARDED_PROTO,
NGHTTP2_TOKEN_X_FRAME_OPTIONS,
NGHTTP2_TOKEN_X_POWERED_BY,
NGHTTP2_TOKEN_X_REQUESTED_WITH,
NGHTTP2_TOKEN_X_UA_COMPATIBLE,
NGHTTP2_TOKEN_X_WAP_PROFILE,
NGHTTP2_TOKEN_X_WEBKIT_CSP,
NGHTTP2_TOKEN_X_XSS_PROTECTION,
} nghttp2_token; } nghttp2_token;
typedef enum { typedef enum {

View File

@ -25,9 +25,9 @@
#ifndef NGHTTP2_NPN_H #ifndef NGHTTP2_NPN_H
#define NGHTTP2_NPN_H #define NGHTTP2_NPN_H
#ifdef HAVE_CONFIG #ifdef HAVE_CONFIG_H
#include <config.h> #include <config.h>
#endif /* HAVE_CONFIG */ #endif /* HAVE_CONFIG_H */
#include <nghttp2/nghttp2.h> #include <nghttp2/nghttp2.h>

View File

@ -1221,11 +1221,12 @@ int nghttp2_session_adjust_idle_stream(nghttp2_session *session) {
size_t max; size_t max;
int rv; int rv;
/* Make minimum number of idle streams 16, which is arbitrary chosen /* Make minimum number of idle streams 16, and maximum 100, which
number. */ are arbitrary chosen numbers. */
max = nghttp2_max(16, max = nghttp2_min(
nghttp2_min(session->local_settings.max_concurrent_streams, 100, nghttp2_max(
session->pending_local_max_concurrent_stream)); 16, nghttp2_min(session->local_settings.max_concurrent_streams,
session->pending_local_max_concurrent_stream)));
DEBUGF(fprintf(stderr, "stream: adjusting kept idle streams " DEBUGF(fprintf(stderr, "stream: adjusting kept idle streams "
"num_idle_streams=%zu, max=%zu\n", "num_idle_streams=%zu, max=%zu\n",
@ -5763,10 +5764,12 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
readlen = inbound_frame_payload_readlen(iframe, in, last); readlen = inbound_frame_payload_readlen(iframe, in, last);
if (readlen > 0) {
iframe->lbuf.last = nghttp2_cpymem(iframe->lbuf.last, in, readlen); iframe->lbuf.last = nghttp2_cpymem(iframe->lbuf.last, in, readlen);
iframe->payloadleft -= readlen; iframe->payloadleft -= readlen;
in += readlen; in += readlen;
}
DEBUGF(fprintf(stderr, "recv: readlen=%zu, payloadleft=%zu\n", readlen, DEBUGF(fprintf(stderr, "recv: readlen=%zu, payloadleft=%zu\n", readlen,
iframe->payloadleft)); iframe->payloadleft));

View File

@ -30,14 +30,32 @@
#include "nghttp2_session.h" #include "nghttp2_session.h"
#include "nghttp2_helper.h" #include "nghttp2_helper.h"
/* Maximum distance between any two stream's cycle in the same
prirority queue. Imagine stream A's cycle is A, and stream B's
cycle is B, and A < B. The cycle is unsigned 32 bit integer, it
may get overflow. Because of how we calculate the next cycle
value, if B - A is less than or equals to
NGHTTP2_MAX_CYCLE_DISTANCE, A and B are in the same scale, in other
words, B is really greater than or equal to A. Otherwise, A is a
result of overflow, and it is actually A > B if we consider that
fact. */
#define NGHTTP2_MAX_CYCLE_DISTANCE (16384 * 256 + 255)
static int stream_less(const void *lhsx, const void *rhsx) { static int stream_less(const void *lhsx, const void *rhsx) {
const nghttp2_stream *lhs, *rhs; const nghttp2_stream *lhs, *rhs;
lhs = nghttp2_struct_of(lhsx, nghttp2_stream, pq_entry); lhs = nghttp2_struct_of(lhsx, nghttp2_stream, pq_entry);
rhs = nghttp2_struct_of(rhsx, nghttp2_stream, pq_entry); rhs = nghttp2_struct_of(rhsx, nghttp2_stream, pq_entry);
return lhs->cycle < rhs->cycle || if (lhs->cycle == rhs->cycle) {
(lhs->cycle == rhs->cycle && lhs->seq < rhs->seq); return lhs->seq < rhs->seq;
}
if (lhs->cycle < rhs->cycle) {
return rhs->cycle - lhs->cycle <= NGHTTP2_MAX_CYCLE_DISTANCE;
}
return lhs->cycle - rhs->cycle > NGHTTP2_MAX_CYCLE_DISTANCE;
} }
void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id, void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
@ -116,14 +134,14 @@ static int stream_subtree_active(nghttp2_stream *stream) {
/* /*
* Returns next cycle for |stream|. * Returns next cycle for |stream|.
*/ */
static void stream_next_cycle(nghttp2_stream *stream, uint64_t last_cycle) { static void stream_next_cycle(nghttp2_stream *stream, uint32_t last_cycle) {
size_t penalty; uint32_t penalty;
penalty = penalty = (uint32_t)stream->last_writelen * NGHTTP2_MAX_WEIGHT +
stream->last_writelen * NGHTTP2_MAX_WEIGHT + stream->pending_penalty; stream->pending_penalty;
stream->cycle = last_cycle + penalty / (uint32_t)stream->weight; stream->cycle = last_cycle + penalty / (uint32_t)stream->weight;
stream->pending_penalty = (uint32_t)(penalty % (uint32_t)stream->weight); stream->pending_penalty = penalty % (uint32_t)stream->weight;
} }
static int stream_obq_push(nghttp2_stream *dep_stream, nghttp2_stream *stream) { static int stream_obq_push(nghttp2_stream *dep_stream, nghttp2_stream *stream) {
@ -229,9 +247,9 @@ void nghttp2_stream_reschedule(nghttp2_stream *stream) {
void nghttp2_stream_change_weight(nghttp2_stream *stream, int32_t weight) { void nghttp2_stream_change_weight(nghttp2_stream *stream, int32_t weight) {
nghttp2_stream *dep_stream; nghttp2_stream *dep_stream;
uint64_t last_cycle; uint32_t last_cycle;
int32_t old_weight; int32_t old_weight;
size_t wlen_penalty; uint32_t wlen_penalty;
if (stream->weight == weight) { if (stream->weight == weight) {
return; return;
@ -254,7 +272,7 @@ void nghttp2_stream_change_weight(nghttp2_stream *stream, int32_t weight) {
nghttp2_pq_remove(&dep_stream->obq, &stream->pq_entry); nghttp2_pq_remove(&dep_stream->obq, &stream->pq_entry);
wlen_penalty = stream->last_writelen * NGHTTP2_MAX_WEIGHT; wlen_penalty = (uint32_t)stream->last_writelen * NGHTTP2_MAX_WEIGHT;
/* Compute old stream->pending_penalty we used to calculate /* Compute old stream->pending_penalty we used to calculate
stream->cycle */ stream->cycle */
@ -270,7 +288,9 @@ void nghttp2_stream_change_weight(nghttp2_stream *stream, int32_t weight) {
place */ place */
stream_next_cycle(stream, last_cycle); stream_next_cycle(stream, last_cycle);
if (stream->cycle < dep_stream->descendant_last_cycle) { if (stream->cycle < dep_stream->descendant_last_cycle &&
(dep_stream->descendant_last_cycle - stream->cycle) <=
NGHTTP2_MAX_CYCLE_DISTANCE) {
stream->cycle = dep_stream->descendant_last_cycle; stream->cycle = dep_stream->descendant_last_cycle;
} }

View File

@ -147,9 +147,9 @@ struct nghttp2_stream {
/* Received body so far */ /* Received body so far */
int64_t recv_content_length; int64_t recv_content_length;
/* Base last_cycle for direct descendent streams. */ /* Base last_cycle for direct descendent streams. */
uint64_t descendant_last_cycle; uint32_t descendant_last_cycle;
/* Next scheduled time to sent item */ /* Next scheduled time to sent item */
uint64_t cycle; uint32_t cycle;
/* Next seq used for direct descendant streams */ /* Next seq used for direct descendant streams */
uint64_t descendant_next_seq; uint64_t descendant_next_seq;
/* Secondary key for prioritization to break a tie for cycle. This /* Secondary key for prioritization to break a tie for cycle. This

View File

@ -308,7 +308,6 @@ public:
} }
auto handler = auto handler =
make_unique<Http2Handler>(this, fd, ssl, get_next_session_id()); make_unique<Http2Handler>(this, fd, ssl, get_next_session_id());
handler->setup_bev();
if (!ssl) { if (!ssl) {
if (handler->connection_made() != 0) { if (handler->connection_made() != 0) {
return; return;
@ -447,6 +446,7 @@ Stream::Stream(Http2Handler *handler, int32_t stream_id)
file_ent(nullptr), file_ent(nullptr),
body_length(0), body_length(0),
body_offset(0), body_offset(0),
header_buffer_size(0),
stream_id(stream_id), stream_id(stream_id),
echo_upload(false) { echo_upload(false) {
auto config = handler->get_config(); auto config = handler->get_config();
@ -574,7 +574,9 @@ struct ev_loop *Http2Handler::get_loop() const {
Http2Handler::WriteBuf *Http2Handler::get_wb() { return &wb_; } Http2Handler::WriteBuf *Http2Handler::get_wb() { return &wb_; }
int Http2Handler::setup_bev() { return 0; } void Http2Handler::start_settings_timer() {
ev_timer_start(sessions_->get_loop(), &settings_timerev_);
}
int Http2Handler::fill_wb() { int Http2Handler::fill_wb() {
if (data_pending_) { if (data_pending_) {
@ -869,8 +871,6 @@ int Http2Handler::connection_made() {
} }
} }
ev_timer_start(sessions_->get_loop(), &settings_timerev_);
if (ssl_ && !nghttp2::ssl::check_http2_requirement(ssl_)) { if (ssl_ && !nghttp2::ssl::check_http2_requirement(ssl_)) {
terminate_session(NGHTTP2_INADEQUATE_SECURITY); terminate_session(NGHTTP2_INADEQUATE_SECURITY);
} }
@ -1389,6 +1389,13 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
return 0; return 0;
} }
if (stream->header_buffer_size + namelen + valuelen > 64_k) {
hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR);
return 0;
}
stream->header_buffer_size += namelen + valuelen;
auto token = http2::lookup_token(name, namelen); auto token = http2::lookup_token(name, namelen);
http2::index_header(stream->hdidx, token, stream->headers.size()); http2::index_header(stream->hdidx, token, stream->headers.size());
@ -1530,6 +1537,15 @@ int hd_on_frame_send_callback(nghttp2_session *session,
break; break;
} }
case NGHTTP2_SETTINGS: {
if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
return 0;
}
hd->start_settings_timer();
break;
}
case NGHTTP2_PUSH_PROMISE: { case NGHTTP2_PUSH_PROMISE: {
auto promised_stream_id = frame->push_promise.promised_stream_id; auto promised_stream_id = frame->push_promise.promised_stream_id;
auto promised_stream = hd->get_stream(promised_stream_id); auto promised_stream = hd->get_stream(promised_stream_id);

View File

@ -119,6 +119,9 @@ struct Stream {
ev_timer wtimer; ev_timer wtimer;
int64_t body_length; int64_t body_length;
int64_t body_offset; int64_t body_offset;
// Total amount of bytes (sum of name and value length) used in
// headers.
size_t header_buffer_size;
int32_t stream_id; int32_t stream_id;
http2::HeaderIndex hdidx; http2::HeaderIndex hdidx;
bool echo_upload; bool echo_upload;
@ -134,7 +137,7 @@ public:
~Http2Handler(); ~Http2Handler();
void remove_self(); void remove_self();
int setup_bev(); void start_settings_timer();
int on_read(); int on_read();
int on_write(); int on_write();
int connection_made(); int connection_made();

View File

@ -62,7 +62,7 @@ HELPER_OBJECTS = util.cc \
http2.cc timegm.c app_helper.cc nghttp2_gzip.c http2.cc timegm.c app_helper.cc nghttp2_gzip.c
HELPER_HFILES = util.h \ HELPER_HFILES = util.h \
http2.h timegm.h app_helper.h nghttp2_config.h \ http2.h timegm.h app_helper.h nghttp2_config.h \
nghttp2_gzip.h nghttp2_gzip.h network.h
HTML_PARSER_OBJECTS = HTML_PARSER_OBJECTS =
HTML_PARSER_HFILES = HtmlParser.h HTML_PARSER_HFILES = HtmlParser.h

View File

@ -32,7 +32,7 @@ namespace nghttp2 {
namespace asio_http2 { namespace asio_http2 {
namespace client { namespace client {
request_impl::request_impl() : strm_(nullptr) {} request_impl::request_impl() : strm_(nullptr), header_buffer_size_(0) {}
void request_impl::write_trailer(header_map h) { void request_impl::write_trailer(header_map h) {
auto sess = strm_->session(); auto sess = strm_->session();
@ -105,6 +105,12 @@ void request_impl::method(std::string s) { method_ = std::move(s); }
const std::string &request_impl::method() const { return method_; } const std::string &request_impl::method() const { return method_; }
size_t request_impl::header_buffer_size() const { return header_buffer_size_; }
void request_impl::update_header_buffer_size(size_t len) {
header_buffer_size_ += len;
}
} // namespace client } // namespace client
} // namespace asio_http2 } // namespace asio_http2
} // namespace nghttp2 } // namespace nghttp2

View File

@ -75,6 +75,9 @@ public:
void method(std::string s); void method(std::string s);
const std::string &method() const; const std::string &method() const;
size_t header_buffer_size() const;
void update_header_buffer_size(size_t len);
private: private:
header_map header_; header_map header_;
response_cb response_cb_; response_cb response_cb_;
@ -84,6 +87,7 @@ private:
class stream *strm_; class stream *strm_;
uri_ref uri_; uri_ref uri_;
std::string method_; std::string method_;
size_t header_buffer_size_;
}; };
} // namespace client } // namespace client

View File

@ -30,7 +30,8 @@ namespace nghttp2 {
namespace asio_http2 { namespace asio_http2 {
namespace client { namespace client {
response_impl::response_impl() : content_length_(-1), status_code_(0) {} response_impl::response_impl()
: content_length_(-1), header_buffer_size_(0), status_code_(0) {}
void response_impl::on_data(data_cb cb) { data_cb_ = std::move(cb); } void response_impl::on_data(data_cb cb) { data_cb_ = std::move(cb); }
@ -52,6 +53,12 @@ header_map &response_impl::header() { return header_; }
const header_map &response_impl::header() const { return header_; } const header_map &response_impl::header() const { return header_; }
size_t response_impl::header_buffer_size() const { return header_buffer_size_; }
void response_impl::update_header_buffer_size(size_t len) {
header_buffer_size_ += len;
}
} // namespace client } // namespace client
} // namespace asio_http2 } // namespace asio_http2
} // namespace nghttp2 } // namespace nghttp2

View File

@ -53,12 +53,16 @@ public:
header_map &header(); header_map &header();
const header_map &header() const; const header_map &header() const;
size_t header_buffer_size() const;
void update_header_buffer_size(size_t len);
private: private:
data_cb data_cb_; data_cb data_cb_;
header_map header_; header_map header_;
int64_t content_length_; int64_t content_length_;
size_t header_buffer_size_;
int status_code_; int status_code_;
}; };

View File

@ -183,6 +183,12 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
if (token == http2::HD__STATUS) { if (token == http2::HD__STATUS) {
res.status_code(util::parse_uint(value, valuelen)); res.status_code(util::parse_uint(value, valuelen));
} else { } else {
if (res.header_buffer_size() + namelen + valuelen > 64_k) {
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
frame->hd.stream_id, NGHTTP2_INTERNAL_ERROR);
break;
}
res.update_header_buffer_size(namelen + valuelen);
if (token == http2::HD_CONTENT_LENGTH) { if (token == http2::HD_CONTENT_LENGTH) {
res.content_length(util::parse_uint(value, valuelen)); res.content_length(util::parse_uint(value, valuelen));
@ -223,6 +229,13 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
} }
// fall through // fall through
default: default:
if (req.header_buffer_size() + namelen + valuelen > 64_k) {
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
frame->hd.stream_id, NGHTTP2_INTERNAL_ERROR);
break;
}
req.update_header_buffer_size(namelen + valuelen);
req.header().emplace( req.header().emplace(
std::string(name, name + namelen), std::string(name, name + namelen),
header_value{std::string(value, value + valuelen), header_value{std::string(value, value + valuelen),

View File

@ -105,6 +105,13 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
} }
// fall through // fall through
default: default:
if (req.header_buffer_size() + namelen + valuelen > 64_k) {
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id,
NGHTTP2_INTERNAL_ERROR);
break;
}
req.update_header_buffer_size(namelen + valuelen);
req.header().emplace(std::string(name, name + namelen), req.header().emplace(std::string(name, name + namelen),
header_value{std::string(value, value + valuelen), header_value{std::string(value, value + valuelen),
(flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0}); (flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0});

View File

@ -28,7 +28,7 @@ namespace nghttp2 {
namespace asio_http2 { namespace asio_http2 {
namespace server { namespace server {
request_impl::request_impl() : strm_(nullptr) {} request_impl::request_impl() : strm_(nullptr), header_buffer_size_(0) {}
const header_map &request_impl::header() const { return header_; } const header_map &request_impl::header() const { return header_; }
@ -62,6 +62,12 @@ void request_impl::remote_endpoint(boost::asio::ip::tcp::endpoint ep) {
remote_ep_ = std::move(ep); remote_ep_ = std::move(ep);
} }
size_t request_impl::header_buffer_size() const { return header_buffer_size_; }
void request_impl::update_header_buffer_size(size_t len) {
header_buffer_size_ += len;
}
} // namespace server } // namespace server
} // namespace asio_http2 } // namespace asio_http2
} // namespace nghttp2 } // namespace nghttp2

View File

@ -58,6 +58,9 @@ public:
const boost::asio::ip::tcp::endpoint &remote_endpoint() const; const boost::asio::ip::tcp::endpoint &remote_endpoint() const;
void remote_endpoint(boost::asio::ip::tcp::endpoint ep); void remote_endpoint(boost::asio::ip::tcp::endpoint ep);
size_t header_buffer_size() const;
void update_header_buffer_size(size_t len);
private: private:
class stream *strm_; class stream *strm_;
header_map header_; header_map header_;
@ -65,6 +68,7 @@ private:
uri_ref uri_; uri_ref uri_;
data_cb on_data_cb_; data_cb on_data_cb_;
boost::asio::ip::tcp::endpoint remote_ep_; boost::asio::ip::tcp::endpoint remote_ep_;
size_t header_buffer_size_;
}; };
} // namespace server } // namespace server

View File

@ -271,7 +271,7 @@ void copy_url_component(std::string &dest, const http_parser_url *u, int field,
Headers::value_type to_header(const uint8_t *name, size_t namelen, Headers::value_type to_header(const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen, const uint8_t *value, size_t valuelen,
bool no_index, int16_t token) { bool no_index, int32_t token) {
return Header(std::string(reinterpret_cast<const char *>(name), namelen), return Header(std::string(reinterpret_cast<const char *>(name), namelen),
std::string(reinterpret_cast<const char *>(value), valuelen), std::string(reinterpret_cast<const char *>(value), valuelen),
no_index, token); no_index, token);
@ -279,7 +279,7 @@ Headers::value_type to_header(const uint8_t *name, size_t namelen,
void add_header(Headers &nva, const uint8_t *name, size_t namelen, void add_header(Headers &nva, const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen, bool no_index, const uint8_t *value, size_t valuelen, bool no_index,
int16_t token) { int32_t token) {
if (valuelen > 0) { if (valuelen > 0) {
size_t i, j; size_t i, j;
for (i = 0; i < valuelen && (value[i] == ' ' || value[i] == '\t'); ++i) for (i = 0; i < valuelen && (value[i] == ' ' || value[i] == '\t'); ++i)
@ -760,7 +760,7 @@ void init_hdidx(HeaderIndex &hdidx) {
std::fill(std::begin(hdidx), std::end(hdidx), -1); std::fill(std::begin(hdidx), std::end(hdidx), -1);
} }
void index_header(HeaderIndex &hdidx, int16_t token, size_t idx) { void index_header(HeaderIndex &hdidx, int32_t token, size_t idx) {
if (token == -1) { if (token == -1) {
return; return;
} }
@ -768,52 +768,7 @@ void index_header(HeaderIndex &hdidx, int16_t token, size_t idx) {
hdidx[token] = idx; hdidx[token] = idx;
} }
bool check_http2_request_pseudo_header(const HeaderIndex &hdidx, const Headers::value_type *get_header(const HeaderIndex &hdidx, int32_t token,
int16_t token) {
switch (token) {
case HD__AUTHORITY:
case HD__METHOD:
case HD__PATH:
case HD__SCHEME:
return hdidx[token] == -1;
default:
return false;
}
}
bool check_http2_response_pseudo_header(const HeaderIndex &hdidx,
int16_t token) {
switch (token) {
case HD__STATUS:
return hdidx[token] == -1;
default:
return false;
}
}
bool http2_header_allowed(int16_t token) {
switch (token) {
case HD_CONNECTION:
case HD_KEEP_ALIVE:
case HD_PROXY_CONNECTION:
case HD_TRANSFER_ENCODING:
case HD_UPGRADE:
return false;
default:
return true;
}
}
bool http2_mandatory_request_headers_presence(const HeaderIndex &hdidx) {
if (hdidx[HD__METHOD] == -1 || hdidx[HD__PATH] == -1 ||
hdidx[HD__SCHEME] == -1 ||
(hdidx[HD__AUTHORITY] == -1 && hdidx[HD_HOST] == -1)) {
return false;
}
return true;
}
const Headers::value_type *get_header(const HeaderIndex &hdidx, int16_t token,
const Headers &nva) { const Headers &nva) {
auto i = hdidx[token]; auto i = hdidx[token];
if (i == -1) { if (i == -1) {
@ -822,7 +777,7 @@ const Headers::value_type *get_header(const HeaderIndex &hdidx, int16_t token,
return &nva[i]; return &nva[i];
} }
Headers::value_type *get_header(const HeaderIndex &hdidx, int16_t token, Headers::value_type *get_header(const HeaderIndex &hdidx, int32_t token,
Headers &nva) { Headers &nva) {
auto i = hdidx[token]; auto i = hdidx[token];
if (i == -1) { if (i == -1) {

View File

@ -44,7 +44,7 @@ namespace nghttp2 {
struct Header { struct Header {
Header(std::string name, std::string value, bool no_index = false, Header(std::string name, std::string value, bool no_index = false,
int16_t token = -1) int32_t token = -1)
: name(std::move(name)), : name(std::move(name)),
value(std::move(value)), value(std::move(value)),
token(token), token(token),
@ -62,7 +62,7 @@ struct Header {
std::string name; std::string name;
std::string value; std::string value;
int16_t token; int32_t token;
bool no_index; bool no_index;
}; };
@ -89,14 +89,14 @@ void copy_url_component(std::string &dest, const http_parser_url *u, int field,
Headers::value_type to_header(const uint8_t *name, size_t namelen, Headers::value_type to_header(const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen, const uint8_t *value, size_t valuelen,
bool no_index, int16_t token); bool no_index, int32_t token);
// Add name/value pairs to |nva|. If |no_index| is true, this // Add name/value pairs to |nva|. If |no_index| is true, this
// name/value pair won't be indexed when it is forwarded to the next // name/value pair won't be indexed when it is forwarded to the next
// hop. This function strips white spaces around |value|. // hop. This function strips white spaces around |value|.
void add_header(Headers &nva, const uint8_t *name, size_t namelen, void add_header(Headers &nva, const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen, bool no_index, const uint8_t *value, size_t valuelen, bool no_index,
int16_t token); int32_t token);
// Returns pointer to the entry in |nva| which has name |name|. If // Returns pointer to the entry in |nva| which has name |name|. If
// more than one entries which have the name |name|, last occurrence // more than one entries which have the name |name|, last occurrence
@ -277,30 +277,13 @@ int lookup_token(const std::string &name);
// array containing at least HD_MAXIDX elements. // array containing at least HD_MAXIDX elements.
void init_hdidx(HeaderIndex &hdidx); void init_hdidx(HeaderIndex &hdidx);
// Indexes header |token| using index |idx|. // Indexes header |token| using index |idx|.
void index_header(HeaderIndex &hdidx, int16_t token, size_t idx); void index_header(HeaderIndex &hdidx, int32_t token, size_t idx);
// Returns true if HTTP/2 request pseudo header |token| is not indexed
// yet and not -1.
bool check_http2_request_pseudo_header(const HeaderIndex &hdidx, int16_t token);
// Returns true if HTTP/2 response pseudo header |token| is not
// indexed yet and not -1.
bool check_http2_response_pseudo_header(const HeaderIndex &hdidx,
int16_t token);
// Returns true if header field denoted by |token| is allowed for
// HTTP/2.
bool http2_header_allowed(int16_t token);
// Returns true that |hdidx| contains mandatory HTTP/2 request
// headers.
bool http2_mandatory_request_headers_presence(const HeaderIndex &hdidx);
// Returns header denoted by |token| using index |hdidx|. // Returns header denoted by |token| using index |hdidx|.
const Headers::value_type *get_header(const HeaderIndex &hdidx, int16_t token, const Headers::value_type *get_header(const HeaderIndex &hdidx, int32_t token,
const Headers &nva); const Headers &nva);
Headers::value_type *get_header(const HeaderIndex &hdidx, int16_t token, Headers::value_type *get_header(const HeaderIndex &hdidx, int32_t token,
Headers &nva); Headers &nva);
struct LinkHeader { struct LinkHeader {

View File

@ -271,53 +271,6 @@ void test_http2_lookup_token(void) {
CU_ASSERT(http2::HD_EXPECT == http2::lookup_token("expect")); CU_ASSERT(http2::HD_EXPECT == http2::lookup_token("expect"));
} }
void test_http2_check_http2_pseudo_header(void) {
http2::HeaderIndex hdidx;
http2::init_hdidx(hdidx);
CU_ASSERT(http2::check_http2_request_pseudo_header(hdidx, http2::HD__METHOD));
hdidx[http2::HD__PATH] = 0;
CU_ASSERT(http2::check_http2_request_pseudo_header(hdidx, http2::HD__METHOD));
hdidx[http2::HD__METHOD] = 1;
CU_ASSERT(
!http2::check_http2_request_pseudo_header(hdidx, http2::HD__METHOD));
CU_ASSERT(!http2::check_http2_request_pseudo_header(hdidx, http2::HD_VIA));
http2::init_hdidx(hdidx);
CU_ASSERT(
http2::check_http2_response_pseudo_header(hdidx, http2::HD__STATUS));
hdidx[http2::HD__STATUS] = 0;
CU_ASSERT(
!http2::check_http2_response_pseudo_header(hdidx, http2::HD__STATUS));
CU_ASSERT(!http2::check_http2_response_pseudo_header(hdidx, http2::HD_VIA));
}
void test_http2_http2_header_allowed(void) {
CU_ASSERT(http2::http2_header_allowed(http2::HD__PATH));
CU_ASSERT(http2::http2_header_allowed(http2::HD_CONTENT_LENGTH));
CU_ASSERT(!http2::http2_header_allowed(http2::HD_CONNECTION));
}
void test_http2_mandatory_request_headers_presence(void) {
http2::HeaderIndex hdidx;
http2::init_hdidx(hdidx);
CU_ASSERT(!http2::http2_mandatory_request_headers_presence(hdidx));
hdidx[http2::HD__AUTHORITY] = 0;
CU_ASSERT(!http2::http2_mandatory_request_headers_presence(hdidx));
hdidx[http2::HD__METHOD] = 1;
CU_ASSERT(!http2::http2_mandatory_request_headers_presence(hdidx));
hdidx[http2::HD__PATH] = 2;
CU_ASSERT(!http2::http2_mandatory_request_headers_presence(hdidx));
hdidx[http2::HD__SCHEME] = 3;
CU_ASSERT(http2::http2_mandatory_request_headers_presence(hdidx));
hdidx[http2::HD__AUTHORITY] = -1;
hdidx[http2::HD_HOST] = 0;
CU_ASSERT(http2::http2_mandatory_request_headers_presence(hdidx));
}
void test_http2_parse_link_header(void) { void test_http2_parse_link_header(void) {
{ {
// only URI appears; we don't extract URI unless it bears rel=preload // only URI appears; we don't extract URI unless it bears rel=preload

View File

@ -40,9 +40,6 @@ void test_http2_rewrite_location_uri(void);
void test_http2_parse_http_status_code(void); void test_http2_parse_http_status_code(void);
void test_http2_index_header(void); void test_http2_index_header(void);
void test_http2_lookup_token(void); void test_http2_lookup_token(void);
void test_http2_check_http2_pseudo_header(void);
void test_http2_http2_header_allowed(void);
void test_http2_mandatory_request_headers_presence(void);
void test_http2_parse_link_header(void); void test_http2_parse_link_header(void);
void test_http2_path_join(void); void test_http2_path_join(void);
void test_http2_normalize_path(void); void test_http2_normalize_path(void);

61
src/network.h Normal file
View File

@ -0,0 +1,61 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2016 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef NETWORK_H
#define NETWORK_H
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif // HAVE_CONFIG_H
#include <sys/types.h>
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif // HAVE_SYS_SOCKET_H
#include <sys/un.h>
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif // HAVE_NETINET_IN_H
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif // HAVE_ARPA_INET_H
namespace nghttp2 {
union sockaddr_union {
sockaddr_storage storage;
sockaddr sa;
sockaddr_in6 in6;
sockaddr_in in;
sockaddr_un un;
};
struct Address {
size_t len;
union sockaddr_union su;
};
} // namespace nghttp2
#endif // NETWORK_H

View File

@ -155,6 +155,7 @@ Request::Request(const std::string &uri, const http_parser_url &u,
inflater(nullptr), inflater(nullptr),
html_parser(nullptr), html_parser(nullptr),
data_prd(data_prd), data_prd(data_prd),
header_buffer_size(0),
stream_id(-1), stream_id(-1),
status(0), status(0),
level(level), level(level),
@ -272,34 +273,7 @@ bool Request::is_ipv6_literal_addr() const {
} }
} }
bool Request::response_pseudo_header_allowed(int16_t token) const { Headers::value_type *Request::get_res_header(int32_t token) {
if (!res_nva.empty() && res_nva.back().name.c_str()[0] != ':') {
return false;
}
switch (token) {
case http2::HD__STATUS:
return res_hdidx[token] == -1;
default:
return false;
}
}
bool Request::push_request_pseudo_header_allowed(int16_t token) const {
if (!req_nva.empty() && req_nva.back().name.c_str()[0] != ':') {
return false;
}
switch (token) {
case http2::HD__AUTHORITY:
case http2::HD__METHOD:
case http2::HD__PATH:
case http2::HD__SCHEME:
return req_hdidx[token] == -1;
default:
return false;
}
}
Headers::value_type *Request::get_res_header(int16_t token) {
auto idx = res_hdidx[token]; auto idx = res_hdidx[token];
if (idx == -1) { if (idx == -1) {
return nullptr; return nullptr;
@ -307,7 +281,7 @@ Headers::value_type *Request::get_res_header(int16_t token) {
return &res_nva[idx]; return &res_nva[idx];
} }
Headers::value_type *Request::get_req_header(int16_t token) { Headers::value_type *Request::get_req_header(int32_t token) {
auto idx = req_hdidx[token]; auto idx = req_hdidx[token];
if (idx == -1) { if (idx == -1) {
return nullptr; return nullptr;
@ -1736,6 +1710,14 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
break; break;
} }
if (req->header_buffer_size + namelen + valuelen > 64_k) {
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id,
NGHTTP2_INTERNAL_ERROR);
return 0;
}
req->header_buffer_size += namelen + valuelen;
auto token = http2::lookup_token(name, namelen); auto token = http2::lookup_token(name, namelen);
http2::index_header(req->res_hdidx, token, req->res_nva.size()); http2::index_header(req->res_hdidx, token, req->res_nva.size());
@ -1751,6 +1733,15 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
break; break;
} }
if (req->header_buffer_size + namelen + valuelen > 64_k) {
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
frame->push_promise.promised_stream_id,
NGHTTP2_INTERNAL_ERROR);
return 0;
}
req->header_buffer_size += namelen + valuelen;
auto token = http2::lookup_token(name, namelen); auto token = http2::lookup_token(name, namelen);
http2::index_header(req->req_hdidx, token, req->req_nva.size()); http2::index_header(req->req_hdidx, token, req->req_nva.size());
@ -1838,6 +1829,10 @@ int on_frame_recv_callback2(nghttp2_session *session,
if (!req) { if (!req) {
break; break;
} }
// Reset for response header field reception
req->header_buffer_size = 0;
auto scheme = req->get_req_header(http2::HD__SCHEME); auto scheme = req->get_req_header(http2::HD__SCHEME);
auto authority = req->get_req_header(http2::HD__AUTHORITY); auto authority = req->get_req_header(http2::HD__AUTHORITY);
auto path = req->get_req_header(http2::HD__PATH); auto path = req->get_req_header(http2::HD__PATH);

View File

@ -125,11 +125,8 @@ struct Request {
bool is_ipv6_literal_addr() const; bool is_ipv6_literal_addr() const;
bool response_pseudo_header_allowed(int16_t token) const; Headers::value_type *get_res_header(int32_t token);
bool push_request_pseudo_header_allowed(int16_t token) const; Headers::value_type *get_req_header(int32_t token);
Headers::value_type *get_res_header(int16_t token);
Headers::value_type *get_req_header(int16_t token);
void record_request_start_time(); void record_request_start_time();
void record_response_start_time(); void record_response_start_time();
@ -150,6 +147,7 @@ struct Request {
nghttp2_gzip *inflater; nghttp2_gzip *inflater;
HtmlParser *html_parser; HtmlParser *html_parser;
const nghttp2_data_provider *data_prd; const nghttp2_data_provider *data_prd;
size_t header_buffer_size;
int32_t stream_id; int32_t stream_id;
int status; int status;
// Recursion level: 0: first entity, 1: entity linked from first entity // Recursion level: 0: first entity, 1: entity linked from first entity

View File

@ -89,12 +89,6 @@ int main(int argc, char *argv[]) {
shrpx::test_http2_index_header) || shrpx::test_http2_index_header) ||
!CU_add_test(pSuite, "http2_lookup_token", !CU_add_test(pSuite, "http2_lookup_token",
shrpx::test_http2_lookup_token) || shrpx::test_http2_lookup_token) ||
!CU_add_test(pSuite, "http2_check_http2_pseudo_header",
shrpx::test_http2_check_http2_pseudo_header) ||
!CU_add_test(pSuite, "http2_http2_header_allowed",
shrpx::test_http2_http2_header_allowed) ||
!CU_add_test(pSuite, "http2_mandatory_request_headers_presence",
shrpx::test_http2_mandatory_request_headers_presence) ||
!CU_add_test(pSuite, "http2_parse_link_header", !CU_add_test(pSuite, "http2_parse_link_header",
shrpx::test_http2_parse_link_header) || shrpx::test_http2_parse_link_header) ||
!CU_add_test(pSuite, "http2_path_join", shrpx::test_http2_path_join) || !CU_add_test(pSuite, "http2_path_join", shrpx::test_http2_path_join) ||
@ -106,8 +100,8 @@ int main(int argc, char *argv[]) {
shrpx::test_http2_get_pure_path_component) || shrpx::test_http2_get_pure_path_component) ||
!CU_add_test(pSuite, "http2_construct_push_component", !CU_add_test(pSuite, "http2_construct_push_component",
shrpx::test_http2_construct_push_component) || shrpx::test_http2_construct_push_component) ||
!CU_add_test(pSuite, "downstream_field_store_index_headers", !CU_add_test(pSuite, "downstream_field_store_add_header_lower",
shrpx::test_downstream_field_store_index_headers) || shrpx::test_downstream_field_store_add_header_lower) ||
!CU_add_test(pSuite, "downstream_field_store_header", !CU_add_test(pSuite, "downstream_field_store_header",
shrpx::test_downstream_field_store_header) || shrpx::test_downstream_field_store_header) ||
!CU_add_test(pSuite, "downstream_crumble_request_cookie", !CU_add_test(pSuite, "downstream_crumble_request_cookie",
@ -167,6 +161,10 @@ int main(int argc, char *argv[]) {
!CU_add_test(pSuite, "util_get_uint64", shrpx::test_util_get_uint64) || !CU_add_test(pSuite, "util_get_uint64", shrpx::test_util_get_uint64) ||
!CU_add_test(pSuite, "util_parse_config_str_list", !CU_add_test(pSuite, "util_parse_config_str_list",
shrpx::test_util_parse_config_str_list) || shrpx::test_util_parse_config_str_list) ||
!CU_add_test(pSuite, "util_make_http_hostport",
shrpx::test_util_make_http_hostport) ||
!CU_add_test(pSuite, "util_make_hostport",
shrpx::test_util_make_hostport) ||
!CU_add_test(pSuite, "gzip_inflate", test_nghttp2_gzip_inflate) || !CU_add_test(pSuite, "gzip_inflate", test_nghttp2_gzip_inflate) ||
!CU_add_test(pSuite, "buffer_write", nghttp2::test_buffer_write) || !CU_add_test(pSuite, "buffer_write", nghttp2::test_buffer_write) ||
!CU_add_test(pSuite, "pool_recycle", nghttp2::test_pool_recycle) || !CU_add_test(pSuite, "pool_recycle", nghttp2::test_pool_recycle) ||

View File

@ -199,18 +199,18 @@ int chown_to_running_user(const char *path) {
namespace { namespace {
void save_pid() { void save_pid() {
std::ofstream out(get_config()->pid_file.get(), std::ios::binary); std::ofstream out(get_config()->pid_file.c_str(), std::ios::binary);
out << get_config()->pid << "\n"; out << get_config()->pid << "\n";
out.close(); out.close();
if (!out) { if (!out) {
LOG(ERROR) << "Could not save PID to file " << get_config()->pid_file.get(); LOG(ERROR) << "Could not save PID to file " << get_config()->pid_file;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (get_config()->uid != 0) { if (get_config()->uid != 0) {
if (chown_to_running_user(get_config()->pid_file.get()) == -1) { if (chown_to_running_user(get_config()->pid_file.c_str()) == -1) {
auto error = errno; auto error = errno;
LOG(WARN) << "Changing owner of pid file " << get_config()->pid_file.get() LOG(WARN) << "Changing owner of pid file " << get_config()->pid_file
<< " failed: " << strerror(error); << " failed: " << strerror(error);
} }
} }
@ -656,7 +656,7 @@ int create_tcp_server_socket(UpstreamAddr &faddr,
} }
faddr.fd = fd; faddr.fd = fd;
faddr.hostport = util::make_hostport(host.data(), faddr.port); faddr.hostport = util::make_http_hostport(host.data(), faddr.port);
LOG(NOTICE) << "Listening on " << faddr.hostport; LOG(NOTICE) << "Listening on " << faddr.hostport;
@ -946,7 +946,7 @@ int event_loop() {
redirect_stderr_to_errorlog(); redirect_stderr_to_errorlog();
} }
if (get_config()->pid_file) { if (!get_config()->pid_file.empty()) {
save_pid(); save_pid();
} }
@ -1040,7 +1040,7 @@ void fill_default_config() {
*mod_config() = {}; *mod_config() = {};
mod_config()->num_worker = 1; mod_config()->num_worker = 1;
mod_config()->conf_path = strcopy("/etc/nghttpx/nghttpx.conf"); mod_config()->conf_path = "/etc/nghttpx/nghttpx.conf";
mod_config()->pid = getpid(); mod_config()->pid = getpid();
auto &tlsconf = mod_config()->tls; auto &tlsconf = mod_config()->tls;
@ -1051,6 +1051,13 @@ void fill_default_config() {
memcachedconf.max_retry = 3; memcachedconf.max_retry = 3;
memcachedconf.max_fail = 2; memcachedconf.max_fail = 2;
memcachedconf.interval = 10_min; memcachedconf.interval = 10_min;
memcachedconf.family = AF_UNSPEC;
}
auto &session_cacheconf = tlsconf.session_cache;
{
auto &memcachedconf = session_cacheconf.memcached;
memcachedconf.family = AF_UNSPEC;
} }
ticketconf.cipher = EVP_aes_128_cbc(); ticketconf.cipher = EVP_aes_128_cbc();
@ -1060,8 +1067,7 @@ void fill_default_config() {
auto &ocspconf = tlsconf.ocsp; auto &ocspconf = tlsconf.ocsp;
// ocsp update interval = 14400 secs = 4 hours, borrowed from h2o // ocsp update interval = 14400 secs = 4 hours, borrowed from h2o
ocspconf.update_interval = 4_h; ocspconf.update_interval = 4_h;
ocspconf.fetch_ocsp_response_file = ocspconf.fetch_ocsp_response_file = PKGDATADIR "/fetch-ocsp-response";
strcopy(PKGDATADIR "/fetch-ocsp-response");
} }
{ {
@ -1115,7 +1121,7 @@ void fill_default_config() {
accessconf.format = parse_log_format(DEFAULT_ACCESSLOG_FORMAT); accessconf.format = parse_log_format(DEFAULT_ACCESSLOG_FORMAT);
auto &errorconf = loggingconf.error; auto &errorconf = loggingconf.error;
errorconf.file = strcopy("/dev/stderr"); errorconf.file = "/dev/stderr";
} }
loggingconf.syslog_facility = LOG_DAEMON; loggingconf.syslog_facility = LOG_DAEMON;
@ -1159,6 +1165,7 @@ void fill_default_config() {
downstreamconf.connections_per_host = 8; downstreamconf.connections_per_host = 8;
downstreamconf.request_buffer_size = 16_k; downstreamconf.request_buffer_size = 16_k;
downstreamconf.response_buffer_size = 128_k; downstreamconf.response_buffer_size = 128_k;
downstreamconf.family = AF_UNSPEC;
} }
} }
@ -1263,10 +1270,12 @@ Connections:
--backlog=<N> --backlog=<N>
Set listen backlog size. Set listen backlog size.
Default: )" << get_config()->conn.listener.backlog << R"( Default: )" << get_config()->conn.listener.backlog << R"(
--backend-ipv4 --backend-address-family=(auto|IPv4|IPv6)
Resolve backend hostname to IPv4 address only. Specify address family of backend connections. If
--backend-ipv6 "auto" is given, both IPv4 and IPv6 are considered. If
Resolve backend hostname to IPv6 address only. "IPv4" is given, only IPv4 address is considered. If
"IPv6" is given, only IPv6 address is considered.
Default: auto
--backend-http-proxy-uri=<URI> --backend-http-proxy-uri=<URI>
Specify proxy URI in the form Specify proxy URI in the form
http://[<USER>:<PASS>@]<PROXY>:<PORT>. If a proxy http://[<USER>:<PASS>@]<PROXY>:<PORT>. If a proxy
@ -1520,16 +1529,23 @@ SSL/TLS:
ticket key sharing between nghttpx instances is not ticket key sharing between nghttpx instances is not
required. required.
--tls-ticket-key-memcached=<HOST>,<PORT> --tls-ticket-key-memcached=<HOST>,<PORT>
Specify address of memcached server to store session Specify address of memcached server to get TLS ticket
cache. This enables shared TLS ticket key between keys for session resumption. This enables shared TLS
multiple nghttpx instances. nghttpx does not set TLS ticket key between multiple nghttpx instances. nghttpx
ticket key to memcached. The external ticket key does not set TLS ticket key to memcached. The external
generator is required. nghttpx just gets TLS ticket ticket key generator is required. nghttpx just gets TLS
keys from memcached, and use them, possibly replacing ticket keys from memcached, and use them, possibly
current set of keys. It is up to extern TLS ticket key replacing current set of keys. It is up to extern TLS
generator to rotate keys frequently. See "TLS SESSION ticket key generator to rotate keys frequently. See
TICKET RESUMPTION" section in manual page to know the "TLS SESSION TICKET RESUMPTION" section in manual page
data format in memcached entry. to know the data format in memcached entry.
--tls-ticket-key-memcached-address-family=(auto|IPv4|IPv6)
Specify address family of memcached connections to get
TLS ticket keys. If "auto" is given, both IPv4 and IPv6
are considered. If "IPv4" is given, only IPv4 address
is considered. If "IPv6" is given, only IPv6 address is
considered.
Default: auto
--tls-ticket-key-memcached-interval=<DURATION> --tls-ticket-key-memcached-interval=<DURATION>
Set interval to get TLS ticket keys from memcached. Set interval to get TLS ticket keys from memcached.
Default: )" Default: )"
@ -1550,11 +1566,20 @@ SSL/TLS:
Specify cipher to encrypt TLS session ticket. Specify Specify cipher to encrypt TLS session ticket. Specify
either aes-128-cbc or aes-256-cbc. By default, either aes-128-cbc or aes-256-cbc. By default,
aes-128-cbc is used. aes-128-cbc is used.
--tls-ticket-key-memcached-tls
Enable SSL/TLS on memcached connections to get TLS
ticket keys.
--tls-ticket-key-memcached-cert-file=<PATH>
Path to client certificate for memcached connections to
get TLS ticket keys.
--tls-ticket-key-memcached-private-key-file=<PATH>
Path to client private key for memcached connections to
get TLS ticket keys.
--fetch-ocsp-response-file=<PATH> --fetch-ocsp-response-file=<PATH>
Path to fetch-ocsp-response script file. It should be Path to fetch-ocsp-response script file. It should be
absolute path. absolute path.
Default: )" Default: )" << get_config()->tls.ocsp.fetch_ocsp_response_file
<< get_config()->tls.ocsp.fetch_ocsp_response_file.get() << R"( << R"(
--ocsp-update-interval=<DURATION> --ocsp-update-interval=<DURATION>
Set interval to update OCSP response cache. Set interval to update OCSP response cache.
Default: )" Default: )"
@ -1564,6 +1589,22 @@ SSL/TLS:
Specify address of memcached server to store session Specify address of memcached server to store session
cache. This enables shared session cache between cache. This enables shared session cache between
multiple nghttpx instances. multiple nghttpx instances.
--tls-session-cache-memcached-address-family=(auto|IPv4|IPv6)
Specify address family of memcached connections to store
session cache. If "auto" is given, both IPv4 and IPv6
are considered. If "IPv4" is given, only IPv4 address
is considered. If "IPv6" is given, only IPv6 address is
considered.
Default: auto
--tls-session-cache-memcached-tls
Enable SSL/TLS on memcached connections to store session
cache.
--tls-session-cache-memcached-cert-file=<PATH>
Path to client certificate for memcached connections to
store session cache.
--tls-session-cache-memcached-private-key-file=<PATH>
Path to client private key for memcached connections to
store session cache.
--tls-dyn-rec-warmup-threshold=<SIZE> --tls-dyn-rec-warmup-threshold=<SIZE>
Specify the threshold size for TLS dynamic record size Specify the threshold size for TLS dynamic record size
behaviour. During a TLS session, after the threshold behaviour. During a TLS session, after the threshold
@ -1711,7 +1752,7 @@ Logging:
Set path to write error log. To reopen file, send USR1 Set path to write error log. To reopen file, send USR1
signal to nghttpx. stderr will be redirected to the signal to nghttpx. stderr will be redirected to the
error log file unless --errorlog-syslog is used. error log file unless --errorlog-syslog is used.
Default: )" << get_config()->logging.error.file.get() << R"( Default: )" << get_config()->logging.error.file << R"(
--errorlog-syslog --errorlog-syslog
Send error log to syslog. If this option is used, Send error log to syslog. If this option is used,
--errorlog-file option is ignored. --errorlog-file option is ignored.
@ -1852,7 +1893,7 @@ Scripting:
Misc: Misc:
--conf=<PATH> --conf=<PATH>
Load configuration from <PATH>. Load configuration from <PATH>.
Default: )" << get_config()->conf_path.get() << R"( Default: )" << get_config()->conf_path << R"(
--include=<PATH> --include=<PATH>
Load additional configurations from <PATH>. File <PATH> Load additional configurations from <PATH>. File <PATH>
is read when configuration parser encountered this is read when configuration parser encountered this
@ -1878,11 +1919,11 @@ namespace {
void process_options( void process_options(
int argc, char **argv, int argc, char **argv,
std::vector<std::pair<const char *, const char *>> &cmdcfgs) { std::vector<std::pair<const char *, const char *>> &cmdcfgs) {
if (conf_exists(get_config()->conf_path.get())) { if (conf_exists(get_config()->conf_path.c_str())) {
std::set<std::string> include_set; std::set<std::string> include_set;
if (load_config(get_config()->conf_path.get(), include_set) == -1) { if (load_config(get_config()->conf_path.c_str(), include_set) == -1) {
LOG(FATAL) << "Failed to load configuration from " LOG(FATAL) << "Failed to load configuration from "
<< get_config()->conf_path.get(); << get_config()->conf_path;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
assert(include_set.empty()); assert(include_set.empty());
@ -1945,8 +1986,8 @@ void process_options(
{ {
auto &dumpconf = http2conf.upstream.debug.dump; auto &dumpconf = http2conf.upstream.debug.dump;
if (dumpconf.request_header_file) { if (!dumpconf.request_header_file.empty()) {
auto path = dumpconf.request_header_file.get(); auto path = dumpconf.request_header_file.c_str();
auto f = open_file_for_write(path); auto f = open_file_for_write(path);
if (f == nullptr) { if (f == nullptr) {
@ -1966,8 +2007,8 @@ void process_options(
} }
} }
if (dumpconf.response_header_file) { if (!dumpconf.response_header_file.empty()) {
auto path = dumpconf.response_header_file.get(); auto path = dumpconf.response_header_file.c_str();
auto f = open_file_for_write(path); auto f = open_file_for_write(path);
if (f == nullptr) { if (f == nullptr) {
@ -2016,12 +2057,6 @@ void process_options(
listenerconf.addrs.push_back(std::move(addr)); listenerconf.addrs.push_back(std::move(addr));
} }
if (downstreamconf.ipv4 && downstreamconf.ipv6) {
LOG(FATAL) << "--backend-ipv4 and --backend-ipv6 cannot be used at the "
<< "same time.";
exit(EXIT_FAILURE);
}
if (upstreamconf.worker_connections == 0) { if (upstreamconf.worker_connections == 0) {
upstreamconf.worker_connections = std::numeric_limits<size_t>::max(); upstreamconf.worker_connections = std::numeric_limits<size_t>::max();
} }
@ -2050,7 +2085,7 @@ void process_options(
} }
if (!upstreamconf.no_tls && if (!upstreamconf.no_tls &&
(!tlsconf.private_key_file || !tlsconf.cert_file)) { (tlsconf.private_key_file.empty() || tlsconf.cert_file.empty())) {
print_usage(std::cerr); print_usage(std::cerr);
LOG(FATAL) << "Too few arguments"; LOG(FATAL) << "Too few arguments";
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
@ -2058,10 +2093,10 @@ void process_options(
if (!upstreamconf.no_tls && !tlsconf.ocsp.disabled) { if (!upstreamconf.no_tls && !tlsconf.ocsp.disabled) {
struct stat buf; struct stat buf;
if (stat(tlsconf.ocsp.fetch_ocsp_response_file.get(), &buf) != 0) { if (stat(tlsconf.ocsp.fetch_ocsp_response_file.c_str(), &buf) != 0) {
tlsconf.ocsp.disabled = true; tlsconf.ocsp.disabled = true;
LOG(WARN) << "--fetch-ocsp-response-file: " LOG(WARN) << "--fetch-ocsp-response-file: "
<< tlsconf.ocsp.fetch_ocsp_response_file.get() << tlsconf.ocsp.fetch_ocsp_response_file
<< " not found. OCSP stapling has been disabled."; << " not found. OCSP stapling has been disabled.";
} }
} }
@ -2069,18 +2104,18 @@ void process_options(
auto &addr_groups = downstreamconf.addr_groups; auto &addr_groups = downstreamconf.addr_groups;
if (addr_groups.empty()) { if (addr_groups.empty()) {
DownstreamAddr addr; DownstreamAddr addr{};
addr.host = ImmutableString::from_lit(DEFAULT_DOWNSTREAM_HOST); addr.host = ImmutableString::from_lit(DEFAULT_DOWNSTREAM_HOST);
addr.port = DEFAULT_DOWNSTREAM_PORT; addr.port = DEFAULT_DOWNSTREAM_PORT;
DownstreamAddrGroup g("/"); DownstreamAddrGroup g(StringRef::from_lit("/"));
g.addrs.push_back(std::move(addr)); g.addrs.push_back(std::move(addr));
mod_config()->router.add_route(g.pattern.get(), 1, addr_groups.size()); mod_config()->router.add_route(StringRef{g.pattern}, addr_groups.size());
addr_groups.push_back(std::move(g)); addr_groups.push_back(std::move(g));
} else if (get_config()->http2_proxy || get_config()->client_proxy) { } else if (get_config()->http2_proxy || get_config()->client_proxy) {
// We don't support host mapping in these cases. Move all // We don't support host mapping in these cases. Move all
// non-catch-all patterns to catch-all pattern. // non-catch-all patterns to catch-all pattern.
DownstreamAddrGroup catch_all("/"); DownstreamAddrGroup catch_all(StringRef::from_lit("/"));
for (auto &g : addr_groups) { for (auto &g : addr_groups) {
std::move(std::begin(g.addrs), std::end(g.addrs), std::move(std::begin(g.addrs), std::end(g.addrs),
std::back_inserter(catch_all.addrs)); std::back_inserter(catch_all.addrs));
@ -2088,7 +2123,7 @@ void process_options(
std::vector<DownstreamAddrGroup>().swap(addr_groups); std::vector<DownstreamAddrGroup>().swap(addr_groups);
// maybe not necessary? // maybe not necessary?
mod_config()->router = Router(); mod_config()->router = Router();
mod_config()->router.add_route(catch_all.pattern.get(), 1, mod_config()->router.add_route(StringRef{catch_all.pattern},
addr_groups.size()); addr_groups.size());
addr_groups.push_back(std::move(catch_all)); addr_groups.push_back(std::move(catch_all));
} }
@ -2100,11 +2135,11 @@ void process_options(
ssize_t catch_all_group = -1; ssize_t catch_all_group = -1;
for (size_t i = 0; i < addr_groups.size(); ++i) { for (size_t i = 0; i < addr_groups.size(); ++i) {
auto &g = addr_groups[i]; auto &g = addr_groups[i];
if (util::streq(g.pattern.get(), "/")) { if (g.pattern == "/") {
catch_all_group = i; catch_all_group = i;
} }
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Host-path pattern: group " << i << ": '" << g.pattern.get() LOG(INFO) << "Host-path pattern: group " << i << ": '" << g.pattern
<< "'"; << "'";
for (auto &addr : g.addrs) { for (auto &addr : g.addrs) {
LOG(INFO) << "group " << i << " -> " << addr.host.c_str() LOG(INFO) << "group " << i << " -> " << addr.host.c_str()
@ -2142,8 +2177,10 @@ void process_options(
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Use UNIX domain socket path " << path LOG(INFO) << "Use UNIX domain socket path " << path
<< " for backend connection"; << " for backend connection";
}
addr.addr.su.un.sun_family = AF_UNIX; addr.addr.su.un.sun_family = AF_UNIX;
// copy path including terminal NULL // copy path including terminal NULL
@ -2153,47 +2190,63 @@ void process_options(
continue; continue;
} }
addr.hostport = addr.hostport = ImmutableString(
ImmutableString(util::make_hostport(addr.host.c_str(), addr.port)); util::make_http_hostport(StringRef(addr.host), addr.port));
if (resolve_hostname( auto hostport = util::make_hostport(addr.host.c_str(), addr.port);
&addr.addr, addr.host.c_str(), addr.port,
downstreamconf.ipv4 if (resolve_hostname(&addr.addr, addr.host.c_str(), addr.port,
? AF_INET downstreamconf.family) == -1) {
: (downstreamconf.ipv6 ? AF_INET6 : AF_UNSPEC)) == -1) { LOG(FATAL) << "Resolving backend address failed: " << hostport;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
LOG(NOTICE) << "Resolved backend address: " << hostport << " -> "
<< util::to_numeric_addr(&addr.addr);
} }
} }
auto &proxy = mod_config()->downstream_http_proxy; auto &proxy = mod_config()->downstream_http_proxy;
if (!proxy.host.empty()) { if (!proxy.host.empty()) {
if (LOG_ENABLED(INFO)) { auto hostport = util::make_hostport(proxy.host.c_str(), proxy.port);
LOG(INFO) << "Resolving backend http proxy address";
}
if (resolve_hostname(&proxy.addr, proxy.host.c_str(), proxy.port, if (resolve_hostname(&proxy.addr, proxy.host.c_str(), proxy.port,
AF_UNSPEC) == -1) { AF_UNSPEC) == -1) {
LOG(FATAL) << "Resolving backend HTTP proxy address failed: " << hostport;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
LOG(NOTICE) << "Backend HTTP proxy address: " << hostport << " -> "
<< util::to_numeric_addr(&proxy.addr);
} }
{ {
auto &memcachedconf = tlsconf.session_cache.memcached; auto &memcachedconf = tlsconf.session_cache.memcached;
if (memcachedconf.host) { if (!memcachedconf.host.empty()) {
if (resolve_hostname(&memcachedconf.addr, memcachedconf.host.get(), auto hostport = util::make_hostport(StringRef{memcachedconf.host},
memcachedconf.port, AF_UNSPEC) == -1) { memcachedconf.port);
if (resolve_hostname(&memcachedconf.addr, memcachedconf.host.c_str(),
memcachedconf.port, memcachedconf.family) == -1) {
LOG(FATAL)
<< "Resolving memcached address for TLS session cache failed: "
<< hostport;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
LOG(NOTICE) << "Memcached address for TLS session cache: " << hostport
<< " -> " << util::to_numeric_addr(&memcachedconf.addr);
} }
} }
{ {
auto &memcachedconf = tlsconf.ticket.memcached; auto &memcachedconf = tlsconf.ticket.memcached;
if (memcachedconf.host) { if (!memcachedconf.host.empty()) {
if (resolve_hostname(&memcachedconf.addr, memcachedconf.host.get(), auto hostport = util::make_hostport(StringRef{memcachedconf.host},
memcachedconf.port, AF_UNSPEC) == -1) { memcachedconf.port);
if (resolve_hostname(&memcachedconf.addr, memcachedconf.host.c_str(),
memcachedconf.port, memcachedconf.family) == -1) {
LOG(FATAL) << "Resolving memcached address for TLS ticket key failed: "
<< hostport;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
LOG(NOTICE) << "Memcached address for TLS ticket key: " << hostport
<< " -> " << util::to_numeric_addr(&memcachedconf.addr);
} }
} }
@ -2400,6 +2453,21 @@ int main(int argc, char **argv) {
{SHRPX_OPT_BACKEND_HTTP1_TLS, no_argument, &flag, 106}, {SHRPX_OPT_BACKEND_HTTP1_TLS, no_argument, &flag, 106},
{SHRPX_OPT_BACKEND_TLS_SESSION_CACHE_PER_WORKER, required_argument, {SHRPX_OPT_BACKEND_TLS_SESSION_CACHE_PER_WORKER, required_argument,
&flag, 107}, &flag, 107},
{SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_TLS, no_argument, &flag, 108},
{SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE, required_argument,
&flag, 109},
{SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_PRIVATE_KEY_FILE,
required_argument, &flag, 110},
{SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_TLS, no_argument, &flag, 111},
{SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_CERT_FILE, required_argument, &flag,
112},
{SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE, required_argument,
&flag, 113},
{SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_ADDRESS_FAMILY, required_argument,
&flag, 114},
{SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_ADDRESS_FAMILY,
required_argument, &flag, 115},
{SHRPX_OPT_BACKEND_ADDRESS_FAMILY, required_argument, &flag, 116},
{nullptr, 0, nullptr, 0}}; {nullptr, 0, nullptr, 0}};
int option_index = 0; int option_index = 0;
@ -2493,7 +2561,7 @@ int main(int argc, char **argv) {
break; break;
case 12: case 12:
// --conf // --conf
mod_config()->conf_path = strcopy(optarg); mod_config()->conf_path = optarg;
break; break;
case 14: case 14:
// --syslog-facility // --syslog-facility
@ -2858,6 +2926,48 @@ int main(int argc, char **argv) {
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_TLS_SESSION_CACHE_PER_WORKER, cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_TLS_SESSION_CACHE_PER_WORKER,
optarg); optarg);
break; break;
case 108:
// --tls-session-cache-memcached-tls
cmdcfgs.emplace_back(SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_TLS, "yes");
break;
case 109:
// --tls-session-cache-memcached-cert-file
cmdcfgs.emplace_back(SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE,
optarg);
break;
case 110:
// --tls-session-cache-memcached-private-key-file
cmdcfgs.emplace_back(
SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_PRIVATE_KEY_FILE, optarg);
break;
case 111:
// --tls-ticket-key-memcached-tls
cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_TLS, "yes");
break;
case 112:
// --tls-ticket-key-memcached-cert-file
cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_CERT_FILE,
optarg);
break;
case 113:
// --tls-ticket-key-memcached-private-key-file
cmdcfgs.emplace_back(
SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE, optarg);
break;
case 114:
// --tls-ticket-key-memcached-address-family
cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_ADDRESS_FAMILY,
optarg);
break;
case 115:
// --tls-session-cache-memcached-address-family
cmdcfgs.emplace_back(
SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_ADDRESS_FAMILY, optarg);
break;
case 116:
// --backend-address-family
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_ADDRESS_FAMILY, optarg);
break;
default: default:
break; break;
} }

View File

@ -389,7 +389,7 @@ ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl,
pinned_http2sessions_( pinned_http2sessions_(
get_config()->conn.downstream.proto == PROTO_HTTP2 get_config()->conn.downstream.proto == PROTO_HTTP2
? make_unique<std::vector<ssize_t>>( ? make_unique<std::vector<ssize_t>>(
get_config()->conn.downstream.addr_groups.size(), -1) worker->get_downstream_addr_groups().size(), -1)
: nullptr), : nullptr),
ipaddr_(ipaddr), ipaddr_(ipaddr),
port_(port), port_(port),
@ -664,8 +664,8 @@ std::unique_ptr<DownstreamConnection>
ClientHandler::get_downstream_connection(Downstream *downstream) { ClientHandler::get_downstream_connection(Downstream *downstream) {
size_t group; size_t group;
auto &downstreamconf = get_config()->conn.downstream; auto &downstreamconf = get_config()->conn.downstream;
auto &groups = downstreamconf.addr_groups;
auto catch_all = downstreamconf.addr_group_catch_all; auto catch_all = downstreamconf.addr_group_catch_all;
auto &groups = worker_->get_downstream_addr_groups();
const auto &req = downstream->request(); const auto &req = downstream->request();
@ -681,16 +681,19 @@ ClientHandler::get_downstream_connection(Downstream *downstream) {
} else { } else {
auto &router = get_config()->router; auto &router = get_config()->router;
if (!req.authority.empty()) { if (!req.authority.empty()) {
group = match_downstream_addr_group(router, req.authority, req.path, group =
groups, catch_all); match_downstream_addr_group(router, StringRef{req.authority},
StringRef{req.path}, groups, catch_all);
} else { } else {
auto h = req.fs.header(http2::HD_HOST); auto h = req.fs.header(http2::HD_HOST);
if (h) { if (h) {
group = match_downstream_addr_group(router, h->value, req.path, groups, group =
catch_all); match_downstream_addr_group(router, StringRef{h->value},
StringRef{req.path}, groups, catch_all);
} else { } else {
group = match_downstream_addr_group(router, "", req.path, groups, group =
catch_all); match_downstream_addr_group(router, StringRef::from_lit(""),
StringRef{req.path}, groups, catch_all);
} }
} }
} }
@ -743,10 +746,6 @@ MemchunkPool *ClientHandler::get_mcpool() { return worker_->get_mcpool(); }
SSL *ClientHandler::get_ssl() const { return conn_.tls.ssl; } SSL *ClientHandler::get_ssl() const { return conn_.tls.ssl; }
ConnectBlocker *ClientHandler::get_connect_blocker() const {
return worker_->get_connect_blocker();
}
void ClientHandler::direct_http2_upgrade() { void ClientHandler::direct_http2_upgrade() {
upstream_ = make_unique<Http2Upstream>(this); upstream_ = make_unique<Http2Upstream>(this);
alpn_ = NGHTTP2_CLEARTEXT_PROTO_VERSION_ID; alpn_ = NGHTTP2_CLEARTEXT_PROTO_VERSION_ID;

View File

@ -99,7 +99,6 @@ public:
get_downstream_connection(Downstream *downstream); get_downstream_connection(Downstream *downstream);
MemchunkPool *get_mcpool(); MemchunkPool *get_mcpool();
SSL *get_ssl() const; SSL *get_ssl() const;
ConnectBlocker *get_connect_blocker() const;
// Call this function when HTTP/2 connection header is received at // Call this function when HTTP/2 connection header is received at
// the start of the connection. // the start of the connection.
void direct_http2_upgrade(); void direct_http2_upgrade();

View File

@ -54,9 +54,7 @@
#include "shrpx_log.h" #include "shrpx_log.h"
#include "shrpx_ssl.h" #include "shrpx_ssl.h"
#include "shrpx_http.h" #include "shrpx_http.h"
#include "http2.h"
#include "util.h" #include "util.h"
#include "template.h"
#include "base64.h" #include "base64.h"
namespace shrpx { namespace shrpx {
@ -80,42 +78,6 @@ TicketKeys::~TicketKeys() {
} }
} }
DownstreamAddr::DownstreamAddr(const DownstreamAddr &other)
: addr(other.addr),
host(other.host),
hostport(other.hostport),
port(other.port),
host_unix(other.host_unix) {}
DownstreamAddr &DownstreamAddr::operator=(const DownstreamAddr &other) {
if (this == &other) {
return *this;
}
addr = other.addr;
host = other.host;
hostport = other.hostport;
port = other.port;
host_unix = other.host_unix;
return *this;
}
DownstreamAddrGroup::DownstreamAddrGroup(const DownstreamAddrGroup &other)
: pattern(strcopy(other.pattern)), addrs(other.addrs) {}
DownstreamAddrGroup &DownstreamAddrGroup::
operator=(const DownstreamAddrGroup &other) {
if (this == &other) {
return *this;
}
pattern = strcopy(other.pattern);
addrs = other.addrs;
return *this;
}
namespace { namespace {
int split_host_port(char *host, size_t hostlen, uint16_t *port_ptr, int split_host_port(char *host, size_t hostlen, uint16_t *port_ptr,
const char *hostport, size_t hostportlen) { const char *hostport, size_t hostportlen) {
@ -279,7 +241,7 @@ std::string read_passwd_from_file(const char *filename) {
return line; return line;
} }
std::pair<std::string, std::string> parse_header(const char *optarg) { Headers::value_type parse_header(const char *optarg) {
const auto *colon = strchr(optarg, ':'); const auto *colon = strchr(optarg, ':');
if (colon == nullptr || colon == optarg) { if (colon == nullptr || colon == optarg) {
@ -290,16 +252,15 @@ std::pair<std::string, std::string> parse_header(const char *optarg) {
for (; *value == '\t' || *value == ' '; ++value) for (; *value == '\t' || *value == ' '; ++value)
; ;
auto p = std::make_pair(std::string(optarg, colon), auto p =
std::string(value, strlen(value))); Header(std::string(optarg, colon), std::string(value, strlen(value)));
util::inp_strlower(p.first); util::inp_strlower(p.name);
if (!nghttp2_check_header_name( if (!nghttp2_check_header_name(
reinterpret_cast<const uint8_t *>(p.first.c_str()), p.first.size()) || reinterpret_cast<const uint8_t *>(p.name.c_str()), p.name.size()) ||
!nghttp2_check_header_value( !nghttp2_check_header_value(
reinterpret_cast<const uint8_t *>(p.second.c_str()), reinterpret_cast<const uint8_t *>(p.value.c_str()), p.value.size())) {
p.second.size())) { return Header();
return {"", ""};
} }
return p; return p;
@ -575,6 +536,26 @@ std::vector<LogFragment> parse_log_format(const char *optarg) {
return res; return res;
} }
namespace {
int parse_address_family(int *dest, const char *opt, const char *optarg) {
if (util::strieq("auto", optarg)) {
*dest = AF_UNSPEC;
return 0;
}
if (util::strieq("IPv4", optarg)) {
*dest = AF_INET;
return 0;
}
if (util::strieq("IPv6", optarg)) {
*dest = AF_INET6;
return 0;
}
LOG(ERROR) << opt << ": bad value: '" << optarg << "'";
return -1;
}
} // namespace
namespace { namespace {
int parse_duration(ev_tstamp *dest, const char *opt, const char *optarg) { int parse_duration(ev_tstamp *dest, const char *opt, const char *optarg) {
auto t = util::parse_duration_with_unit(optarg); auto t = util::parse_duration_with_unit(optarg);
@ -616,7 +597,7 @@ void parse_mapping(const DownstreamAddr &addr, const char *src) {
pattern += http2::normalize_path(slash, raw_pattern.second); pattern += http2::normalize_path(slash, raw_pattern.second);
} }
for (auto &g : addr_groups) { for (auto &g : addr_groups) {
if (g.pattern.get() == pattern) { if (g.pattern == pattern) {
g.addrs.push_back(addr); g.addrs.push_back(addr);
done = true; done = true;
break; break;
@ -625,11 +606,10 @@ void parse_mapping(const DownstreamAddr &addr, const char *src) {
if (done) { if (done) {
continue; continue;
} }
DownstreamAddrGroup g(pattern); DownstreamAddrGroup g(StringRef{pattern});
g.addrs.push_back(addr); g.addrs.push_back(addr);
mod_config()->router.add_route(g.pattern.get(), strlen(g.pattern.get()), mod_config()->router.add_route(StringRef{g.pattern}, addr_groups.size());
addr_groups.size());
addr_groups.push_back(std::move(g)); addr_groups.push_back(std::move(g));
} }
@ -673,6 +653,7 @@ enum {
SHRPX_OPTID_ADD_X_FORWARDED_FOR, SHRPX_OPTID_ADD_X_FORWARDED_FOR,
SHRPX_OPTID_ALTSVC, SHRPX_OPTID_ALTSVC,
SHRPX_OPTID_BACKEND, SHRPX_OPTID_BACKEND,
SHRPX_OPTID_BACKEND_ADDRESS_FAMILY,
SHRPX_OPTID_BACKEND_HTTP_PROXY_URI, SHRPX_OPTID_BACKEND_HTTP_PROXY_URI,
SHRPX_OPTID_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND, SHRPX_OPTID_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND,
SHRPX_OPTID_BACKEND_HTTP1_CONNECTIONS_PER_HOST, SHRPX_OPTID_BACKEND_HTTP1_CONNECTIONS_PER_HOST,
@ -758,12 +739,20 @@ enum {
SHRPX_OPTID_TLS_DYN_REC_WARMUP_THRESHOLD, SHRPX_OPTID_TLS_DYN_REC_WARMUP_THRESHOLD,
SHRPX_OPTID_TLS_PROTO_LIST, SHRPX_OPTID_TLS_PROTO_LIST,
SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED, SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED,
SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_ADDRESS_FAMILY,
SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE,
SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_PRIVATE_KEY_FILE,
SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_TLS,
SHRPX_OPTID_TLS_TICKET_KEY_CIPHER, SHRPX_OPTID_TLS_TICKET_KEY_CIPHER,
SHRPX_OPTID_TLS_TICKET_KEY_FILE, SHRPX_OPTID_TLS_TICKET_KEY_FILE,
SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED, SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED,
SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_ADDRESS_FAMILY,
SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_CERT_FILE,
SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_INTERVAL, SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_INTERVAL,
SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL, SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL,
SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_RETRY, SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_RETRY,
SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE,
SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_TLS,
SHRPX_OPTID_USER, SHRPX_OPTID_USER,
SHRPX_OPTID_VERIFY_CLIENT, SHRPX_OPTID_VERIFY_CLIENT,
SHRPX_OPTID_VERIFY_CLIENT_CACERT, SHRPX_OPTID_VERIFY_CLIENT_CACERT,
@ -1204,6 +1193,11 @@ int option_lookup_token(const char *name, size_t namelen) {
return SHRPX_OPTID_FRONTEND_WRITE_TIMEOUT; return SHRPX_OPTID_FRONTEND_WRITE_TIMEOUT;
} }
break; break;
case 'y':
if (util::strieq_l("backend-address-famil", name, 21)) {
return SHRPX_OPTID_BACKEND_ADDRESS_FAMILY;
}
break;
} }
break; break;
case 23: case 23:
@ -1325,6 +1319,9 @@ int option_lookup_token(const char *name, size_t namelen) {
if (util::strieq_l("http2-max-concurrent-stream", name, 27)) { if (util::strieq_l("http2-max-concurrent-stream", name, 27)) {
return SHRPX_OPTID_HTTP2_MAX_CONCURRENT_STREAMS; return SHRPX_OPTID_HTTP2_MAX_CONCURRENT_STREAMS;
} }
if (util::strieq_l("tls-ticket-key-memcached-tl", name, 27)) {
return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_TLS;
}
break; break;
} }
break; break;
@ -1337,6 +1334,15 @@ int option_lookup_token(const char *name, size_t namelen) {
break; break;
} }
break; break;
case 31:
switch (name[30]) {
case 's':
if (util::strieq_l("tls-session-cache-memcached-tl", name, 30)) {
return SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_TLS;
}
break;
}
break;
case 33: case 33:
switch (name[32]) { switch (name[32]) {
case 'l': case 'l':
@ -1351,6 +1357,11 @@ int option_lookup_token(const char *name, size_t namelen) {
break; break;
case 34: case 34:
switch (name[33]) { switch (name[33]) {
case 'e':
if (util::strieq_l("tls-ticket-key-memcached-cert-fil", name, 33)) {
return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_CERT_FILE;
}
break;
case 'r': case 'r':
if (util::strieq_l("frontend-http2-dump-request-heade", name, 33)) { if (util::strieq_l("frontend-http2-dump-request-heade", name, 33)) {
return SHRPX_OPTID_FRONTEND_HTTP2_DUMP_REQUEST_HEADER; return SHRPX_OPTID_FRONTEND_HTTP2_DUMP_REQUEST_HEADER;
@ -1396,6 +1407,11 @@ int option_lookup_token(const char *name, size_t namelen) {
break; break;
case 37: case 37:
switch (name[36]) { switch (name[36]) {
case 'e':
if (util::strieq_l("tls-session-cache-memcached-cert-fil", name, 36)) {
return SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE;
}
break;
case 's': case 's':
if (util::strieq_l("frontend-http2-connection-window-bit", name, 36)) { if (util::strieq_l("frontend-http2-connection-window-bit", name, 36)) {
return SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS; return SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS;
@ -1412,6 +1428,45 @@ int option_lookup_token(const char *name, size_t namelen) {
break; break;
} }
break; break;
case 39:
switch (name[38]) {
case 'y':
if (util::strieq_l("tls-ticket-key-memcached-address-famil", name, 38)) {
return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_ADDRESS_FAMILY;
}
break;
}
break;
case 41:
switch (name[40]) {
case 'e':
if (util::strieq_l("tls-ticket-key-memcached-private-key-fil", name,
40)) {
return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE;
}
break;
}
break;
case 42:
switch (name[41]) {
case 'y':
if (util::strieq_l("tls-session-cache-memcached-address-famil", name,
41)) {
return SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_ADDRESS_FAMILY;
}
break;
}
break;
case 44:
switch (name[43]) {
case 'e':
if (util::strieq_l("tls-session-cache-memcached-private-key-fil", name,
43)) {
return SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_PRIVATE_KEY_FILE;
}
break;
}
break;
} }
return -1; return -1;
} }
@ -1431,7 +1486,7 @@ int parse_config(const char *opt, const char *optarg,
if (!pat_delim) { if (!pat_delim) {
pat_delim = optarg + optarglen; pat_delim = optarg + optarglen;
} }
DownstreamAddr addr; DownstreamAddr addr{};
if (util::istarts_with(optarg, SHRPX_UNIX_PATH_PREFIX)) { if (util::istarts_with(optarg, SHRPX_UNIX_PATH_PREFIX)) {
auto path = optarg + str_size(SHRPX_UNIX_PATH_PREFIX); auto path = optarg + str_size(SHRPX_UNIX_PATH_PREFIX);
addr.host = ImmutableString(path, pat_delim); addr.host = ImmutableString(path, pat_delim);
@ -1567,7 +1622,7 @@ int parse_config(const char *opt, const char *optarg,
return parse_duration(&mod_config()->http2.timeout.stream_write, opt, return parse_duration(&mod_config()->http2.timeout.stream_write, opt,
optarg); optarg);
case SHRPX_OPTID_ACCESSLOG_FILE: case SHRPX_OPTID_ACCESSLOG_FILE:
mod_config()->logging.access.file = strcopy(optarg); mod_config()->logging.access.file = optarg;
return 0; return 0;
case SHRPX_OPTID_ACCESSLOG_SYSLOG: case SHRPX_OPTID_ACCESSLOG_SYSLOG:
@ -1579,7 +1634,7 @@ int parse_config(const char *opt, const char *optarg,
return 0; return 0;
case SHRPX_OPTID_ERRORLOG_FILE: case SHRPX_OPTID_ERRORLOG_FILE:
mod_config()->logging.error.file = strcopy(optarg); mod_config()->logging.error.file = optarg;
return 0; return 0;
case SHRPX_OPTID_ERRORLOG_SYSLOG: case SHRPX_OPTID_ERRORLOG_SYSLOG:
@ -1673,7 +1728,7 @@ int parse_config(const char *opt, const char *optarg,
return 0; return 0;
case SHRPX_OPTID_PID_FILE: case SHRPX_OPTID_PID_FILE:
mod_config()->pid_file = strcopy(optarg); mod_config()->pid_file = optarg;
return 0; return 0;
case SHRPX_OPTID_USER: { case SHRPX_OPTID_USER: {
@ -1683,14 +1738,14 @@ int parse_config(const char *opt, const char *optarg,
<< strerror(errno); << strerror(errno);
return -1; return -1;
} }
mod_config()->user = strcopy(pwd->pw_name); mod_config()->user = pwd->pw_name;
mod_config()->uid = pwd->pw_uid; mod_config()->uid = pwd->pw_uid;
mod_config()->gid = pwd->pw_gid; mod_config()->gid = pwd->pw_gid;
return 0; return 0;
} }
case SHRPX_OPTID_PRIVATE_KEY_FILE: case SHRPX_OPTID_PRIVATE_KEY_FILE:
mod_config()->tls.private_key_file = strcopy(optarg); mod_config()->tls.private_key_file = optarg;
return 0; return 0;
case SHRPX_OPTID_PRIVATE_KEY_PASSWD_FILE: { case SHRPX_OPTID_PRIVATE_KEY_PASSWD_FILE: {
@ -1699,16 +1754,16 @@ int parse_config(const char *opt, const char *optarg,
LOG(ERROR) << opt << ": Couldn't read key file's passwd from " << optarg; LOG(ERROR) << opt << ": Couldn't read key file's passwd from " << optarg;
return -1; return -1;
} }
mod_config()->tls.private_key_passwd = strcopy(passwd); mod_config()->tls.private_key_passwd = passwd;
return 0; return 0;
} }
case SHRPX_OPTID_CERTIFICATE_FILE: case SHRPX_OPTID_CERTIFICATE_FILE:
mod_config()->tls.cert_file = strcopy(optarg); mod_config()->tls.cert_file = optarg;
return 0; return 0;
case SHRPX_OPTID_DH_PARAM_FILE: case SHRPX_OPTID_DH_PARAM_FILE:
mod_config()->tls.dh_param_file = strcopy(optarg); mod_config()->tls.dh_param_file = optarg;
return 0; return 0;
case SHRPX_OPTID_SUBCERT: { case SHRPX_OPTID_SUBCERT: {
@ -1749,7 +1804,7 @@ int parse_config(const char *opt, const char *optarg,
return 0; return 0;
} }
case SHRPX_OPTID_CIPHERS: case SHRPX_OPTID_CIPHERS:
mod_config()->tls.ciphers = strcopy(optarg); mod_config()->tls.ciphers = optarg;
return 0; return 0;
case SHRPX_OPTID_CLIENT: case SHRPX_OPTID_CLIENT:
@ -1761,15 +1816,21 @@ int parse_config(const char *opt, const char *optarg,
return 0; return 0;
case SHRPX_OPTID_CACERT: case SHRPX_OPTID_CACERT:
mod_config()->tls.cacert = strcopy(optarg); mod_config()->tls.cacert = optarg;
return 0; return 0;
case SHRPX_OPTID_BACKEND_IPV4: case SHRPX_OPTID_BACKEND_IPV4:
mod_config()->conn.downstream.ipv4 = util::strieq(optarg, "yes"); LOG(WARN) << opt
<< ": deprecated. Use backend-address-family=IPv4 instead.";
mod_config()->conn.downstream.family = AF_INET;
return 0; return 0;
case SHRPX_OPTID_BACKEND_IPV6: case SHRPX_OPTID_BACKEND_IPV6:
mod_config()->conn.downstream.ipv6 = util::strieq(optarg, "yes"); LOG(WARN) << opt
<< ": deprecated. Use backend-address-family=IPv6 instead.";
mod_config()->conn.downstream.family = AF_INET6;
return 0; return 0;
case SHRPX_OPTID_BACKEND_HTTP_PROXY_URI: { case SHRPX_OPTID_BACKEND_HTTP_PROXY_URI: {
@ -1846,25 +1907,23 @@ int parse_config(const char *opt, const char *optarg,
return 0; return 0;
case SHRPX_OPTID_VERIFY_CLIENT_CACERT: case SHRPX_OPTID_VERIFY_CLIENT_CACERT:
mod_config()->tls.client_verify.cacert = strcopy(optarg); mod_config()->tls.client_verify.cacert = optarg;
return 0; return 0;
case SHRPX_OPTID_CLIENT_PRIVATE_KEY_FILE: case SHRPX_OPTID_CLIENT_PRIVATE_KEY_FILE:
mod_config()->tls.client.private_key_file = strcopy(optarg); mod_config()->tls.client.private_key_file = optarg;
return 0; return 0;
case SHRPX_OPTID_CLIENT_CERT_FILE: case SHRPX_OPTID_CLIENT_CERT_FILE:
mod_config()->tls.client.cert_file = strcopy(optarg); mod_config()->tls.client.cert_file = optarg;
return 0; return 0;
case SHRPX_OPTID_FRONTEND_HTTP2_DUMP_REQUEST_HEADER: case SHRPX_OPTID_FRONTEND_HTTP2_DUMP_REQUEST_HEADER:
mod_config()->http2.upstream.debug.dump.request_header_file = mod_config()->http2.upstream.debug.dump.request_header_file = optarg;
strcopy(optarg);
return 0; return 0;
case SHRPX_OPTID_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER: case SHRPX_OPTID_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER:
mod_config()->http2.upstream.debug.dump.response_header_file = mod_config()->http2.upstream.debug.dump.response_header_file = optarg;
strcopy(optarg);
return 0; return 0;
case SHRPX_OPTID_HTTP2_NO_COOKIE_CRUMBLING: case SHRPX_OPTID_HTTP2_NO_COOKIE_CRUMBLING:
@ -1927,7 +1986,7 @@ int parse_config(const char *opt, const char *optarg,
case SHRPX_OPTID_ADD_REQUEST_HEADER: case SHRPX_OPTID_ADD_REQUEST_HEADER:
case SHRPX_OPTID_ADD_RESPONSE_HEADER: { case SHRPX_OPTID_ADD_RESPONSE_HEADER: {
auto p = parse_header(optarg); auto p = parse_header(optarg);
if (p.first.empty()) { if (p.name.empty()) {
LOG(ERROR) << opt << ": invalid header field: " << optarg; LOG(ERROR) << opt << ": invalid header field: " << optarg;
return -1; return -1;
} }
@ -2025,7 +2084,7 @@ int parse_config(const char *opt, const char *optarg,
return parse_uint(&mod_config()->http2.downstream.connections_per_worker, return parse_uint(&mod_config()->http2.downstream.connections_per_worker,
opt, optarg); opt, optarg);
case SHRPX_OPTID_FETCH_OCSP_RESPONSE_FILE: case SHRPX_OPTID_FETCH_OCSP_RESPONSE_FILE:
mod_config()->tls.ocsp.fetch_ocsp_response_file = strcopy(optarg); mod_config()->tls.ocsp.fetch_ocsp_response_file = optarg;
return 0; return 0;
case SHRPX_OPTID_OCSP_UPDATE_INTERVAL: case SHRPX_OPTID_OCSP_UPDATE_INTERVAL:
@ -2093,7 +2152,7 @@ int parse_config(const char *opt, const char *optarg,
} }
auto &memcachedconf = mod_config()->tls.session_cache.memcached; auto &memcachedconf = mod_config()->tls.session_cache.memcached;
memcachedconf.host = strcopy(host); memcachedconf.host = host;
memcachedconf.port = port; memcachedconf.port = port;
return 0; return 0;
@ -2105,7 +2164,7 @@ int parse_config(const char *opt, const char *optarg,
} }
auto &memcachedconf = mod_config()->tls.ticket.memcached; auto &memcachedconf = mod_config()->tls.ticket.memcached;
memcachedconf.host = strcopy(host); memcachedconf.host = host;
memcachedconf.port = port; memcachedconf.port = port;
return 0; return 0;
@ -2146,7 +2205,7 @@ int parse_config(const char *opt, const char *optarg,
case SHRPX_OPTID_MRUBY_FILE: case SHRPX_OPTID_MRUBY_FILE:
#ifdef HAVE_MRUBY #ifdef HAVE_MRUBY
mod_config()->mruby_file = strcopy(optarg); mod_config()->mruby_file = optarg;
#else // !HAVE_MRUBY #else // !HAVE_MRUBY
LOG(WARN) << opt LOG(WARN) << opt
<< ": ignored because mruby support is disabled at build time."; << ": ignored because mruby support is disabled at build time.";
@ -2229,6 +2288,39 @@ int parse_config(const char *opt, const char *optarg,
case SHRPX_OPTID_BACKEND_TLS_SESSION_CACHE_PER_WORKER: case SHRPX_OPTID_BACKEND_TLS_SESSION_CACHE_PER_WORKER:
return parse_uint(&mod_config()->tls.downstream_session_cache_per_worker, return parse_uint(&mod_config()->tls.downstream_session_cache_per_worker,
opt, optarg); opt, optarg);
case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_TLS:
mod_config()->tls.session_cache.memcached.tls = util::strieq(optarg, "yes");
return 0;
case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE:
mod_config()->tls.session_cache.memcached.cert_file = optarg;
return 0;
case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_PRIVATE_KEY_FILE:
mod_config()->tls.session_cache.memcached.private_key_file = optarg;
return 0;
case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_TLS:
mod_config()->tls.ticket.memcached.tls = util::strieq(optarg, "yes");
return 0;
case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_CERT_FILE:
mod_config()->tls.ticket.memcached.cert_file = optarg;
return 0;
case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE:
mod_config()->tls.ticket.memcached.private_key_file = optarg;
return 0;
case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_ADDRESS_FAMILY:
return parse_address_family(&mod_config()->tls.ticket.memcached.family, opt,
optarg);
case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_ADDRESS_FAMILY:
return parse_address_family(
&mod_config()->tls.session_cache.memcached.family, opt, optarg);
case SHRPX_OPTID_BACKEND_ADDRESS_FAMILY:
return parse_address_family(&mod_config()->conn.downstream.family, opt,
optarg);
case SHRPX_OPTID_CONF: case SHRPX_OPTID_CONF:
LOG(WARN) << "conf: ignored"; LOG(WARN) << "conf: ignored";
@ -2409,17 +2501,15 @@ int int_syslog_facility(const char *strfacility) {
} }
namespace { namespace {
size_t size_t match_downstream_addr_group_host(
match_downstream_addr_group_host(const Router &router, const std::string &host, const Router &router, const StringRef &host, const StringRef &path,
const char *path, size_t pathlen, const std::vector<DownstreamAddrGroup> &groups, size_t catch_all) {
const std::vector<DownstreamAddrGroup> &groups, if (path.empty() || path[0] != '/') {
size_t catch_all) { auto group = router.match(host, StringRef::from_lit("/"));
if (pathlen == 0 || *path != '/') {
auto group = router.match(host, "/", 1);
if (group != -1) { if (group != -1) {
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Found pattern with query " << host LOG(INFO) << "Found pattern with query " << host
<< ", matched pattern=" << groups[group].pattern.get(); << ", matched pattern=" << groups[group].pattern;
} }
return group; return group;
} }
@ -2428,24 +2518,23 @@ match_downstream_addr_group_host(const Router &router, const std::string &host,
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Perform mapping selection, using host=" << host LOG(INFO) << "Perform mapping selection, using host=" << host
<< ", path=" << std::string(path, pathlen); << ", path=" << path;
} }
auto group = router.match(host, path, pathlen); auto group = router.match(host, path);
if (group != -1) { if (group != -1) {
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Found pattern with query " << host LOG(INFO) << "Found pattern with query " << host << path
<< std::string(path, pathlen) << ", matched pattern=" << groups[group].pattern;
<< ", matched pattern=" << groups[group].pattern.get();
} }
return group; return group;
} }
group = router.match("", path, pathlen); group = router.match("", path);
if (group != -1) { if (group != -1) {
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Found pattern with query " << std::string(path, pathlen) LOG(INFO) << "Found pattern with query " << path
<< ", matched pattern=" << groups[group].pattern.get(); << ", matched pattern=" << groups[group].pattern;
} }
return group; return group;
} }
@ -2457,11 +2546,9 @@ match_downstream_addr_group_host(const Router &router, const std::string &host,
} }
} // namespace } // namespace
size_t size_t match_downstream_addr_group(
match_downstream_addr_group(const Router &router, const std::string &hostport, const Router &router, const StringRef &hostport, const StringRef &raw_path,
const std::string &raw_path, const std::vector<DownstreamAddrGroup> &groups, size_t catch_all) {
const std::vector<DownstreamAddrGroup> &groups,
size_t catch_all) {
if (std::find(std::begin(hostport), std::end(hostport), '/') != if (std::find(std::begin(hostport), std::end(hostport), '/') !=
std::end(hostport)) { std::end(hostport)) {
// We use '/' specially, and if '/' is included in host, it breaks // We use '/' specially, and if '/' is included in host, it breaks
@ -2471,12 +2558,11 @@ match_downstream_addr_group(const Router &router, const std::string &hostport,
auto fragment = std::find(std::begin(raw_path), std::end(raw_path), '#'); auto fragment = std::find(std::begin(raw_path), std::end(raw_path), '#');
auto query = std::find(std::begin(raw_path), fragment, '?'); auto query = std::find(std::begin(raw_path), fragment, '?');
auto path = raw_path.c_str(); auto path = StringRef{std::begin(raw_path), query};
auto pathlen = query - std::begin(raw_path);
if (hostport.empty()) { if (hostport.empty()) {
return match_downstream_addr_group_host(router, hostport, path, pathlen, return match_downstream_addr_group_host(router, hostport, path, groups,
groups, catch_all); catch_all);
} }
std::string host; std::string host;
@ -2499,7 +2585,7 @@ match_downstream_addr_group(const Router &router, const std::string &hostport,
} }
util::inp_strlower(host); util::inp_strlower(host);
return match_downstream_addr_group_host(router, host, path, pathlen, groups, return match_downstream_addr_group_host(router, StringRef{host}, path, groups,
catch_all); catch_all);
} }

View File

@ -52,12 +52,15 @@
#include "shrpx_router.h" #include "shrpx_router.h"
#include "template.h" #include "template.h"
#include "http2.h"
#include "network.h"
using namespace nghttp2; using namespace nghttp2;
namespace shrpx { namespace shrpx {
struct LogFragment; struct LogFragment;
class ConnectBlocker;
namespace ssl { namespace ssl {
@ -209,22 +212,26 @@ constexpr char SHRPX_OPT_NO_HTTP2_CIPHER_BLACK_LIST[] =
constexpr char SHRPX_OPT_BACKEND_HTTP1_TLS[] = "backend-http1-tls"; constexpr char SHRPX_OPT_BACKEND_HTTP1_TLS[] = "backend-http1-tls";
constexpr char SHRPX_OPT_BACKEND_TLS_SESSION_CACHE_PER_WORKER[] = constexpr char SHRPX_OPT_BACKEND_TLS_SESSION_CACHE_PER_WORKER[] =
"backend-tls-session-cache-per-worker"; "backend-tls-session-cache-per-worker";
constexpr char SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_TLS[] =
"tls-session-cache-memcached-tls";
constexpr char SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE[] =
"tls-session-cache-memcached-cert-file";
constexpr char SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_PRIVATE_KEY_FILE[] =
"tls-session-cache-memcached-private-key-file";
constexpr char SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_ADDRESS_FAMILY[] =
"tls-session-cache-memcached-address-family";
constexpr char SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_TLS[] =
"tls-ticket-key-memcached-tls";
constexpr char SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_CERT_FILE[] =
"tls-ticket-key-memcached-cert-file";
constexpr char SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE[] =
"tls-ticket-key-memcached-private-key-file";
constexpr char SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_ADDRESS_FAMILY[] =
"tls-ticket-key-memcached-address-family";
constexpr char SHRPX_OPT_BACKEND_ADDRESS_FAMILY[] = "backend-address-family";
constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8; constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8;
union sockaddr_union {
sockaddr_storage storage;
sockaddr sa;
sockaddr_in6 in6;
sockaddr_in in;
sockaddr_un un;
};
struct Address {
size_t len;
union sockaddr_union su;
};
enum shrpx_proto { PROTO_HTTP2, PROTO_HTTP }; enum shrpx_proto { PROTO_HTTP2, PROTO_HTTP };
enum shrpx_forwarded_param { enum shrpx_forwarded_param {
@ -270,18 +277,23 @@ struct UpstreamAddr {
int fd; int fd;
}; };
struct DownstreamAddr { struct TLSSessionCache {
DownstreamAddr() : addr{}, port(0), host_unix(false) {} // ASN1 representation of SSL_SESSION object. See
DownstreamAddr(const DownstreamAddr &other); // i2d_SSL_SESSION(3SSL).
DownstreamAddr(DownstreamAddr &&) = default; std::vector<uint8_t> session_data;
DownstreamAddr &operator=(const DownstreamAddr &other); // The last time stamp when this cache entry is created or updated.
DownstreamAddr &operator=(DownstreamAddr &&other) = default; ev_tstamp last_updated;
};
struct DownstreamAddr {
Address addr; Address addr;
// backend address. If |host_unix| is true, this is UNIX domain // backend address. If |host_unix| is true, this is UNIX domain
// socket path. // socket path.
ImmutableString host; ImmutableString host;
ImmutableString hostport; ImmutableString hostport;
ConnectBlocker *connect_blocker;
// Client side TLS session cache
TLSSessionCache tls_session_cache;
// backend port. 0 if |host_unix| is true. // backend port. 0 if |host_unix| is true.
uint16_t port; uint16_t port;
// true if |host| contains UNIX domain socket path. // true if |host| contains UNIX domain socket path.
@ -289,13 +301,10 @@ struct DownstreamAddr {
}; };
struct DownstreamAddrGroup { struct DownstreamAddrGroup {
DownstreamAddrGroup(const std::string &pattern) : pattern(strcopy(pattern)) {} DownstreamAddrGroup(const StringRef &pattern)
DownstreamAddrGroup(const DownstreamAddrGroup &other); : pattern(pattern.c_str(), pattern.size()) {}
DownstreamAddrGroup(DownstreamAddrGroup &&) = default;
DownstreamAddrGroup &operator=(const DownstreamAddrGroup &other);
DownstreamAddrGroup &operator=(DownstreamAddrGroup &&) = default;
std::unique_ptr<char[]> pattern; ImmutableString pattern;
std::vector<DownstreamAddr> addrs; std::vector<DownstreamAddr> addrs;
}; };
@ -334,7 +343,12 @@ struct TLSConfig {
struct { struct {
Address addr; Address addr;
uint16_t port; uint16_t port;
std::unique_ptr<char[]> host; // Hostname of memcached server. This is also used as SNI field
// if TLS is enabled.
ImmutableString host;
// Client private key and certificate for authentication
ImmutableString private_key_file;
ImmutableString cert_file;
ev_tstamp interval; ev_tstamp interval;
// Maximum number of retries when getting TLS ticket key from // Maximum number of retries when getting TLS ticket key from
// mamcached, due to network error. // mamcached, due to network error.
@ -342,6 +356,10 @@ struct TLSConfig {
// Maximum number of consecutive error from memcached, when this // Maximum number of consecutive error from memcached, when this
// limit reached, TLS ticket is disabled. // limit reached, TLS ticket is disabled.
size_t max_fail; size_t max_fail;
// Address family of memcached connection. One of either
// AF_INET, AF_INET6 or AF_UNSPEC.
int family;
bool tls;
} memcached; } memcached;
std::vector<std::string> files; std::vector<std::string> files;
const EVP_CIPHER *cipher; const EVP_CIPHER *cipher;
@ -354,7 +372,16 @@ struct TLSConfig {
struct { struct {
Address addr; Address addr;
uint16_t port; uint16_t port;
std::unique_ptr<char[]> host; // Hostname of memcached server. This is also used as SNI field
// if TLS is enabled.
ImmutableString host;
// Client private key and certificate for authentication
ImmutableString private_key_file;
ImmutableString cert_file;
// Address family of memcached connection. One of either
// AF_INET, AF_INET6 or AF_UNSPEC.
int family;
bool tls;
} memcached; } memcached;
} session_cache; } session_cache;
@ -367,7 +394,7 @@ struct TLSConfig {
// OCSP realted configurations // OCSP realted configurations
struct { struct {
ev_tstamp update_interval; ev_tstamp update_interval;
std::unique_ptr<char[]> fetch_ocsp_response_file; ImmutableString fetch_ocsp_response_file;
bool disabled; bool disabled;
} ocsp; } ocsp;
@ -375,14 +402,14 @@ struct TLSConfig {
struct { struct {
// Path to file containing CA certificate solely used for client // Path to file containing CA certificate solely used for client
// certificate validation // certificate validation
std::unique_ptr<char[]> cacert; ImmutableString cacert;
bool enabled; bool enabled;
} client_verify; } client_verify;
// Client private key and certificate used in backend connections. // Client private key and certificate used in backend connections.
struct { struct {
std::unique_ptr<char[]> private_key_file; ImmutableString private_key_file;
std::unique_ptr<char[]> cert_file; ImmutableString cert_file;
} client; } client;
// The list of (private key file, certificate file) pair // The list of (private key file, certificate file) pair
@ -399,12 +426,12 @@ struct TLSConfig {
long int tls_proto_mask; long int tls_proto_mask;
std::string backend_sni_name; std::string backend_sni_name;
std::chrono::seconds session_timeout; std::chrono::seconds session_timeout;
std::unique_ptr<char[]> private_key_file; ImmutableString private_key_file;
std::unique_ptr<char[]> private_key_passwd; ImmutableString private_key_passwd;
std::unique_ptr<char[]> cert_file; ImmutableString cert_file;
std::unique_ptr<char[]> dh_param_file; ImmutableString dh_param_file;
std::unique_ptr<char[]> ciphers; ImmutableString ciphers;
std::unique_ptr<char[]> cacert; ImmutableString cacert;
bool insecure; bool insecure;
bool no_http2_cipher_black_list; bool no_http2_cipher_black_list;
}; };
@ -430,8 +457,8 @@ struct HttpConfig {
bool strip_incoming; bool strip_incoming;
} xff; } xff;
std::vector<AltSvc> altsvcs; std::vector<AltSvc> altsvcs;
std::vector<std::pair<std::string, std::string>> add_request_headers; Headers add_request_headers;
std::vector<std::pair<std::string, std::string>> add_response_headers; Headers add_response_headers;
StringRef server_name; StringRef server_name;
size_t request_header_field_buffer; size_t request_header_field_buffer;
size_t max_request_header_fields; size_t max_request_header_fields;
@ -446,8 +473,8 @@ struct Http2Config {
struct { struct {
struct { struct {
struct { struct {
std::unique_ptr<char[]> request_header_file; ImmutableString request_header_file;
std::unique_ptr<char[]> response_header_file; ImmutableString response_header_file;
FILE *request_header; FILE *request_header;
FILE *response_header; FILE *response_header;
} dump; } dump;
@ -477,12 +504,12 @@ struct Http2Config {
struct LoggingConfig { struct LoggingConfig {
struct { struct {
std::vector<LogFragment> format; std::vector<LogFragment> format;
std::unique_ptr<char[]> file; ImmutableString file;
// Send accesslog to syslog, ignoring accesslog_file. // Send accesslog to syslog, ignoring accesslog_file.
bool syslog; bool syslog;
} access; } access;
struct { struct {
std::unique_ptr<char[]> file; ImmutableString file;
// Send errorlog to syslog, ignoring errorlog_file. // Send errorlog to syslog, ignoring errorlog_file.
bool syslog; bool syslog;
} error; } error;
@ -537,13 +564,12 @@ struct ConnectionConfig {
size_t response_buffer_size; size_t response_buffer_size;
// downstream protocol; this will be determined by given options. // downstream protocol; this will be determined by given options.
shrpx_proto proto; shrpx_proto proto;
// Address family of backend connection. One of either AF_INET,
// AF_INET6 or AF_UNSPEC. This is ignored if backend connection
// is made via Unix domain socket.
int family;
bool no_tls; bool no_tls;
bool http1_tls; bool http1_tls;
// true if IPv4 only; ipv4 and ipv6 are mutually exclusive; and
// (ipv4 && ipv6) must be false.
bool ipv4;
// true if IPv6 only
bool ipv6;
} downstream; } downstream;
}; };
@ -555,10 +581,10 @@ struct Config {
TLSConfig tls; TLSConfig tls;
LoggingConfig logging; LoggingConfig logging;
ConnectionConfig conn; ConnectionConfig conn;
std::unique_ptr<char[]> pid_file; ImmutableString pid_file;
std::unique_ptr<char[]> conf_path; ImmutableString conf_path;
std::unique_ptr<char[]> user; ImmutableString user;
std::unique_ptr<char[]> mruby_file; ImmutableString mruby_file;
char **original_argv; char **original_argv;
char **argv; char **argv;
char *cwd; char *cwd;
@ -603,7 +629,7 @@ std::string read_passwd_from_file(const char *filename);
// like "NAME: VALUE". We require that NAME is non empty string. ":" // like "NAME: VALUE". We require that NAME is non empty string. ":"
// is allowed at the start of the NAME, but NAME == ":" is not // is allowed at the start of the NAME, but NAME == ":" is not
// allowed. This function returns pair of NAME and VALUE. // allowed. This function returns pair of NAME and VALUE.
std::pair<std::string, std::string> parse_header(const char *optarg); Headers::value_type parse_header(const char *optarg);
std::vector<LogFragment> parse_log_format(const char *optarg); std::vector<LogFragment> parse_log_format(const char *optarg);
@ -630,7 +656,7 @@ read_tls_ticket_key_file(const std::vector<std::string> &files,
// group. The catch-all group index is given in |catch_all|. All // group. The catch-all group index is given in |catch_all|. All
// patterns are given in |groups|. // patterns are given in |groups|.
size_t match_downstream_addr_group( size_t match_downstream_addr_group(
const Router &router, const std::string &hostport, const std::string &path, const Router &router, const StringRef &hostport, const StringRef &path,
const std::vector<DownstreamAddrGroup> &groups, size_t catch_all); const std::vector<DownstreamAddrGroup> &groups, size_t catch_all);
} // namespace shrpx } // namespace shrpx

View File

@ -38,32 +38,32 @@ namespace shrpx {
void test_shrpx_config_parse_header(void) { void test_shrpx_config_parse_header(void) {
auto p = parse_header("a: b"); auto p = parse_header("a: b");
CU_ASSERT("a" == p.first); CU_ASSERT("a" == p.name);
CU_ASSERT("b" == p.second); CU_ASSERT("b" == p.value);
p = parse_header("a: b"); p = parse_header("a: b");
CU_ASSERT("a" == p.first); CU_ASSERT("a" == p.name);
CU_ASSERT("b" == p.second); CU_ASSERT("b" == p.value);
p = parse_header(":a: b"); p = parse_header(":a: b");
CU_ASSERT(p.first.empty()); CU_ASSERT(p.name.empty());
p = parse_header("a: :b"); p = parse_header("a: :b");
CU_ASSERT("a" == p.first); CU_ASSERT("a" == p.name);
CU_ASSERT(":b" == p.second); CU_ASSERT(":b" == p.value);
p = parse_header(": b"); p = parse_header(": b");
CU_ASSERT(p.first.empty()); CU_ASSERT(p.name.empty());
p = parse_header("alpha: bravo charlie"); p = parse_header("alpha: bravo charlie");
CU_ASSERT("alpha" == p.first); CU_ASSERT("alpha" == p.name);
CU_ASSERT("bravo charlie" == p.second); CU_ASSERT("bravo charlie" == p.value);
p = parse_header("a,: b"); p = parse_header("a,: b");
CU_ASSERT(p.first.empty()); CU_ASSERT(p.name.empty());
p = parse_header("a: b\x0a"); p = parse_header("a: b\x0a");
CU_ASSERT(p.first.empty()); CU_ASSERT(p.name.empty());
} }
void test_shrpx_config_parse_log_format(void) { void test_shrpx_config_parse_log_format(void) {
@ -256,7 +256,7 @@ void test_shrpx_config_match_downstream_addr_group(void) {
for (size_t i = 0; i < groups.size(); ++i) { for (size_t i = 0; i < groups.size(); ++i) {
auto &g = groups[i]; auto &g = groups[i];
router.add_route(g.pattern.get(), strlen(g.pattern.get()), i); router.add_route(StringRef{g.pattern}, i);
} }
CU_ASSERT(0 == match_downstream_addr_group(router, "nghttp2.org", "/", groups, CU_ASSERT(0 == match_downstream_addr_group(router, "nghttp2.org", "/", groups,

View File

@ -26,20 +26,16 @@
namespace shrpx { namespace shrpx {
namespace {
const ev_tstamp INITIAL_SLEEP = 2.;
} // namespace
namespace { namespace {
void connect_blocker_cb(struct ev_loop *loop, ev_timer *w, int revents) { void connect_blocker_cb(struct ev_loop *loop, ev_timer *w, int revents) {
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
LOG(INFO) << "unblock downstream connection"; LOG(INFO) << "Unblock";
} }
} }
} // namespace } // namespace
ConnectBlocker::ConnectBlocker(struct ev_loop *loop) ConnectBlocker::ConnectBlocker(std::mt19937 &gen, struct ev_loop *loop)
: loop_(loop), sleep_(INITIAL_SLEEP) { : gen_(gen), loop_(loop), fail_count_(0) {
ev_timer_init(&timer_, connect_blocker_cb, 0., 0.); ev_timer_init(&timer_, connect_blocker_cb, 0., 0.);
} }
@ -47,18 +43,27 @@ ConnectBlocker::~ConnectBlocker() { ev_timer_stop(loop_, &timer_); }
bool ConnectBlocker::blocked() const { return ev_is_active(&timer_); } bool ConnectBlocker::blocked() const { return ev_is_active(&timer_); }
void ConnectBlocker::on_success() { sleep_ = INITIAL_SLEEP; } void ConnectBlocker::on_success() { fail_count_ = 0; }
namespace {
constexpr size_t MAX_BACKOFF_EXP = 10;
} // namespace
void ConnectBlocker::on_failure() { void ConnectBlocker::on_failure() {
if (ev_is_active(&timer_)) { if (ev_is_active(&timer_)) {
return; return;
} }
sleep_ = std::min(128., sleep_ * 2); ++fail_count_;
LOG(WARN) << "connect failure, start sleeping " << sleep_; auto max_backoff = (1 << std::min(MAX_BACKOFF_EXP, fail_count_)) - 1;
auto dist = std::uniform_int_distribution<>(0, max_backoff);
auto backoff = dist(gen_);
ev_timer_set(&timer_, sleep_, 0.); LOG(WARN) << "Could not connect " << fail_count_
<< " times in a row; sleep for " << backoff << " seconds";
ev_timer_set(&timer_, backoff, 0.);
ev_timer_start(loop_, &timer_); ev_timer_start(loop_, &timer_);
} }

View File

@ -27,13 +27,15 @@
#include "shrpx.h" #include "shrpx.h"
#include <random>
#include <ev.h> #include <ev.h>
namespace shrpx { namespace shrpx {
class ConnectBlocker { class ConnectBlocker {
public: public:
ConnectBlocker(struct ev_loop *loop); ConnectBlocker(std::mt19937 &gen, struct ev_loop *loop);
~ConnectBlocker(); ~ConnectBlocker();
// Returns true if making connection is not allowed. // Returns true if making connection is not allowed.
@ -41,14 +43,18 @@ public:
// Call this function if connect operation succeeded. This will // Call this function if connect operation succeeded. This will
// reset sleep_ to minimum value. // reset sleep_ to minimum value.
void on_success(); void on_success();
// Call this function if connect operation failed. This will start // Call this function if connect operations failed. This will start
// timer and blocks connection establishment for sleep_ seconds. // timer and blocks connection establishment with exponential
// backoff.
void on_failure(); void on_failure();
private: private:
std::mt19937 gen_;
ev_timer timer_; ev_timer timer_;
struct ev_loop *loop_; struct ev_loop *loop_;
ev_tstamp sleep_; // The number of consecutive connection failure. Reset to 0 on
// success.
size_t fail_count_;
}; };
} // namespace } // namespace

View File

@ -183,7 +183,7 @@ int ConnectionHandler::create_single_worker() {
nb_.get() nb_.get()
#endif // HAVE_NEVERBLEED #endif // HAVE_NEVERBLEED
); );
auto cl_ssl_ctx = ssl::setup_client_ssl_context( auto cl_ssl_ctx = ssl::setup_downstream_client_ssl_context(
#ifdef HAVE_NEVERBLEED #ifdef HAVE_NEVERBLEED
nb_.get() nb_.get()
#endif // HAVE_NEVERBLEED #endif // HAVE_NEVERBLEED
@ -193,8 +193,23 @@ int ConnectionHandler::create_single_worker() {
all_ssl_ctx_.push_back(cl_ssl_ctx); all_ssl_ctx_.push_back(cl_ssl_ctx);
} }
single_worker_ = make_unique<Worker>(loop_, sv_ssl_ctx, cl_ssl_ctx, cert_tree, auto &tlsconf = get_config()->tls;
ticket_keys_); auto &memcachedconf = get_config()->tls.session_cache.memcached;
SSL_CTX *session_cache_ssl_ctx = nullptr;
if (memcachedconf.tls) {
session_cache_ssl_ctx = ssl::create_ssl_client_context(
#ifdef HAVE_NEVERBLEED
nb_.get(),
#endif // HAVE_NEVERBLEED
StringRef{tlsconf.cacert}, StringRef{memcachedconf.cert_file},
StringRef{memcachedconf.private_key_file}, StringRef(), nullptr);
all_ssl_ctx_.push_back(session_cache_ssl_ctx);
}
single_worker_ =
make_unique<Worker>(loop_, sv_ssl_ctx, cl_ssl_ctx, session_cache_ssl_ctx,
cert_tree, ticket_keys_);
#ifdef HAVE_MRUBY #ifdef HAVE_MRUBY
if (single_worker_->create_mruby_context() != 0) { if (single_worker_->create_mruby_context() != 0) {
return -1; return -1;
@ -215,7 +230,7 @@ int ConnectionHandler::create_worker_thread(size_t num) {
nb_.get() nb_.get()
#endif // HAVE_NEVERBLEED #endif // HAVE_NEVERBLEED
); );
auto cl_ssl_ctx = ssl::setup_client_ssl_context( auto cl_ssl_ctx = ssl::setup_downstream_client_ssl_context(
#ifdef HAVE_NEVERBLEED #ifdef HAVE_NEVERBLEED
nb_.get() nb_.get()
#endif // HAVE_NEVERBLEED #endif // HAVE_NEVERBLEED
@ -225,11 +240,25 @@ int ConnectionHandler::create_worker_thread(size_t num) {
all_ssl_ctx_.push_back(cl_ssl_ctx); all_ssl_ctx_.push_back(cl_ssl_ctx);
} }
auto &tlsconf = get_config()->tls;
auto &memcachedconf = get_config()->tls.session_cache.memcached;
for (size_t i = 0; i < num; ++i) { for (size_t i = 0; i < num; ++i) {
auto loop = ev_loop_new(0); auto loop = ev_loop_new(0);
auto worker = make_unique<Worker>(loop, sv_ssl_ctx, cl_ssl_ctx, cert_tree, SSL_CTX *session_cache_ssl_ctx = nullptr;
ticket_keys_); if (memcachedconf.tls) {
session_cache_ssl_ctx = ssl::create_ssl_client_context(
#ifdef HAVE_NEVERBLEED
nb_.get(),
#endif // HAVE_NEVERBLEED
StringRef{tlsconf.cacert}, StringRef{memcachedconf.cert_file},
StringRef{memcachedconf.private_key_file}, StringRef{}, nullptr);
all_ssl_ctx_.push_back(session_cache_ssl_ctx);
}
auto worker =
make_unique<Worker>(loop, sv_ssl_ctx, cl_ssl_ctx, session_cache_ssl_ctx,
cert_tree, ticket_keys_);
#ifdef HAVE_MRUBY #ifdef HAVE_MRUBY
if (worker->create_mruby_context() != 0) { if (worker->create_mruby_context() != 0) {
return -1; return -1;
@ -432,7 +461,8 @@ int ConnectionHandler::start_ocsp_update(const char *cert_file) {
assert(!ev_is_active(&ocsp_.chldev)); assert(!ev_is_active(&ocsp_.chldev));
char *const argv[] = { char *const argv[] = {
const_cast<char *>(get_config()->tls.ocsp.fetch_ocsp_response_file.get()), const_cast<char *>(
get_config()->tls.ocsp.fetch_ocsp_response_file.c_str()),
const_cast<char *>(cert_file), nullptr}; const_cast<char *>(cert_file), nullptr};
char *const envp[] = {nullptr}; char *const envp[] = {nullptr};
@ -728,6 +758,22 @@ void ConnectionHandler::schedule_next_tls_ticket_key_memcached_get(
ev_timer_start(loop_, w); ev_timer_start(loop_, w);
} }
SSL_CTX *ConnectionHandler::create_tls_ticket_key_memcached_ssl_ctx() {
auto &tlsconf = get_config()->tls;
auto &memcachedconf = get_config()->tls.ticket.memcached;
auto ssl_ctx = ssl::create_ssl_client_context(
#ifdef HAVE_NEVERBLEED
nb_.get(),
#endif // HAVE_NEVERBLEED
StringRef{tlsconf.cacert}, StringRef{memcachedconf.cert_file},
StringRef{memcachedconf.private_key_file}, StringRef{}, nullptr);
all_ssl_ctx_.push_back(ssl_ctx);
return ssl_ctx;
}
#ifdef HAVE_NEVERBLEED #ifdef HAVE_NEVERBLEED
void ConnectionHandler::set_neverbleed(std::unique_ptr<neverbleed_t> nb) { void ConnectionHandler::set_neverbleed(std::unique_ptr<neverbleed_t> nb) {
nb_ = std::move(nb); nb_ = std::move(nb);

View File

@ -129,6 +129,7 @@ public:
on_tls_ticket_key_get_success(const std::shared_ptr<TicketKeys> &ticket_keys, on_tls_ticket_key_get_success(const std::shared_ptr<TicketKeys> &ticket_keys,
ev_timer *w); ev_timer *w);
void schedule_next_tls_ticket_key_memcached_get(ev_timer *w); void schedule_next_tls_ticket_key_memcached_get(ev_timer *w);
SSL_CTX *create_tls_ticket_key_memcached_ssl_ctx();
#ifdef HAVE_NEVERBLEED #ifdef HAVE_NEVERBLEED
void set_neverbleed(std::unique_ptr<neverbleed_t> nb); void set_neverbleed(std::unique_ptr<neverbleed_t> nb);

View File

@ -237,15 +237,15 @@ void Downstream::force_resume_read() {
} }
namespace { namespace {
const Headers::value_type *search_header_linear(const Headers &headers, const Headers::value_type *
const StringRef &name) { search_header_linear_backwards(const Headers &headers, const StringRef &name) {
const Headers::value_type *res = nullptr; for (auto it = headers.rbegin(); it != headers.rend(); ++it) {
for (auto &kv : headers) { auto &kv = *it;
if (kv.name == name) { if (kv.name == name) {
res = &kv; return &kv;
} }
} }
return res; return nullptr;
} }
} // namespace } // namespace
@ -329,23 +329,20 @@ void Downstream::crumble_request_cookie(std::vector<nghttp2_nv> &nva) {
} }
namespace { namespace {
void add_header(bool &key_prev, size_t &sum, Headers &headers, std::string name, void add_header(bool &key_prev, size_t &sum, Headers &headers,
std::string value) { const StringRef &name, const StringRef &value, bool no_index,
int32_t token) {
key_prev = true; key_prev = true;
sum += name.size() + value.size(); sum += name.size() + value.size();
headers.emplace_back(std::move(name), std::move(value)); headers.emplace_back(name.str(), value.str(), no_index, token);
} }
} // namespace } // namespace
namespace { namespace {
void add_header(size_t &sum, Headers &headers, const uint8_t *name, void add_header(size_t &sum, Headers &headers, const StringRef &name,
size_t namelen, const uint8_t *value, size_t valuelen, const StringRef &value, bool no_index, int32_t token) {
bool no_index, int16_t token) { sum += name.size() + value.size();
sum += namelen + valuelen; headers.emplace_back(name.str(), value.str(), no_index, token);
headers.emplace_back(
std::string(reinterpret_cast<const char *>(name), namelen),
std::string(reinterpret_cast<const char *>(value), valuelen), no_index,
token);
} }
} // namespace } // namespace
@ -356,6 +353,8 @@ void append_last_header_key(bool &key_prev, size_t &sum, Headers &headers,
sum += len; sum += len;
auto &item = headers.back(); auto &item = headers.back();
item.name.append(data, len); item.name.append(data, len);
util::inp_strlower(item.name);
item.token = http2::lookup_token(item.name);
} }
} // namespace } // namespace
@ -369,24 +368,14 @@ void append_last_header_value(bool &key_prev, size_t &sum, Headers &headers,
} }
} // namespace } // namespace
int FieldStore::index_headers() { int FieldStore::parse_content_length() {
http2::init_hdidx(hdidx_);
content_length = -1; content_length = -1;
for (size_t i = 0; i < headers_.size(); ++i) { for (auto &kv : headers_) {
auto &kv = headers_[i]; if (kv.token != http2::HD_CONTENT_LENGTH) {
util::inp_strlower(kv.name);
auto token = http2::lookup_token(
reinterpret_cast<const uint8_t *>(kv.name.c_str()), kv.name.size());
if (token < 0) {
continue; continue;
} }
kv.token = token;
http2::index_header(hdidx_, token, i);
if (token == http2::HD_CONTENT_LENGTH) {
auto len = util::parse_uint(kv.value); auto len = util::parse_uint(kv.value);
if (len == -1) { if (len == -1) {
return -1; return -1;
@ -396,40 +385,45 @@ int FieldStore::index_headers() {
} }
content_length = len; content_length = len;
} }
}
return 0; return 0;
} }
const Headers::value_type *FieldStore::header(int16_t token) const { const Headers::value_type *FieldStore::header(int32_t token) const {
return http2::get_header(hdidx_, token, headers_); for (auto it = headers_.rbegin(); it != headers_.rend(); ++it) {
auto &kv = *it;
if (kv.token == token) {
return &kv;
}
}
return nullptr;
} }
Headers::value_type *FieldStore::header(int16_t token) { Headers::value_type *FieldStore::header(int32_t token) {
return http2::get_header(hdidx_, token, headers_); for (auto it = headers_.rbegin(); it != headers_.rend(); ++it) {
auto &kv = *it;
if (kv.token == token) {
return &kv;
}
}
return nullptr;
} }
const Headers::value_type *FieldStore::header(const StringRef &name) const { const Headers::value_type *FieldStore::header(const StringRef &name) const {
return search_header_linear(headers_, name); return search_header_linear_backwards(headers_, name);
} }
void FieldStore::add_header(std::string name, std::string value) { void FieldStore::add_header_lower(const StringRef &name, const StringRef &value,
shrpx::add_header(header_key_prev_, buffer_size_, headers_, std::move(name), bool no_index) {
std::move(value)); auto low_name = name.str();
util::inp_strlower(low_name);
auto token = http2::lookup_token(low_name);
shrpx::add_header(header_key_prev_, buffer_size_, headers_,
StringRef{low_name}, value, no_index, token);
} }
void FieldStore::add_header(std::string name, std::string value, void FieldStore::add_header_token(const StringRef &name, const StringRef &value,
int16_t token) { bool no_index, int32_t token) {
http2::index_header(hdidx_, token, headers_.size()); shrpx::add_header(buffer_size_, headers_, name, value, no_index, token);
buffer_size_ += name.size() + value.size();
headers_.emplace_back(std::move(name), std::move(value), false, token);
}
void FieldStore::add_header(const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen,
bool no_index, int16_t token) {
http2::index_header(hdidx_, token, headers_.size());
shrpx::add_header(buffer_size_, headers_, name, namelen, value, valuelen,
no_index, token);
} }
void FieldStore::append_last_header_key(const char *data, size_t len) { void FieldStore::append_last_header_key(const char *data, size_t len) {
@ -442,23 +436,23 @@ void FieldStore::append_last_header_value(const char *data, size_t len) {
data, len); data, len);
} }
void FieldStore::clear_headers() { void FieldStore::clear_headers() { headers_.clear(); }
headers_.clear();
http2::init_hdidx(hdidx_); void FieldStore::add_trailer_lower(const StringRef &name,
const StringRef &value, bool no_index) {
auto low_name = name.str();
util::inp_strlower(low_name);
auto token = http2::lookup_token(low_name);
shrpx::add_header(trailer_key_prev_, buffer_size_, trailers_,
StringRef{low_name}, value, no_index, token);
} }
void FieldStore::add_trailer(const uint8_t *name, size_t namelen, void FieldStore::add_trailer_token(const StringRef &name,
const uint8_t *value, size_t valuelen, const StringRef &value, bool no_index,
bool no_index, int16_t token) { int32_t token) {
// we never index trailer fields. Header size limit should be // Header size limit should be applied to all header and trailer
// applied to all header and trailer fields combined. // fields combined.
shrpx::add_header(buffer_size_, trailers_, name, namelen, value, valuelen, shrpx::add_header(buffer_size_, trailers_, name, value, no_index, token);
no_index, -1);
}
void FieldStore::add_trailer(std::string name, std::string value) {
shrpx::add_header(trailer_key_prev_, buffer_size_, trailers_, std::move(name),
std::move(value));
} }
void FieldStore::append_last_trailer_key(const char *data, size_t len) { void FieldStore::append_last_trailer_key(const char *data, size_t len) {

View File

@ -56,7 +56,6 @@ public:
buffer_size_(0), buffer_size_(0),
header_key_prev_(false), header_key_prev_(false),
trailer_key_prev_(false) { trailer_key_prev_(false) {
http2::init_hdidx(hdidx_);
headers_.reserve(headers_initial_capacity); headers_.reserve(headers_initial_capacity);
} }
@ -74,33 +73,33 @@ public:
// multiple header have |name| as name, return last occurrence from // multiple header have |name| as name, return last occurrence from
// the beginning. If no such header is found, returns nullptr. // the beginning. If no such header is found, returns nullptr.
// This function must be called after headers are indexed // This function must be called after headers are indexed
const Headers::value_type *header(int16_t token) const; const Headers::value_type *header(int32_t token) const;
Headers::value_type *header(int16_t token); Headers::value_type *header(int32_t token);
// Returns pointer to the header field with the name |name|. If no // Returns pointer to the header field with the name |name|. If no
// such header is found, returns nullptr. // such header is found, returns nullptr.
const Headers::value_type *header(const StringRef &name) const; const Headers::value_type *header(const StringRef &name) const;
void add_header(std::string name, std::string value); void add_header_lower(const StringRef &name, const StringRef &value,
void add_header(std::string name, std::string value, int16_t token); bool no_index);
void add_header(const uint8_t *name, size_t namelen, const uint8_t *value, void add_header_token(const StringRef &name, const StringRef &value,
size_t valuelen, bool no_index, int16_t token); bool no_index, int32_t token);
void append_last_header_key(const char *data, size_t len); void append_last_header_key(const char *data, size_t len);
void append_last_header_value(const char *data, size_t len); void append_last_header_value(const char *data, size_t len);
bool header_key_prev() const { return header_key_prev_; } bool header_key_prev() const { return header_key_prev_; }
// Lower the header field names and indexes header fields. If there // Parses content-length, and records it in the field. If there are
// is any invalid headers (e.g., multiple Content-Length having // multiple Content-Length, returns -1.
// different values), returns -1. int parse_content_length();
int index_headers();
// Empties headers. // Empties headers.
void clear_headers(); void clear_headers();
void add_trailer(const uint8_t *name, size_t namelen, const uint8_t *value, void add_trailer_lower(const StringRef &name, const StringRef &value,
size_t valuelen, bool no_index, int16_t token); bool no_index);
void add_trailer(std::string name, std::string value); void add_trailer_token(const StringRef &name, const StringRef &value,
bool no_index, int32_t token);
void append_last_trailer_key(const char *data, size_t len); void append_last_trailer_key(const char *data, size_t len);
void append_last_trailer_value(const char *data, size_t len); void append_last_trailer_value(const char *data, size_t len);
@ -115,7 +114,6 @@ private:
// trailer fields. For HTTP/1.1, trailer fields are only included // trailer fields. For HTTP/1.1, trailer fields are only included
// with chunked encoding. For HTTP/2, there is no such limit. // with chunked encoding. For HTTP/2, there is no such limit.
Headers trailers_; Headers trailers_;
http2::HeaderIndex hdidx_;
// Sum of the length of name and value in headers_ and trailers_. // Sum of the length of name and value in headers_ and trailers_.
// This could also be increased by add_extra_buffer_size() to take // This could also be increased by add_extra_buffer_size() to take
// into account for request URI in case of HTTP/1.x request. // into account for request URI in case of HTTP/1.x request.

View File

@ -32,17 +32,24 @@
namespace shrpx { namespace shrpx {
void test_downstream_field_store_index_headers(void) { void test_downstream_field_store_add_header_lower(void) {
FieldStore fs(0); FieldStore fs(0);
fs.add_header("1", "0"); fs.add_header_lower(StringRef::from_lit("1"), StringRef::from_lit("0"),
fs.add_header("2", "1"); false);
fs.add_header("Charlie", "2"); fs.add_header_lower(StringRef::from_lit("2"), StringRef::from_lit("1"),
fs.add_header("Alpha", "3"); false);
fs.add_header("Delta", "4"); fs.add_header_lower(StringRef::from_lit("Charlie"), StringRef::from_lit("2"),
fs.add_header("BravO", "5"); false);
fs.add_header(":method", "6"); fs.add_header_lower(StringRef::from_lit("Alpha"), StringRef::from_lit("3"),
fs.add_header(":authority", "7"); false);
fs.index_headers(); fs.add_header_lower(StringRef::from_lit("Delta"), StringRef::from_lit("4"),
false);
fs.add_header_lower(StringRef::from_lit("BravO"), StringRef::from_lit("5"),
false);
fs.add_header_lower(StringRef::from_lit(":method"), StringRef::from_lit("6"),
false);
fs.add_header_lower(StringRef::from_lit(":authority"),
StringRef::from_lit("7"), false);
auto ans = Headers{{"1", "0"}, auto ans = Headers{{"1", "0"},
{"2", "1"}, {"2", "1"},
@ -57,10 +64,13 @@ void test_downstream_field_store_index_headers(void) {
void test_downstream_field_store_header(void) { void test_downstream_field_store_header(void) {
FieldStore fs(0); FieldStore fs(0);
fs.add_header("alpha", "0"); fs.add_header_token(StringRef::from_lit("alpha"), StringRef::from_lit("0"),
fs.add_header(":authority", "1"); false, -1);
fs.add_header("content-length", "2"); fs.add_header_token(StringRef::from_lit(":authority"),
fs.index_headers(); StringRef::from_lit("1"), false, http2::HD__AUTHORITY);
fs.add_header_token(StringRef::from_lit("content-length"),
StringRef::from_lit("2"), false,
http2::HD_CONTENT_LENGTH);
// By token // By token
CU_ASSERT(Header(":authority", "1") == *fs.header(http2::HD__AUTHORITY)); CU_ASSERT(Header(":authority", "1") == *fs.header(http2::HD__AUTHORITY));
@ -74,14 +84,18 @@ void test_downstream_field_store_header(void) {
void test_downstream_crumble_request_cookie(void) { void test_downstream_crumble_request_cookie(void) {
Downstream d(nullptr, nullptr, 0); Downstream d(nullptr, nullptr, 0);
auto &req = d.request(); auto &req = d.request();
req.fs.add_header(":method", "get"); req.fs.add_header_token(StringRef::from_lit(":method"),
req.fs.add_header(":path", "/"); StringRef::from_lit("get"), false, -1);
auto val = "alpha; bravo; ; ;; charlie;;"; req.fs.add_header_token(StringRef::from_lit(":path"),
req.fs.add_header( StringRef::from_lit("/"), false, -1);
reinterpret_cast<const uint8_t *>("cookie"), sizeof("cookie") - 1, req.fs.add_header_token(StringRef::from_lit("cookie"),
reinterpret_cast<const uint8_t *>(val), strlen(val), true, -1); StringRef::from_lit("alpha; bravo; ; ;; charlie;;"),
req.fs.add_header("cookie", ";delta"); true, http2::HD_COOKIE);
req.fs.add_header("cookie", "echo"); req.fs.add_header_token(StringRef::from_lit("cookie"),
StringRef::from_lit(";delta"), false,
http2::HD_COOKIE);
req.fs.add_header_token(StringRef::from_lit("cookie"),
StringRef::from_lit("echo"), false, http2::HD_COOKIE);
std::vector<nghttp2_nv> nva; std::vector<nghttp2_nv> nva;
d.crumble_request_cookie(nva); d.crumble_request_cookie(nva);
@ -114,12 +128,22 @@ void test_downstream_crumble_request_cookie(void) {
void test_downstream_assemble_request_cookie(void) { void test_downstream_assemble_request_cookie(void) {
Downstream d(nullptr, nullptr, 0); Downstream d(nullptr, nullptr, 0);
auto &req = d.request(); auto &req = d.request();
req.fs.add_header(":method", "get"); req.fs.add_header_token(StringRef::from_lit(":method"),
req.fs.add_header(":path", "/"); StringRef::from_lit("get"), false, -1);
req.fs.add_header("cookie", "alpha"); req.fs.add_header_token(StringRef::from_lit(":path"),
req.fs.add_header("cookie", "bravo;"); StringRef::from_lit("/"), false, -1);
req.fs.add_header("cookie", "charlie; "); req.fs.add_header_token(StringRef::from_lit("cookie"),
req.fs.add_header("cookie", "delta;;"); StringRef::from_lit("alpha"), false,
http2::HD_COOKIE);
req.fs.add_header_token(StringRef::from_lit("cookie"),
StringRef::from_lit("bravo;"), false,
http2::HD_COOKIE);
req.fs.add_header_token(StringRef::from_lit("cookie"),
StringRef::from_lit("charlie; "), false,
http2::HD_COOKIE);
req.fs.add_header_token(StringRef::from_lit("cookie"),
StringRef::from_lit("delta;;"), false,
http2::HD_COOKIE);
CU_ASSERT("alpha; bravo; charlie; delta" == d.assemble_request_cookie()); CU_ASSERT("alpha; bravo; charlie; delta" == d.assemble_request_cookie());
} }
@ -129,8 +153,9 @@ void test_downstream_rewrite_location_response_header(void) {
auto &resp = d.response(); auto &resp = d.response();
d.set_request_downstream_host("localhost2"); d.set_request_downstream_host("localhost2");
req.authority = "localhost:8443"; req.authority = "localhost:8443";
resp.fs.add_header("location", "http://localhost2:3000/"); resp.fs.add_header_token(StringRef::from_lit("location"),
resp.fs.index_headers(); StringRef::from_lit("http://localhost2:3000/"),
false, http2::HD_LOCATION);
d.rewrite_location_response_header("https"); d.rewrite_location_response_header("https");
auto location = resp.fs.header(http2::HD_LOCATION); auto location = resp.fs.header(http2::HD_LOCATION);
CU_ASSERT("https://localhost:8443/" == (*location).value); CU_ASSERT("https://localhost:8443/" == (*location).value);

View File

@ -31,7 +31,7 @@
namespace shrpx { namespace shrpx {
void test_downstream_field_store_index_headers(void); void test_downstream_field_store_add_header_lower(void);
void test_downstream_field_store_header(void); void test_downstream_field_store_header(void);
void test_downstream_crumble_request_cookie(void); void test_downstream_crumble_request_cookie(void);
void test_downstream_assemble_request_cookie(void); void test_downstream_assemble_request_cookie(void);

View File

@ -98,6 +98,9 @@ int Http2DownstreamConnection::attach_downstream(Downstream *downstream) {
http2session_->add_downstream_connection(this); http2session_->add_downstream_connection(this);
if (http2session_->get_state() == Http2Session::DISCONNECTED) { if (http2session_->get_state() == Http2Session::DISCONNECTED) {
http2session_->signal_write(); http2session_->signal_write();
if (http2session_->get_state() == Http2Session::DISCONNECTED) {
return -1;
}
} }
downstream_ = downstream; downstream_ = downstream;
@ -422,7 +425,7 @@ int Http2DownstreamConnection::push_request_headers() {
} }
for (auto &p : httpconf.add_request_headers) { for (auto &p : httpconf.add_request_headers) {
nva.push_back(http2::make_nv_nocopy(p.first, p.second)); nva.push_back(http2::make_nv_nocopy(p.name, p.value));
} }
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {

View File

@ -147,8 +147,7 @@ void writecb(struct ev_loop *loop, ev_io *w, int revents) {
} // namespace } // namespace
Http2Session::Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx, Http2Session::Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx,
ConnectBlocker *connect_blocker, Worker *worker, Worker *worker, size_t group, size_t idx)
size_t group, size_t idx)
: conn_(loop, -1, nullptr, worker->get_mcpool(), : conn_(loop, -1, nullptr, worker->get_mcpool(),
get_config()->conn.downstream.timeout.write, get_config()->conn.downstream.timeout.write,
get_config()->conn.downstream.timeout.read, {}, {}, writecb, readcb, get_config()->conn.downstream.timeout.read, {}, {}, writecb, readcb,
@ -156,7 +155,6 @@ Http2Session::Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx,
get_config()->tls.dyn_rec.idle_timeout), get_config()->tls.dyn_rec.idle_timeout),
wb_(worker->get_mcpool()), wb_(worker->get_mcpool()),
worker_(worker), worker_(worker),
connect_blocker_(connect_blocker),
ssl_ctx_(ssl_ctx), ssl_ctx_(ssl_ctx),
addr_(nullptr), addr_(nullptr),
session_(nullptr), session_(nullptr),
@ -254,30 +252,58 @@ int Http2Session::disconnect(bool hard) {
int Http2Session::initiate_connection() { int Http2Session::initiate_connection() {
int rv = 0; int rv = 0;
auto &addrs = get_config()->conn.downstream.addr_groups[group_].addrs; auto &groups = worker_->get_downstream_addr_groups();
auto &addrs = groups[group_].addrs;
auto worker_blocker = worker_->get_connect_blocker();
if (state_ == DISCONNECTED) { if (state_ == DISCONNECTED) {
if (connect_blocker_->blocked()) { if (worker_blocker->blocked()) {
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
DCLOG(INFO, this) SSLOG(INFO, this)
<< "Downstream connection was blocked by connect_blocker"; << "Worker wide backend connection was blocked temporarily";
} }
return -1; return -1;
} }
auto &next_downstream = worker_->get_dgrp(group_)->next; auto &next_downstream = worker_->get_dgrp(group_)->next;
addr_ = &addrs[next_downstream]; auto end = next_downstream;
for (;;) {
auto &addr = addrs[next_downstream];
if (++next_downstream >= addrs.size()) {
next_downstream = 0;
}
auto &connect_blocker = addr.connect_blocker;
if (connect_blocker->blocked()) {
if (LOG_ENABLED(INFO)) {
SSLOG(INFO, this) << "Backend server "
<< util::to_numeric_addr(&addr.addr)
<< " was not available temporarily";
}
if (end == next_downstream) {
return -1;
}
continue;
}
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
SSLOG(INFO, this) << "Using downstream address idx=" << next_downstream SSLOG(INFO, this) << "Using downstream address idx=" << next_downstream
<< " out of " << addrs.size(); << " out of " << addrs.size();
} }
if (++next_downstream >= addrs.size()) { addr_ = &addr;
next_downstream = 0;
break;
} }
} }
auto &connect_blocker = addr_->connect_blocker;
const auto &proxy = get_config()->downstream_http_proxy; const auto &proxy = get_config()->downstream_http_proxy;
if (!proxy.host.empty() && state_ == DISCONNECTED) { if (!proxy.host.empty() && state_ == DISCONNECTED) {
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
@ -288,15 +314,25 @@ int Http2Session::initiate_connection() {
conn_.fd = util::create_nonblock_socket(proxy.addr.su.storage.ss_family); conn_.fd = util::create_nonblock_socket(proxy.addr.su.storage.ss_family);
if (conn_.fd == -1) { if (conn_.fd == -1) {
connect_blocker_->on_failure(); auto error = errno;
SSLOG(WARN, this) << "Backend proxy socket() failed; addr="
<< util::to_numeric_addr(&proxy.addr)
<< ", errno=" << error;
worker_blocker->on_failure();
return -1; return -1;
} }
worker_blocker->on_success();
rv = connect(conn_.fd, &proxy.addr.su.sa, proxy.addr.len); rv = connect(conn_.fd, &proxy.addr.su.sa, proxy.addr.len);
if (rv != 0 && errno != EINPROGRESS) { if (rv != 0 && errno != EINPROGRESS) {
SSLOG(ERROR, this) << "Failed to connect to the proxy " << proxy.host auto error = errno;
<< ":" << proxy.port; SSLOG(WARN, this) << "Backend proxy connect() failed; addr="
connect_blocker_->on_failure(); << util::to_numeric_addr(&proxy.addr)
<< ", errno=" << error;
connect_blocker->on_failure();
return -1; return -1;
} }
@ -356,16 +392,28 @@ int Http2Session::initiate_connection() {
conn_.fd = conn_.fd =
util::create_nonblock_socket(addr_->addr.su.storage.ss_family); util::create_nonblock_socket(addr_->addr.su.storage.ss_family);
if (conn_.fd == -1) { if (conn_.fd == -1) {
connect_blocker_->on_failure(); auto error = errno;
SSLOG(WARN, this)
<< "socket() failed; addr=" << util::to_numeric_addr(&addr_->addr)
<< ", errno=" << error;
worker_blocker->on_failure();
return -1; return -1;
} }
worker_blocker->on_success();
rv = connect(conn_.fd, rv = connect(conn_.fd,
// TODO maybe not thread-safe? // TODO maybe not thread-safe?
const_cast<sockaddr *>(&addr_->addr.su.sa), const_cast<sockaddr *>(&addr_->addr.su.sa),
addr_->addr.len); addr_->addr.len);
if (rv != 0 && errno != EINPROGRESS) { if (rv != 0 && errno != EINPROGRESS) {
connect_blocker_->on_failure(); auto error = errno;
SSLOG(WARN, this) << "connect() failed; addr="
<< util::to_numeric_addr(&addr_->addr)
<< ", errno=" << error;
connect_blocker->on_failure();
return -1; return -1;
} }
@ -383,14 +431,26 @@ int Http2Session::initiate_connection() {
util::create_nonblock_socket(addr_->addr.su.storage.ss_family); util::create_nonblock_socket(addr_->addr.su.storage.ss_family);
if (conn_.fd == -1) { if (conn_.fd == -1) {
connect_blocker_->on_failure(); auto error = errno;
SSLOG(WARN, this)
<< "socket() failed; addr=" << util::to_numeric_addr(&addr_->addr)
<< ", errno=" << error;
worker_blocker->on_failure();
return -1; return -1;
} }
worker_blocker->on_success();
rv = connect(conn_.fd, const_cast<sockaddr *>(&addr_->addr.su.sa), rv = connect(conn_.fd, const_cast<sockaddr *>(&addr_->addr.su.sa),
addr_->addr.len); addr_->addr.len);
if (rv != 0 && errno != EINPROGRESS) { if (rv != 0 && errno != EINPROGRESS) {
connect_blocker_->on_failure(); auto error = errno;
SSLOG(WARN, this) << "connect() failed; addr="
<< util::to_numeric_addr(&addr_->addr)
<< ", errno=" << error;
connect_blocker->on_failure();
return -1; return -1;
} }
@ -735,17 +795,18 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
} }
auto token = http2::lookup_token(name, namelen);
auto no_index = flags & NGHTTP2_NV_FLAG_NO_INDEX;
if (trailer) { if (trailer) {
// just store header fields for trailer part // just store header fields for trailer part
resp.fs.add_trailer(name, namelen, value, valuelen, resp.fs.add_trailer_token(StringRef{name, namelen},
flags & NGHTTP2_NV_FLAG_NO_INDEX, -1); StringRef{value, valuelen}, no_index, token);
return 0; return 0;
} }
auto token = http2::lookup_token(name, namelen); resp.fs.add_header_token(StringRef{name, namelen},
StringRef{value, valuelen}, no_index, token);
resp.fs.add_header(name, namelen, value, valuelen,
flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
return 0; return 0;
} }
case NGHTTP2_PUSH_PROMISE: { case NGHTTP2_PUSH_PROMISE: {
@ -778,7 +839,8 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
} }
auto token = http2::lookup_token(name, namelen); auto token = http2::lookup_token(name, namelen);
promised_req.fs.add_header(name, namelen, value, valuelen, promised_req.fs.add_header_token(StringRef{name, namelen},
StringRef{value, valuelen},
flags & NGHTTP2_NV_FLAG_NO_INDEX, token); flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
return 0; return 0;
} }
@ -926,7 +988,8 @@ int on_response_headers(Http2Session *http2session, Downstream *downstream,
// Otherwise, use chunked encoding to keep upstream connection // Otherwise, use chunked encoding to keep upstream connection
// open. In HTTP2, we are supporsed not to receive // open. In HTTP2, we are supporsed not to receive
// transfer-encoding. // transfer-encoding.
resp.fs.add_header("transfer-encoding", "chunked", resp.fs.add_header_token(StringRef::from_lit("transfer-encoding"),
StringRef::from_lit("chunked"), false,
http2::HD_TRANSFER_ENCODING); http2::HD_TRANSFER_ENCODING);
downstream->set_chunked_response(true); downstream->set_chunked_response(true);
} }
@ -1612,11 +1675,20 @@ int Http2Session::read_noop(const uint8_t *data, size_t datalen) { return 0; }
int Http2Session::write_noop() { return 0; } int Http2Session::write_noop() { return 0; }
int Http2Session::connected() { int Http2Session::connected() {
auto &connect_blocker = addr_->connect_blocker;
if (!util::check_socket_connected(conn_.fd)) { if (!util::check_socket_connected(conn_.fd)) {
if (LOG_ENABLED(INFO)) {
SSLOG(INFO, this) << "Backend connect failed; addr="
<< util::to_numeric_addr(&addr_->addr);
}
connect_blocker->on_failure();
return -1; return -1;
} }
connect_blocker_->on_success(); connect_blocker->on_success();
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
SSLOG(INFO, this) << "Connection established"; SSLOG(INFO, this) << "Connection established";

View File

@ -48,7 +48,6 @@ namespace shrpx {
class Http2DownstreamConnection; class Http2DownstreamConnection;
class Worker; class Worker;
class ConnectBlocker;
struct StreamData { struct StreamData {
StreamData *dlnext, *dlprev; StreamData *dlnext, *dlprev;
@ -57,9 +56,8 @@ struct StreamData {
class Http2Session { class Http2Session {
public: public:
Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx, Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx, Worker *worker,
ConnectBlocker *connect_blocker, Worker *worker, size_t group, size_t group, size_t idx);
size_t idx);
~Http2Session(); ~Http2Session();
// If hard is true, all pending requests are abandoned and // If hard is true, all pending requests are abandoned and
@ -203,7 +201,6 @@ private:
// Used to parse the response from HTTP proxy // Used to parse the response from HTTP proxy
std::unique_ptr<http_parser> proxy_htp_; std::unique_ptr<http_parser> proxy_htp_;
Worker *worker_; Worker *worker_;
ConnectBlocker *connect_blocker_;
// NULL if no TLS is configured // NULL if no TLS is configured
SSL_CTX *ssl_ctx_; SSL_CTX *ssl_ctx_;
// Address of remote endpoint // Address of remote endpoint

View File

@ -201,17 +201,18 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
return 0; return 0;
} }
auto token = http2::lookup_token(name, namelen);
auto no_index = flags & NGHTTP2_NV_FLAG_NO_INDEX;
if (frame->headers.cat == NGHTTP2_HCAT_HEADERS) { if (frame->headers.cat == NGHTTP2_HCAT_HEADERS) {
// just store header fields for trailer part // just store header fields for trailer part
req.fs.add_trailer(name, namelen, value, valuelen, req.fs.add_trailer_token(StringRef{name, namelen},
flags & NGHTTP2_NV_FLAG_NO_INDEX, -1); StringRef{value, valuelen}, no_index, token);
return 0; return 0;
} }
auto token = http2::lookup_token(name, namelen); req.fs.add_header_token(StringRef{name, namelen}, StringRef{value, valuelen},
no_index, token);
req.fs.add_header(name, namelen, value, valuelen,
flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
return 0; return 0;
} }
} // namespace } // namespace
@ -593,7 +594,8 @@ int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame,
req.path = http2::rewrite_clean_path(nv.value, nv.value + nv.valuelen); req.path = http2::rewrite_clean_path(nv.value, nv.value + nv.valuelen);
break; break;
} }
req.fs.add_header(nv.name, nv.namelen, nv.value, nv.valuelen, req.fs.add_header_token(StringRef{nv.name, nv.namelen},
StringRef{nv.value, nv.valuelen},
nv.flags & NGHTTP2_NV_FLAG_NO_INDEX, token); nv.flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
} }
@ -1206,11 +1208,12 @@ int Http2Upstream::send_reply(Downstream *downstream, const uint8_t *body,
} }
const auto &resp = downstream->response(); const auto &resp = downstream->response();
auto &httpconf = get_config()->http;
const auto &headers = resp.fs.headers(); const auto &headers = resp.fs.headers();
auto nva = std::vector<nghttp2_nv>(); auto nva = std::vector<nghttp2_nv>();
// 2 for :status and server // 2 for :status and server
nva.reserve(2 + headers.size()); nva.reserve(2 + headers.size() + httpconf.add_response_headers.size());
std::string status_code_str; std::string status_code_str;
auto response_status_const = http2::stringify_status(resp.http_status); auto response_status_const = http2::stringify_status(resp.http_status);
@ -1242,6 +1245,10 @@ int Http2Upstream::send_reply(Downstream *downstream, const uint8_t *body,
http2::make_nv_ls_nocopy("server", get_config()->http.server_name)); http2::make_nv_ls_nocopy("server", get_config()->http.server_name));
} }
for (auto &p : httpconf.add_response_headers) {
nva.push_back(http2::make_nv_nocopy(p.name, p.value));
}
rv = nghttp2_submit_response(session_, downstream->get_stream_id(), rv = nghttp2_submit_response(session_, downstream->get_stream_id(),
nva.data(), nva.size(), data_prd_ptr); nva.data(), nva.size(), data_prd_ptr);
if (nghttp2_is_fatal(rv)) { if (nghttp2_is_fatal(rv)) {
@ -1436,7 +1443,7 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
} }
for (auto &p : httpconf.add_response_headers) { for (auto &p : httpconf.add_response_headers) {
nva.push_back(http2::make_nv_nocopy(p.first, p.second)); nva.push_back(http2::make_nv_nocopy(p.name, p.value));
} }
if (downstream->get_stream_id() % 2 == 0) { if (downstream->get_stream_id() % 2 == 0) {
@ -1553,7 +1560,9 @@ int Http2Upstream::adjust_pushed_stream_priority(Downstream *downstream) {
} }
if (!util::istarts_with_l(ct->value, "application/javascript") && if (!util::istarts_with_l(ct->value, "application/javascript") &&
!util::istarts_with_l(ct->value, "text/css")) { !util::istarts_with_l(ct->value, "text/css") &&
// for polymer...
!util::istarts_with_l(ct->value, "text/html")) {
return 0; return 0;
} }

View File

@ -129,33 +129,25 @@ HttpDownstreamConnection::HttpDownstreamConnection(
response_htp_{0}, response_htp_{0},
group_(group) {} group_(group) {}
HttpDownstreamConnection::~HttpDownstreamConnection() { HttpDownstreamConnection::~HttpDownstreamConnection() {}
if (conn_.tls.ssl) {
auto session = SSL_get1_session(conn_.tls.ssl);
if (session) {
worker_->cache_downstream_tls_session(addr_, session);
}
}
}
int HttpDownstreamConnection::attach_downstream(Downstream *downstream) { int HttpDownstreamConnection::attach_downstream(Downstream *downstream) {
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
DCLOG(INFO, this) << "Attaching to DOWNSTREAM:" << downstream; DCLOG(INFO, this) << "Attaching to DOWNSTREAM:" << downstream;
} }
auto worker_blocker = worker_->get_connect_blocker();
if (worker_blocker->blocked()) {
if (LOG_ENABLED(INFO)) {
DCLOG(INFO, this)
<< "Worker wide backend connection was blocked temporarily";
}
return SHRPX_ERR_NETWORK;
}
auto &downstreamconf = get_config()->conn.downstream; auto &downstreamconf = get_config()->conn.downstream;
if (conn_.fd == -1) { if (conn_.fd == -1) {
auto connect_blocker = client_handler_->get_connect_blocker();
if (connect_blocker->blocked()) {
if (LOG_ENABLED(INFO)) {
DCLOG(INFO, this)
<< "Downstream connection was blocked by connect_blocker";
}
return -1;
}
if (ssl_ctx_) { if (ssl_ctx_) {
auto ssl = ssl::create_ssl(ssl_ctx_); auto ssl = ssl::create_ssl(ssl_ctx_);
if (!ssl) { if (!ssl) {
@ -167,7 +159,8 @@ int HttpDownstreamConnection::attach_downstream(Downstream *downstream) {
auto &next_downstream = worker_->get_dgrp(group_)->next; auto &next_downstream = worker_->get_dgrp(group_)->next;
auto end = next_downstream; auto end = next_downstream;
auto &addrs = downstreamconf.addr_groups[group_].addrs; auto &groups = worker_->get_downstream_addr_groups();
auto &addrs = groups[group_].addrs;
for (;;) { for (;;) {
auto &addr = addrs[next_downstream]; auto &addr = addrs[next_downstream];
@ -175,22 +168,44 @@ int HttpDownstreamConnection::attach_downstream(Downstream *downstream) {
next_downstream = 0; next_downstream = 0;
} }
auto &connect_blocker = addr.connect_blocker;
if (connect_blocker->blocked()) {
if (LOG_ENABLED(INFO)) {
DCLOG(INFO, this) << "Backend server "
<< util::to_numeric_addr(&addr.addr)
<< " was not available temporarily";
}
if (end == next_downstream) {
return SHRPX_ERR_NETWORK;
}
continue;
}
conn_.fd = util::create_nonblock_socket(addr.addr.su.storage.ss_family); conn_.fd = util::create_nonblock_socket(addr.addr.su.storage.ss_family);
if (conn_.fd == -1) { if (conn_.fd == -1) {
auto error = errno; auto error = errno;
DCLOG(WARN, this) << "socket() failed; errno=" << error; DCLOG(WARN, this) << "socket() failed; addr="
<< util::to_numeric_addr(&addr.addr)
<< ", errno=" << error;
connect_blocker->on_failure(); worker_blocker->on_failure();
return SHRPX_ERR_NETWORK; return SHRPX_ERR_NETWORK;
} }
worker_blocker->on_success();
int rv; int rv;
rv = connect(conn_.fd, &addr.addr.su.sa, addr.addr.len); rv = connect(conn_.fd, &addr.addr.su.sa, addr.addr.len);
if (rv != 0 && errno != EINPROGRESS) { if (rv != 0 && errno != EINPROGRESS) {
auto error = errno; auto error = errno;
DCLOG(WARN, this) << "connect() failed; errno=" << error; DCLOG(WARN, this) << "connect() failed; addr="
<< util::to_numeric_addr(&addr.addr)
<< ", errno=" << error;
connect_blocker->on_failure(); connect_blocker->on_failure();
close(conn_.fd); close(conn_.fd);
@ -218,7 +233,7 @@ int HttpDownstreamConnection::attach_downstream(Downstream *downstream) {
SSL_set_tlsext_host_name(conn_.tls.ssl, sni_name.c_str()); SSL_set_tlsext_host_name(conn_.tls.ssl, sni_name.c_str());
} }
auto session = worker_->reuse_downstream_tls_session(addr_); auto session = ssl::reuse_tls_session(addr_);
if (session) { if (session) {
SSL_set_session(conn_.tls.ssl, session); SSL_set_session(conn_.tls.ssl, session);
SSL_SESSION_free(session); SSL_SESSION_free(session);
@ -417,9 +432,9 @@ int HttpDownstreamConnection::push_request_headers() {
} }
for (auto &p : httpconf.add_request_headers) { for (auto &p : httpconf.add_request_headers) {
buf->append(p.first); buf->append(p.name);
buf->append(": "); buf->append(": ");
buf->append(p.second); buf->append(p.value);
buf->append("\r\n"); buf->append("\r\n");
} }
@ -571,7 +586,7 @@ int htp_hdrs_completecb(http_parser *htp) {
resp.http_major = htp->http_major; resp.http_major = htp->http_major;
resp.http_minor = htp->http_minor; resp.http_minor = htp->http_minor;
if (resp.fs.index_headers() != 0) { if (resp.fs.parse_content_length() != 0) {
downstream->set_response_state(Downstream::MSG_BAD_HEADER); downstream->set_response_state(Downstream::MSG_BAD_HEADER);
return -1; return -1;
} }
@ -690,7 +705,7 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
if (ensure_max_header_fields(downstream, httpconf) != 0) { if (ensure_max_header_fields(downstream, httpconf) != 0) {
return -1; return -1;
} }
resp.fs.add_header(std::string(data, len), ""); resp.fs.add_header_lower(StringRef{data, len}, StringRef{}, false);
} }
} else { } else {
// trailer part // trailer part
@ -703,7 +718,7 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
// wrong place or crash if trailer fields are currently empty. // wrong place or crash if trailer fields are currently empty.
return -1; return -1;
} }
resp.fs.add_trailer(std::string(data, len), ""); resp.fs.add_trailer_lower(StringRef(data, len), StringRef{}, false);
} }
} }
return 0; return 0;
@ -868,6 +883,13 @@ int HttpDownstreamConnection::tls_handshake() {
return -1; return -1;
} }
if (!SSL_session_reused(conn_.tls.ssl)) {
auto session = SSL_get0_session(conn_.tls.ssl);
if (session) {
ssl::try_cache_tls_session(addr_, session, ev_now(conn_.loop));
}
}
do_read_ = &HttpDownstreamConnection::read_tls; do_read_ = &HttpDownstreamConnection::read_tls;
do_write_ = &HttpDownstreamConnection::write_tls; do_write_ = &HttpDownstreamConnection::write_tls;
@ -1011,22 +1033,25 @@ int HttpDownstreamConnection::process_input(const uint8_t *data,
} }
int HttpDownstreamConnection::connected() { int HttpDownstreamConnection::connected() {
auto connect_blocker = client_handler_->get_connect_blocker(); auto connect_blocker = addr_->connect_blocker;
if (!util::check_socket_connected(conn_.fd)) { if (!util::check_socket_connected(conn_.fd)) {
conn_.wlimit.stopw(); conn_.wlimit.stopw();
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
DLOG(INFO, this) << "downstream connect failed"; DCLOG(INFO, this) << "Backend connect failed; addr="
<< util::to_numeric_addr(&addr_->addr);
} }
connect_blocker->on_failure();
downstream_->set_request_state(Downstream::CONNECT_FAIL); downstream_->set_request_state(Downstream::CONNECT_FAIL);
return -1; return -1;
} }
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
DLOG(INFO, this) << "Connected to downstream host"; DCLOG(INFO, this) << "Connected to downstream host";
} }
connect_blocker->on_success(); connect_blocker->on_success();

View File

@ -82,7 +82,7 @@ private:
// nullptr if TLS is not used. // nullptr if TLS is not used.
SSL_CTX *ssl_ctx_; SSL_CTX *ssl_ctx_;
// Address of remote endpoint // Address of remote endpoint
const DownstreamAddr *addr_; DownstreamAddr *addr_;
IOControl ioctrl_; IOControl ioctrl_;
http_parser response_htp_; http_parser response_htp_;
size_t group_; size_t group_;

View File

@ -142,7 +142,7 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE); Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE);
return -1; return -1;
} }
req.fs.add_header(std::string(data, len), ""); req.fs.add_header_lower(StringRef{data, len}, StringRef{}, false);
} }
} else { } else {
// trailer part // trailer part
@ -156,7 +156,7 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
} }
return -1; return -1;
} }
req.fs.add_trailer(std::string(data, len), ""); req.fs.add_trailer_lower(StringRef{data, len}, StringRef{}, false);
} }
} }
return 0; return 0;
@ -270,7 +270,7 @@ int htp_hdrs_completecb(http_parser *htp) {
ULOG(INFO, upstream) << "HTTP request headers\n" << ss.str(); ULOG(INFO, upstream) << "HTTP request headers\n" << ss.str();
} }
if (req.fs.index_headers() != 0) { if (req.fs.parse_content_length() != 0) {
return -1; return -1;
} }
@ -798,6 +798,15 @@ int HttpsUpstream::send_reply(Downstream *downstream, const uint8_t *body,
output->append("\r\n"); output->append("\r\n");
} }
auto &httpconf = get_config()->http;
for (auto &p : httpconf.add_response_headers) {
output->append(p.name);
output->append(": ");
output->append(p.value);
output->append("\r\n");
}
output->append("\r\n"); output->append("\r\n");
output->append(body, bodylen); output->append(body, bodylen);
@ -1022,9 +1031,9 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
} }
for (auto &p : httpconf.add_response_headers) { for (auto &p : httpconf.add_response_headers) {
buf->append(p.first); buf->append(p.name);
buf->append(": "); buf->append(": ");
buf->append(p.second); buf->append(p.value);
buf->append("\r\n"); buf->append("\r\n");
} }

View File

@ -393,23 +393,23 @@ int reopen_log_files() {
auto &accessconf = get_config()->logging.access; auto &accessconf = get_config()->logging.access;
auto &errorconf = get_config()->logging.error; auto &errorconf = get_config()->logging.error;
if (!accessconf.syslog && accessconf.file) { if (!accessconf.syslog && !accessconf.file.empty()) {
new_accesslog_fd = util::open_log_file(accessconf.file.get()); new_accesslog_fd = util::open_log_file(accessconf.file.c_str());
if (new_accesslog_fd == -1) { if (new_accesslog_fd == -1) {
LOG(ERROR) << "Failed to open accesslog file " << accessconf.file.get(); LOG(ERROR) << "Failed to open accesslog file " << accessconf.file;
res = -1; res = -1;
} }
} }
if (!errorconf.syslog && errorconf.file) { if (!errorconf.syslog && !errorconf.file.empty()) {
new_errorlog_fd = util::open_log_file(errorconf.file.get()); new_errorlog_fd = util::open_log_file(errorconf.file.c_str());
if (new_errorlog_fd == -1) { if (new_errorlog_fd == -1) {
if (lgconf->errorlog_fd != -1) { if (lgconf->errorlog_fd != -1) {
LOG(ERROR) << "Failed to open errorlog file " << errorconf.file.get(); LOG(ERROR) << "Failed to open errorlog file " << errorconf.file;
} else { } else {
std::cerr << "Failed to open errorlog file " << errorconf.file.get() std::cerr << "Failed to open errorlog file " << errorconf.file
<< std::endl; << std::endl;
} }

View File

@ -32,6 +32,7 @@
#include "shrpx_memcached_request.h" #include "shrpx_memcached_request.h"
#include "shrpx_memcached_result.h" #include "shrpx_memcached_result.h"
#include "shrpx_config.h" #include "shrpx_config.h"
#include "shrpx_ssl.h"
#include "util.h" #include "util.h"
namespace shrpx { namespace shrpx {
@ -78,7 +79,7 @@ void connectcb(struct ev_loop *loop, ev_io *w, int revents) {
auto conn = static_cast<Connection *>(w->data); auto conn = static_cast<Connection *>(w->data);
auto mconn = static_cast<MemcachedConnection *>(conn->data); auto mconn = static_cast<MemcachedConnection *>(conn->data);
if (mconn->on_connect() != 0) { if (mconn->connected() != 0) {
mconn->disconnect(); mconn->disconnect();
return; return;
} }
@ -91,11 +92,17 @@ constexpr ev_tstamp write_timeout = 10.;
constexpr ev_tstamp read_timeout = 10.; constexpr ev_tstamp read_timeout = 10.;
MemcachedConnection::MemcachedConnection(const Address *addr, MemcachedConnection::MemcachedConnection(const Address *addr,
struct ev_loop *loop) struct ev_loop *loop, SSL_CTX *ssl_ctx,
: conn_(loop, -1, nullptr, nullptr, write_timeout, read_timeout, {}, {}, const StringRef &sni_name,
MemchunkPool *mcpool)
: conn_(loop, -1, nullptr, mcpool, write_timeout, read_timeout, {}, {},
connectcb, readcb, timeoutcb, this, 0, 0.), connectcb, readcb, timeoutcb, this, 0, 0.),
do_read_(&MemcachedConnection::noop),
do_write_(&MemcachedConnection::noop),
sni_name_(sni_name.str()),
parse_state_{}, parse_state_{},
addr_(addr), addr_(addr),
ssl_ctx_(ssl_ctx),
sendsum_(0), sendsum_(0),
connected_(false) {} connected_(false) {}
@ -127,11 +134,21 @@ void MemcachedConnection::disconnect() {
assert(recvbuf_.rleft() == 0); assert(recvbuf_.rleft() == 0);
recvbuf_.reset(); recvbuf_.reset();
do_read_ = do_write_ = &MemcachedConnection::noop;
} }
int MemcachedConnection::initiate_connection() { int MemcachedConnection::initiate_connection() {
assert(conn_.fd == -1); assert(conn_.fd == -1);
if (ssl_ctx_ && !conn_.tls.ssl) {
auto ssl = ssl::create_ssl(ssl_ctx_);
if (!ssl) {
return -1;
}
conn_.set_ssl(ssl);
}
conn_.fd = util::create_nonblock_socket(addr_->su.storage.ss_family); conn_.fd = util::create_nonblock_socket(addr_->su.storage.ss_family);
if (conn_.fd == -1) { if (conn_.fd == -1) {
@ -153,6 +170,14 @@ int MemcachedConnection::initiate_connection() {
return -1; return -1;
} }
if (ssl_ctx_) {
if (!util::numeric_host(sni_name_.c_str())) {
SSL_set_tlsext_host_name(conn_.tls.ssl, sni_name_.c_str());
}
conn_.prepare_client_handshake();
}
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
MCLOG(INFO, this) << "Connecting to memcached server"; MCLOG(INFO, this) << "Connecting to memcached server";
} }
@ -168,7 +193,7 @@ int MemcachedConnection::initiate_connection() {
return 0; return 0;
} }
int MemcachedConnection::on_connect() { int MemcachedConnection::connected() {
if (!util::check_socket_connected(conn_.fd)) { if (!util::check_socket_connected(conn_.fd)) {
conn_.wlimit.stopw(); conn_.wlimit.stopw();
@ -185,15 +210,59 @@ int MemcachedConnection::on_connect() {
connected_ = true; connected_ = true;
ev_set_cb(&conn_.wev, writecb);
conn_.rlimit.startw(); conn_.rlimit.startw();
ev_timer_again(conn_.loop, &conn_.rt); ev_timer_again(conn_.loop, &conn_.rt);
ev_set_cb(&conn_.wev, writecb);
if (conn_.tls.ssl) {
do_read_ = &MemcachedConnection::tls_handshake;
do_write_ = &MemcachedConnection::tls_handshake;
return 0; return 0;
} }
int MemcachedConnection::on_write() { do_read_ = &MemcachedConnection::read_clear;
do_write_ = &MemcachedConnection::write_clear;
return 0;
}
int MemcachedConnection::on_write() { return do_write_(*this); }
int MemcachedConnection::on_read() { return do_read_(*this); }
int MemcachedConnection::tls_handshake() {
ERR_clear_error();
ev_timer_again(conn_.loop, &conn_.rt);
auto rv = conn_.tls_handshake();
if (rv == SHRPX_ERR_INPROGRESS) {
return 0;
}
if (rv < 0) {
return rv;
}
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "SSL/TLS handshake completed";
}
auto &tlsconf = get_config()->tls;
if (!tlsconf.insecure &&
ssl::check_cert(conn_.tls.ssl, addr_, StringRef(sni_name_)) != 0) {
return -1;
}
do_read_ = &MemcachedConnection::read_tls;
do_write_ = &MemcachedConnection::write_tls;
return on_write();
}
int MemcachedConnection::write_tls() {
if (!connected_) { if (!connected_) {
return 0; return 0;
} }
@ -207,19 +276,30 @@ int MemcachedConnection::on_write() {
return 0; return 0;
} }
int rv; std::array<struct iovec, MAX_WR_IOVCNT> iov;
std::array<uint8_t, 16_k> buf;
for (; !sendq_.empty();) { for (; !sendq_.empty();) {
rv = send_request(); auto iovcnt = fill_request_buffer(iov.data(), iov.size());
auto p = std::begin(buf);
for (size_t i = 0; i < iovcnt; ++i) {
auto &v = iov[i];
auto n = std::min(static_cast<size_t>(std::end(buf) - p), v.iov_len);
p = std::copy_n(static_cast<uint8_t *>(v.iov_base), n, p);
if (p == std::end(buf)) {
break;
}
}
if (rv < 0) { auto nwrite = conn_.write_tls(buf.data(), p - std::begin(buf));
if (nwrite < 0) {
return -1; return -1;
} }
if (nwrite == 0) {
if (rv == 1) {
// blocked
return 0; return 0;
} }
drain_send_queue(nwrite);
} }
conn_.wlimit.stopw(); conn_.wlimit.stopw();
@ -228,7 +308,70 @@ int MemcachedConnection::on_write() {
return 0; return 0;
} }
int MemcachedConnection::on_read() { int MemcachedConnection::read_tls() {
if (!connected_) {
return 0;
}
ev_timer_again(conn_.loop, &conn_.rt);
for (;;) {
auto nread = conn_.read_tls(recvbuf_.last, recvbuf_.wleft());
if (nread == 0) {
return 0;
}
if (nread < 0) {
return -1;
}
recvbuf_.write(nread);
if (parse_packet() != 0) {
return -1;
}
}
return 0;
}
int MemcachedConnection::write_clear() {
if (!connected_) {
return 0;
}
ev_timer_again(conn_.loop, &conn_.rt);
if (sendq_.empty()) {
conn_.wlimit.stopw();
ev_timer_stop(conn_.loop, &conn_.wt);
return 0;
}
std::array<struct iovec, MAX_WR_IOVCNT> iov;
for (; !sendq_.empty();) {
auto iovcnt = fill_request_buffer(iov.data(), iov.size());
auto nwrite = conn_.writev_clear(iov.data(), iovcnt);
if (nwrite < 0) {
return -1;
}
if (nwrite == 0) {
return 0;
}
drain_send_queue(nwrite);
}
conn_.wlimit.stopw();
ev_timer_stop(conn_.loop, &conn_.wt);
return 0;
}
int MemcachedConnection::read_clear() {
if (!connected_) { if (!connected_) {
return 0; return 0;
} }
@ -415,9 +558,8 @@ int MemcachedConnection::parse_packet() {
#define MAX_WR_IOVCNT DEFAULT_WR_IOVCNT #define MAX_WR_IOVCNT DEFAULT_WR_IOVCNT
#endif // !defined(IOV_MAX) || IOV_MAX >= DEFAULT_WR_IOVCNT #endif // !defined(IOV_MAX) || IOV_MAX >= DEFAULT_WR_IOVCNT
int MemcachedConnection::send_request() { size_t MemcachedConnection::fill_request_buffer(struct iovec *iov,
ssize_t nwrite; size_t iovlen) {
if (sendsum_ == 0) { if (sendsum_ == 0) {
for (auto &req : sendq_) { for (auto &req : sendq_) {
if (req->canceled) { if (req->canceled) {
@ -438,32 +580,27 @@ int MemcachedConnection::send_request() {
} }
} }
std::array<struct iovec, DEFAULT_WR_IOVCNT> iov; size_t iovcnt = 0;
size_t iovlen = 0;
for (auto &buf : sendbufv_) { for (auto &buf : sendbufv_) {
if (iovlen + 2 > iov.size()) { if (iovcnt + 2 > iovlen) {
break; break;
} }
auto req = buf.req; auto req = buf.req;
if (buf.headbuf.rleft()) { if (buf.headbuf.rleft()) {
iov[iovlen++] = {buf.headbuf.pos, buf.headbuf.rleft()}; iov[iovcnt++] = {buf.headbuf.pos, buf.headbuf.rleft()};
} }
if (buf.send_value_left) { if (buf.send_value_left) {
iov[iovlen++] = {req->value.data() + req->value.size() - iov[iovcnt++] = {req->value.data() + req->value.size() -
buf.send_value_left, buf.send_value_left,
buf.send_value_left}; buf.send_value_left};
} }
} }
nwrite = conn_.writev_clear(iov.data(), iovlen); return iovcnt;
if (nwrite < 0) {
return -1;
}
if (nwrite == 0) {
return 1;
} }
void MemcachedConnection::drain_send_queue(size_t nwrite) {
sendsum_ -= nwrite; sendsum_ -= nwrite;
while (nwrite > 0) { while (nwrite > 0) {
@ -488,8 +625,6 @@ int MemcachedConnection::send_request() {
recvq_.push_back(std::move(sendq_.front())); recvq_.push_back(std::move(sendq_.front()));
sendq_.pop_front(); sendq_.pop_front();
} }
return 0;
} }
size_t MemcachedConnection::serialized_size(MemcachedRequest *req) { size_t MemcachedConnection::serialized_size(MemcachedRequest *req) {
@ -549,4 +684,6 @@ int MemcachedConnection::add_request(std::unique_ptr<MemcachedRequest> req) {
// TODO should we start write timer too? // TODO should we start write timer too?
void MemcachedConnection::signal_write() { conn_.wlimit.startw(); } void MemcachedConnection::signal_write() { conn_.wlimit.startw(); }
int MemcachedConnection::noop() { return 0; }
} // namespace shrpx } // namespace shrpx

View File

@ -35,12 +35,13 @@
#include "shrpx_connection.h" #include "shrpx_connection.h"
#include "buffer.h" #include "buffer.h"
#include "network.h"
using namespace nghttp2; using namespace nghttp2;
namespace shrpx { namespace shrpx {
struct MemcachedRequest; struct MemcachedRequest;
struct Address;
enum { enum {
MEMCACHED_PARSE_HEADER24, MEMCACHED_PARSE_HEADER24,
@ -93,7 +94,9 @@ constexpr uint8_t MEMCACHED_RES_MAGIC = 0x81;
// https://code.google.com/p/memcached/wiki/MemcacheBinaryProtocol // https://code.google.com/p/memcached/wiki/MemcacheBinaryProtocol
class MemcachedConnection { class MemcachedConnection {
public: public:
MemcachedConnection(const Address *addr, struct ev_loop *loop); MemcachedConnection(const Address *addr, struct ev_loop *loop,
SSL_CTX *ssl_ctx, const StringRef &sni_name,
MemchunkPool *mcpool);
~MemcachedConnection(); ~MemcachedConnection();
void disconnect(); void disconnect();
@ -101,23 +104,38 @@ public:
int add_request(std::unique_ptr<MemcachedRequest> req); int add_request(std::unique_ptr<MemcachedRequest> req);
int initiate_connection(); int initiate_connection();
int on_connect(); int connected();
int on_write(); int on_write();
int on_read(); int on_read();
int send_request();
int write_clear();
int read_clear();
int tls_handshake();
int write_tls();
int read_tls();
size_t fill_request_buffer(struct iovec *iov, size_t iovlen);
void drain_send_queue(size_t nwrite);
void make_request(MemcachedSendbuf *sendbuf, MemcachedRequest *req); void make_request(MemcachedSendbuf *sendbuf, MemcachedRequest *req);
int parse_packet(); int parse_packet();
size_t serialized_size(MemcachedRequest *req); size_t serialized_size(MemcachedRequest *req);
void signal_write(); void signal_write();
int noop();
private: private:
Connection conn_; Connection conn_;
std::deque<std::unique_ptr<MemcachedRequest>> recvq_; std::deque<std::unique_ptr<MemcachedRequest>> recvq_;
std::deque<std::unique_ptr<MemcachedRequest>> sendq_; std::deque<std::unique_ptr<MemcachedRequest>> sendq_;
std::deque<MemcachedSendbuf> sendbufv_; std::deque<MemcachedSendbuf> sendbufv_;
std::function<int(MemcachedConnection &)> do_read_, do_write_;
std::string sni_name_;
MemcachedParseState parse_state_; MemcachedParseState parse_state_;
const Address *addr_; const Address *addr_;
SSL_CTX *ssl_ctx_;
// Sum of the bytes to be transmitted in sendbufv_. // Sum of the bytes to be transmitted in sendbufv_.
size_t sendsum_; size_t sendsum_;
bool connected_; bool connected_;

View File

@ -31,8 +31,12 @@
namespace shrpx { namespace shrpx {
MemcachedDispatcher::MemcachedDispatcher(const Address *addr, MemcachedDispatcher::MemcachedDispatcher(const Address *addr,
struct ev_loop *loop) struct ev_loop *loop, SSL_CTX *ssl_ctx,
: loop_(loop), mconn_(make_unique<MemcachedConnection>(addr, loop_)) {} const StringRef &sni_name,
MemchunkPool *mcpool)
: loop_(loop),
mconn_(make_unique<MemcachedConnection>(addr, loop_, ssl_ctx, sni_name,
mcpool)) {}
MemcachedDispatcher::~MemcachedDispatcher() {} MemcachedDispatcher::~MemcachedDispatcher() {}

View File

@ -31,15 +31,21 @@
#include <ev.h> #include <ev.h>
#include <openssl/ssl.h>
#include "memchunk.h"
#include "network.h"
namespace shrpx { namespace shrpx {
struct MemcachedRequest; struct MemcachedRequest;
class MemcachedConnection; class MemcachedConnection;
struct Address;
class MemcachedDispatcher { class MemcachedDispatcher {
public: public:
MemcachedDispatcher(const Address *addr, struct ev_loop *loop); MemcachedDispatcher(const Address *addr, struct ev_loop *loop,
SSL_CTX *ssl_ctx, const StringRef &sni_name,
MemchunkPool *mcpool);
~MemcachedDispatcher(); ~MemcachedDispatcher();
int add_request(std::unique_ptr<MemcachedRequest> req); int add_request(std::unique_ptr<MemcachedRequest> req);

View File

@ -31,7 +31,6 @@
#include "shrpx_config.h" #include "shrpx_config.h"
#include "shrpx_mruby_module.h" #include "shrpx_mruby_module.h"
#include "shrpx_downstream_connection.h" #include "shrpx_downstream_connection.h"
#include "template.h"
namespace shrpx { namespace shrpx {
@ -95,14 +94,6 @@ int MRubyContext::run_app(Downstream *downstream, int phase) {
mrb_->ud = nullptr; mrb_->ud = nullptr;
if (data.request_headers_dirty) {
downstream->request().fs.index_headers();
}
if (data.response_headers_dirty) {
downstream->response().fs.index_headers();
}
return rv; return rv;
} }
@ -146,12 +137,12 @@ mrb_value instantiate_app(mrb_state *mrb, RProc *proc) {
// very hard to write these kind of code because mruby has almost no // very hard to write these kind of code because mruby has almost no
// documentation aobut compiling or generating code, at least at the // documentation aobut compiling or generating code, at least at the
// time of this writing. // time of this writing.
RProc *compile(mrb_state *mrb, const char *filename) { RProc *compile(mrb_state *mrb, const StringRef &filename) {
if (filename == nullptr) { if (filename.empty()) {
return nullptr; return nullptr;
} }
auto infile = fopen(filename, "rb"); auto infile = fopen(filename.c_str(), "rb");
if (infile == nullptr) { if (infile == nullptr) {
return nullptr; return nullptr;
} }
@ -185,8 +176,8 @@ RProc *compile(mrb_state *mrb, const char *filename) {
return proc; return proc;
} }
std::unique_ptr<MRubyContext> create_mruby_context(const char *filename) { std::unique_ptr<MRubyContext> create_mruby_context(const StringRef &filename) {
if (!filename) { if (filename.empty()) {
return make_unique<MRubyContext>(nullptr, mrb_nil_value(), mrb_nil_value()); return make_unique<MRubyContext>(nullptr, mrb_nil_value(), mrb_nil_value());
} }

View File

@ -32,6 +32,8 @@
#include <mruby.h> #include <mruby.h>
#include <mruby/proc.h> #include <mruby/proc.h>
#include "template.h"
using namespace nghttp2; using namespace nghttp2;
namespace shrpx { namespace shrpx {
@ -65,13 +67,11 @@ enum {
struct MRubyAssocData { struct MRubyAssocData {
Downstream *downstream; Downstream *downstream;
int phase; int phase;
bool request_headers_dirty;
bool response_headers_dirty;
}; };
RProc *compile(mrb_state *mrb, const char *filename); RProc *compile(mrb_state *mrb, const StringRef &filename);
std::unique_ptr<MRubyContext> create_mruby_context(const char *filename); std::unique_ptr<MRubyContext> create_mruby_context(const StringRef &filename);
// Return interned |ptr|. // Return interned |ptr|.
mrb_sym intern_ptr(mrb_state *mrb, void *ptr); mrb_sym intern_ptr(mrb_state *mrb, void *ptr);

View File

@ -216,17 +216,20 @@ mrb_value request_mod_header(mrb_state *mrb, mrb_value self, bool repl) {
key = mrb_funcall(mrb, key, "downcase", 0); key = mrb_funcall(mrb, key, "downcase", 0);
auto keyref =
StringRef{RSTRING_PTR(key), static_cast<size_t>(RSTRING_LEN(key))};
auto token = http2::lookup_token(keyref.byte(), keyref.size());
if (repl) { if (repl) {
size_t p = 0; size_t p = 0;
auto &headers = req.fs.headers(); auto &headers = req.fs.headers();
for (size_t i = 0; i < headers.size(); ++i) { for (size_t i = 0; i < headers.size(); ++i) {
auto &hd = headers[i]; auto &kv = headers[i];
if (util::streq(std::begin(hd.name), hd.name.size(), RSTRING_PTR(key), if (kv.name == keyref) {
RSTRING_LEN(key))) {
continue; continue;
} }
if (i != p) { if (i != p) {
headers[p++] = std::move(hd); headers[p++] = std::move(kv);
} }
} }
headers.resize(p); headers.resize(p);
@ -236,16 +239,18 @@ mrb_value request_mod_header(mrb_state *mrb, mrb_value self, bool repl) {
auto n = mrb_ary_len(mrb, values); auto n = mrb_ary_len(mrb, values);
for (int i = 0; i < n; ++i) { for (int i = 0; i < n; ++i) {
auto value = mrb_ary_entry(values, i); auto value = mrb_ary_entry(values, i);
req.fs.add_header(std::string(RSTRING_PTR(key), RSTRING_LEN(key)), req.fs.add_header_token(
std::string(RSTRING_PTR(value), RSTRING_LEN(value))); keyref, StringRef{RSTRING_PTR(value),
static_cast<size_t>(RSTRING_LEN(value))},
false, token);
} }
} else if (!mrb_nil_p(values)) { } else if (!mrb_nil_p(values)) {
req.fs.add_header(std::string(RSTRING_PTR(key), RSTRING_LEN(key)), req.fs.add_header_token(keyref,
std::string(RSTRING_PTR(values), RSTRING_LEN(values))); StringRef{RSTRING_PTR(values),
static_cast<size_t>(RSTRING_LEN(values))},
false, token);
} }
data->request_headers_dirty = true;
return mrb_nil_value(); return mrb_nil_value();
} }
} // namespace } // namespace

View File

@ -117,17 +117,20 @@ mrb_value response_mod_header(mrb_state *mrb, mrb_value self, bool repl) {
key = mrb_funcall(mrb, key, "downcase", 0); key = mrb_funcall(mrb, key, "downcase", 0);
auto keyref =
StringRef{RSTRING_PTR(key), static_cast<size_t>(RSTRING_LEN(key))};
auto token = http2::lookup_token(keyref.byte(), keyref.size());
if (repl) { if (repl) {
size_t p = 0; size_t p = 0;
auto &headers = resp.fs.headers(); auto &headers = resp.fs.headers();
for (size_t i = 0; i < headers.size(); ++i) { for (size_t i = 0; i < headers.size(); ++i) {
auto &hd = headers[i]; auto &kv = headers[i];
if (util::streq(std::begin(hd.name), hd.name.size(), RSTRING_PTR(key), if (kv.name == keyref) {
RSTRING_LEN(key))) {
continue; continue;
} }
if (i != p) { if (i != p) {
headers[p++] = std::move(hd); headers[p++] = std::move(kv);
} }
} }
headers.resize(p); headers.resize(p);
@ -137,16 +140,18 @@ mrb_value response_mod_header(mrb_state *mrb, mrb_value self, bool repl) {
auto n = mrb_ary_len(mrb, values); auto n = mrb_ary_len(mrb, values);
for (int i = 0; i < n; ++i) { for (int i = 0; i < n; ++i) {
auto value = mrb_ary_entry(values, i); auto value = mrb_ary_entry(values, i);
resp.fs.add_header(std::string(RSTRING_PTR(key), RSTRING_LEN(key)), resp.fs.add_header_token(
std::string(RSTRING_PTR(value), RSTRING_LEN(value))); keyref, StringRef{RSTRING_PTR(value),
static_cast<size_t>(RSTRING_LEN(value))},
false, token);
} }
} else if (!mrb_nil_p(values)) { } else if (!mrb_nil_p(values)) {
resp.fs.add_header(std::string(RSTRING_PTR(key), RSTRING_LEN(key)), resp.fs.add_header_token(
std::string(RSTRING_PTR(values), RSTRING_LEN(values))); keyref, StringRef{RSTRING_PTR(values),
static_cast<size_t>(RSTRING_LEN(values))},
false, token);
} }
data->response_headers_dirty = true;
return mrb_nil_value(); return mrb_nil_value();
} }
} // namespace } // namespace
@ -197,11 +202,6 @@ mrb_value response_return(mrb_state *mrb, mrb_value self) {
resp.http_status = 200; resp.http_status = 200;
} }
if (data->response_headers_dirty) {
resp.fs.index_headers();
data->response_headers_dirty = false;
}
if (downstream->expect_response_body() && vallen > 0) { if (downstream->expect_response_body() && vallen > 0) {
body = reinterpret_cast<const uint8_t *>(val); body = reinterpret_cast<const uint8_t *>(val);
bodylen = vallen; bodylen = vallen;
@ -211,7 +211,8 @@ mrb_value response_return(mrb_state *mrb, mrb_value self) {
if (cl) { if (cl) {
cl->value = util::utos(bodylen); cl->value = util::utos(bodylen);
} else { } else {
resp.fs.add_header("content-length", util::utos(bodylen), resp.fs.add_header_token(StringRef::from_lit("content-length"),
StringRef{util::utos(bodylen)}, false,
http2::HD_CONTENT_LENGTH); http2::HD_CONTENT_LENGTH);
} }
resp.fs.content_length = bodylen; resp.fs.content_length = bodylen;
@ -220,7 +221,9 @@ mrb_value response_return(mrb_state *mrb, mrb_value self) {
if (!date) { if (!date) {
auto lgconf = log_config(); auto lgconf = log_config();
lgconf->update_tstamp(std::chrono::system_clock::now()); lgconf->update_tstamp(std::chrono::system_clock::now());
resp.fs.add_header("date", lgconf->time_http_str, http2::HD_DATE); resp.fs.add_header_token(StringRef::from_lit("date"),
StringRef{lgconf->time_http_str}, false,
http2::HD_DATE);
} }
auto upstream = downstream->get_upstream(); auto upstream = downstream->get_upstream();

View File

@ -66,21 +66,21 @@ void Router::add_node(RNode *node, const char *pattern, size_t patlen,
add_next_node(node, std::move(new_node)); add_next_node(node, std::move(new_node));
} }
bool Router::add_route(const char *pattern, size_t patlen, size_t index) { bool Router::add_route(const StringRef &pattern, size_t index) {
auto node = &root_; auto node = &root_;
size_t i = 0; size_t i = 0;
for (;;) { for (;;) {
auto next_node = find_next_node(node, pattern[i]); auto next_node = find_next_node(node, pattern[i]);
if (next_node == nullptr) { if (next_node == nullptr) {
add_node(node, pattern + i, patlen - i, index); add_node(node, pattern.c_str() + i, pattern.size() - i, index);
return true; return true;
} }
node = next_node; node = next_node;
auto slen = patlen - i; auto slen = pattern.size() - i;
auto s = pattern + i; auto s = pattern.c_str() + i;
auto n = std::min(node->len, slen); auto n = std::min(node->len, slen);
size_t j; size_t j;
for (j = 0; j < n && node->s[j] == s[j]; ++j) for (j = 0; j < n && node->s[j] == s[j]; ++j)
@ -125,8 +125,8 @@ bool Router::add_route(const char *pattern, size_t patlen, size_t index) {
i += j; i += j;
assert(patlen > i); assert(pattern.size() > i);
add_node(node, pattern + i, patlen - i, index); add_node(node, pattern.c_str() + i, pattern.size() - i, index);
return true; return true;
} }
@ -259,18 +259,16 @@ const RNode *match_partial(const RNode *node, size_t offset, const char *first,
} }
} // namespace } // namespace
ssize_t Router::match(const std::string &host, const char *path, ssize_t Router::match(const StringRef &host, const StringRef &path) const {
size_t pathlen) const {
const RNode *node; const RNode *node;
size_t offset; size_t offset;
node = node = match_complete(&offset, &root_, std::begin(host), std::end(host));
match_complete(&offset, &root_, host.c_str(), host.c_str() + host.size());
if (node == nullptr) { if (node == nullptr) {
return -1; return -1;
} }
node = match_partial(node, offset, path, path + pathlen); node = match_partial(node, offset, std::begin(path), std::end(path));
if (node == nullptr || node == &root_) { if (node == nullptr || node == &root_) {
return -1; return -1;
} }

View File

@ -55,11 +55,10 @@ struct RNode {
class Router { class Router {
public: public:
Router(); Router();
// Adds route |pattern| of size |patlen| with its |index|. // Adds route |pattern| with its |index|.
bool add_route(const char *pattern, size_t patlen, size_t index); bool add_route(const StringRef &pattern, size_t index);
// Returns the matched index of pattern. -1 if there is no match. // Returns the matched index of pattern. -1 if there is no match.
ssize_t match(const std::string &host, const char *path, ssize_t match(const StringRef &host, const StringRef &path) const;
size_t pathlen) const;
void add_node(RNode *node, const char *pattern, size_t patlen, size_t index); void add_node(RNode *node, const char *pattern, size_t patlen, size_t index);

View File

@ -188,10 +188,13 @@ void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type,
} }
for (size_t i = 0; nv[i]; i += 2) { for (size_t i = 0; nv[i]; i += 2) {
req.fs.add_header(nv[i], nv[i + 1]); auto name = StringRef{nv[i]};
auto value = StringRef{nv[i + 1]};
auto token = http2::lookup_token(name.byte(), name.size());
req.fs.add_header_token(name, value, false, token);
} }
if (req.fs.index_headers() != 0) { if (req.fs.parse_content_length() != 0) {
if (upstream->error_reply(downstream, 400) != 0) { if (upstream->error_reply(downstream, 400) != 0) {
ULOG(FATAL, upstream) << "error_reply failed"; ULOG(FATAL, upstream) << "error_reply failed";
} }
@ -850,9 +853,12 @@ int SpdyUpstream::send_reply(Downstream *downstream, const uint8_t *body,
const auto &headers = resp.fs.headers(); const auto &headers = resp.fs.headers();
auto &httpconf = get_config()->http;
auto nva = std::vector<const char *>(); auto nva = std::vector<const char *>();
// 3 for :status, :version and server // 6 for :status, :version and server. 1 for last terminal nullptr.
nva.reserve(3 + headers.size()); nva.reserve(6 + headers.size() * 2 +
httpconf.add_response_headers.size() * 2 + 1);
nva.push_back(":status"); nva.push_back(":status");
nva.push_back(status_string.c_str()); nva.push_back(status_string.c_str());
@ -879,6 +885,11 @@ int SpdyUpstream::send_reply(Downstream *downstream, const uint8_t *body,
nva.push_back(get_config()->http.server_name.c_str()); nva.push_back(get_config()->http.server_name.c_str());
} }
for (auto &p : httpconf.add_response_headers) {
nva.push_back(p.name.c_str());
nva.push_back(p.value.c_str());
}
nva.push_back(nullptr); nva.push_back(nullptr);
rv = spdylay_submit_response(session_, downstream->get_stream_id(), rv = spdylay_submit_response(session_, downstream->get_stream_id(),
@ -1064,8 +1075,8 @@ int SpdyUpstream::on_downstream_header_complete(Downstream *downstream) {
} }
for (auto &p : httpconf.add_response_headers) { for (auto &p : httpconf.add_response_headers) {
nv[hdidx++] = p.first.c_str(); nv[hdidx++] = p.name.c_str();
nv[hdidx++] = p.second.c_str(); nv[hdidx++] = p.value.c_str();
} }
nv[hdidx++] = 0; nv[hdidx++] = 0;

View File

@ -36,6 +36,7 @@
#include <vector> #include <vector>
#include <string> #include <string>
#include <iomanip>
#include <openssl/crypto.h> #include <openssl/crypto.h>
#include <openssl/x509.h> #include <openssl/x509.h>
@ -124,13 +125,13 @@ set_alpn_prefs(const std::vector<std::string> &protos) {
namespace { namespace {
int ssl_pem_passwd_cb(char *buf, int size, int rwflag, void *user_data) { int ssl_pem_passwd_cb(char *buf, int size, int rwflag, void *user_data) {
auto config = static_cast<Config *>(user_data); auto config = static_cast<Config *>(user_data);
int len = (int)strlen(config->tls.private_key_passwd.get()); auto len = static_cast<int>(config->tls.private_key_passwd.size());
if (size < len + 1) { if (size < len + 1) {
LOG(ERROR) << "ssl_pem_passwd_cb: buf is too small " << size; LOG(ERROR) << "ssl_pem_passwd_cb: buf is too small " << size;
return 0; return 0;
} }
// Copy string including last '\0'. // Copy string including last '\0'.
memcpy(buf, config->tls.private_key_passwd.get(), len + 1); memcpy(buf, config->tls.private_key_passwd.c_str(), len + 1);
return len; return len;
} }
} // namespace } // namespace
@ -485,7 +486,7 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file
SSL_CTX_set_session_id_context(ssl_ctx, sid_ctx, sizeof(sid_ctx) - 1); SSL_CTX_set_session_id_context(ssl_ctx, sid_ctx, sizeof(sid_ctx) - 1);
SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_SERVER); SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_SERVER);
if (tlsconf.session_cache.memcached.host) { if (!tlsconf.session_cache.memcached.host.empty()) {
SSL_CTX_sess_set_new_cb(ssl_ctx, tls_session_new_cb); SSL_CTX_sess_set_new_cb(ssl_ctx, tls_session_new_cb);
SSL_CTX_sess_set_get_cb(ssl_ctx, tls_session_get_cb); SSL_CTX_sess_set_get_cb(ssl_ctx, tls_session_get_cb);
} }
@ -493,8 +494,8 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file
SSL_CTX_set_timeout(ssl_ctx, tlsconf.session_timeout.count()); SSL_CTX_set_timeout(ssl_ctx, tlsconf.session_timeout.count());
const char *ciphers; const char *ciphers;
if (tlsconf.ciphers) { if (!tlsconf.ciphers.empty()) {
ciphers = tlsconf.ciphers.get(); ciphers = tlsconf.ciphers.c_str();
} else { } else {
ciphers = nghttp2::ssl::DEFAULT_CIPHER_LIST; ciphers = nghttp2::ssl::DEFAULT_CIPHER_LIST;
} }
@ -527,9 +528,9 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file
#endif // OPENSSL_NO_EC #endif // OPENSSL_NO_EC
if (tlsconf.dh_param_file) { if (!tlsconf.dh_param_file.empty()) {
// Read DH parameters from file // Read DH parameters from file
auto bio = BIO_new_file(tlsconf.dh_param_file.get(), "r"); auto bio = BIO_new_file(tlsconf.dh_param_file.c_str(), "r");
if (bio == nullptr) { if (bio == nullptr) {
LOG(FATAL) << "BIO_new_file() failed: " LOG(FATAL) << "BIO_new_file() failed: "
<< ERR_error_string(ERR_get_error(), nullptr); << ERR_error_string(ERR_get_error(), nullptr);
@ -548,7 +549,7 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
if (tlsconf.private_key_passwd) { if (!tlsconf.private_key_passwd.empty()) {
SSL_CTX_set_default_passwd_cb(ssl_ctx, ssl_pem_passwd_cb); SSL_CTX_set_default_passwd_cb(ssl_ctx, ssl_pem_passwd_cb);
SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, (void *)get_config()); SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, (void *)get_config());
} }
@ -579,12 +580,12 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file
DIE(); DIE();
} }
if (tlsconf.client_verify.enabled) { if (tlsconf.client_verify.enabled) {
if (tlsconf.client_verify.cacert) { if (!tlsconf.client_verify.cacert.empty()) {
if (SSL_CTX_load_verify_locations( if (SSL_CTX_load_verify_locations(
ssl_ctx, tlsconf.client_verify.cacert.get(), nullptr) != 1) { ssl_ctx, tlsconf.client_verify.cacert.c_str(), nullptr) != 1) {
LOG(FATAL) << "Could not load trusted ca certificates from " LOG(FATAL) << "Could not load trusted ca certificates from "
<< tlsconf.client_verify.cacert.get() << ": " << tlsconf.client_verify.cacert << ": "
<< ERR_error_string(ERR_get_error(), nullptr); << ERR_error_string(ERR_get_error(), nullptr);
DIE(); DIE();
} }
@ -592,10 +593,10 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file
// error even though it returns success. See // error even though it returns success. See
// http://forum.nginx.org/read.php?29,242540 // http://forum.nginx.org/read.php?29,242540
ERR_clear_error(); ERR_clear_error();
auto list = SSL_load_client_CA_file(tlsconf.client_verify.cacert.get()); auto list = SSL_load_client_CA_file(tlsconf.client_verify.cacert.c_str());
if (!list) { if (!list) {
LOG(FATAL) << "Could not load ca certificates from " LOG(FATAL) << "Could not load ca certificates from "
<< tlsconf.client_verify.cacert.get() << ": " << tlsconf.client_verify.cacert << ": "
<< ERR_error_string(ERR_get_error(), nullptr); << ERR_error_string(ERR_get_error(), nullptr);
DIE(); DIE();
} }
@ -660,9 +661,13 @@ int select_h1_next_proto_cb(SSL *ssl, unsigned char **out,
SSL_CTX *create_ssl_client_context( SSL_CTX *create_ssl_client_context(
#ifdef HAVE_NEVERBLEED #ifdef HAVE_NEVERBLEED
neverbleed_t *nb neverbleed_t *nb,
#endif // HAVE_NEVERBLEED #endif // HAVE_NEVERBLEED
) { const StringRef &cacert, const StringRef &cert_file,
const StringRef &private_key_file, const StringRef &alpn,
int (*next_proto_select_cb)(SSL *s, unsigned char **out,
unsigned char *outlen, const unsigned char *in,
unsigned int inlen, void *arg)) {
auto ssl_ctx = SSL_CTX_new(SSLv23_client_method()); auto ssl_ctx = SSL_CTX_new(SSLv23_client_method());
if (!ssl_ctx) { if (!ssl_ctx) {
LOG(FATAL) << ERR_error_string(ERR_get_error(), nullptr); LOG(FATAL) << ERR_error_string(ERR_get_error(), nullptr);
@ -679,8 +684,8 @@ SSL_CTX *create_ssl_client_context(
SSL_CTX_set_options(ssl_ctx, ssl_opts | tlsconf.tls_proto_mask); SSL_CTX_set_options(ssl_ctx, ssl_opts | tlsconf.tls_proto_mask);
const char *ciphers; const char *ciphers;
if (tlsconf.ciphers) { if (!tlsconf.ciphers.empty()) {
ciphers = tlsconf.ciphers.get(); ciphers = tlsconf.ciphers.c_str();
} else { } else {
ciphers = nghttp2::ssl::DEFAULT_CIPHER_LIST; ciphers = nghttp2::ssl::DEFAULT_CIPHER_LIST;
} }
@ -698,71 +703,52 @@ SSL_CTX *create_ssl_client_context(
<< ERR_error_string(ERR_get_error(), nullptr); << ERR_error_string(ERR_get_error(), nullptr);
} }
if (tlsconf.cacert) { if (!cacert.empty()) {
if (SSL_CTX_load_verify_locations(ssl_ctx, tlsconf.cacert.get(), nullptr) != if (SSL_CTX_load_verify_locations(ssl_ctx, cacert.c_str(), nullptr) != 1) {
1) {
LOG(FATAL) << "Could not load trusted ca certificates from " LOG(FATAL) << "Could not load trusted ca certificates from " << cacert
<< tlsconf.cacert.get() << ": " << ": " << ERR_error_string(ERR_get_error(), nullptr);
<< ERR_error_string(ERR_get_error(), nullptr);
DIE(); DIE();
} }
} }
if (tlsconf.client.private_key_file) { if (!cert_file.empty()) {
if (SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file.c_str()) != 1) {
LOG(FATAL) << "Could not load client certificate from " << cert_file
<< ": " << ERR_error_string(ERR_get_error(), nullptr);
DIE();
}
}
if (!private_key_file.empty()) {
#ifndef HAVE_NEVERBLEED #ifndef HAVE_NEVERBLEED
if (SSL_CTX_use_PrivateKey_file(ssl_ctx, if (SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key_file.c_str(),
tlsconf.client.private_key_file.get(),
SSL_FILETYPE_PEM) != 1) { SSL_FILETYPE_PEM) != 1) {
LOG(FATAL) << "Could not load client private key from " LOG(FATAL) << "Could not load client private key from "
<< tlsconf.client.private_key_file.get() << ": " << private_key_file << ": "
<< ERR_error_string(ERR_get_error(), nullptr); << ERR_error_string(ERR_get_error(), nullptr);
DIE(); DIE();
} }
#else // HAVE_NEVERBLEED #else // HAVE_NEVERBLEED
std::array<char, NEVERBLEED_ERRBUF_SIZE> errbuf; std::array<char, NEVERBLEED_ERRBUF_SIZE> errbuf;
if (neverbleed_load_private_key_file(nb, ssl_ctx, if (neverbleed_load_private_key_file(nb, ssl_ctx, private_key_file.c_str(),
tlsconf.client.private_key_file.get(),
errbuf.data()) != 1) { errbuf.data()) != 1) {
LOG(FATAL) << "neverbleed_load_private_key_file failed: " LOG(FATAL) << "neverbleed_load_private_key_file: could not load client "
"private key from " << private_key_file << ": "
<< errbuf.data(); << errbuf.data();
DIE(); DIE();
} }
#endif // HAVE_NEVERBLEED #endif // HAVE_NEVERBLEED
} }
if (tlsconf.client.cert_file) {
if (SSL_CTX_use_certificate_chain_file(
ssl_ctx, tlsconf.client.cert_file.get()) != 1) {
LOG(FATAL) << "Could not load client certificate from "
<< tlsconf.client.cert_file.get() << ": "
<< ERR_error_string(ERR_get_error(), nullptr);
DIE();
}
}
auto &downstreamconf = get_config()->conn.downstream;
if (downstreamconf.proto == PROTO_HTTP2) {
// NPN selection callback // NPN selection callback
SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_h2_next_proto_cb, nullptr); SSL_CTX_set_next_proto_select_cb(ssl_ctx, next_proto_select_cb, nullptr);
#if OPENSSL_VERSION_NUMBER >= 0x10002000L #if OPENSSL_VERSION_NUMBER >= 0x10002000L
// ALPN advertisement; We only advertise HTTP/2 // ALPN advertisement
auto proto_list = util::get_default_alpn(); SSL_CTX_set_alpn_protos(ssl_ctx, alpn.byte(), alpn.size());
SSL_CTX_set_alpn_protos(ssl_ctx, proto_list.data(), proto_list.size());
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
} else {
// NPN selection callback
SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_h1_next_proto_cb, nullptr);
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
SSL_CTX_set_alpn_protos(
ssl_ctx, reinterpret_cast<const unsigned char *>(NGHTTP2_H1_1_ALPN),
str_size(NGHTTP2_H1_1_ALPN));
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
}
return ssl_ctx; return ssl_ctx;
} }
@ -997,7 +983,7 @@ int verify_hostname(X509 *cert, const char *hostname, size_t hlen,
} }
} // namespace } // namespace
int check_cert(SSL *ssl, const DownstreamAddr *addr) { int check_cert(SSL *ssl, const Address *addr, const StringRef &host) {
auto cert = SSL_get_peer_certificate(ssl); auto cert = SSL_get_peer_certificate(ssl);
if (!cert) { if (!cert) {
LOG(ERROR) << "No certificate found"; LOG(ERROR) << "No certificate found";
@ -1011,18 +997,21 @@ int check_cert(SSL *ssl, const DownstreamAddr *addr) {
return -1; return -1;
} }
auto &backend_sni_name = get_config()->tls.backend_sni_name; if (verify_hostname(cert, host.c_str(), host.size(), addr) != 0) {
auto hostname = !backend_sni_name.empty() ? StringRef(backend_sni_name)
: StringRef(addr->host);
if (verify_hostname(cert, hostname.c_str(), hostname.size(), &addr->addr) !=
0) {
LOG(ERROR) << "Certificate verification failed: hostname does not match"; LOG(ERROR) << "Certificate verification failed: hostname does not match";
return -1; return -1;
} }
return 0; return 0;
} }
int check_cert(SSL *ssl, const DownstreamAddr *addr) {
auto &backend_sni_name = get_config()->tls.backend_sni_name;
auto hostname = !backend_sni_name.empty() ? StringRef(backend_sni_name)
: StringRef(addr->host);
return check_cert(ssl, &addr->addr, hostname);
}
CertLookupTree::CertLookupTree() { CertLookupTree::CertLookupTree() {
root_.ssl_ctx = nullptr; root_.ssl_ctx = nullptr;
root_.str = nullptr; root_.str = nullptr;
@ -1257,8 +1246,8 @@ SSL_CTX *setup_server_ssl_context(std::vector<SSL_CTX *> &all_ssl_ctx,
auto &tlsconf = get_config()->tls; auto &tlsconf = get_config()->tls;
auto ssl_ctx = ssl::create_ssl_context(tlsconf.private_key_file.get(), auto ssl_ctx = ssl::create_ssl_context(tlsconf.private_key_file.c_str(),
tlsconf.cert_file.get() tlsconf.cert_file.c_str()
#ifdef HAVE_NEVERBLEED #ifdef HAVE_NEVERBLEED
, ,
nb nb
@ -1293,8 +1282,8 @@ SSL_CTX *setup_server_ssl_context(std::vector<SSL_CTX *> &all_ssl_ctx,
} }
} }
if (ssl::cert_lookup_tree_add_cert_from_file(cert_tree, ssl_ctx, if (ssl::cert_lookup_tree_add_cert_from_file(
tlsconf.cert_file.get()) == -1) { cert_tree, ssl_ctx, tlsconf.cert_file.c_str()) == -1) {
LOG(FATAL) << "Failed to add default certificate."; LOG(FATAL) << "Failed to add default certificate.";
DIE(); DIE();
} }
@ -1304,7 +1293,7 @@ SSL_CTX *setup_server_ssl_context(std::vector<SSL_CTX *> &all_ssl_ctx,
bool downstream_tls_enabled() { return !get_config()->conn.downstream.no_tls; } bool downstream_tls_enabled() { return !get_config()->conn.downstream.no_tls; }
SSL_CTX *setup_client_ssl_context( SSL_CTX *setup_downstream_client_ssl_context(
#ifdef HAVE_NEVERBLEED #ifdef HAVE_NEVERBLEED
neverbleed_t *nb neverbleed_t *nb
#endif // HAVE_NEVERBLEED #endif // HAVE_NEVERBLEED
@ -1313,11 +1302,30 @@ SSL_CTX *setup_client_ssl_context(
return nullptr; return nullptr;
} }
auto &tlsconf = get_config()->tls;
auto &downstreamconf = get_config()->conn.downstream;
std::vector<unsigned char> h2alpn;
StringRef alpn;
int (*next_proto_select_cb)(SSL *s, unsigned char **out,
unsigned char *outlen, const unsigned char *in,
unsigned int inlen, void *arg);
if (downstreamconf.proto == PROTO_HTTP2) {
h2alpn = util::get_default_alpn();
alpn = StringRef(h2alpn.data(), h2alpn.size());
next_proto_select_cb = select_h2_next_proto_cb;
} else {
alpn = StringRef::from_lit(NGHTTP2_H1_1_ALPN);
next_proto_select_cb = select_h1_next_proto_cb;
}
return ssl::create_ssl_client_context( return ssl::create_ssl_client_context(
#ifdef HAVE_NEVERBLEED #ifdef HAVE_NEVERBLEED
nb nb,
#endif // HAVE_NEVERBLEED #endif // HAVE_NEVERBLEED
); StringRef{tlsconf.cacert}, StringRef{tlsconf.client.cert_file},
StringRef{tlsconf.client.private_key_file}, alpn, next_proto_select_cb);
} }
CertLookupTree *create_cert_lookup_tree() { CertLookupTree *create_cert_lookup_tree() {
@ -1328,6 +1336,50 @@ CertLookupTree *create_cert_lookup_tree() {
return new ssl::CertLookupTree(); return new ssl::CertLookupTree();
} }
namespace {
std::vector<uint8_t> serialize_ssl_session(SSL_SESSION *session) {
auto len = i2d_SSL_SESSION(session, nullptr);
auto buf = std::vector<uint8_t>(len);
auto p = buf.data();
i2d_SSL_SESSION(session, &p);
return buf;
}
} // namespace
void try_cache_tls_session(DownstreamAddr *addr, SSL_SESSION *session,
ev_tstamp t) {
auto &cache = addr->tls_session_cache;
if (cache.last_updated + 1_min > t) {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Cache for addr=" << util::to_numeric_addr(&addr->addr)
<< " is still host. Not updating.";
}
return;
}
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Update cache entry for SSL_SESSION=" << session
<< ", addr=" << util::to_numeric_addr(&addr->addr)
<< ", timestamp=" << std::fixed << std::setprecision(6) << t;
}
cache.session_data = serialize_ssl_session(session);
cache.last_updated = t;
}
SSL_SESSION *reuse_tls_session(const DownstreamAddr *addr) {
auto &cache = addr->tls_session_cache;
if (cache.session_data.empty()) {
return nullptr;
}
auto p = cache.session_data.data();
return d2i_SSL_SESSION(nullptr, &p, cache.session_data.size());
}
} // namespace ssl } // namespace ssl
} // namespace shrpx } // namespace shrpx

View File

@ -39,6 +39,8 @@
#include <neverbleed.h> #include <neverbleed.h>
#endif // HAVE_NEVERBLEED #endif // HAVE_NEVERBLEED
#include "network.h"
namespace shrpx { namespace shrpx {
class ClientHandler; class ClientHandler;
@ -72,16 +74,19 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file
// Create client side SSL_CTX // Create client side SSL_CTX
SSL_CTX *create_ssl_client_context( SSL_CTX *create_ssl_client_context(
#ifdef HAVE_NEVERBLEED #ifdef HAVE_NEVERBLEED
neverbleed_t *nb neverbleed_t *nb,
#endif // HAVE_NEVERBLEED #endif // HAVE_NEVERBLEED
); const StringRef &cacert, const StringRef &cert_file,
const StringRef &private_key_file, const StringRef &alpn,
int (*next_proto_select_cb)(SSL *s, unsigned char **out,
unsigned char *outlen, const unsigned char *in,
unsigned int inlen, void *arg));
ClientHandler *accept_connection(Worker *worker, int fd, sockaddr *addr, ClientHandler *accept_connection(Worker *worker, int fd, sockaddr *addr,
int addrlen, const UpstreamAddr *faddr); int addrlen, const UpstreamAddr *faddr);
// Check peer's certificate against first downstream address in // Check peer's certificate against given |address| and |host|.
// Config::downstream_addrs. We only consider first downstream since int check_cert(SSL *ssl, const Address *addr, const StringRef &host);
// we use this function for HTTP/2 downstream link only.
int check_cert(SSL *ssl, const DownstreamAddr *addr); int check_cert(SSL *ssl, const DownstreamAddr *addr);
// Retrieves DNS and IP address in subjectAltNames and commonName from // Retrieves DNS and IP address in subjectAltNames and commonName from
@ -190,7 +195,7 @@ SSL_CTX *setup_server_ssl_context(std::vector<SSL_CTX *> &all_ssl_ctx,
// Setups client side SSL_CTX. This function inspects get_config() // Setups client side SSL_CTX. This function inspects get_config()
// and if downstream_no_tls is true, returns nullptr. Otherwise, only // and if downstream_no_tls is true, returns nullptr. Otherwise, only
// construct SSL_CTX if either client_mode or http2_bridge is true. // construct SSL_CTX if either client_mode or http2_bridge is true.
SSL_CTX *setup_client_ssl_context( SSL_CTX *setup_downstream_client_ssl_context(
#ifdef HAVE_NEVERBLEED #ifdef HAVE_NEVERBLEED
neverbleed_t *nb neverbleed_t *nb
#endif // HAVE_NEVERBLEED #endif // HAVE_NEVERBLEED
@ -212,6 +217,17 @@ bool downstream_tls_enabled();
bool tls_hostname_match(const char *pattern, size_t plen, const char *hostname, bool tls_hostname_match(const char *pattern, size_t plen, const char *hostname,
size_t hlen); size_t hlen);
// Caches |session| which is associated to remote address |addr|.
// |session| is serialized into ASN1 representation, and stored. |t|
// is used as a time stamp. Depending on the existing cache's time
// stamp, |session| might not be cached.
void try_cache_tls_session(DownstreamAddr *addr, SSL_SESSION *session,
ev_tstamp t);
// Returns cached session associated |addr|. If no cache entry is
// found associated to |addr|, nullptr will be returned.
SSL_SESSION *reuse_tls_session(const DownstreamAddr *addr);
} // namespace ssl } // namespace ssl
} // namespace shrpx } // namespace shrpx

View File

@ -67,19 +67,20 @@ std::random_device rd;
} // namespace } // namespace
Worker::Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx, Worker::Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx,
SSL_CTX *tls_session_cache_memcached_ssl_ctx,
ssl::CertLookupTree *cert_tree, ssl::CertLookupTree *cert_tree,
const std::shared_ptr<TicketKeys> &ticket_keys) const std::shared_ptr<TicketKeys> &ticket_keys)
: randgen_(rd()), : randgen_(rd()),
dconn_pool_(get_config()->conn.downstream.addr_groups.size()), dconn_pool_(get_config()->conn.downstream.addr_groups.size()),
worker_stat_(get_config()->conn.downstream.addr_groups.size()), worker_stat_(get_config()->conn.downstream.addr_groups.size()),
dgrps_(get_config()->conn.downstream.addr_groups.size()), dgrps_(get_config()->conn.downstream.addr_groups.size()),
downstream_tls_session_cache_size_(0),
loop_(loop), loop_(loop),
sv_ssl_ctx_(sv_ssl_ctx), sv_ssl_ctx_(sv_ssl_ctx),
cl_ssl_ctx_(cl_ssl_ctx), cl_ssl_ctx_(cl_ssl_ctx),
cert_tree_(cert_tree), cert_tree_(cert_tree),
ticket_keys_(ticket_keys), ticket_keys_(ticket_keys),
connect_blocker_(make_unique<ConnectBlocker>(loop_)), downstream_addr_groups_(get_config()->conn.downstream.addr_groups),
connect_blocker_(make_unique<ConnectBlocker>(randgen_, loop_)),
graceful_shutdown_(false) { graceful_shutdown_(false) {
ev_async_init(&w_, eventcb); ev_async_init(&w_, eventcb);
w_.data = this; w_.data = this;
@ -90,9 +91,11 @@ Worker::Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx,
auto &session_cacheconf = get_config()->tls.session_cache; auto &session_cacheconf = get_config()->tls.session_cache;
if (session_cacheconf.memcached.host) { if (!session_cacheconf.memcached.host.empty()) {
session_cache_memcached_dispatcher_ = make_unique<MemcachedDispatcher>( session_cache_memcached_dispatcher_ = make_unique<MemcachedDispatcher>(
&session_cacheconf.memcached.addr, loop); &session_cacheconf.memcached.addr, loop,
tls_session_cache_memcached_ssl_ctx,
StringRef{session_cacheconf.memcached.host}, &mcpool_);
} }
auto &downstreamconf = get_config()->conn.downstream; auto &downstreamconf = get_config()->conn.downstream;
@ -106,21 +109,27 @@ Worker::Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx,
m = downstreamconf.addr_groups[group].addrs.size(); m = downstreamconf.addr_groups[group].addrs.size();
} }
for (size_t idx = 0; idx < m; ++idx) { for (size_t idx = 0; idx < m; ++idx) {
dgrp.http2sessions.push_back(make_unique<Http2Session>( dgrp.http2sessions.push_back(
loop_, cl_ssl_ctx, connect_blocker_.get(), this, group, idx)); make_unique<Http2Session>(loop_, cl_ssl_ctx, this, group, idx));
} }
++group; ++group;
} }
} }
for (auto &group : downstream_addr_groups_) {
for (auto &addr : group.addrs) {
addr.connect_blocker = new ConnectBlocker(randgen_, loop_);
}
}
} }
Worker::~Worker() { Worker::~Worker() {
ev_async_stop(loop_, &w_); ev_async_stop(loop_, &w_);
ev_timer_stop(loop_, &mcpool_clear_timer_); ev_timer_stop(loop_, &mcpool_clear_timer_);
for (auto &p : downstream_tls_session_cache_) { for (auto &group : downstream_addr_groups_) {
for (auto session : p.second) { for (auto &addr : group.addrs) {
SSL_SESSION_free(session); delete addr.connect_blocker;
} }
} }
} }
@ -262,10 +271,6 @@ Http2Session *Worker::next_http2_session(size_t group) {
return res; return res;
} }
ConnectBlocker *Worker::get_connect_blocker() const {
return connect_blocker_.get();
}
struct ev_loop *Worker::get_loop() const { struct ev_loop *Worker::get_loop() const {
return loop_; return loop_;
} }
@ -293,8 +298,7 @@ std::mt19937 &Worker::get_randgen() { return randgen_; }
#ifdef HAVE_MRUBY #ifdef HAVE_MRUBY
int Worker::create_mruby_context() { int Worker::create_mruby_context() {
auto mruby_file = get_config()->mruby_file.get(); mruby_ctx_ = mruby::create_mruby_context(StringRef{get_config()->mruby_file});
mruby_ctx_ = mruby::create_mruby_context(mruby_file);
if (!mruby_ctx_) { if (!mruby_ctx_) {
return -1; return -1;
} }
@ -307,57 +311,12 @@ mruby::MRubyContext *Worker::get_mruby_context() const {
} }
#endif // HAVE_MRUBY #endif // HAVE_MRUBY
void Worker::cache_downstream_tls_session(const DownstreamAddr *addr, std::vector<DownstreamAddrGroup> &Worker::get_downstream_addr_groups() {
SSL_SESSION *session) { return downstream_addr_groups_;
auto &tlsconf = get_config()->tls;
auto max = tlsconf.downstream_session_cache_per_worker;
if (max == 0) {
return;
} }
if (downstream_tls_session_cache_size_ >= max) { ConnectBlocker *Worker::get_connect_blocker() const {
// It is implementation dependent which item is returned from return connect_blocker_.get();
// std::begin(). Probably, this depends on hash algorithm. If it
// is random fashion, then we are mostly OK.
auto it = std::begin(downstream_tls_session_cache_);
assert(it != std::end(downstream_tls_session_cache_));
auto &v = (*it).second;
assert(!v.empty());
auto sess = v.front();
v.pop_front();
SSL_SESSION_free(sess);
if (v.empty()) {
downstream_tls_session_cache_.erase(it);
}
}
auto it = downstream_tls_session_cache_.find(addr);
if (it == std::end(downstream_tls_session_cache_)) {
std::tie(it, std::ignore) = downstream_tls_session_cache_.emplace(
addr, std::deque<SSL_SESSION *>());
}
(*it).second.push_back(session);
++downstream_tls_session_cache_size_;
}
SSL_SESSION *Worker::reuse_downstream_tls_session(const DownstreamAddr *addr) {
auto it = downstream_tls_session_cache_.find(addr);
if (it == std::end(downstream_tls_session_cache_)) {
return nullptr;
}
auto &v = (*it).second;
assert(!v.empty());
auto session = v.back();
v.pop_back();
--downstream_tls_session_cache_size_;
if (v.empty()) {
downstream_tls_session_cache_.erase(it);
}
return session;
} }
} // namespace shrpx } // namespace shrpx

View File

@ -104,6 +104,7 @@ struct WorkerEvent {
class Worker { class Worker {
public: public:
Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx, Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx,
SSL_CTX *tls_session_cache_memcached_ssl_ctx,
ssl::CertLookupTree *cert_tree, ssl::CertLookupTree *cert_tree,
const std::shared_ptr<TicketKeys> &ticket_keys); const std::shared_ptr<TicketKeys> &ticket_keys);
~Worker(); ~Worker();
@ -122,7 +123,6 @@ public:
WorkerStat *get_worker_stat(); WorkerStat *get_worker_stat();
DownstreamConnectionPool *get_dconn_pool(); DownstreamConnectionPool *get_dconn_pool();
Http2Session *next_http2_session(size_t group); Http2Session *next_http2_session(size_t group);
ConnectBlocker *get_connect_blocker() const;
struct ev_loop *get_loop() const; struct ev_loop *get_loop() const;
SSL_CTX *get_sv_ssl_ctx() const; SSL_CTX *get_sv_ssl_ctx() const;
SSL_CTX *get_cl_ssl_ctx() const; SSL_CTX *get_cl_ssl_ctx() const;
@ -145,16 +145,9 @@ public:
mruby::MRubyContext *get_mruby_context() const; mruby::MRubyContext *get_mruby_context() const;
#endif // HAVE_MRUBY #endif // HAVE_MRUBY
// Caches |session| which is associated to downstream address std::vector<DownstreamAddrGroup> &get_downstream_addr_groups();
// |addr|. The caller is responsible to increment the reference
// count of |session|, since this function does not do so. ConnectBlocker *get_connect_blocker() const;
void cache_downstream_tls_session(const DownstreamAddr *addr,
SSL_SESSION *session);
// Returns cached session associated |addr|. If non-nullptr value
// is returned, its cache entry was successfully removed from cache.
// If no cache entry is found associated to |addr|, nullptr will be
// returned.
SSL_SESSION *reuse_downstream_tls_session(const DownstreamAddr *addr);
private: private:
#ifndef NOTHREADS #ifndef NOTHREADS
@ -170,15 +163,6 @@ private:
WorkerStat worker_stat_; WorkerStat worker_stat_;
std::vector<DownstreamGroup> dgrps_; std::vector<DownstreamGroup> dgrps_;
// Cache for SSL_SESSION for downstream connections. SSL_SESSION is
// associated to downstream address. One address has multiple
// SSL_SESSION objects. New SSL_SESSION is appended to the deque.
// When doing eviction due to storage limitation, the SSL_SESSION
// which sits at the front of deque is removed.
std::unordered_map<const DownstreamAddr *, std::deque<SSL_SESSION *>>
downstream_tls_session_cache_;
size_t downstream_tls_session_cache_size_;
std::unique_ptr<MemcachedDispatcher> session_cache_memcached_dispatcher_; std::unique_ptr<MemcachedDispatcher> session_cache_memcached_dispatcher_;
#ifdef HAVE_MRUBY #ifdef HAVE_MRUBY
std::unique_ptr<mruby::MRubyContext> mruby_ctx_; std::unique_ptr<mruby::MRubyContext> mruby_ctx_;
@ -192,6 +176,9 @@ private:
ssl::CertLookupTree *cert_tree_; ssl::CertLookupTree *cert_tree_;
std::shared_ptr<TicketKeys> ticket_keys_; std::shared_ptr<TicketKeys> ticket_keys_;
std::vector<DownstreamAddrGroup> downstream_addr_groups_;
// Worker level blocker for downstream connection. For example,
// this is used when file decriptor is exhausted.
std::unique_ptr<ConnectBlocker> connect_blocker_; std::unique_ptr<ConnectBlocker> connect_blocker_;
bool graceful_shutdown_; bool graceful_shutdown_;

View File

@ -64,7 +64,7 @@ void drop_privileges(
#endif // HAVE_NEVERBLEED #endif // HAVE_NEVERBLEED
) { ) {
if (getuid() == 0 && get_config()->uid != 0) { if (getuid() == 0 && get_config()->uid != 0) {
if (initgroups(get_config()->user.get(), get_config()->gid) != 0) { if (initgroups(get_config()->user.c_str(), get_config()->gid) != 0) {
auto error = errno; auto error = errno;
LOG(FATAL) << "Could not change supplementary groups: " LOG(FATAL) << "Could not change supplementary groups: "
<< strerror(error); << strerror(error);
@ -86,7 +86,7 @@ void drop_privileges(
} }
#ifdef HAVE_NEVERBLEED #ifdef HAVE_NEVERBLEED
if (nb) { if (nb) {
neverbleed_setuidgid(nb, get_config()->user.get(), 1); neverbleed_setuidgid(nb, get_config()->user.c_str(), 1);
} }
#endif // HAVE_NEVERBLEED #endif // HAVE_NEVERBLEED
} }
@ -420,13 +420,24 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) {
#endif // HAVE_NEVERBLEED #endif // HAVE_NEVERBLEED
MemchunkPool mcpool;
ev_timer renew_ticket_key_timer; ev_timer renew_ticket_key_timer;
if (!upstreamconf.no_tls) { if (!upstreamconf.no_tls) {
auto &ticketconf = get_config()->tls.ticket; auto &ticketconf = get_config()->tls.ticket;
auto &memcachedconf = ticketconf.memcached;
if (!memcachedconf.host.empty()) {
SSL_CTX *ssl_ctx = nullptr;
if (memcachedconf.tls) {
ssl_ctx = conn_handler.create_tls_ticket_key_memcached_ssl_ctx();
}
if (ticketconf.memcached.host) {
conn_handler.set_tls_ticket_key_memcached_dispatcher( conn_handler.set_tls_ticket_key_memcached_dispatcher(
make_unique<MemcachedDispatcher>(&ticketconf.memcached.addr, loop)); make_unique<MemcachedDispatcher>(
&ticketconf.memcached.addr, loop, ssl_ctx,
StringRef{memcachedconf.host}, &mcpool));
ev_timer_init(&renew_ticket_key_timer, memcached_get_ticket_key_cb, 0., ev_timer_init(&renew_ticket_key_timer, memcached_get_ticket_key_cb, 0.,
0.); 0.);

View File

@ -36,19 +36,21 @@ namespace nghttp2 {
namespace ssl { namespace ssl {
// Recommended general purpose "Non-Backward Compatible" cipher by // Recommended general purpose "Intermediate compatibility" cipher
// mozilla. // suites by mozilla.
// //
// https://wiki.mozilla.org/Security/Server_Side_TLS // https://wiki.mozilla.org/Security/Server_Side_TLS
const char *const DEFAULT_CIPHER_LIST = const char *const DEFAULT_CIPHER_LIST =
"ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-" "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-"
"AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:" "AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-"
"DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-" "SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-"
"AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-" "AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-"
"AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-" "ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-"
"AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:" "AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-"
"DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:" "SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-"
"!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK"; "ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-"
"SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-"
"SHA:DES-CBC3-SHA:!DSS";
namespace { namespace {
std::vector<std::mutex> ssl_global_locks; std::vector<std::mutex> ssl_global_locks;
@ -117,571 +119,25 @@ TLSSessionInfo *get_tls_session_info(TLSSessionInfo *tls_info, SSL *ssl) {
return tls_info; return tls_info;
} }
// The black listed cipher suites for HTTP/2 described in RFC 7540. /* Conditional logic w/ lookup tables to check if id is one of the
enum { the black listed cipher suites for HTTP/2 described in RFC 7540.
TLS_NULL_WITH_NULL_NULL = 0x0000u, https://github.com/jay/http2_blacklisted_ciphers
TLS_RSA_WITH_NULL_MD5 = 0x0001u, */
TLS_RSA_WITH_NULL_SHA = 0x0002u, #define IS_CIPHER_BANNED_METHOD2(id) ( \
TLS_RSA_EXPORT_WITH_RC4_40_MD5 = 0x0003u, (0x0000 <= id && id <= 0x00FF && \
TLS_RSA_WITH_RC4_128_MD5 = 0x0004u, "\xFF\xFF\xFF\xCF\xFF\xFF\xFF\xFF\x7F\x00\x00\x00\x80\x3F\x00\x00" \
TLS_RSA_WITH_RC4_128_SHA = 0x0005u, "\xF0\xFF\xFF\x3F\xF3\xF3\xFF\xFF\x3F\x00\x00\x00\x00\x00\x00\x80" \
TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = 0x0006u, [(id & 0xFF) / 8] & (1 << (id % 8))) || \
TLS_RSA_WITH_IDEA_CBC_SHA = 0x0007u, (0xC000 <= id && id <= 0xC0FF && \
TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0008u, "\xFE\xFF\xFF\xFF\xFF\x67\xFE\xFF\xFF\xFF\x33\xCF\xFC\xCF\xFF\xCF" \
TLS_RSA_WITH_DES_CBC_SHA = 0x0009u, "\x3C\xF3\xFC\x3F\x33\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0x000Au, [(id & 0xFF) / 8] & (1 << (id % 8))) \
TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x000Bu, )
TLS_DH_DSS_WITH_DES_CBC_SHA = 0x000Cu,
TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = 0x000Du,
TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x000Eu,
TLS_DH_RSA_WITH_DES_CBC_SHA = 0x000Fu,
TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = 0x0010u,
TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x0011u,
TLS_DHE_DSS_WITH_DES_CBC_SHA = 0x0012u,
TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 0x0013u,
TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0014u,
TLS_DHE_RSA_WITH_DES_CBC_SHA = 0x0015u,
TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0x0016u,
TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 = 0x0017u,
TLS_DH_anon_WITH_RC4_128_MD5 = 0x0018u,
TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA = 0x0019u,
TLS_DH_anon_WITH_DES_CBC_SHA = 0x001Au,
TLS_DH_anon_WITH_3DES_EDE_CBC_SHA = 0x001Bu,
TLS_KRB5_WITH_DES_CBC_SHA = 0x001Eu,
TLS_KRB5_WITH_3DES_EDE_CBC_SHA = 0x001Fu,
TLS_KRB5_WITH_RC4_128_SHA = 0x0020u,
TLS_KRB5_WITH_IDEA_CBC_SHA = 0x0021u,
TLS_KRB5_WITH_DES_CBC_MD5 = 0x0022u,
TLS_KRB5_WITH_3DES_EDE_CBC_MD5 = 0x0023u,
TLS_KRB5_WITH_RC4_128_MD5 = 0x0024u,
TLS_KRB5_WITH_IDEA_CBC_MD5 = 0x0025u,
TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA = 0x0026u,
TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA = 0x0027u,
TLS_KRB5_EXPORT_WITH_RC4_40_SHA = 0x0028u,
TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5 = 0x0029u,
TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5 = 0x002Au,
TLS_KRB5_EXPORT_WITH_RC4_40_MD5 = 0x002Bu,
TLS_PSK_WITH_NULL_SHA = 0x002Cu,
TLS_DHE_PSK_WITH_NULL_SHA = 0x002Du,
TLS_RSA_PSK_WITH_NULL_SHA = 0x002Eu,
TLS_RSA_WITH_AES_128_CBC_SHA = 0x002Fu,
TLS_DH_DSS_WITH_AES_128_CBC_SHA = 0x0030u,
TLS_DH_RSA_WITH_AES_128_CBC_SHA = 0x0031u,
TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 0x0032u,
TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0x0033u,
TLS_DH_anon_WITH_AES_128_CBC_SHA = 0x0034u,
TLS_RSA_WITH_AES_256_CBC_SHA = 0x0035u,
TLS_DH_DSS_WITH_AES_256_CBC_SHA = 0x0036u,
TLS_DH_RSA_WITH_AES_256_CBC_SHA = 0x0037u,
TLS_DHE_DSS_WITH_AES_256_CBC_SHA = 0x0038u,
TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0x0039u,
TLS_DH_anon_WITH_AES_256_CBC_SHA = 0x003Au,
TLS_RSA_WITH_NULL_SHA256 = 0x003Bu,
TLS_RSA_WITH_AES_128_CBC_SHA256 = 0x003Cu,
TLS_RSA_WITH_AES_256_CBC_SHA256 = 0x003Du,
TLS_DH_DSS_WITH_AES_128_CBC_SHA256 = 0x003Eu,
TLS_DH_RSA_WITH_AES_128_CBC_SHA256 = 0x003Fu,
TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = 0x0040u,
TLS_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0041u,
TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA = 0x0042u,
TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0043u,
TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA = 0x0044u,
TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0045u,
TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA = 0x0046u,
TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x0067u,
TLS_DH_DSS_WITH_AES_256_CBC_SHA256 = 0x0068u,
TLS_DH_RSA_WITH_AES_256_CBC_SHA256 = 0x0069u,
TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 = 0x006Au,
TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x006Bu,
TLS_DH_anon_WITH_AES_128_CBC_SHA256 = 0x006Cu,
TLS_DH_anon_WITH_AES_256_CBC_SHA256 = 0x006Du,
TLS_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0084u,
TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA = 0x0085u,
TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0086u,
TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA = 0x0087u,
TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0088u,
TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA = 0x0089u,
TLS_PSK_WITH_RC4_128_SHA = 0x008Au,
TLS_PSK_WITH_3DES_EDE_CBC_SHA = 0x008Bu,
TLS_PSK_WITH_AES_128_CBC_SHA = 0x008Cu,
TLS_PSK_WITH_AES_256_CBC_SHA = 0x008Du,
TLS_DHE_PSK_WITH_RC4_128_SHA = 0x008Eu,
TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA = 0x008Fu,
TLS_DHE_PSK_WITH_AES_128_CBC_SHA = 0x0090u,
TLS_DHE_PSK_WITH_AES_256_CBC_SHA = 0x0091u,
TLS_RSA_PSK_WITH_RC4_128_SHA = 0x0092u,
TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA = 0x0093u,
TLS_RSA_PSK_WITH_AES_128_CBC_SHA = 0x0094u,
TLS_RSA_PSK_WITH_AES_256_CBC_SHA = 0x0095u,
TLS_RSA_WITH_SEED_CBC_SHA = 0x0096u,
TLS_DH_DSS_WITH_SEED_CBC_SHA = 0x0097u,
TLS_DH_RSA_WITH_SEED_CBC_SHA = 0x0098u,
TLS_DHE_DSS_WITH_SEED_CBC_SHA = 0x0099u,
TLS_DHE_RSA_WITH_SEED_CBC_SHA = 0x009Au,
TLS_DH_anon_WITH_SEED_CBC_SHA = 0x009Bu,
TLS_RSA_WITH_AES_128_GCM_SHA256 = 0x009Cu,
TLS_RSA_WITH_AES_256_GCM_SHA384 = 0x009Du,
TLS_DH_RSA_WITH_AES_128_GCM_SHA256 = 0x00A0u,
TLS_DH_RSA_WITH_AES_256_GCM_SHA384 = 0x00A1u,
TLS_DH_DSS_WITH_AES_128_GCM_SHA256 = 0x00A4u,
TLS_DH_DSS_WITH_AES_256_GCM_SHA384 = 0x00A5u,
TLS_DH_anon_WITH_AES_128_GCM_SHA256 = 0x00A6u,
TLS_DH_anon_WITH_AES_256_GCM_SHA384 = 0x00A7u,
TLS_PSK_WITH_AES_128_GCM_SHA256 = 0x00A8u,
TLS_PSK_WITH_AES_256_GCM_SHA384 = 0x00A9u,
TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 = 0x00ACu,
TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 = 0x00ADu,
TLS_PSK_WITH_AES_128_CBC_SHA256 = 0x00AEu,
TLS_PSK_WITH_AES_256_CBC_SHA384 = 0x00AFu,
TLS_PSK_WITH_NULL_SHA256 = 0x00B0u,
TLS_PSK_WITH_NULL_SHA384 = 0x00B1u,
TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 = 0x00B2u,
TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 = 0x00B3u,
TLS_DHE_PSK_WITH_NULL_SHA256 = 0x00B4u,
TLS_DHE_PSK_WITH_NULL_SHA384 = 0x00B5u,
TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 = 0x00B6u,
TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 = 0x00B7u,
TLS_RSA_PSK_WITH_NULL_SHA256 = 0x00B8u,
TLS_RSA_PSK_WITH_NULL_SHA384 = 0x00B9u,
TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BAu,
TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BBu,
TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BCu,
TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BDu,
TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BEu,
TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BFu,
TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C0u,
TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C1u,
TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C2u,
TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C3u,
TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C4u,
TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C5u,
TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FFu,
TLS_ECDH_ECDSA_WITH_NULL_SHA = 0xC001u,
TLS_ECDH_ECDSA_WITH_RC4_128_SHA = 0xC002u,
TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC003u,
TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA = 0xC004u,
TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA = 0xC005u,
TLS_ECDHE_ECDSA_WITH_NULL_SHA = 0xC006u,
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA = 0xC007u,
TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC008u,
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0xC009u,
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 0xC00Au,
TLS_ECDH_RSA_WITH_NULL_SHA = 0xC00Bu,
TLS_ECDH_RSA_WITH_RC4_128_SHA = 0xC00Cu,
TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA = 0xC00Du,
TLS_ECDH_RSA_WITH_AES_128_CBC_SHA = 0xC00Eu,
TLS_ECDH_RSA_WITH_AES_256_CBC_SHA = 0xC00Fu,
TLS_ECDHE_RSA_WITH_NULL_SHA = 0xC010u,
TLS_ECDHE_RSA_WITH_RC4_128_SHA = 0xC011u,
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA = 0xC012u,
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013u,
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014u,
TLS_ECDH_anon_WITH_NULL_SHA = 0xC015u,
TLS_ECDH_anon_WITH_RC4_128_SHA = 0xC016u,
TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA = 0xC017u,
TLS_ECDH_anon_WITH_AES_128_CBC_SHA = 0xC018u,
TLS_ECDH_anon_WITH_AES_256_CBC_SHA = 0xC019u,
TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA = 0xC01Au,
TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA = 0xC01Bu,
TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA = 0xC01Cu,
TLS_SRP_SHA_WITH_AES_128_CBC_SHA = 0xC01Du,
TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA = 0xC01Eu,
TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA = 0xC01Fu,
TLS_SRP_SHA_WITH_AES_256_CBC_SHA = 0xC020u,
TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA = 0xC021u,
TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA = 0xC022u,
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC023u,
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC024u,
TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC025u,
TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC026u,
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027u,
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 = 0xC028u,
TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 = 0xC029u,
TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 = 0xC02Au,
TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02Du,
TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02Eu,
TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 = 0xC031u,
TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 = 0xC032u,
TLS_ECDHE_PSK_WITH_RC4_128_SHA = 0xC033u,
TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA = 0xC034u,
TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA = 0xC035u,
TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA = 0xC036u,
TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 = 0xC037u,
TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 = 0xC038u,
TLS_ECDHE_PSK_WITH_NULL_SHA = 0xC039u,
TLS_ECDHE_PSK_WITH_NULL_SHA256 = 0xC03Au,
TLS_ECDHE_PSK_WITH_NULL_SHA384 = 0xC03Bu,
TLS_RSA_WITH_ARIA_128_CBC_SHA256 = 0xC03Cu,
TLS_RSA_WITH_ARIA_256_CBC_SHA384 = 0xC03Du,
TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256 = 0xC03Eu,
TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384 = 0xC03Fu,
TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256 = 0xC040u,
TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384 = 0xC041u,
TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256 = 0xC042u,
TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384 = 0xC043u,
TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256 = 0xC044u,
TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384 = 0xC045u,
TLS_DH_anon_WITH_ARIA_128_CBC_SHA256 = 0xC046u,
TLS_DH_anon_WITH_ARIA_256_CBC_SHA384 = 0xC047u,
TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256 = 0xC048u,
TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384 = 0xC049u,
TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256 = 0xC04Au,
TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384 = 0xC04Bu,
TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256 = 0xC04Cu,
TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384 = 0xC04Du,
TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256 = 0xC04Eu,
TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384 = 0xC04Fu,
TLS_RSA_WITH_ARIA_128_GCM_SHA256 = 0xC050u,
TLS_RSA_WITH_ARIA_256_GCM_SHA384 = 0xC051u,
TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256 = 0xC054u,
TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384 = 0xC055u,
TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256 = 0xC058u,
TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384 = 0xC059u,
TLS_DH_anon_WITH_ARIA_128_GCM_SHA256 = 0xC05Au,
TLS_DH_anon_WITH_ARIA_256_GCM_SHA384 = 0xC05Bu,
TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256 = 0xC05Eu,
TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384 = 0xC05Fu,
TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256 = 0xC062u,
TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384 = 0xC063u,
TLS_PSK_WITH_ARIA_128_CBC_SHA256 = 0xC064u,
TLS_PSK_WITH_ARIA_256_CBC_SHA384 = 0xC065u,
TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256 = 0xC066u,
TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384 = 0xC067u,
TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256 = 0xC068u,
TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384 = 0xC069u,
TLS_PSK_WITH_ARIA_128_GCM_SHA256 = 0xC06Au,
TLS_PSK_WITH_ARIA_256_GCM_SHA384 = 0xC06Bu,
TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256 = 0xC06Eu,
TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384 = 0xC06Fu,
TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256 = 0xC070u,
TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384 = 0xC071u,
TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 = 0xC072u,
TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 = 0xC073u,
TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 = 0xC074u,
TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 = 0xC075u,
TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0xC076u,
TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 = 0xC077u,
TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0xC078u,
TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384 = 0xC079u,
TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC07Au,
TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC07Bu,
TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC07Eu,
TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC07Fu,
TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256 = 0xC082u,
TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384 = 0xC083u,
TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256 = 0xC084u,
TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384 = 0xC085u,
TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC088u,
TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC089u,
TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC08Cu,
TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC08Du,
TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256 = 0xC08Eu,
TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384 = 0xC08Fu,
TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256 = 0xC092u,
TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384 = 0xC093u,
TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256 = 0xC094u,
TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384 = 0xC095u,
TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 = 0xC096u,
TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 = 0xC097u,
TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256 = 0xC098u,
TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384 = 0xC099u,
TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 = 0xC09Au,
TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 = 0xC09Bu,
TLS_RSA_WITH_AES_128_CCM = 0xC09Cu,
TLS_RSA_WITH_AES_256_CCM = 0xC09Du,
TLS_RSA_WITH_AES_128_CCM_8 = 0xC0A0u,
TLS_RSA_WITH_AES_256_CCM_8 = 0xC0A1u,
TLS_PSK_WITH_AES_128_CCM = 0xC0A4u,
TLS_PSK_WITH_AES_256_CCM = 0xC0A5u,
TLS_PSK_WITH_AES_128_CCM_8 = 0xC0A8u,
TLS_PSK_WITH_AES_256_CCM_8 = 0xC0A9u,
};
bool check_http2_cipher_black_list(SSL *ssl) { bool check_http2_cipher_black_list(SSL *ssl) {
auto cipher = SSL_get_current_cipher(ssl); int id = SSL_CIPHER_get_id(SSL_get_current_cipher(ssl)) & 0xFFFFFF;
// Cipher suites in RFC 7540 black list are not allowed in HTTP/2. return IS_CIPHER_BANNED_METHOD2(id);
switch (SSL_CIPHER_get_id(cipher) & 0xffffu) {
case TLS_NULL_WITH_NULL_NULL:
case TLS_RSA_WITH_NULL_MD5:
case TLS_RSA_WITH_NULL_SHA:
case TLS_RSA_EXPORT_WITH_RC4_40_MD5:
case TLS_RSA_WITH_RC4_128_MD5:
case TLS_RSA_WITH_RC4_128_SHA:
case TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5:
case TLS_RSA_WITH_IDEA_CBC_SHA:
case TLS_RSA_EXPORT_WITH_DES40_CBC_SHA:
case TLS_RSA_WITH_DES_CBC_SHA:
case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
case TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA:
case TLS_DH_DSS_WITH_DES_CBC_SHA:
case TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
case TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA:
case TLS_DH_RSA_WITH_DES_CBC_SHA:
case TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA:
case TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA:
case TLS_DHE_DSS_WITH_DES_CBC_SHA:
case TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
case TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA:
case TLS_DHE_RSA_WITH_DES_CBC_SHA:
case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
case TLS_DH_anon_EXPORT_WITH_RC4_40_MD5:
case TLS_DH_anon_WITH_RC4_128_MD5:
case TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA:
case TLS_DH_anon_WITH_DES_CBC_SHA:
case TLS_DH_anon_WITH_3DES_EDE_CBC_SHA:
case TLS_KRB5_WITH_DES_CBC_SHA:
case TLS_KRB5_WITH_3DES_EDE_CBC_SHA:
case TLS_KRB5_WITH_RC4_128_SHA:
case TLS_KRB5_WITH_IDEA_CBC_SHA:
case TLS_KRB5_WITH_DES_CBC_MD5:
case TLS_KRB5_WITH_3DES_EDE_CBC_MD5:
case TLS_KRB5_WITH_RC4_128_MD5:
case TLS_KRB5_WITH_IDEA_CBC_MD5:
case TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA:
case TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA:
case TLS_KRB5_EXPORT_WITH_RC4_40_SHA:
case TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5:
case TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5:
case TLS_KRB5_EXPORT_WITH_RC4_40_MD5:
case TLS_PSK_WITH_NULL_SHA:
case TLS_DHE_PSK_WITH_NULL_SHA:
case TLS_RSA_PSK_WITH_NULL_SHA:
case TLS_RSA_WITH_AES_128_CBC_SHA:
case TLS_DH_DSS_WITH_AES_128_CBC_SHA:
case TLS_DH_RSA_WITH_AES_128_CBC_SHA:
case TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
case TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
case TLS_DH_anon_WITH_AES_128_CBC_SHA:
case TLS_RSA_WITH_AES_256_CBC_SHA:
case TLS_DH_DSS_WITH_AES_256_CBC_SHA:
case TLS_DH_RSA_WITH_AES_256_CBC_SHA:
case TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
case TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
case TLS_DH_anon_WITH_AES_256_CBC_SHA:
case TLS_RSA_WITH_NULL_SHA256:
case TLS_RSA_WITH_AES_128_CBC_SHA256:
case TLS_RSA_WITH_AES_256_CBC_SHA256:
case TLS_DH_DSS_WITH_AES_128_CBC_SHA256:
case TLS_DH_RSA_WITH_AES_128_CBC_SHA256:
case TLS_DHE_DSS_WITH_AES_128_CBC_SHA256:
case TLS_RSA_WITH_CAMELLIA_128_CBC_SHA:
case TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA:
case TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA:
case TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA:
case TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA:
case TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA:
case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
case TLS_DH_DSS_WITH_AES_256_CBC_SHA256:
case TLS_DH_RSA_WITH_AES_256_CBC_SHA256:
case TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:
case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
case TLS_DH_anon_WITH_AES_128_CBC_SHA256:
case TLS_DH_anon_WITH_AES_256_CBC_SHA256:
case TLS_RSA_WITH_CAMELLIA_256_CBC_SHA:
case TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA:
case TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA:
case TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA:
case TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA:
case TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA:
case TLS_PSK_WITH_RC4_128_SHA:
case TLS_PSK_WITH_3DES_EDE_CBC_SHA:
case TLS_PSK_WITH_AES_128_CBC_SHA:
case TLS_PSK_WITH_AES_256_CBC_SHA:
case TLS_DHE_PSK_WITH_RC4_128_SHA:
case TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
case TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
case TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
case TLS_RSA_PSK_WITH_RC4_128_SHA:
case TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
case TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
case TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
case TLS_RSA_WITH_SEED_CBC_SHA:
case TLS_DH_DSS_WITH_SEED_CBC_SHA:
case TLS_DH_RSA_WITH_SEED_CBC_SHA:
case TLS_DHE_DSS_WITH_SEED_CBC_SHA:
case TLS_DHE_RSA_WITH_SEED_CBC_SHA:
case TLS_DH_anon_WITH_SEED_CBC_SHA:
case TLS_RSA_WITH_AES_128_GCM_SHA256:
case TLS_RSA_WITH_AES_256_GCM_SHA384:
case TLS_DH_RSA_WITH_AES_128_GCM_SHA256:
case TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
case TLS_DH_DSS_WITH_AES_128_GCM_SHA256:
case TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
case TLS_DH_anon_WITH_AES_128_GCM_SHA256:
case TLS_DH_anon_WITH_AES_256_GCM_SHA384:
case TLS_PSK_WITH_AES_128_GCM_SHA256:
case TLS_PSK_WITH_AES_256_GCM_SHA384:
case TLS_RSA_PSK_WITH_AES_128_GCM_SHA256:
case TLS_RSA_PSK_WITH_AES_256_GCM_SHA384:
case TLS_PSK_WITH_AES_128_CBC_SHA256:
case TLS_PSK_WITH_AES_256_CBC_SHA384:
case TLS_PSK_WITH_NULL_SHA256:
case TLS_PSK_WITH_NULL_SHA384:
case TLS_DHE_PSK_WITH_AES_128_CBC_SHA256:
case TLS_DHE_PSK_WITH_AES_256_CBC_SHA384:
case TLS_DHE_PSK_WITH_NULL_SHA256:
case TLS_DHE_PSK_WITH_NULL_SHA384:
case TLS_RSA_PSK_WITH_AES_128_CBC_SHA256:
case TLS_RSA_PSK_WITH_AES_256_CBC_SHA384:
case TLS_RSA_PSK_WITH_NULL_SHA256:
case TLS_RSA_PSK_WITH_NULL_SHA384:
case TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256:
case TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256:
case TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
case TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256:
case TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
case TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256:
case TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256:
case TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256:
case TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256:
case TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256:
case TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256:
case TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256:
case TLS_EMPTY_RENEGOTIATION_INFO_SCSV:
case TLS_ECDH_ECDSA_WITH_NULL_SHA:
case TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
case TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
case TLS_ECDHE_ECDSA_WITH_NULL_SHA:
case TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
case TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
case TLS_ECDH_RSA_WITH_NULL_SHA:
case TLS_ECDH_RSA_WITH_RC4_128_SHA:
case TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
case TLS_ECDHE_RSA_WITH_NULL_SHA:
case TLS_ECDHE_RSA_WITH_RC4_128_SHA:
case TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
case TLS_ECDH_anon_WITH_NULL_SHA:
case TLS_ECDH_anon_WITH_RC4_128_SHA:
case TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA:
case TLS_ECDH_anon_WITH_AES_128_CBC_SHA:
case TLS_ECDH_anon_WITH_AES_256_CBC_SHA:
case TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA:
case TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA:
case TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA:
case TLS_SRP_SHA_WITH_AES_128_CBC_SHA:
case TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA:
case TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA:
case TLS_SRP_SHA_WITH_AES_256_CBC_SHA:
case TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA:
case TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA:
case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
case TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
case TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
case TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
case TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
case TLS_ECDHE_PSK_WITH_RC4_128_SHA:
case TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA:
case TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA:
case TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA:
case TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256:
case TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384:
case TLS_ECDHE_PSK_WITH_NULL_SHA:
case TLS_ECDHE_PSK_WITH_NULL_SHA256:
case TLS_ECDHE_PSK_WITH_NULL_SHA384:
case TLS_RSA_WITH_ARIA_128_CBC_SHA256:
case TLS_RSA_WITH_ARIA_256_CBC_SHA384:
case TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256:
case TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384:
case TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256:
case TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384:
case TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256:
case TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384:
case TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256:
case TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384:
case TLS_DH_anon_WITH_ARIA_128_CBC_SHA256:
case TLS_DH_anon_WITH_ARIA_256_CBC_SHA384:
case TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256:
case TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384:
case TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256:
case TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384:
case TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256:
case TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384:
case TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256:
case TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384:
case TLS_RSA_WITH_ARIA_128_GCM_SHA256:
case TLS_RSA_WITH_ARIA_256_GCM_SHA384:
case TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256:
case TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384:
case TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256:
case TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384:
case TLS_DH_anon_WITH_ARIA_128_GCM_SHA256:
case TLS_DH_anon_WITH_ARIA_256_GCM_SHA384:
case TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256:
case TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384:
case TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256:
case TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384:
case TLS_PSK_WITH_ARIA_128_CBC_SHA256:
case TLS_PSK_WITH_ARIA_256_CBC_SHA384:
case TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256:
case TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384:
case TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256:
case TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384:
case TLS_PSK_WITH_ARIA_128_GCM_SHA256:
case TLS_PSK_WITH_ARIA_256_GCM_SHA384:
case TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256:
case TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384:
case TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256:
case TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384:
case TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
case TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
case TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
case TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
case TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
case TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384:
case TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
case TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384:
case TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256:
case TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384:
case TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
case TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
case TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256:
case TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384:
case TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256:
case TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384:
case TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
case TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
case TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
case TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
case TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256:
case TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384:
case TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256:
case TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384:
case TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256:
case TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384:
case TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256:
case TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
case TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256:
case TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384:
case TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256:
case TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
case TLS_RSA_WITH_AES_128_CCM:
case TLS_RSA_WITH_AES_256_CCM:
case TLS_RSA_WITH_AES_128_CCM_8:
case TLS_RSA_WITH_AES_256_CCM_8:
case TLS_PSK_WITH_AES_128_CCM:
case TLS_PSK_WITH_AES_256_CCM:
case TLS_PSK_WITH_AES_128_CCM_8:
case TLS_PSK_WITH_AES_256_CCM_8:
return true;
}
return false;
} }
bool check_http2_tls_version(SSL *ssl) { bool check_http2_tls_version(SSL *ssl) {

View File

@ -387,18 +387,31 @@ public:
using const_pointer = const value_type *; using const_pointer = const value_type *;
using const_iterator = const_pointer; using const_iterator = const_pointer;
StringRef() : base(""), len(0) {} constexpr StringRef() : base(""), len(0) {}
explicit StringRef(const std::string &s) : base(s.c_str()), len(s.size()) {} explicit StringRef(const std::string &s) : base(s.c_str()), len(s.size()) {}
explicit StringRef(const ImmutableString &s) explicit StringRef(const ImmutableString &s)
: base(s.c_str()), len(s.size()) {} : base(s.c_str()), len(s.size()) {}
StringRef(const char *s) : base(s), len(strlen(s)) {} StringRef(const char *s) : base(s), len(strlen(s)) {}
StringRef(const char *s, size_t n) : base(s), len(n) {} template <typename CharT>
constexpr StringRef(const CharT *s, size_t n)
: base(reinterpret_cast<const char *>(s)), len(n) {}
template <typename InputIt> template <typename InputIt>
StringRef(InputIt first, InputIt last) StringRef(InputIt first, InputIt last)
: base(&*first), len(std::distance(first, last)) {}
template <typename InputIt>
StringRef(InputIt *first, InputIt *last)
: base(first), len(std::distance(first, last)) {} : base(first), len(std::distance(first, last)) {}
template <size_t N> static StringRef from_lit(const char(&s)[N]) { template <typename CharT, size_t N>
constexpr static StringRef from_lit(const CharT(&s)[N]) {
return StringRef(s, N - 1); return StringRef(s, N - 1);
} }
static StringRef from_maybe_nullptr(const char *s) {
if (s == nullptr) {
return StringRef();
}
return StringRef(s);
}
const_iterator begin() const { return base; }; const_iterator begin() const { return base; };
const_iterator cbegin() const { return base; }; const_iterator cbegin() const { return base; };
@ -412,6 +425,9 @@ public:
const_reference operator[](size_type pos) const { return *(base + pos); } const_reference operator[](size_type pos) const { return *(base + pos); }
std::string str() const { return std::string(base, len); } std::string str() const { return std::string(base, len); }
const uint8_t *byte() const {
return reinterpret_cast<const uint8_t *>(base);
}
private: private:
const char *base; const char *base;

View File

@ -57,7 +57,6 @@
#include <nghttp2/nghttp2.h> #include <nghttp2/nghttp2.h>
#include "timegm.h" #include "timegm.h"
#include "template.h"
namespace nghttp2 { namespace nghttp2 {
@ -651,6 +650,43 @@ std::string numeric_name(const struct sockaddr *sa, socklen_t salen) {
return host.data(); return host.data();
} }
std::string to_numeric_addr(const Address *addr) {
auto family = addr->su.storage.ss_family;
if (family == AF_UNIX) {
return addr->su.un.sun_path;
}
std::array<char, NI_MAXHOST> host;
std::array<char, NI_MAXSERV> serv;
auto rv =
getnameinfo(&addr->su.sa, addr->len, host.data(), host.size(),
serv.data(), serv.size(), NI_NUMERICHOST | NI_NUMERICSERV);
if (rv != 0) {
return "unknown";
}
auto hostlen = strlen(host.data());
auto servlen = strlen(serv.data());
std::string s;
char *p;
if (family == AF_INET6) {
s.resize(hostlen + servlen + 2 + 1);
p = &s[0];
*p++ = '[';
p = std::copy_n(host.data(), hostlen, p);
*p++ = ']';
} else {
s.resize(hostlen + servlen + 1);
p = &s[0];
p = std::copy_n(host.data(), hostlen, p);
}
*p++ = ':';
std::copy_n(serv.data(), servlen, p);
return s;
}
static int STDERR_COPY = -1; static int STDERR_COPY = -1;
static int STDOUT_COPY = -1; static int STDOUT_COPY = -1;
@ -1114,25 +1150,53 @@ std::string dtos(double n) {
return utos(static_cast<int64_t>(n)) + "." + (f.size() == 1 ? "0" : "") + f; return utos(static_cast<int64_t>(n)) + "." + (f.size() == 1 ? "0" : "") + f;
} }
std::string make_hostport(const char *host, uint16_t port) { std::string make_http_hostport(const StringRef &host, uint16_t port) {
auto ipv6 = ipv6_numeric_addr(host);
std::string hostport;
if (ipv6) {
hostport += '[';
}
hostport += host;
if (ipv6) {
hostport += ']';
}
if (port != 80 && port != 443) { if (port != 80 && port != 443) {
hostport += ':'; return make_hostport(host, port);
hostport += utos(port);
} }
auto ipv6 = ipv6_numeric_addr(host.c_str());
std::string hostport;
hostport.resize(host.size() + (ipv6 ? 2 : 0));
auto p = &hostport[0];
if (ipv6) {
*p++ = '[';
}
p = std::copy_n(host.c_str(), host.size(), p);
if (ipv6) {
*p++ = ']';
}
return hostport;
}
std::string make_hostport(const StringRef &host, uint16_t port) {
auto ipv6 = ipv6_numeric_addr(host.c_str());
auto serv = utos(port);
std::string hostport;
hostport.resize(host.size() + (ipv6 ? 2 : 0) + 1 + serv.size());
auto p = &hostport[0];
if (ipv6) {
*p++ = '[';
}
p = std::copy_n(host.c_str(), host.size(), p);
if (ipv6) {
*p++ = ']';
}
*p++ = ':';
std::copy_n(serv.c_str(), serv.size(), p);
return hostport; return hostport;
} }

View File

@ -49,6 +49,9 @@
#include "http-parser/http_parser.h" #include "http-parser/http_parser.h"
#include "template.h"
#include "network.h"
namespace nghttp2 { namespace nghttp2 {
// The additional HTTP/2 protocol ALPN protocol identifier we also // The additional HTTP/2 protocol ALPN protocol identifier we also
@ -459,6 +462,12 @@ bool numeric_host(const char *hostname, int family);
// failed, "unknown" is returned. // failed, "unknown" is returned.
std::string numeric_name(const struct sockaddr *sa, socklen_t salen); std::string numeric_name(const struct sockaddr *sa, socklen_t salen);
// Returns string representation of numeric address and port of
// |addr|. If address family is AF_UNIX, this return path to UNIX
// domain socket. Otherwise, the format is like <HOST>:<PORT>. For
// IPv6 address, address is enclosed by square brackets ([]).
std::string to_numeric_addr(const Address *addr);
// Makes internal copy of stderr (and possibly stdout in the future), // Makes internal copy of stderr (and possibly stdout in the future),
// which is then used as pointer to /dev/stderr or /proc/self/fd/2 // which is then used as pointer to /dev/stderr or /proc/self/fd/2
void store_original_fds(); void store_original_fds();
@ -618,7 +627,11 @@ std::string format_duration(double t);
// Creates "host:port" string using given |host| and |port|. If // Creates "host:port" string using given |host| and |port|. If
// |host| is numeric IPv6 address (e.g., ::1), it is enclosed by "[" // |host| is numeric IPv6 address (e.g., ::1), it is enclosed by "["
// and "]". If |port| is 80 or 443, port part is omitted. // and "]". If |port| is 80 or 443, port part is omitted.
std::string make_hostport(const char *host, uint16_t port); std::string make_http_hostport(const StringRef &host, uint16_t port);
// Just like make_http_hostport(), but doesn't treat 80 and 443
// specially.
std::string make_hostport(const StringRef &host, uint16_t port);
// Dumps |src| of length |len| in the format similar to `hexdump -C`. // Dumps |src| of length |len| in the format similar to `hexdump -C`.
void hexdump(FILE *out, const uint8_t *src, size_t len); void hexdump(FILE *out, const uint8_t *src, size_t len);

View File

@ -455,4 +455,15 @@ void test_util_parse_config_str_list(void) {
CU_ASSERT("charlie" == res[2]); CU_ASSERT("charlie" == res[2]);
} }
void test_util_make_http_hostport(void) {
CU_ASSERT("localhost" == util::make_http_hostport("localhost", 80));
CU_ASSERT("[::1]" == util::make_http_hostport("::1", 443));
CU_ASSERT("localhost:3000" == util::make_http_hostport("localhost", 3000));
}
void test_util_make_hostport(void) {
CU_ASSERT("localhost:80" == util::make_hostport("localhost", 80));
CU_ASSERT("[::1]:443" == util::make_hostport("::1", 443));
}
} // namespace shrpx } // namespace shrpx

View File

@ -57,6 +57,8 @@ void test_util_parse_http_date(void);
void test_util_localtime_date(void); void test_util_localtime_date(void);
void test_util_get_uint64(void); void test_util_get_uint64(void);
void test_util_parse_config_str_list(void); void test_util_parse_config_str_list(void);
void test_util_make_http_hostport(void);
void test_util_make_hostport(void);
} // namespace shrpx } // namespace shrpx

View File

@ -36,10 +36,6 @@
#include "nghttp2_test_helper.h" #include "nghttp2_test_helper.h"
#include "nghttp2_priority_spec.h" #include "nghttp2_priority_spec.h"
#define OB_CTRL(ITEM) nghttp2_outbound_item_get_ctrl_frame(ITEM)
#define OB_CTRL_TYPE(ITEM) nghttp2_outbound_item_get_ctrl_frame_type(ITEM)
#define OB_DATA(ITEM) nghttp2_outbound_item_get_data_frame(ITEM)
typedef struct { typedef struct {
uint8_t buf[65535]; uint8_t buf[65535];
size_t length; size_t length;

View File

@ -55,7 +55,9 @@ mruby:
all-local: mruby all-local: mruby
clean-local: clean-local:
-rm -rf "${abs_builddir}/mruby/build" MRUBY_CONFIG="${srcdir}/build_config.rb" \
BUILD_DIR="${abs_builddir}/mruby/build" \
"${srcdir}/mruby/minirake" -f "${srcdir}/mruby/Rakefile" clean
endif # HAVE_MRUBY endif # HAVE_MRUBY