mirror of
https://github.com/nghttp2/nghttp2.git
synced 2025-12-07 18:48:54 +08:00
Compare commits
179 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d1f9f9a3aa | ||
|
|
2415a22757 | ||
|
|
887d4d2a41 | ||
|
|
1c8e625045 | ||
|
|
36e931e0d7 | ||
|
|
31d4077638 | ||
|
|
c098b4ac70 | ||
|
|
32446a5197 | ||
|
|
40c1b29f36 | ||
|
|
bc933e9981 | ||
|
|
ba34e911e1 | ||
|
|
8aac5d6af2 | ||
|
|
56bdfd1df2 | ||
|
|
dbffb8995b | ||
|
|
ebfae904ab | ||
|
|
827abb57e9 | ||
|
|
9aee43f7d8 | ||
|
|
34bf153653 | ||
|
|
2782ef67de | ||
|
|
9d15f9b00d | ||
|
|
3e72711e23 | ||
|
|
f4bb8776d0 | ||
|
|
95ffb4565f | ||
|
|
f3a415f623 | ||
|
|
936d4aca1a | ||
|
|
216ae0a328 | ||
|
|
9672bc322f | ||
|
|
b68be1e1fb | ||
|
|
f2a7275700 | ||
|
|
177d0a513f | ||
|
|
dfc02843b6 | ||
|
|
11c8803b92 | ||
|
|
c9a4f293a1 | ||
|
|
61579ad20f | ||
|
|
9678daa46a | ||
|
|
23ecfd412d | ||
|
|
3ff148811b | ||
|
|
6f1347fc8b | ||
|
|
1703201084 | ||
|
|
7921029e33 | ||
|
|
b7159f80b2 | ||
|
|
f0b5a8db8c | ||
|
|
094168a58f | ||
|
|
63e43bff99 | ||
|
|
02b7116d42 | ||
|
|
aa3373a107 | ||
|
|
7aabc6b125 | ||
|
|
466e4b7a1e | ||
|
|
76a425226f | ||
|
|
2b707bff27 | ||
|
|
9055323b67 | ||
|
|
67804cfc8c | ||
|
|
2344932b45 | ||
|
|
35ebdd35bc | ||
|
|
ac81003669 | ||
|
|
c999987baf | ||
|
|
529a59d309 | ||
|
|
52f6417813 | ||
|
|
660bc389e6 | ||
|
|
bfc26e8299 | ||
|
|
47106c0756 | ||
|
|
49fa914db5 | ||
|
|
93eabc642b | ||
|
|
2d273f8237 | ||
|
|
a53f0f0a17 | ||
|
|
1bd98dcf4f | ||
|
|
eebed206c9 | ||
|
|
fe74600a5f | ||
|
|
08e2d7cdb3 | ||
|
|
2593036053 | ||
|
|
a6effb4d23 | ||
|
|
17215002a1 | ||
|
|
0e469ed221 | ||
|
|
093eb51f8c | ||
|
|
5f1866fd6b | ||
|
|
d8c8a4631d | ||
|
|
6b12f17f44 | ||
|
|
eb0c82d91f | ||
|
|
7adfa5dea7 | ||
|
|
17758126fa | ||
|
|
b440f585bc | ||
|
|
63a13ccb18 | ||
|
|
72877379ec | ||
|
|
8449958425 | ||
|
|
9037641592 | ||
|
|
c0078ab45a | ||
|
|
3a41e4dd1a | ||
|
|
3297a303bf | ||
|
|
f1580f95d4 | ||
|
|
1e150bcf61 | ||
|
|
ca371e3ba9 | ||
|
|
61dda40b44 | ||
|
|
5ad753b90c | ||
|
|
0a1beea13a | ||
|
|
00e722f02c | ||
|
|
ff22862b9d | ||
|
|
b2264ad57e | ||
|
|
b0227d4051 | ||
|
|
28b643e531 | ||
|
|
82f942c3a3 | ||
|
|
e4a727f86c | ||
|
|
b624ca6dcd | ||
|
|
ba4c268172 | ||
|
|
00175eac33 | ||
|
|
396dde1347 | ||
|
|
042a59117d | ||
|
|
b8717208c7 | ||
|
|
c3a5fe7185 | ||
|
|
c8b6a79225 | ||
|
|
92e66fc167 | ||
|
|
64ffc1fc73 | ||
|
|
304ff6a6f9 | ||
|
|
fc39f2d9d2 | ||
|
|
0d806978e6 | ||
|
|
62c43ce2be | ||
|
|
88eaeb5d1c | ||
|
|
60c0c2dd56 | ||
|
|
6c147aa1c5 | ||
|
|
2a9b23bfab | ||
|
|
4fb4617d20 | ||
|
|
ede0f6aa32 | ||
|
|
4e7271a88f | ||
|
|
6d49110a33 | ||
|
|
b540aa34d0 | ||
|
|
15fa38c72f | ||
|
|
e7de5e9f6c | ||
|
|
5c10534b88 | ||
|
|
b3e5d49a3e | ||
|
|
6806196404 | ||
|
|
2e38208d74 | ||
|
|
cde79052dd | ||
|
|
e763770f3e | ||
|
|
26d49c1dc3 | ||
|
|
bb4e2f6a24 | ||
|
|
344cc1b5c3 | ||
|
|
21f9b6d8bb | ||
|
|
024d0d09ee | ||
|
|
e4b2847d31 | ||
|
|
ee07694783 | ||
|
|
8741503db1 | ||
|
|
eec409dba7 | ||
|
|
95ca4f55d5 | ||
|
|
231665d67b | ||
|
|
4c05558273 | ||
|
|
35c3b36549 | ||
|
|
03872bfacd | ||
|
|
5e9bcbec9a | ||
|
|
aa07fe7fa6 | ||
|
|
1d99b425ca | ||
|
|
7a6a59178a | ||
|
|
4e44fccdcf | ||
|
|
703c77ec89 | ||
|
|
a06af3fa85 | ||
|
|
686a303cb5 | ||
|
|
b95df43384 | ||
|
|
5659e295b3 | ||
|
|
5b195092e1 | ||
|
|
85bb37ab7c | ||
|
|
c5f3eee3be | ||
|
|
1cfdf386ff | ||
|
|
6d8fe72174 | ||
|
|
98253b1d0d | ||
|
|
ad93cea544 | ||
|
|
1739b5a0e6 | ||
|
|
9ef8e24049 | ||
|
|
7fbbaf01a9 | ||
|
|
5a6f312182 | ||
|
|
2b441ef9af | ||
|
|
e14da859b6 | ||
|
|
0248d979fe | ||
|
|
0caefe20ef | ||
|
|
9c84f60ba0 | ||
|
|
83cc2511e3 | ||
|
|
eb4e402aae | ||
|
|
5d611d2e24 | ||
|
|
837e716306 | ||
|
|
061a557839 | ||
|
|
d9893d014c | ||
|
|
3785cf07ba |
@@ -17,7 +17,7 @@ BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
BinPackParameters: true
|
||||
ColumnLimit: 80
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
DerivePointerAlignment: false
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
IndentCaseLabels: false
|
||||
|
||||
81
AUTHORS
81
AUTHORS
@@ -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
|
||||
|
||||
1
COPYING
1
COPYING
@@ -1,6 +1,7 @@
|
||||
The MIT License
|
||||
|
||||
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
|
||||
a copy of this software and associated documentation files (the
|
||||
|
||||
43
README.rst
43
README.rst
@@ -58,9 +58,9 @@ To build the documentation, you need to install:
|
||||
|
||||
* sphinx (http://sphinx-doc.org/)
|
||||
|
||||
To build and run the application programs (``nghttp``, ``nghttpd`` and
|
||||
``nghttpx``) in the ``src`` directory, the following packages are
|
||||
required:
|
||||
To build and run the application programs (``nghttp``, ``nghttpd``,
|
||||
``nghttpx`` and ``h2load``) in the ``src`` directory, the following packages
|
||||
are required:
|
||||
|
||||
* OpenSSL >= 1.0.1
|
||||
* 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 \
|
||||
libjemalloc-dev cython python3-dev python-setuptools
|
||||
|
||||
spdylay is not packaged in Ubuntu, so you need to build it yourself:
|
||||
http://tatsuhiro-t.github.io/spdylay/
|
||||
From Ubuntu 15.10, spdylay has been available as a package named
|
||||
`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
|
||||
<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
|
||||
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)
|
||||
--------------------------------------------
|
||||
|
||||
@@ -639,7 +651,9 @@ push.
|
||||
<https://istlsfastyet.com/#server-performance>`_ in TLS, such as
|
||||
session IDs, session tickets (with automatic key rotation), OCSP
|
||||
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:
|
||||
|
||||
@@ -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,
|
||||
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
|
||||
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
|
||||
SSL/TLS in the backend connection by default. To disable SSL/TLS, use
|
||||
@@ -1462,3 +1478,16 @@ full real name when contributing!
|
||||
See `Contribution Guidelines
|
||||
<https://nghttp2.org/documentation/contribute.html>`_ for more
|
||||
details.
|
||||
|
||||
Release schedule
|
||||
----------------
|
||||
|
||||
In general, we follow `Semantic Versioning <http://semver.org/>`_. We
|
||||
release MINOR version update every month, and usually we ship it
|
||||
around 25th day of every month.
|
||||
|
||||
We may release PATCH releases between the regular releases, mainly for
|
||||
severe security bug fixes.
|
||||
|
||||
We have no plan to break API compatibility changes involving soname
|
||||
bump, so MAJOR version will stay 1 for the foreseeable future.
|
||||
|
||||
70
configure.ac
70
configure.ac
@@ -25,7 +25,7 @@ dnl Do not change user variables!
|
||||
dnl http://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html
|
||||
|
||||
AC_PREREQ(2.61)
|
||||
AC_INIT([nghttp2], [1.7.0], [t-tujikawa@users.sourceforge.net])
|
||||
AC_INIT([nghttp2], [1.8.0], [t-tujikawa@users.sourceforge.net])
|
||||
AC_CONFIG_AUX_DIR([.])
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
@@ -46,9 +46,9 @@ m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
|
||||
dnl See versioning rule:
|
||||
dnl http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
||||
AC_SUBST(LT_CURRENT, 18)
|
||||
AC_SUBST(LT_REVISION, 1)
|
||||
AC_SUBST(LT_AGE, 4)
|
||||
AC_SUBST(LT_CURRENT, 19)
|
||||
AC_SUBST(LT_REVISION, 0)
|
||||
AC_SUBST(LT_AGE, 5)
|
||||
|
||||
major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"`
|
||||
minor=`echo $PACKAGE_VERSION |cut -d. -f2 | sed -e "s/[^0-9]//g"`
|
||||
@@ -76,7 +76,7 @@ AC_ARG_ENABLE([threads],
|
||||
|
||||
AC_ARG_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])
|
||||
|
||||
AC_ARG_ENABLE([hpack-tools],
|
||||
@@ -185,14 +185,24 @@ 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([NGHTTP2_NORETURN], [__attribute__((noreturn))], [Hint to the compiler that a function never return])
|
||||
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])
|
||||
d])
|
||||
AC_DEFINE([_U_], , [Hint to the compiler that a function parameter is not used])
|
||||
AC_DEFINE([NGHTTP2_NORETURN], , [Hint to the compiler that a function never return])
|
||||
fi
|
||||
|
||||
save_CXXFLAGS="$CXXFLAGS"
|
||||
CXXFLAGS=
|
||||
|
||||
AX_CXX_COMPILE_STDCXX_11([noext], [optional])
|
||||
|
||||
CXX1XCXXFLAGS="$CXXFLAGS"
|
||||
CXXFLAGS="$save_CXXFLAGS"
|
||||
AC_SUBST([CXX1XCXXFLAGS])
|
||||
|
||||
AC_LANG_PUSH(C++)
|
||||
|
||||
save_CXXFLAGS="$CXXFLAGS"
|
||||
CXXFLAGS="$CXXFLAGS $CXX1XCXXFLAGS"
|
||||
|
||||
# Check that std::future is available.
|
||||
AC_MSG_CHECKING([whether std::future is available])
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
|
||||
@@ -226,6 +236,8 @@ std::map<int, int>().emplace(1, 2);
|
||||
[have_std_map_emplace=no
|
||||
AC_MSG_RESULT([no])])
|
||||
|
||||
CXXFLAGS=$save_CXXFLAGS
|
||||
|
||||
AC_LANG_POP()
|
||||
|
||||
# Checks for libraries.
|
||||
@@ -256,9 +268,16 @@ if test "x${have_zlib}" = "xno"; then
|
||||
fi
|
||||
|
||||
# dl: openssl requires libdl when it is statically linked.
|
||||
LIBS_OLD=$LIBS
|
||||
AC_SEARCH_LIBS([dlopen], [dl], [APPLDFLAGS="-ldl $APPLDFLAGS"], [], [])
|
||||
LIBS=$LIBS_OLD
|
||||
case "${host_os}" in
|
||||
*bsd*)
|
||||
# dlopen is in libc on *BSD
|
||||
;;
|
||||
*)
|
||||
save_LIBS=$LIBS
|
||||
AC_SEARCH_LIBS([dlopen], [dl], [APPLDFLAGS="-ldl $APPLDFLAGS"], [], [])
|
||||
LIBS=$save_LIBS
|
||||
;;
|
||||
esac
|
||||
|
||||
# cunit
|
||||
PKG_CHECK_MODULES([CUNIT], [cunit >= 2.1], [have_cunit=yes], [have_cunit=no])
|
||||
@@ -291,7 +310,7 @@ AM_CONDITIONAL([HAVE_CUNIT], [ test "x${have_cunit}" = "xyes" ])
|
||||
|
||||
# libev (for src)
|
||||
# libev does not have pkg-config file. Check it in an old way.
|
||||
LIBS_OLD=$LIBS
|
||||
save_LIBS=$LIBS
|
||||
# android requires -lm for floor
|
||||
AC_CHECK_LIB([ev], [ev_time], [have_libev=yes], [have_libev=no], [-lm])
|
||||
if test "x${have_libev}" = "xyes"; then
|
||||
@@ -303,7 +322,7 @@ if test "x${have_libev}" = "xyes"; then
|
||||
AC_SUBST([LIBEV_CFLAGS])
|
||||
fi
|
||||
fi
|
||||
LIBS=$LIBS_OLD
|
||||
LIBS=$save_LIBS
|
||||
|
||||
# openssl (for src)
|
||||
PKG_CHECK_MODULES([OPENSSL], [openssl >= 1.0.1],
|
||||
@@ -333,7 +352,10 @@ fi
|
||||
# libxml2 (for src/nghttp)
|
||||
have_libxml2=no
|
||||
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
|
||||
AC_DEFINE([HAVE_LIBXML2], [1], [Define to 1 if you have `libxml2` library.])
|
||||
fi
|
||||
@@ -349,7 +371,7 @@ AM_CONDITIONAL([HAVE_LIBXML2], [ test "x${have_libxml2}" = "xyes" ])
|
||||
# jemalloc
|
||||
have_jemalloc=no
|
||||
if test "x${request_jemalloc}" != "xno"; then
|
||||
LIBS_OLD=$LIBS
|
||||
save_LIBS=$LIBS
|
||||
AC_SEARCH_LIBS([malloc_stats_print], [jemalloc], [have_jemalloc=yes], [],
|
||||
[$PTHREAD_LDFLAGS])
|
||||
|
||||
@@ -365,7 +387,7 @@ if test "x${request_jemalloc}" != "xno"; then
|
||||
fi
|
||||
fi
|
||||
|
||||
LIBS=$LIBS_OLD
|
||||
LIBS=$save_LIBS
|
||||
|
||||
if test "x${have_jemalloc}" = "xyes" &&
|
||||
test "x${jemalloc_libs}" != "xnone required"; then
|
||||
@@ -632,8 +654,13 @@ AC_CHECK_FUNC([timerfd_create],
|
||||
|
||||
# For cygwin: we can link initgroups, so AC_CHECK_FUNCS succeeds, but
|
||||
# cygwin disables initgroups due to feature test macro magic with our
|
||||
# configuration.
|
||||
AC_CHECK_DECLS([initgroups], [], [], [[#include <grp.h>]])
|
||||
# configuration. FreeBSD declares initgroups() in unistd.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
|
||||
AX_HAVE_EPOLL([have_epoll=yes], [have_epoll=no])
|
||||
@@ -642,8 +669,8 @@ AM_CONDITIONAL([ENABLE_TINY_NGHTTPD],
|
||||
[ test "x${have_epoll}" = "xyes" &&
|
||||
test "x${have_timerfd_create}" = "xyes"])
|
||||
|
||||
ac_save_CFLAGS=$CFLAGS
|
||||
ac_save_CXXFLAGS=$CXXFLAGS
|
||||
save_CFLAGS=$CFLAGS
|
||||
save_CXXFLAGS=$CXXFLAGS
|
||||
|
||||
CFLAGS=
|
||||
CXXFLAGS=
|
||||
@@ -707,8 +734,8 @@ fi
|
||||
WARNCFLAGS=$CFLAGS
|
||||
WARNCXXFLAGS=$CXXFLAGS
|
||||
|
||||
CFLAGS=$ac_save_CFLAGS
|
||||
CXXFLAGS=$ac_save_CXXFLAGS
|
||||
CFLAGS=$save_CFLAGS
|
||||
CXXFLAGS=$save_CXXFLAGS
|
||||
|
||||
AC_SUBST([WARNCFLAGS])
|
||||
AC_SUBST([WARNCXXFLAGS])
|
||||
@@ -797,6 +824,7 @@ AC_MSG_NOTICE([summary of build options:
|
||||
C preprocessor: ${CPP}
|
||||
CPPFLAGS: ${CPPFLAGS}
|
||||
WARNCFLAGS: ${WARNCFLAGS}
|
||||
CXX1XCXXFLAGS: ${CXX1XCXXFLAGS}
|
||||
EXTRACFLAG: ${EXTRACFLAG}
|
||||
LIBS: ${LIBS}
|
||||
Library:
|
||||
|
||||
@@ -58,6 +58,7 @@ APIDOCS= \
|
||||
nghttp2_option_set_no_http_messaging.rst \
|
||||
nghttp2_option_set_no_recv_client_magic.rst \
|
||||
nghttp2_option_set_peer_max_concurrent_streams.rst \
|
||||
nghttp2_option_set_user_recv_extension_type.rst \
|
||||
nghttp2_pack_settings_payload.rst \
|
||||
nghttp2_priority_spec_check_default.rst \
|
||||
nghttp2_priority_spec_default_init.rst \
|
||||
@@ -70,16 +71,19 @@ APIDOCS= \
|
||||
nghttp2_session_callbacks_set_on_begin_frame_callback.rst \
|
||||
nghttp2_session_callbacks_set_on_begin_headers_callback.rst \
|
||||
nghttp2_session_callbacks_set_on_data_chunk_recv_callback.rst \
|
||||
nghttp2_session_callbacks_set_on_extension_chunk_recv_callback.rst \
|
||||
nghttp2_session_callbacks_set_on_frame_not_send_callback.rst \
|
||||
nghttp2_session_callbacks_set_on_frame_recv_callback.rst \
|
||||
nghttp2_session_callbacks_set_on_frame_send_callback.rst \
|
||||
nghttp2_session_callbacks_set_on_header_callback.rst \
|
||||
nghttp2_session_callbacks_set_on_invalid_frame_recv_callback.rst \
|
||||
nghttp2_session_callbacks_set_on_stream_close_callback.rst \
|
||||
nghttp2_session_callbacks_set_pack_extension_callback.rst \
|
||||
nghttp2_session_callbacks_set_recv_callback.rst \
|
||||
nghttp2_session_callbacks_set_select_padding_callback.rst \
|
||||
nghttp2_session_callbacks_set_send_callback.rst \
|
||||
nghttp2_session_callbacks_set_send_data_callback.rst \
|
||||
nghttp2_session_callbacks_set_unpack_extension_callback.rst \
|
||||
nghttp2_session_client_new.rst \
|
||||
nghttp2_session_client_new2.rst \
|
||||
nghttp2_session_client_new3.rst \
|
||||
@@ -131,6 +135,7 @@ APIDOCS= \
|
||||
nghttp2_stream_get_weight.rst \
|
||||
nghttp2_strerror.rst \
|
||||
nghttp2_submit_data.rst \
|
||||
nghttp2_submit_extension.rst \
|
||||
nghttp2_submit_goaway.rst \
|
||||
nghttp2_submit_headers.rst \
|
||||
nghttp2_submit_ping.rst \
|
||||
@@ -145,15 +150,18 @@ APIDOCS= \
|
||||
nghttp2_submit_window_update.rst \
|
||||
nghttp2_version.rst
|
||||
|
||||
EXTRA_DIST = \
|
||||
mkapiref.py \
|
||||
RST_FILES = \
|
||||
README.rst \
|
||||
programmers-guide.rst \
|
||||
$(APIDOCS) \
|
||||
nghttp.1.rst \
|
||||
nghttpd.1.rst \
|
||||
nghttpx.1.rst \
|
||||
h2load.1.rst \
|
||||
h2load.1.rst
|
||||
|
||||
EXTRA_DIST = \
|
||||
mkapiref.py \
|
||||
$(RST_FILES) \
|
||||
$(APIDOCS) \
|
||||
sources/index.rst \
|
||||
sources/tutorial-client.rst \
|
||||
sources/tutorial-server.rst \
|
||||
@@ -227,13 +235,15 @@ help:
|
||||
|
||||
apiref.rst: \
|
||||
$(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 \
|
||||
apiref.rst macros.rst enums.rst types.rst . $^
|
||||
|
||||
$(APIDOCS): apiref.rst
|
||||
|
||||
clean-local:
|
||||
[ $(srcdir) = $(builddir) ] || for i in $(RST_FILES); do [ -e $(builddir)/$$i ] && rm $(builddir)/$$i; done
|
||||
-rm -f apiref.rst
|
||||
-rm -f $(APIDOCS)
|
||||
-rm -rf $(BUILDDIR)/*
|
||||
|
||||
@@ -8,7 +8,7 @@ _nghttpd()
|
||||
_get_comp_words_by_ref cur prev
|
||||
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
|
||||
|
||||
@@ -8,7 +8,7 @@ _nghttpx()
|
||||
_get_comp_words_by_ref cur prev
|
||||
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 --conf --worker-write-burst --npn-list --fetch-ocsp-response-file --no-via --tls-session-cache-memcached-cert-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 --tls-ticket-key-memcached-cert-file --ocsp-update-interval --forwarded-by --tls-session-cache-memcached-private-key-file --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 --insecure --log-level --host-rewrite --tls-session-cache-memcached-tls --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-ticket-key-memcached-tls --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 --tls-ticket-key-memcached-address-family --padding --tls-session-cache-memcached-address-family --stream-write-timeout --cacert --tls-ticket-key-memcached-private-key-file --backend-address-family --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
|
||||
|
||||
@@ -41,7 +41,7 @@ import sys, os
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#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 -----------------------------------------------------
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "H2LOAD" "1" "January 25, 2016" "1.7.0" "nghttp2"
|
||||
.TH "H2LOAD" "1" "February 25, 2016" "1.8.0" "nghttp2"
|
||||
.SH NAME
|
||||
h2load \- HTTP/2 benchmarking tool
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTP" "1" "January 25, 2016" "1.7.0" "nghttp2"
|
||||
.TH "NGHTTP" "1" "February 25, 2016" "1.8.0" "nghttp2"
|
||||
.SH NAME
|
||||
nghttp \- HTTP/2 client
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTPD" "1" "January 25, 2016" "1.7.0" "nghttp2"
|
||||
.TH "NGHTTPD" "1" "February 25, 2016" "1.8.0" "nghttp2"
|
||||
.SH NAME
|
||||
nghttpd \- HTTP/2 server
|
||||
.
|
||||
@@ -139,6 +139,17 @@ Make error response gzipped.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.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>
|
||||
Path to file that contains DH parameters in PEM format.
|
||||
Without this option, DHE cipher suites are not
|
||||
|
||||
@@ -104,6 +104,15 @@ OPTIONS
|
||||
|
||||
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>
|
||||
|
||||
Path to file that contains DH parameters in PEM format.
|
||||
|
||||
194
doc/nghttpx.1
194
doc/nghttpx.1
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTPX" "1" "January 25, 2016" "1.7.0" "nghttp2"
|
||||
.TH "NGHTTPX" "1" "February 25, 2016" "1.8.0" "nghttp2"
|
||||
.SH NAME
|
||||
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
|
||||
assumes all addresses including both IPv4 and IPv6.
|
||||
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
|
||||
Default: \fB*,3000\fP
|
||||
.UNINDENT
|
||||
@@ -134,13 +136,13 @@ Default: \fB512\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-backend\-ipv4
|
||||
Resolve backend hostname to IPv4 address only.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-backend\-ipv6
|
||||
Resolve backend hostname to IPv6 address only.
|
||||
.B \-\-backend\-address\-family=(auto|IPv4|IPv6)
|
||||
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.
|
||||
.sp
|
||||
Default: \fBauto\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -163,6 +165,22 @@ be specified by \fI\%\-\-backend\-read\-timeout\fP and
|
||||
.B \-\-accept\-proxy\-protocol
|
||||
Accept PROXY protocol version 1 on frontend connection.
|
||||
.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
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -396,19 +414,17 @@ described in OpenSSL ciphers(1).
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-k, \-\-insecure
|
||||
Don\(aqt verify backend server\(aqs certificate if \fI\%\-p\fP,
|
||||
\fI\%\-\-client\fP or \fI\%\-\-http2\-bridge\fP are given and
|
||||
\fI\%\-\-backend\-no\-tls\fP is not given.
|
||||
Don\(aqt verify backend server\(aqs certificate if TLS is
|
||||
enabled for backend connections.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-cacert=<PATH>
|
||||
Set path to trusted CA certificate file if \fI\%\-p\fP, \fI\%\-\-client\fP
|
||||
or \fI\%\-\-http2\-bridge\fP are given and \fI\%\-\-backend\-no\-tls\fP is not
|
||||
given. The file must be in PEM format. It can contain
|
||||
multiple certificates. If the linked OpenSSL is
|
||||
configured to load system wide certificates, they are
|
||||
loaded at startup regardless of this option.
|
||||
Set path to trusted CA certificate file used in backend
|
||||
TLS connections. The file must be in PEM format. It
|
||||
can contain multiple certificates. If the linked
|
||||
OpenSSL is configured to load system wide certificates,
|
||||
they are loaded at startup regardless of this option.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -518,16 +534,27 @@ required.
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-tls\-ticket\-key\-memcached=<HOST>,<PORT>
|
||||
Specify address of memcached server to store session
|
||||
cache. This enables shared TLS ticket key between
|
||||
multiple nghttpx instances. nghttpx does not set TLS
|
||||
ticket key to memcached. The external ticket key
|
||||
generator is required. nghttpx just gets TLS ticket
|
||||
keys from memcached, and use them, possibly replacing
|
||||
current set of keys. It is up to extern TLS ticket key
|
||||
generator to rotate keys frequently. See "TLS SESSION
|
||||
TICKET RESUMPTION" section in manual page to know the
|
||||
data format in memcached entry.
|
||||
Specify address of memcached server to get TLS ticket
|
||||
keys for session resumption. This enables shared TLS
|
||||
ticket key between multiple nghttpx instances. nghttpx
|
||||
does not set TLS ticket key to memcached. The external
|
||||
ticket key generator is required. nghttpx just gets TLS
|
||||
ticket keys from memcached, and use them, possibly
|
||||
replacing current set of keys. It is up to extern TLS
|
||||
ticket key generator to rotate keys frequently. See
|
||||
"TLS SESSION TICKET RESUMPTION" section in manual page
|
||||
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
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -565,6 +592,24 @@ aes\-128\-cbc is used.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.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>
|
||||
Path to fetch\-ocsp\-response script file. It should be
|
||||
absolute path.
|
||||
@@ -592,6 +637,35 @@ multiple nghttpx instances.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.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>
|
||||
Specify the threshold size for TLS dynamic record size
|
||||
behaviour. During a TLS session, after the threshold
|
||||
@@ -616,6 +690,13 @@ TLS HTTP/2 backends.
|
||||
.sp
|
||||
Default: \fB1s\fP
|
||||
.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
|
||||
.SS HTTP/2 and SPDY
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -666,11 +747,6 @@ Default: \fB16\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-backend\-no\-tls
|
||||
Disable SSL/TLS on backend connections.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-http2\-no\-cookie\-crumbling
|
||||
Don\(aqt crumble cookie header field.
|
||||
.UNINDENT
|
||||
@@ -868,11 +944,12 @@ Specify the parameter value sent out with "by" parameter
|
||||
of Forwarded header field. If "obfuscated" is given,
|
||||
the string is randomly generated at startup. If "ip" is
|
||||
given, the interface address of the connection,
|
||||
including port number, is sent with "by" parameter.
|
||||
User can also specify the static obfuscated string. The
|
||||
limitation is that it must start with "_", and only
|
||||
consists of character set [A\-Za\-z0\-9._\-], as described
|
||||
in RFC 7239.
|
||||
including port number, is sent with "by" parameter. In
|
||||
case of UNIX domain socket, "localhost" is used instead
|
||||
of address and port. User can also specify the static
|
||||
obfuscated string. The limitation is that it must start
|
||||
with "_", and only consists of character set
|
||||
[A\-Za\-z0\-9._\-], as described in RFC 7239.
|
||||
.sp
|
||||
Default: \fBobfuscated\fP
|
||||
.UNINDENT
|
||||
@@ -884,7 +961,8 @@ parameter of Forwarded header field. If "obfuscated" is
|
||||
given, the string is randomly generated for each client
|
||||
connection. If "ip" is given, the remote client address
|
||||
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
|
||||
Default: \fBobfuscated\fP
|
||||
.UNINDENT
|
||||
@@ -940,22 +1018,42 @@ Example: \fI\%\-\-add\-response\-header\fP="foo: bar"
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-header\-field\-buffer=<SIZE>
|
||||
.B \-\-request\-header\-field\-buffer=<SIZE>
|
||||
Set maximum buffer size for incoming HTTP request header
|
||||
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
|
||||
Default: \fB64K\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-max\-header\-fields=<N>
|
||||
.B \-\-max\-request\-header\-fields=<N>
|
||||
Set maximum number of incoming HTTP request header
|
||||
fields, which appear in one request or response header
|
||||
field list.
|
||||
fields. If trailer fields exist, they are counted
|
||||
towards this number.
|
||||
.sp
|
||||
Default: \fB100\fP
|
||||
.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
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -1203,6 +1301,10 @@ insert serialized session data to memcached with
|
||||
\fBnghttpx:tls\-session\-cache:\fP + lowercased hex string of session ID
|
||||
as a memcached entry key, with expiry time 12 hours. Session timeout
|
||||
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
|
||||
.sp
|
||||
By default, session ticket is shared by all worker threads. The
|
||||
@@ -1247,6 +1349,10 @@ used, LEN must be 48. If
|
||||
keys. The key appeared first is used as encryption key. All the
|
||||
remaining keys are used as decryption only.
|
||||
.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
|
||||
from the given file. In this case, nghttpx does not rotate key
|
||||
automatically. To rotate key, one has to restart nghttpx (see
|
||||
|
||||
@@ -104,7 +104,9 @@ Connections
|
||||
Set frontend host and port. If <HOST> is '\*', it
|
||||
assumes all addresses including both IPv4 and IPv6.
|
||||
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``
|
||||
|
||||
@@ -114,13 +116,14 @@ Connections
|
||||
|
||||
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
|
||||
|
||||
Resolve backend hostname to IPv6 address only.
|
||||
Default: ``auto``
|
||||
|
||||
.. option:: --backend-http-proxy-uri=<URI>
|
||||
|
||||
@@ -141,6 +144,20 @@ Connections
|
||||
|
||||
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
|
||||
~~~~~~~~~~~
|
||||
@@ -354,18 +371,16 @@ SSL/TLS
|
||||
|
||||
.. option:: -k, --insecure
|
||||
|
||||
Don't verify backend server's certificate if :option:`-p`\,
|
||||
:option:`--client` or :option:`\--http2-bridge` are given and
|
||||
:option:`--backend-no-tls` is not given.
|
||||
Don't verify backend server's certificate if TLS is
|
||||
enabled for backend connections.
|
||||
|
||||
.. option:: --cacert=<PATH>
|
||||
|
||||
Set path to trusted CA certificate file if :option:`-p`\, :option:`--client`
|
||||
or :option:`--http2-bridge` are given and :option:`\--backend-no-tls` is not
|
||||
given. The file must be in PEM format. It can contain
|
||||
multiple certificates. If the linked OpenSSL is
|
||||
configured to load system wide certificates, they are
|
||||
loaded at startup regardless of this option.
|
||||
Set path to trusted CA certificate file used in backend
|
||||
TLS connections. The file must be in PEM format. It
|
||||
can contain multiple certificates. If the linked
|
||||
OpenSSL is configured to load system wide certificates,
|
||||
they are loaded at startup regardless of this option.
|
||||
|
||||
.. option:: --private-key-passwd-file=<PATH>
|
||||
|
||||
@@ -463,16 +478,26 @@ SSL/TLS
|
||||
|
||||
.. option:: --tls-ticket-key-memcached=<HOST>,<PORT>
|
||||
|
||||
Specify address of memcached server to store session
|
||||
cache. This enables shared TLS ticket key between
|
||||
multiple nghttpx instances. nghttpx does not set TLS
|
||||
ticket key to memcached. The external ticket key
|
||||
generator is required. nghttpx just gets TLS ticket
|
||||
keys from memcached, and use them, possibly replacing
|
||||
current set of keys. It is up to extern TLS ticket key
|
||||
generator to rotate keys frequently. See "TLS SESSION
|
||||
TICKET RESUMPTION" section in manual page to know the
|
||||
data format in memcached entry.
|
||||
Specify address of memcached server to get TLS ticket
|
||||
keys for session resumption. This enables shared TLS
|
||||
ticket key between multiple nghttpx instances. nghttpx
|
||||
does not set TLS ticket key to memcached. The external
|
||||
ticket key generator is required. nghttpx just gets TLS
|
||||
ticket keys from memcached, and use them, possibly
|
||||
replacing current set of keys. It is up to extern TLS
|
||||
ticket key generator to rotate keys frequently. See
|
||||
"TLS SESSION TICKET RESUMPTION" section in manual page
|
||||
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>
|
||||
|
||||
@@ -504,6 +529,21 @@ SSL/TLS
|
||||
either aes-128-cbc or aes-256-cbc. By default,
|
||||
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>
|
||||
|
||||
Path to fetch-ocsp-response script file. It should be
|
||||
@@ -527,6 +567,31 @@ SSL/TLS
|
||||
cache. This enables shared session cache between
|
||||
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>
|
||||
|
||||
Specify the threshold size for TLS dynamic record size
|
||||
@@ -551,6 +616,12 @@ SSL/TLS
|
||||
|
||||
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.
|
||||
|
||||
|
||||
HTTP/2 and SPDY
|
||||
~~~~~~~~~~~~~~~
|
||||
@@ -596,10 +667,6 @@ HTTP/2 and SPDY
|
||||
|
||||
Default: ``16``
|
||||
|
||||
.. option:: --backend-no-tls
|
||||
|
||||
Disable SSL/TLS on backend connections.
|
||||
|
||||
.. option:: --http2-no-cookie-crumbling
|
||||
|
||||
Don't crumble cookie header field.
|
||||
@@ -773,11 +840,12 @@ HTTP
|
||||
of Forwarded header field. If "obfuscated" is given,
|
||||
the string is randomly generated at startup. If "ip" is
|
||||
given, the interface address of the connection,
|
||||
including port number, is sent with "by" parameter.
|
||||
User can also specify the static obfuscated string. The
|
||||
limitation is that it must start with "_", and only
|
||||
consists of character set [A-Za-z0-9._-], as described
|
||||
in RFC 7239.
|
||||
including port number, is sent with "by" parameter. In
|
||||
case of UNIX domain socket, "localhost" is used instead
|
||||
of address and port. User can also specify the static
|
||||
obfuscated string. The limitation is that it must start
|
||||
with "_", and only consists of character set
|
||||
[A-Za-z0-9._-], as described in RFC 7239.
|
||||
|
||||
Default: ``obfuscated``
|
||||
|
||||
@@ -788,7 +856,8 @@ HTTP
|
||||
given, the string is randomly generated for each client
|
||||
connection. If "ip" is given, the remote client address
|
||||
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``
|
||||
|
||||
@@ -836,22 +905,40 @@ HTTP
|
||||
used several times to specify multiple header fields.
|
||||
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
|
||||
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``
|
||||
|
||||
.. option:: --max-header-fields=<N>
|
||||
.. option:: --max-request-header-fields=<N>
|
||||
|
||||
Set maximum number of incoming HTTP request header
|
||||
fields, which appear in one request or response header
|
||||
field list.
|
||||
fields. If trailer fields exist, they are counted
|
||||
towards this number.
|
||||
|
||||
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
|
||||
~~~~~
|
||||
@@ -1091,6 +1178,10 @@ insert serialized session data to memcached with
|
||||
as a memcached entry key, with expiry time 12 hours. Session timeout
|
||||
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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -1130,6 +1221,10 @@ used, LEN must be 48. If
|
||||
keys. The key appeared first is used as encryption key. All the
|
||||
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
|
||||
from the given file. In this case, nghttpx does not rotate key
|
||||
automatically. To rotate key, one has to restart nghttpx (see
|
||||
|
||||
@@ -150,6 +150,10 @@ insert serialized session data to memcached with
|
||||
as a memcached entry key, with expiry time 12 hours. Session timeout
|
||||
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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -189,6 +193,10 @@ used, LEN must be 48. If
|
||||
keys. The key appeared first is used as encryption key. All the
|
||||
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
|
||||
from the given file. In this case, nghttpx does not rotate key
|
||||
automatically. To rotate key, one has to restart nghttpx (see
|
||||
|
||||
@@ -1,6 +1,62 @@
|
||||
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
|
||||
--------
|
||||
|
||||
|
||||
@@ -1,33 +1,43 @@
|
||||
.. program:: h2load
|
||||
|
||||
h2load - HTTP/2 benchmarking tool - HOW-TO
|
||||
==========================================
|
||||
|
||||
h2load is benchmarking tool for HTTP/2 and HTTP/1.1. If built with
|
||||
spdylay (http://tatsuhiro-t.github.io/spdylay/) library, it also
|
||||
supports SPDY protocol. It supports SSL/TLS and clear text for all
|
||||
supported protocols.
|
||||
:doc:`h2load.1` is benchmarking tool for HTTP/2 and HTTP/1.1. If
|
||||
built with spdylay (http://tatsuhiro-t.github.io/spdylay/) library, it
|
||||
also supports SPDY protocol. It supports SSL/TLS and clear text for
|
||||
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
|
||||
-----------
|
||||
|
||||
In order to set benchmark settings, specify following 3 options.
|
||||
|
||||
``-n``
|
||||
:option:`-n`
|
||||
The number of total requests. Default: 1
|
||||
|
||||
``-c``
|
||||
:option:`-c`
|
||||
The number of concurrent clients. Default: 1
|
||||
|
||||
``-m``
|
||||
The max concurrent streams to issue per client.
|
||||
If ``auto`` is given, the number of given URIs is used.
|
||||
Default: ``auto``
|
||||
:option:`-m`
|
||||
The max concurrent streams to issue per client. Default: 1
|
||||
|
||||
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
|
||||
protocol in cleartext connection, use ``--no-tls-proto`` option. For
|
||||
convenience, ``--h1`` option forces HTTP/1.1 for both cleartext and
|
||||
SSL/TLS connections.
|
||||
protocol in cleartext connection, use :option:`--no-tls-proto` option.
|
||||
For convenience, :option:`--h1` option forces HTTP/1.1 for both
|
||||
cleartext and SSL/TLS connections.
|
||||
|
||||
Here is a command-line to perform benchmark to URI \https://localhost
|
||||
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
|
||||
receiver flow control window size, there are following options:
|
||||
|
||||
``-w``
|
||||
:option:`-w`
|
||||
Sets the stream level initial window size to
|
||||
(2**<N>)-1. For SPDY, 2**<N> is used instead.
|
||||
|
||||
``-W``
|
||||
:option:`-W`
|
||||
Sets the connection level initial window size to
|
||||
(2**<N>)-1. For SPDY, if <N> is strictly less
|
||||
than 16, this option is ignored. Otherwise
|
||||
@@ -76,17 +86,17 @@ Multi-Threading
|
||||
---------------
|
||||
|
||||
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.
|
||||
|
||||
``-t``
|
||||
:option:`-t`
|
||||
The number of native threads. Default: 1
|
||||
|
||||
Selecting protocol for clear text
|
||||
---------------------------------
|
||||
|
||||
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
|
||||
-------------
|
||||
@@ -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
|
||||
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.
|
||||
|
||||
@@ -1,35 +1,44 @@
|
||||
.. program:: nghttpx
|
||||
|
||||
nghttpx - HTTP/2 proxy - HOW-TO
|
||||
===============================
|
||||
|
||||
nghttpx is a proxy translating protocols between HTTP/2 and other
|
||||
protocols (e.g., HTTP/1, SPDY). It operates in several modes and each
|
||||
mode may require additional programs to work with. This article
|
||||
describes each operation mode and explains the intended use-cases. It
|
||||
also covers some useful options later.
|
||||
:doc:`nghttpx.1` is a proxy translating protocols between HTTP/2 and
|
||||
other protocols (e.g., HTTP/1, SPDY). It operates in several modes
|
||||
and each mode may require additional programs to work with. This
|
||||
article describes each operation mode and explains the intended
|
||||
use-cases. It also covers some useful options later.
|
||||
|
||||
Default mode
|
||||
------------
|
||||
|
||||
If nghttpx is invoked without any ``-s``, ``-p`` and ``--client``, it
|
||||
operates in default mode. In this mode, nghttpx frontend listens for
|
||||
HTTP/2 requests and translates them to HTTP/1 requests. Thus it works
|
||||
as reverse proxy (gateway) for HTTP/2 clients to HTTP/1 web server.
|
||||
HTTP/1 requests are also supported in frontend as a fallback. If
|
||||
nghttpx is linked with spdylay library and frontend connection is
|
||||
SSL/TLS, the frontend also supports SPDY protocol.
|
||||
If nghttpx is invoked without any :option:`--http2-proxy`,
|
||||
:option:`--client`, and :option:`--client-proxy`, it operates in
|
||||
default mode. In this mode, nghttpx frontend listens for HTTP/2
|
||||
requests and translates them to HTTP/1 requests. Thus it works as
|
||||
reverse proxy (gateway) for HTTP/2 clients to HTTP/1 web server. This
|
||||
is also known as "HTTP/2 router". HTTP/1 requests are also supported
|
||||
in frontend as a fallback. If nghttpx is linked with spdylay library
|
||||
and frontend connection is SSL/TLS, the frontend also supports SPDY
|
||||
protocol.
|
||||
|
||||
By default, this mode's frontend connection is encrypted using
|
||||
SSL/TLS. So server's private key and certificate must be supplied to
|
||||
the command line (or through configuration file). In this case, the
|
||||
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
|
||||
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
|
||||
HTTP/2 using HTTP Upgrade. Starting HTTP/2 connection by sending
|
||||
HTTP/2 connection preface is also supported.
|
||||
|
||||
By default, backend HTTP/1 connections are not encrypted. To enable
|
||||
TLS on HTTP/1 backend connections, use :option:`--backend-http1-tls`
|
||||
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
|
||||
nghttpx listen to encrypted HTTP/2 requests at port 8443, and a
|
||||
backend HTTP/1 web server is configured to listen to HTTP/1 request at
|
||||
@@ -45,19 +54,19 @@ example, you can send GET request to the server using nghttp::
|
||||
HTTP/2 proxy mode
|
||||
-----------------
|
||||
|
||||
If nghttpx is invoked with ``-s`` option, it operates in HTTP/2 proxy
|
||||
mode. The supported protocols in frontend and backend connections are
|
||||
the same in `default mode`_. The difference is that this mode acts
|
||||
like forward proxy and assumes the backend is HTTP/1 proxy server
|
||||
(e.g., squid, traffic server). So HTTP/1 request must include
|
||||
absolute URI in request line.
|
||||
If nghttpx is invoked with :option:`--http2-proxy` (or its shorthand
|
||||
:option:`-s`) option, it operates in HTTP/2 proxy mode. The supported
|
||||
protocols in frontend and backend connections are the same in `default
|
||||
mode`_. The difference is that this mode acts like forward proxy and
|
||||
assumes the backend is HTTP/1 proxy server (e.g., squid, traffic
|
||||
server). So HTTP/1 request must include absolute URI in request line.
|
||||
|
||||
By default, frontend connection is encrypted. So this mode is also
|
||||
called secure proxy. If nghttpx is linked with spdylay, it supports
|
||||
SPDY protocols and it works as so called SPDY proxy.
|
||||
|
||||
With ``--frontend-no-tls`` option, SSL/TLS is turned off in frontend
|
||||
connection, so the connection gets insecure.
|
||||
With :option:`--frontend-no-tls` option, SSL/TLS is turned off in
|
||||
frontend connection, so the connection gets insecure.
|
||||
|
||||
The backend must be HTTP/1 proxy server. nghttpx supports multiple
|
||||
backend server addresses. It translates incoming requests to HTTP/1
|
||||
@@ -91,7 +100,9 @@ Chromium require valid certificate for secure proxy.
|
||||
For Firefox, open Preference window and select Advanced then click
|
||||
Network tab. Clicking Connection Settings button will show the
|
||||
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
|
||||
|
||||
@@ -107,25 +118,27 @@ configuration items to edit::
|
||||
CONFIG proxy.config.url_remap.remap_required INT 0
|
||||
|
||||
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
|
||||
security implications.
|
||||
|
||||
Client mode
|
||||
-----------
|
||||
|
||||
If nghttpx is invoked with ``--client`` option, it operates in client
|
||||
mode. In this mode, nghttpx listens for plain, unencrypted HTTP/2 and
|
||||
HTTP/1 requests and translates them to encrypted HTTP/2 requests to
|
||||
the backend. User cannot enable SSL/TLS in frontend connection.
|
||||
If nghttpx is invoked with :option:`--client` option, it operates in
|
||||
client mode. In this mode, nghttpx listens for plain, unencrypted
|
||||
HTTP/2 and HTTP/1 requests and translates them to encrypted HTTP/2
|
||||
requests to the backend. User cannot enable SSL/TLS in frontend
|
||||
connection.
|
||||
|
||||
HTTP/1 frontend connection can be upgraded to HTTP/2 using HTTP
|
||||
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
|
||||
(thread) is determined by number of ``-b`` option. To adjust this
|
||||
value, use ``--backend-http2-connections-per-worker`` option.
|
||||
(thread) is determined by number of :option:`--backend` option. To
|
||||
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.,
|
||||
nghttpd). The one use-case of this mode is utilize existing HTTP/1
|
||||
@@ -137,9 +150,10 @@ mode to access to that web server::
|
||||
|
||||
.. note::
|
||||
|
||||
You may need ``-k`` option if HTTP/2 server enables SSL/TLS and
|
||||
its certificate is self-signed. But please note that it is
|
||||
insecure.
|
||||
You may need :option:`--insecure` (or its shorthand :option:`-k`)
|
||||
option if HTTP/2 server enables SSL/TLS and its 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 access HTTP/2 server via nghttpx::
|
||||
|
||||
@@ -148,18 +162,19 @@ Then you can use curl to access HTTP/2 server via nghttpx::
|
||||
Client proxy mode
|
||||
-----------------
|
||||
|
||||
If nghttpx is invoked with ``-p`` option, it operates in client proxy
|
||||
mode. This mode behaves like `client mode`_, but it works like
|
||||
forward proxy. So HTTP/1 request must include absolute URI in request
|
||||
line.
|
||||
If nghttpx is invoked with :option:`--client-proxy` (or its shorthand
|
||||
:option:`-p`) option, it operates in client proxy mode. This mode
|
||||
behaves like `client mode`_, but it works like forward proxy. So
|
||||
HTTP/1 request must include absolute URI in request line.
|
||||
|
||||
HTTP/1 frontend connection can be upgraded to HTTP/2 using HTTP
|
||||
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
|
||||
(thread) is determined by number of ``-b`` option. To adjust this
|
||||
value, use ``--backend-http2-connections-per-worker`` option.
|
||||
(thread) is determined by number of :option:`--backend` option. To
|
||||
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
|
||||
`HTTP/2 proxy mode`_ as backend server. The one use-case of this mode
|
||||
@@ -177,8 +192,9 @@ that server, invoke nghttpx like this::
|
||||
|
||||
.. note::
|
||||
|
||||
You may need ``-k`` option if HTTP/2 server's certificate is
|
||||
self-signed. But please note that it is insecure.
|
||||
You may need :option:`--insecure` (or its shorthand :option:`-k`)
|
||||
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::
|
||||
|
||||
@@ -190,23 +206,24 @@ proxy.
|
||||
HTTP/2 bridge mode
|
||||
------------------
|
||||
|
||||
If nghttpx is invoked with ``--http2-bridge`` option, it operates in
|
||||
HTTP/2 bridge mode. The supported protocols in frontend connections
|
||||
are the same in `default mode`_. The protocol in backend is HTTP/2
|
||||
only.
|
||||
If nghttpx is invoked with :option:`--http2-bridge` option, it
|
||||
operates in HTTP/2 bridge mode. The supported protocols in frontend
|
||||
connections are the same in `default mode`_. The protocol in backend
|
||||
is HTTP/2 only.
|
||||
|
||||
With ``--frontend-no-tls`` option, SSL/TLS is turned off in frontend
|
||||
connection, so the connection gets insecure. To disable SSL/TLS in
|
||||
backend connection, use ``--backend-no-tls`` option.
|
||||
With :option:`--frontend-no-tls` option, SSL/TLS is turned off in
|
||||
frontend connection, so the connection gets insecure. To disable
|
||||
SSL/TLS in backend connection, use :option:`--backend-no-tls` option.
|
||||
|
||||
By default, the number of backend HTTP/2 connections per worker
|
||||
(thread) is determined by number of ``-b`` option. To adjust this
|
||||
value, use ``--backend-http2-connections-per-worker`` option.
|
||||
(thread) is determined by number of :option:`--backend` option. To
|
||||
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
|
||||
proxy. If backend server is HTTP/2 proxy, use
|
||||
``--no-location-rewrite`` and ``--no-host-rewrite`` options to disable
|
||||
rewriting location, host and :authority header field.
|
||||
:option:`--no-location-rewrite` option to disable rewriting
|
||||
``Location`` header field.
|
||||
|
||||
The use-case of this mode is aggregate the incoming connections to one
|
||||
HTTP/2 connection. One backend HTTP/2 connection is created per
|
||||
@@ -217,26 +234,48 @@ Disable SSL/TLS
|
||||
|
||||
In `default mode`_, `HTTP/2 proxy mode`_ and `HTTP/2 bridge mode`_,
|
||||
frontend connections are encrypted with SSL/TLS by default. To turn
|
||||
off SSL/TLS, use ``--frontend-no-tls`` option. If this option is
|
||||
used, the private key and certificate are not required to run nghttpx.
|
||||
off SSL/TLS, use :option:`--frontend-no-tls` option. If this option
|
||||
is used, the private key and certificate are not required to run
|
||||
nghttpx.
|
||||
|
||||
In `client mode`_, `client proxy mode`_ and `HTTP/2 bridge mode`_,
|
||||
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
|
||||
------------------------------------
|
||||
|
||||
By default, nghttpx tries to read CA certificate from system. But
|
||||
depending on the system you use, this may fail or is not supported.
|
||||
To specify CA certificate manually, use ``--cacert`` option. The
|
||||
specified file must be PEM format and can contain multiple
|
||||
To specify CA certificate manually, use :option:`--cacert` option.
|
||||
The specified file must be PEM format and can contain multiple
|
||||
certificates.
|
||||
|
||||
By default, nghttpx validates server's certificate. If you want to
|
||||
turn off this validation, knowing this is really insecure and what you
|
||||
are doing, you can use ``-k`` option to disable certificate
|
||||
validation.
|
||||
are doing, you can use :option:`--insecure` option to disable
|
||||
certificate validation.
|
||||
|
||||
Read/write rate limit
|
||||
---------------------
|
||||
@@ -245,9 +284,9 @@ nghttpx supports transfer rate limiting on frontend connections. You
|
||||
can do rate limit per frontend connection for reading and writing
|
||||
individually.
|
||||
|
||||
To perform rate limit for reading, use ``--read-rate`` and
|
||||
``--read-burst`` options. For writing, use ``--write-rate`` and
|
||||
``--write-burst``.
|
||||
To perform rate limit for reading, use :option:`--read-rate` and
|
||||
:option:`--read-burst` options. For writing, use
|
||||
:option:`--write-rate` and :option:`--write-burst`.
|
||||
|
||||
Please note that rate limit is performed on top of TCP and nothing to
|
||||
do with HTTP/2 flow control.
|
||||
@@ -289,14 +328,64 @@ Re-opening log files
|
||||
When rotating log files, it is desirable to re-open log files after
|
||||
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 files specified by ``--accesslog-file`` and
|
||||
``--errorlog-file`` options.
|
||||
re-open files specified by :option:`--accesslog-file` and
|
||||
:option:`--errorlog-file` options.
|
||||
|
||||
Multiple backend addresses
|
||||
--------------------------
|
||||
|
||||
nghttpx supports multiple backend addresses. To specify them, just
|
||||
use ``-b`` option repeatedly. For example, to use backend1:8080 and
|
||||
backend2:8080, use command-line like this: ``-bbackend1,8080
|
||||
-bbackend2,8080``. For HTTP/2 backend, see also
|
||||
``--backend-http2-connections-per-worker`` option.
|
||||
use :option:`--backend` (or its shorthand :option:`-b`) option
|
||||
repeatedly. For example, to use ``192.168.0.10:8080`` and
|
||||
``192.168.0.11:8080``, use command-line like this:
|
||||
``-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.
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
if ENABLE_EXAMPLES
|
||||
|
||||
AM_CFLAGS = $(WARNCFLAGS)
|
||||
AM_CXXFLAGS = $(WARNCXXFLAGS)
|
||||
AM_CXXFLAGS = $(WARNCXXFLAGS) $(CXX1XCXXFLAGS)
|
||||
AM_CPPFLAGS = \
|
||||
-I$(top_srcdir)/lib/includes \
|
||||
-I$(top_builddir)/lib/includes \
|
||||
|
||||
@@ -289,8 +289,6 @@ static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define MAX_OUTLEN 4096
|
||||
|
||||
/*
|
||||
* The implementation of nghttp2_on_data_chunk_recv_callback type. We
|
||||
* use this function to print the received response body.
|
||||
|
||||
@@ -295,7 +295,7 @@ static size_t http_date(char *buf, time_t t) {
|
||||
static char date[29];
|
||||
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) {
|
||||
size_t nwrite = 0;
|
||||
|
||||
32
genauthoritychartbl.py
Executable file
32
genauthoritychartbl.py
Executable file
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env python
|
||||
import sys
|
||||
|
||||
def name(i):
|
||||
if i < 0x21:
|
||||
return \
|
||||
['NUL ', 'SOH ', 'STX ', 'ETX ', 'EOT ', 'ENQ ', 'ACK ', 'BEL ',
|
||||
'BS ', 'HT ', 'LF ', 'VT ', 'FF ', 'CR ', 'SO ', 'SI ',
|
||||
'DLE ', 'DC1 ', 'DC2 ', 'DC3 ', 'DC4 ', 'NAK ', 'SYN ', 'ETB ',
|
||||
'CAN ', 'EM ', 'SUB ', 'ESC ', 'FS ', 'GS ', 'RS ', 'US ',
|
||||
'SPC '][i]
|
||||
elif i == 0x7f:
|
||||
return 'DEL '
|
||||
|
||||
for i in range(256):
|
||||
if chr(i) in [
|
||||
"-", ".", "_", "~",
|
||||
"!", "$", "&", "'", "(", ")",
|
||||
"*", "+", ",", ";", "=",
|
||||
"%", "@", ":", "[", "]"] or\
|
||||
('0' <= chr(i) and chr(i) <= '9') or \
|
||||
('A' <= chr(i) and chr(i) <= 'Z') or \
|
||||
('a' <= chr(i) and chr(i) <= 'z'):
|
||||
sys.stdout.write('1 /* {} */, '.format(chr(i)))
|
||||
elif (0x21 <= i and i < 0x7f):
|
||||
sys.stdout.write('0 /* {} */, '.format(chr(i)))
|
||||
elif 0x80 <= i:
|
||||
sys.stdout.write('0 /* {} */, '.format(hex(i)))
|
||||
else:
|
||||
sys.stdout.write('0 /* {} */, '.format(name(i)))
|
||||
if (i + 1)%4 == 0:
|
||||
sys.stdout.write('\n')
|
||||
@@ -62,11 +62,67 @@ HEADERS = [
|
||||
('vary', 58),
|
||||
('via', 59),
|
||||
('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),
|
||||
('keep-alive',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),
|
||||
('last-event-id', None),
|
||||
('negotiate', None),
|
||||
('origin', None),
|
||||
('p3p', None),
|
||||
('pragma', 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-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):
|
||||
|
||||
@@ -88,12 +88,11 @@ OPTIONS = [
|
||||
"fetch-ocsp-response-file",
|
||||
"ocsp-update-interval",
|
||||
"no-ocsp",
|
||||
"header-field-buffer",
|
||||
"max-header-fields",
|
||||
"include",
|
||||
"tls-ticket-key-cipher",
|
||||
"host-rewrite",
|
||||
"tls-session-cache-memcached",
|
||||
"tls-session-cache-memcached-tls",
|
||||
"tls-ticket-key-memcached",
|
||||
"tls-ticket-key-memcached-interval",
|
||||
"tls-ticket-key-memcached-max-retry",
|
||||
@@ -107,7 +106,23 @@ OPTIONS = [
|
||||
"add-forwarded",
|
||||
"strip-incoming-forwarded",
|
||||
"forwarded-by",
|
||||
"forwarded-for"
|
||||
"forwarded-for",
|
||||
"response-header-field-buffer",
|
||||
"max-response-header-fields",
|
||||
"request-header-field-buffer",
|
||||
"max-request-header-fields",
|
||||
"header-field-buffer",
|
||||
"max-header-fields",
|
||||
"no-http2-cipher-black-list",
|
||||
"backend-http1-tls",
|
||||
"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 = [
|
||||
|
||||
@@ -21,11 +21,14 @@
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
EXTRA_DIST = \
|
||||
GO_FILES = \
|
||||
nghttpx_http1_test.go \
|
||||
nghttpx_http2_test.go \
|
||||
nghttpx_spdy_test.go \
|
||||
server_tester.go \
|
||||
server_tester.go
|
||||
|
||||
EXTRA_DIST = \
|
||||
$(GO_FILES) \
|
||||
server.key \
|
||||
server.crt \
|
||||
alt-server.key \
|
||||
@@ -43,4 +46,5 @@ itprep-local:
|
||||
go get -d -v golang.org/x/net/websocket
|
||||
|
||||
it-local:
|
||||
for i in $(GO_FILES); do [ -e $(builddir)/$$i ] || cp $(srcdir)/$$i $(builddir); done
|
||||
sh setenv go test -v
|
||||
|
||||
@@ -2,4 +2,5 @@ package nghttp2
|
||||
|
||||
const (
|
||||
buildDir = "@top_builddir@"
|
||||
sourceDir = "@top_srcdir@"
|
||||
)
|
||||
|
||||
@@ -29,7 +29,8 @@ import (
|
||||
const (
|
||||
serverBin = buildDir + "/src/nghttpx"
|
||||
serverPort = 3009
|
||||
testDir = buildDir + "/integration-tests"
|
||||
testDir = sourceDir + "/integration-tests"
|
||||
logDir = buildDir + "/integration-tests"
|
||||
)
|
||||
|
||||
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"
|
||||
b := "-b" + strings.Replace(backendURL.Host, ":", ",", -1)
|
||||
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)
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ THIS_MAKEFILE := $(lastword $(MAKEFILE_LIST))
|
||||
USE_CYTHON := 1
|
||||
#USE_CYTHON := 0
|
||||
|
||||
_VERSION := $(shell grep AC_INIT ../configure.ac | cut -d'[' -f3 | sed -e 's/-DEV], //g')
|
||||
_VERSION := $(shell grep AC_INIT ../configure.ac | cut -d'[' -f3 | sed -r -e 's/(-DEV)?], //g')
|
||||
_VERSION := $(subst ., ,$(_VERSION))
|
||||
VER_MAJOR := $(word 1,$(_VERSION))
|
||||
VER_MINOR := $(word 2,$(_VERSION))
|
||||
|
||||
@@ -382,6 +382,10 @@ typedef enum {
|
||||
* Unexpected internal error, but recovered.
|
||||
*/
|
||||
NGHTTP2_ERR_INTERNAL = -534,
|
||||
/**
|
||||
* Indicates that a processing was canceled.
|
||||
*/
|
||||
NGHTTP2_ERR_CANCEL = -535,
|
||||
/**
|
||||
* The errors < :enum:`NGHTTP2_ERR_FATAL` mean that the library is
|
||||
* under unexpected condition and processing was terminated (e.g.,
|
||||
@@ -1608,6 +1612,14 @@ typedef int (*nghttp2_on_begin_headers_callback)(nghttp2_session *session,
|
||||
*
|
||||
* To set this callback to :type:`nghttp2_session_callbacks`, use
|
||||
* `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,
|
||||
const nghttp2_frame *frame,
|
||||
@@ -1692,6 +1704,99 @@ typedef int (*nghttp2_on_begin_frame_callback)(nghttp2_session *session,
|
||||
const nghttp2_frame_hd *hd,
|
||||
void *user_data);
|
||||
|
||||
/**
|
||||
* @functypedef
|
||||
*
|
||||
* Callback function invoked when chunk of extension frame payload is
|
||||
* received. The |hd| points to frame header. The received
|
||||
* chunk is |data| of length |len|.
|
||||
*
|
||||
* The implementation of this function must return 0 if it succeeds.
|
||||
*
|
||||
* To abort processing this extension frame, return
|
||||
* :enum:`NGHTTP2_ERR_CANCEL`.
|
||||
*
|
||||
* If fatal error occurred, application should return
|
||||
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. In this case,
|
||||
* `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions
|
||||
* immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. If the
|
||||
* other values are returned, currently they are treated as
|
||||
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
|
||||
*/
|
||||
typedef int (*nghttp2_on_extension_chunk_recv_callback)(
|
||||
nghttp2_session *session, const nghttp2_frame_hd *hd, const uint8_t *data,
|
||||
size_t len, void *user_data);
|
||||
|
||||
/**
|
||||
* @functypedef
|
||||
*
|
||||
* Callback function invoked when library asks the application to
|
||||
* unpack extension payload from its wire format. The extension
|
||||
* payload has been passed to the application using
|
||||
* :type:`nghttp2_on_extension_chunk_recv_callback`. The frame header
|
||||
* is already unpacked by the library and provided as |hd|.
|
||||
*
|
||||
* To receive extension frames, the application must tell desired
|
||||
* extension frame type to the library using
|
||||
* `nghttp2_option_set_user_recv_extension_type()`.
|
||||
*
|
||||
* The implementation of this function may store the pointer to the
|
||||
* created object as a result of unpacking in |*payload|, and returns
|
||||
* 0. The pointer stored in |*payload| is opaque to the library, and
|
||||
* the library does not own its pointer. |*payload| is initialized as
|
||||
* ``NULL``. The |*payload| is available as ``frame->ext.payload`` in
|
||||
* :type:`nghttp2_on_frame_recv_callback`. Therefore if application
|
||||
* can free that memory inside :type:`nghttp2_on_frame_recv_callback`
|
||||
* callback. Of course, application has a liberty not ot use
|
||||
* |*payload|, and do its own mechanism to process extension frames.
|
||||
*
|
||||
* To abort processing this extension frame, return
|
||||
* :enum:`NGHTTP2_ERR_CANCEL`.
|
||||
*
|
||||
* If fatal error occurred, application should return
|
||||
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. In this case,
|
||||
* `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions
|
||||
* immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. If the
|
||||
* other values are returned, currently they are treated as
|
||||
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
|
||||
*/
|
||||
typedef int (*nghttp2_unpack_extension_callback)(nghttp2_session *session,
|
||||
void **payload,
|
||||
const nghttp2_frame_hd *hd,
|
||||
void *user_data);
|
||||
|
||||
/**
|
||||
* @functypedef
|
||||
*
|
||||
* Callback function invoked when library asks the application to pack
|
||||
* extension payload in its wire format. The frame header will be
|
||||
* packed by library. Application must pack payload only.
|
||||
* ``frame->ext.payload`` is the object passed to
|
||||
* `nghttp2_submit_extension()` as payload parameter. Application
|
||||
* must pack extension payload to the |buf| of its capacity |len|
|
||||
* bytes. The |len| is at least 16KiB.
|
||||
*
|
||||
* The implementation of this function should return the number of
|
||||
* bytes written into |buf| when it succeeds.
|
||||
*
|
||||
* To abort processing this extension frame, return
|
||||
* :enum:`NGHTTP2_ERR_CANCEL`, and
|
||||
* :type:`nghttp2_on_frame_not_send_callback` will be invoked.
|
||||
*
|
||||
* If fatal error occurred, application should return
|
||||
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. In this case,
|
||||
* `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions
|
||||
* immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. If the
|
||||
* other values are returned, currently they are treated as
|
||||
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. If the return value is
|
||||
* strictly larger than |len|, it is treated as
|
||||
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
|
||||
*/
|
||||
typedef ssize_t (*nghttp2_pack_extension_callback)(nghttp2_session *session,
|
||||
uint8_t *buf, size_t len,
|
||||
const nghttp2_frame *frame,
|
||||
void *user_data);
|
||||
|
||||
struct nghttp2_session_callbacks;
|
||||
|
||||
/**
|
||||
@@ -1884,6 +1989,37 @@ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_send_data_callback(
|
||||
nghttp2_session_callbacks *cbs,
|
||||
nghttp2_send_data_callback send_data_callback);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* Sets callback function invoked when the library asks the
|
||||
* application to pack extension frame payload in wire format.
|
||||
*/
|
||||
NGHTTP2_EXTERN void nghttp2_session_callbacks_set_pack_extension_callback(
|
||||
nghttp2_session_callbacks *cbs,
|
||||
nghttp2_pack_extension_callback pack_extension_callback);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* Sets callback function invoked when the library asks the
|
||||
* application to unpack extension frame payload from wire format.
|
||||
*/
|
||||
NGHTTP2_EXTERN void nghttp2_session_callbacks_set_unpack_extension_callback(
|
||||
nghttp2_session_callbacks *cbs,
|
||||
nghttp2_unpack_extension_callback unpack_extension_callback);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* Sets callback function invoked when chunk of extension frame
|
||||
* payload is received.
|
||||
*/
|
||||
NGHTTP2_EXTERN void
|
||||
nghttp2_session_callbacks_set_on_extension_chunk_recv_callback(
|
||||
nghttp2_session_callbacks *cbs,
|
||||
nghttp2_on_extension_chunk_recv_callback on_extension_chunk_recv_callback);
|
||||
|
||||
/**
|
||||
* @functypedef
|
||||
*
|
||||
@@ -2098,6 +2234,23 @@ NGHTTP2_EXTERN void
|
||||
nghttp2_option_set_max_reserved_remote_streams(nghttp2_option *option,
|
||||
uint32_t val);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* Sets extension frame type the application is willing to handle with
|
||||
* user defined callbacks (see
|
||||
* :type:`nghttp2_on_extension_chunk_recv_callback` and
|
||||
* :type:`nghttp2_unpack_extension_callback`). The |type| is
|
||||
* extension frame type, and must be strictly greater than 0x9.
|
||||
* Otherwise, this function does nothing. The application can call
|
||||
* this function multiple times to set more than one frame type to
|
||||
* receive. The application does not have to call this function if it
|
||||
* just sends extension frames.
|
||||
*/
|
||||
NGHTTP2_EXTERN void
|
||||
nghttp2_option_set_user_recv_extension_type(nghttp2_option *option,
|
||||
uint8_t type);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
@@ -3768,6 +3921,48 @@ NGHTTP2_EXTERN int nghttp2_submit_window_update(nghttp2_session *session,
|
||||
int32_t stream_id,
|
||||
int32_t window_size_increment);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* Submits extension frame.
|
||||
*
|
||||
* Application can pass arbitrary frame flags and stream ID in |flags|
|
||||
* and |stream_id| respectively. The |payload| is opaque pointer, and
|
||||
* it can be accessible though ``frame->ext.payload`` in
|
||||
* :type:`nghttp2_pack_extension_callback`. The library will not own
|
||||
* passed |payload| pointer.
|
||||
*
|
||||
* The application must set :type:`nghttp2_pack_extension_callback`
|
||||
* using `nghttp2_session_callbacks_set_pack_extension_callback()`.
|
||||
*
|
||||
* The application should retain the memory pointed by |payload| until
|
||||
* the transmission of extension frame is done (which is indicated by
|
||||
* :type:`nghttp2_on_frame_send_callback`), or transmission fails
|
||||
* (which is indicated by :type:`nghttp2_on_frame_not_send_callback`).
|
||||
* If application does not touch this memory region after packing it
|
||||
* into a wire format, application can free it inside
|
||||
* :type:`nghttp2_pack_extension_callback`.
|
||||
*
|
||||
* The standard HTTP/2 frame cannot be sent with this function, so
|
||||
* |type| must be strictly grater than 0x9. Otherwise, this function
|
||||
* will fail with error code :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* :enum:`NGHTTP2_ERR_INVALID_STATE`
|
||||
* If :type:`nghttp2_pack_extension_callback` is not set.
|
||||
* :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
|
||||
* If |type| specifies standard HTTP/2 frame type. The frame
|
||||
* types in the rage [0x0, 0x9], both inclusive, are standard
|
||||
* HTTP/2 frame type, and cannot be sent using this function.
|
||||
* :enum:`NGHTTP2_ERR_NOMEM`
|
||||
* Out of memory
|
||||
*/
|
||||
NGHTTP2_EXTERN int nghttp2_submit_extension(nghttp2_session *session,
|
||||
uint8_t type, uint8_t flags,
|
||||
int32_t stream_id, void *payload);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
|
||||
@@ -127,3 +127,21 @@ void nghttp2_session_callbacks_set_send_data_callback(
|
||||
nghttp2_send_data_callback send_data_callback) {
|
||||
cbs->send_data_callback = send_data_callback;
|
||||
}
|
||||
|
||||
void nghttp2_session_callbacks_set_pack_extension_callback(
|
||||
nghttp2_session_callbacks *cbs,
|
||||
nghttp2_pack_extension_callback pack_extension_callback) {
|
||||
cbs->pack_extension_callback = pack_extension_callback;
|
||||
}
|
||||
|
||||
void nghttp2_session_callbacks_set_unpack_extension_callback(
|
||||
nghttp2_session_callbacks *cbs,
|
||||
nghttp2_unpack_extension_callback unpack_extension_callback) {
|
||||
cbs->unpack_extension_callback = unpack_extension_callback;
|
||||
}
|
||||
|
||||
void nghttp2_session_callbacks_set_on_extension_chunk_recv_callback(
|
||||
nghttp2_session_callbacks *cbs,
|
||||
nghttp2_on_extension_chunk_recv_callback on_extension_chunk_recv_callback) {
|
||||
cbs->on_extension_chunk_recv_callback = on_extension_chunk_recv_callback;
|
||||
}
|
||||
|
||||
@@ -107,6 +107,9 @@ struct nghttp2_session_callbacks {
|
||||
*/
|
||||
nghttp2_on_begin_frame_callback on_begin_frame_callback;
|
||||
nghttp2_send_data_callback send_data_callback;
|
||||
nghttp2_pack_extension_callback pack_extension_callback;
|
||||
nghttp2_unpack_extension_callback unpack_extension_callback;
|
||||
nghttp2_on_extension_chunk_recv_callback on_extension_chunk_recv_callback;
|
||||
};
|
||||
|
||||
#endif /* NGHTTP2_CALLBACKS_H */
|
||||
|
||||
@@ -184,6 +184,15 @@ void nghttp2_frame_data_init(nghttp2_data *frame, uint8_t flags,
|
||||
|
||||
void nghttp2_frame_data_free(nghttp2_data *frame _U_) {}
|
||||
|
||||
void nghttp2_frame_extension_init(nghttp2_extension *frame, uint8_t type,
|
||||
uint8_t flags, int32_t stream_id,
|
||||
void *payload) {
|
||||
nghttp2_frame_hd_init(&frame->hd, 0, type, flags, stream_id);
|
||||
frame->payload = payload;
|
||||
}
|
||||
|
||||
void nghttp2_frame_extension_free(nghttp2_extension *frame _U_) {}
|
||||
|
||||
size_t nghttp2_frame_priority_len(uint8_t flags) {
|
||||
if (flags & NGHTTP2_FLAG_PRIORITY) {
|
||||
return NGHTTP2_PRIORITY_SPECLEN;
|
||||
|
||||
@@ -439,6 +439,12 @@ void nghttp2_frame_window_update_init(nghttp2_window_update *frame,
|
||||
|
||||
void nghttp2_frame_window_update_free(nghttp2_window_update *frame);
|
||||
|
||||
void nghttp2_frame_extension_init(nghttp2_extension *frame, uint8_t type,
|
||||
uint8_t flags, int32_t stream_id,
|
||||
void *payload);
|
||||
|
||||
void nghttp2_frame_extension_free(nghttp2_extension *frame);
|
||||
|
||||
/*
|
||||
* Returns the number of padding bytes after payload. The total
|
||||
* padding length is given in the |padlen|. The returned value does
|
||||
|
||||
306
lib/nghttp2_hd.c
306
lib/nghttp2_hd.c
@@ -137,6 +137,26 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
|
||||
return NGHTTP2_TOKEN_AGE;
|
||||
}
|
||||
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;
|
||||
case 4:
|
||||
@@ -197,16 +217,31 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
|
||||
break;
|
||||
case 6:
|
||||
switch (name[5]) {
|
||||
case 'a':
|
||||
if (lstreq("pragm", name, 5)) {
|
||||
return NGHTTP2_TOKEN_PRAGMA;
|
||||
}
|
||||
break;
|
||||
case 'e':
|
||||
if (lstreq("cooki", name, 5)) {
|
||||
return NGHTTP2_TOKEN_COOKIE;
|
||||
}
|
||||
break;
|
||||
case 'n':
|
||||
if (lstreq("origi", name, 5)) {
|
||||
return NGHTTP2_TOKEN_ORIGIN;
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
if (lstreq("serve", name, 5)) {
|
||||
return NGHTTP2_TOKEN_SERVER;
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
if (lstreq("statu", name, 5)) {
|
||||
return NGHTTP2_TOKEN_STATUS;
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
if (lstreq("accep", name, 5)) {
|
||||
return NGHTTP2_TOKEN_ACCEPT;
|
||||
@@ -219,6 +254,11 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
|
||||
break;
|
||||
case 7:
|
||||
switch (name[6]) {
|
||||
case 'c':
|
||||
if (lstreq("alt-sv", name, 6)) {
|
||||
return NGHTTP2_TOKEN_ALT_SVC;
|
||||
}
|
||||
break;
|
||||
case 'd':
|
||||
if (lstreq(":metho", name, 6)) {
|
||||
return NGHTTP2_TOKEN__METHOD;
|
||||
@@ -237,6 +277,14 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
|
||||
if (lstreq("upgrad", name, 6)) {
|
||||
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;
|
||||
case 'h':
|
||||
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)) {
|
||||
return NGHTTP2_TOKEN_REFERER;
|
||||
}
|
||||
if (lstreq("traile", name, 6)) {
|
||||
return NGHTTP2_TOKEN_TRAILER;
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
if (lstreq(":statu", name, 6)) {
|
||||
@@ -295,6 +346,25 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
|
||||
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:
|
||||
switch (name[9]) {
|
||||
case 'e':
|
||||
@@ -310,6 +380,11 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
|
||||
return NGHTTP2_TOKEN_CONNECTION;
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
if (lstreq("alternate", name, 9)) {
|
||||
return NGHTTP2_TOKEN_ALTERNATES;
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
if (lstreq("user-agen", name, 9)) {
|
||||
return NGHTTP2_TOKEN_USER_AGENT;
|
||||
@@ -324,6 +399,16 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
|
||||
break;
|
||||
case 11:
|
||||
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':
|
||||
if (lstreq("retry-afte", name, 10)) {
|
||||
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;
|
||||
}
|
||||
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':
|
||||
if (lstreq("max-forward", name, 11)) {
|
||||
return NGHTTP2_TOKEN_MAX_FORWARDS;
|
||||
}
|
||||
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;
|
||||
case 13:
|
||||
switch (name[12]) {
|
||||
case 'd':
|
||||
if (lstreq("last-event-i", name, 12)) {
|
||||
return NGHTTP2_TOKEN_LAST_EVENT_ID;
|
||||
}
|
||||
if (lstreq("last-modifie", name, 12)) {
|
||||
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)) {
|
||||
return NGHTTP2_TOKEN_CONTENT_RANGE;
|
||||
}
|
||||
if (lstreq("x-wap-profil", name, 12)) {
|
||||
return NGHTTP2_TOKEN_X_WAP_PROFILE;
|
||||
}
|
||||
break;
|
||||
case 'h':
|
||||
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)) {
|
||||
return NGHTTP2_TOKEN_AUTHORIZATION;
|
||||
}
|
||||
if (lstreq("x-api-versio", name, 12)) {
|
||||
return NGHTTP2_TOKEN_X_API_VERSION;
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
if (lstreq("accept-range", name, 12)) {
|
||||
@@ -381,11 +493,21 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
|
||||
break;
|
||||
case 14:
|
||||
switch (name[13]) {
|
||||
case 'd':
|
||||
if (lstreq("x-att-devicei", name, 13)) {
|
||||
return NGHTTP2_TOKEN_X_ATT_DEVICEID;
|
||||
}
|
||||
break;
|
||||
case 'h':
|
||||
if (lstreq("content-lengt", name, 13)) {
|
||||
return NGHTTP2_TOKEN_CONTENT_LENGTH;
|
||||
}
|
||||
break;
|
||||
case 'p':
|
||||
if (lstreq("x-cache-looku", name, 13)) {
|
||||
return NGHTTP2_TOKEN_X_CACHE_LOOKUP;
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
if (lstreq("accept-charse", name, 13)) {
|
||||
return NGHTTP2_TOKEN_ACCEPT_CHARSET;
|
||||
@@ -396,15 +518,40 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
|
||||
case 15:
|
||||
switch (name[14]) {
|
||||
case 'e':
|
||||
if (lstreq("accept-datetim", name, 14)) {
|
||||
return NGHTTP2_TOKEN_ACCEPT_DATETIME;
|
||||
}
|
||||
if (lstreq("accept-languag", name, 14)) {
|
||||
return NGHTTP2_TOKEN_ACCEPT_LANGUAGE;
|
||||
}
|
||||
if (lstreq("x-ua-compatibl", name, 14)) {
|
||||
return NGHTTP2_TOKEN_X_UA_COMPATIBLE;
|
||||
}
|
||||
break;
|
||||
case 'g':
|
||||
if (lstreq("accept-encodin", name, 14)) {
|
||||
return NGHTTP2_TOKEN_ACCEPT_ENCODING;
|
||||
}
|
||||
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;
|
||||
case 16:
|
||||
@@ -422,6 +569,11 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
|
||||
return NGHTTP2_TOKEN_CONTENT_ENCODING;
|
||||
}
|
||||
break;
|
||||
case 'h':
|
||||
if (lstreq("x-requested-wit", name, 15)) {
|
||||
return NGHTTP2_TOKEN_X_REQUESTED_WITH;
|
||||
}
|
||||
break;
|
||||
case 'n':
|
||||
if (lstreq("content-locatio", name, 15)) {
|
||||
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)) {
|
||||
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;
|
||||
@@ -444,6 +604,16 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
|
||||
return NGHTTP2_TOKEN_TRANSFER_ENCODING;
|
||||
}
|
||||
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;
|
||||
case 18:
|
||||
@@ -453,6 +623,11 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
|
||||
return NGHTTP2_TOKEN_PROXY_AUTHENTICATE;
|
||||
}
|
||||
break;
|
||||
case 'n':
|
||||
if (lstreq("x-content-duratio", name, 17)) {
|
||||
return NGHTTP2_TOKEN_X_CONTENT_DURATION;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 19:
|
||||
@@ -472,12 +647,80 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
|
||||
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:
|
||||
switch (name[24]) {
|
||||
case 's':
|
||||
if (lstreq("upgrade-insecure-request", name, 24)) {
|
||||
return NGHTTP2_TOKEN_UPGRADE_INSECURE_REQUESTS;
|
||||
}
|
||||
break;
|
||||
case 'y':
|
||||
if (lstreq("strict-transport-securit", name, 24)) {
|
||||
return NGHTTP2_TOKEN_STRICT_TRANSPORT_SECURITY;
|
||||
}
|
||||
if (lstreq("x-content-security-polic", name, 24)) {
|
||||
return NGHTTP2_TOKEN_X_CONTENT_SECURITY_POLICY;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@@ -490,6 +733,59 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
|
||||
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;
|
||||
}
|
||||
@@ -617,8 +913,8 @@ static nghttp2_hd_entry *hd_map_find(nghttp2_hd_map *map, int *exact_match,
|
||||
*exact_match = 0;
|
||||
|
||||
for (p = map->table[hash & (HD_MAP_SIZE - 1)]; p; p = p->next) {
|
||||
if (hash != p->hash || token != p->token ||
|
||||
(token == -1 && !name_eq(&p->nv, nv))) {
|
||||
if (token != p->token ||
|
||||
(token == -1 && (hash != p->hash || !name_eq(&p->nv, nv)))) {
|
||||
continue;
|
||||
}
|
||||
if (!res) {
|
||||
@@ -1444,7 +1740,7 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs,
|
||||
int indexing_mode;
|
||||
int token;
|
||||
nghttp2_mem *mem;
|
||||
uint32_t hash;
|
||||
uint32_t hash = 0;
|
||||
|
||||
DEBUGF(fprintf(stderr, "deflatehd: deflating %.*s: %.*s\n", (int)nv->namelen,
|
||||
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;
|
||||
|
||||
token = lookup_token(nv->name, nv->namelen);
|
||||
if (token == -1 || token > NGHTTP2_TOKEN_WWW_AUTHENTICATE) {
|
||||
if (token == -1) {
|
||||
hash = name_hash(nv);
|
||||
} else {
|
||||
} else if (token <= NGHTTP2_TOKEN_WWW_AUTHENTICATE) {
|
||||
hash = static_table[token].hash;
|
||||
}
|
||||
|
||||
|
||||
@@ -105,11 +105,67 @@ typedef enum {
|
||||
NGHTTP2_TOKEN_VARY = 58,
|
||||
NGHTTP2_TOKEN_VIA = 59,
|
||||
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_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_LAST_EVENT_ID,
|
||||
NGHTTP2_TOKEN_NEGOTIATE,
|
||||
NGHTTP2_TOKEN_ORIGIN,
|
||||
NGHTTP2_TOKEN_P3P,
|
||||
NGHTTP2_TOKEN_PRAGMA,
|
||||
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;
|
||||
|
||||
typedef enum {
|
||||
|
||||
@@ -288,6 +288,8 @@ const char *nghttp2_strerror(int error_code) {
|
||||
return "Stream was refused";
|
||||
case NGHTTP2_ERR_INTERNAL:
|
||||
return "Internal error";
|
||||
case NGHTTP2_ERR_CANCEL:
|
||||
return "Cancel";
|
||||
case NGHTTP2_ERR_NOMEM:
|
||||
return "Out of memory";
|
||||
case NGHTTP2_ERR_CALLBACK_FAILURE:
|
||||
|
||||
@@ -25,9 +25,9 @@
|
||||
#ifndef NGHTTP2_NPN_H
|
||||
#define NGHTTP2_NPN_H
|
||||
|
||||
#ifdef HAVE_CONFIG
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /* HAVE_CONFIG */
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
|
||||
@@ -62,3 +62,14 @@ void nghttp2_option_set_max_reserved_remote_streams(nghttp2_option *option,
|
||||
option->opt_set_mask |= NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS;
|
||||
option->max_reserved_remote_streams = val;
|
||||
}
|
||||
|
||||
void nghttp2_option_set_user_recv_extension_type(nghttp2_option *option,
|
||||
uint8_t type) {
|
||||
if (type < 10) {
|
||||
return;
|
||||
}
|
||||
|
||||
option->opt_set_mask |= NGHTTP2_OPT_USER_RECV_EXT_TYPES;
|
||||
option->user_recv_ext_types[type / 8] =
|
||||
(uint8_t)(option->user_recv_ext_types[type / 8] | (1 << (type & 0x7)));
|
||||
}
|
||||
|
||||
@@ -59,7 +59,8 @@ typedef enum {
|
||||
NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS = 1 << 1,
|
||||
NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC = 1 << 2,
|
||||
NGHTTP2_OPT_NO_HTTP_MESSAGING = 1 << 3,
|
||||
NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS = 1 << 4
|
||||
NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS = 1 << 4,
|
||||
NGHTTP2_OPT_USER_RECV_EXT_TYPES = 1 << 5
|
||||
} nghttp2_option_flag;
|
||||
|
||||
/**
|
||||
@@ -91,6 +92,10 @@ struct nghttp2_option {
|
||||
* NGHTTP2_OPT_NO_HTTP_MESSAGING
|
||||
*/
|
||||
int no_http_messaging;
|
||||
/**
|
||||
* NGHTTP2_OPT_USER_RECV_EXT_TYPES
|
||||
*/
|
||||
uint8_t user_recv_ext_types[32];
|
||||
};
|
||||
|
||||
#endif /* NGHTTP2_OPTION_H */
|
||||
|
||||
@@ -72,6 +72,9 @@ void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem) {
|
||||
case NGHTTP2_WINDOW_UPDATE:
|
||||
nghttp2_frame_window_update_free(&frame->window_update);
|
||||
break;
|
||||
default:
|
||||
nghttp2_frame_extension_free(&frame->ext);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -231,6 +231,8 @@ static void session_inbound_frame_reset(nghttp2_session *session) {
|
||||
nghttp2_session_new(), we rely on the fact that
|
||||
iframe->frame.hd.type is 0, so that no free is performed. */
|
||||
switch (iframe->frame.hd.type) {
|
||||
case NGHTTP2_DATA:
|
||||
break;
|
||||
case NGHTTP2_HEADERS:
|
||||
nghttp2_frame_headers_free(&iframe->frame.headers, mem);
|
||||
break;
|
||||
@@ -255,6 +257,10 @@ static void session_inbound_frame_reset(nghttp2_session *session) {
|
||||
case NGHTTP2_WINDOW_UPDATE:
|
||||
nghttp2_frame_window_update_free(&iframe->frame.window_update);
|
||||
break;
|
||||
default:
|
||||
/* extension frame */
|
||||
nghttp2_frame_extension_free(&iframe->frame.ext);
|
||||
break;
|
||||
}
|
||||
|
||||
memset(&iframe->frame, 0, sizeof(nghttp2_frame));
|
||||
@@ -405,6 +411,11 @@ static int session_new(nghttp2_session **session_ptr,
|
||||
|
||||
(*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_HTTP_MESSAGING;
|
||||
}
|
||||
|
||||
if (option->opt_set_mask & NGHTTP2_OPT_USER_RECV_EXT_TYPES) {
|
||||
memcpy((*session_ptr)->user_recv_ext_types, option->user_recv_ext_types,
|
||||
sizeof((*session_ptr)->user_recv_ext_types));
|
||||
}
|
||||
}
|
||||
|
||||
(*session_ptr)->callbacks = *callbacks;
|
||||
@@ -1216,11 +1227,12 @@ int nghttp2_session_adjust_idle_stream(nghttp2_session *session) {
|
||||
size_t max;
|
||||
int rv;
|
||||
|
||||
/* Make minimum number of idle streams 16, which is arbitrary chosen
|
||||
number. */
|
||||
max = nghttp2_max(16,
|
||||
nghttp2_min(session->local_settings.max_concurrent_streams,
|
||||
session->pending_local_max_concurrent_stream));
|
||||
/* Make minimum number of idle streams 16, and maximum 100, which
|
||||
are arbitrary chosen numbers. */
|
||||
max = nghttp2_min(
|
||||
100, nghttp2_max(
|
||||
16, nghttp2_min(session->local_settings.max_concurrent_streams,
|
||||
session->pending_local_max_concurrent_stream)));
|
||||
|
||||
DEBUGF(fprintf(stderr, "stream: adjusting kept idle streams "
|
||||
"num_idle_streams=%zu, max=%zu\n",
|
||||
@@ -1291,7 +1303,9 @@ static int session_allow_incoming_new_stream(nghttp2_session *session) {
|
||||
* This function returns nonzero if session is closing.
|
||||
*/
|
||||
static int session_is_closing(nghttp2_session *session) {
|
||||
return (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) != 0;
|
||||
return (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) != 0 ||
|
||||
(nghttp2_session_want_read(session) == 0 &&
|
||||
nghttp2_session_want_write(session) == 0);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1322,8 +1336,8 @@ static int session_predicate_for_stream_send(nghttp2_session *session,
|
||||
|
||||
int nghttp2_session_check_request_allowed(nghttp2_session *session) {
|
||||
return !session->server && session->next_stream_id <= INT32_MAX &&
|
||||
(session->goaway_flags &
|
||||
(NGHTTP2_GOAWAY_TERM_ON_SEND | NGHTTP2_GOAWAY_RECV)) == 0;
|
||||
(session->goaway_flags & NGHTTP2_GOAWAY_RECV) == 0 &&
|
||||
!session_is_closing(session);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1345,10 +1359,11 @@ static int session_predicate_request_headers_send(nghttp2_session *session,
|
||||
if (item->aux_data.headers.canceled) {
|
||||
return NGHTTP2_ERR_STREAM_CLOSING;
|
||||
}
|
||||
/* If we are terminating session (NGHTTP2_GOAWAY_TERM_ON_SEND) or
|
||||
GOAWAY was received from peer, new request is not allowed. */
|
||||
if (session->goaway_flags &
|
||||
(NGHTTP2_GOAWAY_TERM_ON_SEND | NGHTTP2_GOAWAY_RECV)) {
|
||||
/* If we are terminating session (NGHTTP2_GOAWAY_TERM_ON_SEND),
|
||||
GOAWAY was received from peer, or session is about to close, new
|
||||
request is not allowed. */
|
||||
if ((session->goaway_flags & NGHTTP2_GOAWAY_RECV) ||
|
||||
session_is_closing(session)) {
|
||||
return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED;
|
||||
}
|
||||
return 0;
|
||||
@@ -1745,6 +1760,41 @@ static size_t session_estimate_headers_payload(nghttp2_session *session,
|
||||
additional;
|
||||
}
|
||||
|
||||
static int session_pack_extension(nghttp2_session *session, nghttp2_bufs *bufs,
|
||||
nghttp2_frame *frame) {
|
||||
ssize_t rv;
|
||||
nghttp2_buf *buf;
|
||||
size_t buflen;
|
||||
size_t framelen;
|
||||
|
||||
assert(session->callbacks.pack_extension_callback);
|
||||
|
||||
buf = &bufs->head->buf;
|
||||
buflen = nghttp2_min(nghttp2_buf_avail(buf), NGHTTP2_MAX_PAYLOADLEN);
|
||||
|
||||
rv = session->callbacks.pack_extension_callback(session, buf->last, buflen,
|
||||
frame, session->user_data);
|
||||
if (rv == NGHTTP2_ERR_CANCEL) {
|
||||
return (int)rv;
|
||||
}
|
||||
|
||||
if (rv < 0 || (size_t)rv > buflen) {
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
framelen = (size_t)rv;
|
||||
|
||||
frame->hd.length = framelen;
|
||||
|
||||
assert(buf->pos == buf->last);
|
||||
buf->last += framelen;
|
||||
buf->pos -= NGHTTP2_FRAME_HDLEN;
|
||||
|
||||
nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function serializes frame for transmission.
|
||||
*
|
||||
@@ -1903,6 +1953,13 @@ static int session_prep_frame(nghttp2_session *session,
|
||||
if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
|
||||
assert(session->obq_flood_counter_ > 0);
|
||||
--session->obq_flood_counter_;
|
||||
/* When session is about to close, don't send SETTINGS ACK.
|
||||
We are required to send SETTINGS without ACK though; for
|
||||
example, we have to send SETTINGS as a part of connection
|
||||
preface. */
|
||||
if (session_is_closing(session)) {
|
||||
return NGHTTP2_ERR_SESSION_CLOSING;
|
||||
}
|
||||
}
|
||||
|
||||
rv = nghttp2_frame_pack_settings(&session->aob.framebufs,
|
||||
@@ -1978,8 +2035,22 @@ static int session_prep_frame(nghttp2_session *session,
|
||||
nghttp2_frame_pack_window_update(&session->aob.framebufs,
|
||||
&frame->window_update);
|
||||
break;
|
||||
case NGHTTP2_CONTINUATION:
|
||||
/* We never handle CONTINUATION here. */
|
||||
assert(0);
|
||||
break;
|
||||
default:
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
/* extension frame */
|
||||
if (session_is_closing(session)) {
|
||||
return NGHTTP2_ERR_SESSION_CLOSING;
|
||||
}
|
||||
|
||||
rv = session_pack_extension(session, &session->aob.framebufs, frame);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
@@ -3063,6 +3134,47 @@ static int session_call_on_header(nghttp2_session *session,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
session_call_on_extension_chunk_recv_callback(nghttp2_session *session,
|
||||
const uint8_t *data, size_t len) {
|
||||
int rv;
|
||||
nghttp2_inbound_frame *iframe = &session->iframe;
|
||||
nghttp2_frame *frame = &iframe->frame;
|
||||
|
||||
if (session->callbacks.on_extension_chunk_recv_callback) {
|
||||
rv = session->callbacks.on_extension_chunk_recv_callback(
|
||||
session, &frame->hd, data, len, session->user_data);
|
||||
if (rv == NGHTTP2_ERR_CANCEL) {
|
||||
return rv;
|
||||
}
|
||||
if (rv != 0) {
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int session_call_unpack_extension_callback(nghttp2_session *session) {
|
||||
int rv;
|
||||
nghttp2_inbound_frame *iframe = &session->iframe;
|
||||
nghttp2_frame *frame = &iframe->frame;
|
||||
void *payload = NULL;
|
||||
|
||||
rv = session->callbacks.unpack_extension_callback(
|
||||
session, &payload, &frame->hd, session->user_data);
|
||||
if (rv == NGHTTP2_ERR_CANCEL) {
|
||||
return rv;
|
||||
}
|
||||
if (rv != 0) {
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
frame->ext.payload = payload;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handles frame size error.
|
||||
*
|
||||
@@ -4401,6 +4513,24 @@ static int session_process_window_update_frame(nghttp2_session *session) {
|
||||
return nghttp2_session_on_window_update_received(session, frame);
|
||||
}
|
||||
|
||||
static int session_process_extension_frame(nghttp2_session *session) {
|
||||
int rv;
|
||||
nghttp2_inbound_frame *iframe = &session->iframe;
|
||||
nghttp2_frame *frame = &iframe->frame;
|
||||
|
||||
rv = session_call_unpack_extension_callback(session);
|
||||
if (nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* This handles the case where rv == NGHTTP2_ERR_CANCEL as well */
|
||||
if (rv != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return session_call_on_frame_received(session, frame);
|
||||
}
|
||||
|
||||
int nghttp2_session_on_data_received(nghttp2_session *session,
|
||||
nghttp2_frame *frame) {
|
||||
int rv = 0;
|
||||
@@ -5219,11 +5349,21 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
|
||||
default:
|
||||
DEBUGF(fprintf(stderr, "recv: unknown frame\n"));
|
||||
|
||||
/* Silently ignore unknown frame type. */
|
||||
if (!session->callbacks.unpack_extension_callback ||
|
||||
(session->user_recv_ext_types[iframe->frame.hd.type / 8] &
|
||||
(1 << (iframe->frame.hd.type & 0x7))) == 0) {
|
||||
/* Silently ignore unknown frame type. */
|
||||
|
||||
busy = 1;
|
||||
|
||||
iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
busy = 1;
|
||||
|
||||
iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
|
||||
iframe->state = NGHTTP2_IB_READ_EXTENSION_PAYLOAD;
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -5630,10 +5770,12 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
|
||||
|
||||
readlen = inbound_frame_payload_readlen(iframe, in, last);
|
||||
|
||||
iframe->lbuf.last = nghttp2_cpymem(iframe->lbuf.last, in, readlen);
|
||||
if (readlen > 0) {
|
||||
iframe->lbuf.last = nghttp2_cpymem(iframe->lbuf.last, in, readlen);
|
||||
|
||||
iframe->payloadleft -= readlen;
|
||||
in += readlen;
|
||||
iframe->payloadleft -= readlen;
|
||||
in += readlen;
|
||||
}
|
||||
|
||||
DEBUGF(fprintf(stderr, "recv: readlen=%zu, payloadleft=%zu\n", readlen,
|
||||
iframe->payloadleft));
|
||||
@@ -5921,6 +6063,44 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
|
||||
break;
|
||||
case NGHTTP2_IB_IGN_ALL:
|
||||
return (ssize_t)inlen;
|
||||
case NGHTTP2_IB_READ_EXTENSION_PAYLOAD:
|
||||
DEBUGF(fprintf(stderr, "recv: [IB_READ_EXTENSION_PAYLOAD]\n"));
|
||||
|
||||
readlen = inbound_frame_payload_readlen(iframe, in, last);
|
||||
iframe->payloadleft -= readlen;
|
||||
in += readlen;
|
||||
|
||||
DEBUGF(fprintf(stderr, "recv: readlen=%zu, payloadleft=%zu\n", readlen,
|
||||
iframe->payloadleft));
|
||||
|
||||
if (readlen > 0) {
|
||||
rv = session_call_on_extension_chunk_recv_callback(
|
||||
session, in - readlen, readlen);
|
||||
if (nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (rv != 0) {
|
||||
busy = 1;
|
||||
|
||||
iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (iframe->payloadleft > 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
rv = session_process_extension_frame(session);
|
||||
if (nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
session_inbound_frame_reset(session);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!busy && in == last) {
|
||||
|
||||
@@ -105,7 +105,8 @@ typedef enum {
|
||||
NGHTTP2_IB_READ_PAD_DATA,
|
||||
NGHTTP2_IB_READ_DATA,
|
||||
NGHTTP2_IB_IGN_DATA,
|
||||
NGHTTP2_IB_IGN_ALL
|
||||
NGHTTP2_IB_IGN_ALL,
|
||||
NGHTTP2_IB_READ_EXTENSION_PAYLOAD
|
||||
} nghttp2_inbound_state;
|
||||
|
||||
#define NGHTTP2_INBOUND_NUM_IV 7
|
||||
@@ -304,6 +305,13 @@ struct nghttp2_session {
|
||||
this session. The nonzero does not necessarily mean
|
||||
WINDOW_UPDATE is not queued. */
|
||||
uint8_t window_update_queued;
|
||||
/* Bitfield of extension frame types that application is willing to
|
||||
receive. To designate the bit of given frame type i, use
|
||||
user_recv_ext_types[i / 8] & (1 << (i & 0x7)). First 10 frame
|
||||
types are standard frame types and not used in this bitfield. If
|
||||
bit is set, it indicates that incoming frame with that type is
|
||||
passed to user defined callbacks, otherwise they are ignored. */
|
||||
uint8_t user_recv_ext_types[32];
|
||||
};
|
||||
|
||||
/* Struct used when updating initial window size of each active
|
||||
|
||||
@@ -30,14 +30,32 @@
|
||||
#include "nghttp2_session.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) {
|
||||
const nghttp2_stream *lhs, *rhs;
|
||||
|
||||
lhs = nghttp2_struct_of(lhsx, nghttp2_stream, pq_entry);
|
||||
rhs = nghttp2_struct_of(rhsx, nghttp2_stream, pq_entry);
|
||||
|
||||
return lhs->cycle < rhs->cycle ||
|
||||
(lhs->cycle == rhs->cycle && lhs->seq < rhs->seq);
|
||||
if (lhs->cycle == rhs->cycle) {
|
||||
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,
|
||||
@@ -116,14 +134,14 @@ static int stream_subtree_active(nghttp2_stream *stream) {
|
||||
/*
|
||||
* Returns next cycle for |stream|.
|
||||
*/
|
||||
static void stream_next_cycle(nghttp2_stream *stream, uint64_t last_cycle) {
|
||||
size_t penalty;
|
||||
static void stream_next_cycle(nghttp2_stream *stream, uint32_t last_cycle) {
|
||||
uint32_t penalty;
|
||||
|
||||
penalty =
|
||||
stream->last_writelen * NGHTTP2_MAX_WEIGHT + stream->pending_penalty;
|
||||
penalty = (uint32_t)stream->last_writelen * NGHTTP2_MAX_WEIGHT +
|
||||
stream->pending_penalty;
|
||||
|
||||
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) {
|
||||
@@ -229,9 +247,9 @@ void nghttp2_stream_reschedule(nghttp2_stream *stream) {
|
||||
|
||||
void nghttp2_stream_change_weight(nghttp2_stream *stream, int32_t weight) {
|
||||
nghttp2_stream *dep_stream;
|
||||
uint64_t last_cycle;
|
||||
uint32_t last_cycle;
|
||||
int32_t old_weight;
|
||||
size_t wlen_penalty;
|
||||
uint32_t wlen_penalty;
|
||||
|
||||
if (stream->weight == weight) {
|
||||
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);
|
||||
|
||||
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
|
||||
stream->cycle */
|
||||
@@ -270,7 +288,9 @@ void nghttp2_stream_change_weight(nghttp2_stream *stream, int32_t weight) {
|
||||
place */
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -147,9 +147,9 @@ struct nghttp2_stream {
|
||||
/* Received body so far */
|
||||
int64_t recv_content_length;
|
||||
/* Base last_cycle for direct descendent streams. */
|
||||
uint64_t descendant_last_cycle;
|
||||
uint32_t descendant_last_cycle;
|
||||
/* Next scheduled time to sent item */
|
||||
uint64_t cycle;
|
||||
uint32_t cycle;
|
||||
/* Next seq used for direct descendant streams */
|
||||
uint64_t descendant_next_seq;
|
||||
/* Secondary key for prioritization to break a tie for cycle. This
|
||||
|
||||
@@ -530,3 +530,40 @@ ssize_t nghttp2_pack_settings_payload(uint8_t *buf, size_t buflen,
|
||||
|
||||
return (ssize_t)nghttp2_frame_pack_settings_payload(buf, iv, niv);
|
||||
}
|
||||
|
||||
int nghttp2_submit_extension(nghttp2_session *session, uint8_t type,
|
||||
uint8_t flags, int32_t stream_id, void *payload) {
|
||||
int rv;
|
||||
nghttp2_outbound_item *item;
|
||||
nghttp2_frame *frame;
|
||||
nghttp2_mem *mem;
|
||||
|
||||
mem = &session->mem;
|
||||
|
||||
if (type <= NGHTTP2_CONTINUATION) {
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (!session->callbacks.pack_extension_callback) {
|
||||
return NGHTTP2_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
|
||||
if (item == NULL) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
nghttp2_outbound_item_init(item);
|
||||
|
||||
frame = &item->frame;
|
||||
nghttp2_frame_extension_init(&frame->ext, type, flags, stream_id, payload);
|
||||
|
||||
rv = nghttp2_session_add_item(session, item);
|
||||
if (rv != 0) {
|
||||
nghttp2_frame_extension_free(&frame->ext);
|
||||
nghttp2_mem_free(mem, item);
|
||||
return rv;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ git checkout refs/tags/$TAG
|
||||
git log --pretty=fuller --date=short refs/tags/$PREV_TAG..HEAD > ChangeLog
|
||||
|
||||
git submodule update --init
|
||||
|
||||
autoreconf -i
|
||||
./configure --with-mruby && \
|
||||
make dist-bzip2 && make dist-gzip && make dist-xz || echo "error"
|
||||
make distclean
|
||||
|
||||
5
releasechk
Executable file
5
releasechk
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
git submodule update --init
|
||||
./configure --with-mruby --with-neverbleed --enable-asio-lib
|
||||
make -j3 distcheck DISTCHECK_CONFIGURE_FLAGS="--with-mruby --with-neverbleed --enable-asio-lib --enable-werror"
|
||||
@@ -101,12 +101,26 @@ template <typename Array> void append_nv(Stream *stream, const Array &nva) {
|
||||
} // namespace
|
||||
|
||||
Config::Config()
|
||||
: mime_types_file("/etc/mime.types"), stream_read_timeout(1_min),
|
||||
stream_write_timeout(1_min), data_ptr(nullptr), padding(0), num_worker(1),
|
||||
max_concurrent_streams(100), header_table_size(-1), port(0),
|
||||
verbose(false), daemon(false), verify_client(false), no_tls(false),
|
||||
error_gzip(false), early_response(false), hexdump(false),
|
||||
echo_upload(false), no_content_length(false) {}
|
||||
: mime_types_file("/etc/mime.types"),
|
||||
stream_read_timeout(1_min),
|
||||
stream_write_timeout(1_min),
|
||||
data_ptr(nullptr),
|
||||
padding(0),
|
||||
num_worker(1),
|
||||
max_concurrent_streams(100),
|
||||
header_table_size(-1),
|
||||
window_bits(-1),
|
||||
connection_window_bits(-1),
|
||||
port(0),
|
||||
verbose(false),
|
||||
daemon(false),
|
||||
verify_client(false),
|
||||
no_tls(false),
|
||||
error_gzip(false),
|
||||
early_response(false),
|
||||
hexdump(false),
|
||||
echo_upload(false),
|
||||
no_content_length(false) {}
|
||||
|
||||
Config::~Config() {}
|
||||
|
||||
@@ -225,8 +239,13 @@ class Sessions {
|
||||
public:
|
||||
Sessions(HttpServer *sv, struct ev_loop *loop, const Config *config,
|
||||
SSL_CTX *ssl_ctx)
|
||||
: sv_(sv), loop_(loop), config_(config), ssl_ctx_(ssl_ctx),
|
||||
callbacks_(nullptr), next_session_id_(1), tstamp_cached_(ev_now(loop)),
|
||||
: sv_(sv),
|
||||
loop_(loop),
|
||||
config_(config),
|
||||
ssl_ctx_(ssl_ctx),
|
||||
callbacks_(nullptr),
|
||||
next_session_id_(1),
|
||||
tstamp_cached_(ev_now(loop)),
|
||||
cached_date_(util::http_date(tstamp_cached_)) {
|
||||
nghttp2_session_callbacks_new(&callbacks_);
|
||||
|
||||
@@ -289,7 +308,6 @@ public:
|
||||
}
|
||||
auto handler =
|
||||
make_unique<Http2Handler>(this, fd, ssl, get_next_session_id());
|
||||
handler->setup_bev();
|
||||
if (!ssl) {
|
||||
if (handler->connection_made() != 0) {
|
||||
return;
|
||||
@@ -424,8 +442,13 @@ void release_fd_cb(struct ev_loop *loop, ev_timer *w, int revents) {
|
||||
} // namespace
|
||||
|
||||
Stream::Stream(Http2Handler *handler, int32_t stream_id)
|
||||
: handler(handler), file_ent(nullptr), body_length(0), body_offset(0),
|
||||
stream_id(stream_id), echo_upload(false) {
|
||||
: handler(handler),
|
||||
file_ent(nullptr),
|
||||
body_length(0),
|
||||
body_offset(0),
|
||||
header_buffer_size(0),
|
||||
stream_id(stream_id),
|
||||
echo_upload(false) {
|
||||
auto config = handler->get_config();
|
||||
ev_timer_init(&rtimer, stream_timeout_cb, 0., config->stream_read_timeout);
|
||||
ev_timer_init(&wtimer, stream_timeout_cb, 0., config->stream_write_timeout);
|
||||
@@ -496,8 +519,13 @@ void writecb(struct ev_loop *loop, ev_io *w, int revents) {
|
||||
|
||||
Http2Handler::Http2Handler(Sessions *sessions, int fd, SSL *ssl,
|
||||
int64_t session_id)
|
||||
: session_id_(session_id), session_(nullptr), sessions_(sessions),
|
||||
ssl_(ssl), data_pending_(nullptr), data_pendinglen_(0), fd_(fd) {
|
||||
: session_id_(session_id),
|
||||
session_(nullptr),
|
||||
sessions_(sessions),
|
||||
ssl_(ssl),
|
||||
data_pending_(nullptr),
|
||||
data_pendinglen_(0),
|
||||
fd_(fd) {
|
||||
ev_timer_init(&settings_timerev_, settings_timeout_cb, 10., 0.);
|
||||
ev_io_init(&wev_, writecb, fd, EV_WRITE);
|
||||
ev_io_init(&rev_, readcb, fd, EV_READ);
|
||||
@@ -546,7 +574,9 @@ struct ev_loop *Http2Handler::get_loop() const {
|
||||
|
||||
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() {
|
||||
if (data_pending_) {
|
||||
@@ -819,12 +849,27 @@ int Http2Handler::connection_made() {
|
||||
entry[niv].value = config->header_table_size;
|
||||
++niv;
|
||||
}
|
||||
|
||||
if (config->window_bits != -1) {
|
||||
entry[niv].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
|
||||
entry[niv].value = (1 << config->window_bits) - 1;
|
||||
++niv;
|
||||
}
|
||||
|
||||
r = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, entry.data(), niv);
|
||||
if (r != 0) {
|
||||
return r;
|
||||
}
|
||||
|
||||
ev_timer_start(sessions_->get_loop(), &settings_timerev_);
|
||||
if (config->connection_window_bits != -1) {
|
||||
r = nghttp2_submit_window_update(
|
||||
session_, NGHTTP2_FLAG_NONE, 0,
|
||||
(1 << config->connection_window_bits) - 1 -
|
||||
NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE);
|
||||
if (r != 0) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
if (ssl_ && !nghttp2::ssl::check_http2_requirement(ssl_)) {
|
||||
terminate_session(NGHTTP2_INADEQUATE_SECURITY);
|
||||
@@ -864,6 +909,21 @@ int Http2Handler::verify_npn_result() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
namespace {
|
||||
std::string make_trailer_header_value(const Headers &trailer) {
|
||||
if (trailer.empty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
auto trailer_names = trailer[0].name;
|
||||
for (size_t i = 1; i < trailer.size(); ++i) {
|
||||
trailer_names += ", ";
|
||||
trailer_names += trailer[i].name;
|
||||
}
|
||||
return trailer_names;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int Http2Handler::submit_file_response(const std::string &status,
|
||||
Stream *stream, time_t last_modified,
|
||||
off_t file_length,
|
||||
@@ -888,14 +948,8 @@ int Http2Handler::submit_file_response(const std::string &status,
|
||||
if (content_type) {
|
||||
nva[nvlen++] = http2::make_nv_ls("content-type", *content_type);
|
||||
}
|
||||
auto &trailer = get_config()->trailer;
|
||||
std::string trailer_names;
|
||||
if (!trailer.empty()) {
|
||||
trailer_names = trailer[0].name;
|
||||
for (size_t i = 1; i < trailer.size(); ++i) {
|
||||
trailer_names += ", ";
|
||||
trailer_names += trailer[i].name;
|
||||
}
|
||||
auto trailer_names = make_trailer_header_value(get_config()->trailer);
|
||||
if (!trailer_names.empty()) {
|
||||
nva[nvlen++] = http2::make_nv_ls("trailer", trailer_names);
|
||||
}
|
||||
return nghttp2_submit_response(session_, stream->stream_id, nva.data(), nvlen,
|
||||
@@ -906,10 +960,19 @@ int Http2Handler::submit_response(const std::string &status, int32_t stream_id,
|
||||
const Headers &headers,
|
||||
nghttp2_data_provider *data_prd) {
|
||||
auto nva = std::vector<nghttp2_nv>();
|
||||
nva.reserve(3 + headers.size());
|
||||
nva.reserve(4 + headers.size());
|
||||
nva.push_back(http2::make_nv_ls(":status", status));
|
||||
nva.push_back(http2::make_nv_ll("server", NGHTTPD_SERVER));
|
||||
nva.push_back(http2::make_nv_ls("date", sessions_->get_cached_date()));
|
||||
|
||||
std::string trailer_names;
|
||||
if (data_prd) {
|
||||
trailer_names = make_trailer_header_value(get_config()->trailer);
|
||||
if (!trailer_names.empty()) {
|
||||
nva.push_back(http2::make_nv_ls("trailer", trailer_names));
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &nv : headers) {
|
||||
nva.push_back(http2::make_nv(nv.name, nv.value, nv.no_index));
|
||||
}
|
||||
@@ -920,11 +983,21 @@ int Http2Handler::submit_response(const std::string &status, int32_t stream_id,
|
||||
|
||||
int Http2Handler::submit_response(const std::string &status, int32_t stream_id,
|
||||
nghttp2_data_provider *data_prd) {
|
||||
auto nva =
|
||||
make_array(http2::make_nv_ls(":status", status),
|
||||
http2::make_nv_ll("server", NGHTTPD_SERVER),
|
||||
http2::make_nv_ls("date", sessions_->get_cached_date()));
|
||||
return nghttp2_submit_response(session_, stream_id, nva.data(), nva.size(),
|
||||
auto nva = make_array(http2::make_nv_ls(":status", status),
|
||||
http2::make_nv_ll("server", NGHTTPD_SERVER),
|
||||
http2::make_nv_ls("date", sessions_->get_cached_date()),
|
||||
http2::make_nv_ll("", ""));
|
||||
size_t nvlen = 3;
|
||||
|
||||
std::string trailer_names;
|
||||
if (data_prd) {
|
||||
trailer_names = make_trailer_header_value(get_config()->trailer);
|
||||
if (!trailer_names.empty()) {
|
||||
nva[nvlen++] = http2::make_nv_ls("trailer", trailer_names);
|
||||
}
|
||||
}
|
||||
|
||||
return nghttp2_submit_response(session_, stream_id, nva.data(), nvlen,
|
||||
data_prd);
|
||||
}
|
||||
|
||||
@@ -1316,6 +1389,13 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||
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);
|
||||
|
||||
http2::index_header(stream->hdidx, token, stream->headers.size());
|
||||
@@ -1457,6 +1537,15 @@ int hd_on_frame_send_callback(nghttp2_session *session,
|
||||
|
||||
break;
|
||||
}
|
||||
case NGHTTP2_SETTINGS: {
|
||||
if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
hd->start_settings_timer();
|
||||
|
||||
break;
|
||||
}
|
||||
case NGHTTP2_PUSH_PROMISE: {
|
||||
auto promised_stream_id = frame->push_promise.promised_stream_id;
|
||||
auto promised_stream = hd->get_stream(promised_stream_id);
|
||||
|
||||
@@ -67,6 +67,8 @@ struct Config {
|
||||
size_t num_worker;
|
||||
size_t max_concurrent_streams;
|
||||
ssize_t header_table_size;
|
||||
int window_bits;
|
||||
int connection_window_bits;
|
||||
uint16_t port;
|
||||
bool verbose;
|
||||
bool daemon;
|
||||
@@ -87,9 +89,16 @@ struct FileEntry {
|
||||
FileEntry(std::string path, int64_t length, int64_t mtime, int fd,
|
||||
const std::string *content_type, ev_tstamp last_valid,
|
||||
bool stale = false)
|
||||
: path(std::move(path)), length(length), mtime(mtime),
|
||||
last_valid(last_valid), content_type(content_type), dlnext(nullptr),
|
||||
dlprev(nullptr), fd(fd), usecount(1), stale(stale) {}
|
||||
: path(std::move(path)),
|
||||
length(length),
|
||||
mtime(mtime),
|
||||
last_valid(last_valid),
|
||||
content_type(content_type),
|
||||
dlnext(nullptr),
|
||||
dlprev(nullptr),
|
||||
fd(fd),
|
||||
usecount(1),
|
||||
stale(stale) {}
|
||||
std::string path;
|
||||
std::multimap<std::string, std::unique_ptr<FileEntry>>::iterator it;
|
||||
int64_t length;
|
||||
@@ -110,6 +119,9 @@ struct Stream {
|
||||
ev_timer wtimer;
|
||||
int64_t body_length;
|
||||
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;
|
||||
http2::HeaderIndex hdidx;
|
||||
bool echo_upload;
|
||||
@@ -125,7 +137,7 @@ public:
|
||||
~Http2Handler();
|
||||
|
||||
void remove_self();
|
||||
int setup_bev();
|
||||
void start_settings_timer();
|
||||
int on_read();
|
||||
int on_write();
|
||||
int connection_made();
|
||||
|
||||
@@ -27,7 +27,7 @@ check_PROGRAMS =
|
||||
TESTS =
|
||||
|
||||
AM_CFLAGS = $(WARNCFLAGS)
|
||||
AM_CXXFLAGS = $(WARNCXXFLAGS)
|
||||
AM_CXXFLAGS = $(WARNCXXFLAGS) $(CXX1XCXXFLAGS)
|
||||
AM_CPPFLAGS = \
|
||||
-DPKGDATADIR='"$(pkgdatadir)"' \
|
||||
-I$(top_srcdir)/lib/includes \
|
||||
@@ -62,7 +62,7 @@ HELPER_OBJECTS = util.cc \
|
||||
http2.cc timegm.c app_helper.cc nghttp2_gzip.c
|
||||
HELPER_HFILES = util.h \
|
||||
http2.h timegm.h app_helper.h nghttp2_config.h \
|
||||
nghttp2_gzip.h
|
||||
nghttp2_gzip.h network.h
|
||||
|
||||
HTML_PARSER_OBJECTS =
|
||||
HTML_PARSER_HFILES = HtmlParser.h
|
||||
@@ -176,7 +176,8 @@ nghttpx_unittest_SOURCES = shrpx-unittest.cc \
|
||||
nghttp2_gzip.c nghttp2_gzip.h \
|
||||
buffer_test.cc buffer_test.h \
|
||||
memchunk_test.cc memchunk_test.h \
|
||||
template_test.cc template_test.h
|
||||
template_test.cc template_test.h \
|
||||
base64_test.cc base64_test.h
|
||||
nghttpx_unittest_CPPFLAGS = ${AM_CPPFLAGS} \
|
||||
-DNGHTTP2_TESTS_DIR=\"$(top_srcdir)/tests\"
|
||||
nghttpx_unittest_LDADD = libnghttpx.a ${LDADD} @CUNIT_LIBS@ @TESTLDADD@
|
||||
|
||||
@@ -121,7 +121,7 @@ const char *strsettingsid(int32_t id) {
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
const char *strframetype(uint8_t type) {
|
||||
std::string strframetype(uint8_t type) {
|
||||
switch (type) {
|
||||
case NGHTTP2_DATA:
|
||||
return "DATA";
|
||||
@@ -141,9 +141,13 @@ const char *strframetype(uint8_t type) {
|
||||
return "GOAWAY";
|
||||
case NGHTTP2_WINDOW_UPDATE:
|
||||
return "WINDOW_UPDATE";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
std::string s = "extension(0x";
|
||||
s += util::format_hex(&type, 1);
|
||||
s += ')';
|
||||
|
||||
return s;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
@@ -280,7 +284,7 @@ const char *frame_name_ansi_esc(print_type ptype) {
|
||||
namespace {
|
||||
void print_frame(print_type ptype, const nghttp2_frame *frame) {
|
||||
fprintf(outfile, "%s%s%s frame ", frame_name_ansi_esc(ptype),
|
||||
strframetype(frame->hd.type), ansi_escend());
|
||||
strframetype(frame->hd.type).c_str(), ansi_escend());
|
||||
print_frame_hd(frame->hd);
|
||||
if (frame->hd.flags) {
|
||||
print_frame_attr_indent();
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
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) {
|
||||
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_; }
|
||||
|
||||
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 asio_http2
|
||||
} // namespace nghttp2
|
||||
|
||||
@@ -75,6 +75,9 @@ public:
|
||||
void method(std::string s);
|
||||
const std::string &method() const;
|
||||
|
||||
size_t header_buffer_size() const;
|
||||
void update_header_buffer_size(size_t len);
|
||||
|
||||
private:
|
||||
header_map header_;
|
||||
response_cb response_cb_;
|
||||
@@ -84,6 +87,7 @@ private:
|
||||
class stream *strm_;
|
||||
uri_ref uri_;
|
||||
std::string method_;
|
||||
size_t header_buffer_size_;
|
||||
};
|
||||
|
||||
} // namespace client
|
||||
|
||||
@@ -30,7 +30,8 @@ namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
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); }
|
||||
|
||||
@@ -52,6 +53,12 @@ header_map &response_impl::header() { 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 asio_http2
|
||||
} // namespace nghttp2
|
||||
|
||||
@@ -53,12 +53,16 @@ public:
|
||||
header_map &header();
|
||||
const header_map &header() const;
|
||||
|
||||
size_t header_buffer_size() const;
|
||||
void update_header_buffer_size(size_t len);
|
||||
|
||||
private:
|
||||
data_cb data_cb_;
|
||||
|
||||
header_map header_;
|
||||
|
||||
int64_t content_length_;
|
||||
size_t header_buffer_size_;
|
||||
int status_code_;
|
||||
};
|
||||
|
||||
|
||||
@@ -39,15 +39,33 @@ using boost::asio::ip::tcp;
|
||||
|
||||
session::session(boost::asio::io_service &io_service, const std::string &host,
|
||||
const std::string &service)
|
||||
: impl_(std::make_shared<session_tcp_impl>(io_service, host, service)) {
|
||||
: impl_(std::make_shared<session_tcp_impl>(
|
||||
io_service, host, service, boost::posix_time::seconds(60))) {
|
||||
impl_->start_resolve(host, service);
|
||||
}
|
||||
|
||||
session::session(boost::asio::io_service &io_service, const std::string &host,
|
||||
const std::string &service,
|
||||
const boost::posix_time::time_duration &connect_timeout)
|
||||
: impl_(std::make_shared<session_tcp_impl>(io_service, host, service,
|
||||
connect_timeout)) {
|
||||
impl_->start_resolve(host, service);
|
||||
}
|
||||
|
||||
session::session(boost::asio::io_service &io_service,
|
||||
boost::asio::ssl::context &tls_ctx, const std::string &host,
|
||||
const std::string &service)
|
||||
: impl_(std::make_shared<session_tls_impl>(
|
||||
io_service, tls_ctx, host, service, boost::posix_time::seconds(60))) {
|
||||
impl_->start_resolve(host, service);
|
||||
}
|
||||
|
||||
session::session(boost::asio::io_service &io_service,
|
||||
boost::asio::ssl::context &tls_ctx, const std::string &host,
|
||||
const std::string &service,
|
||||
const boost::posix_time::time_duration &connect_timeout)
|
||||
: impl_(std::make_shared<session_tls_impl>(io_service, tls_ctx, host,
|
||||
service)) {
|
||||
service, connect_timeout)) {
|
||||
impl_->start_resolve(host, service);
|
||||
}
|
||||
|
||||
@@ -97,10 +115,6 @@ const request *session::submit(boost::system::error_code &ec,
|
||||
return impl_->submit(ec, method, uri, std::move(cb), std::move(h));
|
||||
}
|
||||
|
||||
void session::connect_timeout(const boost::posix_time::time_duration &t) {
|
||||
impl_->connect_timeout(t);
|
||||
}
|
||||
|
||||
void session::read_timeout(const boost::posix_time::time_duration &t) {
|
||||
impl_->read_timeout(t);
|
||||
}
|
||||
|
||||
@@ -38,12 +38,21 @@ namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace client {
|
||||
|
||||
session_impl::session_impl(boost::asio::io_service &io_service)
|
||||
: wblen_(0), io_service_(io_service), resolver_(io_service),
|
||||
deadline_(io_service), connect_timeout_(boost::posix_time::seconds(60)),
|
||||
read_timeout_(boost::posix_time::seconds(60)), session_(nullptr),
|
||||
data_pending_(nullptr), data_pendinglen_(0), writing_(false),
|
||||
inside_callback_(false), stopped_(false) {}
|
||||
session_impl::session_impl(
|
||||
boost::asio::io_service &io_service,
|
||||
const boost::posix_time::time_duration &connect_timeout)
|
||||
: wblen_(0),
|
||||
io_service_(io_service),
|
||||
resolver_(io_service),
|
||||
deadline_(io_service),
|
||||
connect_timeout_(connect_timeout),
|
||||
read_timeout_(boost::posix_time::seconds(60)),
|
||||
session_(nullptr),
|
||||
data_pending_(nullptr),
|
||||
data_pendinglen_(0),
|
||||
writing_(false),
|
||||
inside_callback_(false),
|
||||
stopped_(false) {}
|
||||
|
||||
session_impl::~session_impl() {
|
||||
// finish up all active stream
|
||||
@@ -174,6 +183,12 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||
if (token == http2::HD__STATUS) {
|
||||
res.status_code(util::parse_uint(value, valuelen));
|
||||
} 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) {
|
||||
res.content_length(util::parse_uint(value, valuelen));
|
||||
@@ -214,6 +229,13 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||
}
|
||||
// fall through
|
||||
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),
|
||||
header_value{std::string(value, value + valuelen),
|
||||
@@ -698,9 +720,7 @@ void session_impl::stop() {
|
||||
stopped_ = true;
|
||||
}
|
||||
|
||||
void session_impl::connect_timeout(const boost::posix_time::time_duration &t) {
|
||||
connect_timeout_ = t;
|
||||
}
|
||||
bool session_impl::stopped() const { return stopped_; }
|
||||
|
||||
void session_impl::read_timeout(const boost::posix_time::time_duration &t) {
|
||||
read_timeout_ = t;
|
||||
|
||||
@@ -43,7 +43,8 @@ using boost::asio::ip::tcp;
|
||||
|
||||
class session_impl : public std::enable_shared_from_this<session_impl> {
|
||||
public:
|
||||
session_impl(boost::asio::io_service &io_service);
|
||||
session_impl(boost::asio::io_service &io_service,
|
||||
const boost::posix_time::time_duration &connect_timeout);
|
||||
virtual ~session_impl();
|
||||
|
||||
void start_resolve(const std::string &host, const std::string &service);
|
||||
@@ -91,10 +92,10 @@ public:
|
||||
void do_read();
|
||||
void do_write();
|
||||
|
||||
void connect_timeout(const boost::posix_time::time_duration &t);
|
||||
void read_timeout(const boost::posix_time::time_duration &t);
|
||||
|
||||
void stop();
|
||||
bool stopped() const;
|
||||
|
||||
protected:
|
||||
boost::array<uint8_t, 8_k> rb_;
|
||||
|
||||
@@ -28,10 +28,11 @@ namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace client {
|
||||
|
||||
session_tcp_impl::session_tcp_impl(boost::asio::io_service &io_service,
|
||||
const std::string &host,
|
||||
const std::string &service)
|
||||
: session_impl(io_service), socket_(io_service) {}
|
||||
session_tcp_impl::session_tcp_impl(
|
||||
boost::asio::io_service &io_service, const std::string &host,
|
||||
const std::string &service,
|
||||
const boost::posix_time::time_duration &connect_timeout)
|
||||
: session_impl(io_service, connect_timeout), socket_(io_service) {}
|
||||
|
||||
session_tcp_impl::~session_tcp_impl() {}
|
||||
|
||||
@@ -39,6 +40,10 @@ void session_tcp_impl::start_connect(tcp::resolver::iterator endpoint_it) {
|
||||
boost::asio::async_connect(socket_, endpoint_it,
|
||||
[this](const boost::system::error_code &ec,
|
||||
tcp::resolver::iterator endpoint_it) {
|
||||
if (stopped()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ec) {
|
||||
not_connected(ec);
|
||||
return;
|
||||
|
||||
@@ -38,7 +38,8 @@ using boost::asio::ip::tcp;
|
||||
class session_tcp_impl : public session_impl {
|
||||
public:
|
||||
session_tcp_impl(boost::asio::io_service &io_service, const std::string &host,
|
||||
const std::string &service);
|
||||
const std::string &service,
|
||||
const boost::posix_time::time_duration &connect_timeout);
|
||||
virtual ~session_tcp_impl();
|
||||
|
||||
virtual void start_connect(tcp::resolver::iterator endpoint_it);
|
||||
|
||||
@@ -29,11 +29,11 @@ namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace client {
|
||||
|
||||
session_tls_impl::session_tls_impl(boost::asio::io_service &io_service,
|
||||
boost::asio::ssl::context &tls_ctx,
|
||||
const std::string &host,
|
||||
const std::string &service)
|
||||
: session_impl(io_service), socket_(io_service, tls_ctx) {
|
||||
session_tls_impl::session_tls_impl(
|
||||
boost::asio::io_service &io_service, boost::asio::ssl::context &tls_ctx,
|
||||
const std::string &host, const std::string &service,
|
||||
const boost::posix_time::time_duration &connect_timeout)
|
||||
: session_impl(io_service, connect_timeout), socket_(io_service, tls_ctx) {
|
||||
// this callback setting is no effect is
|
||||
// ssl::context::set_verify_mode(boost::asio::ssl::verify_peer) is
|
||||
// not used, which is what we want.
|
||||
@@ -46,6 +46,10 @@ void session_tls_impl::start_connect(tcp::resolver::iterator endpoint_it) {
|
||||
boost::asio::async_connect(
|
||||
socket(), endpoint_it, [this](const boost::system::error_code &ec,
|
||||
tcp::resolver::iterator endpoint_it) {
|
||||
if (stopped()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ec) {
|
||||
not_connected(ec);
|
||||
return;
|
||||
@@ -54,6 +58,10 @@ void session_tls_impl::start_connect(tcp::resolver::iterator endpoint_it) {
|
||||
socket_.async_handshake(
|
||||
boost::asio::ssl::stream_base::client,
|
||||
[this, endpoint_it](const boost::system::error_code &ec) {
|
||||
if (stopped()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ec) {
|
||||
not_connected(ec);
|
||||
return;
|
||||
|
||||
@@ -41,7 +41,8 @@ class session_tls_impl : public session_impl {
|
||||
public:
|
||||
session_tls_impl(boost::asio::io_service &io_service,
|
||||
boost::asio::ssl::context &tls_ctx, const std::string &host,
|
||||
const std::string &service);
|
||||
const std::string &service,
|
||||
const boost::posix_time::time_duration &connect_timeout);
|
||||
virtual ~session_tls_impl();
|
||||
|
||||
virtual void start_connect(tcp::resolver::iterator endpoint_it);
|
||||
|
||||
@@ -69,10 +69,13 @@ public:
|
||||
const boost::posix_time::time_duration &tls_handshake_timeout,
|
||||
const boost::posix_time::time_duration &read_timeout,
|
||||
SocketArgs &&... args)
|
||||
: socket_(std::forward<SocketArgs>(args)...), mux_(mux),
|
||||
: socket_(std::forward<SocketArgs>(args)...),
|
||||
mux_(mux),
|
||||
deadline_(socket_.get_io_service()),
|
||||
tls_handshake_timeout_(tls_handshake_timeout),
|
||||
read_timeout_(read_timeout), writing_(false), stopped_(false) {}
|
||||
read_timeout_(read_timeout),
|
||||
writing_(false),
|
||||
stopped_(false) {}
|
||||
|
||||
/// Start the first asynchronous operation for the connection.
|
||||
void start() {
|
||||
|
||||
@@ -105,6 +105,13 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||
}
|
||||
// fall through
|
||||
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),
|
||||
header_value{std::string(value, value + valuelen),
|
||||
(flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0});
|
||||
@@ -230,8 +237,14 @@ int on_frame_not_send_callback(nghttp2_session *session,
|
||||
http2_handler::http2_handler(boost::asio::io_service &io_service,
|
||||
boost::asio::ip::tcp::endpoint ep,
|
||||
connection_write writefun, serve_mux &mux)
|
||||
: writefun_(writefun), mux_(mux), io_service_(io_service), remote_ep_(ep),
|
||||
session_(nullptr), buf_(nullptr), buflen_(0), inside_callback_(false),
|
||||
: writefun_(writefun),
|
||||
mux_(mux),
|
||||
io_service_(io_service),
|
||||
remote_ep_(ep),
|
||||
session_(nullptr),
|
||||
buf_(nullptr),
|
||||
buflen_(0),
|
||||
inside_callback_(false),
|
||||
tstamp_cached_(time(nullptr)),
|
||||
formatted_date_(util::http_date(tstamp_cached_)) {}
|
||||
|
||||
|
||||
@@ -38,7 +38,8 @@ namespace asio_http2 {
|
||||
namespace server {
|
||||
|
||||
http2_impl::http2_impl()
|
||||
: num_threads_(1), backlog_(-1),
|
||||
: num_threads_(1),
|
||||
backlog_(-1),
|
||||
tls_handshake_timeout_(boost::posix_time::seconds(60)),
|
||||
read_timeout_(boost::posix_time::seconds(60)) {}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
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_; }
|
||||
|
||||
@@ -62,6 +62,12 @@ void request_impl::remote_endpoint(boost::asio::ip::tcp::endpoint 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 asio_http2
|
||||
} // namespace nghttp2
|
||||
|
||||
@@ -58,6 +58,9 @@ public:
|
||||
const boost::asio::ip::tcp::endpoint &remote_endpoint() const;
|
||||
void remote_endpoint(boost::asio::ip::tcp::endpoint ep);
|
||||
|
||||
size_t header_buffer_size() const;
|
||||
void update_header_buffer_size(size_t len);
|
||||
|
||||
private:
|
||||
class stream *strm_;
|
||||
header_map header_;
|
||||
@@ -65,6 +68,7 @@ private:
|
||||
uri_ref uri_;
|
||||
data_cb on_data_cb_;
|
||||
boost::asio::ip::tcp::endpoint remote_ep_;
|
||||
size_t header_buffer_size_;
|
||||
};
|
||||
|
||||
} // namespace server
|
||||
|
||||
@@ -36,8 +36,11 @@ namespace asio_http2 {
|
||||
namespace server {
|
||||
|
||||
response_impl::response_impl()
|
||||
: strm_(nullptr), generator_cb_(deferred_generator()), status_code_(200),
|
||||
state_(response_state::INITIAL), pushed_(false),
|
||||
: strm_(nullptr),
|
||||
generator_cb_(deferred_generator()),
|
||||
status_code_(200),
|
||||
state_(response_state::INITIAL),
|
||||
pushed_(false),
|
||||
push_promise_sent_(false) {}
|
||||
|
||||
unsigned int response_impl::status_code() const { return status_code_; }
|
||||
|
||||
141
src/base64.h
141
src/base64.h
@@ -33,9 +33,8 @@ namespace nghttp2 {
|
||||
|
||||
namespace base64 {
|
||||
|
||||
template <typename InputIterator>
|
||||
std::string encode(InputIterator first, InputIterator last) {
|
||||
static const char CHAR_TABLE[] = {
|
||||
template <typename InputIt> std::string encode(InputIt first, InputIt last) {
|
||||
static constexpr char CHAR_TABLE[] = {
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
|
||||
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
|
||||
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
|
||||
@@ -48,39 +47,38 @@ std::string encode(InputIterator first, InputIterator last) {
|
||||
return res;
|
||||
}
|
||||
size_t r = len % 3;
|
||||
InputIterator j = last - r;
|
||||
char temp[4];
|
||||
res.resize((len + 2) / 3 * 4);
|
||||
auto j = last - r;
|
||||
auto p = std::begin(res);
|
||||
while (first != j) {
|
||||
int n = static_cast<unsigned char>(*first++) << 16;
|
||||
n += static_cast<unsigned char>(*first++) << 8;
|
||||
n += static_cast<unsigned char>(*first++);
|
||||
temp[0] = CHAR_TABLE[n >> 18];
|
||||
temp[1] = CHAR_TABLE[(n >> 12) & 0x3fu];
|
||||
temp[2] = CHAR_TABLE[(n >> 6) & 0x3fu];
|
||||
temp[3] = CHAR_TABLE[n & 0x3fu];
|
||||
res.append(temp, sizeof(temp));
|
||||
uint32_t n = static_cast<uint8_t>(*first++) << 16;
|
||||
n += static_cast<uint8_t>(*first++) << 8;
|
||||
n += static_cast<uint8_t>(*first++);
|
||||
*p++ = CHAR_TABLE[n >> 18];
|
||||
*p++ = CHAR_TABLE[(n >> 12) & 0x3fu];
|
||||
*p++ = CHAR_TABLE[(n >> 6) & 0x3fu];
|
||||
*p++ = CHAR_TABLE[n & 0x3fu];
|
||||
}
|
||||
|
||||
if (r == 2) {
|
||||
int n = static_cast<unsigned char>(*first++) << 16;
|
||||
n += static_cast<unsigned char>(*first++) << 8;
|
||||
temp[0] = CHAR_TABLE[n >> 18];
|
||||
temp[1] = CHAR_TABLE[(n >> 12) & 0x3fu];
|
||||
temp[2] = CHAR_TABLE[(n >> 6) & 0x3fu];
|
||||
temp[3] = '=';
|
||||
res.append(temp, sizeof(temp));
|
||||
uint32_t n = static_cast<uint8_t>(*first++) << 16;
|
||||
n += static_cast<uint8_t>(*first++) << 8;
|
||||
*p++ = CHAR_TABLE[n >> 18];
|
||||
*p++ = CHAR_TABLE[(n >> 12) & 0x3fu];
|
||||
*p++ = CHAR_TABLE[(n >> 6) & 0x3fu];
|
||||
*p++ = '=';
|
||||
} else if (r == 1) {
|
||||
int n = static_cast<unsigned char>(*first++) << 16;
|
||||
temp[0] = CHAR_TABLE[n >> 18];
|
||||
temp[1] = CHAR_TABLE[(n >> 12) & 0x3fu];
|
||||
temp[2] = '=';
|
||||
temp[3] = '=';
|
||||
res.append(temp, sizeof(temp));
|
||||
uint32_t n = static_cast<uint8_t>(*first++) << 16;
|
||||
*p++ = CHAR_TABLE[n >> 18];
|
||||
*p++ = CHAR_TABLE[(n >> 12) & 0x3fu];
|
||||
*p++ = '=';
|
||||
*p++ = '=';
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename InputIterator>
|
||||
InputIterator getNext(InputIterator first, InputIterator last, const int *tbl) {
|
||||
template <typename InputIt>
|
||||
InputIt next_decode_input(InputIt first, InputIt last, const int *tbl) {
|
||||
for (; first != last; ++first) {
|
||||
if (tbl[static_cast<size_t>(*first)] != -1 || *first == '=') {
|
||||
break;
|
||||
@@ -89,9 +87,8 @@ InputIterator getNext(InputIterator first, InputIterator last, const int *tbl) {
|
||||
return first;
|
||||
}
|
||||
|
||||
template <typename InputIterator>
|
||||
std::string decode(InputIterator first, InputIterator last) {
|
||||
static const int INDEX_TABLE[] = {
|
||||
template <typename InputIt> std::string decode(InputIt first, InputIt last) {
|
||||
static constexpr int INDEX_TABLE[] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57,
|
||||
@@ -107,59 +104,47 @@ std::string decode(InputIterator first, InputIterator last) {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1};
|
||||
std::string res;
|
||||
InputIterator k[4];
|
||||
int eq = 0;
|
||||
for (; first != last;) {
|
||||
for (int i = 1; i <= 4; ++i) {
|
||||
k[i - 1] = getNext(first, last, INDEX_TABLE);
|
||||
if (k[i - 1] == last) {
|
||||
// If i == 1, input may look like this: "TWFu\n" (i.e.,
|
||||
// garbage at the end)
|
||||
if (i != 1) {
|
||||
res.clear();
|
||||
}
|
||||
return res;
|
||||
} else if (*k[i - 1] == '=' && eq == 0) {
|
||||
eq = i;
|
||||
}
|
||||
first = k[i - 1] + 1;
|
||||
}
|
||||
if (eq) {
|
||||
break;
|
||||
}
|
||||
int n = (INDEX_TABLE[static_cast<unsigned char>(*k[0])] << 18) +
|
||||
(INDEX_TABLE[static_cast<unsigned char>(*k[1])] << 12) +
|
||||
(INDEX_TABLE[static_cast<unsigned char>(*k[2])] << 6) +
|
||||
INDEX_TABLE[static_cast<unsigned char>(*k[3])];
|
||||
res += n >> 16;
|
||||
res += n >> 8 & 0xffu;
|
||||
res += n & 0xffu;
|
||||
auto len = last - first;
|
||||
if (len % 4 != 0) {
|
||||
return "";
|
||||
}
|
||||
if (eq) {
|
||||
if (eq <= 2) {
|
||||
res.clear();
|
||||
return res;
|
||||
} else {
|
||||
for (int i = eq; i <= 4; ++i) {
|
||||
if (*k[i - 1] != '=') {
|
||||
res.clear();
|
||||
std::string res;
|
||||
res.resize(len / 4 * 3);
|
||||
|
||||
auto p = std::begin(res);
|
||||
for (; first != last;) {
|
||||
uint32_t n = 0;
|
||||
for (int i = 1; i <= 4; ++i, ++first) {
|
||||
auto idx = INDEX_TABLE[static_cast<size_t>(*first)];
|
||||
if (idx == -1) {
|
||||
if (i <= 2) {
|
||||
return "";
|
||||
}
|
||||
if (i == 3) {
|
||||
if (*first == '=' && *(first + 1) == '=' && first + 2 == last) {
|
||||
*p++ = n >> 16;
|
||||
res.resize(p - std::begin(res));
|
||||
return res;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
if (*first == '=' && first + 1 == last) {
|
||||
*p++ = n >> 16;
|
||||
*p++ = n >> 8 & 0xffu;
|
||||
res.resize(p - std::begin(res));
|
||||
return res;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
if (eq == 3) {
|
||||
int n = (INDEX_TABLE[static_cast<unsigned char>(*k[0])] << 18) +
|
||||
(INDEX_TABLE[static_cast<unsigned char>(*k[1])] << 12);
|
||||
res += n >> 16;
|
||||
} else if (eq == 4) {
|
||||
int n = (INDEX_TABLE[static_cast<unsigned char>(*k[0])] << 18) +
|
||||
(INDEX_TABLE[static_cast<unsigned char>(*k[1])] << 12) +
|
||||
(INDEX_TABLE[static_cast<unsigned char>(*k[2])] << 6);
|
||||
res += n >> 16;
|
||||
res += n >> 8 & 0xffu;
|
||||
}
|
||||
|
||||
n += idx << (24 - i * 6);
|
||||
}
|
||||
|
||||
*p++ = n >> 16;
|
||||
*p++ = n >> 8 & 0xffu;
|
||||
*p++ = n & 0xffu;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
109
src/base64_test.cc
Normal file
109
src/base64_test.cc
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#include "base64_test.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
#include <CUnit/CUnit.h>
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
#include "base64.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
void test_base64_encode(void) {
|
||||
{
|
||||
std::string in = "\xff";
|
||||
auto out = base64::encode(std::begin(in), std::end(in));
|
||||
CU_ASSERT("/w==" == out);
|
||||
}
|
||||
{
|
||||
std::string in = "\xff\xfe";
|
||||
auto out = base64::encode(std::begin(in), std::end(in));
|
||||
CU_ASSERT("//4=" == out);
|
||||
}
|
||||
{
|
||||
std::string in = "\xff\xfe\xfd";
|
||||
auto out = base64::encode(std::begin(in), std::end(in));
|
||||
CU_ASSERT("//79" == out);
|
||||
}
|
||||
{
|
||||
std::string in = "\xff\xfe\xfd\xfc";
|
||||
auto out = base64::encode(std::begin(in), std::end(in));
|
||||
CU_ASSERT("//79/A==" == out);
|
||||
}
|
||||
}
|
||||
|
||||
void test_base64_decode(void) {
|
||||
{
|
||||
std::string in = "/w==";
|
||||
auto out = base64::decode(std::begin(in), std::end(in));
|
||||
CU_ASSERT("\xff" == out);
|
||||
}
|
||||
{
|
||||
std::string in = "//4=";
|
||||
auto out = base64::decode(std::begin(in), std::end(in));
|
||||
CU_ASSERT("\xff\xfe" == out);
|
||||
}
|
||||
{
|
||||
std::string in = "//79";
|
||||
auto out = base64::decode(std::begin(in), std::end(in));
|
||||
CU_ASSERT("\xff\xfe\xfd" == out);
|
||||
}
|
||||
{
|
||||
std::string in = "//79/A==";
|
||||
auto out = base64::decode(std::begin(in), std::end(in));
|
||||
CU_ASSERT("\xff\xfe\xfd\xfc" == out);
|
||||
}
|
||||
{
|
||||
// we check the number of valid input must be multiples of 4
|
||||
std::string in = "//79=";
|
||||
auto out = base64::decode(std::begin(in), std::end(in));
|
||||
CU_ASSERT("" == out);
|
||||
}
|
||||
{
|
||||
// ending invalid character at the boundary of multiples of 4 is
|
||||
// bad
|
||||
std::string in = "bmdodHRw\n";
|
||||
auto out = base64::decode(std::begin(in), std::end(in));
|
||||
CU_ASSERT("" == out);
|
||||
}
|
||||
{
|
||||
// after seeing '=', subsequent input must be also '='.
|
||||
std::string in = "//79/A=A";
|
||||
auto out = base64::decode(std::begin(in), std::end(in));
|
||||
CU_ASSERT("" == out);
|
||||
}
|
||||
{
|
||||
// additional '=' at the end is bad
|
||||
std::string in = "//79/A======";
|
||||
auto out = base64::decode(std::begin(in), std::end(in));
|
||||
CU_ASSERT("" == out);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace nghttp2
|
||||
39
src/base64_test.h
Normal file
39
src/base64_test.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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 BASE64_TEST_H
|
||||
#define BASE64_TEST_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif // HAVE_CONFIG_H
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
void test_base64_encode(void);
|
||||
void test_base64_decode(void);
|
||||
|
||||
} // namespace nghttp2
|
||||
|
||||
#endif // BASE64_TEST_H
|
||||
@@ -79,11 +79,26 @@ bool recorded(const std::chrono::steady_clock::time_point &t) {
|
||||
} // namespace
|
||||
|
||||
Config::Config()
|
||||
: data_length(-1), addrs(nullptr), nreqs(1), nclients(1), nthreads(1),
|
||||
max_concurrent_streams(-1), window_bits(30), connection_window_bits(30),
|
||||
rate(0), rate_period(1.0), conn_active_timeout(0.),
|
||||
conn_inactivity_timeout(0.), no_tls_proto(PROTO_HTTP2), data_fd(-1),
|
||||
port(0), default_port(0), verbose(false), timing_script(false) {}
|
||||
: data_length(-1),
|
||||
addrs(nullptr),
|
||||
nreqs(1),
|
||||
nclients(1),
|
||||
nthreads(1),
|
||||
max_concurrent_streams(-1),
|
||||
window_bits(30),
|
||||
connection_window_bits(30),
|
||||
rate(0),
|
||||
rate_period(1.0),
|
||||
conn_active_timeout(0.),
|
||||
conn_inactivity_timeout(0.),
|
||||
no_tls_proto(PROTO_HTTP2),
|
||||
data_fd(-1),
|
||||
port(0),
|
||||
default_port(0),
|
||||
verbose(false),
|
||||
timing_script(false),
|
||||
base_uri_unix(false),
|
||||
unix_addr{} {}
|
||||
|
||||
Config::~Config() {
|
||||
if (base_uri_unix) {
|
||||
@@ -106,9 +121,18 @@ constexpr size_t MAX_SAMPLES = 1000000;
|
||||
} // namespace
|
||||
|
||||
Stats::Stats(size_t req_todo, size_t nclients)
|
||||
: req_todo(req_todo), req_started(0), req_done(0), req_success(0),
|
||||
req_status_success(0), req_failed(0), req_error(0), req_timedout(0),
|
||||
bytes_total(0), bytes_head(0), bytes_head_decomp(0), bytes_body(0),
|
||||
: req_todo(req_todo),
|
||||
req_started(0),
|
||||
req_done(0),
|
||||
req_success(0),
|
||||
req_status_success(0),
|
||||
req_failed(0),
|
||||
req_error(0),
|
||||
req_timedout(0),
|
||||
bytes_total(0),
|
||||
bytes_head(0),
|
||||
bytes_head_decomp(0),
|
||||
bytes_body(0),
|
||||
status() {}
|
||||
|
||||
Stream::Stream() : req_stat{}, status_success(-1) {}
|
||||
@@ -290,9 +314,18 @@ void client_request_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
|
||||
} // namespace
|
||||
|
||||
Client::Client(uint32_t id, Worker *worker, size_t req_todo)
|
||||
: cstat{}, worker(worker), ssl(nullptr), next_addr(config.addrs),
|
||||
current_addr(nullptr), reqidx(0), state(CLIENT_IDLE), req_todo(req_todo),
|
||||
req_started(0), req_done(0), id(id), fd(-1),
|
||||
: cstat{},
|
||||
worker(worker),
|
||||
ssl(nullptr),
|
||||
next_addr(config.addrs),
|
||||
current_addr(nullptr),
|
||||
reqidx(0),
|
||||
state(CLIENT_IDLE),
|
||||
req_todo(req_todo),
|
||||
req_started(0),
|
||||
req_done(0),
|
||||
id(id),
|
||||
fd(-1),
|
||||
new_connection_requested(false) {
|
||||
ev_io_init(&wev, writecb, 0, EV_WRITE);
|
||||
ev_io_init(&rev, readcb, 0, EV_READ);
|
||||
@@ -1094,11 +1127,20 @@ void Client::try_new_connection() { new_connection_requested = true; }
|
||||
|
||||
Worker::Worker(uint32_t id, SSL_CTX *ssl_ctx, size_t req_todo, size_t nclients,
|
||||
size_t rate, size_t max_samples, Config *config)
|
||||
: stats(req_todo, nclients), loop(ev_loop_new(0)), ssl_ctx(ssl_ctx),
|
||||
config(config), id(id), tls_info_report_done(false),
|
||||
app_info_report_done(false), nconns_made(0), nclients(nclients),
|
||||
nreqs_per_client(req_todo / nclients), nreqs_rem(req_todo % nclients),
|
||||
rate(rate), max_samples(max_samples), next_client_id(0) {
|
||||
: stats(req_todo, nclients),
|
||||
loop(ev_loop_new(0)),
|
||||
ssl_ctx(ssl_ctx),
|
||||
config(config),
|
||||
id(id),
|
||||
tls_info_report_done(false),
|
||||
app_info_report_done(false),
|
||||
nconns_made(0),
|
||||
nclients(nclients),
|
||||
nreqs_per_client(req_todo / nclients),
|
||||
nreqs_rem(req_todo % nclients),
|
||||
rate(rate),
|
||||
max_samples(max_samples),
|
||||
next_client_id(0) {
|
||||
if (!config->is_rate_mode()) {
|
||||
progress_interval = std::max(static_cast<size_t>(1), req_todo / 10);
|
||||
} else {
|
||||
|
||||
@@ -41,7 +41,10 @@ using namespace nghttp2;
|
||||
namespace h2load {
|
||||
|
||||
Http1Session::Http1Session(Client *client)
|
||||
: stream_req_counter_(1), stream_resp_counter_(1), client_(client), htp_(),
|
||||
: stream_req_counter_(1),
|
||||
stream_resp_counter_(1),
|
||||
client_(client),
|
||||
htp_(),
|
||||
complete_(false) {
|
||||
http_parser_init(&htp_, HTTP_RESPONSE);
|
||||
htp_.data = this;
|
||||
|
||||
55
src/http2.cc
55
src/http2.cc
@@ -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,
|
||||
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),
|
||||
std::string(reinterpret_cast<const char *>(value), valuelen),
|
||||
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,
|
||||
const uint8_t *value, size_t valuelen, bool no_index,
|
||||
int16_t token) {
|
||||
int32_t token) {
|
||||
if (valuelen > 0) {
|
||||
size_t i, j;
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
@@ -768,52 +768,7 @@ void index_header(HeaderIndex &hdidx, int16_t token, size_t idx) {
|
||||
hdidx[token] = idx;
|
||||
}
|
||||
|
||||
bool check_http2_request_pseudo_header(const HeaderIndex &hdidx,
|
||||
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::value_type *get_header(const HeaderIndex &hdidx, int32_t token,
|
||||
const Headers &nva) {
|
||||
auto i = hdidx[token];
|
||||
if (i == -1) {
|
||||
@@ -822,7 +777,7 @@ const Headers::value_type *get_header(const HeaderIndex &hdidx, int16_t token,
|
||||
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) {
|
||||
auto i = hdidx[token];
|
||||
if (i == -1) {
|
||||
|
||||
35
src/http2.h
35
src/http2.h
@@ -44,8 +44,10 @@ namespace nghttp2 {
|
||||
|
||||
struct Header {
|
||||
Header(std::string name, std::string value, bool no_index = false,
|
||||
int16_t token = -1)
|
||||
: name(std::move(name)), value(std::move(value)), token(token),
|
||||
int32_t token = -1)
|
||||
: name(std::move(name)),
|
||||
value(std::move(value)),
|
||||
token(token),
|
||||
no_index(no_index) {}
|
||||
|
||||
Header() : token(-1), no_index(false) {}
|
||||
@@ -60,7 +62,7 @@ struct Header {
|
||||
|
||||
std::string name;
|
||||
std::string value;
|
||||
int16_t token;
|
||||
int32_t token;
|
||||
bool no_index;
|
||||
};
|
||||
|
||||
@@ -87,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,
|
||||
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
|
||||
// name/value pair won't be indexed when it is forwarded to the next
|
||||
// hop. This function strips white spaces around |value|.
|
||||
void add_header(Headers &nva, const uint8_t *name, size_t namelen,
|
||||
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
|
||||
// more than one entries which have the name |name|, last occurrence
|
||||
@@ -275,30 +277,13 @@ int lookup_token(const std::string &name);
|
||||
// array containing at least HD_MAXIDX elements.
|
||||
void init_hdidx(HeaderIndex &hdidx);
|
||||
// Indexes header |token| using index |idx|.
|
||||
void index_header(HeaderIndex &hdidx, int16_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);
|
||||
void index_header(HeaderIndex &hdidx, int32_t token, size_t idx);
|
||||
|
||||
// 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);
|
||||
|
||||
Headers::value_type *get_header(const HeaderIndex &hdidx, int16_t token,
|
||||
Headers::value_type *get_header(const HeaderIndex &hdidx, int32_t token,
|
||||
Headers &nva);
|
||||
|
||||
struct LinkHeader {
|
||||
|
||||
@@ -271,53 +271,6 @@ void test_http2_lookup_token(void) {
|
||||
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) {
|
||||
{
|
||||
// only URI appears; we don't extract URI unless it bears rel=preload
|
||||
|
||||
@@ -40,9 +40,6 @@ void test_http2_rewrite_location_uri(void);
|
||||
void test_http2_parse_http_status_code(void);
|
||||
void test_http2_index_header(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_path_join(void);
|
||||
void test_http2_normalize_path(void);
|
||||
|
||||
@@ -123,15 +123,33 @@ class session_impl;
|
||||
class session {
|
||||
public:
|
||||
// Starts HTTP/2 session by connecting to |host| and |service|
|
||||
// (e.g., "80") using clear text TCP connection.
|
||||
// (e.g., "80") using clear text TCP connection with connect timeout
|
||||
// 60 seconds.
|
||||
session(boost::asio::io_service &io_service, const std::string &host,
|
||||
const std::string &service);
|
||||
|
||||
// Starts HTTP/2 session by connecting to |host| and |service|
|
||||
// (e.g., "443") using encrypted SSL/TLS connection.
|
||||
// (e.g., "80") using clear text TCP connection with given connect
|
||||
// timeout.
|
||||
session(boost::asio::io_service &io_service, const std::string &host,
|
||||
const std::string &service,
|
||||
const boost::posix_time::time_duration &connect_timeout);
|
||||
|
||||
// Starts HTTP/2 session by connecting to |host| and |service|
|
||||
// (e.g., "443") using encrypted SSL/TLS connection with connect
|
||||
// timeout 60 seconds.
|
||||
session(boost::asio::io_service &io_service,
|
||||
boost::asio::ssl::context &tls_context, const std::string &host,
|
||||
const std::string &service);
|
||||
|
||||
// Starts HTTP/2 session by connecting to |host| and |service|
|
||||
// (e.g., "443") using encrypted SSL/TLS connection with given
|
||||
// connect timeout.
|
||||
session(boost::asio::io_service &io_service,
|
||||
boost::asio::ssl::context &tls_context, const std::string &host,
|
||||
const std::string &service,
|
||||
const boost::posix_time::time_duration &connect_timeout);
|
||||
|
||||
~session();
|
||||
|
||||
session(session &&other) noexcept;
|
||||
@@ -144,9 +162,6 @@ public:
|
||||
// and session is terminated.
|
||||
void on_error(error_cb cb) const;
|
||||
|
||||
// Sets connect timeout, which defaults to 60 seconds.
|
||||
void connect_timeout(const boost::posix_time::time_duration &t);
|
||||
|
||||
// Sets read timeout, which defaults to 60 seconds.
|
||||
void read_timeout(const boost::posix_time::time_duration &t);
|
||||
|
||||
|
||||
@@ -32,13 +32,23 @@ namespace nghttp2 {
|
||||
namespace util {
|
||||
|
||||
EvbufferBuffer::EvbufferBuffer()
|
||||
: evbuffer_(nullptr), bucket_(nullptr), buf_(nullptr), bufmax_(0),
|
||||
buflen_(0), limit_(0), writelen_(0) {}
|
||||
: evbuffer_(nullptr),
|
||||
bucket_(nullptr),
|
||||
buf_(nullptr),
|
||||
bufmax_(0),
|
||||
buflen_(0),
|
||||
limit_(0),
|
||||
writelen_(0) {}
|
||||
|
||||
EvbufferBuffer::EvbufferBuffer(evbuffer *evbuffer, uint8_t *buf, size_t bufmax,
|
||||
ssize_t limit)
|
||||
: evbuffer_(evbuffer), bucket_(limit == -1 ? nullptr : evbuffer_new()),
|
||||
buf_(buf), bufmax_(bufmax), buflen_(0), limit_(limit), writelen_(0) {}
|
||||
: evbuffer_(evbuffer),
|
||||
bucket_(limit == -1 ? nullptr : evbuffer_new()),
|
||||
buf_(buf),
|
||||
bufmax_(bufmax),
|
||||
buflen_(0),
|
||||
limit_(limit),
|
||||
writelen_(0) {}
|
||||
|
||||
void EvbufferBuffer::reset(evbuffer *evbuffer, uint8_t *buf, size_t bufmax,
|
||||
ssize_t limit) {
|
||||
|
||||
@@ -41,9 +41,19 @@
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
#define DEFAULT_WR_IOVCNT 16
|
||||
|
||||
#if defined(IOV_MAX) && IOV_MAX < DEFAULT_WR_IOVCNT
|
||||
#define MAX_WR_IOVCNT IOV_MAX
|
||||
#else // !defined(IOV_MAX) || IOV_MAX >= DEFAULT_WR_IOVCNT
|
||||
#define MAX_WR_IOVCNT DEFAULT_WR_IOVCNT
|
||||
#endif // !defined(IOV_MAX) || IOV_MAX >= DEFAULT_WR_IOVCNT
|
||||
|
||||
template <size_t N> struct Memchunk {
|
||||
Memchunk(std::unique_ptr<Memchunk> next_chunk)
|
||||
: pos(std::begin(buf)), last(pos), knext(std::move(next_chunk)),
|
||||
: pos(std::begin(buf)),
|
||||
last(pos),
|
||||
knext(std::move(next_chunk)),
|
||||
next(nullptr) {}
|
||||
size_t len() const { return last - pos; }
|
||||
size_t left() const { return std::end(buf) - last; }
|
||||
@@ -199,6 +209,36 @@ template <typename Memchunk> struct Memchunks {
|
||||
|
||||
return first - static_cast<uint8_t *>(dest);
|
||||
}
|
||||
size_t remove(Memchunks &dest, size_t count) {
|
||||
if (!tail || count == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto left = count;
|
||||
auto m = head;
|
||||
|
||||
while (m) {
|
||||
auto next = m->next;
|
||||
auto n = std::min(left, m->len());
|
||||
|
||||
assert(m->len());
|
||||
dest.append(m->pos, n);
|
||||
m->pos += n;
|
||||
len -= n;
|
||||
left -= n;
|
||||
if (m->len() > 0) {
|
||||
break;
|
||||
}
|
||||
pool->recycle(m);
|
||||
m = next;
|
||||
}
|
||||
head = m;
|
||||
if (head == nullptr) {
|
||||
tail = nullptr;
|
||||
}
|
||||
|
||||
return count - left;
|
||||
}
|
||||
size_t drain(size_t count) {
|
||||
auto ndata = count;
|
||||
auto m = head;
|
||||
@@ -252,8 +292,12 @@ template <typename Memchunk> struct Memchunks {
|
||||
// Wrapper around Memchunks to offer "peeking" functionality.
|
||||
template <typename Memchunk> struct PeekMemchunks {
|
||||
PeekMemchunks(Pool<Memchunk> *pool)
|
||||
: memchunks(pool), cur(nullptr), cur_pos(nullptr), cur_last(nullptr),
|
||||
len(0), peeking(true) {}
|
||||
: memchunks(pool),
|
||||
cur(nullptr),
|
||||
cur_pos(nullptr),
|
||||
cur_last(nullptr),
|
||||
len(0),
|
||||
peeking(true) {}
|
||||
PeekMemchunks(const PeekMemchunks &) = delete;
|
||||
PeekMemchunks(PeekMemchunks &&other) noexcept
|
||||
: memchunks(std::move(other.memchunks)),
|
||||
@@ -374,14 +418,6 @@ using MemchunkPool = Pool<Memchunk16K>;
|
||||
using DefaultMemchunks = Memchunks<Memchunk16K>;
|
||||
using DefaultPeekMemchunks = PeekMemchunks<Memchunk16K>;
|
||||
|
||||
#define DEFAULT_WR_IOVCNT 16
|
||||
|
||||
#if defined(IOV_MAX) && IOV_MAX < DEFAULT_WR_IOVCNT
|
||||
#define MAX_WR_IOVCNT IOV_MAX
|
||||
#else // !defined(IOV_MAX) || IOV_MAX >= DEFAULT_WR_IOVCNT
|
||||
#define MAX_WR_IOVCNT DEFAULT_WR_IOVCNT
|
||||
#endif // !defined(IOV_MAX) || IOV_MAX >= DEFAULT_WR_IOVCNT
|
||||
|
||||
inline int limit_iovec(struct iovec *iov, int iovcnt, size_t max) {
|
||||
if (max == 0) {
|
||||
return 0;
|
||||
|
||||
61
src/network.h
Normal file
61
src/network.h
Normal 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
|
||||
113
src/nghttp.cc
113
src/nghttp.cc
@@ -94,13 +94,26 @@ constexpr auto anchors = std::array<Anchor, 5>{{
|
||||
|
||||
Config::Config()
|
||||
: header_table_size(-1),
|
||||
min_header_table_size(std::numeric_limits<uint32_t>::max()), padding(0),
|
||||
max_concurrent_streams(100), peer_max_concurrent_streams(100),
|
||||
weight(NGHTTP2_DEFAULT_WEIGHT), multiply(1), timeout(0.), window_bits(-1),
|
||||
connection_window_bits(-1), verbose(0), null_out(false),
|
||||
remote_name(false), get_assets(false), stat(false), upgrade(false),
|
||||
continuation(false), no_content_length(false), no_dep(false),
|
||||
hexdump(false), no_push(false) {
|
||||
min_header_table_size(std::numeric_limits<uint32_t>::max()),
|
||||
padding(0),
|
||||
max_concurrent_streams(100),
|
||||
peer_max_concurrent_streams(100),
|
||||
weight(NGHTTP2_DEFAULT_WEIGHT),
|
||||
multiply(1),
|
||||
timeout(0.),
|
||||
window_bits(-1),
|
||||
connection_window_bits(-1),
|
||||
verbose(0),
|
||||
null_out(false),
|
||||
remote_name(false),
|
||||
get_assets(false),
|
||||
stat(false),
|
||||
upgrade(false),
|
||||
continuation(false),
|
||||
no_content_length(false),
|
||||
no_dep(false),
|
||||
hexdump(false),
|
||||
no_push(false) {
|
||||
nghttp2_option_new(&http2_option);
|
||||
nghttp2_option_set_peer_max_concurrent_streams(http2_option,
|
||||
peer_max_concurrent_streams);
|
||||
@@ -133,9 +146,19 @@ std::string strip_fragment(const char *raw_uri) {
|
||||
Request::Request(const std::string &uri, const http_parser_url &u,
|
||||
const nghttp2_data_provider *data_prd, int64_t data_length,
|
||||
const nghttp2_priority_spec &pri_spec, int level)
|
||||
: uri(uri), u(u), pri_spec(pri_spec), data_length(data_length),
|
||||
data_offset(0), response_len(0), inflater(nullptr), html_parser(nullptr),
|
||||
data_prd(data_prd), stream_id(-1), status(0), level(level),
|
||||
: uri(uri),
|
||||
u(u),
|
||||
pri_spec(pri_spec),
|
||||
data_length(data_length),
|
||||
data_offset(0),
|
||||
response_len(0),
|
||||
inflater(nullptr),
|
||||
html_parser(nullptr),
|
||||
data_prd(data_prd),
|
||||
header_buffer_size(0),
|
||||
stream_id(-1),
|
||||
status(0),
|
||||
level(level),
|
||||
expect_final_response(false) {
|
||||
http2::init_hdidx(res_hdidx);
|
||||
http2::init_hdidx(req_hdidx);
|
||||
@@ -250,34 +273,7 @@ bool Request::is_ipv6_literal_addr() const {
|
||||
}
|
||||
}
|
||||
|
||||
bool Request::response_pseudo_header_allowed(int16_t token) const {
|
||||
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) {
|
||||
Headers::value_type *Request::get_res_header(int32_t token) {
|
||||
auto idx = res_hdidx[token];
|
||||
if (idx == -1) {
|
||||
return nullptr;
|
||||
@@ -285,7 +281,7 @@ Headers::value_type *Request::get_res_header(int16_t token) {
|
||||
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];
|
||||
if (idx == -1) {
|
||||
return nullptr;
|
||||
@@ -467,10 +463,20 @@ void settings_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
|
||||
|
||||
HttpClient::HttpClient(const nghttp2_session_callbacks *callbacks,
|
||||
struct ev_loop *loop, SSL_CTX *ssl_ctx)
|
||||
: session(nullptr), callbacks(callbacks), loop(loop), ssl_ctx(ssl_ctx),
|
||||
ssl(nullptr), addrs(nullptr), next_addr(nullptr), cur_addr(nullptr),
|
||||
complete(0), success(0), settings_payloadlen(0), state(ClientState::IDLE),
|
||||
upgrade_response_status_code(0), fd(-1),
|
||||
: session(nullptr),
|
||||
callbacks(callbacks),
|
||||
loop(loop),
|
||||
ssl_ctx(ssl_ctx),
|
||||
ssl(nullptr),
|
||||
addrs(nullptr),
|
||||
next_addr(nullptr),
|
||||
cur_addr(nullptr),
|
||||
complete(0),
|
||||
success(0),
|
||||
settings_payloadlen(0),
|
||||
state(ClientState::IDLE),
|
||||
upgrade_response_status_code(0),
|
||||
fd(-1),
|
||||
upgrade_response_complete(false) {
|
||||
ev_io_init(&wev, writecb, 0, EV_WRITE);
|
||||
ev_io_init(&rev, readcb, 0, EV_READ);
|
||||
@@ -1704,6 +1710,14 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||
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);
|
||||
|
||||
http2::index_header(req->res_hdidx, token, req->res_nva.size());
|
||||
@@ -1719,6 +1733,15 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||
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);
|
||||
|
||||
http2::index_header(req->req_hdidx, token, req->req_nva.size());
|
||||
@@ -1806,6 +1829,10 @@ int on_frame_recv_callback2(nghttp2_session *session,
|
||||
if (!req) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Reset for response header field reception
|
||||
req->header_buffer_size = 0;
|
||||
|
||||
auto scheme = req->get_req_header(http2::HD__SCHEME);
|
||||
auto authority = req->get_req_header(http2::HD__AUTHORITY);
|
||||
auto path = req->get_req_header(http2::HD__PATH);
|
||||
|
||||
@@ -125,11 +125,8 @@ struct Request {
|
||||
|
||||
bool is_ipv6_literal_addr() const;
|
||||
|
||||
bool response_pseudo_header_allowed(int16_t token) const;
|
||||
bool push_request_pseudo_header_allowed(int16_t token) const;
|
||||
|
||||
Headers::value_type *get_res_header(int16_t token);
|
||||
Headers::value_type *get_req_header(int16_t token);
|
||||
Headers::value_type *get_res_header(int32_t token);
|
||||
Headers::value_type *get_req_header(int32_t token);
|
||||
|
||||
void record_request_start_time();
|
||||
void record_response_start_time();
|
||||
@@ -150,6 +147,7 @@ struct Request {
|
||||
nghttp2_gzip *inflater;
|
||||
HtmlParser *html_parser;
|
||||
const nghttp2_data_provider *data_prd;
|
||||
size_t header_buffer_size;
|
||||
int32_t stream_id;
|
||||
int status;
|
||||
// Recursion level: 0: first entity, 1: entity linked from first entity
|
||||
|
||||
@@ -143,6 +143,11 @@ Options:
|
||||
Default: 1
|
||||
-e, --error-gzip
|
||||
Make error response gzipped.
|
||||
-w, --window-bits=<N>
|
||||
Sets the stream level initial window size to 2**<N>-1.
|
||||
-W, --connection-window-bits=<N>
|
||||
Sets the connection level initial window size to
|
||||
2**<N>-1.
|
||||
--dh-param-file=<PATH>
|
||||
Path to file that contains DH parameters in PEM format.
|
||||
Without this option, DHE cipher suites are not
|
||||
@@ -202,6 +207,8 @@ int main(int argc, char **argv) {
|
||||
{"max-concurrent-streams", required_argument, nullptr, 'm'},
|
||||
{"workers", required_argument, nullptr, 'n'},
|
||||
{"error-gzip", no_argument, nullptr, 'e'},
|
||||
{"window-bits", required_argument, nullptr, 'w'},
|
||||
{"connection-window-bits", required_argument, nullptr, 'W'},
|
||||
{"no-tls", no_argument, &flag, 1},
|
||||
{"color", no_argument, &flag, 2},
|
||||
{"version", no_argument, &flag, 3},
|
||||
@@ -214,7 +221,7 @@ int main(int argc, char **argv) {
|
||||
{"no-content-length", no_argument, &flag, 10},
|
||||
{nullptr, 0, nullptr, 0}};
|
||||
int option_index = 0;
|
||||
int c = getopt_long(argc, argv, "DVb:c:d:ehm:n:p:va:", long_options,
|
||||
int c = getopt_long(argc, argv, "DVb:c:d:ehm:n:p:va:w:W:", long_options,
|
||||
&option_index);
|
||||
char *end;
|
||||
if (c == -1) {
|
||||
@@ -281,6 +288,26 @@ int main(int argc, char **argv) {
|
||||
std::cerr << "-p: Bad option value: " << optarg << std::endl;
|
||||
}
|
||||
break;
|
||||
case 'w':
|
||||
case 'W': {
|
||||
char *endptr;
|
||||
errno = 0;
|
||||
auto n = strtoul(optarg, &endptr, 10);
|
||||
if (errno != 0 || *endptr != '\0' || n >= 31) {
|
||||
std::cerr << "-" << static_cast<char>(c)
|
||||
<< ": specify the integer in the range [0, 30], inclusive"
|
||||
<< std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (c == 'w') {
|
||||
config.window_bits = n;
|
||||
} else {
|
||||
config.connection_window_bits = n;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case '?':
|
||||
util::show_candidates(argv[optind - 1], long_options);
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include "memchunk_test.h"
|
||||
#include "template_test.h"
|
||||
#include "shrpx_http_test.h"
|
||||
#include "base64_test.h"
|
||||
#include "shrpx_config.h"
|
||||
#include "ssl.h"
|
||||
|
||||
@@ -88,12 +89,6 @@ int main(int argc, char *argv[]) {
|
||||
shrpx::test_http2_index_header) ||
|
||||
!CU_add_test(pSuite, "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",
|
||||
shrpx::test_http2_parse_link_header) ||
|
||||
!CU_add_test(pSuite, "http2_path_join", shrpx::test_http2_path_join) ||
|
||||
@@ -105,8 +100,8 @@ int main(int argc, char *argv[]) {
|
||||
shrpx::test_http2_get_pure_path_component) ||
|
||||
!CU_add_test(pSuite, "http2_construct_push_component",
|
||||
shrpx::test_http2_construct_push_component) ||
|
||||
!CU_add_test(pSuite, "downstream_field_store_index_headers",
|
||||
shrpx::test_downstream_field_store_index_headers) ||
|
||||
!CU_add_test(pSuite, "downstream_field_store_add_header_lower",
|
||||
shrpx::test_downstream_field_store_add_header_lower) ||
|
||||
!CU_add_test(pSuite, "downstream_field_store_header",
|
||||
shrpx::test_downstream_field_store_header) ||
|
||||
!CU_add_test(pSuite, "downstream_crumble_request_cookie",
|
||||
@@ -166,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_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, "buffer_write", nghttp2::test_buffer_write) ||
|
||||
!CU_add_test(pSuite, "pool_recycle", nghttp2::test_pool_recycle) ||
|
||||
@@ -186,7 +185,9 @@ int main(int argc, char *argv[]) {
|
||||
!CU_add_test(pSuite, "template_immutable_string",
|
||||
nghttp2::test_template_immutable_string) ||
|
||||
!CU_add_test(pSuite, "template_string_ref",
|
||||
nghttp2::test_template_string_ref)) {
|
||||
nghttp2::test_template_string_ref) ||
|
||||
!CU_add_test(pSuite, "base64_encode", nghttp2::test_base64_encode) ||
|
||||
!CU_add_test(pSuite, "base64_decode", nghttp2::test_base64_decode)) {
|
||||
CU_cleanup_registry();
|
||||
return CU_get_error();
|
||||
}
|
||||
|
||||
891
src/shrpx.cc
891
src/shrpx.cc
File diff suppressed because it is too large
Load Diff
@@ -45,16 +45,16 @@ void acceptcb(struct ev_loop *loop, ev_io *w, int revent) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
AcceptHandler::AcceptHandler(int fd, ConnectionHandler *h)
|
||||
: conn_hnr_(h), fd_(fd) {
|
||||
ev_io_init(&wev_, acceptcb, fd_, EV_READ);
|
||||
AcceptHandler::AcceptHandler(const UpstreamAddr *faddr, ConnectionHandler *h)
|
||||
: conn_hnr_(h), faddr_(faddr) {
|
||||
ev_io_init(&wev_, acceptcb, faddr_->fd, EV_READ);
|
||||
wev_.data = this;
|
||||
ev_io_start(conn_hnr_->get_loop(), &wev_);
|
||||
}
|
||||
|
||||
AcceptHandler::~AcceptHandler() {
|
||||
ev_io_stop(conn_hnr_->get_loop(), &wev_);
|
||||
close(fd_);
|
||||
close(faddr_->fd);
|
||||
}
|
||||
|
||||
void AcceptHandler::accept_connection() {
|
||||
@@ -63,10 +63,10 @@ void AcceptHandler::accept_connection() {
|
||||
socklen_t addrlen = sizeof(sockaddr);
|
||||
|
||||
#ifdef HAVE_ACCEPT4
|
||||
auto cfd =
|
||||
accept4(fd_, &sockaddr.sa, &addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC);
|
||||
#else // !HAVE_ACCEPT4
|
||||
auto cfd = accept(fd_, &sockaddr.sa, &addrlen);
|
||||
auto cfd = accept4(faddr_->fd, &sockaddr.sa, &addrlen,
|
||||
SOCK_NONBLOCK | SOCK_CLOEXEC);
|
||||
#else // !HAVE_ACCEPT4
|
||||
auto cfd = accept(faddr_->fd, &sockaddr.sa, &addrlen);
|
||||
#endif // !HAVE_ACCEPT4
|
||||
|
||||
if (cfd == -1) {
|
||||
@@ -101,7 +101,7 @@ void AcceptHandler::accept_connection() {
|
||||
|
||||
util::make_socket_nodelay(cfd);
|
||||
|
||||
conn_hnr_->handle_connection(cfd, &sockaddr.sa, addrlen);
|
||||
conn_hnr_->handle_connection(cfd, &sockaddr.sa, addrlen, faddr_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,6 +109,6 @@ void AcceptHandler::enable() { ev_io_start(conn_hnr_->get_loop(), &wev_); }
|
||||
|
||||
void AcceptHandler::disable() { ev_io_stop(conn_hnr_->get_loop(), &wev_); }
|
||||
|
||||
int AcceptHandler::get_fd() const { return fd_; }
|
||||
int AcceptHandler::get_fd() const { return faddr_->fd; }
|
||||
|
||||
} // namespace shrpx
|
||||
|
||||
@@ -32,10 +32,11 @@
|
||||
namespace shrpx {
|
||||
|
||||
class ConnectionHandler;
|
||||
struct UpstreamAddr;
|
||||
|
||||
class AcceptHandler {
|
||||
public:
|
||||
AcceptHandler(int fd, ConnectionHandler *h);
|
||||
AcceptHandler(const UpstreamAddr *faddr, ConnectionHandler *h);
|
||||
~AcceptHandler();
|
||||
void accept_connection();
|
||||
void enable();
|
||||
@@ -45,7 +46,7 @@ public:
|
||||
private:
|
||||
ev_io wev_;
|
||||
ConnectionHandler *conn_hnr_;
|
||||
int fd_;
|
||||
const UpstreamAddr *faddr_;
|
||||
};
|
||||
|
||||
} // namespace shrpx
|
||||
|
||||
@@ -127,6 +127,10 @@ int ClientHandler::read_clear() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!ev_is_active(&conn_.rev)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto nread = conn_.read_clear(rb_.last, rb_.wleft());
|
||||
|
||||
if (nread == 0) {
|
||||
@@ -220,6 +224,10 @@ int ClientHandler::read_tls() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!ev_is_active(&conn_.rev)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto nread = conn_.read_tls(rb_.last, rb_.wleft());
|
||||
|
||||
if (nread == 0) {
|
||||
@@ -369,7 +377,8 @@ int ClientHandler::upstream_http1_connhd_read() {
|
||||
}
|
||||
|
||||
ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl,
|
||||
const char *ipaddr, const char *port)
|
||||
const char *ipaddr, const char *port, int family,
|
||||
const UpstreamAddr *faddr)
|
||||
: conn_(worker->get_loop(), fd, ssl, worker->get_mcpool(),
|
||||
get_config()->conn.upstream.timeout.write,
|
||||
get_config()->conn.upstream.timeout.read,
|
||||
@@ -380,9 +389,12 @@ ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl,
|
||||
pinned_http2sessions_(
|
||||
get_config()->conn.downstream.proto == PROTO_HTTP2
|
||||
? make_unique<std::vector<ssize_t>>(
|
||||
get_config()->conn.downstream.addr_groups.size(), -1)
|
||||
worker->get_downstream_addr_groups().size(), -1)
|
||||
: nullptr),
|
||||
ipaddr_(ipaddr), port_(port), worker_(worker),
|
||||
ipaddr_(ipaddr),
|
||||
port_(port),
|
||||
faddr_(faddr),
|
||||
worker_(worker),
|
||||
left_connhd_len_(NGHTTP2_CLIENT_MAGIC_LEN),
|
||||
should_close_after_write_(false) {
|
||||
|
||||
@@ -406,11 +418,19 @@ ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl,
|
||||
|
||||
auto &fwdconf = get_config()->http.forwarded;
|
||||
|
||||
if ((fwdconf.params & FORWARDED_FOR) &&
|
||||
fwdconf.for_node_type == FORWARDED_NODE_OBFUSCATED) {
|
||||
forwarded_for_obfuscated_ = "_";
|
||||
forwarded_for_obfuscated_ += util::random_alpha_digit(
|
||||
worker_->get_randgen(), SHRPX_OBFUSCATED_NODE_LENGTH);
|
||||
if (fwdconf.params & FORWARDED_FOR) {
|
||||
if (fwdconf.for_node_type == FORWARDED_NODE_OBFUSCATED) {
|
||||
forwarded_for_ = "_";
|
||||
forwarded_for_ += util::random_alpha_digit(worker_->get_randgen(),
|
||||
SHRPX_OBFUSCATED_NODE_LENGTH);
|
||||
} else if (family == AF_INET6) {
|
||||
forwarded_for_ = "[";
|
||||
forwarded_for_ += ipaddr_;
|
||||
forwarded_for_ += ']';
|
||||
} else {
|
||||
// family == AF_INET or family == AF_UNIX
|
||||
forwarded_for_ = ipaddr_;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -644,8 +664,8 @@ std::unique_ptr<DownstreamConnection>
|
||||
ClientHandler::get_downstream_connection(Downstream *downstream) {
|
||||
size_t group;
|
||||
auto &downstreamconf = get_config()->conn.downstream;
|
||||
auto &groups = downstreamconf.addr_groups;
|
||||
auto catch_all = downstreamconf.addr_group_catch_all;
|
||||
auto &groups = worker_->get_downstream_addr_groups();
|
||||
|
||||
const auto &req = downstream->request();
|
||||
|
||||
@@ -661,16 +681,19 @@ ClientHandler::get_downstream_connection(Downstream *downstream) {
|
||||
} else {
|
||||
auto &router = get_config()->router;
|
||||
if (!req.authority.empty()) {
|
||||
group = match_downstream_addr_group(router, req.authority, req.path,
|
||||
groups, catch_all);
|
||||
group =
|
||||
match_downstream_addr_group(router, StringRef{req.authority},
|
||||
StringRef{req.path}, groups, catch_all);
|
||||
} else {
|
||||
auto h = req.fs.header(http2::HD_HOST);
|
||||
if (h) {
|
||||
group = match_downstream_addr_group(router, h->value, req.path, groups,
|
||||
catch_all);
|
||||
group =
|
||||
match_downstream_addr_group(router, StringRef{h->value},
|
||||
StringRef{req.path}, groups, catch_all);
|
||||
} else {
|
||||
group = match_downstream_addr_group(router, "", req.path, groups,
|
||||
catch_all);
|
||||
group =
|
||||
match_downstream_addr_group(router, StringRef::from_lit(""),
|
||||
StringRef{req.path}, groups, catch_all);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -702,8 +725,8 @@ ClientHandler::get_downstream_connection(Downstream *downstream) {
|
||||
}
|
||||
dconn = make_unique<Http2DownstreamConnection>(dconn_pool, http2session);
|
||||
} else {
|
||||
dconn =
|
||||
make_unique<HttpDownstreamConnection>(dconn_pool, group, conn_.loop);
|
||||
dconn = make_unique<HttpDownstreamConnection>(dconn_pool, group,
|
||||
conn_.loop, worker_);
|
||||
}
|
||||
dconn->set_client_handler(this);
|
||||
return dconn;
|
||||
@@ -723,10 +746,6 @@ MemchunkPool *ClientHandler::get_mcpool() { return worker_->get_mcpool(); }
|
||||
|
||||
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() {
|
||||
upstream_ = make_unique<Http2Upstream>(this);
|
||||
alpn_ = NGHTTP2_CLEARTEXT_PROTO_VERSION_ID;
|
||||
@@ -753,17 +772,6 @@ int ClientHandler::perform_http2_upgrade(HttpsUpstream *http) {
|
||||
"Upgrade: " NGHTTP2_CLEARTEXT_PROTO_VERSION_ID "\r\n"
|
||||
"\r\n";
|
||||
|
||||
auto required_size = str_size(res) + input->rleft();
|
||||
|
||||
if (output->wleft() < required_size) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this)
|
||||
<< "HTTP Upgrade failed because of insufficient buffer space: need "
|
||||
<< required_size << ", available " << output->wleft();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (upstream->upgrade_upstream(http) != 0) {
|
||||
return -1;
|
||||
}
|
||||
@@ -775,11 +783,8 @@ int ClientHandler::perform_http2_upgrade(HttpsUpstream *http) {
|
||||
on_read_ = &ClientHandler::upstream_http2_connhd_read;
|
||||
write_ = &ClientHandler::write_clear;
|
||||
|
||||
auto nread =
|
||||
downstream->get_response_buf()->remove(output->last, output->wleft());
|
||||
output->write(nread);
|
||||
|
||||
output->write(res, str_size(res));
|
||||
input->remove(*output, input->rleft());
|
||||
output->append(res, str_size(res));
|
||||
upstream_ = std::move(upstream);
|
||||
|
||||
signal_write();
|
||||
@@ -855,8 +860,8 @@ void ClientHandler::write_accesslog(Downstream *downstream) {
|
||||
std::chrono::high_resolution_clock::now(), // request_end_time
|
||||
|
||||
req.http_major, req.http_minor, resp.http_status,
|
||||
downstream->response_sent_body_length, StringRef(port_),
|
||||
get_config()->conn.listener.port, get_config()->pid,
|
||||
downstream->response_sent_body_length, StringRef(port_), faddr_->port,
|
||||
get_config()->pid,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -879,7 +884,7 @@ void ClientHandler::write_accesslog(int major, int minor, unsigned int status,
|
||||
highres_now, // request_end_time
|
||||
major, minor, // major, minor
|
||||
status, body_bytes_sent, StringRef(port_),
|
||||
get_config()->conn.listener.port, get_config()->pid,
|
||||
faddr_->port, get_config()->pid,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -969,7 +974,7 @@ int ClientHandler::proxy_protocol_read() {
|
||||
|
||||
--end;
|
||||
|
||||
constexpr const char HEADER[] = "PROXY ";
|
||||
constexpr char HEADER[] = "PROXY ";
|
||||
|
||||
if (static_cast<size_t>(end - rb_.pos) < str_size(HEADER)) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
@@ -1120,63 +1125,18 @@ int ClientHandler::proxy_protocol_read() {
|
||||
return on_proxy_protocol_finish();
|
||||
}
|
||||
|
||||
const std::string &ClientHandler::get_forwarded_by() {
|
||||
StringRef ClientHandler::get_forwarded_by() {
|
||||
auto &fwdconf = get_config()->http.forwarded;
|
||||
|
||||
if (fwdconf.by_node_type == FORWARDED_NODE_OBFUSCATED) {
|
||||
return fwdconf.by_obfuscated;
|
||||
}
|
||||
if (!local_hostport_.empty()) {
|
||||
return local_hostport_;
|
||||
return StringRef(fwdconf.by_obfuscated);
|
||||
}
|
||||
|
||||
auto &listenerconf = get_config()->conn.listener;
|
||||
|
||||
// For UNIX domain socket listener, just return empty string.
|
||||
if (listenerconf.host_unix) {
|
||||
return local_hostport_;
|
||||
}
|
||||
|
||||
int rv;
|
||||
sockaddr_union su;
|
||||
socklen_t addrlen = sizeof(su);
|
||||
|
||||
rv = getsockname(conn_.fd, &su.sa, &addrlen);
|
||||
if (rv != 0) {
|
||||
return local_hostport_;
|
||||
}
|
||||
|
||||
char host[NI_MAXHOST];
|
||||
rv = getnameinfo(&su.sa, addrlen, host, sizeof(host), nullptr, 0,
|
||||
NI_NUMERICHOST);
|
||||
if (rv != 0) {
|
||||
return local_hostport_;
|
||||
}
|
||||
|
||||
if (su.storage.ss_family == AF_INET6) {
|
||||
local_hostport_ = "[";
|
||||
local_hostport_ += host;
|
||||
local_hostport_ += "]:";
|
||||
} else {
|
||||
local_hostport_ = host;
|
||||
local_hostport_ += ':';
|
||||
}
|
||||
|
||||
local_hostport_ += util::utos(listenerconf.port);
|
||||
|
||||
return local_hostport_;
|
||||
return StringRef(faddr_->hostport);
|
||||
}
|
||||
|
||||
const std::string &ClientHandler::get_forwarded_for() const {
|
||||
if (get_config()->http.forwarded.for_node_type == FORWARDED_NODE_OBFUSCATED) {
|
||||
return forwarded_for_obfuscated_;
|
||||
}
|
||||
|
||||
if (get_config()->conn.listener.host_unix) {
|
||||
return EMPTY_STRING;
|
||||
}
|
||||
|
||||
return ipaddr_;
|
||||
return forwarded_for_;
|
||||
}
|
||||
|
||||
} // namespace shrpx
|
||||
|
||||
@@ -53,7 +53,7 @@ struct WorkerStat;
|
||||
class ClientHandler {
|
||||
public:
|
||||
ClientHandler(Worker *worker, int fd, SSL *ssl, const char *ipaddr,
|
||||
const char *port);
|
||||
const char *port, int family, const UpstreamAddr *faddr);
|
||||
~ClientHandler();
|
||||
|
||||
int noop();
|
||||
@@ -99,7 +99,6 @@ public:
|
||||
get_downstream_connection(Downstream *downstream);
|
||||
MemchunkPool *get_mcpool();
|
||||
SSL *get_ssl() const;
|
||||
ConnectBlocker *get_connect_blocker() const;
|
||||
// Call this function when HTTP/2 connection header is received at
|
||||
// the start of the connection.
|
||||
void direct_http2_upgrade();
|
||||
@@ -136,7 +135,7 @@ public:
|
||||
|
||||
// Returns string suitable for use in "by" parameter of Forwarded
|
||||
// header field.
|
||||
const std::string &get_forwarded_by();
|
||||
StringRef get_forwarded_by();
|
||||
// Returns string suitable for use in "for" parameter of Forwarded
|
||||
// header field.
|
||||
const std::string &get_forwarded_for() const;
|
||||
@@ -152,13 +151,13 @@ private:
|
||||
std::string port_;
|
||||
// The ALPN identifier negotiated for this connection.
|
||||
std::string alpn_;
|
||||
// Host and port of this socket (e.g., "[::1]:8443")
|
||||
std::string local_hostport_;
|
||||
// The obfuscated version of client address used in "for" parameter
|
||||
// of Forwarded header field.
|
||||
std::string forwarded_for_obfuscated_;
|
||||
// The client address used in "for" parameter of Forwarded header
|
||||
// field.
|
||||
std::string forwarded_for_;
|
||||
std::function<int(ClientHandler &)> read_, write_;
|
||||
std::function<int(ClientHandler &)> on_read_, on_write_;
|
||||
// Address of frontend listening socket
|
||||
const UpstreamAddr *faddr_;
|
||||
Worker *worker_;
|
||||
// The number of bytes of HTTP/2 client connection header to read
|
||||
size_t left_connhd_len_;
|
||||
|
||||
@@ -54,9 +54,7 @@
|
||||
#include "shrpx_log.h"
|
||||
#include "shrpx_ssl.h"
|
||||
#include "shrpx_http.h"
|
||||
#include "http2.h"
|
||||
#include "util.h"
|
||||
#include "template.h"
|
||||
#include "base64.h"
|
||||
|
||||
namespace shrpx {
|
||||
@@ -80,39 +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 {
|
||||
int split_host_port(char *host, size_t hostlen, uint16_t *port_ptr,
|
||||
const char *hostport, size_t hostportlen) {
|
||||
@@ -276,7 +241,7 @@ std::string read_passwd_from_file(const char *filename) {
|
||||
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, ':');
|
||||
|
||||
if (colon == nullptr || colon == optarg) {
|
||||
@@ -287,16 +252,15 @@ std::pair<std::string, std::string> parse_header(const char *optarg) {
|
||||
for (; *value == '\t' || *value == ' '; ++value)
|
||||
;
|
||||
|
||||
auto p = std::make_pair(std::string(optarg, colon),
|
||||
std::string(value, strlen(value)));
|
||||
util::inp_strlower(p.first);
|
||||
auto p =
|
||||
Header(std::string(optarg, colon), std::string(value, strlen(value)));
|
||||
util::inp_strlower(p.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(
|
||||
reinterpret_cast<const uint8_t *>(p.second.c_str()),
|
||||
p.second.size())) {
|
||||
return {"", ""};
|
||||
reinterpret_cast<const uint8_t *>(p.value.c_str()), p.value.size())) {
|
||||
return Header();
|
||||
}
|
||||
|
||||
return p;
|
||||
@@ -572,6 +536,26 @@ std::vector<LogFragment> parse_log_format(const char *optarg) {
|
||||
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 {
|
||||
int parse_duration(ev_tstamp *dest, const char *opt, const char *optarg) {
|
||||
auto t = util::parse_duration_with_unit(optarg);
|
||||
@@ -613,7 +597,7 @@ void parse_mapping(const DownstreamAddr &addr, const char *src) {
|
||||
pattern += http2::normalize_path(slash, raw_pattern.second);
|
||||
}
|
||||
for (auto &g : addr_groups) {
|
||||
if (g.pattern.get() == pattern) {
|
||||
if (g.pattern == pattern) {
|
||||
g.addrs.push_back(addr);
|
||||
done = true;
|
||||
break;
|
||||
@@ -622,11 +606,10 @@ void parse_mapping(const DownstreamAddr &addr, const char *src) {
|
||||
if (done) {
|
||||
continue;
|
||||
}
|
||||
DownstreamAddrGroup g(pattern);
|
||||
DownstreamAddrGroup g(StringRef{pattern});
|
||||
g.addrs.push_back(addr);
|
||||
|
||||
mod_config()->router.add_route(g.pattern.get(), strlen(g.pattern.get()),
|
||||
addr_groups.size());
|
||||
mod_config()->router.add_route(StringRef{g.pattern}, addr_groups.size());
|
||||
|
||||
addr_groups.push_back(std::move(g));
|
||||
}
|
||||
@@ -670,9 +653,11 @@ enum {
|
||||
SHRPX_OPTID_ADD_X_FORWARDED_FOR,
|
||||
SHRPX_OPTID_ALTSVC,
|
||||
SHRPX_OPTID_BACKEND,
|
||||
SHRPX_OPTID_BACKEND_ADDRESS_FAMILY,
|
||||
SHRPX_OPTID_BACKEND_HTTP_PROXY_URI,
|
||||
SHRPX_OPTID_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND,
|
||||
SHRPX_OPTID_BACKEND_HTTP1_CONNECTIONS_PER_HOST,
|
||||
SHRPX_OPTID_BACKEND_HTTP1_TLS,
|
||||
SHRPX_OPTID_BACKEND_HTTP2_CONNECTION_WINDOW_BITS,
|
||||
SHRPX_OPTID_BACKEND_HTTP2_CONNECTIONS_PER_WORKER,
|
||||
SHRPX_OPTID_BACKEND_HTTP2_WINDOW_BITS,
|
||||
@@ -723,8 +708,11 @@ enum {
|
||||
SHRPX_OPTID_LISTENER_DISABLE_TIMEOUT,
|
||||
SHRPX_OPTID_LOG_LEVEL,
|
||||
SHRPX_OPTID_MAX_HEADER_FIELDS,
|
||||
SHRPX_OPTID_MAX_REQUEST_HEADER_FIELDS,
|
||||
SHRPX_OPTID_MAX_RESPONSE_HEADER_FIELDS,
|
||||
SHRPX_OPTID_MRUBY_FILE,
|
||||
SHRPX_OPTID_NO_HOST_REWRITE,
|
||||
SHRPX_OPTID_NO_HTTP2_CIPHER_BLACK_LIST,
|
||||
SHRPX_OPTID_NO_LOCATION_REWRITE,
|
||||
SHRPX_OPTID_NO_OCSP,
|
||||
SHRPX_OPTID_NO_SERVER_PUSH,
|
||||
@@ -737,6 +725,8 @@ enum {
|
||||
SHRPX_OPTID_PRIVATE_KEY_PASSWD_FILE,
|
||||
SHRPX_OPTID_READ_BURST,
|
||||
SHRPX_OPTID_READ_RATE,
|
||||
SHRPX_OPTID_REQUEST_HEADER_FIELD_BUFFER,
|
||||
SHRPX_OPTID_RESPONSE_HEADER_FIELD_BUFFER,
|
||||
SHRPX_OPTID_RLIMIT_NOFILE,
|
||||
SHRPX_OPTID_STREAM_READ_TIMEOUT,
|
||||
SHRPX_OPTID_STREAM_WRITE_TIMEOUT,
|
||||
@@ -748,12 +738,20 @@ enum {
|
||||
SHRPX_OPTID_TLS_DYN_REC_WARMUP_THRESHOLD,
|
||||
SHRPX_OPTID_TLS_PROTO_LIST,
|
||||
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_FILE,
|
||||
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_MAX_FAIL,
|
||||
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_VERIFY_CLIENT,
|
||||
SHRPX_OPTID_VERIFY_CLIENT_CACERT,
|
||||
@@ -1069,6 +1067,9 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
if (util::strieq_l("backend-http1-tl", name, 16)) {
|
||||
return SHRPX_OPTID_BACKEND_HTTP1_TLS;
|
||||
}
|
||||
if (util::strieq_l("max-header-field", name, 16)) {
|
||||
return SHRPX_OPTID_MAX_HEADER_FIELDS;
|
||||
}
|
||||
@@ -1191,6 +1192,11 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
return SHRPX_OPTID_FRONTEND_WRITE_TIMEOUT;
|
||||
}
|
||||
break;
|
||||
case 'y':
|
||||
if (util::strieq_l("backend-address-famil", name, 21)) {
|
||||
return SHRPX_OPTID_BACKEND_ADDRESS_FAMILY;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 23:
|
||||
@@ -1246,6 +1252,9 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
if (util::strieq_l("backend-http2-window-bit", name, 24)) {
|
||||
return SHRPX_OPTID_BACKEND_HTTP2_WINDOW_BITS;
|
||||
}
|
||||
if (util::strieq_l("max-request-header-field", name, 24)) {
|
||||
return SHRPX_OPTID_MAX_REQUEST_HEADER_FIELDS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@@ -1255,11 +1264,17 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
if (util::strieq_l("frontend-http2-window-bit", name, 25)) {
|
||||
return SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS;
|
||||
}
|
||||
if (util::strieq_l("max-response-header-field", name, 25)) {
|
||||
return SHRPX_OPTID_MAX_RESPONSE_HEADER_FIELDS;
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
if (util::strieq_l("backend-keep-alive-timeou", name, 25)) {
|
||||
return SHRPX_OPTID_BACKEND_KEEP_ALIVE_TIMEOUT;
|
||||
}
|
||||
if (util::strieq_l("no-http2-cipher-black-lis", name, 25)) {
|
||||
return SHRPX_OPTID_NO_HTTP2_CIPHER_BLACK_LIST;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@@ -1270,6 +1285,11 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
return SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED;
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
if (util::strieq_l("request-header-field-buffe", name, 26)) {
|
||||
return SHRPX_OPTID_REQUEST_HEADER_FIELD_BUFFER;
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
if (util::strieq_l("worker-frontend-connection", name, 26)) {
|
||||
return SHRPX_OPTID_WORKER_FRONTEND_CONNECTIONS;
|
||||
@@ -1289,10 +1309,18 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
return SHRPX_OPTID_TLS_DYN_REC_WARMUP_THRESHOLD;
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
if (util::strieq_l("response-header-field-buffe", name, 27)) {
|
||||
return SHRPX_OPTID_RESPONSE_HEADER_FIELD_BUFFER;
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
if (util::strieq_l("http2-max-concurrent-stream", name, 27)) {
|
||||
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;
|
||||
@@ -1305,6 +1333,15 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
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:
|
||||
switch (name[32]) {
|
||||
case 'l':
|
||||
@@ -1319,6 +1356,11 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
break;
|
||||
case 34:
|
||||
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':
|
||||
if (util::strieq_l("frontend-http2-dump-request-heade", name, 33)) {
|
||||
return SHRPX_OPTID_FRONTEND_HTTP2_DUMP_REQUEST_HEADER;
|
||||
@@ -1361,6 +1403,11 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
break;
|
||||
case 37:
|
||||
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':
|
||||
if (util::strieq_l("frontend-http2-connection-window-bit", name, 36)) {
|
||||
return SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS;
|
||||
@@ -1377,6 +1424,45 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
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;
|
||||
}
|
||||
@@ -1396,7 +1482,7 @@ int parse_config(const char *opt, const char *optarg,
|
||||
if (!pat_delim) {
|
||||
pat_delim = optarg + optarglen;
|
||||
}
|
||||
DownstreamAddr addr;
|
||||
DownstreamAddr addr{};
|
||||
if (util::istarts_with(optarg, SHRPX_UNIX_PATH_PREFIX)) {
|
||||
auto path = optarg + str_size(SHRPX_UNIX_PATH_PREFIX);
|
||||
addr.host = ImmutableString(path, pat_delim);
|
||||
@@ -1425,11 +1511,15 @@ int parse_config(const char *opt, const char *optarg,
|
||||
case SHRPX_OPTID_FRONTEND: {
|
||||
auto &listenerconf = mod_config()->conn.listener;
|
||||
|
||||
UpstreamAddr addr{};
|
||||
addr.fd = -1;
|
||||
|
||||
if (util::istarts_with(optarg, SHRPX_UNIX_PATH_PREFIX)) {
|
||||
auto path = optarg + str_size(SHRPX_UNIX_PATH_PREFIX);
|
||||
listenerconf.host = strcopy(path);
|
||||
listenerconf.port = 0;
|
||||
listenerconf.host_unix = true;
|
||||
addr.host = ImmutableString(path);
|
||||
addr.host_unix = true;
|
||||
|
||||
listenerconf.addrs.push_back(std::move(addr));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1439,9 +1529,26 @@ int parse_config(const char *opt, const char *optarg,
|
||||
return -1;
|
||||
}
|
||||
|
||||
listenerconf.host = strcopy(host);
|
||||
listenerconf.port = port;
|
||||
listenerconf.host_unix = false;
|
||||
addr.host = ImmutableString(host);
|
||||
addr.port = port;
|
||||
|
||||
if (util::numeric_host(host, AF_INET)) {
|
||||
addr.family = AF_INET;
|
||||
listenerconf.addrs.push_back(std::move(addr));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (util::numeric_host(host, AF_INET6)) {
|
||||
addr.family = AF_INET6;
|
||||
listenerconf.addrs.push_back(std::move(addr));
|
||||
return 0;
|
||||
}
|
||||
|
||||
addr.family = AF_INET;
|
||||
listenerconf.addrs.push_back(addr);
|
||||
|
||||
addr.family = AF_INET6;
|
||||
listenerconf.addrs.push_back(std::move(addr));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1511,7 +1618,7 @@ int parse_config(const char *opt, const char *optarg,
|
||||
return parse_duration(&mod_config()->http2.timeout.stream_write, opt,
|
||||
optarg);
|
||||
case SHRPX_OPTID_ACCESSLOG_FILE:
|
||||
mod_config()->logging.access.file = strcopy(optarg);
|
||||
mod_config()->logging.access.file = optarg;
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_ACCESSLOG_SYSLOG:
|
||||
@@ -1523,7 +1630,7 @@ int parse_config(const char *opt, const char *optarg,
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_ERRORLOG_FILE:
|
||||
mod_config()->logging.error.file = strcopy(optarg);
|
||||
mod_config()->logging.error.file = optarg;
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_ERRORLOG_SYSLOG:
|
||||
@@ -1617,7 +1724,7 @@ int parse_config(const char *opt, const char *optarg,
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_PID_FILE:
|
||||
mod_config()->pid_file = strcopy(optarg);
|
||||
mod_config()->pid_file = optarg;
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_USER: {
|
||||
@@ -1627,14 +1734,14 @@ int parse_config(const char *opt, const char *optarg,
|
||||
<< strerror(errno);
|
||||
return -1;
|
||||
}
|
||||
mod_config()->user = strcopy(pwd->pw_name);
|
||||
mod_config()->user = pwd->pw_name;
|
||||
mod_config()->uid = pwd->pw_uid;
|
||||
mod_config()->gid = pwd->pw_gid;
|
||||
|
||||
return 0;
|
||||
}
|
||||
case SHRPX_OPTID_PRIVATE_KEY_FILE:
|
||||
mod_config()->tls.private_key_file = strcopy(optarg);
|
||||
mod_config()->tls.private_key_file = optarg;
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_PRIVATE_KEY_PASSWD_FILE: {
|
||||
@@ -1643,16 +1750,16 @@ int parse_config(const char *opt, const char *optarg,
|
||||
LOG(ERROR) << opt << ": Couldn't read key file's passwd from " << optarg;
|
||||
return -1;
|
||||
}
|
||||
mod_config()->tls.private_key_passwd = strcopy(passwd);
|
||||
mod_config()->tls.private_key_passwd = passwd;
|
||||
|
||||
return 0;
|
||||
}
|
||||
case SHRPX_OPTID_CERTIFICATE_FILE:
|
||||
mod_config()->tls.cert_file = strcopy(optarg);
|
||||
mod_config()->tls.cert_file = optarg;
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_DH_PARAM_FILE:
|
||||
mod_config()->tls.dh_param_file = strcopy(optarg);
|
||||
mod_config()->tls.dh_param_file = optarg;
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_SUBCERT: {
|
||||
@@ -1693,7 +1800,7 @@ int parse_config(const char *opt, const char *optarg,
|
||||
return 0;
|
||||
}
|
||||
case SHRPX_OPTID_CIPHERS:
|
||||
mod_config()->tls.ciphers = strcopy(optarg);
|
||||
mod_config()->tls.ciphers = optarg;
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_CLIENT:
|
||||
@@ -1705,15 +1812,21 @@ int parse_config(const char *opt, const char *optarg,
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_CACERT:
|
||||
mod_config()->tls.cacert = strcopy(optarg);
|
||||
mod_config()->tls.cacert = optarg;
|
||||
|
||||
return 0;
|
||||
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;
|
||||
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;
|
||||
case SHRPX_OPTID_BACKEND_HTTP_PROXY_URI: {
|
||||
@@ -1790,25 +1903,23 @@ int parse_config(const char *opt, const char *optarg,
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_VERIFY_CLIENT_CACERT:
|
||||
mod_config()->tls.client_verify.cacert = strcopy(optarg);
|
||||
mod_config()->tls.client_verify.cacert = optarg;
|
||||
|
||||
return 0;
|
||||
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;
|
||||
case SHRPX_OPTID_CLIENT_CERT_FILE:
|
||||
mod_config()->tls.client.cert_file = strcopy(optarg);
|
||||
mod_config()->tls.client.cert_file = optarg;
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_FRONTEND_HTTP2_DUMP_REQUEST_HEADER:
|
||||
mod_config()->http2.upstream.debug.dump.request_header_file =
|
||||
strcopy(optarg);
|
||||
mod_config()->http2.upstream.debug.dump.request_header_file = optarg;
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER:
|
||||
mod_config()->http2.upstream.debug.dump.response_header_file =
|
||||
strcopy(optarg);
|
||||
mod_config()->http2.upstream.debug.dump.response_header_file = optarg;
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_HTTP2_NO_COOKIE_CRUMBLING:
|
||||
@@ -1871,7 +1982,7 @@ int parse_config(const char *opt, const char *optarg,
|
||||
case SHRPX_OPTID_ADD_REQUEST_HEADER:
|
||||
case SHRPX_OPTID_ADD_RESPONSE_HEADER: {
|
||||
auto p = parse_header(optarg);
|
||||
if (p.first.empty()) {
|
||||
if (p.name.empty()) {
|
||||
LOG(ERROR) << opt << ": invalid header field: " << optarg;
|
||||
return -1;
|
||||
}
|
||||
@@ -1969,7 +2080,7 @@ int parse_config(const char *opt, const char *optarg,
|
||||
return parse_uint(&mod_config()->http2.downstream.connections_per_worker,
|
||||
opt, optarg);
|
||||
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;
|
||||
case SHRPX_OPTID_OCSP_UPDATE_INTERVAL:
|
||||
@@ -1979,10 +2090,24 @@ int parse_config(const char *opt, const char *optarg,
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_HEADER_FIELD_BUFFER:
|
||||
return parse_uint_with_unit(&mod_config()->http.header_field_buffer, opt,
|
||||
optarg);
|
||||
LOG(WARN) << opt
|
||||
<< ": deprecated. Use request-header-field-buffer instead.";
|
||||
// fall through
|
||||
case SHRPX_OPTID_REQUEST_HEADER_FIELD_BUFFER:
|
||||
return parse_uint_with_unit(&mod_config()->http.request_header_field_buffer,
|
||||
opt, optarg);
|
||||
case SHRPX_OPTID_MAX_HEADER_FIELDS:
|
||||
return parse_uint(&mod_config()->http.max_header_fields, opt, optarg);
|
||||
LOG(WARN) << opt << ": deprecated. Use max-request-header-fields instead.";
|
||||
// fall through
|
||||
case SHRPX_OPTID_MAX_REQUEST_HEADER_FIELDS:
|
||||
return parse_uint(&mod_config()->http.max_request_header_fields, opt,
|
||||
optarg);
|
||||
case SHRPX_OPTID_RESPONSE_HEADER_FIELD_BUFFER:
|
||||
return parse_uint_with_unit(
|
||||
&mod_config()->http.response_header_field_buffer, opt, optarg);
|
||||
case SHRPX_OPTID_MAX_RESPONSE_HEADER_FIELDS:
|
||||
return parse_uint(&mod_config()->http.max_response_header_fields, opt,
|
||||
optarg);
|
||||
case SHRPX_OPTID_INCLUDE: {
|
||||
if (included_set.count(optarg)) {
|
||||
LOG(ERROR) << opt << ": " << optarg << " has already been included";
|
||||
@@ -2023,7 +2148,7 @@ int parse_config(const char *opt, const char *optarg,
|
||||
}
|
||||
|
||||
auto &memcachedconf = mod_config()->tls.session_cache.memcached;
|
||||
memcachedconf.host = strcopy(host);
|
||||
memcachedconf.host = host;
|
||||
memcachedconf.port = port;
|
||||
|
||||
return 0;
|
||||
@@ -2035,7 +2160,7 @@ int parse_config(const char *opt, const char *optarg,
|
||||
}
|
||||
|
||||
auto &memcachedconf = mod_config()->tls.ticket.memcached;
|
||||
memcachedconf.host = strcopy(host);
|
||||
memcachedconf.host = host;
|
||||
memcachedconf.port = port;
|
||||
|
||||
return 0;
|
||||
@@ -2076,7 +2201,7 @@ int parse_config(const char *opt, const char *optarg,
|
||||
|
||||
case SHRPX_OPTID_MRUBY_FILE:
|
||||
#ifdef HAVE_MRUBY
|
||||
mod_config()->mruby_file = strcopy(optarg);
|
||||
mod_config()->mruby_file = optarg;
|
||||
#else // !HAVE_MRUBY
|
||||
LOG(WARN) << opt
|
||||
<< ": ignored because mruby support is disabled at build time.";
|
||||
@@ -2148,6 +2273,47 @@ int parse_config(const char *opt, const char *optarg,
|
||||
|
||||
return 0;
|
||||
}
|
||||
case SHRPX_OPTID_NO_HTTP2_CIPHER_BLACK_LIST:
|
||||
mod_config()->tls.no_http2_cipher_black_list = util::strieq(optarg, "yes");
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_BACKEND_HTTP1_TLS:
|
||||
mod_config()->conn.downstream.http1_tls = util::strieq(optarg, "yes");
|
||||
|
||||
return 0;
|
||||
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:
|
||||
LOG(WARN) << "conf: ignored";
|
||||
|
||||
@@ -2328,17 +2494,15 @@ int int_syslog_facility(const char *strfacility) {
|
||||
}
|
||||
|
||||
namespace {
|
||||
size_t
|
||||
match_downstream_addr_group_host(const Router &router, const std::string &host,
|
||||
const char *path, size_t pathlen,
|
||||
const std::vector<DownstreamAddrGroup> &groups,
|
||||
size_t catch_all) {
|
||||
if (pathlen == 0 || *path != '/') {
|
||||
auto group = router.match(host, "/", 1);
|
||||
size_t match_downstream_addr_group_host(
|
||||
const Router &router, const StringRef &host, const StringRef &path,
|
||||
const std::vector<DownstreamAddrGroup> &groups, size_t catch_all) {
|
||||
if (path.empty() || path[0] != '/') {
|
||||
auto group = router.match(host, StringRef::from_lit("/"));
|
||||
if (group != -1) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "Found pattern with query " << host
|
||||
<< ", matched pattern=" << groups[group].pattern.get();
|
||||
<< ", matched pattern=" << groups[group].pattern;
|
||||
}
|
||||
return group;
|
||||
}
|
||||
@@ -2347,24 +2511,23 @@ match_downstream_addr_group_host(const Router &router, const std::string &host,
|
||||
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
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 (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "Found pattern with query " << host
|
||||
<< std::string(path, pathlen)
|
||||
<< ", matched pattern=" << groups[group].pattern.get();
|
||||
LOG(INFO) << "Found pattern with query " << host << path
|
||||
<< ", matched pattern=" << groups[group].pattern;
|
||||
}
|
||||
return group;
|
||||
}
|
||||
|
||||
group = router.match("", path, pathlen);
|
||||
group = router.match("", path);
|
||||
if (group != -1) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "Found pattern with query " << std::string(path, pathlen)
|
||||
<< ", matched pattern=" << groups[group].pattern.get();
|
||||
LOG(INFO) << "Found pattern with query " << path
|
||||
<< ", matched pattern=" << groups[group].pattern;
|
||||
}
|
||||
return group;
|
||||
}
|
||||
@@ -2376,11 +2539,9 @@ match_downstream_addr_group_host(const Router &router, const std::string &host,
|
||||
}
|
||||
} // namespace
|
||||
|
||||
size_t
|
||||
match_downstream_addr_group(const Router &router, const std::string &hostport,
|
||||
const std::string &raw_path,
|
||||
const std::vector<DownstreamAddrGroup> &groups,
|
||||
size_t catch_all) {
|
||||
size_t match_downstream_addr_group(
|
||||
const Router &router, const StringRef &hostport, const StringRef &raw_path,
|
||||
const std::vector<DownstreamAddrGroup> &groups, size_t catch_all) {
|
||||
if (std::find(std::begin(hostport), std::end(hostport), '/') !=
|
||||
std::end(hostport)) {
|
||||
// We use '/' specially, and if '/' is included in host, it breaks
|
||||
@@ -2390,12 +2551,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 query = std::find(std::begin(raw_path), fragment, '?');
|
||||
auto path = raw_path.c_str();
|
||||
auto pathlen = query - std::begin(raw_path);
|
||||
auto path = StringRef{std::begin(raw_path), query};
|
||||
|
||||
if (hostport.empty()) {
|
||||
return match_downstream_addr_group_host(router, hostport, path, pathlen,
|
||||
groups, catch_all);
|
||||
return match_downstream_addr_group_host(router, hostport, path, groups,
|
||||
catch_all);
|
||||
}
|
||||
|
||||
std::string host;
|
||||
@@ -2418,7 +2578,7 @@ match_downstream_addr_group(const Router &router, const std::string &hostport,
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -52,12 +52,15 @@
|
||||
|
||||
#include "shrpx_router.h"
|
||||
#include "template.h"
|
||||
#include "http2.h"
|
||||
#include "network.h"
|
||||
|
||||
using namespace nghttp2;
|
||||
|
||||
namespace shrpx {
|
||||
|
||||
struct LogFragment;
|
||||
class ConnectBlocker;
|
||||
|
||||
namespace ssl {
|
||||
|
||||
@@ -196,22 +199,37 @@ constexpr char SHRPX_OPT_STRIP_INCOMING_FORWARDED[] =
|
||||
"strip-incoming-forwarded";
|
||||
constexpr static char SHRPX_OPT_FORWARDED_BY[] = "forwarded-by";
|
||||
constexpr char SHRPX_OPT_FORWARDED_FOR[] = "forwarded-for";
|
||||
constexpr char SHRPX_OPT_REQUEST_HEADER_FIELD_BUFFER[] =
|
||||
"request-header-field-buffer";
|
||||
constexpr char SHRPX_OPT_MAX_REQUEST_HEADER_FIELDS[] =
|
||||
"max-request-header-fields";
|
||||
constexpr char SHRPX_OPT_RESPONSE_HEADER_FIELD_BUFFER[] =
|
||||
"response-header-field-buffer";
|
||||
constexpr char SHRPX_OPT_MAX_RESPONSE_HEADER_FIELDS[] =
|
||||
"max-response-header-fields";
|
||||
constexpr char SHRPX_OPT_NO_HTTP2_CIPHER_BLACK_LIST[] =
|
||||
"no-http2-cipher-black-list";
|
||||
constexpr char SHRPX_OPT_BACKEND_HTTP1_TLS[] = "backend-http1-tls";
|
||||
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;
|
||||
|
||||
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_forwarded_param {
|
||||
@@ -239,18 +257,41 @@ struct AltSvc {
|
||||
uint16_t port;
|
||||
};
|
||||
|
||||
struct DownstreamAddr {
|
||||
DownstreamAddr() : addr{}, port(0), host_unix(false) {}
|
||||
DownstreamAddr(const DownstreamAddr &other);
|
||||
DownstreamAddr(DownstreamAddr &&) = default;
|
||||
DownstreamAddr &operator=(const DownstreamAddr &other);
|
||||
DownstreamAddr &operator=(DownstreamAddr &&other) = default;
|
||||
struct UpstreamAddr {
|
||||
// The frontend address (e.g., FQDN, hostname, IP address). If
|
||||
// |host_unix| is true, this is UNIX domain socket path.
|
||||
ImmutableString host;
|
||||
// For TCP socket, this is <IP address>:<PORT>. For IPv6 address,
|
||||
// address is surrounded by square brackets. If socket is UNIX
|
||||
// domain socket, this is "localhost".
|
||||
ImmutableString hostport;
|
||||
// frontend port. 0 if |host_unix| is true.
|
||||
uint16_t port;
|
||||
// For TCP socket, this is either AF_INET or AF_INET6. For UNIX
|
||||
// domain socket, this is 0.
|
||||
int family;
|
||||
// true if |host| contains UNIX domain socket path.
|
||||
bool host_unix;
|
||||
int fd;
|
||||
};
|
||||
|
||||
struct TLSSessionCache {
|
||||
// ASN1 representation of SSL_SESSION object. See
|
||||
// i2d_SSL_SESSION(3SSL).
|
||||
std::vector<uint8_t> session_data;
|
||||
// The last time stamp when this cache entry is created or updated.
|
||||
ev_tstamp last_updated;
|
||||
};
|
||||
|
||||
struct DownstreamAddr {
|
||||
Address addr;
|
||||
// backend address. If |host_unix| is true, this is UNIX domain
|
||||
// socket path.
|
||||
ImmutableString host;
|
||||
ImmutableString hostport;
|
||||
ConnectBlocker *connect_blocker;
|
||||
// Client side TLS session cache
|
||||
TLSSessionCache tls_session_cache;
|
||||
// backend port. 0 if |host_unix| is true.
|
||||
uint16_t port;
|
||||
// true if |host| contains UNIX domain socket path.
|
||||
@@ -258,13 +299,10 @@ struct DownstreamAddr {
|
||||
};
|
||||
|
||||
struct DownstreamAddrGroup {
|
||||
DownstreamAddrGroup(const std::string &pattern) : pattern(strcopy(pattern)) {}
|
||||
DownstreamAddrGroup(const DownstreamAddrGroup &other);
|
||||
DownstreamAddrGroup(DownstreamAddrGroup &&) = default;
|
||||
DownstreamAddrGroup &operator=(const DownstreamAddrGroup &other);
|
||||
DownstreamAddrGroup &operator=(DownstreamAddrGroup &&) = default;
|
||||
DownstreamAddrGroup(const StringRef &pattern)
|
||||
: pattern(pattern.c_str(), pattern.size()) {}
|
||||
|
||||
std::unique_ptr<char[]> pattern;
|
||||
ImmutableString pattern;
|
||||
std::vector<DownstreamAddr> addrs;
|
||||
};
|
||||
|
||||
@@ -303,7 +341,12 @@ struct TLSConfig {
|
||||
struct {
|
||||
Address addr;
|
||||
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;
|
||||
// Maximum number of retries when getting TLS ticket key from
|
||||
// mamcached, due to network error.
|
||||
@@ -311,6 +354,10 @@ struct TLSConfig {
|
||||
// Maximum number of consecutive error from memcached, when this
|
||||
// limit reached, TLS ticket is disabled.
|
||||
size_t max_fail;
|
||||
// Address family of memcached connection. One of either
|
||||
// AF_INET, AF_INET6 or AF_UNSPEC.
|
||||
int family;
|
||||
bool tls;
|
||||
} memcached;
|
||||
std::vector<std::string> files;
|
||||
const EVP_CIPHER *cipher;
|
||||
@@ -323,7 +370,16 @@ struct TLSConfig {
|
||||
struct {
|
||||
Address addr;
|
||||
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;
|
||||
} session_cache;
|
||||
|
||||
@@ -336,7 +392,7 @@ struct TLSConfig {
|
||||
// OCSP realted configurations
|
||||
struct {
|
||||
ev_tstamp update_interval;
|
||||
std::unique_ptr<char[]> fetch_ocsp_response_file;
|
||||
ImmutableString fetch_ocsp_response_file;
|
||||
bool disabled;
|
||||
} ocsp;
|
||||
|
||||
@@ -344,14 +400,14 @@ struct TLSConfig {
|
||||
struct {
|
||||
// Path to file containing CA certificate solely used for client
|
||||
// certificate validation
|
||||
std::unique_ptr<char[]> cacert;
|
||||
ImmutableString cacert;
|
||||
bool enabled;
|
||||
} client_verify;
|
||||
|
||||
// Client private key and certificate used in backend connections.
|
||||
struct {
|
||||
std::unique_ptr<char[]> private_key_file;
|
||||
std::unique_ptr<char[]> cert_file;
|
||||
ImmutableString private_key_file;
|
||||
ImmutableString cert_file;
|
||||
} client;
|
||||
|
||||
// The list of (private key file, certificate file) pair
|
||||
@@ -367,13 +423,14 @@ struct TLSConfig {
|
||||
long int tls_proto_mask;
|
||||
std::string backend_sni_name;
|
||||
std::chrono::seconds session_timeout;
|
||||
std::unique_ptr<char[]> private_key_file;
|
||||
std::unique_ptr<char[]> private_key_passwd;
|
||||
std::unique_ptr<char[]> cert_file;
|
||||
std::unique_ptr<char[]> dh_param_file;
|
||||
std::unique_ptr<char[]> ciphers;
|
||||
std::unique_ptr<char[]> cacert;
|
||||
ImmutableString private_key_file;
|
||||
ImmutableString private_key_passwd;
|
||||
ImmutableString cert_file;
|
||||
ImmutableString dh_param_file;
|
||||
ImmutableString ciphers;
|
||||
ImmutableString cacert;
|
||||
bool insecure;
|
||||
bool no_http2_cipher_black_list;
|
||||
};
|
||||
|
||||
struct HttpConfig {
|
||||
@@ -397,11 +454,13 @@ struct HttpConfig {
|
||||
bool strip_incoming;
|
||||
} xff;
|
||||
std::vector<AltSvc> altsvcs;
|
||||
std::vector<std::pair<std::string, std::string>> add_request_headers;
|
||||
std::vector<std::pair<std::string, std::string>> add_response_headers;
|
||||
Headers add_request_headers;
|
||||
Headers add_response_headers;
|
||||
StringRef server_name;
|
||||
size_t header_field_buffer;
|
||||
size_t max_header_fields;
|
||||
size_t request_header_field_buffer;
|
||||
size_t max_request_header_fields;
|
||||
size_t response_header_field_buffer;
|
||||
size_t max_response_header_fields;
|
||||
bool no_via;
|
||||
bool no_location_rewrite;
|
||||
bool no_host_rewrite;
|
||||
@@ -411,8 +470,8 @@ struct Http2Config {
|
||||
struct {
|
||||
struct {
|
||||
struct {
|
||||
std::unique_ptr<char[]> request_header_file;
|
||||
std::unique_ptr<char[]> response_header_file;
|
||||
ImmutableString request_header_file;
|
||||
ImmutableString response_header_file;
|
||||
FILE *request_header;
|
||||
FILE *response_header;
|
||||
} dump;
|
||||
@@ -442,12 +501,12 @@ struct Http2Config {
|
||||
struct LoggingConfig {
|
||||
struct {
|
||||
std::vector<LogFragment> format;
|
||||
std::unique_ptr<char[]> file;
|
||||
ImmutableString file;
|
||||
// Send accesslog to syslog, ignoring accesslog_file.
|
||||
bool syslog;
|
||||
} access;
|
||||
struct {
|
||||
std::unique_ptr<char[]> file;
|
||||
ImmutableString file;
|
||||
// Send errorlog to syslog, ignoring errorlog_file.
|
||||
bool syslog;
|
||||
} error;
|
||||
@@ -464,14 +523,8 @@ struct ConnectionConfig {
|
||||
struct {
|
||||
ev_tstamp sleep;
|
||||
} timeout;
|
||||
// address of frontend connection. This could be a path to UNIX
|
||||
// domain socket. In this case, |host_unix| must be true.
|
||||
std::unique_ptr<char[]> host;
|
||||
// frontend listening port. 0 if frontend listens on UNIX domain
|
||||
// socket, in this case |host_unix| must be true.
|
||||
uint16_t port;
|
||||
// true if host contains UNIX domain socket path
|
||||
bool host_unix;
|
||||
// address of frontend acceptors
|
||||
std::vector<UpstreamAddr> addrs;
|
||||
int backlog;
|
||||
// TCP fastopen. If this is positive, it is passed to
|
||||
// setsockopt() along with TCP_FASTOPEN.
|
||||
@@ -508,12 +561,12 @@ struct ConnectionConfig {
|
||||
size_t response_buffer_size;
|
||||
// downstream protocol; this will be determined by given options.
|
||||
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;
|
||||
// true if IPv4 only; ipv4 and ipv6 are mutually exclusive; and
|
||||
// (ipv4 && ipv6) must be false.
|
||||
bool ipv4;
|
||||
// true if IPv6 only
|
||||
bool ipv6;
|
||||
bool http1_tls;
|
||||
} downstream;
|
||||
};
|
||||
|
||||
@@ -525,10 +578,10 @@ struct Config {
|
||||
TLSConfig tls;
|
||||
LoggingConfig logging;
|
||||
ConnectionConfig conn;
|
||||
std::unique_ptr<char[]> pid_file;
|
||||
std::unique_ptr<char[]> conf_path;
|
||||
std::unique_ptr<char[]> user;
|
||||
std::unique_ptr<char[]> mruby_file;
|
||||
ImmutableString pid_file;
|
||||
ImmutableString conf_path;
|
||||
ImmutableString user;
|
||||
ImmutableString mruby_file;
|
||||
char **original_argv;
|
||||
char **argv;
|
||||
char *cwd;
|
||||
@@ -573,7 +626,7 @@ std::string read_passwd_from_file(const char *filename);
|
||||
// like "NAME: VALUE". We require that NAME is non empty string. ":"
|
||||
// is allowed at the start of the NAME, but NAME == ":" is not
|
||||
// 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);
|
||||
|
||||
@@ -600,7 +653,7 @@ read_tls_ticket_key_file(const std::vector<std::string> &files,
|
||||
// group. The catch-all group index is given in |catch_all|. All
|
||||
// patterns are given in |groups|.
|
||||
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);
|
||||
|
||||
} // namespace shrpx
|
||||
|
||||
@@ -38,32 +38,32 @@ namespace shrpx {
|
||||
|
||||
void test_shrpx_config_parse_header(void) {
|
||||
auto p = parse_header("a: b");
|
||||
CU_ASSERT("a" == p.first);
|
||||
CU_ASSERT("b" == p.second);
|
||||
CU_ASSERT("a" == p.name);
|
||||
CU_ASSERT("b" == p.value);
|
||||
|
||||
p = parse_header("a: b");
|
||||
CU_ASSERT("a" == p.first);
|
||||
CU_ASSERT("b" == p.second);
|
||||
CU_ASSERT("a" == p.name);
|
||||
CU_ASSERT("b" == p.value);
|
||||
|
||||
p = parse_header(":a: b");
|
||||
CU_ASSERT(p.first.empty());
|
||||
CU_ASSERT(p.name.empty());
|
||||
|
||||
p = parse_header("a: :b");
|
||||
CU_ASSERT("a" == p.first);
|
||||
CU_ASSERT(":b" == p.second);
|
||||
CU_ASSERT("a" == p.name);
|
||||
CU_ASSERT(":b" == p.value);
|
||||
|
||||
p = parse_header(": b");
|
||||
CU_ASSERT(p.first.empty());
|
||||
CU_ASSERT(p.name.empty());
|
||||
|
||||
p = parse_header("alpha: bravo charlie");
|
||||
CU_ASSERT("alpha" == p.first);
|
||||
CU_ASSERT("bravo charlie" == p.second);
|
||||
CU_ASSERT("alpha" == p.name);
|
||||
CU_ASSERT("bravo charlie" == p.value);
|
||||
|
||||
p = parse_header("a,: b");
|
||||
CU_ASSERT(p.first.empty());
|
||||
CU_ASSERT(p.name.empty());
|
||||
|
||||
p = parse_header("a: b\x0a");
|
||||
CU_ASSERT(p.first.empty());
|
||||
CU_ASSERT(p.name.empty());
|
||||
}
|
||||
|
||||
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) {
|
||||
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,
|
||||
|
||||
@@ -26,20 +26,16 @@
|
||||
|
||||
namespace shrpx {
|
||||
|
||||
namespace {
|
||||
const ev_tstamp INITIAL_SLEEP = 2.;
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void connect_blocker_cb(struct ev_loop *loop, ev_timer *w, int revents) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "unblock downstream connection";
|
||||
LOG(INFO) << "Unblock";
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
ConnectBlocker::ConnectBlocker(struct ev_loop *loop)
|
||||
: loop_(loop), sleep_(INITIAL_SLEEP) {
|
||||
ConnectBlocker::ConnectBlocker(std::mt19937 &gen, struct ev_loop *loop)
|
||||
: gen_(gen), loop_(loop), fail_count_(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_); }
|
||||
|
||||
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() {
|
||||
if (ev_is_active(&timer_)) {
|
||||
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_);
|
||||
}
|
||||
|
||||
|
||||
@@ -27,13 +27,15 @@
|
||||
|
||||
#include "shrpx.h"
|
||||
|
||||
#include <random>
|
||||
|
||||
#include <ev.h>
|
||||
|
||||
namespace shrpx {
|
||||
|
||||
class ConnectBlocker {
|
||||
public:
|
||||
ConnectBlocker(struct ev_loop *loop);
|
||||
ConnectBlocker(std::mt19937 &gen, struct ev_loop *loop);
|
||||
~ConnectBlocker();
|
||||
|
||||
// Returns true if making connection is not allowed.
|
||||
@@ -41,14 +43,18 @@ public:
|
||||
// Call this function if connect operation succeeded. This will
|
||||
// reset sleep_ to minimum value.
|
||||
void on_success();
|
||||
// Call this function if connect operation failed. This will start
|
||||
// timer and blocks connection establishment for sleep_ seconds.
|
||||
// Call this function if connect operations failed. This will start
|
||||
// timer and blocks connection establishment with exponential
|
||||
// backoff.
|
||||
void on_failure();
|
||||
|
||||
private:
|
||||
std::mt19937 gen_;
|
||||
ev_timer timer_;
|
||||
struct ev_loop *loop_;
|
||||
ev_tstamp sleep_;
|
||||
// The number of consecutive connection failure. Reset to 0 on
|
||||
// success.
|
||||
size_t fail_count_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -51,8 +51,12 @@ Connection::Connection(struct ev_loop *loop, int fd, SSL *ssl,
|
||||
: tls{DefaultMemchunks(mcpool), DefaultPeekMemchunks(mcpool)},
|
||||
wlimit(loop, &wev, write_limit.rate, write_limit.burst),
|
||||
rlimit(loop, &rev, read_limit.rate, read_limit.burst, this),
|
||||
writecb(writecb), readcb(readcb), timeoutcb(timeoutcb), loop(loop),
|
||||
data(data), fd(fd),
|
||||
writecb(writecb),
|
||||
readcb(readcb),
|
||||
timeoutcb(timeoutcb),
|
||||
loop(loop),
|
||||
data(data),
|
||||
fd(fd),
|
||||
tls_dyn_rec_warmup_threshold(tls_dyn_rec_warmup_threshold),
|
||||
tls_dyn_rec_idle_timeout(tls_dyn_rec_idle_timeout) {
|
||||
|
||||
@@ -484,10 +488,17 @@ int Connection::check_http2_requirement() {
|
||||
!util::check_h2_is_selected(next_proto, next_proto_len)) {
|
||||
return 0;
|
||||
}
|
||||
if (!nghttp2::ssl::check_http2_requirement(tls.ssl)) {
|
||||
if (!nghttp2::ssl::check_http2_tls_version(tls.ssl)) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "TLSv1.2 and/or black listed cipher suite was negotiated. "
|
||||
"HTTP/2 must not be used.";
|
||||
LOG(INFO) << "TLSv1.2 was not negotiated. HTTP/2 must not be used.";
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
if (!get_config()->tls.no_http2_cipher_black_list &&
|
||||
nghttp2::ssl::check_http2_cipher_black_list(tls.ssl)) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "The negotiated cipher suite is in HTTP/2 cipher suite "
|
||||
"black list. HTTP/2 must not be used.";
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -107,9 +107,12 @@ std::random_device rd;
|
||||
} // namespace
|
||||
|
||||
ConnectionHandler::ConnectionHandler(struct ev_loop *loop)
|
||||
: gen_(rd()), single_worker_(nullptr), loop_(loop),
|
||||
: gen_(rd()),
|
||||
single_worker_(nullptr),
|
||||
loop_(loop),
|
||||
tls_ticket_key_memcached_get_retry_count_(0),
|
||||
tls_ticket_key_memcached_fail_count_(0), worker_round_robin_cnt_(0),
|
||||
tls_ticket_key_memcached_fail_count_(0),
|
||||
worker_round_robin_cnt_(0),
|
||||
graceful_shutdown_(false) {
|
||||
ev_timer_init(&disable_acceptor_timer_, acceptor_disable_cb, 0., 0.);
|
||||
disable_acceptor_timer_.data = this;
|
||||
@@ -180,7 +183,7 @@ int ConnectionHandler::create_single_worker() {
|
||||
nb_.get()
|
||||
#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
|
||||
nb_.get()
|
||||
#endif // HAVE_NEVERBLEED
|
||||
@@ -190,8 +193,23 @@ int ConnectionHandler::create_single_worker() {
|
||||
all_ssl_ctx_.push_back(cl_ssl_ctx);
|
||||
}
|
||||
|
||||
single_worker_ = make_unique<Worker>(loop_, sv_ssl_ctx, cl_ssl_ctx, cert_tree,
|
||||
ticket_keys_);
|
||||
auto &tlsconf = get_config()->tls;
|
||||
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
|
||||
if (single_worker_->create_mruby_context() != 0) {
|
||||
return -1;
|
||||
@@ -212,7 +230,7 @@ int ConnectionHandler::create_worker_thread(size_t num) {
|
||||
nb_.get()
|
||||
#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
|
||||
nb_.get()
|
||||
#endif // HAVE_NEVERBLEED
|
||||
@@ -222,11 +240,25 @@ int ConnectionHandler::create_worker_thread(size_t num) {
|
||||
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) {
|
||||
auto loop = ev_loop_new(0);
|
||||
|
||||
auto worker = make_unique<Worker>(loop, sv_ssl_ctx, cl_ssl_ctx, cert_tree,
|
||||
ticket_keys_);
|
||||
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);
|
||||
}
|
||||
auto worker =
|
||||
make_unique<Worker>(loop, sv_ssl_ctx, cl_ssl_ctx, session_cache_ssl_ctx,
|
||||
cert_tree, ticket_keys_);
|
||||
#ifdef HAVE_MRUBY
|
||||
if (worker->create_mruby_context() != 0) {
|
||||
return -1;
|
||||
@@ -294,7 +326,8 @@ void ConnectionHandler::graceful_shutdown_worker() {
|
||||
#endif // NOTHREADS
|
||||
}
|
||||
|
||||
int ConnectionHandler::handle_connection(int fd, sockaddr *addr, int addrlen) {
|
||||
int ConnectionHandler::handle_connection(int fd, sockaddr *addr, int addrlen,
|
||||
const UpstreamAddr *faddr) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LLOG(INFO, this) << "Accepted connection. fd=" << fd;
|
||||
}
|
||||
@@ -314,7 +347,7 @@ int ConnectionHandler::handle_connection(int fd, sockaddr *addr, int addrlen) {
|
||||
}
|
||||
|
||||
auto client =
|
||||
ssl::accept_connection(single_worker_.get(), fd, addr, addrlen);
|
||||
ssl::accept_connection(single_worker_.get(), fd, addr, addrlen, faddr);
|
||||
if (!client) {
|
||||
LLOG(ERROR, this) << "ClientHandler creation failed";
|
||||
|
||||
@@ -335,6 +368,7 @@ int ConnectionHandler::handle_connection(int fd, sockaddr *addr, int addrlen) {
|
||||
wev.client_fd = fd;
|
||||
memcpy(&wev.client_addr, addr, addrlen);
|
||||
wev.client_addrlen = addrlen;
|
||||
wev.faddr = faddr;
|
||||
|
||||
workers_[idx]->send(wev);
|
||||
|
||||
@@ -349,39 +383,19 @@ Worker *ConnectionHandler::get_single_worker() const {
|
||||
return single_worker_.get();
|
||||
}
|
||||
|
||||
void ConnectionHandler::set_acceptor(std::unique_ptr<AcceptHandler> h) {
|
||||
acceptor_ = std::move(h);
|
||||
}
|
||||
|
||||
AcceptHandler *ConnectionHandler::get_acceptor() const {
|
||||
return acceptor_.get();
|
||||
}
|
||||
|
||||
void ConnectionHandler::set_acceptor6(std::unique_ptr<AcceptHandler> h) {
|
||||
acceptor6_ = std::move(h);
|
||||
}
|
||||
|
||||
AcceptHandler *ConnectionHandler::get_acceptor6() const {
|
||||
return acceptor6_.get();
|
||||
void ConnectionHandler::add_acceptor(std::unique_ptr<AcceptHandler> h) {
|
||||
acceptors_.push_back(std::move(h));
|
||||
}
|
||||
|
||||
void ConnectionHandler::enable_acceptor() {
|
||||
if (acceptor_) {
|
||||
acceptor_->enable();
|
||||
}
|
||||
|
||||
if (acceptor6_) {
|
||||
acceptor6_->enable();
|
||||
for (auto &a : acceptors_) {
|
||||
a->enable();
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionHandler::disable_acceptor() {
|
||||
if (acceptor_) {
|
||||
acceptor_->disable();
|
||||
}
|
||||
|
||||
if (acceptor6_) {
|
||||
acceptor6_->disable();
|
||||
for (auto &a : acceptors_) {
|
||||
a->disable();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -397,11 +411,8 @@ void ConnectionHandler::sleep_acceptor(ev_tstamp t) {
|
||||
}
|
||||
|
||||
void ConnectionHandler::accept_pending_connection() {
|
||||
if (acceptor_) {
|
||||
acceptor_->accept_connection();
|
||||
}
|
||||
if (acceptor6_) {
|
||||
acceptor6_->accept_connection();
|
||||
for (auto &a : acceptors_) {
|
||||
a->accept_connection();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -450,7 +461,8 @@ int ConnectionHandler::start_ocsp_update(const char *cert_file) {
|
||||
assert(!ev_is_active(&ocsp_.chldev));
|
||||
|
||||
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};
|
||||
char *const envp[] = {nullptr};
|
||||
|
||||
@@ -746,6 +758,22 @@ void ConnectionHandler::schedule_next_tls_ticket_key_memcached_get(
|
||||
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
|
||||
void ConnectionHandler::set_neverbleed(std::unique_ptr<neverbleed_t> nb) {
|
||||
nb_ = std::move(nb);
|
||||
|
||||
@@ -58,6 +58,7 @@ class Worker;
|
||||
struct WorkerStat;
|
||||
struct TicketKeys;
|
||||
class MemcachedDispatcher;
|
||||
struct UpstreamAddr;
|
||||
|
||||
struct OCSPUpdateContext {
|
||||
// ocsp response buffer
|
||||
@@ -79,7 +80,8 @@ class ConnectionHandler {
|
||||
public:
|
||||
ConnectionHandler(struct ev_loop *loop);
|
||||
~ConnectionHandler();
|
||||
int handle_connection(int fd, sockaddr *addr, int addrlen);
|
||||
int handle_connection(int fd, sockaddr *addr, int addrlen,
|
||||
const UpstreamAddr *faddr);
|
||||
// Creates Worker object for single threaded configuration.
|
||||
int create_single_worker();
|
||||
// Creates |num| Worker objects for multi threaded configuration.
|
||||
@@ -92,10 +94,7 @@ public:
|
||||
const std::shared_ptr<TicketKeys> &get_ticket_keys() const;
|
||||
struct ev_loop *get_loop() const;
|
||||
Worker *get_single_worker() const;
|
||||
void set_acceptor(std::unique_ptr<AcceptHandler> h);
|
||||
AcceptHandler *get_acceptor() const;
|
||||
void set_acceptor6(std::unique_ptr<AcceptHandler> h);
|
||||
AcceptHandler *get_acceptor6() const;
|
||||
void add_acceptor(std::unique_ptr<AcceptHandler> h);
|
||||
void enable_acceptor();
|
||||
void disable_acceptor();
|
||||
void sleep_acceptor(ev_tstamp t);
|
||||
@@ -130,6 +129,7 @@ public:
|
||||
on_tls_ticket_key_get_success(const std::shared_ptr<TicketKeys> &ticket_keys,
|
||||
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
|
||||
void set_neverbleed(std::unique_ptr<neverbleed_t> nb);
|
||||
@@ -154,10 +154,7 @@ private:
|
||||
// Worker object.
|
||||
std::shared_ptr<TicketKeys> ticket_keys_;
|
||||
struct ev_loop *loop_;
|
||||
// acceptor for IPv4 address or UNIX domain socket.
|
||||
std::unique_ptr<AcceptHandler> acceptor_;
|
||||
// acceptor for IPv6 address
|
||||
std::unique_ptr<AcceptHandler> acceptor6_;
|
||||
std::vector<std::unique_ptr<AcceptHandler>> acceptors_;
|
||||
#ifdef HAVE_NEVERBLEED
|
||||
std::unique_ptr<neverbleed_t> nb_;
|
||||
#endif // HAVE_NEVERBLEED
|
||||
|
||||
@@ -113,15 +113,26 @@ void downstream_wtimeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
|
||||
// upstream could be nullptr for unittests
|
||||
Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool,
|
||||
int32_t stream_id)
|
||||
: dlnext(nullptr), dlprev(nullptr), response_sent_body_length(0),
|
||||
: dlnext(nullptr),
|
||||
dlprev(nullptr),
|
||||
response_sent_body_length(0),
|
||||
request_start_time_(std::chrono::high_resolution_clock::now()),
|
||||
request_buf_(mcpool), response_buf_(mcpool), upstream_(upstream),
|
||||
blocked_link_(nullptr), num_retry_(0), stream_id_(stream_id),
|
||||
assoc_stream_id_(-1), downstream_stream_id_(-1),
|
||||
request_buf_(mcpool),
|
||||
response_buf_(mcpool),
|
||||
upstream_(upstream),
|
||||
blocked_link_(nullptr),
|
||||
num_retry_(0),
|
||||
stream_id_(stream_id),
|
||||
assoc_stream_id_(-1),
|
||||
downstream_stream_id_(-1),
|
||||
response_rst_stream_error_code_(NGHTTP2_NO_ERROR),
|
||||
request_state_(INITIAL), response_state_(INITIAL),
|
||||
dispatch_state_(DISPATCH_NONE), upgraded_(false), chunked_request_(false),
|
||||
chunked_response_(false), expect_final_response_(false),
|
||||
request_state_(INITIAL),
|
||||
response_state_(INITIAL),
|
||||
dispatch_state_(DISPATCH_NONE),
|
||||
upgraded_(false),
|
||||
chunked_request_(false),
|
||||
chunked_response_(false),
|
||||
expect_final_response_(false),
|
||||
request_pending_(false) {
|
||||
|
||||
auto &timeoutconf = get_config()->http2.timeout;
|
||||
@@ -226,15 +237,15 @@ void Downstream::force_resume_read() {
|
||||
}
|
||||
|
||||
namespace {
|
||||
const Headers::value_type *search_header_linear(const Headers &headers,
|
||||
const StringRef &name) {
|
||||
const Headers::value_type *res = nullptr;
|
||||
for (auto &kv : headers) {
|
||||
const Headers::value_type *
|
||||
search_header_linear_backwards(const Headers &headers, const StringRef &name) {
|
||||
for (auto it = headers.rbegin(); it != headers.rend(); ++it) {
|
||||
auto &kv = *it;
|
||||
if (kv.name == name) {
|
||||
res = &kv;
|
||||
return &kv;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
return nullptr;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -318,23 +329,20 @@ void Downstream::crumble_request_cookie(std::vector<nghttp2_nv> &nva) {
|
||||
}
|
||||
|
||||
namespace {
|
||||
void add_header(bool &key_prev, size_t &sum, Headers &headers, std::string name,
|
||||
std::string value) {
|
||||
void add_header(bool &key_prev, size_t &sum, Headers &headers,
|
||||
const StringRef &name, const StringRef &value, bool no_index,
|
||||
int32_t token) {
|
||||
key_prev = true;
|
||||
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 {
|
||||
void add_header(size_t &sum, Headers &headers, const uint8_t *name,
|
||||
size_t namelen, const uint8_t *value, size_t valuelen,
|
||||
bool no_index, int16_t token) {
|
||||
sum += namelen + valuelen;
|
||||
headers.emplace_back(
|
||||
std::string(reinterpret_cast<const char *>(name), namelen),
|
||||
std::string(reinterpret_cast<const char *>(value), valuelen), no_index,
|
||||
token);
|
||||
void add_header(size_t &sum, Headers &headers, const StringRef &name,
|
||||
const StringRef &value, bool no_index, int32_t token) {
|
||||
sum += name.size() + value.size();
|
||||
headers.emplace_back(name.str(), value.str(), no_index, token);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -345,6 +353,8 @@ void append_last_header_key(bool &key_prev, size_t &sum, Headers &headers,
|
||||
sum += len;
|
||||
auto &item = headers.back();
|
||||
item.name.append(data, len);
|
||||
util::inp_strlower(item.name);
|
||||
item.token = http2::lookup_token(item.name);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -358,67 +368,62 @@ void append_last_header_value(bool &key_prev, size_t &sum, Headers &headers,
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int FieldStore::index_headers() {
|
||||
http2::init_hdidx(hdidx_);
|
||||
int FieldStore::parse_content_length() {
|
||||
content_length = -1;
|
||||
|
||||
for (size_t i = 0; i < headers_.size(); ++i) {
|
||||
auto &kv = headers_[i];
|
||||
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) {
|
||||
for (auto &kv : headers_) {
|
||||
if (kv.token != http2::HD_CONTENT_LENGTH) {
|
||||
continue;
|
||||
}
|
||||
|
||||
kv.token = token;
|
||||
http2::index_header(hdidx_, token, i);
|
||||
|
||||
if (token == http2::HD_CONTENT_LENGTH) {
|
||||
auto len = util::parse_uint(kv.value);
|
||||
if (len == -1) {
|
||||
return -1;
|
||||
}
|
||||
if (content_length != -1) {
|
||||
return -1;
|
||||
}
|
||||
content_length = len;
|
||||
auto len = util::parse_uint(kv.value);
|
||||
if (len == -1) {
|
||||
return -1;
|
||||
}
|
||||
if (content_length != -1) {
|
||||
return -1;
|
||||
}
|
||||
content_length = len;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const Headers::value_type *FieldStore::header(int16_t token) const {
|
||||
return http2::get_header(hdidx_, token, headers_);
|
||||
const Headers::value_type *FieldStore::header(int32_t token) const {
|
||||
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) {
|
||||
return http2::get_header(hdidx_, token, headers_);
|
||||
Headers::value_type *FieldStore::header(int32_t token) {
|
||||
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 {
|
||||
return search_header_linear(headers_, name);
|
||||
return search_header_linear_backwards(headers_, name);
|
||||
}
|
||||
|
||||
void FieldStore::add_header(std::string name, std::string value) {
|
||||
shrpx::add_header(header_key_prev_, buffer_size_, headers_, std::move(name),
|
||||
std::move(value));
|
||||
void FieldStore::add_header_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(header_key_prev_, buffer_size_, headers_,
|
||||
StringRef{low_name}, value, no_index, token);
|
||||
}
|
||||
|
||||
void FieldStore::add_header(std::string name, std::string value,
|
||||
int16_t token) {
|
||||
http2::index_header(hdidx_, token, headers_.size());
|
||||
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::add_header_token(const StringRef &name, const StringRef &value,
|
||||
bool no_index, int32_t token) {
|
||||
shrpx::add_header(buffer_size_, headers_, name, value, no_index, token);
|
||||
}
|
||||
|
||||
void FieldStore::append_last_header_key(const char *data, size_t len) {
|
||||
@@ -431,23 +436,23 @@ void FieldStore::append_last_header_value(const char *data, size_t len) {
|
||||
data, len);
|
||||
}
|
||||
|
||||
void FieldStore::clear_headers() {
|
||||
headers_.clear();
|
||||
http2::init_hdidx(hdidx_);
|
||||
void FieldStore::clear_headers() { headers_.clear(); }
|
||||
|
||||
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,
|
||||
const uint8_t *value, size_t valuelen,
|
||||
bool no_index, int16_t token) {
|
||||
// we never index trailer fields. Header size limit should be
|
||||
// applied to all header and trailer fields combined.
|
||||
shrpx::add_header(buffer_size_, trailers_, name, namelen, value, valuelen,
|
||||
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::add_trailer_token(const StringRef &name,
|
||||
const StringRef &value, bool no_index,
|
||||
int32_t token) {
|
||||
// Header size limit should be applied to all header and trailer
|
||||
// fields combined.
|
||||
shrpx::add_header(buffer_size_, trailers_, name, value, no_index, token);
|
||||
}
|
||||
|
||||
void FieldStore::append_last_trailer_key(const char *data, size_t len) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user