mirror of
https://github.com/nghttp2/nghttp2.git
synced 2026-03-29 09:19:18 +08:00
Compare commits
200 Commits
v1.16.x
...
asio_threa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d1f4dafd73 | ||
|
|
13fc54c6a9 | ||
|
|
36a2023310 | ||
|
|
51e474c097 | ||
|
|
0df13452aa | ||
|
|
aad3e275d1 | ||
|
|
1649948e78 | ||
|
|
6d3e010ae7 | ||
|
|
7dddac081e | ||
|
|
588dd33241 | ||
|
|
2d9be885ec | ||
|
|
14ccb24be5 | ||
|
|
025ec85144 | ||
|
|
bd97886d8e | ||
|
|
0b1ddad62b | ||
|
|
540853bde8 | ||
|
|
c757f7d848 | ||
|
|
052f3a3871 | ||
|
|
2ae83e871b | ||
|
|
1cc08c0a51 | ||
|
|
16be89f9cc | ||
|
|
b72c5f104e | ||
|
|
7e6eb7e02a | ||
|
|
712b08e8ed | ||
|
|
fbf156d22d | ||
|
|
965a0e87e5 | ||
|
|
2b55ca1ce6 | ||
|
|
534c01896c | ||
|
|
c25197ac40 | ||
|
|
becae65257 | ||
|
|
ba9f2c3ae2 | ||
|
|
0387525b77 | ||
|
|
5311185333 | ||
|
|
2fc2a27ac1 | ||
|
|
db938afd66 | ||
|
|
89ddc47616 | ||
|
|
90b7849af1 | ||
|
|
3176e872b3 | ||
|
|
16206d5f67 | ||
|
|
0f33749790 | ||
|
|
5e7e4c0cc0 | ||
|
|
25503ad763 | ||
|
|
8f513fceca | ||
|
|
91af4ed70d | ||
|
|
685e926494 | ||
|
|
a2afd393ed | ||
|
|
4e9098bccf | ||
|
|
33aa327ef5 | ||
|
|
9067ff5eee | ||
|
|
efeede4192 | ||
|
|
6a8749873f | ||
|
|
b1b8308555 | ||
|
|
9b574a5a76 | ||
|
|
0567f1f038 | ||
|
|
4be5de1163 | ||
|
|
9db1c9467c | ||
|
|
3444b42d44 | ||
|
|
6595ae26ea | ||
|
|
41d8a3ac09 | ||
|
|
175001a8d9 | ||
|
|
7cf9e00283 | ||
|
|
8a3eb3f066 | ||
|
|
7e1a0d204b | ||
|
|
cbca2e35b5 | ||
|
|
fc9bdf024f | ||
|
|
3f97e6cd3a | ||
|
|
4fa150c494 | ||
|
|
e8b2508036 | ||
|
|
ac399e41ac | ||
|
|
95dd908834 | ||
|
|
9c7e54d9b5 | ||
|
|
3c03024881 | ||
|
|
36dfc0a56a | ||
|
|
55bf6cdb15 | ||
|
|
0abc220013 | ||
|
|
c28900990a | ||
|
|
5108193d7b | ||
|
|
79a24f5dd9 | ||
|
|
83c759572c | ||
|
|
1a07fb000b | ||
|
|
4aab15999d | ||
|
|
441982674f | ||
|
|
8256c6e070 | ||
|
|
ae87a44b94 | ||
|
|
87d1692e27 | ||
|
|
1d2f008656 | ||
|
|
b064d8a9ff | ||
|
|
528af200b6 | ||
|
|
c6827a7dac | ||
|
|
55ecb082ee | ||
|
|
5f2cf461e6 | ||
|
|
b313386988 | ||
|
|
3933280d29 | ||
|
|
2b6073900f | ||
|
|
d1ba43a69f | ||
|
|
a0779edec4 | ||
|
|
d70fefe72f | ||
|
|
b52db072f1 | ||
|
|
ab0b98db61 | ||
|
|
4245d98653 | ||
|
|
93b4866f5b | ||
|
|
25df164219 | ||
|
|
ba03c082e9 | ||
|
|
bcfa333322 | ||
|
|
c4aeadd57d | ||
|
|
e6b4454e48 | ||
|
|
3226d21609 | ||
|
|
3d20c2dce6 | ||
|
|
cd83d70e7b | ||
|
|
a0ce5ea9ab | ||
|
|
3c600c103f | ||
|
|
841ac75c3e | ||
|
|
80a96817aa | ||
|
|
ecc05e0a1a | ||
|
|
359730af54 | ||
|
|
e9cb19c80e | ||
|
|
049e064e28 | ||
|
|
0463928a1e | ||
|
|
02d34c8c4c | ||
|
|
46acf32c41 | ||
|
|
cab0a76795 | ||
|
|
0c76cebbfc | ||
|
|
5029b85b25 | ||
|
|
0b71d9b828 | ||
|
|
464d7c4ec6 | ||
|
|
ed21b631ae | ||
|
|
950e2d9954 | ||
|
|
71c054a789 | ||
|
|
d2f456e5b1 | ||
|
|
30a44b26d3 | ||
|
|
7dff758f8b | ||
|
|
bd3ececdd8 | ||
|
|
77416b0ac2 | ||
|
|
fce9efd341 | ||
|
|
928fda1d70 | ||
|
|
2d9d654507 | ||
|
|
7398e57174 | ||
|
|
503ec82f4d | ||
|
|
22bd9fb530 | ||
|
|
e007b6b031 | ||
|
|
c487cd888f | ||
|
|
fd403a85c8 | ||
|
|
a06a8c36a4 | ||
|
|
0967ee9cb9 | ||
|
|
d66d34f9b9 | ||
|
|
264a98d106 | ||
|
|
7c11d2d9bb | ||
|
|
b58d7b406f | ||
|
|
5ed9e4c83b | ||
|
|
e7da2a669e | ||
|
|
d66377d4b6 | ||
|
|
77a324fa46 | ||
|
|
38b5cad4e3 | ||
|
|
29014643a9 | ||
|
|
0872f6babe | ||
|
|
b6a9cf9ffa | ||
|
|
5645cad577 | ||
|
|
85ba33c08f | ||
|
|
ff64f64e1d | ||
|
|
bdd3425028 | ||
|
|
70e02cddd3 | ||
|
|
d1c0a17cc2 | ||
|
|
35c5cbbc21 | ||
|
|
2ff31bdd2b | ||
|
|
2fa3d34af1 | ||
|
|
fa3452ec68 | ||
|
|
7451f2f212 | ||
|
|
e9ab75a386 | ||
|
|
d83949bc88 | ||
|
|
50f42a80c9 | ||
|
|
00bd76fc3d | ||
|
|
93ea6b581e | ||
|
|
8e52a5c7f3 | ||
|
|
0aa35e574a | ||
|
|
5a81f2441f | ||
|
|
2b75aff32e | ||
|
|
f4474d57ec | ||
|
|
d0310c8aee | ||
|
|
8471c9e92e | ||
|
|
f5a4c9d971 | ||
|
|
a0dd8918eb | ||
|
|
baa9b1cac0 | ||
|
|
38443d2195 | ||
|
|
208d71561a | ||
|
|
25fbc7b435 | ||
|
|
6bd95d885d | ||
|
|
c171097dea | ||
|
|
6bcdb178a5 | ||
|
|
5e10cc4cad | ||
|
|
95e6c875f0 | ||
|
|
6eb2829ee8 | ||
|
|
6ad9ddcdea | ||
|
|
e082b7be72 | ||
|
|
da01d8dedb | ||
|
|
ca6f6511f2 | ||
|
|
ee8440408c | ||
|
|
9cd695a1db | ||
|
|
4b45906f46 | ||
|
|
d448eb54f9 | ||
|
|
65739fe754 |
@@ -28,6 +28,7 @@ addons:
|
||||
- libevent-dev
|
||||
- libjansson-dev
|
||||
- libjemalloc-dev
|
||||
- libc-ares-dev
|
||||
- cmake
|
||||
- cmake-data
|
||||
before_install:
|
||||
@@ -54,7 +55,8 @@ before_script:
|
||||
- if [ "$CI_BUILD" = "autotools" ]; then ./configure --enable-werror --with-mruby --with-neverbleed LIBSPDYLAY_CFLAGS="-I$SPDYLAY_HOME/lib/includes" LIBSPDYLAY_LIBS="-L$SPDYLAY_HOME/lib/.libs -lspdylay" CPPFLAGS=-fsanitize=address LDFLAGS=-fsanitize=address; fi
|
||||
- if [ "$CI_BUILD" = "cmake" ]; then cmake -DENABLE_WERROR=1 -DWITH_MRUBY=1 -DWITH_NEVERBLEED=1 -DSPDYLAY_INCLUDE_DIR="$SPDYLAY_HOME/lib/includes" -DSPDYLAY_LIBRARY="$SPDYLAY_HOME/lib/.libs/libspdylay.so"; fi
|
||||
script:
|
||||
- make check
|
||||
- if [ "$CI_BUILD" = "autotools" ]; then make distcheck; fi
|
||||
- if [ "$CI_BUILD" = "cmake" ]; then make check; fi
|
||||
# As of April, 23, 2016, golang http2 build fails, probably because
|
||||
# the default go version is too old.
|
||||
# - cd integration-tests
|
||||
|
||||
4
AUTHORS
4
AUTHORS
@@ -21,6 +21,7 @@ Anders Bakken
|
||||
Andreas Pohl
|
||||
Andy Davies
|
||||
Ant Bryan
|
||||
Benedikt Christoph Wolters
|
||||
Bernard Spil
|
||||
Brian Card
|
||||
Brian Suh
|
||||
@@ -48,6 +49,7 @@ Kit Chan
|
||||
Kyle Schomp
|
||||
Lucas Pardue
|
||||
MATSUMOTO Ryosuke
|
||||
Matt Rudary
|
||||
Mike Conlen
|
||||
Mike Frysinger
|
||||
Nicholas Hurley
|
||||
@@ -78,9 +80,11 @@ Zhuoyun Wei
|
||||
acesso
|
||||
ayanamist
|
||||
bxshi
|
||||
dalf
|
||||
es
|
||||
fangdingjun
|
||||
kumagi
|
||||
makovich
|
||||
mod-h2-dev
|
||||
moparisthebest
|
||||
snnn
|
||||
|
||||
@@ -24,12 +24,12 @@
|
||||
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
# XXX using 1.8.90 instead of 1.9.0-DEV
|
||||
project(nghttp2 VERSION 1.16.1)
|
||||
project(nghttp2 VERSION 1.19.90)
|
||||
|
||||
# See versioning rule:
|
||||
# http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
||||
set(LT_CURRENT 26)
|
||||
set(LT_REVISION 1)
|
||||
set(LT_REVISION 4)
|
||||
set(LT_AGE 12)
|
||||
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
@@ -59,6 +59,7 @@ find_package(PythonInterp)
|
||||
# Auto-detection of features that can be toggled
|
||||
find_package(OpenSSL 1.0.1)
|
||||
find_package(Libev 4.11)
|
||||
find_package(Libcares 1.7.5)
|
||||
find_package(ZLIB 1.2.3)
|
||||
if(OPENSSL_FOUND AND LIBEV_FOUND AND ZLIB_FOUND)
|
||||
set(ENABLE_APP_DEFAULT ON)
|
||||
@@ -207,6 +208,14 @@ if(LIBEVENT_FOUND)
|
||||
# Must both link the core and openssl libraries.
|
||||
set(LIBEVENT_OPENSSL_LIBRARIES ${LIBEVENT_LIBRARIES})
|
||||
endif()
|
||||
# libc-ares (for src)
|
||||
set(HAVE_LIBCARES ${LIBCARES_FOUND})
|
||||
if(LIBCARES_FOUND)
|
||||
set(LIBCARES_INCLUDE_DIRS ${LIBCARES_INCLUDE_DIR})
|
||||
else()
|
||||
set(LIBCARES_INCLUDE_DIRS "")
|
||||
set(LIBCARES_LIBRARIES "")
|
||||
endif()
|
||||
# jansson (for src/nghttp, src/deflatehd and src/inflatehd)
|
||||
set(HAVE_JANSSON ${JANSSON_FOUND})
|
||||
# libxml2 (for src/nghttp)
|
||||
@@ -304,7 +313,6 @@ include(CheckFunctionExists)
|
||||
check_function_exists(_Exit HAVE__EXIT)
|
||||
check_function_exists(accept4 HAVE_ACCEPT4)
|
||||
|
||||
# timerfd_create was added in linux kernel 2.6.25
|
||||
include(CheckSymbolExists)
|
||||
# XXX does this correctly detect initgroups (un)availability on cygwin?
|
||||
check_symbol_exists(initgroups grp.h HAVE_DECL_INITGROUPS)
|
||||
@@ -316,13 +324,6 @@ if(NOT HAVE_DECL_INITGROUPS AND HAVE_UNISTD_H)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
check_function_exists(timerfd_create HAVE_TIMERFD_CREATE)
|
||||
# Checks for epoll availability, primarily for examples/tiny-nghttpd
|
||||
check_symbol_exists(epoll_create sys/epoll.h HAVE_EPOLL)
|
||||
if(HAVE_EPOLL AND HAVE_TIMERFD_CREATE)
|
||||
set(ENABLE_TINY_NGHTTPD 1)
|
||||
endif()
|
||||
|
||||
set(WARNCFLAGS)
|
||||
set(WARNCXXFLAGS)
|
||||
if(CMAKE_C_COMPILER_ID MATCHES "MSVC")
|
||||
@@ -499,6 +500,7 @@ message(STATUS "summary of build options:
|
||||
OpenSSL: ${HAVE_OPENSSL} (LIBS='${OPENSSL_LIBRARIES}')
|
||||
Libxml2: ${HAVE_LIBXML2} (LIBS='${LIBXML2_LIBRARIES}')
|
||||
Libev: ${HAVE_LIBEV} (LIBS='${LIBEV_LIBRARIES}')
|
||||
Libc-ares: ${HAVE_LIBCARES} (LIBS='${LIBCARES_LIBRARIES}')
|
||||
Libevent(SSL): ${HAVE_LIBEVENT_OPENSSL} (LIBS='${LIBEVENT_OPENSSL_LIBRARIES}')
|
||||
Spdylay: ${HAVE_SPDYLAY} (LIBS='${SPDYLAY_LIBRARIES}')
|
||||
Jansson: ${HAVE_JANSSON} (LIBS='${JANSSON_LIBRARIES}')
|
||||
|
||||
@@ -45,7 +45,8 @@ EXTRA_DIST = nghttpx.conf.sample proxy.pac.sample android-config android-make \
|
||||
cmake/Version.cmake \
|
||||
cmake/FindCython.cmake \
|
||||
cmake/FindLibevent.cmake \
|
||||
cmake/FindJansson.cmake
|
||||
cmake/FindJansson.cmake \
|
||||
cmake/FindLibcares.cmake
|
||||
|
||||
.PHONY: clang-format
|
||||
|
||||
|
||||
12
README.rst
12
README.rst
@@ -70,6 +70,7 @@ are required:
|
||||
* OpenSSL >= 1.0.1
|
||||
* libev >= 4.11
|
||||
* zlib >= 1.2.3
|
||||
* libc-ares >= 1.7.5
|
||||
|
||||
ALPN support requires OpenSSL >= 1.0.2 (released 22 January 2015).
|
||||
LibreSSL >= 2.2.0 can be used instead of OpenSSL, but OpenSSL has more
|
||||
@@ -98,6 +99,11 @@ To mitigate heap fragmentation in long running server programs
|
||||
|
||||
* jemalloc
|
||||
|
||||
.. note::
|
||||
|
||||
Alpine Linux currently does not support malloc replacement
|
||||
due to musl limitations. See details in issue `#762 <https://github.com/nghttp2/nghttp2/issues/762>`_.
|
||||
|
||||
libnghttp2_asio C++ library requires the following packages:
|
||||
|
||||
* libboost-dev >= 1.54.0
|
||||
@@ -115,7 +121,7 @@ If you are using Ubuntu 14.04 LTS (trusty) or Debian 7.0 (wheezy) and above run
|
||||
|
||||
sudo apt-get install g++ make binutils autoconf automake autotools-dev libtool pkg-config \
|
||||
zlib1g-dev libcunit1-dev libssl-dev libxml2-dev libev-dev libevent-dev libjansson-dev \
|
||||
libjemalloc-dev cython python3-dev python-setuptools
|
||||
libc-ares-dev libjemalloc-dev cython python3-dev python-setuptools
|
||||
|
||||
From Ubuntu 15.10, spdylay has been available as a package named
|
||||
`libspdylay-dev`. For the earlier Ubuntu release, you need to build
|
||||
@@ -149,6 +155,7 @@ used:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ git submodule update --init
|
||||
$ autoreconf -i
|
||||
$ automake
|
||||
$ autoconf
|
||||
@@ -159,8 +166,7 @@ To compile the source code, gcc >= 4.8.3 or clang >= 3.4 is required.
|
||||
|
||||
.. note::
|
||||
|
||||
To enable mruby support in nghttpx, run ``git submodule update
|
||||
--init`` before running configure script, and use ``--with-mruby``
|
||||
To enable mruby support in nghttpx, and use ``--with-mruby``
|
||||
configure option.
|
||||
|
||||
.. note::
|
||||
|
||||
@@ -39,8 +39,9 @@ PATH="$TOOLCHAIN"/bin:"$PATH"
|
||||
--without-libxml2 \
|
||||
--disable-python-bindings \
|
||||
--disable-examples \
|
||||
CC="$TOOLCHAIN"/bin/arm-linux-androideabi-gcc \
|
||||
CXX="$TOOLCHAIN"/bin/arm-linux-androideabi-g++ \
|
||||
--disable-threads \
|
||||
CC="$TOOLCHAIN"/bin/arm-linux-androideabi-clang \
|
||||
CXX="$TOOLCHAIN"/bin/arm-linux-androideabi-clang++ \
|
||||
CPPFLAGS="-fPIE -I$PREFIX/include" \
|
||||
PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \
|
||||
LDFLAGS="-fPIE -pie -L$PREFIX/lib"
|
||||
|
||||
53
appveyor.yml
Normal file
53
appveyor.yml
Normal file
@@ -0,0 +1,53 @@
|
||||
# Notes:
|
||||
# - Minimal appveyor.yml file is an empty file. All sections are optional.
|
||||
# - Indent each level of configuration with 2 spaces. Do not use tabs!
|
||||
# - All section names are case-sensitive.
|
||||
# - Section names should be unique on each level.
|
||||
|
||||
#---------------------------------#
|
||||
# general configuration #
|
||||
#---------------------------------#
|
||||
|
||||
# version format
|
||||
#version: 0.10.{build}
|
||||
|
||||
# branches to build
|
||||
branches:
|
||||
# blacklist
|
||||
except:
|
||||
- gh-pages
|
||||
|
||||
# Do not build on tags (GitHub only)
|
||||
skip_tags: true
|
||||
|
||||
#---------------------------------#
|
||||
# environment configuration #
|
||||
#---------------------------------#
|
||||
|
||||
os: Windows Server 2012
|
||||
|
||||
# scripts that run after cloning repository
|
||||
install:
|
||||
# install Win-Flex-Bison
|
||||
#- cmd: cinst winflexbison -y
|
||||
|
||||
#---------------------------------#
|
||||
# build configuration #
|
||||
#---------------------------------#
|
||||
|
||||
# scripts to run before build
|
||||
before_build:
|
||||
- cmd: cmake .
|
||||
|
||||
# scripts to run *after* solution is built and *before* automatic packaging occurs (web apps, NuGet packages, Azure Cloud Services)
|
||||
# before_package:
|
||||
|
||||
# scripts to run after build
|
||||
# after_build:
|
||||
|
||||
# to run your custom scripts instead of automatic MSBuild
|
||||
build_script:
|
||||
- cmd: cmake --build .
|
||||
|
||||
# to disable automatic builds
|
||||
# build: off
|
||||
40
cmake/FindLibcares.cmake
Normal file
40
cmake/FindLibcares.cmake
Normal file
@@ -0,0 +1,40 @@
|
||||
# - Try to find libcares
|
||||
# Once done this will define
|
||||
# LIBCARES_FOUND - System has libcares
|
||||
# LIBCARES_INCLUDE_DIRS - The libcares include directories
|
||||
# LIBCARES_LIBRARIES - The libraries needed to use libcares
|
||||
|
||||
find_package(PkgConfig QUIET)
|
||||
pkg_check_modules(PC_LIBCARES QUIET libcares)
|
||||
|
||||
find_path(LIBCARES_INCLUDE_DIR
|
||||
NAMES ares.h
|
||||
HINTS ${PC_LIBCARES_INCLUDE_DIRS}
|
||||
)
|
||||
find_library(LIBCARES_LIBRARY
|
||||
NAMES cares
|
||||
HINTS ${PC_LIBCARES_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
if(LIBCARES_INCLUDE_DIR)
|
||||
set(_version_regex "^#define[ \t]+ARES_VERSION_STR[ \t]+\"([^\"]+)\".*")
|
||||
file(STRINGS "${LIBCARES_INCLUDE_DIR}/ares_version.h"
|
||||
LIBCARES_VERSION REGEX "${_version_regex}")
|
||||
string(REGEX REPLACE "${_version_regex}" "\\1"
|
||||
LIBCARES_VERSION "${LIBCARES_VERSION}")
|
||||
unset(_version_regex)
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
# handle the QUIETLY and REQUIRED arguments and set LIBCARES_FOUND to TRUE
|
||||
# if all listed variables are TRUE and the requested version matches.
|
||||
find_package_handle_standard_args(Libcares REQUIRED_VARS
|
||||
LIBCARES_LIBRARY LIBCARES_INCLUDE_DIR
|
||||
VERSION_VAR LIBCARES_VERSION)
|
||||
|
||||
if(LIBCARES_FOUND)
|
||||
set(LIBCARES_LIBRARIES ${LIBCARES_LIBRARY})
|
||||
set(LIBCARES_INCLUDE_DIRS ${LIBCARES_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
mark_as_advanced(LIBCARES_INCLUDE_DIR LIBCARES_LIBRARY)
|
||||
61
configure.ac
61
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.16.1], [t-tujikawa@users.sourceforge.net])
|
||||
AC_INIT([nghttp2], [1.20.0-DEV], [t-tujikawa@users.sourceforge.net])
|
||||
AC_CONFIG_AUX_DIR([.])
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
@@ -45,7 +45,7 @@ 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, 26)
|
||||
AC_SUBST(LT_REVISION, 1)
|
||||
AC_SUBST(LT_REVISION, 4)
|
||||
AC_SUBST(LT_AGE, 12)
|
||||
|
||||
major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"`
|
||||
@@ -370,6 +370,13 @@ if test "x${have_openssl}" = "xno"; then
|
||||
AC_MSG_NOTICE($OPENSSL_PKG_ERRORS)
|
||||
fi
|
||||
|
||||
# c-ares (for src)
|
||||
PKG_CHECK_MODULES([LIBCARES], [libcares >= 1.7.5], [have_libcares=yes],
|
||||
[have_libcares=no])
|
||||
if test "x${have_libcares}" = "xno"; then
|
||||
AC_MSG_NOTICE($LIBCARES_PKG_ERRORS)
|
||||
fi
|
||||
|
||||
# libevent_openssl (for examples)
|
||||
# 2.0.8 is required because we use evconnlistener_set_error_cb()
|
||||
PKG_CHECK_MODULES([LIBEVENT_OPENSSL], [libevent_openssl >= 2.0.8],
|
||||
@@ -389,15 +396,12 @@ else
|
||||
fi
|
||||
|
||||
# libxml2 (for src/nghttp)
|
||||
have_libxml2=no
|
||||
if test "x${request_libxml2}" != "xno"; then
|
||||
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
|
||||
PKG_CHECK_MODULES([LIBXML2], [libxml-2.0 >= 2.7.7],
|
||||
[have_libxml2=yes], [have_libxml2=no])
|
||||
if test "x${have_libxml2}" = "xyes"; then
|
||||
AC_DEFINE([HAVE_LIBXML2], [1], [Define to 1 if you have `libxml2` library.])
|
||||
fi
|
||||
else
|
||||
AC_MSG_NOTICE($LIBXML2_PKG_ERRORS)
|
||||
fi
|
||||
|
||||
if test "x${request_libxml2}" = "xyes" &&
|
||||
@@ -479,13 +483,14 @@ if test "x${request_asio_lib}" = "xyes"; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# The nghttp, nghttpd and nghttpx under src depend on zlib, OpenSSL
|
||||
# and libev
|
||||
# The nghttp, nghttpd and nghttpx under src depend on zlib, OpenSSL,
|
||||
# libev, and libc-ares.
|
||||
enable_app=no
|
||||
if test "x${request_app}" != "xno" &&
|
||||
test "x${have_zlib}" = "xyes" &&
|
||||
test "x${have_openssl}" = "xyes" &&
|
||||
test "x${have_libev}" = "xyes"; then
|
||||
test "x${have_libev}" = "xyes" &&
|
||||
test "x${have_libcares}" = "xyes"; then
|
||||
enable_app=yes
|
||||
fi
|
||||
|
||||
@@ -640,6 +645,26 @@ AC_SYS_LARGEFILE
|
||||
AC_CHECK_MEMBER([struct tm.tm_gmtoff], [have_struct_tm_tm_gmtoff=yes],
|
||||
[have_struct_tm_tm_gmtoff=no], [[#include <time.h>]])
|
||||
|
||||
AC_CHECK_MEMBER([struct sockaddr_in.sin_len],
|
||||
[AC_DEFINE([HAVE_SOCKADDR_IN_SIN_LEN],[1],
|
||||
[Define to 1 if struct sockaddr_in has sin_len member.])],
|
||||
[],
|
||||
[[
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
]])
|
||||
|
||||
AC_CHECK_MEMBER([struct sockaddr_in6.sin6_len],
|
||||
[AC_DEFINE([HAVE_SOCKADDR_IN6_SIN6_LEN],[1],
|
||||
[Define to 1 if struct sockaddr_in6 has sin6_len member.])],
|
||||
[],
|
||||
[[
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
]])
|
||||
|
||||
if test "x$have_struct_tm_tm_gmtoff" = "xyes"; then
|
||||
AC_DEFINE([HAVE_STRUCT_TM_TM_GMTOFF], [1],
|
||||
[Define to 1 if you have `struct tm.tm_gmtoff` member.])
|
||||
@@ -701,13 +726,6 @@ AC_CHECK_DECLS([initgroups], [], [], [[
|
||||
#include <grp.h>
|
||||
]])
|
||||
|
||||
# Checks for epoll availability, primarily for examples/tiny-nghttpd
|
||||
AX_HAVE_EPOLL([have_epoll=yes], [have_epoll=no])
|
||||
|
||||
AM_CONDITIONAL([ENABLE_TINY_NGHTTPD],
|
||||
[ test "x${have_epoll}" = "xyes" &&
|
||||
test "x${have_timerfd_create}" = "xyes"])
|
||||
|
||||
save_CFLAGS=$CFLAGS
|
||||
save_CXXFLAGS=$CXXFLAGS
|
||||
|
||||
@@ -888,8 +906,9 @@ AC_MSG_NOTICE([summary of build options:
|
||||
Failmalloc: ${enable_failmalloc}
|
||||
Libs:
|
||||
OpenSSL: ${have_openssl} (CFLAGS='${OPENSSL_CFLAGS}' LIBS='${OPENSSL_LIBS}')
|
||||
Libxml2: ${have_libxml2} (CFLAGS='${XML_CPPFLAGS}' LIBS='${XML_LIBS}')
|
||||
Libxml2: ${have_libxml2} (CFLAGS='${LIBXML2_CPPFLAGS}' LIBS='${LIBXML2_LIBS}')
|
||||
Libev: ${have_libev} (CFLAGS='${LIBEV_CFLAGS}' LIBS='${LIBEV_LIBS}')
|
||||
Libc-ares ${have_libcares} (CFLAGS='${LIBCARES_CFLAGS}' LIBS='${LIBCARES_LIBS}')
|
||||
Libevent(SSL): ${have_libevent_openssl} (CFLAGS='${LIBEVENT_OPENSSL_CFLAGS}' LIBS='${LIBEVENT_OPENSSL_LIBS}')
|
||||
Spdylay: ${have_spdylay} (CFLAGS='${LIBSPDYLAY_CFLAGS}' LIBS='${LIBSPDYLAY_LIBS}')
|
||||
Jansson: ${have_jansson} (CFLAGS='${JANSSON_CFLAGS}' LIBS='${JANSSON_LIBS}')
|
||||
|
||||
@@ -266,7 +266,7 @@ apiref.rst: \
|
||||
$(APIDOCS): apiref.rst
|
||||
|
||||
clean-local:
|
||||
[ $(srcdir) = $(builddir) ] || for i in $(RST_FILES); do [ -e $(builddir)/$$i ] && rm $(builddir)/$$i; done
|
||||
[ $(srcdir) = $(builddir) ] || for i in $(RST_FILES); do [ -e $(builddir)/$$i ] && rm -f $(builddir)/$$i; done
|
||||
-rm -f apiref.rst
|
||||
-rm -f $(APIDOCS)
|
||||
-rm -rf $(BUILDDIR)/*
|
||||
|
||||
@@ -8,7 +8,7 @@ _nghttpx()
|
||||
_get_comp_words_by_ref cur prev
|
||||
case $cur in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W '--worker-read-rate --include --frontend-http2-dump-response-header --tls-ticket-key-file --verify-client-cacert --max-response-header-fields --backend-http2-window-size --backend-request-buffer --max-request-header-fields --fastopen --tls-ticket-key-memcached --conf --backend-http2-max-concurrent-streams --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 --backend-connect-timeout --forwarded-for --accesslog-syslog --frontend-http2-read-timeout --listener-disable-timeout --ciphers --strip-incoming-x-forwarded-for --no-server-rewrite --private-key-passwd-file --backend-keep-alive-timeout --backend-http-proxy-uri --rlimit-nofile --tls-ticket-key-memcached-cert-file --ocsp-update-interval --forwarded-by --tls-session-cache-memcached-private-key-file --error-page --backend-write-timeout --tls-dyn-rec-warmup-threshold --tls-ticket-key-memcached-max-retry --frontend-http2-window-size --http2-no-cookie-crumbling --worker-read-burst --dh-param-file --accesslog-format --errorlog-syslog --request-header-field-buffer --api-max-request-body --frontend-http2-decoder-dynamic-table-size --errorlog-file --frontend-http2-max-concurrent-streams --frontend-write-timeout --tls-ticket-key-cipher --read-burst --backend --server-name --insecure --backend-max-backoff --log-level --host-rewrite --tls-proto-list --tls-ticket-key-memcached-interval --frontend-http2-setting-timeout --frontend-http2-connection-window-size --worker-frontend-connections --syslog-facility --no-server-push --no-location-rewrite --tls-session-cache-memcached --no-ocsp --frontend-http2-encoder-dynamic-table-size --workers --add-forwarded --worker-write-rate --add-request-header --backend-http2-settings-timeout --subcert --ecdh-curves --no-kqueue --help --frontend-frame-debug --tls-sct-dir --pid-file --frontend-http2-dump-request-header --daemon --write-rate --altsvc --backend-http2-decoder-dynamic-table-size --user --add-x-forwarded-for --frontend-read-timeout --tls-ticket-key-memcached-max-fail --backlog --write-burst --backend-connections-per-host --response-header-field-buffer --tls-ticket-key-memcached-address-family --padding --tls-session-cache-memcached-address-family --stream-write-timeout --cacert --tls-ticket-key-memcached-private-key-file --backend-address-family --backend-http2-connection-window-size --version --add-response-header --backend-read-timeout --frontend-http2-optimize-window-size --frontend --accesslog-file --http2-proxy --backend-http2-encoder-dynamic-table-size --client-private-key-file --client-cert-file --accept-proxy-protocol --tls-dyn-rec-idle-timeout --frontend-http2-optimize-write-buffer-size --verify-client --backend-response-buffer --read-rate --backend-connections-per-frontend --strip-incoming-forwarded ' -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W '--worker-read-rate --include --frontend-http2-dump-response-header --tls-ticket-key-file --verify-client-cacert --max-response-header-fields --backend-http2-window-size --frontend-keep-alive-timeout --backend-request-buffer --max-request-header-fields --fastopen --backend-connect-timeout --conf --dns-lookup-timeout --backend-http2-max-concurrent-streams --worker-write-burst --npn-list --dns-max-try --fetch-ocsp-response-file --no-via --tls-session-cache-memcached-cert-file --no-http2-cipher-black-list --mruby-file --client-no-http2-cipher-black-list --stream-read-timeout --client-ciphers --forwarded-for --accesslog-syslog --dns-cache-timeout --frontend-http2-read-timeout --listener-disable-timeout --ciphers --client-psk-secrets --strip-incoming-x-forwarded-for --no-server-rewrite --private-key-passwd-file --backend-keep-alive-timeout --backend-http-proxy-uri --rlimit-nofile --tls-ticket-key-memcached-cert-file --ocsp-update-interval --forwarded-by --tls-session-cache-memcached-private-key-file --error-page --backend-write-timeout --tls-dyn-rec-warmup-threshold --tls-ticket-key-memcached-max-retry --frontend-http2-window-size --http2-no-cookie-crumbling --worker-read-burst --dh-param-file --accesslog-format --errorlog-syslog --request-header-field-buffer --api-max-request-body --frontend-http2-decoder-dynamic-table-size --errorlog-file --frontend-http2-max-concurrent-streams --psk-secrets --frontend-write-timeout --tls-ticket-key-cipher --read-burst --backend --server-name --insecure --backend-max-backoff --log-level --host-rewrite --tls-proto-list --tls-ticket-key-memcached-interval --frontend-http2-setting-timeout --frontend-http2-connection-window-size --worker-frontend-connections --syslog-facility --no-server-push --no-location-rewrite --tls-session-cache-memcached --no-ocsp --frontend-http2-encoder-dynamic-table-size --workers --add-forwarded --worker-write-rate --add-request-header --backend-http2-settings-timeout --subcert --ecdh-curves --no-kqueue --help --frontend-frame-debug --tls-sct-dir --pid-file --frontend-http2-dump-request-header --daemon --write-rate --altsvc --backend-http2-decoder-dynamic-table-size --user --add-x-forwarded-for --frontend-read-timeout --tls-ticket-key-memcached-max-fail --backlog --write-burst --backend-connections-per-host --response-header-field-buffer --tls-ticket-key-memcached-address-family --padding --tls-session-cache-memcached-address-family --stream-write-timeout --cacert --tls-ticket-key-memcached-private-key-file --accesslog-write-early --backend-address-family --backend-http2-connection-window-size --version --add-response-header --backend-read-timeout --frontend-http2-optimize-window-size --frontend --accesslog-file --http2-proxy --backend-http2-encoder-dynamic-table-size --client-private-key-file --client-cert-file --tls-ticket-key-memcached --tls-dyn-rec-idle-timeout --frontend-http2-optimize-write-buffer-size --verify-client --backend-response-buffer --read-rate --backend-connections-per-frontend --strip-incoming-forwarded ' -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
_filedir
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "H2LOAD" "1" "Nov 13, 2016" "1.16.1" "nghttp2"
|
||||
.TH "H2LOAD" "1" "Jan 25, 2017" "1.19.0" "nghttp2"
|
||||
.SH NAME
|
||||
h2load \- HTTP/2 benchmarking tool
|
||||
.
|
||||
@@ -123,6 +123,8 @@ Add/Override a header to the requests.
|
||||
.B \-\-ciphers=<SUITE>
|
||||
Set allowed cipher list. The format of the string is
|
||||
described in OpenSSL ciphers(1).
|
||||
.sp
|
||||
Default: \fBECDHE\-ECDSA\-CHACHA20\-POLY1305:ECDHE\-RSA\-CHACHA20\-POLY1305:ECDHE\-ECDSA\-AES128\-GCM\-SHA256:ECDHE\-RSA\-AES128\-GCM\-SHA256:ECDHE\-ECDSA\-AES256\-GCM\-SHA384:ECDHE\-RSA\-AES256\-GCM\-SHA384:DHE\-RSA\-AES128\-GCM\-SHA256:DHE\-RSA\-AES256\-GCM\-SHA384:ECDHE\-ECDSA\-AES128\-SHA256:ECDHE\-RSA\-AES128\-SHA256:ECDHE\-ECDSA\-AES128\-SHA:ECDHE\-RSA\-AES256\-SHA384:ECDHE\-RSA\-AES128\-SHA:ECDHE\-ECDSA\-AES256\-SHA384:ECDHE\-ECDSA\-AES256\-SHA:ECDHE\-RSA\-AES256\-SHA:DHE\-RSA\-AES128\-SHA256:DHE\-RSA\-AES128\-SHA:DHE\-RSA\-AES256\-SHA256:DHE\-RSA\-AES256\-SHA:ECDHE\-ECDSA\-DES\-CBC3\-SHA:ECDHE\-RSA\-DES\-CBC3\-SHA:EDH\-RSA\-DES\-CBC3\-SHA:AES128\-GCM\-SHA256:AES256\-GCM\-SHA384:AES128\-SHA256:AES256\-SHA256:AES128\-SHA:AES256\-SHA:DES\-CBC3\-SHA:!DSS\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
|
||||
@@ -96,6 +96,8 @@ OPTIONS
|
||||
Set allowed cipher list. The format of the string is
|
||||
described in OpenSSL ciphers(1).
|
||||
|
||||
Default: ``ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS``
|
||||
|
||||
.. option:: -p, --no-tls-proto=<PROTOID>
|
||||
|
||||
Specify ALPN identifier of the protocol to be used when
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTP" "1" "Nov 13, 2016" "1.16.1" "nghttp2"
|
||||
.TH "NGHTTP" "1" "Jan 25, 2017" "1.19.0" "nghttp2"
|
||||
.SH NAME
|
||||
nghttp \- HTTP/2 client
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTPD" "1" "Nov 13, 2016" "1.16.1" "nghttp2"
|
||||
.TH "NGHTTPD" "1" "Jan 25, 2017" "1.19.0" "nghttp2"
|
||||
.SH NAME
|
||||
nghttpd \- HTTP/2 server
|
||||
.
|
||||
|
||||
179
doc/nghttpx.1
179
doc/nghttpx.1
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTPX" "1" "Nov 13, 2016" "1.16.1" "nghttp2"
|
||||
.TH "NGHTTPX" "1" "Jan 25, 2017" "1.19.0" "nghttp2"
|
||||
.SH NAME
|
||||
nghttpx \- HTTP/2 proxy
|
||||
.
|
||||
@@ -120,13 +120,13 @@ together forming load balancing group.
|
||||
Several parameters <PARAM> are accepted after <PATTERN>.
|
||||
The parameters are delimited by ";". The available
|
||||
parameters are: "proto=<PROTO>", "tls",
|
||||
"sni=<SNI_HOST>", "fall=<N>", "rise=<N>", and
|
||||
"affinity=<METHOD>". The parameter consists of keyword,
|
||||
and optionally followed by "=" and value. For example,
|
||||
the parameter "proto=h2" consists of the keyword "proto"
|
||||
and value "h2". The parameter "tls" consists of the
|
||||
keyword "tls" without value. Each parameter is
|
||||
described as follows.
|
||||
"sni=<SNI_HOST>", "fall=<N>", "rise=<N>",
|
||||
"affinity=<METHOD>", and "dns". The parameter consists
|
||||
of keyword, and optionally followed by "=" and value.
|
||||
For example, the parameter "proto=h2" consists of the
|
||||
keyword "proto" and value "h2". The parameter "tls"
|
||||
consists of the keyword "tls" without value. Each
|
||||
parameter is described as follows.
|
||||
.sp
|
||||
The backend application protocol can be specified using
|
||||
optional "proto" parameter, and in the form of
|
||||
@@ -175,6 +175,14 @@ session affinity is desired. The session affinity may
|
||||
break if one of the backend gets unreachable, or backend
|
||||
settings are reloaded or replaced by API.
|
||||
.sp
|
||||
By default, name resolution of backend host name is done
|
||||
at start up, or reloading configuration. If "dns"
|
||||
parameter is given, name resolution takes place
|
||||
dynamically. This is useful if backend address changes
|
||||
frequently. If "dns" is given, name resolution of
|
||||
backend host name at start up, or reloading
|
||||
configuration is skipped.
|
||||
.sp
|
||||
Since ";" and ":" are used as delimiter, <PATTERN> must
|
||||
not contain these characters. Since ";" has special
|
||||
meaning in shell, the option value must be quoted.
|
||||
@@ -210,6 +218,10 @@ specify "healthmon" parameter. This is disabled by
|
||||
default. Any requests which come through this address
|
||||
are replied with 200 HTTP status, without no body.
|
||||
.sp
|
||||
To accept PROXY protocol version 1 on frontend
|
||||
connection, specify "proxyproto" parameter. This is
|
||||
disabled by default.
|
||||
.sp
|
||||
Default: \fB*,3000\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
@@ -217,7 +229,7 @@ Default: \fB*,3000\fP
|
||||
.B \-\-backlog=<N>
|
||||
Set listen backlog size.
|
||||
.sp
|
||||
Default: \fB512\fP
|
||||
Default: \fB65536\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -245,11 +257,6 @@ timeouts when connecting and making CONNECT request can
|
||||
be specified by \fI\%\-\-backend\-read\-timeout\fP and
|
||||
\fI\%\-\-backend\-write\-timeout\fP options.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-accept\-proxy\-protocol
|
||||
Accept PROXY protocol version 1 on frontend connection.
|
||||
.UNINDENT
|
||||
.SS Performance
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -426,6 +433,14 @@ Default: \fB30s\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-frontend\-keep\-alive\-timeout=<DURATION>
|
||||
Specify keep\-alive timeout for frontend HTTP/1
|
||||
connection.
|
||||
.sp
|
||||
Default: \fB1m\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-stream\-read\-timeout=<DURATION>
|
||||
Specify read timeout for HTTP/2 and SPDY streams. 0
|
||||
means no timeout.
|
||||
@@ -465,7 +480,8 @@ Default: \fB30s\fP
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-backend\-keep\-alive\-timeout=<DURATION>
|
||||
Specify keep\-alive timeout for backend connection.
|
||||
Specify keep\-alive timeout for backend HTTP/1
|
||||
connection.
|
||||
.sp
|
||||
Default: \fB2s\fP
|
||||
.UNINDENT
|
||||
@@ -512,8 +528,18 @@ Default: \fB2m\fP
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-ciphers=<SUITE>
|
||||
Set allowed cipher list. The format of the string is
|
||||
described in OpenSSL ciphers(1).
|
||||
Set allowed cipher list for frontend connection. The
|
||||
format of the string is described in OpenSSL ciphers(1).
|
||||
.sp
|
||||
Default: \fBECDHE\-ECDSA\-CHACHA20\-POLY1305:ECDHE\-RSA\-CHACHA20\-POLY1305:ECDHE\-ECDSA\-AES128\-GCM\-SHA256:ECDHE\-RSA\-AES128\-GCM\-SHA256:ECDHE\-ECDSA\-AES256\-GCM\-SHA384:ECDHE\-RSA\-AES256\-GCM\-SHA384:DHE\-RSA\-AES128\-GCM\-SHA256:DHE\-RSA\-AES256\-GCM\-SHA384:ECDHE\-ECDSA\-AES128\-SHA256:ECDHE\-RSA\-AES128\-SHA256:ECDHE\-ECDSA\-AES128\-SHA:ECDHE\-RSA\-AES256\-SHA384:ECDHE\-RSA\-AES128\-SHA:ECDHE\-ECDSA\-AES256\-SHA384:ECDHE\-ECDSA\-AES256\-SHA:ECDHE\-RSA\-AES256\-SHA:DHE\-RSA\-AES128\-SHA256:DHE\-RSA\-AES128\-SHA:DHE\-RSA\-AES256\-SHA256:DHE\-RSA\-AES256\-SHA:ECDHE\-ECDSA\-DES\-CBC3\-SHA:ECDHE\-RSA\-DES\-CBC3\-SHA:EDH\-RSA\-DES\-CBC3\-SHA:AES128\-GCM\-SHA256:AES256\-GCM\-SHA384:AES128\-SHA256:AES256\-SHA256:AES128\-SHA:AES256\-SHA:DES\-CBC3\-SHA:!DSS\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-client\-ciphers=<SUITE>
|
||||
Set allowed cipher list for backend connection. The
|
||||
format of the string is described in OpenSSL ciphers(1).
|
||||
.sp
|
||||
Default: \fBECDHE\-ECDSA\-CHACHA20\-POLY1305:ECDHE\-RSA\-CHACHA20\-POLY1305:ECDHE\-ECDSA\-AES128\-GCM\-SHA256:ECDHE\-RSA\-AES128\-GCM\-SHA256:ECDHE\-ECDSA\-AES256\-GCM\-SHA384:ECDHE\-RSA\-AES256\-GCM\-SHA384:DHE\-RSA\-AES128\-GCM\-SHA256:DHE\-RSA\-AES256\-GCM\-SHA384:ECDHE\-ECDSA\-AES128\-SHA256:ECDHE\-RSA\-AES128\-SHA256:ECDHE\-ECDSA\-AES128\-SHA:ECDHE\-RSA\-AES256\-SHA384:ECDHE\-RSA\-AES128\-SHA:ECDHE\-ECDSA\-AES256\-SHA384:ECDHE\-ECDSA\-AES256\-SHA:ECDHE\-RSA\-AES256\-SHA:DHE\-RSA\-AES128\-SHA256:DHE\-RSA\-AES128\-SHA:DHE\-RSA\-AES256\-SHA256:DHE\-RSA\-AES256\-SHA:ECDHE\-ECDSA\-DES\-CBC3\-SHA:ECDHE\-RSA\-DES\-CBC3\-SHA:EDH\-RSA\-DES\-CBC3\-SHA:AES128\-GCM\-SHA256:AES256\-GCM\-SHA384:AES128\-SHA256:AES256\-SHA256:AES128\-SHA:AES256\-SHA:DES\-CBC3\-SHA:!DSS\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -803,9 +829,18 @@ Default: \fB1s\fP
|
||||
.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.
|
||||
Allow black listed cipher suite on frontend HTTP/2
|
||||
connection. See
|
||||
\fI\%https://tools.ietf.org/html/rfc7540#appendix\-A\fP for the
|
||||
complete HTTP/2 cipher suites black list.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-client\-no\-http2\-cipher\-black\-list
|
||||
Allow black listed cipher suite on backend HTTP/2
|
||||
connection. See
|
||||
\fI\%https://tools.ietf.org/html/rfc7540#appendix\-A\fP for the
|
||||
complete HTTP/2 cipher suites black list.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -819,6 +854,39 @@ argument <CERT>, or certificate option in configuration
|
||||
file. For additional certificates, use \fI\%\-\-subcert\fP
|
||||
option. This option requires OpenSSL >= 1.0.2.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-psk\-secrets=<PATH>
|
||||
Read list of PSK identity and secrets from <PATH>. This
|
||||
is used for frontend connection. The each line of input
|
||||
file is formatted as <identity>:<hex\-secret>, where
|
||||
<identity> is PSK identity, and <hex\-secret> is secret
|
||||
in hex. An empty line, and line which starts with \(aq#\(aq
|
||||
are skipped. The default enabled cipher list might not
|
||||
contain any PSK cipher suite. In that case, desired PSK
|
||||
cipher suites must be enabled using \fI\%\-\-ciphers\fP option.
|
||||
The desired PSK cipher suite may be black listed by
|
||||
HTTP/2. To use those cipher suites with HTTP/2,
|
||||
consider to use \fI\%\-\-no\-http2\-cipher\-black\-list\fP option.
|
||||
But be aware its implications.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-client\-psk\-secrets=<PATH>
|
||||
Read PSK identity and secrets from <PATH>. This is used
|
||||
for backend connection. The each line of input file is
|
||||
formatted as <identity>:<hex\-secret>, where <identity>
|
||||
is PSK identity, and <hex\-secret> is secret in hex. An
|
||||
empty line, and line which starts with \(aq#\(aq are skipped.
|
||||
The first identity and secret pair encountered is used.
|
||||
The default enabled cipher list might not contain any
|
||||
PSK cipher suite. In that case, desired PSK cipher
|
||||
suites must be enabled using \fI\%\-\-client\-ciphers\fP option.
|
||||
The desired PSK cipher suite may be black listed by
|
||||
HTTP/2. To use those cipher suites with HTTP/2,
|
||||
consider to use \fI\%\-\-client\-no\-http2\-cipher\-black\-list\fP
|
||||
option. But be aware its implications.
|
||||
.UNINDENT
|
||||
.SS HTTP/2 and SPDY
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -1057,6 +1125,13 @@ Default: \fB$remote_addr \- \- [$time_local] "$request" $status $body_bytes_sent
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-accesslog\-write\-early
|
||||
Write access log when response header fields are
|
||||
received from backend rather than when request
|
||||
transaction finishes.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-errorlog\-file=<PATH>
|
||||
Set path to write error log. To reopen file, send USR1
|
||||
signal to nghttpx. stderr will be redirected to the
|
||||
@@ -1238,7 +1313,7 @@ backend server, the custom error pages are not used.
|
||||
.B \-\-server\-name=<NAME>
|
||||
Change server response header field value to <NAME>.
|
||||
.sp
|
||||
Default: \fBnghttpx nghttp2/1.16.1\fP
|
||||
Default: \fBnghttpx nghttp2/1.19.0\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -1255,6 +1330,33 @@ Set the maximum size of request body for API request.
|
||||
.sp
|
||||
Default: \fB16K\fP
|
||||
.UNINDENT
|
||||
.SS DNS
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-dns\-cache\-timeout=<DURATION>
|
||||
Set duration that cached DNS results remain valid. Note
|
||||
that nghttpx caches the unsuccessful results as well.
|
||||
.sp
|
||||
Default: \fB10s\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-dns\-lookup\-timeout=<DURATION>
|
||||
Set timeout that DNS server is given to respond to the
|
||||
initial DNS query. For the 2nd and later queries,
|
||||
server is given time based on this timeout, and it is
|
||||
scaled linearly.
|
||||
.sp
|
||||
Default: \fB5s\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-dns\-max\-try=<N>
|
||||
Set the number of DNS query before nghttpx gives up name
|
||||
lookup.
|
||||
.sp
|
||||
Default: \fB2\fP
|
||||
.UNINDENT
|
||||
.SS Debug
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -1393,6 +1495,35 @@ positional arguments in command\-line, use \fBprivate\-key\-file\fP and
|
||||
.sp
|
||||
\fI\%\-\-conf\fP option cannot be used in the configuration file and
|
||||
will be ignored if specified.
|
||||
.TP
|
||||
.B Error log
|
||||
Error log is written to stderr by default. It can be configured
|
||||
using \fI\%\-\-errorlog\-file\fP\&. The format of log message is as
|
||||
follows:
|
||||
.sp
|
||||
<datetime> <master\-pid> <current\-pid> <thread\-id> <level> (<filename>:<line>) <msg>
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B <datetime>
|
||||
It is a conbination of date and time when the log is written. It
|
||||
is in ISO 8601 format.
|
||||
.TP
|
||||
.B <master\-pid>
|
||||
It is a master process ID.
|
||||
.TP
|
||||
.B <current\-pid>
|
||||
It is a process ID which writes this log.
|
||||
.TP
|
||||
.B <thread\-id>
|
||||
It is a thread ID which writes this log. It would be unique
|
||||
within <current\-pid>.
|
||||
.TP
|
||||
.B <filename> and <line>
|
||||
They are source file name, and line number which produce this log.
|
||||
.TP
|
||||
.B <msg>
|
||||
It is a log message body.
|
||||
.UNINDENT
|
||||
.UNINDENT
|
||||
.SH SIGNALS
|
||||
.INDENT 0.0
|
||||
@@ -1936,9 +2067,9 @@ connections or requests. It also avoids any process creation as is
|
||||
the case with hot swapping with signals.
|
||||
.sp
|
||||
The one limitation is that only numeric IP address is allowd in
|
||||
\fI\%backend\fP in request body while non numeric
|
||||
hostname is allowed in command\-line or configuration file is read
|
||||
using \fI\%\-\-conf\fP\&.
|
||||
\fI\%backend\fP in request body unless "dns" parameter
|
||||
is used while non numeric hostname is allowed in command\-line or
|
||||
configuration file is read using \fI\%\-\-conf\fP\&.
|
||||
.SH SEE ALSO
|
||||
.sp
|
||||
\fBnghttp(1)\fP, \fBnghttpd(1)\fP, \fBh2load(1)\fP
|
||||
|
||||
@@ -104,13 +104,13 @@ Connections
|
||||
Several parameters <PARAM> are accepted after <PATTERN>.
|
||||
The parameters are delimited by ";". The available
|
||||
parameters are: "proto=<PROTO>", "tls",
|
||||
"sni=<SNI_HOST>", "fall=<N>", "rise=<N>", and
|
||||
"affinity=<METHOD>". The parameter consists of keyword,
|
||||
and optionally followed by "=" and value. For example,
|
||||
the parameter "proto=h2" consists of the keyword "proto"
|
||||
and value "h2". The parameter "tls" consists of the
|
||||
keyword "tls" without value. Each parameter is
|
||||
described as follows.
|
||||
"sni=<SNI_HOST>", "fall=<N>", "rise=<N>",
|
||||
"affinity=<METHOD>", and "dns". The parameter consists
|
||||
of keyword, and optionally followed by "=" and value.
|
||||
For example, the parameter "proto=h2" consists of the
|
||||
keyword "proto" and value "h2". The parameter "tls"
|
||||
consists of the keyword "tls" without value. Each
|
||||
parameter is described as follows.
|
||||
|
||||
The backend application protocol can be specified using
|
||||
optional "proto" parameter, and in the form of
|
||||
@@ -159,6 +159,14 @@ Connections
|
||||
break if one of the backend gets unreachable, or backend
|
||||
settings are reloaded or replaced by API.
|
||||
|
||||
By default, name resolution of backend host name is done
|
||||
at start up, or reloading configuration. If "dns"
|
||||
parameter is given, name resolution takes place
|
||||
dynamically. This is useful if backend address changes
|
||||
frequently. If "dns" is given, name resolution of
|
||||
backend host name at start up, or reloading
|
||||
configuration is skipped.
|
||||
|
||||
Since ";" and ":" are used as delimiter, <PATTERN> must
|
||||
not contain these characters. Since ";" has special
|
||||
meaning in shell, the option value must be quoted.
|
||||
@@ -194,6 +202,10 @@ Connections
|
||||
default. Any requests which come through this address
|
||||
are replied with 200 HTTP status, without no body.
|
||||
|
||||
To accept PROXY protocol version 1 on frontend
|
||||
connection, specify "proxyproto" parameter. This is
|
||||
disabled by default.
|
||||
|
||||
|
||||
Default: ``*,3000``
|
||||
|
||||
@@ -201,7 +213,7 @@ Connections
|
||||
|
||||
Set listen backlog size.
|
||||
|
||||
Default: ``512``
|
||||
Default: ``65536``
|
||||
|
||||
.. option:: --backend-address-family=(auto|IPv4|IPv6)
|
||||
|
||||
@@ -227,10 +239,6 @@ Connections
|
||||
be specified by :option:`--backend-read-timeout` and
|
||||
:option:`--backend-write-timeout` options.
|
||||
|
||||
.. option:: --accept-proxy-protocol
|
||||
|
||||
Accept PROXY protocol version 1 on frontend connection.
|
||||
|
||||
|
||||
Performance
|
||||
~~~~~~~~~~~
|
||||
@@ -391,6 +399,13 @@ Timeout
|
||||
|
||||
Default: ``30s``
|
||||
|
||||
.. option:: --frontend-keep-alive-timeout=<DURATION>
|
||||
|
||||
Specify keep-alive timeout for frontend HTTP/1
|
||||
connection.
|
||||
|
||||
Default: ``1m``
|
||||
|
||||
.. option:: --stream-read-timeout=<DURATION>
|
||||
|
||||
Specify read timeout for HTTP/2 and SPDY streams. 0
|
||||
@@ -426,7 +441,8 @@ Timeout
|
||||
|
||||
.. option:: --backend-keep-alive-timeout=<DURATION>
|
||||
|
||||
Specify keep-alive timeout for backend connection.
|
||||
Specify keep-alive timeout for backend HTTP/1
|
||||
connection.
|
||||
|
||||
Default: ``2s``
|
||||
|
||||
@@ -471,8 +487,17 @@ SSL/TLS
|
||||
|
||||
.. option:: --ciphers=<SUITE>
|
||||
|
||||
Set allowed cipher list. The format of the string is
|
||||
described in OpenSSL ciphers(1).
|
||||
Set allowed cipher list for frontend connection. The
|
||||
format of the string is described in OpenSSL ciphers(1).
|
||||
|
||||
Default: ``ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS``
|
||||
|
||||
.. option:: --client-ciphers=<SUITE>
|
||||
|
||||
Set allowed cipher list for backend connection. The
|
||||
format of the string is described in OpenSSL ciphers(1).
|
||||
|
||||
Default: ``ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS``
|
||||
|
||||
.. option:: --ecdh-curves=<LIST>
|
||||
|
||||
@@ -731,9 +756,17 @@ SSL/TLS
|
||||
|
||||
.. 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.
|
||||
Allow black listed cipher suite on frontend HTTP/2
|
||||
connection. See
|
||||
https://tools.ietf.org/html/rfc7540#appendix-A for the
|
||||
complete HTTP/2 cipher suites black list.
|
||||
|
||||
.. option:: --client-no-http2-cipher-black-list
|
||||
|
||||
Allow black listed cipher suite on backend HTTP/2
|
||||
connection. See
|
||||
https://tools.ietf.org/html/rfc7540#appendix-A for the
|
||||
complete HTTP/2 cipher suites black list.
|
||||
|
||||
.. option:: --tls-sct-dir=<DIR>
|
||||
|
||||
@@ -746,6 +779,37 @@ SSL/TLS
|
||||
file. For additional certificates, use :option:`--subcert`
|
||||
option. This option requires OpenSSL >= 1.0.2.
|
||||
|
||||
.. option:: --psk-secrets=<PATH>
|
||||
|
||||
Read list of PSK identity and secrets from <PATH>. This
|
||||
is used for frontend connection. The each line of input
|
||||
file is formatted as <identity>:<hex-secret>, where
|
||||
<identity> is PSK identity, and <hex-secret> is secret
|
||||
in hex. An empty line, and line which starts with '#'
|
||||
are skipped. The default enabled cipher list might not
|
||||
contain any PSK cipher suite. In that case, desired PSK
|
||||
cipher suites must be enabled using :option:`--ciphers` option.
|
||||
The desired PSK cipher suite may be black listed by
|
||||
HTTP/2. To use those cipher suites with HTTP/2,
|
||||
consider to use :option:`--no-http2-cipher-black-list` option.
|
||||
But be aware its implications.
|
||||
|
||||
.. option:: --client-psk-secrets=<PATH>
|
||||
|
||||
Read PSK identity and secrets from <PATH>. This is used
|
||||
for backend connection. The each line of input file is
|
||||
formatted as <identity>:<hex-secret>, where <identity>
|
||||
is PSK identity, and <hex-secret> is secret in hex. An
|
||||
empty line, and line which starts with '#' are skipped.
|
||||
The first identity and secret pair encountered is used.
|
||||
The default enabled cipher list might not contain any
|
||||
PSK cipher suite. In that case, desired PSK cipher
|
||||
suites must be enabled using :option:`--client-ciphers` option.
|
||||
The desired PSK cipher suite may be black listed by
|
||||
HTTP/2. To use those cipher suites with HTTP/2,
|
||||
consider to use :option:`--client-no-http2-cipher-black-list`
|
||||
option. But be aware its implications.
|
||||
|
||||
|
||||
HTTP/2 and SPDY
|
||||
~~~~~~~~~~~~~~~
|
||||
@@ -953,6 +1017,12 @@ Logging
|
||||
|
||||
Default: ``$remote_addr - - [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"``
|
||||
|
||||
.. option:: --accesslog-write-early
|
||||
|
||||
Write access log when response header fields are
|
||||
received from backend rather than when request
|
||||
transaction finishes.
|
||||
|
||||
.. option:: --errorlog-file=<PATH>
|
||||
|
||||
Set path to write error log. To reopen file, send USR1
|
||||
@@ -1118,7 +1188,7 @@ HTTP
|
||||
|
||||
Change server response header field value to <NAME>.
|
||||
|
||||
Default: ``nghttpx nghttp2/1.16.1``
|
||||
Default: ``nghttpx nghttp2/1.19.0``
|
||||
|
||||
.. option:: --no-server-rewrite
|
||||
|
||||
@@ -1137,6 +1207,33 @@ API
|
||||
Default: ``16K``
|
||||
|
||||
|
||||
DNS
|
||||
~~~
|
||||
|
||||
.. option:: --dns-cache-timeout=<DURATION>
|
||||
|
||||
Set duration that cached DNS results remain valid. Note
|
||||
that nghttpx caches the unsuccessful results as well.
|
||||
|
||||
Default: ``10s``
|
||||
|
||||
.. option:: --dns-lookup-timeout=<DURATION>
|
||||
|
||||
Set timeout that DNS server is given to respond to the
|
||||
initial DNS query. For the 2nd and later queries,
|
||||
server is given time based on this timeout, and it is
|
||||
scaled linearly.
|
||||
|
||||
Default: ``5s``
|
||||
|
||||
.. option:: --dns-max-try=<N>
|
||||
|
||||
Set the number of DNS query before nghttpx gives up name
|
||||
lookup.
|
||||
|
||||
Default: ``2``
|
||||
|
||||
|
||||
Debug
|
||||
~~~~~
|
||||
|
||||
@@ -1266,6 +1363,33 @@ FILES
|
||||
:option:`--conf` option cannot be used in the configuration file and
|
||||
will be ignored if specified.
|
||||
|
||||
Error log
|
||||
Error log is written to stderr by default. It can be configured
|
||||
using :option:`--errorlog-file`. The format of log message is as
|
||||
follows:
|
||||
|
||||
<datetime> <master-pid> <current-pid> <thread-id> <level> (<filename>:<line>) <msg>
|
||||
|
||||
<datetime>
|
||||
It is a conbination of date and time when the log is written. It
|
||||
is in ISO 8601 format.
|
||||
|
||||
<master-pid>
|
||||
It is a master process ID.
|
||||
|
||||
<current-pid>
|
||||
It is a process ID which writes this log.
|
||||
|
||||
<thread-id>
|
||||
It is a thread ID which writes this log. It would be unique
|
||||
within <current-pid>.
|
||||
|
||||
<filename> and <line>
|
||||
They are source file name, and line number which produce this log.
|
||||
|
||||
<msg>
|
||||
It is a log message body.
|
||||
|
||||
SIGNALS
|
||||
-------
|
||||
|
||||
@@ -1768,9 +1892,9 @@ connections or requests. It also avoids any process creation as is
|
||||
the case with hot swapping with signals.
|
||||
|
||||
The one limitation is that only numeric IP address is allowd in
|
||||
:option:`backend <--backend>` in request body while non numeric
|
||||
hostname is allowed in command-line or configuration file is read
|
||||
using :option:`--conf`.
|
||||
:option:`backend <--backend>` in request body unless "dns" parameter
|
||||
is used while non numeric hostname is allowed in command-line or
|
||||
configuration file is read using :option:`--conf`.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
|
||||
@@ -41,6 +41,33 @@ FILES
|
||||
:option:`--conf` option cannot be used in the configuration file and
|
||||
will be ignored if specified.
|
||||
|
||||
Error log
|
||||
Error log is written to stderr by default. It can be configured
|
||||
using :option:`--errorlog-file`. The format of log message is as
|
||||
follows:
|
||||
|
||||
<datetime> <master-pid> <current-pid> <thread-id> <level> (<filename>:<line>) <msg>
|
||||
|
||||
<datetime>
|
||||
It is a conbination of date and time when the log is written. It
|
||||
is in ISO 8601 format.
|
||||
|
||||
<master-pid>
|
||||
It is a master process ID.
|
||||
|
||||
<current-pid>
|
||||
It is a process ID which writes this log.
|
||||
|
||||
<thread-id>
|
||||
It is a thread ID which writes this log. It would be unique
|
||||
within <current-pid>.
|
||||
|
||||
<filename> and <line>
|
||||
They are source file name, and line number which produce this log.
|
||||
|
||||
<msg>
|
||||
It is a log message body.
|
||||
|
||||
SIGNALS
|
||||
-------
|
||||
|
||||
@@ -522,11 +549,11 @@ some cases where the error has occurred before reaching API endpoint
|
||||
|
||||
The following section describes available API endpoints.
|
||||
|
||||
PUT /api/v1beta1/backendconfig
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
POST /api/v1beta1/backendconfig
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This API replaces the current backend server settings with the
|
||||
requested ones. The request method should be PUT, but POST is also
|
||||
requested ones. The request method should be POST, but PUT is also
|
||||
acceptable. The request body must be nghttpx configuration file
|
||||
format. For configuration file format, see `FILES`_ section. The
|
||||
line separator inside the request body must be single LF (0x0A).
|
||||
@@ -543,9 +570,9 @@ connections or requests. It also avoids any process creation as is
|
||||
the case with hot swapping with signals.
|
||||
|
||||
The one limitation is that only numeric IP address is allowd in
|
||||
:option:`backend <--backend>` in request body while non numeric
|
||||
hostname is allowed in command-line or configuration file is read
|
||||
using :option:`--conf`.
|
||||
:option:`backend <--backend>` in request body unless "dns" parameter
|
||||
is used while non numeric hostname is allowed in command-line or
|
||||
configuration file is read using :option:`--conf`.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
|
||||
@@ -173,6 +173,11 @@ parsed as 64 bit signed integer. The sum of data length in the
|
||||
following DATA frames must match with the number in "Content-Length"
|
||||
header field if it is present (this does not include padding bytes).
|
||||
|
||||
RFC 7230 says that server must not send "Content-Length" in any
|
||||
response with 1xx, and 204 status code. It also says that
|
||||
"Content-Length" is not allowed in any response with 200 status code
|
||||
to a CONNECT request. nghttp2 enforces them as well.
|
||||
|
||||
Any deviation results in stream error of type PROTOCOL_ERROR. If
|
||||
error is found in PUSH_PROMISE frame, stream error is raised against
|
||||
promised stream.
|
||||
|
||||
@@ -22,16 +22,17 @@ unpacked:
|
||||
.. code-block:: text
|
||||
|
||||
$ build/tools/make_standalone_toolchain.py \
|
||||
--arch arm --api 16 --stl gnustl
|
||||
--arch arm --api 16 --stl gnustl \
|
||||
--install-dir $ANDROID_HOME/toolchain
|
||||
|
||||
The API level (``--api``) is not important here because we don't use
|
||||
Android specific C/C++ API.
|
||||
|
||||
The dependent libraries, such as OpenSSL and libev should be built
|
||||
with the toolchain and installed under ``$ANDROID_HOME/usr/local``.
|
||||
We recommend to build these libraries as static library to make the
|
||||
deployment easier. libxml2 support is currently disabled.
|
||||
The dependent libraries, such as OpenSSL, libev, and c-ares should be
|
||||
built with the toolchain and installed under
|
||||
``$ANDROID_HOME/usr/local``. We recommend to build these libraries as
|
||||
static library to make the deployment easier. libxml2 support is
|
||||
currently disabled.
|
||||
|
||||
Although zlib comes with Android NDK, it seems not to be a part of
|
||||
public API, so we have to built it for our own. That also provides us
|
||||
@@ -96,6 +97,26 @@ patch, to configure libev, use the following script:
|
||||
|
||||
And run ``make install`` to build and install.
|
||||
|
||||
To configure c-ares, use the following script:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
#!/bin/sh -e
|
||||
|
||||
if [ -z "$ANDROID_HOME" ]; then
|
||||
echo 'No $ANDROID_HOME specified.'
|
||||
exit 1
|
||||
fi
|
||||
PREFIX=$ANDROID_HOME/usr/local
|
||||
TOOLCHAIN=$ANDROID_HOME/toolchain
|
||||
PATH=$TOOLCHAIN/bin:$PATH
|
||||
|
||||
./configure \
|
||||
--host=arm-linux-androideabi \
|
||||
--build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \
|
||||
--prefix=$PREFIX \
|
||||
--disable-shared
|
||||
|
||||
To configure zlib, use the following script:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
@@ -343,10 +343,9 @@ requests, do this:
|
||||
backend=serv1,3000;/;proto=h2
|
||||
backend=serv1,3000;/ws/;proto=http/1.1
|
||||
|
||||
Note that the backends share the same pattern must have the same
|
||||
backend protocol. The default backend protocol is HTTP/1.1.
|
||||
The default backend protocol is HTTP/1.1.
|
||||
|
||||
TLS can be enabed per pattern basis:
|
||||
TLS can be enabled per pattern basis:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
@@ -356,6 +355,96 @@ TLS can be enabed per pattern basis:
|
||||
In the above case, connection to serv1 will be encrypted by TLS. On
|
||||
the other hand, connection to serv2 will not be encrypted by TLS.
|
||||
|
||||
Dynamic hostname lookup
|
||||
-----------------------
|
||||
|
||||
By default, nghttpx performs backend hostname lookup at start up, or
|
||||
configuration reload, and keeps using them in its entire session. To
|
||||
make nghttpx perform hostname lookup dynamically, use ``dns``
|
||||
parameter in :option:`--backend` option, like so:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
backend=foo.example.com;;dns
|
||||
|
||||
nghttpx will cache resolved addresses for certain period of time. To
|
||||
change this cache period, use :option:`--dns-cache-timeout`.
|
||||
|
||||
Enable PROXY protocol
|
||||
---------------------
|
||||
|
||||
PROXY protocol can be enabled per frontend. In order to enable PROXY
|
||||
protocol, use ``proxyproto`` parameter in :option:`--frontend` option,
|
||||
like so:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
frontend=*,443;proxyproto
|
||||
|
||||
PSK cipher suites
|
||||
-----------------
|
||||
|
||||
nghttpx supports pre-shared key (PSK) cipher suites for both frontend
|
||||
and backend TLS connections. For frontend connection, use
|
||||
:option:`--psk-secrets` option to specify a file which contains PSK
|
||||
identity and secrets. The format of the file is
|
||||
``<identity>:<hex-secret>``, where ``<identity>`` is PSK identity, and
|
||||
``<hex-secret>`` is PSK secret in hex, like so:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
client1:9567800e065e078085c241d54a01c6c3f24b3bab71a606600f4c6ad2c134f3b9
|
||||
client2:b1376c3f8f6dcf7c886c5bdcceecd1e6f1d708622b6ddd21bda26ebd0c0bca99
|
||||
|
||||
nghttpx server accepts any of the identity and secret pairs in the
|
||||
file. The default cipher suite list does not contain PSK cipher
|
||||
suites. In order to use PSK, PSK cipher suite must be enabled by
|
||||
using :option:`--ciphers` option. The desired PSK cipher suite may be
|
||||
listed in `HTTP/2 cipher black list
|
||||
<https://tools.ietf.org/html/rfc7540#appendix-A>`_. In order to use
|
||||
such PSK cipher suite with HTTP/2, disable HTTP/2 cipher black list by
|
||||
using :option:`--no-http2-cipher-black-list` option. But you should
|
||||
understand its implications.
|
||||
|
||||
At the time of writing, even if only PSK cipher suites are specified
|
||||
in :option:`--ciphers` option, certificate and private key are still
|
||||
required.
|
||||
|
||||
For backend connection, use :option:`--client-psk-secrets` option to
|
||||
specify a file which contains single PSK identity and secret. The
|
||||
format is the same as the file used by :option:`--psk-secrets`
|
||||
described above, but only first identity and secret pair is solely
|
||||
used, like so:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
client2:b1376c3f8f6dcf7c886c5bdcceecd1e6f1d708622b6ddd21bda26ebd0c0bca99
|
||||
|
||||
The default cipher suite list does not contain PSK cipher suites. In
|
||||
order to use PSK, PSK cipher suite must be enabled by using
|
||||
:option:`--client-ciphers` option. The desired PSK cipher suite may
|
||||
be listed in `HTTP/2 cipher black list
|
||||
<https://tools.ietf.org/html/rfc7540#appendix-A>`_. In order to use
|
||||
such PSK cipher suite with HTTP/2, disable HTTP/2 cipher black list by
|
||||
using :option:`--client-no-http2-cipher-black-list` option. But you
|
||||
should understand its implications.
|
||||
|
||||
Migration from nghttpx v1.18.x or earlier
|
||||
-----------------------------------------
|
||||
|
||||
As of nghttpx v1.19.0, :option:`--ciphers` option only changes cipher
|
||||
list for frontend TLS connection. In order to change cipher list for
|
||||
backend connection, use :option:`--client-ciphers` option.
|
||||
|
||||
Similarly, :option:`--no-http2-cipher-black-list` option only disables
|
||||
HTTP/2 cipher black list for frontend connection. In order to disable
|
||||
HTTP/2 cipher black list for backend connection, use
|
||||
:option:`--client-no-http2-cipher-black-list` option.
|
||||
|
||||
``--accept-proxy-protocol`` option was deprecated. Instead, use
|
||||
``proxyproto`` parameter in :option:`--frontend` option to enable
|
||||
PROXY protocol support per frontend.
|
||||
|
||||
Migration from nghttpx v1.8.0 or earlier
|
||||
----------------------------------------
|
||||
|
||||
|
||||
@@ -29,10 +29,6 @@ if(ENABLE_EXAMPLES)
|
||||
add_executable(libevent-server libevent-server.c $<TARGET_OBJECTS:http-parser>)
|
||||
add_executable(deflate deflate.c $<TARGET_OBJECTS:http-parser>)
|
||||
|
||||
if(ENABLE_TINY_NGHTTPD)
|
||||
add_executable(tiny-nghttpd tiny-nghttpd.c $<TARGET_OBJECTS:http-parser>)
|
||||
endif()
|
||||
|
||||
if(ENABLE_ASIO_LIB)
|
||||
foreach(name asio-sv asio-sv2 asio-cl asio-cl2)
|
||||
add_executable(${name} ${name}.cc $<TARGET_OBJECTS:http-parser>)
|
||||
|
||||
@@ -51,14 +51,6 @@ libevent_server_SOURCES = libevent-server.c
|
||||
|
||||
deflate_SOURCES = deflate.c
|
||||
|
||||
if ENABLE_TINY_NGHTTPD
|
||||
|
||||
noinst_PROGRAMS += tiny-nghttpd
|
||||
|
||||
tiny_nghttpd_SOURCES = tiny-nghttpd.c
|
||||
|
||||
endif # ENABLE_TINY_NGHTTPD
|
||||
|
||||
if ENABLE_ASIO_LIB
|
||||
|
||||
noinst_PROGRAMS += asio-sv asio-sv2 asio-cl asio-cl2
|
||||
|
||||
@@ -36,12 +36,25 @@
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include <nghttp2/asio_http2_server.h>
|
||||
|
||||
using namespace nghttp2::asio_http2;
|
||||
using namespace nghttp2::asio_http2::server;
|
||||
|
||||
namespace {
|
||||
void run_forever(boost::asio::io_service &io_service, size_t num_threads) {
|
||||
std::vector<std::thread> ts;
|
||||
for (size_t i = 0; i < num_threads; ++i) {
|
||||
ts.emplace_back([&io_service]() { io_service.run(); });
|
||||
}
|
||||
for (auto &t : ts) {
|
||||
t.join();
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
try {
|
||||
// Check command line arguments.
|
||||
@@ -58,9 +71,9 @@ int main(int argc, char *argv[]) {
|
||||
std::string port = argv[2];
|
||||
std::size_t num_threads = std::stoi(argv[3]);
|
||||
|
||||
http2 server;
|
||||
boost::asio::io_service io_service;
|
||||
|
||||
server.num_threads(num_threads);
|
||||
http2 server(io_service);
|
||||
|
||||
server.handle("/", [](const request &req, const response &res) {
|
||||
res.write_head(200, {{"foo", {"bar"}}});
|
||||
@@ -136,11 +149,16 @@ int main(int argc, char *argv[]) {
|
||||
if (server.listen_and_serve(ec, tls, addr, port)) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
}
|
||||
|
||||
run_forever(io_service, num_threads);
|
||||
} else {
|
||||
if (server.listen_and_serve(ec, addr, port)) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
}
|
||||
|
||||
run_forever(io_service, num_threads);
|
||||
}
|
||||
|
||||
} catch (std::exception &e) {
|
||||
std::cerr << "exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
@@ -43,12 +43,25 @@
|
||||
#endif // HAVE_FCNTL_H
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include <nghttp2/asio_http2_server.h>
|
||||
|
||||
using namespace nghttp2::asio_http2;
|
||||
using namespace nghttp2::asio_http2::server;
|
||||
|
||||
namespace {
|
||||
void run_forever(boost::asio::io_service &io_service, size_t num_threads) {
|
||||
std::vector<std::thread> ts;
|
||||
for (size_t i = 0; i < num_threads; ++i) {
|
||||
ts.emplace_back([&io_service]() { io_service.run(); });
|
||||
}
|
||||
for (auto &t : ts) {
|
||||
t.join();
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
try {
|
||||
// Check command line arguments.
|
||||
@@ -65,9 +78,9 @@ int main(int argc, char *argv[]) {
|
||||
std::size_t num_threads = std::stoi(argv[3]);
|
||||
std::string docroot = argv[4];
|
||||
|
||||
http2 server;
|
||||
boost::asio::io_service io_service;
|
||||
|
||||
server.num_threads(num_threads);
|
||||
http2 server(io_service);
|
||||
|
||||
server.handle("/", [&docroot](const request &req, const response &res) {
|
||||
auto path = percent_decode(req.uri().path);
|
||||
@@ -112,10 +125,14 @@ int main(int argc, char *argv[]) {
|
||||
if (server.listen_and_serve(ec, tls, addr, port)) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
}
|
||||
|
||||
run_forever(io_service, num_threads);
|
||||
} else {
|
||||
if (server.listen_and_serve(ec, addr, port)) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
}
|
||||
|
||||
run_forever(io_service, num_threads);
|
||||
}
|
||||
} catch (std::exception &e) {
|
||||
std::cerr << "exception: " << e.what() << "\n";
|
||||
|
||||
@@ -287,7 +287,7 @@ static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||
int rv;
|
||||
|
||||
if (session_data->stream_data->stream_id == stream_id) {
|
||||
fprintf(stderr, "Stream %d closed with error_code=%d\n", stream_id,
|
||||
fprintf(stderr, "Stream %d closed with error_code=%u\n", stream_id,
|
||||
error_code);
|
||||
rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
|
||||
if (rv != 0) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -149,6 +149,15 @@ OPTIONS = [
|
||||
"ecdh-curves",
|
||||
"tls-sct-dir",
|
||||
"backend-connect-timeout",
|
||||
"dns-cache-timeout",
|
||||
"dns-lookup-timeout",
|
||||
"dns-max-try",
|
||||
"frontend-keep-alive-timeout",
|
||||
"psk-secrets",
|
||||
"client-psk-secrets",
|
||||
"client-no-http2-cipher-black-list",
|
||||
"client-ciphers",
|
||||
"accesslog-write-early",
|
||||
]
|
||||
|
||||
LOGVARS = [
|
||||
|
||||
@@ -796,6 +796,30 @@ func TestH1H2RespPhaseReturn(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H2TE tests that "te: trailers" header is forwarded to HTTP/2
|
||||
// backend server by stripping other encodings.
|
||||
func TestH1H2TE(t *testing.T) {
|
||||
st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if got, want := r.Header.Get("te"), "trailers"; got != want {
|
||||
t.Errorf("te: %v; want %v", got, want)
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http1(requestParam{
|
||||
name: "TestH1H2TE",
|
||||
header: []hpack.HeaderField{
|
||||
pair("te", "foo,trailers,bar"),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http1() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1APIBackendconfig exercise backendconfig API endpoint routine
|
||||
// for successful case.
|
||||
func TestH1APIBackendconfig(t *testing.T) {
|
||||
|
||||
@@ -1369,6 +1369,42 @@ func TestH2H1ProxyProtocolV1InvalidID(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1ExternalDNS tests that DNS resolution using external DNS
|
||||
// with HTTP/1 backend works.
|
||||
func TestH2H1ExternalDNS(t *testing.T) {
|
||||
st := newServerTester([]string{"--external-dns"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1ExternalDNS",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("status = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1DNS tests that DNS resolution without external DNS with
|
||||
// HTTP/1 backend works.
|
||||
func TestH2H1DNS(t *testing.T) {
|
||||
st := newServerTester([]string{"--dns"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1DNS",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("status = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1GracefulShutdown tests graceful shutdown.
|
||||
func TestH2H1GracefulShutdown(t *testing.T) {
|
||||
st := newServerTester(nil, t, noopHandler)
|
||||
@@ -1845,6 +1881,42 @@ func TestH2H2RespPhaseReturn(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H2ExternalDNS tests that DNS resolution using external DNS
|
||||
// with HTTP/2 backend works.
|
||||
func TestH2H2ExternalDNS(t *testing.T) {
|
||||
st := newServerTester([]string{"--http2-bridge", "--external-dns"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H2ExternalDNS",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("status = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H2DNS tests that DNS resolution without external DNS with
|
||||
// HTTP/2 backend works.
|
||||
func TestH2H2DNS(t *testing.T) {
|
||||
st := newServerTester([]string{"--http2-bridge", "--dns"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H2DNS",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("status = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2APIBackendconfig exercise backendconfig API endpoint routine
|
||||
// for successful case.
|
||||
func TestH2APIBackendconfig(t *testing.T) {
|
||||
|
||||
@@ -101,10 +101,20 @@ func newServerTesterInternal(src_args []string, t *testing.T, handler http.Handl
|
||||
args := []string{}
|
||||
|
||||
backendTLS := false
|
||||
dns := false
|
||||
externalDNS := false
|
||||
acceptProxyProtocol := false
|
||||
for _, k := range src_args {
|
||||
switch k {
|
||||
case "--http2-bridge":
|
||||
backendTLS = true
|
||||
case "--dns":
|
||||
dns = true
|
||||
case "--external-dns":
|
||||
dns = true
|
||||
externalDNS = true
|
||||
case "--accept-proxy-protocol":
|
||||
acceptProxyProtocol = true
|
||||
default:
|
||||
args = append(args, k)
|
||||
}
|
||||
@@ -117,7 +127,7 @@ func newServerTesterInternal(src_args []string, t *testing.T, handler http.Handl
|
||||
ts.TLS = new(tls.Config)
|
||||
ts.TLS.NextProtos = append(ts.TLS.NextProtos, "h2")
|
||||
ts.StartTLS()
|
||||
args = append(args, "-k", "--backend-tls")
|
||||
args = append(args, "-k")
|
||||
} else {
|
||||
ts.Start()
|
||||
}
|
||||
@@ -134,17 +144,36 @@ func newServerTesterInternal(src_args []string, t *testing.T, handler http.Handl
|
||||
|
||||
// URL.Host looks like "127.0.0.1:8080", but we want
|
||||
// "127.0.0.1,8080"
|
||||
b := "-b" + strings.Replace(backendURL.Host, ":", ",", -1)
|
||||
if backendTLS {
|
||||
b += ";;proto=h2;tls"
|
||||
b := "-b"
|
||||
if !externalDNS {
|
||||
b += fmt.Sprintf("%v;", strings.Replace(backendURL.Host, ":", ",", -1))
|
||||
} else {
|
||||
sep := strings.LastIndex(backendURL.Host, ":")
|
||||
if sep == -1 {
|
||||
t.Fatalf("backendURL.Host %v does not contain separator ':'", backendURL.Host)
|
||||
}
|
||||
// We use awesome service xip.io.
|
||||
b += fmt.Sprintf("%v.xip.io,%v;", backendURL.Host[:sep], backendURL.Host[sep+1:])
|
||||
}
|
||||
|
||||
noTLS := "no-tls"
|
||||
if backendTLS {
|
||||
b += ";proto=h2;tls"
|
||||
}
|
||||
if dns {
|
||||
b += ";dns"
|
||||
}
|
||||
|
||||
noTLS := ";no-tls"
|
||||
if frontendTLS {
|
||||
noTLS = ""
|
||||
}
|
||||
|
||||
args = append(args, fmt.Sprintf("-f127.0.0.1,%v;%v", serverPort, noTLS), b,
|
||||
var proxyProto string
|
||||
if acceptProxyProtocol {
|
||||
proxyProto = ";proxyproto"
|
||||
}
|
||||
|
||||
args = append(args, fmt.Sprintf("-f127.0.0.1,%v%v%v", serverPort, noTLS, proxyProto), b,
|
||||
"--errorlog-file="+logDir+"/log.txt", "-LINFO")
|
||||
|
||||
authority := fmt.Sprintf("127.0.0.1:%v", connectPort)
|
||||
|
||||
@@ -26,8 +26,19 @@ set(NGHTTP2_SOURCES
|
||||
nghttp2_debug.c
|
||||
)
|
||||
|
||||
set(NGHTTP2_RES "")
|
||||
|
||||
if(WIN32)
|
||||
configure_file(
|
||||
version.rc.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/version.rc
|
||||
@ONLY)
|
||||
|
||||
set(NGHTTP2_RES ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
|
||||
endif()
|
||||
|
||||
# Public shared library
|
||||
add_library(nghttp2 SHARED ${NGHTTP2_SOURCES})
|
||||
add_library(nghttp2 SHARED ${NGHTTP2_SOURCES} ${NGHTTP2_RES})
|
||||
set_target_properties(nghttp2 PROPERTIES
|
||||
COMPILE_FLAGS "${WARNCFLAGS}"
|
||||
VERSION ${LT_VERSION} SOVERSION ${LT_SOVERSION}
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
SUBDIRS = includes
|
||||
|
||||
EXTRA_DIST = Makefile.msvc CMakeLists.txt
|
||||
EXTRA_DIST = Makefile.msvc CMakeLists.txt version.rc.in
|
||||
|
||||
AM_CFLAGS = $(WARNCFLAGS) $(EXTRACFLAG)
|
||||
AM_CPPFLAGS = -I$(srcdir)/includes -I$(builddir)/includes -DBUILDING_NGHTTP2 \
|
||||
|
||||
@@ -3566,7 +3566,7 @@ NGHTTP2_EXTERN int nghttp2_session_upgrade2(nghttp2_session *session,
|
||||
* Serializes the SETTINGS values |iv| in the |buf|. The size of the
|
||||
* |buf| is specified by |buflen|. The number of entries in the |iv|
|
||||
* array is given by |niv|. The required space in |buf| for the |niv|
|
||||
* entries is ``8*niv`` bytes and if the given buffer is too small, an
|
||||
* entries is ``6*niv`` bytes and if the given buffer is too small, an
|
||||
* error is returned. This function is used mainly for creating a
|
||||
* SETTINGS payload to be sent with the ``HTTP2-Settings`` header
|
||||
* field in an HTTP Upgrade request. The data written in |buf| is NOT
|
||||
|
||||
@@ -869,7 +869,9 @@ int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, const nghttp2_nv *nva,
|
||||
p->name = nva[i].name;
|
||||
p->namelen = nva[i].namelen;
|
||||
} else {
|
||||
if (nva[i].namelen) {
|
||||
memcpy(data, nva[i].name, nva[i].namelen);
|
||||
}
|
||||
p->name = data;
|
||||
p->namelen = nva[i].namelen;
|
||||
data[p->namelen] = '\0';
|
||||
@@ -881,7 +883,9 @@ int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, const nghttp2_nv *nva,
|
||||
p->value = nva[i].value;
|
||||
p->valuelen = nva[i].valuelen;
|
||||
} else {
|
||||
if (nva[i].valuelen) {
|
||||
memcpy(data, nva[i].value, nva[i].valuelen);
|
||||
}
|
||||
p->value = data;
|
||||
p->valuelen = nva[i].valuelen;
|
||||
data[p->valuelen] = '\0';
|
||||
|
||||
@@ -2281,10 +2281,6 @@ ssize_t nghttp2_hd_decode_length(uint32_t *res, size_t *shift_ptr, int *fin,
|
||||
return decode_length(res, shift_ptr, fin, initial, shift, in, last, prefix);
|
||||
}
|
||||
|
||||
static size_t hd_get_num_table_entries(nghttp2_hd_context *context) {
|
||||
return context->hd_table.len + NGHTTP2_STATIC_TABLE_LENGTH;
|
||||
}
|
||||
|
||||
static const nghttp2_nv *hd_get_table_entry(nghttp2_hd_context *context,
|
||||
size_t idx) {
|
||||
if (idx == 0) {
|
||||
@@ -2301,7 +2297,7 @@ static const nghttp2_nv *hd_get_table_entry(nghttp2_hd_context *context,
|
||||
}
|
||||
|
||||
size_t nghttp2_hd_deflate_get_num_table_entries(nghttp2_hd_deflater *deflater) {
|
||||
return hd_get_num_table_entries(&deflater->ctx);
|
||||
return get_max_index(&deflater->ctx);
|
||||
}
|
||||
|
||||
const nghttp2_nv *
|
||||
@@ -2320,7 +2316,7 @@ nghttp2_hd_deflate_get_max_dynamic_table_size(nghttp2_hd_deflater *deflater) {
|
||||
}
|
||||
|
||||
size_t nghttp2_hd_inflate_get_num_table_entries(nghttp2_hd_inflater *inflater) {
|
||||
return hd_get_num_table_entries(&inflater->ctx);
|
||||
return get_max_index(&inflater->ctx);
|
||||
}
|
||||
|
||||
const nghttp2_nv *
|
||||
|
||||
@@ -503,6 +503,10 @@ int nghttp2_check_header_value(const uint8_t *value, size_t len) {
|
||||
}
|
||||
|
||||
uint8_t *nghttp2_cpymem(uint8_t *dest, const void *src, size_t len) {
|
||||
if (len == 0) {
|
||||
return dest;
|
||||
}
|
||||
|
||||
memcpy(dest, src, len);
|
||||
|
||||
return dest + len;
|
||||
|
||||
@@ -250,6 +250,25 @@ static int http_response_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
|
||||
break;
|
||||
}
|
||||
case NGHTTP2_TOKEN_CONTENT_LENGTH: {
|
||||
if (stream->status_code == 204) {
|
||||
/* content-length header field in 204 response is prohibited by
|
||||
RFC 7230. But some widely used servers send content-length:
|
||||
0. Until they get fixed, we ignore it. */
|
||||
if (stream->content_length != -1) {
|
||||
/* Found multiple content-length field */
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
if (!lstrieq("0", nv->value->base, nv->value->len)) {
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
stream->content_length = 0;
|
||||
return NGHTTP2_ERR_REMOVE_HTTP_HEADER;
|
||||
}
|
||||
if (stream->status_code / 100 == 1 ||
|
||||
(stream->status_code == 200 &&
|
||||
(stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT))) {
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
if (stream->content_length != -1) {
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
|
||||
@@ -46,7 +46,13 @@ typedef enum {
|
||||
* Invalid HTTP header field was received but it can be treated as
|
||||
* if it was not received because of compatibility reasons.
|
||||
*/
|
||||
NGHTTP2_ERR_IGN_HTTP_HEADER = -105
|
||||
NGHTTP2_ERR_IGN_HTTP_HEADER = -105,
|
||||
/*
|
||||
* Invalid HTTP header field was received, and it is ignored.
|
||||
* Unlike NGHTTP2_ERR_IGN_HTTP_HEADER, this does not invoke
|
||||
* nghttp2_on_invalid_header_callback.
|
||||
*/
|
||||
NGHTTP2_ERR_REMOVE_HTTP_HEADER = -106
|
||||
} nghttp2_internal_error;
|
||||
|
||||
#endif /* NGHTTP2_INT_H */
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <assert.h>
|
||||
|
||||
#include "nghttp2_mem.h"
|
||||
#include "nghttp2_helper.h"
|
||||
|
||||
int nghttp2_rcbuf_new(nghttp2_rcbuf **rcbuf_ptr, size_t size,
|
||||
nghttp2_mem *mem) {
|
||||
@@ -58,10 +59,8 @@ int nghttp2_rcbuf_new2(nghttp2_rcbuf **rcbuf_ptr, const uint8_t *src,
|
||||
return rv;
|
||||
}
|
||||
|
||||
memcpy((*rcbuf_ptr)->base, src, srclen);
|
||||
|
||||
(*rcbuf_ptr)->len = srclen;
|
||||
(*rcbuf_ptr)->base[srclen] = '\0';
|
||||
*nghttp2_cpymem((*rcbuf_ptr)->base, src, srclen) = '\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1051,17 +1051,24 @@ nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session,
|
||||
flags |= NGHTTP2_STREAM_FLAG_PUSH;
|
||||
}
|
||||
|
||||
nghttp2_stream_init(stream, stream_id, flags, initial_state, pri_spec->weight,
|
||||
if (stream_alloc) {
|
||||
nghttp2_stream_init(stream, stream_id, flags, initial_state,
|
||||
pri_spec->weight,
|
||||
(int32_t)session->remote_settings.initial_window_size,
|
||||
(int32_t)session->local_settings.initial_window_size,
|
||||
stream_user_data, mem);
|
||||
|
||||
if (stream_alloc) {
|
||||
rv = nghttp2_map_insert(&session->streams, &stream->map_entry);
|
||||
if (rv != 0) {
|
||||
nghttp2_stream_free(stream);
|
||||
nghttp2_mem_free(mem, stream);
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
stream->flags = flags;
|
||||
stream->state = initial_state;
|
||||
stream->weight = pri_spec->weight;
|
||||
stream->stream_user_data = stream_user_data;
|
||||
}
|
||||
|
||||
switch (initial_state) {
|
||||
|
||||
@@ -365,7 +365,7 @@ int32_t nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags _U_,
|
||||
return promised_stream_id;
|
||||
}
|
||||
|
||||
int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags,
|
||||
int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags _U_,
|
||||
int32_t stream_id,
|
||||
int32_t window_size_increment) {
|
||||
int rv;
|
||||
@@ -373,7 +373,6 @@ int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags,
|
||||
if (window_size_increment == 0) {
|
||||
return 0;
|
||||
}
|
||||
flags = 0;
|
||||
if (stream_id == 0) {
|
||||
rv = nghttp2_adjust_local_window_size(
|
||||
&session->local_window_size, &session->recv_window_size,
|
||||
@@ -404,14 +403,14 @@ int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags,
|
||||
nghttp2_max(0, stream->consumed_size - window_size_increment);
|
||||
}
|
||||
|
||||
return nghttp2_session_add_window_update(session, flags, stream_id,
|
||||
return nghttp2_session_add_window_update(session, 0, stream_id,
|
||||
window_size_increment);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nghttp2_session_set_local_window_size(nghttp2_session *session,
|
||||
uint8_t flags, int32_t stream_id,
|
||||
uint8_t flags _U_, int32_t stream_id,
|
||||
int32_t window_size) {
|
||||
int32_t window_size_increment;
|
||||
nghttp2_stream *stream;
|
||||
@@ -421,8 +420,6 @@ int nghttp2_session_set_local_window_size(nghttp2_session *session,
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
flags = 0;
|
||||
|
||||
if (stream_id == 0) {
|
||||
window_size_increment = window_size - session->local_window_size;
|
||||
|
||||
@@ -472,7 +469,7 @@ int nghttp2_session_set_local_window_size(nghttp2_session *session,
|
||||
}
|
||||
|
||||
if (window_size_increment > 0) {
|
||||
return nghttp2_session_add_window_update(session, flags, stream_id,
|
||||
return nghttp2_session_add_window_update(session, 0, stream_id,
|
||||
window_size_increment);
|
||||
}
|
||||
|
||||
|
||||
40
lib/version.rc.in
Normal file
40
lib/version.rc.in
Normal file
@@ -0,0 +1,40 @@
|
||||
#include <winver.h>
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
|
||||
FILEVERSION @PROJECT_VERSION_MAJOR@, @PROJECT_VERSION_MINOR@, @PROJECT_VERSION_PATCH@, 0
|
||||
PRODUCTVERSION @PROJECT_VERSION_MAJOR@, @PROJECT_VERSION_MINOR@, @PROJECT_VERSION_PATCH@, 0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
FILEOS 0x40004L
|
||||
FILETYPE 0x2L
|
||||
FILESUBTYPE 0x0L
|
||||
#ifdef _DEBUG
|
||||
#define VER_STR "@PROJECT_VERSION@.0 (MSVC debug)"
|
||||
#define DBG "d"
|
||||
FILEFLAGS 0x1L
|
||||
#else
|
||||
#define VER_STR "@PROJECT_VERSION@.0 (MSVC release)"
|
||||
#define DBG ""
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "https://nghttp2.org/"
|
||||
VALUE "FileDescription", "nghttp2; HTTP/2 C library"
|
||||
VALUE "FileVersion", VER_STR
|
||||
VALUE "InternalName", "nghttp2" DBG
|
||||
VALUE "LegalCopyright", "The MIT License"
|
||||
VALUE "LegalTrademarks", ""
|
||||
VALUE "OriginalFilename", "nghttp2" DBG ".dll"
|
||||
VALUE "ProductName", "NGHTTP2."
|
||||
VALUE "ProductVersion", VER_STR
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
||||
@@ -1,104 +0,0 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_have_epoll.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_HAVE_EPOLL([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
|
||||
# AX_HAVE_EPOLL_PWAIT([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# This macro determines whether the system supports the epoll I/O event
|
||||
# interface. A neat usage example would be:
|
||||
#
|
||||
# AX_HAVE_EPOLL(
|
||||
# [AX_CONFIG_FEATURE_ENABLE(epoll)],
|
||||
# [AX_CONFIG_FEATURE_DISABLE(epoll)])
|
||||
# AX_CONFIG_FEATURE(
|
||||
# [epoll], [This platform supports epoll(7)],
|
||||
# [HAVE_EPOLL], [This platform supports epoll(7).])
|
||||
#
|
||||
# The epoll interface was added to the Linux kernel in version 2.5.45, and
|
||||
# the macro verifies that a kernel newer than this is installed. This
|
||||
# check is somewhat unreliable if <linux/version.h> doesn't match the
|
||||
# running kernel, but it is necessary regardless, because glibc comes with
|
||||
# stubs for the epoll_create(), epoll_wait(), etc. that allow programs to
|
||||
# compile and link even if the kernel is too old; the problem would then
|
||||
# be detected only at runtime.
|
||||
#
|
||||
# Linux kernel version 2.6.19 adds the epoll_pwait() call in addition to
|
||||
# epoll_wait(). The availability of that function can be tested with the
|
||||
# second macro. Generally speaking, it is safe to assume that
|
||||
# AX_HAVE_EPOLL would succeed if AX_HAVE_EPOLL_PWAIT has, but not the
|
||||
# other way round.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Peter Simons <simons@cryp.to>
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 10
|
||||
|
||||
AC_DEFUN([AX_HAVE_EPOLL], [dnl
|
||||
ax_have_epoll_cppflags="${CPPFLAGS}"
|
||||
AC_CHECK_HEADER([linux/version.h], [CPPFLAGS="${CPPFLAGS} -DHAVE_LINUX_VERSION_H"])
|
||||
AC_MSG_CHECKING([for Linux epoll(7) interface])
|
||||
AC_CACHE_VAL([ax_cv_have_epoll], [dnl
|
||||
AC_LINK_IFELSE([dnl
|
||||
AC_LANG_PROGRAM([dnl
|
||||
#include <sys/epoll.h>
|
||||
#ifdef HAVE_LINUX_VERSION_H
|
||||
# include <linux/version.h>
|
||||
# if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,45)
|
||||
# error linux kernel version is too old to have epoll
|
||||
# endif
|
||||
#endif
|
||||
], [dnl
|
||||
int fd, rc;
|
||||
struct epoll_event ev;
|
||||
fd = epoll_create(128);
|
||||
rc = epoll_wait(fd, &ev, 1, 0);])],
|
||||
[ax_cv_have_epoll=yes],
|
||||
[ax_cv_have_epoll=no])])
|
||||
CPPFLAGS="${ax_have_epoll_cppflags}"
|
||||
AS_IF([test "${ax_cv_have_epoll}" = "yes"],
|
||||
[AC_MSG_RESULT([yes])
|
||||
$1],[AC_MSG_RESULT([no])
|
||||
$2])
|
||||
])dnl
|
||||
|
||||
AC_DEFUN([AX_HAVE_EPOLL_PWAIT], [dnl
|
||||
ax_have_epoll_cppflags="${CPPFLAGS}"
|
||||
AC_CHECK_HEADER([linux/version.h],
|
||||
[CPPFLAGS="${CPPFLAGS} -DHAVE_LINUX_VERSION_H"])
|
||||
AC_MSG_CHECKING([for Linux epoll(7) interface with signals extension])
|
||||
AC_CACHE_VAL([ax_cv_have_epoll_pwait], [dnl
|
||||
AC_LINK_IFELSE([dnl
|
||||
AC_LANG_PROGRAM([dnl
|
||||
#ifdef HAVE_LINUX_VERSION_H
|
||||
# include <linux/version.h>
|
||||
# if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
|
||||
# error linux kernel version is too old to have epoll_pwait
|
||||
# endif
|
||||
#endif
|
||||
#include <sys/epoll.h>
|
||||
#include <signal.h>
|
||||
], [dnl
|
||||
int fd, rc;
|
||||
struct epoll_event ev;
|
||||
fd = epoll_create(128);
|
||||
rc = epoll_wait(fd, &ev, 1, 0);
|
||||
rc = epoll_pwait(fd, &ev, 1, 0, (sigset_t const *)(0));])],
|
||||
[ax_cv_have_epoll_pwait=yes],
|
||||
[ax_cv_have_epoll_pwait=no])])
|
||||
CPPFLAGS="${ax_have_epoll_cppflags}"
|
||||
AS_IF([test "${ax_cv_have_epoll_pwait}" = "yes"],
|
||||
[AC_MSG_RESULT([yes])
|
||||
$1],[AC_MSG_RESULT([no])
|
||||
$2])
|
||||
])dnl
|
||||
@@ -146,7 +146,7 @@ def send_and_receive_ocsp(respder_fn, cmd, cert_fn, issuer_fn, ocsp_uri,
|
||||
'-noverify', '-respout', respder_fn
|
||||
]
|
||||
ver = openssl_version.lower()
|
||||
if ver.startswith('openssl 1.') or ver.startswith('libressl '):
|
||||
if ver.startswith('openssl 1.0.') or ver.startswith('libressl '):
|
||||
args.extend(['-header', 'Host', ocsp_host])
|
||||
resp = run_openssl(args, allow_tempfail=True)
|
||||
return resp.decode('utf-8')
|
||||
|
||||
@@ -19,6 +19,7 @@ include_directories(
|
||||
${LIBXML2_INCLUDE_DIRS}
|
||||
${LIBEV_INCLUDE_DIRS}
|
||||
${OPENSSL_INCLUDE_DIRS}
|
||||
${LIBCARES_INCLUDE_DIRS}
|
||||
${JANSSON_INCLUDE_DIRS}
|
||||
${ZLIB_INCLUDE_DIRS}
|
||||
)
|
||||
@@ -31,6 +32,7 @@ link_libraries(
|
||||
${LIBXML2_LIBRARIES}
|
||||
${LIBEV_LIBRARIES}
|
||||
${OPENSSL_LIBRARIES}
|
||||
${LIBCARES_LIBRARIES}
|
||||
${JANSSON_LIBRARIES}
|
||||
${ZLIB_LIBRARIES}
|
||||
${APP_LIBRARIES}
|
||||
@@ -112,6 +114,9 @@ if(ENABLE_APP)
|
||||
shrpx_api_downstream_connection.cc
|
||||
shrpx_health_monitor_downstream_connection.cc
|
||||
shrpx_exec.cc
|
||||
shrpx_dns_resolver.cc
|
||||
shrpx_dual_dns_resolver.cc
|
||||
shrpx_dns_tracker.cc
|
||||
xsi_strerror.c
|
||||
)
|
||||
if(HAVE_SPDYLAY)
|
||||
@@ -214,7 +219,6 @@ if(ENABLE_ASIO_LIB)
|
||||
ssl.cc
|
||||
timegm.c
|
||||
asio_common.cc
|
||||
asio_io_service_pool.cc
|
||||
asio_server_http2.cc
|
||||
asio_server_http2_impl.cc
|
||||
asio_server.cc
|
||||
|
||||
@@ -55,6 +55,21 @@ StringRef get_attr(const xmlChar **attrs, const StringRef &name) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
ResourceType
|
||||
get_resource_type_for_preload_as(const StringRef &attribute_value) {
|
||||
if (util::strieq_l("image", attribute_value)) {
|
||||
return REQ_IMG;
|
||||
} else if (util::strieq_l("style", attribute_value)) {
|
||||
return REQ_CSS;
|
||||
} else if (util::strieq_l("script", attribute_value)) {
|
||||
return REQ_UNBLOCK_JS;
|
||||
} else {
|
||||
return REQ_OTHERS;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void add_link(ParserData *parser_data, const StringRef &uri,
|
||||
ResourceType res_type) {
|
||||
@@ -88,6 +103,13 @@ void start_element_func(void *user_data, const xmlChar *src_name,
|
||||
add_link(parser_data, href_attr, REQ_OTHERS);
|
||||
} else if (util::strieq_l("stylesheet", rel_attr)) {
|
||||
add_link(parser_data, href_attr, REQ_CSS);
|
||||
} else if (util::strieq_l("preload", rel_attr)) {
|
||||
auto as_attr = get_attr(attrs, StringRef::from_lit("as"));
|
||||
if (as_attr.empty()) {
|
||||
return;
|
||||
}
|
||||
add_link(parser_data, href_attr,
|
||||
get_resource_type_for_preload_as(as_attr));
|
||||
}
|
||||
} else if (util::strieq_l("img", name)) {
|
||||
auto src_attr = get_attr(attrs, StringRef::from_lit("src"));
|
||||
|
||||
@@ -557,7 +557,7 @@ Http2Handler::~Http2Handler() {
|
||||
on_session_closed(this, session_id_);
|
||||
nghttp2_session_del(session_);
|
||||
if (ssl_) {
|
||||
SSL_set_shutdown(ssl_, SSL_RECEIVED_SHUTDOWN);
|
||||
SSL_set_shutdown(ssl_, SSL_get_shutdown(ssl_) | SSL_RECEIVED_SHUTDOWN);
|
||||
ERR_clear_error();
|
||||
SSL_shutdown(ssl_);
|
||||
}
|
||||
@@ -1921,13 +1921,18 @@ namespace {
|
||||
FileEntry make_status_body(int status, uint16_t port) {
|
||||
BlockAllocator balloc(1024, 1024);
|
||||
|
||||
auto status_string = http2::get_status_string(balloc, status);
|
||||
auto status_string = http2::stringify_status(balloc, status);
|
||||
auto reason_pharase = http2::get_reason_phrase(status);
|
||||
|
||||
std::string body;
|
||||
body = "<html><head><title>";
|
||||
body += status_string;
|
||||
body += ' ';
|
||||
body += reason_pharase;
|
||||
body += "</title></head><body><h1>";
|
||||
body += status_string;
|
||||
body += ' ';
|
||||
body += reason_pharase;
|
||||
body += "</h1><hr><address>";
|
||||
body += NGHTTPD_SERVER;
|
||||
body += " at port ";
|
||||
|
||||
@@ -41,9 +41,10 @@ AM_CPPFLAGS = \
|
||||
-I$(top_srcdir)/src/includes \
|
||||
-I$(top_srcdir)/third-party \
|
||||
@LIBSPDYLAY_CFLAGS@ \
|
||||
@XML_CPPFLAGS@ \
|
||||
@LIBXML2_CFLAGS@ \
|
||||
@LIBEV_CFLAGS@ \
|
||||
@OPENSSL_CFLAGS@ \
|
||||
@LIBCARES_CFLAGS@ \
|
||||
@JANSSON_CFLAGS@ \
|
||||
@ZLIB_CFLAGS@ \
|
||||
@DEFS@
|
||||
@@ -52,9 +53,10 @@ LDADD = $(top_builddir)/lib/libnghttp2.la \
|
||||
$(top_builddir)/third-party/libhttp-parser.la \
|
||||
@JEMALLOC_LIBS@ \
|
||||
@LIBSPDYLAY_LIBS@ \
|
||||
@XML_LIBS@ \
|
||||
@LIBXML2_LIBS@ \
|
||||
@LIBEV_LIBS@ \
|
||||
@OPENSSL_LIBS@ \
|
||||
@LIBCARES_LIBS@ \
|
||||
@JANSSON_LIBS@ \
|
||||
@ZLIB_LIBS@ \
|
||||
@APPLDFLAGS@
|
||||
@@ -139,6 +141,9 @@ NGHTTPX_SRCS = \
|
||||
shrpx_health_monitor_downstream_connection.cc \
|
||||
shrpx_health_monitor_downstream_connection.h \
|
||||
shrpx_exec.cc shrpx_exec.h \
|
||||
shrpx_dns_resolver.cc shrpx_dns_resolver.h \
|
||||
shrpx_dual_dns_resolver.cc shrpx_dual_dns_resolver.h \
|
||||
shrpx_dns_tracker.cc shrpx_dns_tracker.h \
|
||||
buffer.h memchunk.h template.h allocator.h \
|
||||
xsi_strerror.c xsi_strerror.h
|
||||
|
||||
@@ -238,7 +243,6 @@ libnghttp2_asio_la_SOURCES = \
|
||||
ssl_compat.h \
|
||||
timegm.c timegm.h \
|
||||
asio_common.cc asio_common.h \
|
||||
asio_io_service_pool.cc asio_io_service_pool.h \
|
||||
asio_server_http2.cc \
|
||||
asio_server_http2_impl.cc asio_server_http2_impl.h \
|
||||
asio_server.cc asio_server.h \
|
||||
|
||||
@@ -69,17 +69,17 @@ void session_impl::start_resolve(const std::string &host,
|
||||
const std::string &service) {
|
||||
deadline_.expires_from_now(connect_timeout_);
|
||||
|
||||
auto self = this->shared_from_this();
|
||||
auto self = shared_from_this();
|
||||
|
||||
resolver_.async_resolve({host, service},
|
||||
[this, self](const boost::system::error_code &ec,
|
||||
[self](const boost::system::error_code &ec,
|
||||
tcp::resolver::iterator endpoint_it) {
|
||||
if (ec) {
|
||||
not_connected(ec);
|
||||
self->not_connected(ec);
|
||||
return;
|
||||
}
|
||||
|
||||
start_connect(endpoint_it);
|
||||
self->start_connect(endpoint_it);
|
||||
});
|
||||
|
||||
deadline_.async_wait(std::bind(&session_impl::handle_deadline, self));
|
||||
@@ -597,38 +597,38 @@ void session_impl::do_read() {
|
||||
|
||||
auto self = this->shared_from_this();
|
||||
|
||||
read_socket([this, self](const boost::system::error_code &ec,
|
||||
read_socket([self](const boost::system::error_code &ec,
|
||||
std::size_t bytes_transferred) {
|
||||
if (ec) {
|
||||
if (!should_stop()) {
|
||||
call_error_cb(ec);
|
||||
if (!self->should_stop()) {
|
||||
self->call_error_cb(ec);
|
||||
}
|
||||
stop();
|
||||
self->stop();
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
callback_guard cg(*this);
|
||||
callback_guard cg(*self);
|
||||
|
||||
auto rv =
|
||||
nghttp2_session_mem_recv(session_, rb_.data(), bytes_transferred);
|
||||
nghttp2_session_mem_recv(self->session_, self->rb_.data(), bytes_transferred);
|
||||
|
||||
if (rv != static_cast<ssize_t>(bytes_transferred)) {
|
||||
call_error_cb(make_error_code(
|
||||
self->call_error_cb(make_error_code(
|
||||
static_cast<nghttp2_error>(rv < 0 ? rv : NGHTTP2_ERR_PROTO)));
|
||||
stop();
|
||||
self->stop();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
do_write();
|
||||
self->do_write();
|
||||
|
||||
if (should_stop()) {
|
||||
stop();
|
||||
if (self->should_stop()) {
|
||||
self->stop();
|
||||
return;
|
||||
}
|
||||
|
||||
do_read();
|
||||
self->do_read();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -695,17 +695,17 @@ void session_impl::do_write() {
|
||||
auto self = this->shared_from_this();
|
||||
|
||||
write_socket(
|
||||
[this, self](const boost::system::error_code &ec, std::size_t n) {
|
||||
[self](const boost::system::error_code &ec, std::size_t n) {
|
||||
if (ec) {
|
||||
call_error_cb(ec);
|
||||
stop();
|
||||
self->call_error_cb(ec);
|
||||
self->stop();
|
||||
return;
|
||||
}
|
||||
|
||||
wblen_ = 0;
|
||||
writing_ = false;
|
||||
self->wblen_ = 0;
|
||||
self->writing_ = false;
|
||||
|
||||
do_write();
|
||||
self->do_write();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -37,19 +37,20 @@ session_tcp_impl::session_tcp_impl(
|
||||
session_tcp_impl::~session_tcp_impl() {}
|
||||
|
||||
void session_tcp_impl::start_connect(tcp::resolver::iterator endpoint_it) {
|
||||
auto self = shared_from_this();
|
||||
boost::asio::async_connect(socket_, endpoint_it,
|
||||
[this](const boost::system::error_code &ec,
|
||||
[self](const boost::system::error_code &ec,
|
||||
tcp::resolver::iterator endpoint_it) {
|
||||
if (stopped()) {
|
||||
if (self->stopped()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ec) {
|
||||
not_connected(ec);
|
||||
self->not_connected(ec);
|
||||
return;
|
||||
}
|
||||
|
||||
connected(endpoint_it);
|
||||
self->connected(endpoint_it);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -43,37 +43,38 @@ session_tls_impl::session_tls_impl(
|
||||
session_tls_impl::~session_tls_impl() {}
|
||||
|
||||
void session_tls_impl::start_connect(tcp::resolver::iterator endpoint_it) {
|
||||
auto self = std::static_pointer_cast<session_tls_impl>(shared_from_this());
|
||||
boost::asio::async_connect(
|
||||
socket(), endpoint_it, [this](const boost::system::error_code &ec,
|
||||
socket(), endpoint_it, [self](const boost::system::error_code &ec,
|
||||
tcp::resolver::iterator endpoint_it) {
|
||||
if (stopped()) {
|
||||
if (self->stopped()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ec) {
|
||||
not_connected(ec);
|
||||
self->not_connected(ec);
|
||||
return;
|
||||
}
|
||||
|
||||
socket_.async_handshake(
|
||||
self->socket_.async_handshake(
|
||||
boost::asio::ssl::stream_base::client,
|
||||
[this, endpoint_it](const boost::system::error_code &ec) {
|
||||
if (stopped()) {
|
||||
[self, endpoint_it](const boost::system::error_code &ec) {
|
||||
if (self->stopped()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ec) {
|
||||
not_connected(ec);
|
||||
self->not_connected(ec);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tls_h2_negotiated(socket_)) {
|
||||
not_connected(make_error_code(
|
||||
if (!tls_h2_negotiated(self->socket_)) {
|
||||
self->not_connected(make_error_code(
|
||||
NGHTTP2_ASIO_ERR_TLS_NO_APP_PROTO_NEGOTIATED));
|
||||
return;
|
||||
}
|
||||
|
||||
connected(endpoint_it);
|
||||
self->connected(endpoint_it);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2014 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.
|
||||
*/
|
||||
// We wrote this code based on the original code which has the
|
||||
// following license:
|
||||
//
|
||||
// io_service_pool.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#include "asio_io_service_pool.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
namespace asio_http2 {
|
||||
|
||||
io_service_pool::io_service_pool(std::size_t pool_size) : next_io_service_(0) {
|
||||
if (pool_size == 0) {
|
||||
throw std::runtime_error("io_service_pool size is 0");
|
||||
}
|
||||
|
||||
// Give all the io_services work to do so that their run() functions will not
|
||||
// exit until they are explicitly stopped.
|
||||
for (std::size_t i = 0; i < pool_size; ++i) {
|
||||
auto io_service = std::make_shared<boost::asio::io_service>();
|
||||
auto work = std::make_shared<boost::asio::io_service::work>(*io_service);
|
||||
io_services_.push_back(io_service);
|
||||
work_.push_back(work);
|
||||
}
|
||||
}
|
||||
|
||||
void io_service_pool::run(bool asynchronous) {
|
||||
// Create a pool of threads to run all of the io_services.
|
||||
for (std::size_t i = 0; i < io_services_.size(); ++i) {
|
||||
futures_.push_back(std::async(std::launch::async,
|
||||
(size_t(boost::asio::io_service::*)(void)) &
|
||||
boost::asio::io_service::run,
|
||||
io_services_[i]));
|
||||
}
|
||||
|
||||
if (!asynchronous) {
|
||||
join();
|
||||
}
|
||||
}
|
||||
|
||||
void io_service_pool::join() {
|
||||
// Wait for all threads in the pool to exit.
|
||||
for (auto &fut : futures_) {
|
||||
fut.get();
|
||||
}
|
||||
}
|
||||
|
||||
void io_service_pool::stop() {
|
||||
// Explicitly stop all io_services.
|
||||
for (auto &iosv : io_services_) {
|
||||
iosv->stop();
|
||||
}
|
||||
}
|
||||
|
||||
boost::asio::io_service &io_service_pool::get_io_service() {
|
||||
// Use a round-robin scheme to choose the next io_service to use.
|
||||
auto &io_service = *io_services_[next_io_service_];
|
||||
++next_io_service_;
|
||||
if (next_io_service_ == io_services_.size()) {
|
||||
next_io_service_ = 0;
|
||||
}
|
||||
return io_service;
|
||||
}
|
||||
|
||||
const std::vector<std::shared_ptr<boost::asio::io_service>> &
|
||||
io_service_pool::io_services() const {
|
||||
return io_services_;
|
||||
}
|
||||
|
||||
} // namespace asio_http2
|
||||
|
||||
} // namespace nghttp2
|
||||
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2014 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.
|
||||
*/
|
||||
// We wrote this code based on the original code which has the
|
||||
// following license:
|
||||
//
|
||||
// io_service_pool.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_IO_SERVICE_POOL_H
|
||||
#define ASIO_IO_SERVICE_POOL_H
|
||||
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <future>
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
#include <nghttp2/asio_http2.h>
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
namespace asio_http2 {
|
||||
|
||||
/// A pool of io_service objects.
|
||||
class io_service_pool : private boost::noncopyable {
|
||||
public:
|
||||
/// Construct the io_service pool.
|
||||
explicit io_service_pool(std::size_t pool_size);
|
||||
|
||||
/// Run all io_service objects in the pool.
|
||||
void run(bool asynchronous = false);
|
||||
|
||||
/// Stop all io_service objects in the pool.
|
||||
void stop();
|
||||
|
||||
/// Join on all io_service objects in the pool.
|
||||
void join();
|
||||
|
||||
/// Get an io_service to use.
|
||||
boost::asio::io_service &get_io_service();
|
||||
|
||||
/// Get access to all io_service objects.
|
||||
const std::vector<std::shared_ptr<boost::asio::io_service>> &
|
||||
io_services() const;
|
||||
|
||||
private:
|
||||
/// The pool of io_services.
|
||||
std::vector<std::shared_ptr<boost::asio::io_service>> io_services_;
|
||||
|
||||
/// The work that keeps the io_services running.
|
||||
std::vector<std::shared_ptr<boost::asio::io_service::work>> work_;
|
||||
|
||||
/// The next io_service to use for a connection.
|
||||
std::size_t next_io_service_;
|
||||
|
||||
/// Futures to all the io_service objects
|
||||
std::vector<std::future<std::size_t>> futures_;
|
||||
};
|
||||
|
||||
} // namespace asio_http2
|
||||
|
||||
} // namespace nghttp2
|
||||
|
||||
#endif // ASIO_IO_SERVICE_POOL_H
|
||||
@@ -44,10 +44,10 @@ namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace server {
|
||||
|
||||
server::server(std::size_t io_service_pool_size,
|
||||
server::server(boost::asio::io_service &service,
|
||||
const boost::posix_time::time_duration &tls_handshake_timeout,
|
||||
const boost::posix_time::time_duration &read_timeout)
|
||||
: io_service_pool_(io_service_pool_size),
|
||||
: service_(service),
|
||||
tls_handshake_timeout_(tls_handshake_timeout),
|
||||
read_timeout_(read_timeout) {}
|
||||
|
||||
@@ -70,8 +70,6 @@ server::listen_and_serve(boost::system::error_code &ec,
|
||||
}
|
||||
}
|
||||
|
||||
io_service_pool_.run(asynchronous);
|
||||
|
||||
return ec;
|
||||
}
|
||||
|
||||
@@ -81,7 +79,7 @@ boost::system::error_code server::bind_and_listen(boost::system::error_code &ec,
|
||||
int backlog) {
|
||||
// Open the acceptor with the option to reuse the address (i.e.
|
||||
// SO_REUSEADDR).
|
||||
tcp::resolver resolver(io_service_pool_.get_io_service());
|
||||
tcp::resolver resolver(service_);
|
||||
tcp::resolver::query query(address, port);
|
||||
auto it = resolver.resolve(query, ec);
|
||||
if (ec) {
|
||||
@@ -90,7 +88,7 @@ boost::system::error_code server::bind_and_listen(boost::system::error_code &ec,
|
||||
|
||||
for (; it != tcp::resolver::iterator(); ++it) {
|
||||
tcp::endpoint endpoint = *it;
|
||||
auto acceptor = tcp::acceptor(io_service_pool_.get_io_service());
|
||||
auto acceptor = tcp::acceptor(service_);
|
||||
|
||||
if (acceptor.open(endpoint.protocol(), ec)) {
|
||||
continue;
|
||||
@@ -126,7 +124,7 @@ void server::start_accept(boost::asio::ssl::context &tls_context,
|
||||
tcp::acceptor &acceptor, serve_mux &mux) {
|
||||
auto new_connection = std::make_shared<connection<ssl_socket>>(
|
||||
mux, tls_handshake_timeout_, read_timeout_,
|
||||
io_service_pool_.get_io_service(), tls_context);
|
||||
service_, tls_context);
|
||||
|
||||
acceptor.async_accept(
|
||||
new_connection->socket().lowest_layer(),
|
||||
@@ -159,8 +157,7 @@ void server::start_accept(boost::asio::ssl::context &tls_context,
|
||||
|
||||
void server::start_accept(tcp::acceptor &acceptor, serve_mux &mux) {
|
||||
auto new_connection = std::make_shared<connection<tcp::socket>>(
|
||||
mux, tls_handshake_timeout_, read_timeout_,
|
||||
io_service_pool_.get_io_service());
|
||||
mux, tls_handshake_timeout_, read_timeout_, service_);
|
||||
|
||||
acceptor.async_accept(
|
||||
new_connection->socket(), [this, &acceptor, &mux, new_connection](
|
||||
@@ -170,25 +167,18 @@ void server::start_accept(tcp::acceptor &acceptor, serve_mux &mux) {
|
||||
new_connection->start_read_deadline();
|
||||
new_connection->start();
|
||||
}
|
||||
|
||||
if (acceptor.is_open()) {
|
||||
start_accept(acceptor, mux);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void server::stop() {
|
||||
io_service_pool_.stop();
|
||||
for (auto &acceptor : acceptors_) {
|
||||
acceptor.close();
|
||||
}
|
||||
}
|
||||
|
||||
void server::join() { io_service_pool_.join(); }
|
||||
|
||||
const std::vector<std::shared_ptr<boost::asio::io_service>> &
|
||||
server::io_services() const {
|
||||
return io_service_pool_.io_services();
|
||||
}
|
||||
|
||||
} // namespace server
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
||||
|
||||
@@ -44,11 +44,10 @@
|
||||
#include <memory>
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/asio/io_service.hpp>
|
||||
|
||||
#include <nghttp2/asio_http2_server.h>
|
||||
|
||||
#include "asio_io_service_pool.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
namespace asio_http2 {
|
||||
@@ -63,7 +62,7 @@ using ssl_socket = boost::asio::ssl::stream<tcp::socket>;
|
||||
|
||||
class server : private boost::noncopyable {
|
||||
public:
|
||||
explicit server(std::size_t io_service_pool_size,
|
||||
explicit server(boost::asio::io_service &service,
|
||||
const boost::posix_time::time_duration &tls_handshake_timeout,
|
||||
const boost::posix_time::time_duration &read_timeout);
|
||||
|
||||
@@ -91,10 +90,7 @@ private:
|
||||
const std::string &address,
|
||||
const std::string &port,
|
||||
int backlog);
|
||||
|
||||
/// The pool of io_service objects used to perform asynchronous
|
||||
/// operations.
|
||||
io_service_pool io_service_pool_;
|
||||
boost::asio::io_service &service_;
|
||||
|
||||
/// Acceptor used to listen for incoming connections.
|
||||
std::vector<tcp::acceptor> acceptors_;
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace asio_http2 {
|
||||
|
||||
namespace server {
|
||||
|
||||
http2::http2() : impl_(make_unique<http2_impl>()) {}
|
||||
http2::http2(boost::asio::io_service &service) : impl_(make_unique<http2_impl>(service)) {}
|
||||
|
||||
http2::~http2() {}
|
||||
|
||||
@@ -65,8 +65,6 @@ boost::system::error_code http2::listen_and_serve(
|
||||
return impl_->listen_and_serve(ec, &tls_context, address, port, asynchronous);
|
||||
}
|
||||
|
||||
void http2::num_threads(size_t num_threads) { impl_->num_threads(num_threads); }
|
||||
|
||||
void http2::backlog(int backlog) { impl_->backlog(backlog); }
|
||||
|
||||
void http2::tls_handshake_timeout(const boost::posix_time::time_duration &t) {
|
||||
@@ -83,13 +81,6 @@ bool http2::handle(std::string pattern, request_cb cb) {
|
||||
|
||||
void http2::stop() { impl_->stop(); }
|
||||
|
||||
void http2::join() { return impl_->join(); }
|
||||
|
||||
const std::vector<std::shared_ptr<boost::asio::io_service>> &
|
||||
http2::io_services() const {
|
||||
return impl_->io_services();
|
||||
}
|
||||
|
||||
} // namespace server
|
||||
|
||||
} // namespace asio_http2
|
||||
|
||||
@@ -249,7 +249,15 @@ http2_handler::http2_handler(boost::asio::io_service &io_service,
|
||||
tstamp_cached_(time(nullptr)),
|
||||
formatted_date_(util::http_date(tstamp_cached_)) {}
|
||||
|
||||
http2_handler::~http2_handler() { nghttp2_session_del(session_); }
|
||||
http2_handler::~http2_handler() {
|
||||
for (auto &p : streams_) {
|
||||
auto &strm = p.second;
|
||||
strm->response().impl().call_on_close(NGHTTP2_INTERNAL_ERROR);
|
||||
close_stream(strm->get_stream_id());
|
||||
}
|
||||
|
||||
nghttp2_session_del(session_);
|
||||
}
|
||||
|
||||
const std::string &http2_handler::http_date() {
|
||||
auto t = time(nullptr);
|
||||
|
||||
@@ -37,8 +37,8 @@ namespace asio_http2 {
|
||||
|
||||
namespace server {
|
||||
|
||||
http2_impl::http2_impl()
|
||||
: num_threads_(1),
|
||||
http2_impl::http2_impl(boost::asio::io_service &service)
|
||||
: service_(service),
|
||||
backlog_(-1),
|
||||
tls_handshake_timeout_(boost::posix_time::seconds(60)),
|
||||
read_timeout_(boost::posix_time::seconds(60)) {}
|
||||
@@ -47,13 +47,11 @@ boost::system::error_code http2_impl::listen_and_serve(
|
||||
boost::system::error_code &ec, boost::asio::ssl::context *tls_context,
|
||||
const std::string &address, const std::string &port, bool asynchronous) {
|
||||
server_.reset(
|
||||
new server(num_threads_, tls_handshake_timeout_, read_timeout_));
|
||||
new server(service_, tls_handshake_timeout_, read_timeout_));
|
||||
return server_->listen_and_serve(ec, tls_context, address, port, backlog_,
|
||||
mux_, asynchronous);
|
||||
}
|
||||
|
||||
void http2_impl::num_threads(size_t num_threads) { num_threads_ = num_threads; }
|
||||
|
||||
void http2_impl::backlog(int backlog) { backlog_ = backlog; }
|
||||
|
||||
void http2_impl::tls_handshake_timeout(
|
||||
@@ -71,13 +69,6 @@ bool http2_impl::handle(std::string pattern, request_cb cb) {
|
||||
|
||||
void http2_impl::stop() { return server_->stop(); }
|
||||
|
||||
void http2_impl::join() { return server_->join(); }
|
||||
|
||||
const std::vector<std::shared_ptr<boost::asio::io_service>> &
|
||||
http2_impl::io_services() const {
|
||||
return server_->io_services();
|
||||
}
|
||||
|
||||
} // namespace server
|
||||
|
||||
} // namespace asio_http2
|
||||
|
||||
@@ -41,7 +41,7 @@ class server;
|
||||
|
||||
class http2_impl {
|
||||
public:
|
||||
http2_impl();
|
||||
http2_impl(boost::asio::io_service &service);
|
||||
boost::system::error_code listen_and_serve(
|
||||
boost::system::error_code &ec, boost::asio::ssl::context *tls_context,
|
||||
const std::string &address, const std::string &port, bool asynchronous);
|
||||
@@ -51,13 +51,10 @@ public:
|
||||
void read_timeout(const boost::posix_time::time_duration &t);
|
||||
bool handle(std::string pattern, request_cb cb);
|
||||
void stop();
|
||||
void join();
|
||||
const std::vector<std::shared_ptr<boost::asio::io_service>> &
|
||||
io_services() const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<server> server_;
|
||||
std::size_t num_threads_;
|
||||
boost::asio::io_service &service_;
|
||||
int backlog_;
|
||||
serve_mux mux_;
|
||||
boost::posix_time::time_duration tls_handshake_timeout_;
|
||||
|
||||
@@ -36,11 +36,16 @@ std::string create_html(int status_code) {
|
||||
BlockAllocator balloc(1024, 1024);
|
||||
std::string res;
|
||||
res.reserve(512);
|
||||
auto status = ::nghttp2::http2::get_status_string(balloc, status_code);
|
||||
auto status_string = ::nghttp2::http2::stringify_status(balloc, status_code);
|
||||
auto reason_phrase = ::nghttp2::http2::get_reason_phrase(status_code);
|
||||
res += R"(<!DOCTYPE html><html lang="en"><title>)";
|
||||
res += status;
|
||||
res += status_string;
|
||||
res += ' ';
|
||||
res += reason_phrase;
|
||||
res += "</title><body><h1>";
|
||||
res += status;
|
||||
res += status_string;
|
||||
res += ' ';
|
||||
res += reason_phrase;
|
||||
res += "</h1></body></html>";
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -373,7 +373,7 @@ OPTIONS:
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
static struct option long_options[] = {
|
||||
constexpr static struct option long_options[] = {
|
||||
{"http1text", no_argument, nullptr, 't'},
|
||||
{"table-size", required_argument, nullptr, 's'},
|
||||
{"deflate-table-size", required_argument, nullptr, 'S'},
|
||||
|
||||
@@ -79,7 +79,8 @@ bool recorded(const std::chrono::steady_clock::time_point &t) {
|
||||
} // namespace
|
||||
|
||||
Config::Config()
|
||||
: data_length(-1),
|
||||
: ciphers(ssl::DEFAULT_CIPHER_LIST),
|
||||
data_length(-1),
|
||||
addrs(nullptr),
|
||||
nreqs(1),
|
||||
nclients(1),
|
||||
@@ -268,9 +269,7 @@ void conn_timeout_cb(EV_P_ ev_timer *w, int revents) {
|
||||
|
||||
namespace {
|
||||
bool check_stop_client_request_timeout(Client *client, ev_timer *w) {
|
||||
auto nreq = client->req_todo - client->req_started;
|
||||
|
||||
if (nreq == 0 ||
|
||||
if (client->req_left == 0 ||
|
||||
client->streams.size() >= client->session->max_concurrent_streams()) {
|
||||
// no more requests to make, stop timer
|
||||
ev_timer_stop(client->worker->loop, w);
|
||||
@@ -329,6 +328,8 @@ Client::Client(uint32_t id, Worker *worker, size_t req_todo)
|
||||
reqidx(0),
|
||||
state(CLIENT_IDLE),
|
||||
req_todo(req_todo),
|
||||
req_left(req_todo),
|
||||
req_inflight(0),
|
||||
req_started(0),
|
||||
req_done(0),
|
||||
id(id),
|
||||
@@ -466,16 +467,13 @@ int Client::try_again_or_fail() {
|
||||
|
||||
if (new_connection_requested) {
|
||||
new_connection_requested = false;
|
||||
if (req_started < req_todo) {
|
||||
if (req_left) {
|
||||
// At the moment, we don't have a facility to re-start request
|
||||
// already in in-flight. Make them fail.
|
||||
auto req_abandoned = req_started - req_done;
|
||||
worker->stats.req_failed += req_inflight;
|
||||
worker->stats.req_error += req_inflight;
|
||||
|
||||
worker->stats.req_failed += req_abandoned;
|
||||
worker->stats.req_error += req_abandoned;
|
||||
worker->stats.req_done += req_abandoned;
|
||||
|
||||
req_done = req_started;
|
||||
req_inflight = 0;
|
||||
|
||||
// Keep using current address
|
||||
if (connect() == 0) {
|
||||
@@ -509,7 +507,7 @@ void Client::disconnect() {
|
||||
ev_io_stop(worker->loop, &wev);
|
||||
ev_io_stop(worker->loop, &rev);
|
||||
if (ssl) {
|
||||
SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);
|
||||
SSL_set_shutdown(ssl, SSL_get_shutdown(ssl) | SSL_RECEIVED_SHUTDOWN);
|
||||
ERR_clear_error();
|
||||
|
||||
if (SSL_shutdown(ssl) != 1) {
|
||||
@@ -527,16 +525,18 @@ void Client::disconnect() {
|
||||
}
|
||||
|
||||
int Client::submit_request() {
|
||||
++worker->stats.req_started;
|
||||
if (session->submit_request() != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
++worker->stats.req_started;
|
||||
--req_left;
|
||||
++req_started;
|
||||
++req_inflight;
|
||||
|
||||
// if an active timeout is set and this is the last request to be submitted
|
||||
// on this connection, start the active timeout.
|
||||
if (worker->config->conn_active_timeout > 0. && req_started >= req_todo) {
|
||||
if (worker->config->conn_active_timeout > 0. && req_left == 0) {
|
||||
ev_timer_start(worker->loop, &conn_active_watcher);
|
||||
}
|
||||
|
||||
@@ -544,40 +544,36 @@ int Client::submit_request() {
|
||||
}
|
||||
|
||||
void Client::process_timedout_streams() {
|
||||
for (auto &req_stat : worker->stats.req_stats) {
|
||||
for (auto &p : streams) {
|
||||
auto &req_stat = p.second.req_stat;
|
||||
if (!req_stat.completed) {
|
||||
req_stat.stream_close_time = std::chrono::steady_clock::now();
|
||||
}
|
||||
}
|
||||
|
||||
auto req_timed_out = req_todo - req_done;
|
||||
worker->stats.req_timedout += req_timed_out;
|
||||
worker->stats.req_timedout += req_inflight;
|
||||
|
||||
process_abandoned_streams();
|
||||
}
|
||||
|
||||
void Client::process_abandoned_streams() {
|
||||
auto req_abandoned = req_todo - req_done;
|
||||
auto req_abandoned = req_inflight + req_left;
|
||||
|
||||
worker->stats.req_failed += req_abandoned;
|
||||
worker->stats.req_error += req_abandoned;
|
||||
worker->stats.req_done += req_abandoned;
|
||||
|
||||
req_done = req_todo;
|
||||
req_inflight = 0;
|
||||
req_left = 0;
|
||||
}
|
||||
|
||||
void Client::process_request_failure() {
|
||||
auto req_abandoned = req_todo - req_started;
|
||||
worker->stats.req_failed += req_left;
|
||||
worker->stats.req_error += req_left;
|
||||
|
||||
worker->stats.req_failed += req_abandoned;
|
||||
worker->stats.req_error += req_abandoned;
|
||||
worker->stats.req_done += req_abandoned;
|
||||
req_left = 0;
|
||||
|
||||
req_done += req_abandoned;
|
||||
|
||||
if (req_done == req_todo) {
|
||||
if (req_inflight == 0) {
|
||||
terminate_session();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -595,7 +591,8 @@ void print_server_tmp_key(SSL *ssl) {
|
||||
|
||||
std::cout << "Server Temp Key: ";
|
||||
|
||||
switch (EVP_PKEY_id(key)) {
|
||||
auto pkey_id = EVP_PKEY_id(key);
|
||||
switch (pkey_id) {
|
||||
case EVP_PKEY_RSA:
|
||||
std::cout << "RSA " << EVP_PKEY_bits(key) << " bits" << std::endl;
|
||||
break;
|
||||
@@ -615,6 +612,10 @@ void print_server_tmp_key(SSL *ssl) {
|
||||
<< std::endl;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
std::cout << OBJ_nid2sn(pkey_id) << " " << EVP_PKEY_bits(key) << " bits"
|
||||
<< std::endl;
|
||||
break;
|
||||
}
|
||||
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||
}
|
||||
@@ -705,6 +706,9 @@ void Client::on_status_code(int32_t stream_id, uint16_t status) {
|
||||
}
|
||||
|
||||
void Client::on_stream_close(int32_t stream_id, bool success, bool final) {
|
||||
++req_done;
|
||||
--req_inflight;
|
||||
|
||||
auto req_stat = get_req_stat(stream_id);
|
||||
if (!req_stat) {
|
||||
return;
|
||||
@@ -735,23 +739,19 @@ void Client::on_stream_close(int32_t stream_id, bool success, bool final) {
|
||||
}
|
||||
|
||||
++worker->stats.req_done;
|
||||
++req_done;
|
||||
|
||||
worker->report_progress();
|
||||
streams.erase(stream_id);
|
||||
if (req_done == req_todo) {
|
||||
if (req_left == 0 && req_inflight == 0) {
|
||||
terminate_session();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!config.timing_script && !final) {
|
||||
if (req_started < req_todo) {
|
||||
if (submit_request() != 0) {
|
||||
if (!config.timing_script && !final && req_left > 0 &&
|
||||
submit_request() != 0) {
|
||||
process_request_failure();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RequestStat *Client::get_req_stat(int32_t stream_id) {
|
||||
@@ -865,8 +865,7 @@ int Client::connection_made() {
|
||||
record_connect_time();
|
||||
|
||||
if (!config.timing_script) {
|
||||
auto nreq =
|
||||
std::min(req_todo - req_started, session->max_concurrent_streams());
|
||||
auto nreq = std::min(req_left, session->max_concurrent_streams());
|
||||
for (; nreq > 0; --nreq) {
|
||||
if (submit_request() != 0) {
|
||||
process_request_failure();
|
||||
@@ -1697,6 +1696,8 @@ Options:
|
||||
--ciphers=<SUITE>
|
||||
Set allowed cipher list. The format of the string is
|
||||
described in OpenSSL ciphers(1).
|
||||
Default: )"
|
||||
<< config.ciphers << R"(
|
||||
-p, --no-tls-proto=<PROTOID>
|
||||
Specify ALPN identifier of the protocol to be used when
|
||||
accessing http URI without SSL/TLS.)";
|
||||
@@ -1831,7 +1832,7 @@ int main(int argc, char **argv) {
|
||||
bool nreqs_set_manually = false;
|
||||
while (1) {
|
||||
static int flag = 0;
|
||||
static option long_options[] = {
|
||||
constexpr static option long_options[] = {
|
||||
{"requests", required_argument, nullptr, 'n'},
|
||||
{"clients", required_argument, nullptr, 'c'},
|
||||
{"data", required_argument, nullptr, 'd'},
|
||||
@@ -2241,15 +2242,8 @@ int main(int argc, char **argv) {
|
||||
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
|
||||
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
|
||||
|
||||
const char *ciphers;
|
||||
if (config.ciphers.empty()) {
|
||||
ciphers = ssl::DEFAULT_CIPHER_LIST;
|
||||
} else {
|
||||
ciphers = config.ciphers.c_str();
|
||||
}
|
||||
|
||||
if (SSL_CTX_set_cipher_list(ssl_ctx, ciphers) == 0) {
|
||||
std::cerr << "SSL_CTX_set_cipher_list with " << ciphers
|
||||
if (SSL_CTX_set_cipher_list(ssl_ctx, config.ciphers.c_str()) == 0) {
|
||||
std::cerr << "SSL_CTX_set_cipher_list with " << config.ciphers
|
||||
<< " failed: " << ERR_error_string(ERR_get_error(), nullptr)
|
||||
<< std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
@@ -292,6 +292,11 @@ struct Client {
|
||||
ClientState state;
|
||||
// The number of requests this client has to issue.
|
||||
size_t req_todo;
|
||||
// The number of requests left to issue
|
||||
size_t req_left;
|
||||
// The number of requests currently have started, but not abandoned
|
||||
// or finished.
|
||||
size_t req_inflight;
|
||||
// The number of requests this client has issued so far.
|
||||
size_t req_started;
|
||||
// The number of requests this client has done so far.
|
||||
|
||||
@@ -100,7 +100,7 @@ int htp_msg_completecb(http_parser *htp) {
|
||||
http_parser_pause(htp, 1);
|
||||
// Connection is going down. If we have still request to do,
|
||||
// create new connection and keep on doing the job.
|
||||
if (client->req_started < client->req_todo) {
|
||||
if (client->req_left) {
|
||||
client->try_new_connection();
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ int htp_body_cb(http_parser *htp, const char *data, size_t len) {
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
http_parser_settings htp_hooks = {
|
||||
constexpr http_parser_settings htp_hooks = {
|
||||
htp_msg_begincb, // http_cb on_message_begin;
|
||||
nullptr, // http_data_cb on_url;
|
||||
htp_statuscb, // http_data_cb on_status;
|
||||
|
||||
@@ -113,7 +113,7 @@ ssize_t send_callback(spdylay_session *session, const uint8_t *data,
|
||||
auto &wb = client->wb;
|
||||
|
||||
if (wb.rleft() >= BACKOFF_WRITE_BUFFER_THRES) {
|
||||
return SPDYLAY_ERR_DEFERRED;
|
||||
return SPDYLAY_ERR_WOULDBLOCK;
|
||||
}
|
||||
|
||||
return wb.append(data, length);
|
||||
|
||||
132
src/http2.cc
132
src/http2.cc
@@ -30,107 +30,107 @@ namespace nghttp2 {
|
||||
|
||||
namespace http2 {
|
||||
|
||||
StringRef get_status_string(BlockAllocator &balloc, unsigned int status_code) {
|
||||
StringRef get_reason_phrase(unsigned int status_code) {
|
||||
switch (status_code) {
|
||||
case 100:
|
||||
return StringRef::from_lit("100 Continue");
|
||||
return StringRef::from_lit("Continue");
|
||||
case 101:
|
||||
return StringRef::from_lit("101 Switching Protocols");
|
||||
return StringRef::from_lit("Switching Protocols");
|
||||
case 200:
|
||||
return StringRef::from_lit("200 OK");
|
||||
return StringRef::from_lit("OK");
|
||||
case 201:
|
||||
return StringRef::from_lit("201 Created");
|
||||
return StringRef::from_lit("Created");
|
||||
case 202:
|
||||
return StringRef::from_lit("202 Accepted");
|
||||
return StringRef::from_lit("Accepted");
|
||||
case 203:
|
||||
return StringRef::from_lit("203 Non-Authoritative Information");
|
||||
return StringRef::from_lit("Non-Authoritative Information");
|
||||
case 204:
|
||||
return StringRef::from_lit("204 No Content");
|
||||
return StringRef::from_lit("No Content");
|
||||
case 205:
|
||||
return StringRef::from_lit("205 Reset Content");
|
||||
return StringRef::from_lit("Reset Content");
|
||||
case 206:
|
||||
return StringRef::from_lit("206 Partial Content");
|
||||
return StringRef::from_lit("Partial Content");
|
||||
case 300:
|
||||
return StringRef::from_lit("300 Multiple Choices");
|
||||
return StringRef::from_lit("Multiple Choices");
|
||||
case 301:
|
||||
return StringRef::from_lit("301 Moved Permanently");
|
||||
return StringRef::from_lit("Moved Permanently");
|
||||
case 302:
|
||||
return StringRef::from_lit("302 Found");
|
||||
return StringRef::from_lit("Found");
|
||||
case 303:
|
||||
return StringRef::from_lit("303 See Other");
|
||||
return StringRef::from_lit("See Other");
|
||||
case 304:
|
||||
return StringRef::from_lit("304 Not Modified");
|
||||
return StringRef::from_lit("Not Modified");
|
||||
case 305:
|
||||
return StringRef::from_lit("305 Use Proxy");
|
||||
// case 306: return StringRef::from_lit("306 (Unused)");
|
||||
return StringRef::from_lit("Use Proxy");
|
||||
// case 306: return StringRef::from_lit("(Unused)");
|
||||
case 307:
|
||||
return StringRef::from_lit("307 Temporary Redirect");
|
||||
return StringRef::from_lit("Temporary Redirect");
|
||||
case 308:
|
||||
return StringRef::from_lit("308 Permanent Redirect");
|
||||
return StringRef::from_lit("Permanent Redirect");
|
||||
case 400:
|
||||
return StringRef::from_lit("400 Bad Request");
|
||||
return StringRef::from_lit("Bad Request");
|
||||
case 401:
|
||||
return StringRef::from_lit("401 Unauthorized");
|
||||
return StringRef::from_lit("Unauthorized");
|
||||
case 402:
|
||||
return StringRef::from_lit("402 Payment Required");
|
||||
return StringRef::from_lit("Payment Required");
|
||||
case 403:
|
||||
return StringRef::from_lit("403 Forbidden");
|
||||
return StringRef::from_lit("Forbidden");
|
||||
case 404:
|
||||
return StringRef::from_lit("404 Not Found");
|
||||
return StringRef::from_lit("Not Found");
|
||||
case 405:
|
||||
return StringRef::from_lit("405 Method Not Allowed");
|
||||
return StringRef::from_lit("Method Not Allowed");
|
||||
case 406:
|
||||
return StringRef::from_lit("406 Not Acceptable");
|
||||
return StringRef::from_lit("Not Acceptable");
|
||||
case 407:
|
||||
return StringRef::from_lit("407 Proxy Authentication Required");
|
||||
return StringRef::from_lit("Proxy Authentication Required");
|
||||
case 408:
|
||||
return StringRef::from_lit("408 Request Timeout");
|
||||
return StringRef::from_lit("Request Timeout");
|
||||
case 409:
|
||||
return StringRef::from_lit("409 Conflict");
|
||||
return StringRef::from_lit("Conflict");
|
||||
case 410:
|
||||
return StringRef::from_lit("410 Gone");
|
||||
return StringRef::from_lit("Gone");
|
||||
case 411:
|
||||
return StringRef::from_lit("411 Length Required");
|
||||
return StringRef::from_lit("Length Required");
|
||||
case 412:
|
||||
return StringRef::from_lit("412 Precondition Failed");
|
||||
return StringRef::from_lit("Precondition Failed");
|
||||
case 413:
|
||||
return StringRef::from_lit("413 Payload Too Large");
|
||||
return StringRef::from_lit("Payload Too Large");
|
||||
case 414:
|
||||
return StringRef::from_lit("414 URI Too Long");
|
||||
return StringRef::from_lit("URI Too Long");
|
||||
case 415:
|
||||
return StringRef::from_lit("415 Unsupported Media Type");
|
||||
return StringRef::from_lit("Unsupported Media Type");
|
||||
case 416:
|
||||
return StringRef::from_lit("416 Requested Range Not Satisfiable");
|
||||
return StringRef::from_lit("Requested Range Not Satisfiable");
|
||||
case 417:
|
||||
return StringRef::from_lit("417 Expectation Failed");
|
||||
return StringRef::from_lit("Expectation Failed");
|
||||
case 421:
|
||||
return StringRef::from_lit("421 Misdirected Request");
|
||||
return StringRef::from_lit("Misdirected Request");
|
||||
case 426:
|
||||
return StringRef::from_lit("426 Upgrade Required");
|
||||
return StringRef::from_lit("Upgrade Required");
|
||||
case 428:
|
||||
return StringRef::from_lit("428 Precondition Required");
|
||||
return StringRef::from_lit("Precondition Required");
|
||||
case 429:
|
||||
return StringRef::from_lit("429 Too Many Requests");
|
||||
return StringRef::from_lit("Too Many Requests");
|
||||
case 431:
|
||||
return StringRef::from_lit("431 Request Header Fields Too Large");
|
||||
return StringRef::from_lit("Request Header Fields Too Large");
|
||||
case 451:
|
||||
return StringRef::from_lit("451 Unavailable For Legal Reasons");
|
||||
return StringRef::from_lit("Unavailable For Legal Reasons");
|
||||
case 500:
|
||||
return StringRef::from_lit("500 Internal Server Error");
|
||||
return StringRef::from_lit("Internal Server Error");
|
||||
case 501:
|
||||
return StringRef::from_lit("501 Not Implemented");
|
||||
return StringRef::from_lit("Not Implemented");
|
||||
case 502:
|
||||
return StringRef::from_lit("502 Bad Gateway");
|
||||
return StringRef::from_lit("Bad Gateway");
|
||||
case 503:
|
||||
return StringRef::from_lit("503 Service Unavailable");
|
||||
return StringRef::from_lit("Service Unavailable");
|
||||
case 504:
|
||||
return StringRef::from_lit("504 Gateway Timeout");
|
||||
return StringRef::from_lit("Gateway Timeout");
|
||||
case 505:
|
||||
return StringRef::from_lit("505 HTTP Version Not Supported");
|
||||
return StringRef::from_lit("HTTP Version Not Supported");
|
||||
case 511:
|
||||
return StringRef::from_lit("511 Network Authentication Required");
|
||||
return StringRef::from_lit("Network Authentication Required");
|
||||
default:
|
||||
return util::make_string_ref_uint(balloc, status_code);
|
||||
return StringRef{};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -477,6 +477,11 @@ void dump_nv(FILE *out, const HeaderRefs &nva) {
|
||||
fflush(out);
|
||||
}
|
||||
|
||||
void erase_header(HeaderRef *hd) {
|
||||
hd->name = StringRef{};
|
||||
hd->token = -1;
|
||||
}
|
||||
|
||||
StringRef rewrite_location_uri(BlockAllocator &balloc, const StringRef &uri,
|
||||
const http_parser_url &u,
|
||||
const StringRef &match_host,
|
||||
@@ -1665,6 +1670,31 @@ StringRef copy_lower(BlockAllocator &balloc, const StringRef &src) {
|
||||
return StringRef{iov.base, p};
|
||||
}
|
||||
|
||||
bool contains_trailers(const StringRef &s) {
|
||||
constexpr auto trailers = StringRef::from_lit("trailers");
|
||||
|
||||
for (auto p = std::begin(s), end = std::end(s);; ++p) {
|
||||
p = std::find_if(p, end, [](char c) { return c != ' ' && c != '\t'; });
|
||||
if (p == end || static_cast<size_t>(end - p) < trailers.size()) {
|
||||
return false;
|
||||
}
|
||||
if (util::strieq(trailers, StringRef{p, p + trailers.size()})) {
|
||||
// Make sure that there is no character other than white spaces
|
||||
// before next "," or end of string.
|
||||
p = std::find_if(p + trailers.size(), end,
|
||||
[](char c) { return c != ' ' && c != '\t'; });
|
||||
if (p == end || *p == ',') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Skip to next ",".
|
||||
p = std::find_if(p, end, [](char c) { return c == ','; });
|
||||
if (p == end) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace http2
|
||||
|
||||
} // namespace nghttp2
|
||||
|
||||
12
src/http2.h
12
src/http2.h
@@ -94,9 +94,9 @@ using HeaderRefs = std::vector<HeaderRef>;
|
||||
|
||||
namespace http2 {
|
||||
|
||||
// Returns string version of |status code| followed by reason
|
||||
// string. (e.g., "404 Not Found").
|
||||
StringRef get_status_string(BlockAllocator &balloc, unsigned int status_code);
|
||||
// Returns reason-phrase for given |status code|. If there is no
|
||||
// known reason-phrase for the given code, returns empty string.
|
||||
StringRef get_reason_phrase(unsigned int status_code);
|
||||
|
||||
// Returns string version of |status_code|. (e.g., "404")
|
||||
StringRef stringify_status(BlockAllocator &balloc, unsigned int status_code);
|
||||
@@ -227,6 +227,9 @@ void dump_nv(FILE *out, const Headers &nva);
|
||||
|
||||
void dump_nv(FILE *out, const HeaderRefs &nva);
|
||||
|
||||
// Ereases header in |hd|.
|
||||
void erase_header(HeaderRef *hd);
|
||||
|
||||
// Rewrites redirection URI which usually appears in location header
|
||||
// field. The |uri| is the URI in the location header field. The |u|
|
||||
// stores the result of parsed |uri|. The |request_authority| is the
|
||||
@@ -384,6 +387,9 @@ int construct_push_component(BlockAllocator &balloc, StringRef &scheme,
|
||||
// Copies |src| and return its lower-cased version.
|
||||
StringRef copy_lower(BlockAllocator &balloc, const StringRef &src);
|
||||
|
||||
// Returns true if te header field value |s| contains "trailers".
|
||||
bool contains_trailers(const StringRef &s);
|
||||
|
||||
} // namespace http2
|
||||
|
||||
} // namespace nghttp2
|
||||
|
||||
@@ -962,4 +962,20 @@ void test_http2_construct_push_component(void) {
|
||||
CU_ASSERT("/b/?q=a" == path);
|
||||
}
|
||||
|
||||
void test_http2_contains_trailers(void) {
|
||||
CU_ASSERT(!http2::contains_trailers(StringRef::from_lit("")));
|
||||
CU_ASSERT(http2::contains_trailers(StringRef::from_lit("trailers")));
|
||||
// Match must be case-insensitive.
|
||||
CU_ASSERT(http2::contains_trailers(StringRef::from_lit("TRAILERS")));
|
||||
CU_ASSERT(!http2::contains_trailers(StringRef::from_lit("trailer")));
|
||||
CU_ASSERT(!http2::contains_trailers(StringRef::from_lit("trailers 3")));
|
||||
CU_ASSERT(http2::contains_trailers(StringRef::from_lit("trailers,")));
|
||||
CU_ASSERT(http2::contains_trailers(StringRef::from_lit("trailers,foo")));
|
||||
CU_ASSERT(http2::contains_trailers(StringRef::from_lit("foo,trailers")));
|
||||
CU_ASSERT(http2::contains_trailers(StringRef::from_lit("foo,trailers,bar")));
|
||||
CU_ASSERT(
|
||||
http2::contains_trailers(StringRef::from_lit("foo, trailers ,bar")));
|
||||
CU_ASSERT(http2::contains_trailers(StringRef::from_lit(",trailers")));
|
||||
}
|
||||
|
||||
} // namespace shrpx
|
||||
|
||||
@@ -46,6 +46,7 @@ void test_http2_normalize_path(void);
|
||||
void test_http2_rewrite_clean_path(void);
|
||||
void test_http2_get_pure_path_component(void);
|
||||
void test_http2_construct_push_component(void);
|
||||
void test_http2_contains_trailers(void);
|
||||
|
||||
} // namespace shrpx
|
||||
|
||||
|
||||
@@ -132,7 +132,7 @@ class http2_impl;
|
||||
|
||||
class http2 {
|
||||
public:
|
||||
http2();
|
||||
http2(boost::asio::io_service &service);
|
||||
~http2();
|
||||
|
||||
http2(http2 &&other) noexcept;
|
||||
@@ -190,10 +190,6 @@ public:
|
||||
// equivalent .- and ..-free URL.
|
||||
bool handle(std::string pattern, request_cb cb);
|
||||
|
||||
// Sets number of native threads to handle incoming HTTP request.
|
||||
// It defaults to 1.
|
||||
void num_threads(size_t num_threads);
|
||||
|
||||
// Sets the maximum length to which the queue of pending
|
||||
// connections.
|
||||
void backlog(int backlog);
|
||||
@@ -207,13 +203,6 @@ public:
|
||||
// Gracefully stop http2 server
|
||||
void stop();
|
||||
|
||||
// Join on http2 server and wait for it to fully stop
|
||||
void join();
|
||||
|
||||
// Get access to the io_service objects.
|
||||
const std::vector<std::shared_ptr<boost::asio::io_service>> &
|
||||
io_services() const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<http2_impl> impl_;
|
||||
};
|
||||
|
||||
@@ -251,7 +251,7 @@ OPTIONS:
|
||||
;
|
||||
}
|
||||
|
||||
static struct option long_options[] = {
|
||||
constexpr static struct option long_options[] = {
|
||||
{"dump-header-table", no_argument, nullptr, 'd'}, {nullptr, 0, nullptr, 0}};
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
@@ -434,6 +434,102 @@ inline int limit_iovec(struct iovec *iov, int iovcnt, size_t max) {
|
||||
return iovcnt;
|
||||
}
|
||||
|
||||
// MemchunkBuffer is similar to Buffer, but it uses pooled Memchunk
|
||||
// for its underlying buffer.
|
||||
template <typename Memchunk> struct MemchunkBuffer {
|
||||
MemchunkBuffer(Pool<Memchunk> *pool) : pool(pool), chunk(nullptr) {}
|
||||
MemchunkBuffer(const MemchunkBuffer &) = delete;
|
||||
MemchunkBuffer(MemchunkBuffer &&other) noexcept
|
||||
: pool(other.pool), chunk(other.chunk) {
|
||||
other.chunk = nullptr;
|
||||
}
|
||||
MemchunkBuffer &operator=(const MemchunkBuffer &) = delete;
|
||||
MemchunkBuffer &operator=(MemchunkBuffer &&other) noexcept {
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
pool = other.pool;
|
||||
chunk = other.chunk;
|
||||
|
||||
other.chunk = nullptr;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
~MemchunkBuffer() {
|
||||
if (!pool || !chunk) {
|
||||
return;
|
||||
}
|
||||
pool->recycle(chunk);
|
||||
}
|
||||
|
||||
// Ensures that the underlying buffer is allocated.
|
||||
void ensure_chunk() {
|
||||
if (chunk) {
|
||||
return;
|
||||
}
|
||||
chunk = pool->get();
|
||||
}
|
||||
|
||||
// Releases the underlying buffer.
|
||||
void release_chunk() {
|
||||
if (!chunk) {
|
||||
return;
|
||||
}
|
||||
pool->recycle(chunk);
|
||||
chunk = nullptr;
|
||||
}
|
||||
|
||||
// Returns true if the underlying buffer is allocated.
|
||||
bool chunk_avail() const { return chunk != nullptr; }
|
||||
|
||||
// The functions below must be called after the underlying buffer is
|
||||
// allocated (use ensure_chunk).
|
||||
|
||||
// MemchunkBuffer provides the same interface functions with Buffer.
|
||||
// Since we has chunk as a member variable, pos and last are
|
||||
// implemented as wrapper functions.
|
||||
|
||||
uint8_t *pos() const { return chunk->pos; }
|
||||
uint8_t *last() const { return chunk->last; }
|
||||
|
||||
size_t rleft() const { return chunk->len(); }
|
||||
size_t wleft() const { return chunk->left(); }
|
||||
size_t write(const void *src, size_t count) {
|
||||
count = std::min(count, wleft());
|
||||
auto p = static_cast<const uint8_t *>(src);
|
||||
chunk->last = std::copy_n(p, count, chunk->last);
|
||||
return count;
|
||||
}
|
||||
size_t write(size_t count) {
|
||||
count = std::min(count, wleft());
|
||||
chunk->last += count;
|
||||
return count;
|
||||
}
|
||||
size_t drain(size_t count) {
|
||||
count = std::min(count, rleft());
|
||||
chunk->pos += count;
|
||||
return count;
|
||||
}
|
||||
size_t drain_reset(size_t count) {
|
||||
count = std::min(count, rleft());
|
||||
std::copy(chunk->pos + count, chunk->last, std::begin(chunk->buf));
|
||||
chunk->last = std::begin(chunk->buf) + (chunk->last - (chunk->pos + count));
|
||||
chunk->pos = std::begin(chunk->buf);
|
||||
return count;
|
||||
}
|
||||
void reset() { chunk->reset(); }
|
||||
uint8_t *begin() { return std::begin(chunk->buf); }
|
||||
uint8_t &operator[](size_t n) { return chunk->buf[n]; }
|
||||
const uint8_t &operator[](size_t n) const { return chunk->buf[n]; }
|
||||
|
||||
Pool<Memchunk> *pool;
|
||||
Memchunk *chunk;
|
||||
};
|
||||
|
||||
using DefaultMemchunkBuffer = MemchunkBuffer<Memchunk16K>;
|
||||
|
||||
} // namespace nghttp2
|
||||
|
||||
#endif // MEMCHUNK_H
|
||||
|
||||
163
src/nghttp.cc
163
src/nghttp.cc
@@ -105,6 +105,7 @@ Config::Config()
|
||||
window_bits(-1),
|
||||
connection_window_bits(-1),
|
||||
verbose(0),
|
||||
port_override(0),
|
||||
null_out(false),
|
||||
remote_name(false),
|
||||
get_assets(false),
|
||||
@@ -156,7 +157,6 @@ Request::Request(const std::string &uri, const http_parser_url &u,
|
||||
data_offset(0),
|
||||
response_len(0),
|
||||
inflater(nullptr),
|
||||
html_parser(nullptr),
|
||||
data_prd(data_prd),
|
||||
header_buffer_size(0),
|
||||
stream_id(-1),
|
||||
@@ -167,10 +167,7 @@ Request::Request(const std::string &uri, const http_parser_url &u,
|
||||
http2::init_hdidx(req_hdidx);
|
||||
}
|
||||
|
||||
Request::~Request() {
|
||||
nghttp2_gzip_inflate_del(inflater);
|
||||
delete html_parser;
|
||||
}
|
||||
Request::~Request() { nghttp2_gzip_inflate_del(inflater); }
|
||||
|
||||
void Request::init_inflater() {
|
||||
int rv;
|
||||
@@ -178,7 +175,57 @@ void Request::init_inflater() {
|
||||
assert(rv == 0);
|
||||
}
|
||||
|
||||
void Request::init_html_parser() { html_parser = new HtmlParser(uri); }
|
||||
StringRef Request::get_real_scheme() const {
|
||||
return config.scheme_override.empty()
|
||||
? util::get_uri_field(uri.c_str(), u, UF_SCHEMA)
|
||||
: StringRef{config.scheme_override};
|
||||
}
|
||||
|
||||
StringRef Request::get_real_host() const {
|
||||
return config.host_override.empty()
|
||||
? util::get_uri_field(uri.c_str(), u, UF_HOST)
|
||||
: StringRef{config.host_override};
|
||||
}
|
||||
|
||||
uint16_t Request::get_real_port() const {
|
||||
auto scheme = get_real_scheme();
|
||||
return config.host_override.empty()
|
||||
? util::has_uri_field(u, UF_PORT) ? u.port
|
||||
: scheme == "https" ? 443 : 80
|
||||
: config.port_override == 0 ? scheme == "https" ? 443 : 80
|
||||
: config.port_override;
|
||||
}
|
||||
|
||||
void Request::init_html_parser() {
|
||||
// We crawl HTML using overridden scheme, host, and port.
|
||||
auto scheme = get_real_scheme();
|
||||
auto host = get_real_host();
|
||||
auto port = get_real_port();
|
||||
auto ipv6_lit =
|
||||
std::find(std::begin(host), std::end(host), ':') != std::end(host);
|
||||
|
||||
auto base_uri = scheme.str();
|
||||
base_uri += "://";
|
||||
if (ipv6_lit) {
|
||||
base_uri += '[';
|
||||
}
|
||||
base_uri += host;
|
||||
if (ipv6_lit) {
|
||||
base_uri += ']';
|
||||
}
|
||||
if (!((scheme == "https" && port == 443) ||
|
||||
(scheme == "http" && port == 80))) {
|
||||
base_uri += ':';
|
||||
base_uri += util::utos(port);
|
||||
}
|
||||
base_uri += util::get_uri_field(uri.c_str(), u, UF_PATH);
|
||||
if (util::has_uri_field(u, UF_QUERY)) {
|
||||
base_uri += '?';
|
||||
base_uri += util::get_uri_field(uri.c_str(), u, UF_QUERY);
|
||||
}
|
||||
|
||||
html_parser = make_unique<HtmlParser>(base_uri);
|
||||
}
|
||||
|
||||
int Request::update_html_parser(const uint8_t *data, size_t len, int fin) {
|
||||
if (!html_parser) {
|
||||
@@ -371,7 +418,7 @@ int htp_msg_completecb(http_parser *htp) {
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
http_parser_settings htp_hooks = {
|
||||
constexpr http_parser_settings htp_hooks = {
|
||||
htp_msg_begincb, // http_cb on_message_begin;
|
||||
nullptr, // http_data_cb on_url;
|
||||
htp_statuscb, // http_data_cb on_status;
|
||||
@@ -625,20 +672,11 @@ int HttpClient::initiate_connection() {
|
||||
|
||||
// If the user overrode the :authority or host header, use that
|
||||
// value for the SNI extension
|
||||
const char *host_string = nullptr;
|
||||
auto i =
|
||||
std::find_if(std::begin(config.headers), std::end(config.headers),
|
||||
[](const Header &nv) {
|
||||
return ":authority" == nv.name || "host" == nv.name;
|
||||
});
|
||||
if (i != std::end(config.headers)) {
|
||||
host_string = (*i).value.c_str();
|
||||
} else {
|
||||
host_string = host.c_str();
|
||||
}
|
||||
const auto &host_string =
|
||||
config.host_override.empty() ? host : config.host_override;
|
||||
|
||||
if (!util::numeric_host(host_string)) {
|
||||
SSL_set_tlsext_host_name(ssl, host_string);
|
||||
if (!util::numeric_host(host_string.c_str())) {
|
||||
SSL_set_tlsext_host_name(ssl, host_string.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -701,7 +739,7 @@ void HttpClient::disconnect() {
|
||||
session = nullptr;
|
||||
|
||||
if (ssl) {
|
||||
SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);
|
||||
SSL_set_shutdown(ssl, SSL_get_shutdown(ssl) | SSL_RECEIVED_SHUTDOWN);
|
||||
ERR_clear_error();
|
||||
SSL_shutdown(ssl);
|
||||
SSL_free(ssl);
|
||||
@@ -1591,24 +1629,38 @@ void update_html_parser(HttpClient *client, Request *req, const uint8_t *data,
|
||||
}
|
||||
req->update_html_parser(data, len, fin);
|
||||
|
||||
auto scheme = req->get_real_scheme();
|
||||
auto host = req->get_real_host();
|
||||
auto port = req->get_real_port();
|
||||
|
||||
for (auto &p : req->html_parser->get_links()) {
|
||||
auto uri = strip_fragment(p.first.c_str());
|
||||
auto res_type = p.second;
|
||||
|
||||
http_parser_url u{};
|
||||
if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) == 0 &&
|
||||
util::fieldeq(uri.c_str(), u, req->uri.c_str(), req->u, UF_SCHEMA) &&
|
||||
util::fieldeq(uri.c_str(), u, req->uri.c_str(), req->u, UF_HOST) &&
|
||||
util::porteq(uri.c_str(), u, req->uri.c_str(), req->u)) {
|
||||
if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!util::fieldeq(uri.c_str(), u, UF_SCHEMA, scheme) ||
|
||||
!util::fieldeq(uri.c_str(), u, UF_HOST, host)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto link_port =
|
||||
util::has_uri_field(u, UF_PORT) ? u.port : scheme == "https" ? 443 : 80;
|
||||
|
||||
if (port != link_port) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// No POST data for assets
|
||||
auto pri_spec = resolve_dep(res_type);
|
||||
|
||||
if (client->add_request(uri, nullptr, 0, pri_spec, req->level + 1)) {
|
||||
|
||||
submit_request(client, config.headers, client->reqvec.back().get());
|
||||
}
|
||||
}
|
||||
}
|
||||
req->html_parser->clear_links();
|
||||
}
|
||||
} // namespace
|
||||
@@ -2168,24 +2220,6 @@ int client_select_next_proto_cb(SSL *ssl, unsigned char **out,
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
// Recommended general purpose "Intermediate compatibility" cipher by
|
||||
// mozilla.
|
||||
//
|
||||
// https://wiki.mozilla.org/Security/Server_Side_TLS
|
||||
const char *const CIPHER_LIST =
|
||||
"ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-"
|
||||
"AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:"
|
||||
"DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-"
|
||||
"AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-"
|
||||
"AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-"
|
||||
"AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:"
|
||||
"DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-"
|
||||
"SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-"
|
||||
"SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!"
|
||||
"aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA";
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int communicate(
|
||||
const std::string &scheme, const std::string &host, uint16_t port,
|
||||
@@ -2212,7 +2246,7 @@ int communicate(
|
||||
SSL_CTX_set_options(ssl_ctx, ssl_opts);
|
||||
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
|
||||
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
|
||||
if (SSL_CTX_set_cipher_list(ssl_ctx, CIPHER_LIST) == 0) {
|
||||
if (SSL_CTX_set_cipher_list(ssl_ctx, ssl::DEFAULT_CIPHER_LIST) == 0) {
|
||||
std::cerr << "[ERROR] " << ERR_error_string(ERR_get_error(), nullptr)
|
||||
<< std::endl;
|
||||
result = -1;
|
||||
@@ -2680,7 +2714,7 @@ int main(int argc, char **argv) {
|
||||
bool color = false;
|
||||
while (1) {
|
||||
static int flag = 0;
|
||||
static option long_options[] = {
|
||||
constexpr static option long_options[] = {
|
||||
{"verbose", no_argument, nullptr, 'v'},
|
||||
{"null-out", no_argument, nullptr, 'n'},
|
||||
{"remote-name", no_argument, nullptr, 'O'},
|
||||
@@ -2950,6 +2984,41 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
config.weight.insert(std::end(config.weight), argc - optind, weight_to_fill);
|
||||
|
||||
// Find scheme overridden by extra header fields.
|
||||
auto scheme_it =
|
||||
std::find_if(std::begin(config.headers), std::end(config.headers),
|
||||
[](const Header &nv) { return nv.name == ":scheme"; });
|
||||
if (scheme_it != std::end(config.headers)) {
|
||||
config.scheme_override = (*scheme_it).value;
|
||||
}
|
||||
|
||||
// Find host and port overridden by extra header fields.
|
||||
auto authority_it =
|
||||
std::find_if(std::begin(config.headers), std::end(config.headers),
|
||||
[](const Header &nv) { return nv.name == ":authority"; });
|
||||
if (authority_it == std::end(config.headers)) {
|
||||
authority_it =
|
||||
std::find_if(std::begin(config.headers), std::end(config.headers),
|
||||
[](const Header &nv) { return nv.name == "host"; });
|
||||
}
|
||||
|
||||
if (authority_it != std::end(config.headers)) {
|
||||
// authority_it may looks like "host:port".
|
||||
auto uri = "https://" + (*authority_it).value;
|
||||
http_parser_url u{};
|
||||
if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) {
|
||||
std::cerr << "[ERROR] Could not parse authority in "
|
||||
<< (*authority_it).name << ": " << (*authority_it).value
|
||||
<< std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
config.host_override = util::get_uri_field(uri.c_str(), u, UF_HOST).str();
|
||||
if (util::has_uri_field(u, UF_PORT)) {
|
||||
config.port_override = u.port;
|
||||
}
|
||||
}
|
||||
|
||||
set_color_output(color || isatty(fileno(stdout)));
|
||||
|
||||
nghttp2_option_set_peer_max_concurrent_streams(
|
||||
|
||||
14
src/nghttp.h
14
src/nghttp.h
@@ -69,6 +69,8 @@ struct Config {
|
||||
std::string keyfile;
|
||||
std::string datafile;
|
||||
std::string harfile;
|
||||
std::string scheme_override;
|
||||
std::string host_override;
|
||||
nghttp2_option *http2_option;
|
||||
int64_t header_table_size;
|
||||
int64_t min_header_table_size;
|
||||
@@ -82,6 +84,7 @@ struct Config {
|
||||
int window_bits;
|
||||
int connection_window_bits;
|
||||
int verbose;
|
||||
uint16_t port_override;
|
||||
bool null_out;
|
||||
bool remote_name;
|
||||
bool get_assets;
|
||||
@@ -151,6 +154,15 @@ struct Request {
|
||||
void record_response_start_time();
|
||||
void record_response_end_time();
|
||||
|
||||
// Returns scheme taking into account overridden scheme.
|
||||
StringRef get_real_scheme() const;
|
||||
// Returns request host, without port, taking into account
|
||||
// overridden host.
|
||||
StringRef get_real_host() const;
|
||||
// Returns request port, taking into account overridden host, port,
|
||||
// and scheme.
|
||||
uint16_t get_real_port() const;
|
||||
|
||||
Headers res_nva;
|
||||
Headers req_nva;
|
||||
std::string method;
|
||||
@@ -164,7 +176,7 @@ struct Request {
|
||||
// Number of bytes received from server
|
||||
int64_t response_len;
|
||||
nghttp2_gzip *inflater;
|
||||
HtmlParser *html_parser;
|
||||
std::unique_ptr<HtmlParser> html_parser;
|
||||
const nghttp2_data_provider *data_prd;
|
||||
size_t header_buffer_size;
|
||||
int32_t stream_id;
|
||||
|
||||
@@ -202,7 +202,7 @@ int main(int argc, char **argv) {
|
||||
|
||||
while (1) {
|
||||
static int flag = 0;
|
||||
static option long_options[] = {
|
||||
constexpr static option long_options[] = {
|
||||
{"address", required_argument, nullptr, 'a'},
|
||||
{"daemon", no_argument, nullptr, 'D'},
|
||||
{"htdocs", required_argument, nullptr, 'd'},
|
||||
|
||||
@@ -102,6 +102,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, "http2_contains_trailers",
|
||||
shrpx::test_http2_contains_trailers) ||
|
||||
!CU_add_test(pSuite, "downstream_field_store_append_last_header",
|
||||
shrpx::test_downstream_field_store_append_last_header) ||
|
||||
!CU_add_test(pSuite, "downstream_field_store_header",
|
||||
@@ -179,6 +181,9 @@ int main(int argc, char *argv[]) {
|
||||
!CU_add_test(pSuite, "util_random_alpha_digit",
|
||||
shrpx::test_util_random_alpha_digit) ||
|
||||
!CU_add_test(pSuite, "util_format_hex", shrpx::test_util_format_hex) ||
|
||||
!CU_add_test(pSuite, "util_is_hex_string",
|
||||
shrpx::test_util_is_hex_string) ||
|
||||
!CU_add_test(pSuite, "util_decode_hex", shrpx::test_util_decode_hex) ||
|
||||
!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) ||
|
||||
|
||||
206
src/shrpx.cc
206
src/shrpx.cc
@@ -411,7 +411,7 @@ void exec_binary() {
|
||||
LOG(ERROR) << "Unblocking all signals failed: "
|
||||
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
||||
|
||||
_Exit(EXIT_FAILURE);
|
||||
nghttp2_Exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
auto exec_path =
|
||||
@@ -419,7 +419,7 @@ void exec_binary() {
|
||||
|
||||
if (!exec_path) {
|
||||
LOG(ERROR) << "Could not resolve the executable path";
|
||||
_Exit(EXIT_FAILURE);
|
||||
nghttp2_Exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
auto argv = make_unique<char *[]>(suconfig.argc + 1);
|
||||
@@ -487,12 +487,12 @@ void exec_binary() {
|
||||
}
|
||||
|
||||
// restores original stderr
|
||||
util::restore_original_fds();
|
||||
restore_original_fds();
|
||||
|
||||
if (execve(argv[0], argv.get(), envp.get()) == -1) {
|
||||
auto error = errno;
|
||||
LOG(ERROR) << "execve failed: errno=" << error;
|
||||
_Exit(EXIT_FAILURE);
|
||||
nghttp2_Exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
@@ -1170,7 +1170,7 @@ pid_t fork_worker_process(int &main_ipc_fd,
|
||||
LOG(FATAL) << "Unblocking all signals failed: "
|
||||
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
||||
|
||||
_Exit(EXIT_FAILURE);
|
||||
nghttp2_Exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
close(ipc_fd[1]);
|
||||
@@ -1179,13 +1179,13 @@ pid_t fork_worker_process(int &main_ipc_fd,
|
||||
if (rv != 0) {
|
||||
LOG(FATAL) << "Worker process returned error";
|
||||
|
||||
_Exit(EXIT_FAILURE);
|
||||
nghttp2_Exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
LOG(NOTICE) << "Worker process shutting down momentarily";
|
||||
|
||||
// call exit(...) instead of _Exit to get leak sanitizer report
|
||||
_Exit(EXIT_SUCCESS);
|
||||
// call exit(...) instead of nghttp2_Exit to get leak sanitizer report
|
||||
nghttp2_Exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
// parent process
|
||||
@@ -1354,6 +1354,9 @@ void fill_default_config(Config *config) {
|
||||
}
|
||||
|
||||
tlsconf.session_timeout = std::chrono::hours(12);
|
||||
tlsconf.ciphers = StringRef::from_lit(nghttp2::ssl::DEFAULT_CIPHER_LIST);
|
||||
tlsconf.client.ciphers =
|
||||
StringRef::from_lit(nghttp2::ssl::DEFAULT_CIPHER_LIST);
|
||||
#if OPENSSL_1_1_API
|
||||
tlsconf.ecdh_curves = StringRef::from_lit("X25519:P-256:P-384:P-521");
|
||||
#else // !OPENSSL_1_1_API
|
||||
@@ -1444,7 +1447,7 @@ void fill_default_config(Config *config) {
|
||||
auto &listenerconf = connconf.listener;
|
||||
{
|
||||
// Default accept() backlog
|
||||
listenerconf.backlog = 512;
|
||||
listenerconf.backlog = 65536;
|
||||
listenerconf.timeout.sleep = 30_s;
|
||||
}
|
||||
}
|
||||
@@ -1461,6 +1464,9 @@ void fill_default_config(Config *config) {
|
||||
|
||||
// Write timeout for HTTP2/non-HTTP2 upstream connection
|
||||
timeoutconf.write = 30_s;
|
||||
|
||||
// Keep alive timeout for HTTP/1 upstream connection
|
||||
timeoutconf.idle_read = 1_min;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1486,6 +1492,14 @@ void fill_default_config(Config *config) {
|
||||
|
||||
auto &apiconf = config->api;
|
||||
apiconf.max_request_body = 16_k;
|
||||
|
||||
auto &dnsconf = config->dns;
|
||||
{
|
||||
auto &timeoutconf = dnsconf.timeout;
|
||||
timeoutconf.cache = 10_s;
|
||||
timeoutconf.lookup = 5_s;
|
||||
}
|
||||
dnsconf.max_try = 2;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -1587,13 +1601,13 @@ Connections:
|
||||
Several parameters <PARAM> are accepted after <PATTERN>.
|
||||
The parameters are delimited by ";". The available
|
||||
parameters are: "proto=<PROTO>", "tls",
|
||||
"sni=<SNI_HOST>", "fall=<N>", "rise=<N>", and
|
||||
"affinity=<METHOD>". The parameter consists of keyword,
|
||||
and optionally followed by "=" and value. For example,
|
||||
the parameter "proto=h2" consists of the keyword "proto"
|
||||
and value "h2". The parameter "tls" consists of the
|
||||
keyword "tls" without value. Each parameter is
|
||||
described as follows.
|
||||
"sni=<SNI_HOST>", "fall=<N>", "rise=<N>",
|
||||
"affinity=<METHOD>", "dns", and "frontend-tls". The
|
||||
parameter consists of keyword, and optionally followed
|
||||
by "=" and value. For example, the parameter "proto=h2"
|
||||
consists of the keyword "proto" and value "h2". The
|
||||
parameter "tls" consists of the keyword "tls" without
|
||||
value. Each parameter is described as follows.
|
||||
|
||||
The backend application protocol can be specified using
|
||||
optional "proto" parameter, and in the form of
|
||||
@@ -1642,6 +1656,25 @@ Connections:
|
||||
break if one of the backend gets unreachable, or backend
|
||||
settings are reloaded or replaced by API.
|
||||
|
||||
By default, name resolution of backend host name is done
|
||||
at start up, or reloading configuration. If "dns"
|
||||
parameter is given, name resolution takes place
|
||||
dynamically. This is useful if backend address changes
|
||||
frequently. If "dns" is given, name resolution of
|
||||
backend host name at start up, or reloading
|
||||
configuration is skipped.
|
||||
|
||||
If "frontend-tls" parameter is used, the matched backend
|
||||
requires frontend TLS connection. In other words, even
|
||||
if pattern is matched, frontend connection is not TLS
|
||||
protected, the request is forwarded to one of catch-all
|
||||
backends. For this reason, catch-all backend cannot
|
||||
have "frontend-tls" parameter. If at least one backend
|
||||
has "frontend-tls" parameter, this feature is enabled
|
||||
for all backend servers sharing the same <PATTERN>. It
|
||||
is advised to set "frontend-tls" parameter to all
|
||||
backends explicitly if this feature is desired.
|
||||
|
||||
Since ";" and ":" are used as delimiter, <PATTERN> must
|
||||
not contain these characters. Since ";" has special
|
||||
meaning in shell, the option value must be quoted.
|
||||
@@ -1675,6 +1708,10 @@ Connections:
|
||||
default. Any requests which come through this address
|
||||
are replied with 200 HTTP status, without no body.
|
||||
|
||||
To accept PROXY protocol version 1 on frontend
|
||||
connection, specify "proxyproto" parameter. This is
|
||||
disabled by default.
|
||||
|
||||
Default: *,3000
|
||||
--backlog=<N>
|
||||
Set listen backlog size.
|
||||
@@ -1699,8 +1736,6 @@ Connections:
|
||||
timeouts when connecting and making CONNECT request can
|
||||
be specified by --backend-read-timeout and
|
||||
--backend-write-timeout options.
|
||||
--accept-proxy-protocol
|
||||
Accept PROXY protocol version 1 on frontend connection.
|
||||
|
||||
Performance:
|
||||
-n, --workers=<N>
|
||||
@@ -1812,6 +1847,11 @@ Timeout:
|
||||
Specify write timeout for all frontend connections.
|
||||
Default: )"
|
||||
<< util::duration_str(config->conn.upstream.timeout.write) << R"(
|
||||
--frontend-keep-alive-timeout=<DURATION>
|
||||
Specify keep-alive timeout for frontend HTTP/1
|
||||
connection.
|
||||
Default: )"
|
||||
<< util::duration_str(config->conn.upstream.timeout.idle_read) << R"(
|
||||
--stream-read-timeout=<DURATION>
|
||||
Specify read timeout for HTTP/2 and SPDY streams. 0
|
||||
means no timeout.
|
||||
@@ -1836,7 +1876,8 @@ Timeout:
|
||||
Default: )"
|
||||
<< util::duration_str(config->conn.downstream->timeout.connect) << R"(
|
||||
--backend-keep-alive-timeout=<DURATION>
|
||||
Specify keep-alive timeout for backend connection.
|
||||
Specify keep-alive timeout for backend HTTP/1
|
||||
connection.
|
||||
Default: )"
|
||||
<< util::duration_str(config->conn.downstream->timeout.idle_read) << R"(
|
||||
--listener-disable-timeout=<DURATION>
|
||||
@@ -1869,8 +1910,15 @@ Timeout:
|
||||
|
||||
SSL/TLS:
|
||||
--ciphers=<SUITE>
|
||||
Set allowed cipher list. The format of the string is
|
||||
described in OpenSSL ciphers(1).
|
||||
Set allowed cipher list for frontend connection. The
|
||||
format of the string is described in OpenSSL ciphers(1).
|
||||
Default: )"
|
||||
<< config->tls.ciphers << R"(
|
||||
--client-ciphers=<SUITE>
|
||||
Set allowed cipher list for backend connection. The
|
||||
format of the string is described in OpenSSL ciphers(1).
|
||||
Default: )"
|
||||
<< config->tls.client.ciphers << R"(
|
||||
--ecdh-curves=<LIST>
|
||||
Set supported curve list for frontend connections.
|
||||
<LIST> is a colon separated list of curve NID or names
|
||||
@@ -2064,9 +2112,15 @@ SSL/TLS:
|
||||
Default: )"
|
||||
<< util::duration_str(config->tls.dyn_rec.idle_timeout) << R"(
|
||||
--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.
|
||||
Allow black listed cipher suite on frontend HTTP/2
|
||||
connection. See
|
||||
https://tools.ietf.org/html/rfc7540#appendix-A for the
|
||||
complete HTTP/2 cipher suites black list.
|
||||
--client-no-http2-cipher-black-list
|
||||
Allow black listed cipher suite on backend HTTP/2
|
||||
connection. See
|
||||
https://tools.ietf.org/html/rfc7540#appendix-A for the
|
||||
complete HTTP/2 cipher suites black list.
|
||||
--tls-sct-dir=<DIR>
|
||||
Specifies the directory where *.sct files exist. All
|
||||
*.sct files in <DIR> are read, and sent as
|
||||
@@ -2076,6 +2130,33 @@ SSL/TLS:
|
||||
argument <CERT>, or certificate option in configuration
|
||||
file. For additional certificates, use --subcert
|
||||
option. This option requires OpenSSL >= 1.0.2.
|
||||
--psk-secrets=<PATH>
|
||||
Read list of PSK identity and secrets from <PATH>. This
|
||||
is used for frontend connection. The each line of input
|
||||
file is formatted as <identity>:<hex-secret>, where
|
||||
<identity> is PSK identity, and <hex-secret> is secret
|
||||
in hex. An empty line, and line which starts with '#'
|
||||
are skipped. The default enabled cipher list might not
|
||||
contain any PSK cipher suite. In that case, desired PSK
|
||||
cipher suites must be enabled using --ciphers option.
|
||||
The desired PSK cipher suite may be black listed by
|
||||
HTTP/2. To use those cipher suites with HTTP/2,
|
||||
consider to use --no-http2-cipher-black-list option.
|
||||
But be aware its implications.
|
||||
--client-psk-secrets=<PATH>
|
||||
Read PSK identity and secrets from <PATH>. This is used
|
||||
for backend connection. The each line of input file is
|
||||
formatted as <identity>:<hex-secret>, where <identity>
|
||||
is PSK identity, and <hex-secret> is secret in hex. An
|
||||
empty line, and line which starts with '#' are skipped.
|
||||
The first identity and secret pair encountered is used.
|
||||
The default enabled cipher list might not contain any
|
||||
PSK cipher suite. In that case, desired PSK cipher
|
||||
suites must be enabled using --client-ciphers option.
|
||||
The desired PSK cipher suite may be black listed by
|
||||
HTTP/2. To use those cipher suites with HTTP/2,
|
||||
consider to use --client-no-http2-cipher-black-list
|
||||
option. But be aware its implications.
|
||||
|
||||
HTTP/2 and SPDY:
|
||||
-c, --frontend-http2-max-concurrent-streams=<N>
|
||||
@@ -2237,6 +2318,10 @@ Logging:
|
||||
|
||||
Default: )"
|
||||
<< DEFAULT_ACCESSLOG_FORMAT << R"(
|
||||
--accesslog-write-early
|
||||
Write access log when response header fields are
|
||||
received from backend rather than when request
|
||||
transaction finishes.
|
||||
--errorlog-file=<PATH>
|
||||
Set path to write error log. To reopen file, send USR1
|
||||
signal to nghttpx. stderr will be redirected to the
|
||||
@@ -2368,6 +2453,25 @@ API:
|
||||
Default: )"
|
||||
<< util::utos_unit(config->api.max_request_body) << R"(
|
||||
|
||||
DNS:
|
||||
--dns-cache-timeout=<DURATION>
|
||||
Set duration that cached DNS results remain valid. Note
|
||||
that nghttpx caches the unsuccessful results as well.
|
||||
Default: )"
|
||||
<< util::duration_str(config->dns.timeout.cache) << R"(
|
||||
--dns-lookup-timeout=<DURATION>
|
||||
Set timeout that DNS server is given to respond to the
|
||||
initial DNS query. For the 2nd and later queries,
|
||||
server is given time based on this timeout, and it is
|
||||
scaled linearly.
|
||||
Default: )"
|
||||
<< util::duration_str(config->dns.timeout.lookup) << R"(
|
||||
--dns-max-try=<N>
|
||||
Set the number of DNS query before nghttpx gives up name
|
||||
lookup.
|
||||
Default: )"
|
||||
<< config->dns.max_try << R"(
|
||||
|
||||
Debug:
|
||||
--frontend-http2-dump-request-header=<PATH>
|
||||
Dumps request headers received by HTTP/2 frontend to the
|
||||
@@ -2789,7 +2893,7 @@ int main(int argc, char **argv) {
|
||||
fill_default_config(mod_config());
|
||||
|
||||
// make copy of stderr
|
||||
util::store_original_fds();
|
||||
store_original_fds();
|
||||
|
||||
// First open log files with default configuration, so that we can
|
||||
// log errors/warnings while reading configuration files.
|
||||
@@ -2822,7 +2926,7 @@ int main(int argc, char **argv) {
|
||||
|
||||
while (1) {
|
||||
static int flag = 0;
|
||||
static option long_options[] = {
|
||||
static constexpr option long_options[] = {
|
||||
{SHRPX_OPT_DAEMON.c_str(), no_argument, nullptr, 'D'},
|
||||
{SHRPX_OPT_LOG_LEVEL.c_str(), required_argument, nullptr, 'L'},
|
||||
{SHRPX_OPT_BACKEND.c_str(), required_argument, nullptr, 'b'},
|
||||
@@ -3027,6 +3131,17 @@ int main(int argc, char **argv) {
|
||||
{SHRPX_OPT_TLS_SCT_DIR.c_str(), required_argument, &flag, 141},
|
||||
{SHRPX_OPT_BACKEND_CONNECT_TIMEOUT.c_str(), required_argument, &flag,
|
||||
142},
|
||||
{SHRPX_OPT_DNS_CACHE_TIMEOUT.c_str(), required_argument, &flag, 143},
|
||||
{SHRPX_OPT_DNS_LOOKUP_TIMEOUT.c_str(), required_argument, &flag, 144},
|
||||
{SHRPX_OPT_DNS_MAX_TRY.c_str(), required_argument, &flag, 145},
|
||||
{SHRPX_OPT_FRONTEND_KEEP_ALIVE_TIMEOUT.c_str(), required_argument,
|
||||
&flag, 146},
|
||||
{SHRPX_OPT_PSK_SECRETS.c_str(), required_argument, &flag, 147},
|
||||
{SHRPX_OPT_CLIENT_PSK_SECRETS.c_str(), required_argument, &flag, 148},
|
||||
{SHRPX_OPT_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST.c_str(), no_argument,
|
||||
&flag, 149},
|
||||
{SHRPX_OPT_CLIENT_CIPHERS.c_str(), required_argument, &flag, 150},
|
||||
{SHRPX_OPT_ACCESSLOG_WRITE_EARLY.c_str(), no_argument, &flag, 151},
|
||||
{nullptr, 0, nullptr, 0}};
|
||||
|
||||
int option_index = 0;
|
||||
@@ -3700,6 +3815,45 @@ int main(int argc, char **argv) {
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_CONNECT_TIMEOUT,
|
||||
StringRef{optarg});
|
||||
break;
|
||||
case 143:
|
||||
// --dns-cache-timeout
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_DNS_CACHE_TIMEOUT, StringRef{optarg});
|
||||
break;
|
||||
case 144:
|
||||
// --dns-lookup-timeou
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_DNS_LOOKUP_TIMEOUT, StringRef{optarg});
|
||||
break;
|
||||
case 145:
|
||||
// --dns-max-try
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_DNS_MAX_TRY, StringRef{optarg});
|
||||
break;
|
||||
case 146:
|
||||
// --frontend-keep-alive-timeout
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_KEEP_ALIVE_TIMEOUT,
|
||||
StringRef{optarg});
|
||||
break;
|
||||
case 147:
|
||||
// --psk-secrets
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_PSK_SECRETS, StringRef{optarg});
|
||||
break;
|
||||
case 148:
|
||||
// --client-psk-secrets
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_PSK_SECRETS, StringRef{optarg});
|
||||
break;
|
||||
case 149:
|
||||
// --client-no-http2-cipher-black-list
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST,
|
||||
StringRef::from_lit("yes"));
|
||||
break;
|
||||
case 150:
|
||||
// --client-ciphers
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_CIPHERS, StringRef{optarg});
|
||||
break;
|
||||
case 151:
|
||||
// --accesslog-write-early
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_ACCESSLOG_WRITE_EARLY,
|
||||
StringRef::from_lit("yes"));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
12
src/shrpx.h
12
src/shrpx.h
@@ -36,13 +36,15 @@
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#ifndef HAVE__EXIT
|
||||
#define nghttp2_Exit(status) _exit(status)
|
||||
#else // HAVE__EXIT
|
||||
#define nghttp2_Exit(status) _Exit(status)
|
||||
#endif // HAVE__EXIT
|
||||
|
||||
#include "shrpx_log.h"
|
||||
|
||||
#ifndef HAVE__EXIT
|
||||
#define _Exit(status) _exit(status)
|
||||
#endif // !HAVE__EXIT
|
||||
|
||||
#define DIE() _Exit(EXIT_FAILURE)
|
||||
#define DIE() nghttp2_Exit(EXIT_FAILURE)
|
||||
|
||||
#if defined(HAVE_DECL_INITGROUPS) && !HAVE_DECL_INITGROUPS
|
||||
inline int initgroups(const char *user, gid_t group) { return 0; }
|
||||
|
||||
@@ -117,6 +117,7 @@ void writecb(struct ev_loop *loop, ev_io *w, int revents) {
|
||||
int ClientHandler::noop() { return 0; }
|
||||
|
||||
int ClientHandler::read_clear() {
|
||||
rb_.ensure_chunk();
|
||||
for (;;) {
|
||||
if (rb_.rleft() && on_read() != 0) {
|
||||
return -1;
|
||||
@@ -132,9 +133,12 @@ int ClientHandler::read_clear() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto nread = conn_.read_clear(rb_.last, rb_.wleft());
|
||||
auto nread = conn_.read_clear(rb_.last(), rb_.wleft());
|
||||
|
||||
if (nread == 0) {
|
||||
if (rb_.rleft() == 0) {
|
||||
rb_.release_chunk();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -209,6 +213,8 @@ int ClientHandler::tls_handshake() {
|
||||
int ClientHandler::read_tls() {
|
||||
ERR_clear_error();
|
||||
|
||||
rb_.ensure_chunk();
|
||||
|
||||
for (;;) {
|
||||
// we should process buffered data first before we read EOF.
|
||||
if (rb_.rleft() && on_read() != 0) {
|
||||
@@ -225,9 +231,12 @@ int ClientHandler::read_tls() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto nread = conn_.read_tls(rb_.last, rb_.wleft());
|
||||
auto nread = conn_.read_tls(rb_.last(), rb_.wleft());
|
||||
|
||||
if (nread == 0) {
|
||||
if (rb_.rleft() == 0) {
|
||||
rb_.release_chunk();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -303,7 +312,7 @@ int ClientHandler::upstream_write() {
|
||||
int ClientHandler::upstream_http2_connhd_read() {
|
||||
auto nread = std::min(left_connhd_len_, rb_.rleft());
|
||||
if (memcmp(NGHTTP2_CLIENT_MAGIC + NGHTTP2_CLIENT_MAGIC_LEN - left_connhd_len_,
|
||||
rb_.pos, nread) != 0) {
|
||||
rb_.pos(), nread) != 0) {
|
||||
// There is no downgrade path here. Just drop the connection.
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this) << "invalid client connection header";
|
||||
@@ -332,7 +341,7 @@ int ClientHandler::upstream_http2_connhd_read() {
|
||||
int ClientHandler::upstream_http1_connhd_read() {
|
||||
auto nread = std::min(left_connhd_len_, rb_.rleft());
|
||||
if (memcmp(NGHTTP2_CLIENT_MAGIC + NGHTTP2_CLIENT_MAGIC_LEN - left_connhd_len_,
|
||||
rb_.pos, nread) != 0) {
|
||||
rb_.pos(), nread) != 0) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this) << "This is HTTP/1.1 connection, "
|
||||
<< "but may be upgraded to HTTP/2 later.";
|
||||
@@ -386,6 +395,7 @@ ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl,
|
||||
// so the required space is 64 + 48 + 16 + 48 + 16 + 16 + 16 +
|
||||
// 32 + 8 + 8 * 8 = 328.
|
||||
balloc_(512, 512),
|
||||
rb_(worker->get_mcpool()),
|
||||
conn_(worker->get_loop(), fd, ssl, worker->get_mcpool(),
|
||||
get_config()->conn.upstream.timeout.write,
|
||||
get_config()->conn.upstream.timeout.read,
|
||||
@@ -413,7 +423,8 @@ ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl,
|
||||
|
||||
auto config = get_config();
|
||||
|
||||
if (config->conn.upstream.accept_proxy_protocol) {
|
||||
if (faddr_->accept_proxy_protocol ||
|
||||
config->conn.upstream.accept_proxy_protocol) {
|
||||
read_ = &ClientHandler::read_clear;
|
||||
write_ = &ClientHandler::noop;
|
||||
on_read_ = &ClientHandler::proxy_protocol_read;
|
||||
@@ -646,10 +657,12 @@ int ClientHandler::do_read() { return read_(*this); }
|
||||
int ClientHandler::do_write() { return write_(*this); }
|
||||
|
||||
int ClientHandler::on_read() {
|
||||
if (rb_.chunk_avail()) {
|
||||
auto rv = on_read_(*this);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
conn_.handle_tls_pending_read();
|
||||
return 0;
|
||||
}
|
||||
@@ -995,6 +1008,13 @@ ClientHandler::get_downstream_connection(Downstream *downstream) {
|
||||
CLOG(INFO, this) << "Downstream address group_idx: " << group_idx;
|
||||
}
|
||||
|
||||
if (groups[group_idx]->shared_addr->require_upstream_tls && !conn_.tls.ssl) {
|
||||
CLOG(INFO, this) << "Downstream address group " << group_idx
|
||||
<< " requires frontend TLS connection. Send request to "
|
||||
"catch-all group.";
|
||||
group_idx = catch_all;
|
||||
}
|
||||
|
||||
auto &group = groups[group_idx];
|
||||
auto &shared_addr = group->shared_addr;
|
||||
|
||||
@@ -1177,100 +1197,25 @@ void ClientHandler::start_immediate_shutdown() {
|
||||
ev_timer_start(conn_.loop, &reneg_shutdown_timer_);
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Construct absolute request URI from |Request|, mainly to log
|
||||
// request URI for proxy request (HTTP/2 proxy or client proxy). This
|
||||
// is mostly same routine found in
|
||||
// HttpDownstreamConnection::push_request_headers(), but vastly
|
||||
// simplified since we only care about absolute URI.
|
||||
StringRef construct_absolute_request_uri(BlockAllocator &balloc,
|
||||
const Request &req) {
|
||||
if (req.authority.empty()) {
|
||||
return req.path;
|
||||
}
|
||||
|
||||
auto len = req.authority.size() + req.path.size();
|
||||
if (req.scheme.empty()) {
|
||||
len += str_size("http://");
|
||||
} else {
|
||||
len += req.scheme.size() + str_size("://");
|
||||
}
|
||||
|
||||
auto iov = make_byte_ref(balloc, len + 1);
|
||||
auto p = iov.base;
|
||||
|
||||
if (req.scheme.empty()) {
|
||||
// We may have to log the request which lacks scheme (e.g.,
|
||||
// http/1.1 with origin form).
|
||||
p = util::copy_lit(p, "http://");
|
||||
} else {
|
||||
p = std::copy(std::begin(req.scheme), std::end(req.scheme), p);
|
||||
p = util::copy_lit(p, "://");
|
||||
}
|
||||
p = std::copy(std::begin(req.authority), std::end(req.authority), p);
|
||||
p = std::copy(std::begin(req.path), std::end(req.path), p);
|
||||
*p = '\0';
|
||||
|
||||
return StringRef{iov.base, p};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void ClientHandler::write_accesslog(Downstream *downstream) {
|
||||
nghttp2::ssl::TLSSessionInfo tls_info;
|
||||
const auto &req = downstream->request();
|
||||
const auto &resp = downstream->response();
|
||||
auto &req = downstream->request();
|
||||
|
||||
auto &balloc = downstream->get_block_allocator();
|
||||
auto config = get_config();
|
||||
|
||||
if (!req.tstamp) {
|
||||
auto lgconf = log_config();
|
||||
lgconf->update_tstamp(std::chrono::system_clock::now());
|
||||
req.tstamp = lgconf->tstamp;
|
||||
}
|
||||
|
||||
upstream_accesslog(
|
||||
config->logging.access.format,
|
||||
LogSpec{
|
||||
downstream, downstream->get_addr(), ipaddr_,
|
||||
http2::to_method_string(req.method),
|
||||
|
||||
req.method == HTTP_CONNECT
|
||||
? StringRef(req.authority)
|
||||
: config->http2_proxy
|
||||
? StringRef(construct_absolute_request_uri(balloc, req))
|
||||
: req.path.empty()
|
||||
? req.method == HTTP_OPTIONS
|
||||
? StringRef::from_lit("*")
|
||||
: StringRef::from_lit("-")
|
||||
: StringRef(req.path),
|
||||
|
||||
alpn_, nghttp2::ssl::get_tls_session_info(&tls_info, conn_.tls.ssl),
|
||||
|
||||
std::chrono::system_clock::now(), // time_now
|
||||
downstream->get_request_start_time(), // request_start_time
|
||||
downstream, ipaddr_, alpn_,
|
||||
nghttp2::ssl::get_tls_session_info(&tls_info, conn_.tls.ssl),
|
||||
std::chrono::high_resolution_clock::now(), // request_end_time
|
||||
|
||||
req.http_major, req.http_minor, resp.http_status,
|
||||
downstream->response_sent_body_length, port_, faddr_->port,
|
||||
config->pid,
|
||||
});
|
||||
}
|
||||
|
||||
void ClientHandler::write_accesslog(int major, int minor, unsigned int status,
|
||||
int64_t body_bytes_sent) {
|
||||
auto time_now = std::chrono::system_clock::now();
|
||||
auto highres_now = std::chrono::high_resolution_clock::now();
|
||||
nghttp2::ssl::TLSSessionInfo tls_info;
|
||||
auto config = get_config();
|
||||
|
||||
upstream_accesslog(
|
||||
config->logging.access.format,
|
||||
LogSpec{
|
||||
nullptr, nullptr, ipaddr_,
|
||||
StringRef::from_lit("-"), // method
|
||||
StringRef::from_lit("-"), // path,
|
||||
alpn_, nghttp2::ssl::get_tls_session_info(&tls_info, conn_.tls.ssl),
|
||||
time_now,
|
||||
highres_now, // request_start_time TODO is
|
||||
// there a better value?
|
||||
highres_now, // request_end_time
|
||||
major, minor, // major, minor
|
||||
status, body_bytes_sent, port_, faddr_->port, config->pid,
|
||||
port_, faddr_->port, config->pid,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1278,6 +1223,14 @@ ClientHandler::ReadBuf *ClientHandler::get_rb() { return &rb_; }
|
||||
|
||||
void ClientHandler::signal_write() { conn_.wlimit.startw(); }
|
||||
|
||||
void ClientHandler::signal_write_no_wait() {
|
||||
// ev_feed_event works without starting watcher. But rate limiter
|
||||
// requires active watcher. Without that, we might not send pending
|
||||
// data. Also ClientHandler::write_tls requires it.
|
||||
conn_.wlimit.startw();
|
||||
ev_feed_event(conn_.loop, &conn_.wev, EV_WRITE);
|
||||
}
|
||||
|
||||
RateLimit *ClientHandler::get_rlimit() { return &conn_.rlimit; }
|
||||
RateLimit *ClientHandler::get_wlimit() { return &conn_.wlimit; }
|
||||
|
||||
@@ -1316,7 +1269,7 @@ ssize_t parse_proxy_line_port(const uint8_t *first, const uint8_t *last) {
|
||||
|
||||
int ClientHandler::on_proxy_protocol_finish() {
|
||||
if (conn_.tls.ssl) {
|
||||
conn_.tls.rbuf.append(rb_.pos, rb_.rleft());
|
||||
conn_.tls.rbuf.append(rb_.pos(), rb_.rleft());
|
||||
rb_.reset();
|
||||
}
|
||||
|
||||
@@ -1337,7 +1290,7 @@ int ClientHandler::proxy_protocol_read() {
|
||||
CLOG(INFO, this) << "PROXY-protocol: Started";
|
||||
}
|
||||
|
||||
auto first = rb_.pos;
|
||||
auto first = rb_.pos();
|
||||
|
||||
// NULL character really destroys functions which expects NULL
|
||||
// terminated string. We won't expect it in PROXY protocol line, so
|
||||
@@ -1346,12 +1299,12 @@ int ClientHandler::proxy_protocol_read() {
|
||||
|
||||
constexpr size_t MAX_PROXY_LINELEN = 107;
|
||||
|
||||
auto bufend = rb_.pos + std::min(MAX_PROXY_LINELEN, rb_.rleft());
|
||||
auto bufend = rb_.pos() + std::min(MAX_PROXY_LINELEN, rb_.rleft());
|
||||
|
||||
auto end =
|
||||
std::find_first_of(rb_.pos, bufend, std::begin(chrs), std::end(chrs));
|
||||
std::find_first_of(rb_.pos(), bufend, std::begin(chrs), std::end(chrs));
|
||||
|
||||
if (end == bufend || *end == '\0' || end == rb_.pos || *(end - 1) != '\r') {
|
||||
if (end == bufend || *end == '\0' || end == rb_.pos() || *(end - 1) != '\r') {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this) << "PROXY-protocol-v1: No ending CR LF sequence found";
|
||||
}
|
||||
@@ -1362,14 +1315,14 @@ int ClientHandler::proxy_protocol_read() {
|
||||
|
||||
constexpr auto HEADER = StringRef::from_lit("PROXY ");
|
||||
|
||||
if (static_cast<size_t>(end - rb_.pos) < HEADER.size()) {
|
||||
if (static_cast<size_t>(end - rb_.pos()) < HEADER.size()) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this) << "PROXY-protocol-v1: PROXY version 1 ID not found";
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!util::streq(HEADER, StringRef{rb_.pos, HEADER.size()})) {
|
||||
if (!util::streq(HEADER, StringRef{rb_.pos(), HEADER.size()})) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this) << "PROXY-protocol-v1: Bad PROXY protocol version 1 ID";
|
||||
}
|
||||
@@ -1380,22 +1333,22 @@ int ClientHandler::proxy_protocol_read() {
|
||||
|
||||
int family;
|
||||
|
||||
if (rb_.pos[0] == 'T') {
|
||||
if (end - rb_.pos < 5) {
|
||||
if (rb_.pos()[0] == 'T') {
|
||||
if (end - rb_.pos() < 5) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this) << "PROXY-protocol-v1: INET protocol family not found";
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rb_.pos[1] != 'C' || rb_.pos[2] != 'P') {
|
||||
if (rb_.pos()[1] != 'C' || rb_.pos()[2] != 'P') {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this) << "PROXY-protocol-v1: Unknown INET protocol family";
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (rb_.pos[3]) {
|
||||
switch (rb_.pos()[3]) {
|
||||
case '4':
|
||||
family = AF_INET;
|
||||
break;
|
||||
@@ -1411,26 +1364,26 @@ int ClientHandler::proxy_protocol_read() {
|
||||
|
||||
rb_.drain(5);
|
||||
} else {
|
||||
if (end - rb_.pos < 7) {
|
||||
if (end - rb_.pos() < 7) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this) << "PROXY-protocol-v1: INET protocol family not found";
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
if (!util::streq_l("UNKNOWN", rb_.pos, 7)) {
|
||||
if (!util::streq_l("UNKNOWN", rb_.pos(), 7)) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this) << "PROXY-protocol-v1: Unknown INET protocol family";
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
rb_.drain(end + 2 - rb_.pos);
|
||||
rb_.drain(end + 2 - rb_.pos());
|
||||
|
||||
return on_proxy_protocol_finish();
|
||||
}
|
||||
|
||||
// source address
|
||||
auto token_end = std::find(rb_.pos, end, ' ');
|
||||
auto token_end = std::find(rb_.pos(), end, ' ');
|
||||
if (token_end == end) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this) << "PROXY-protocol-v1: Source address not found";
|
||||
@@ -1439,20 +1392,20 @@ int ClientHandler::proxy_protocol_read() {
|
||||
}
|
||||
|
||||
*token_end = '\0';
|
||||
if (!util::numeric_host(reinterpret_cast<const char *>(rb_.pos), family)) {
|
||||
if (!util::numeric_host(reinterpret_cast<const char *>(rb_.pos()), family)) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this) << "PROXY-protocol-v1: Invalid source address";
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto src_addr = rb_.pos;
|
||||
auto src_addrlen = token_end - rb_.pos;
|
||||
auto src_addr = rb_.pos();
|
||||
auto src_addrlen = token_end - rb_.pos();
|
||||
|
||||
rb_.drain(token_end - rb_.pos + 1);
|
||||
rb_.drain(token_end - rb_.pos() + 1);
|
||||
|
||||
// destination address
|
||||
token_end = std::find(rb_.pos, end, ' ');
|
||||
token_end = std::find(rb_.pos(), end, ' ');
|
||||
if (token_end == end) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this) << "PROXY-protocol-v1: Destination address not found";
|
||||
@@ -1461,7 +1414,7 @@ int ClientHandler::proxy_protocol_read() {
|
||||
}
|
||||
|
||||
*token_end = '\0';
|
||||
if (!util::numeric_host(reinterpret_cast<const char *>(rb_.pos), family)) {
|
||||
if (!util::numeric_host(reinterpret_cast<const char *>(rb_.pos()), family)) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this) << "PROXY-protocol-v1: Invalid destination address";
|
||||
}
|
||||
@@ -1470,26 +1423,26 @@ int ClientHandler::proxy_protocol_read() {
|
||||
|
||||
// Currently we don't use destination address
|
||||
|
||||
rb_.drain(token_end - rb_.pos + 1);
|
||||
rb_.drain(token_end - rb_.pos() + 1);
|
||||
|
||||
// source port
|
||||
auto n = parse_proxy_line_port(rb_.pos, end);
|
||||
if (n <= 0 || *(rb_.pos + n) != ' ') {
|
||||
auto n = parse_proxy_line_port(rb_.pos(), end);
|
||||
if (n <= 0 || *(rb_.pos() + n) != ' ') {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this) << "PROXY-protocol-v1: Invalid source port";
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
rb_.pos[n] = '\0';
|
||||
auto src_port = rb_.pos;
|
||||
rb_.pos()[n] = '\0';
|
||||
auto src_port = rb_.pos();
|
||||
auto src_portlen = n;
|
||||
|
||||
rb_.drain(n + 1);
|
||||
|
||||
// destination port
|
||||
n = parse_proxy_line_port(rb_.pos, end);
|
||||
if (n <= 0 || rb_.pos + n != end) {
|
||||
n = parse_proxy_line_port(rb_.pos(), end);
|
||||
if (n <= 0 || rb_.pos() + n != end) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this) << "PROXY-protocol-v1: Invalid destination port";
|
||||
}
|
||||
@@ -1498,14 +1451,14 @@ int ClientHandler::proxy_protocol_read() {
|
||||
|
||||
// Currently we don't use destination port
|
||||
|
||||
rb_.drain(end + 2 - rb_.pos);
|
||||
rb_.drain(end + 2 - rb_.pos());
|
||||
|
||||
ipaddr_ =
|
||||
make_string_ref(balloc_, StringRef{src_addr, src_addr + src_addrlen});
|
||||
port_ = make_string_ref(balloc_, StringRef{src_port, src_port + src_portlen});
|
||||
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this) << "PROXY-protocol-v1: Finished, " << (rb_.pos - first)
|
||||
CLOG(INFO, this) << "PROXY-protocol-v1: Finished, " << (rb_.pos() - first)
|
||||
<< " bytes read";
|
||||
}
|
||||
|
||||
|
||||
@@ -118,13 +118,9 @@ public:
|
||||
// must not be nullptr.
|
||||
void write_accesslog(Downstream *downstream);
|
||||
|
||||
// Writes upstream accesslog. This function is used if
|
||||
// corresponding Downstream object is not available.
|
||||
void write_accesslog(int major, int minor, unsigned int status,
|
||||
int64_t body_bytes_sent);
|
||||
Worker *get_worker() const;
|
||||
|
||||
using ReadBuf = Buffer<16_k>;
|
||||
using ReadBuf = DefaultMemchunkBuffer;
|
||||
|
||||
ReadBuf *get_rb();
|
||||
|
||||
@@ -132,6 +128,8 @@ public:
|
||||
RateLimit *get_wlimit();
|
||||
|
||||
void signal_write();
|
||||
// Use this for HTTP/1 frontend since it produces better result.
|
||||
void signal_write_no_wait();
|
||||
ev_io *get_wev();
|
||||
|
||||
void setup_upstream_io_callback();
|
||||
@@ -169,6 +167,7 @@ private:
|
||||
// sure that the allocations must be bounded, and not proportional
|
||||
// to the number of requests.
|
||||
BlockAllocator balloc_;
|
||||
DefaultMemchunkBuffer rb_;
|
||||
Connection conn_;
|
||||
ev_timer reneg_shutdown_timer_;
|
||||
std::unique_ptr<Upstream> upstream_;
|
||||
@@ -195,7 +194,6 @@ private:
|
||||
bool should_close_after_write_;
|
||||
// true if affinity_hash_ is computed
|
||||
bool affinity_hash_computed_;
|
||||
ReadBuf rb_;
|
||||
};
|
||||
|
||||
} // namespace shrpx
|
||||
|
||||
@@ -677,6 +677,7 @@ int parse_memcached_connection_params(MemcachedConnectionParams &out,
|
||||
struct UpstreamParams {
|
||||
int alt_mode;
|
||||
bool tls;
|
||||
bool proxyproto;
|
||||
};
|
||||
|
||||
namespace {
|
||||
@@ -705,6 +706,8 @@ int parse_upstream_params(UpstreamParams &out, const StringRef &src_params) {
|
||||
return -1;
|
||||
}
|
||||
out.alt_mode = ALTMODE_HEALTHMON;
|
||||
} else if (util::strieq_l("proxyproto", param)) {
|
||||
out.proxyproto = true;
|
||||
} else if (!param.empty()) {
|
||||
LOG(ERROR) << "frontend: " << param << ": unknown keyword";
|
||||
return -1;
|
||||
@@ -728,6 +731,8 @@ struct DownstreamParams {
|
||||
shrpx_proto proto;
|
||||
shrpx_session_affinity affinity;
|
||||
bool tls;
|
||||
bool dns;
|
||||
bool frontend_tls;
|
||||
};
|
||||
|
||||
namespace {
|
||||
@@ -801,6 +806,10 @@ int parse_downstream_params(DownstreamParams &out,
|
||||
LOG(ERROR) << "backend: affinity: value must be either none or ip";
|
||||
return -1;
|
||||
}
|
||||
} else if (util::strieq_l("dns", param)) {
|
||||
out.dns = true;
|
||||
} else if (util::strieq_l("frontend-tls", param)) {
|
||||
out.frontend_tls = true;
|
||||
} else if (!param.empty()) {
|
||||
LOG(ERROR) << "backend: " << param << ": unknown keyword";
|
||||
return -1;
|
||||
@@ -841,11 +850,17 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (addr.host_unix && params.dns) {
|
||||
LOG(ERROR) << "backend: dns: cannot be used for UNIX domain socket";
|
||||
return -1;
|
||||
}
|
||||
|
||||
addr.fall = params.fall;
|
||||
addr.rise = params.rise;
|
||||
addr.proto = params.proto;
|
||||
addr.tls = params.tls;
|
||||
addr.sni = make_string_ref(downstreamconf.balloc, params.sni);
|
||||
addr.dns = params.dns;
|
||||
|
||||
auto &routerconf = downstreamconf.router;
|
||||
auto &router = routerconf.router;
|
||||
@@ -887,6 +902,11 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
|
||||
if (params.affinity != AFFINITY_NONE) {
|
||||
g.affinity = params.affinity;
|
||||
}
|
||||
// If at least one backend requires frontend TLS connection,
|
||||
// enable it for all backends sharing the same pattern.
|
||||
if (params.frontend_tls) {
|
||||
g.require_upstream_tls = true;
|
||||
}
|
||||
g.addrs.push_back(addr);
|
||||
done = true;
|
||||
break;
|
||||
@@ -901,6 +921,7 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
|
||||
auto &g = addr_groups.back();
|
||||
g.addrs.push_back(addr);
|
||||
g.affinity = params.affinity;
|
||||
g.require_upstream_tls = params.frontend_tls;
|
||||
|
||||
if (pattern[0] == '*') {
|
||||
// wildcard pattern
|
||||
@@ -1187,6 +1208,134 @@ int read_tls_sct_from_dir(std::vector<uint8_t> &dst, const StringRef &opt,
|
||||
}
|
||||
} // namespace
|
||||
|
||||
#if !LIBRESSL_IN_USE
|
||||
namespace {
|
||||
// Reads PSK secrets from path, and parses each line. The result is
|
||||
// directly stored into config->tls.psk_secrets. This function
|
||||
// returns 0 if it succeeds, or -1.
|
||||
int parse_psk_secrets(Config *config, const StringRef &path) {
|
||||
auto &tlsconf = config->tls;
|
||||
|
||||
std::ifstream f(path.c_str(), std::ios::binary);
|
||||
if (!f) {
|
||||
LOG(ERROR) << SHRPX_OPT_PSK_SECRETS << ": could not open file " << path;
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t lineno = 0;
|
||||
std::string line;
|
||||
while (std::getline(f, line)) {
|
||||
++lineno;
|
||||
if (line.empty() || line[0] == '#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto sep_it = std::find(std::begin(line), std::end(line), ':');
|
||||
if (sep_it == std::end(line)) {
|
||||
LOG(ERROR) << SHRPX_OPT_PSK_SECRETS
|
||||
<< ": could not fine separator at line " << lineno;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sep_it == std::begin(line)) {
|
||||
LOG(ERROR) << SHRPX_OPT_PSK_SECRETS << ": empty identity at line "
|
||||
<< lineno;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sep_it + 1 == std::end(line)) {
|
||||
LOG(ERROR) << SHRPX_OPT_PSK_SECRETS << ": empty secret at line "
|
||||
<< lineno;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!util::is_hex_string(StringRef{sep_it + 1, std::end(line)})) {
|
||||
LOG(ERROR) << SHRPX_OPT_PSK_SECRETS
|
||||
<< ": secret must be hex string at line " << lineno;
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto identity =
|
||||
make_string_ref(config->balloc, StringRef{std::begin(line), sep_it});
|
||||
|
||||
auto secret =
|
||||
util::decode_hex(config->balloc, StringRef{sep_it + 1, std::end(line)});
|
||||
|
||||
auto rv = tlsconf.psk_secrets.emplace(identity, secret);
|
||||
if (!rv.second) {
|
||||
LOG(ERROR) << SHRPX_OPT_PSK_SECRETS
|
||||
<< ": identity has already been registered at line " << lineno;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
#endif // !LIBRESSL_IN_USE
|
||||
|
||||
#if !LIBRESSL_IN_USE
|
||||
namespace {
|
||||
// Reads PSK secrets from path, and parses each line. The result is
|
||||
// directly stored into config->tls.client.psk. This function returns
|
||||
// 0 if it succeeds, or -1.
|
||||
int parse_client_psk_secrets(Config *config, const StringRef &path) {
|
||||
auto &tlsconf = config->tls;
|
||||
|
||||
std::ifstream f(path.c_str(), std::ios::binary);
|
||||
if (!f) {
|
||||
LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS << ": could not open file "
|
||||
<< path;
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t lineno = 0;
|
||||
std::string line;
|
||||
while (std::getline(f, line)) {
|
||||
++lineno;
|
||||
if (line.empty() || line[0] == '#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto sep_it = std::find(std::begin(line), std::end(line), ':');
|
||||
if (sep_it == std::end(line)) {
|
||||
LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS
|
||||
<< ": could not find separator at line " << lineno;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sep_it == std::begin(line)) {
|
||||
LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS << ": empty identity at line "
|
||||
<< lineno;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sep_it + 1 == std::end(line)) {
|
||||
LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS << ": empty secret at line "
|
||||
<< lineno;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!util::is_hex_string(StringRef{sep_it + 1, std::end(line)})) {
|
||||
LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS
|
||||
<< ": secret must be hex string at line " << lineno;
|
||||
return -1;
|
||||
}
|
||||
|
||||
tlsconf.client.psk.identity =
|
||||
make_string_ref(config->balloc, StringRef{std::begin(line), sep_it});
|
||||
|
||||
tlsconf.client.psk.secret =
|
||||
util::decode_hex(config->balloc, StringRef{sep_it + 1, std::end(line)});
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
#endif // !LIBRESSL_IN_USE
|
||||
|
||||
// generated by gennghttpxfun.py
|
||||
int option_lookup_token(const char *name, size_t namelen) {
|
||||
switch (namelen) {
|
||||
@@ -1354,6 +1503,9 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
if (util::strieq_l("ecdh-curve", name, 10)) {
|
||||
return SHRPX_OPTID_ECDH_CURVES;
|
||||
}
|
||||
if (util::strieq_l("psk-secret", name, 10)) {
|
||||
return SHRPX_OPTID_PSK_SECRETS;
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
if (util::strieq_l("write-burs", name, 10)) {
|
||||
@@ -1361,6 +1513,9 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
}
|
||||
break;
|
||||
case 'y':
|
||||
if (util::strieq_l("dns-max-tr", name, 10)) {
|
||||
return SHRPX_OPTID_DNS_MAX_TRY;
|
||||
}
|
||||
if (util::strieq_l("http2-prox", name, 10)) {
|
||||
return SHRPX_OPTID_HTTP2_PROXY;
|
||||
}
|
||||
@@ -1443,6 +1598,9 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
if (util::strieq_l("backend-no-tl", name, 13)) {
|
||||
return SHRPX_OPTID_BACKEND_NO_TLS;
|
||||
}
|
||||
if (util::strieq_l("client-cipher", name, 13)) {
|
||||
return SHRPX_OPTID_CLIENT_CIPHERS;
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
if (util::strieq_l("tls-proto-lis", name, 13)) {
|
||||
@@ -1522,6 +1680,9 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
if (util::strieq_l("dns-cache-timeou", name, 16)) {
|
||||
return SHRPX_OPTID_DNS_CACHE_TIMEOUT;
|
||||
}
|
||||
if (util::strieq_l("worker-read-burs", name, 16)) {
|
||||
return SHRPX_OPTID_WORKER_READ_BURST;
|
||||
}
|
||||
@@ -1535,7 +1696,15 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
return SHRPX_OPTID_ADD_REQUEST_HEADER;
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
if (util::strieq_l("client-psk-secret", name, 17)) {
|
||||
return SHRPX_OPTID_CLIENT_PSK_SECRETS;
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
if (util::strieq_l("dns-lookup-timeou", name, 17)) {
|
||||
return SHRPX_OPTID_DNS_LOOKUP_TIMEOUT;
|
||||
}
|
||||
if (util::strieq_l("worker-write-burs", name, 17)) {
|
||||
return SHRPX_OPTID_WORKER_WRITE_BURST;
|
||||
}
|
||||
@@ -1630,6 +1799,11 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
return SHRPX_OPTID_FRONTEND_READ_TIMEOUT;
|
||||
}
|
||||
break;
|
||||
case 'y':
|
||||
if (util::strieq_l("accesslog-write-earl", name, 20)) {
|
||||
return SHRPX_OPTID_ACCESSLOG_WRITE_EARLY;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 22:
|
||||
@@ -1771,6 +1945,9 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
if (util::strieq_l("frontend-http2-read-timeou", name, 26)) {
|
||||
return SHRPX_OPTID_FRONTEND_HTTP2_READ_TIMEOUT;
|
||||
}
|
||||
if (util::strieq_l("frontend-keep-alive-timeou", name, 26)) {
|
||||
return SHRPX_OPTID_FRONTEND_KEEP_ALIVE_TIMEOUT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@@ -1848,6 +2025,11 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL;
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
if (util::strieq_l("client-no-http2-cipher-black-lis", name, 32)) {
|
||||
return SHRPX_OPTID_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 34:
|
||||
@@ -2070,6 +2252,7 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
||||
addr.fd = -1;
|
||||
addr.tls = params.tls;
|
||||
addr.alt_mode = params.alt_mode;
|
||||
addr.accept_proxy_protocol = params.proxyproto;
|
||||
|
||||
if (addr.alt_mode == ALTMODE_API) {
|
||||
apiconf.enabled = true;
|
||||
@@ -2862,6 +3045,8 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
||||
#endif // !HAVE_MRUBY
|
||||
return 0;
|
||||
case SHRPX_OPTID_ACCEPT_PROXY_PROTOCOL:
|
||||
LOG(WARN) << opt << ": deprecated. Use proxyproto keyword in "
|
||||
<< SHRPX_OPT_FRONTEND << " instead.";
|
||||
config->conn.upstream.accept_proxy_protocol = util::strieq_l("yes", optarg);
|
||||
|
||||
return 0;
|
||||
@@ -3090,6 +3275,58 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
||||
LOG(WARN) << opt << ": This option requires OpenSSL >= 1.0.2";
|
||||
return 0;
|
||||
#endif // !(!LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L)
|
||||
case SHRPX_OPTID_DNS_CACHE_TIMEOUT:
|
||||
return parse_duration(&config->dns.timeout.cache, opt, optarg);
|
||||
case SHRPX_OPTID_DNS_LOOKUP_TIMEOUT:
|
||||
return parse_duration(&config->dns.timeout.lookup, opt, optarg);
|
||||
case SHRPX_OPTID_DNS_MAX_TRY: {
|
||||
int n;
|
||||
if (parse_uint(&n, opt, optarg) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (n > 5) {
|
||||
LOG(ERROR) << opt << ": must be smaller than or equal to 5";
|
||||
return -1;
|
||||
}
|
||||
|
||||
config->dns.max_try = n;
|
||||
return 0;
|
||||
}
|
||||
case SHRPX_OPTID_FRONTEND_KEEP_ALIVE_TIMEOUT:
|
||||
return parse_duration(&config->conn.upstream.timeout.idle_read, opt,
|
||||
optarg);
|
||||
case SHRPX_OPTID_PSK_SECRETS:
|
||||
#if !LIBRESSL_IN_USE
|
||||
return parse_psk_secrets(config, optarg);
|
||||
#else // LIBRESSL_IN_USE
|
||||
LOG(WARN)
|
||||
<< opt
|
||||
<< ": ignored because underlying TLS library does not support PSK";
|
||||
return 0;
|
||||
#endif // LIBRESSL_IN_USE
|
||||
case SHRPX_OPTID_CLIENT_PSK_SECRETS:
|
||||
#if !LIBRESSL_IN_USE
|
||||
return parse_client_psk_secrets(config, optarg);
|
||||
#else // LIBRESSL_IN_USE
|
||||
LOG(WARN)
|
||||
<< opt
|
||||
<< ": ignored because underlying TLS library does not support PSK";
|
||||
return 0;
|
||||
#endif // LIBRESSL_IN_USE
|
||||
case SHRPX_OPTID_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST:
|
||||
config->tls.client.no_http2_cipher_black_list =
|
||||
util::strieq_l("yes", optarg);
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_CLIENT_CIPHERS:
|
||||
config->tls.client.ciphers = make_string_ref(config->balloc, optarg);
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_ACCESSLOG_WRITE_EARLY:
|
||||
config->logging.access.write_early = util::strieq_l("yes", optarg);
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_CONF:
|
||||
LOG(WARN) << "conf: ignored";
|
||||
|
||||
@@ -3397,6 +3634,12 @@ int configure_downstream_group(Config *config, bool http2_proxy,
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (addr_groups[catch_all_group].require_upstream_tls) {
|
||||
LOG(FATAL)
|
||||
<< "backend: Catch-all backend cannot have frontend-tls parameter";
|
||||
return -1;
|
||||
}
|
||||
|
||||
downstreamconf.addr_group_catch_all = catch_all_group;
|
||||
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
@@ -3442,6 +3685,7 @@ int configure_downstream_group(Config *config, bool http2_proxy,
|
||||
auto hostport =
|
||||
util::make_hostport(downstreamconf.balloc, addr.host, addr.port);
|
||||
|
||||
if (!addr.dns) {
|
||||
if (resolve_hostname(&addr.addr, addr.host.c_str(), addr.port,
|
||||
downstreamconf.family, resolve_flags) == -1) {
|
||||
LOG(FATAL) << "Resolving backend address failed: " << hostport;
|
||||
@@ -3452,14 +3696,27 @@ int configure_downstream_group(Config *config, bool http2_proxy,
|
||||
LOG(INFO) << "Resolved backend address: " << hostport << " -> "
|
||||
<< util::to_numeric_addr(&addr.addr);
|
||||
}
|
||||
} else {
|
||||
LOG(INFO) << "Resolving backend address " << hostport
|
||||
<< " takes place dynamically";
|
||||
}
|
||||
}
|
||||
|
||||
if (g.affinity == AFFINITY_IP) {
|
||||
size_t idx = 0;
|
||||
for (auto &addr : g.addrs) {
|
||||
StringRef key;
|
||||
if (addr.dns) {
|
||||
if (addr.host_unix) {
|
||||
key = addr.host;
|
||||
} else {
|
||||
key = addr.hostport;
|
||||
}
|
||||
} else {
|
||||
auto p = reinterpret_cast<uint8_t *>(&addr.addr.su);
|
||||
rv = compute_affinity_hash(g.affinity_hash, idx,
|
||||
StringRef{p, addr.addr.len});
|
||||
key = StringRef{p, addr.addr.len};
|
||||
}
|
||||
rv = compute_affinity_hash(g.affinity_hash, idx, key);
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -312,6 +312,21 @@ constexpr auto SHRPX_OPT_ECDH_CURVES = StringRef::from_lit("ecdh-curves");
|
||||
constexpr auto SHRPX_OPT_TLS_SCT_DIR = StringRef::from_lit("tls-sct-dir");
|
||||
constexpr auto SHRPX_OPT_BACKEND_CONNECT_TIMEOUT =
|
||||
StringRef::from_lit("backend-connect-timeout");
|
||||
constexpr auto SHRPX_OPT_DNS_CACHE_TIMEOUT =
|
||||
StringRef::from_lit("dns-cache-timeout");
|
||||
constexpr auto SHRPX_OPT_DNS_LOOKUP_TIMEOUT =
|
||||
StringRef::from_lit("dns-lookup-timeout");
|
||||
constexpr auto SHRPX_OPT_DNS_MAX_TRY = StringRef::from_lit("dns-max-try");
|
||||
constexpr auto SHRPX_OPT_FRONTEND_KEEP_ALIVE_TIMEOUT =
|
||||
StringRef::from_lit("frontend-keep-alive-timeout");
|
||||
constexpr auto SHRPX_OPT_PSK_SECRETS = StringRef::from_lit("psk-secrets");
|
||||
constexpr auto SHRPX_OPT_CLIENT_PSK_SECRETS =
|
||||
StringRef::from_lit("client-psk-secrets");
|
||||
constexpr auto SHRPX_OPT_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST =
|
||||
StringRef::from_lit("client-no-http2-cipher-black-list");
|
||||
constexpr auto SHRPX_OPT_CLIENT_CIPHERS = StringRef::from_lit("client-ciphers");
|
||||
constexpr auto SHRPX_OPT_ACCESSLOG_WRITE_EARLY =
|
||||
StringRef::from_lit("accesslog-write-early");
|
||||
|
||||
constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8;
|
||||
|
||||
@@ -375,10 +390,13 @@ struct UpstreamAddr {
|
||||
bool host_unix;
|
||||
// true if TLS is enabled.
|
||||
bool tls;
|
||||
// true if client is supposed to send PROXY protocol v1 header.
|
||||
bool accept_proxy_protocol;
|
||||
int fd;
|
||||
};
|
||||
|
||||
struct DownstreamAddrConfig {
|
||||
// Resolved address if |dns| is false
|
||||
Address addr;
|
||||
// backend address. If |host_unix| is true, this is UNIX domain
|
||||
// socket path. This must be NULL terminated string.
|
||||
@@ -397,6 +415,8 @@ struct DownstreamAddrConfig {
|
||||
// true if |host| contains UNIX domain socket path.
|
||||
bool host_unix;
|
||||
bool tls;
|
||||
// true if dynamic DNS is enabled
|
||||
bool dns;
|
||||
};
|
||||
|
||||
// Mapping hash to idx which is an index into
|
||||
@@ -410,7 +430,9 @@ struct AffinityHash {
|
||||
|
||||
struct DownstreamAddrGroupConfig {
|
||||
DownstreamAddrGroupConfig(const StringRef &pattern)
|
||||
: pattern(pattern), affinity(AFFINITY_NONE) {}
|
||||
: pattern(pattern),
|
||||
affinity(AFFINITY_NONE),
|
||||
require_upstream_tls(false) {}
|
||||
|
||||
StringRef pattern;
|
||||
std::vector<DownstreamAddrConfig> addrs;
|
||||
@@ -419,6 +441,8 @@ struct DownstreamAddrGroupConfig {
|
||||
std::vector<AffinityHash> affinity_hash;
|
||||
// Session affinity
|
||||
shrpx_session_affinity affinity;
|
||||
// true if this group requires that client connection must be TLS.
|
||||
bool require_upstream_tls;
|
||||
};
|
||||
|
||||
struct TicketKey {
|
||||
@@ -531,12 +555,23 @@ struct TLSConfig {
|
||||
bool enabled;
|
||||
} client_verify;
|
||||
|
||||
// Client private key and certificate used in backend connections.
|
||||
// Client (backend connection) TLS configuration.
|
||||
struct {
|
||||
// Client PSK configuration
|
||||
struct {
|
||||
// identity must be NULL terminated string.
|
||||
StringRef identity;
|
||||
StringRef secret;
|
||||
} psk;
|
||||
StringRef private_key_file;
|
||||
StringRef cert_file;
|
||||
StringRef ciphers;
|
||||
bool no_http2_cipher_black_list;
|
||||
} client;
|
||||
|
||||
// PSK secrets. The key is identity, and the associated value is
|
||||
// its secret.
|
||||
std::map<StringRef, StringRef> psk_secrets;
|
||||
// The list of additional TLS certificate pair
|
||||
std::vector<TLSCertificate> subcerts;
|
||||
std::vector<unsigned char> alpn_prefs;
|
||||
@@ -657,6 +692,9 @@ struct LoggingConfig {
|
||||
StringRef file;
|
||||
// Send accesslog to syslog, ignoring accesslog_file.
|
||||
bool syslog;
|
||||
// Write accesslog when response headers are received from
|
||||
// backend, rather than response body is received and sent.
|
||||
bool write_early;
|
||||
} access;
|
||||
struct {
|
||||
StringRef file;
|
||||
@@ -758,12 +796,14 @@ struct ConnectionConfig {
|
||||
ev_tstamp http2_read;
|
||||
ev_tstamp read;
|
||||
ev_tstamp write;
|
||||
ev_tstamp idle_read;
|
||||
} timeout;
|
||||
struct {
|
||||
RateLimitConfig read;
|
||||
RateLimitConfig write;
|
||||
} ratelimit;
|
||||
size_t worker_connections;
|
||||
// Deprecated. See UpstreamAddr.accept_proxy_protocol.
|
||||
bool accept_proxy_protocol;
|
||||
} upstream;
|
||||
|
||||
@@ -777,6 +817,16 @@ struct APIConfig {
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
struct DNSConfig {
|
||||
struct {
|
||||
ev_tstamp cache;
|
||||
ev_tstamp lookup;
|
||||
} timeout;
|
||||
// The number of tries name resolver makes before abandoning
|
||||
// request.
|
||||
size_t max_try;
|
||||
};
|
||||
|
||||
struct Config {
|
||||
Config()
|
||||
: balloc(4096, 4096),
|
||||
@@ -787,6 +837,7 @@ struct Config {
|
||||
logging{},
|
||||
conn{},
|
||||
api{},
|
||||
dns{},
|
||||
num_worker{0},
|
||||
padding{0},
|
||||
rlimit_nofile{0},
|
||||
@@ -815,6 +866,7 @@ struct Config {
|
||||
LoggingConfig logging;
|
||||
ConnectionConfig conn;
|
||||
APIConfig api;
|
||||
DNSConfig dns;
|
||||
StringRef pid_file;
|
||||
StringRef conf_path;
|
||||
StringRef user;
|
||||
@@ -845,6 +897,7 @@ enum {
|
||||
SHRPX_OPTID_ACCESSLOG_FILE,
|
||||
SHRPX_OPTID_ACCESSLOG_FORMAT,
|
||||
SHRPX_OPTID_ACCESSLOG_SYSLOG,
|
||||
SHRPX_OPTID_ACCESSLOG_WRITE_EARLY,
|
||||
SHRPX_OPTID_ADD_FORWARDED,
|
||||
SHRPX_OPTID_ADD_REQUEST_HEADER,
|
||||
SHRPX_OPTID_ADD_RESPONSE_HEADER,
|
||||
@@ -886,11 +939,17 @@ enum {
|
||||
SHRPX_OPTID_CIPHERS,
|
||||
SHRPX_OPTID_CLIENT,
|
||||
SHRPX_OPTID_CLIENT_CERT_FILE,
|
||||
SHRPX_OPTID_CLIENT_CIPHERS,
|
||||
SHRPX_OPTID_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST,
|
||||
SHRPX_OPTID_CLIENT_PRIVATE_KEY_FILE,
|
||||
SHRPX_OPTID_CLIENT_PROXY,
|
||||
SHRPX_OPTID_CLIENT_PSK_SECRETS,
|
||||
SHRPX_OPTID_CONF,
|
||||
SHRPX_OPTID_DAEMON,
|
||||
SHRPX_OPTID_DH_PARAM_FILE,
|
||||
SHRPX_OPTID_DNS_CACHE_TIMEOUT,
|
||||
SHRPX_OPTID_DNS_LOOKUP_TIMEOUT,
|
||||
SHRPX_OPTID_DNS_MAX_TRY,
|
||||
SHRPX_OPTID_ECDH_CURVES,
|
||||
SHRPX_OPTID_ERROR_PAGE,
|
||||
SHRPX_OPTID_ERRORLOG_FILE,
|
||||
@@ -914,6 +973,7 @@ enum {
|
||||
SHRPX_OPTID_FRONTEND_HTTP2_SETTINGS_TIMEOUT,
|
||||
SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS,
|
||||
SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_SIZE,
|
||||
SHRPX_OPTID_FRONTEND_KEEP_ALIVE_TIMEOUT,
|
||||
SHRPX_OPTID_FRONTEND_NO_TLS,
|
||||
SHRPX_OPTID_FRONTEND_READ_TIMEOUT,
|
||||
SHRPX_OPTID_FRONTEND_WRITE_TIMEOUT,
|
||||
@@ -945,6 +1005,7 @@ enum {
|
||||
SHRPX_OPTID_PID_FILE,
|
||||
SHRPX_OPTID_PRIVATE_KEY_FILE,
|
||||
SHRPX_OPTID_PRIVATE_KEY_PASSWD_FILE,
|
||||
SHRPX_OPTID_PSK_SECRETS,
|
||||
SHRPX_OPTID_READ_BURST,
|
||||
SHRPX_OPTID_READ_RATE,
|
||||
SHRPX_OPTID_REQUEST_HEADER_FIELD_BUFFER,
|
||||
|
||||
@@ -67,7 +67,9 @@ Connection::Connection(struct ev_loop *loop, int fd, SSL *ssl,
|
||||
fd(fd),
|
||||
tls_dyn_rec_warmup_threshold(tls_dyn_rec_warmup_threshold),
|
||||
tls_dyn_rec_idle_timeout(tls_dyn_rec_idle_timeout),
|
||||
proto(proto) {
|
||||
proto(proto),
|
||||
last_read(0.),
|
||||
read_timeout(read_timeout) {
|
||||
|
||||
ev_io_init(&wev, writecb, fd, EV_WRITE);
|
||||
ev_io_init(&rev, readcb, fd, EV_READ);
|
||||
@@ -93,7 +95,8 @@ Connection::~Connection() { disconnect(); }
|
||||
|
||||
void Connection::disconnect() {
|
||||
if (tls.ssl) {
|
||||
SSL_set_shutdown(tls.ssl, SSL_RECEIVED_SHUTDOWN);
|
||||
SSL_set_shutdown(tls.ssl,
|
||||
SSL_get_shutdown(tls.ssl) | SSL_RECEIVED_SHUTDOWN);
|
||||
ERR_clear_error();
|
||||
|
||||
if (tls.cached_session) {
|
||||
@@ -138,7 +141,10 @@ void Connection::disconnect() {
|
||||
|
||||
void Connection::prepare_client_handshake() { SSL_set_connect_state(tls.ssl); }
|
||||
|
||||
void Connection::prepare_server_handshake() { SSL_set_accept_state(tls.ssl); }
|
||||
void Connection::prepare_server_handshake() {
|
||||
SSL_set_accept_state(tls.ssl);
|
||||
tls.server_handshake = true;
|
||||
}
|
||||
|
||||
// BIO implementation is inspired by openldap implementation:
|
||||
// http://www.openldap.org/devel/cvsweb.cgi/~checkout~/libraries/libldap/tls_o.c
|
||||
@@ -499,8 +505,14 @@ int Connection::write_tls_pending_handshake() {
|
||||
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "SSL/TLS handshake completed";
|
||||
if (SSL_session_reused(tls.ssl)) {
|
||||
LOG(INFO) << "SSL/TLS session reused";
|
||||
nghttp2::ssl::TLSSessionInfo tls_info{};
|
||||
if (nghttp2::ssl::get_tls_session_info(&tls_info, tls.ssl)) {
|
||||
LOG(INFO) << "cipher=" << tls_info.cipher
|
||||
<< " protocol=" << tls_info.protocol
|
||||
<< " resumption=" << (tls_info.session_reused ? "yes" : "no")
|
||||
<< " session_id="
|
||||
<< util::format_hex(tls_info.session_id,
|
||||
tls_info.session_id_length);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -527,7 +539,15 @@ int Connection::check_http2_requirement() {
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
if (!get_config()->tls.no_http2_cipher_black_list &&
|
||||
|
||||
auto check_black_list = false;
|
||||
if (tls.server_handshake) {
|
||||
check_black_list = !get_config()->tls.no_http2_cipher_black_list;
|
||||
} else {
|
||||
check_black_list = !get_config()->tls.client.no_http2_cipher_black_list;
|
||||
}
|
||||
|
||||
if (check_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 "
|
||||
@@ -809,4 +829,27 @@ int Connection::get_tcp_hint(TCPHint *hint) const {
|
||||
#endif // !defined(TCP_INFO) || !defined(TCP_NOTSENT_LOWAT)
|
||||
}
|
||||
|
||||
void Connection::again_rt(ev_tstamp t) {
|
||||
read_timeout = t;
|
||||
rt.repeat = t;
|
||||
ev_timer_again(loop, &rt);
|
||||
last_read = ev_now(loop);
|
||||
}
|
||||
|
||||
void Connection::again_rt() {
|
||||
rt.repeat = read_timeout;
|
||||
ev_timer_again(loop, &rt);
|
||||
last_read = ev_now(loop);
|
||||
}
|
||||
|
||||
bool Connection::expired_rt() {
|
||||
auto delta = read_timeout - (ev_now(loop) - last_read);
|
||||
if (delta < 1e-9) {
|
||||
return true;
|
||||
}
|
||||
rt.repeat = delta;
|
||||
ev_timer_again(loop, &rt);
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace shrpx
|
||||
|
||||
@@ -64,6 +64,8 @@ struct TLSConnection {
|
||||
int handshake_state;
|
||||
bool initial_handshake_done;
|
||||
bool reneg_started;
|
||||
// true if ssl is prepared to do handshake as server.
|
||||
bool server_handshake;
|
||||
};
|
||||
|
||||
struct TCPHint {
|
||||
@@ -125,6 +127,17 @@ struct Connection {
|
||||
|
||||
int get_tcp_hint(TCPHint *hint) const;
|
||||
|
||||
// These functions are provided for read timer which is frequently
|
||||
// restarted. We do a trick to make a bit more efficient than just
|
||||
// calling ev_timer_again().
|
||||
|
||||
// Restarts read timer with timeout value |t|.
|
||||
void again_rt(ev_tstamp t);
|
||||
// Restarts read timer without chainging timeout.
|
||||
void again_rt();
|
||||
// Returns true if read timer expired.
|
||||
bool expired_rt();
|
||||
|
||||
TLSConnection tls;
|
||||
ev_io wev;
|
||||
ev_io rev;
|
||||
@@ -141,6 +154,11 @@ struct Connection {
|
||||
// used in this object at the moment. The rest of the program may
|
||||
// use this value when it is useful.
|
||||
shrpx_proto proto;
|
||||
// The point of time when last read is observed. Note: sinde we use
|
||||
// |rt| as idle timer, the activity is not limited to read.
|
||||
ev_tstamp last_read;
|
||||
// Timeout for read timer |rt|.
|
||||
ev_tstamp read_timeout;
|
||||
};
|
||||
|
||||
// Creates BIO_method shared by all SSL objects. If nghttp2 is built
|
||||
|
||||
@@ -499,7 +499,23 @@ void ConnectionHandler::cancel_ocsp_update() {
|
||||
return;
|
||||
}
|
||||
|
||||
kill(ocsp_.proc.pid, SIGTERM);
|
||||
int rv;
|
||||
|
||||
rv = kill(ocsp_.proc.pid, SIGTERM);
|
||||
if (rv != 0) {
|
||||
auto error = errno;
|
||||
LOG(ERROR) << "Could not send signal to OCSP query process: errno="
|
||||
<< error;
|
||||
}
|
||||
|
||||
while ((rv = waitpid(ocsp_.proc.pid, nullptr, 0)) == -1 && errno == EINTR)
|
||||
;
|
||||
if (rv == -1) {
|
||||
auto error = errno;
|
||||
LOG(ERROR) << "Error occurred while we were waiting for the completion of "
|
||||
"OCSP query process: errno="
|
||||
<< error;
|
||||
}
|
||||
}
|
||||
|
||||
// inspired by h2o_read_command function from h2o project:
|
||||
|
||||
351
src/shrpx_dns_resolver.cc
Normal file
351
src/shrpx_dns_resolver.cc
Normal file
@@ -0,0 +1,351 @@
|
||||
/*
|
||||
* 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 "shrpx_dns_resolver.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "shrpx_log.h"
|
||||
#include "shrpx_connection.h"
|
||||
#include "shrpx_config.h"
|
||||
|
||||
namespace shrpx {
|
||||
|
||||
namespace {
|
||||
void sock_state_cb(void *data, int s, int read, int write) {
|
||||
auto resolv = static_cast<DNSResolver *>(data);
|
||||
|
||||
if (resolv->get_status(nullptr) != DNS_STATUS_RUNNING) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (read) {
|
||||
resolv->start_rev(s);
|
||||
} else {
|
||||
resolv->stop_rev(s);
|
||||
}
|
||||
if (write) {
|
||||
resolv->start_wev(s);
|
||||
} else {
|
||||
resolv->stop_wev(s);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void host_cb(void *arg, int status, int timeouts, hostent *hostent) {
|
||||
auto resolv = static_cast<DNSResolver *>(arg);
|
||||
resolv->on_result(status, hostent);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void process_result(DNSResolver *resolv) {
|
||||
auto cb = resolv->get_complete_cb();
|
||||
if (!cb) {
|
||||
return;
|
||||
}
|
||||
Address result;
|
||||
auto status = resolv->get_status(&result);
|
||||
switch (status) {
|
||||
case DNS_STATUS_OK:
|
||||
case DNS_STATUS_ERROR:
|
||||
cb(status, &result);
|
||||
break;
|
||||
}
|
||||
// resolv may be deleted here.
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void readcb(struct ev_loop *loop, ev_io *w, int revents) {
|
||||
auto resolv = static_cast<DNSResolver *>(w->data);
|
||||
resolv->on_read(w->fd);
|
||||
process_result(resolv);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void writecb(struct ev_loop *loop, ev_io *w, int revents) {
|
||||
auto resolv = static_cast<DNSResolver *>(w->data);
|
||||
resolv->on_write(w->fd);
|
||||
process_result(resolv);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
|
||||
auto resolv = static_cast<DNSResolver *>(w->data);
|
||||
resolv->on_timeout();
|
||||
process_result(resolv);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void stop_ev(struct ev_loop *loop,
|
||||
const std::vector<std::unique_ptr<ev_io>> &evs) {
|
||||
for (auto &w : evs) {
|
||||
ev_io_stop(loop, w.get());
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DNSResolver::DNSResolver(struct ev_loop *loop)
|
||||
: result_{},
|
||||
loop_(loop),
|
||||
channel_(nullptr),
|
||||
family_(AF_UNSPEC),
|
||||
status_(DNS_STATUS_IDLE) {
|
||||
ev_timer_init(&timer_, timeoutcb, 0., 0.);
|
||||
timer_.data = this;
|
||||
}
|
||||
|
||||
DNSResolver::~DNSResolver() {
|
||||
if (channel_) {
|
||||
ares_destroy(channel_);
|
||||
}
|
||||
|
||||
stop_ev(loop_, revs_);
|
||||
stop_ev(loop_, wevs_);
|
||||
|
||||
ev_timer_stop(loop_, &timer_);
|
||||
}
|
||||
|
||||
int DNSResolver::resolve(const StringRef &name, int family) {
|
||||
if (status_ != DNS_STATUS_IDLE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "Start resolving host " << name << " in IPv"
|
||||
<< (family == AF_INET ? "4" : "6");
|
||||
}
|
||||
|
||||
name_ = name;
|
||||
family_ = family;
|
||||
|
||||
int rv;
|
||||
|
||||
auto &dnsconf = get_config()->dns;
|
||||
|
||||
ares_options opts{};
|
||||
opts.sock_state_cb = sock_state_cb;
|
||||
opts.sock_state_cb_data = this;
|
||||
opts.timeout = static_cast<int>(dnsconf.timeout.lookup * 1000);
|
||||
opts.tries = dnsconf.max_try;
|
||||
|
||||
auto optmask = ARES_OPT_SOCK_STATE_CB | ARES_OPT_TIMEOUTMS | ARES_OPT_TRIES;
|
||||
|
||||
ares_channel chan;
|
||||
rv = ares_init_options(&chan, &opts, optmask);
|
||||
if (rv != ARES_SUCCESS) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "ares_init_options failed: " << ares_strerror(rv);
|
||||
}
|
||||
status_ = DNS_STATUS_ERROR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
channel_ = chan;
|
||||
status_ = DNS_STATUS_RUNNING;
|
||||
|
||||
ares_gethostbyname(channel_, name_.c_str(), family_, host_cb, this);
|
||||
reset_timeout();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int DNSResolver::on_read(int fd) { return handle_event(fd, ARES_SOCKET_BAD); }
|
||||
|
||||
int DNSResolver::on_write(int fd) { return handle_event(ARES_SOCKET_BAD, fd); }
|
||||
|
||||
int DNSResolver::on_timeout() {
|
||||
return handle_event(ARES_SOCKET_BAD, ARES_SOCKET_BAD);
|
||||
}
|
||||
|
||||
int DNSResolver::handle_event(int rfd, int wfd) {
|
||||
if (status_ == DNS_STATUS_IDLE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ares_process_fd(channel_, rfd, wfd);
|
||||
|
||||
switch (status_) {
|
||||
case DNS_STATUS_RUNNING: {
|
||||
reset_timeout();
|
||||
return 0;
|
||||
}
|
||||
case DNS_STATUS_OK:
|
||||
return 0;
|
||||
case DNS_STATUS_ERROR:
|
||||
return -1;
|
||||
default:
|
||||
// Unreachable
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
void DNSResolver::reset_timeout() {
|
||||
if (status_ != DNS_STATUS_RUNNING) {
|
||||
return;
|
||||
}
|
||||
timeval tvout;
|
||||
auto tv = ares_timeout(channel_, nullptr, &tvout);
|
||||
if (tv == nullptr) {
|
||||
return;
|
||||
}
|
||||
// To avoid that timer_.repeat becomes 0, which makes ev_timer_again
|
||||
// useless, add tiny fraction of time.
|
||||
timer_.repeat = tv->tv_sec + tv->tv_usec / 1000000. + 1e-9;
|
||||
ev_timer_again(loop_, &timer_);
|
||||
}
|
||||
|
||||
int DNSResolver::get_status(Address *result) const {
|
||||
if (status_ != DNS_STATUS_OK) {
|
||||
return status_;
|
||||
}
|
||||
|
||||
if (result) {
|
||||
memcpy(result, &result_, sizeof(result_));
|
||||
}
|
||||
|
||||
return status_;
|
||||
}
|
||||
|
||||
namespace {
|
||||
void start_ev(std::vector<std::unique_ptr<ev_io>> &evs, struct ev_loop *loop,
|
||||
int fd, int event, IOCb cb, void *data) {
|
||||
for (auto &w : evs) {
|
||||
if (w->fd == fd) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (auto &w : evs) {
|
||||
if (w->fd == -1) {
|
||||
ev_io_set(w.get(), fd, event);
|
||||
ev_io_start(loop, w.get());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto w = make_unique<ev_io>();
|
||||
ev_io_init(w.get(), cb, fd, event);
|
||||
w->data = data;
|
||||
ev_io_start(loop, w.get());
|
||||
evs.emplace_back(std::move(w));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void stop_ev(std::vector<std::unique_ptr<ev_io>> &evs, struct ev_loop *loop,
|
||||
int fd, int event) {
|
||||
for (auto &w : evs) {
|
||||
if (w->fd == fd) {
|
||||
ev_io_stop(loop, w.get());
|
||||
ev_io_set(w.get(), -1, event);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void DNSResolver::start_rev(int fd) {
|
||||
start_ev(revs_, loop_, fd, EV_READ, readcb, this);
|
||||
}
|
||||
|
||||
void DNSResolver::stop_rev(int fd) { stop_ev(revs_, loop_, fd, EV_READ); }
|
||||
|
||||
void DNSResolver::start_wev(int fd) {
|
||||
start_ev(wevs_, loop_, fd, EV_WRITE, writecb, this);
|
||||
}
|
||||
|
||||
void DNSResolver::stop_wev(int fd) { stop_ev(wevs_, loop_, fd, EV_WRITE); }
|
||||
|
||||
void DNSResolver::on_result(int status, hostent *hostent) {
|
||||
stop_ev(loop_, revs_);
|
||||
stop_ev(loop_, wevs_);
|
||||
ev_timer_stop(loop_, &timer_);
|
||||
|
||||
if (status != ARES_SUCCESS) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "Name lookup for " << name_
|
||||
<< " failed: " << ares_strerror(status);
|
||||
}
|
||||
status_ = DNS_STATUS_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
auto ap = *hostent->h_addr_list;
|
||||
if (!ap) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "Name lookup for " << name_ << "failed: no address returned";
|
||||
}
|
||||
status_ = DNS_STATUS_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (hostent->h_addrtype) {
|
||||
case AF_INET:
|
||||
status_ = DNS_STATUS_OK;
|
||||
result_.len = sizeof(result_.su.in);
|
||||
result_.su.in = {};
|
||||
result_.su.in.sin_family = AF_INET;
|
||||
#ifdef HAVE_SOCKADDR_IN_SIN_LEN
|
||||
result_.su.in.sin_len = sizeof(result_.su.in);
|
||||
#endif // HAVE_SOCKADDR_IN_SIN_LEN
|
||||
memcpy(&result_.su.in.sin_addr, ap, sizeof(result_.su.in.sin_addr));
|
||||
break;
|
||||
case AF_INET6:
|
||||
status_ = DNS_STATUS_OK;
|
||||
result_.len = sizeof(result_.su.in6);
|
||||
result_.su.in6 = {};
|
||||
result_.su.in6.sin6_family = AF_INET6;
|
||||
#ifdef HAVE_SOCKADDR_IN6_SIN6_LEN
|
||||
result_.su.in6.sin6_len = sizeof(result_.su.in6);
|
||||
#endif // HAVE_SOCKADDR_IN6_SIN6_LEN
|
||||
memcpy(&result_.su.in6.sin6_addr, ap, sizeof(result_.su.in6.sin6_addr));
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
if (status_ == DNS_STATUS_OK) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "Name lookup succeeded: " << name_ << " -> "
|
||||
<< util::numeric_name(&result_.su.sa, result_.len);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
status_ = DNS_STATUS_ERROR;
|
||||
}
|
||||
|
||||
void DNSResolver::set_complete_cb(CompleteCb cb) {
|
||||
completeCb_ = std::move(cb);
|
||||
}
|
||||
|
||||
CompleteCb DNSResolver::get_complete_cb() const { return completeCb_; }
|
||||
|
||||
} // namespace shrpx
|
||||
116
src/shrpx_dns_resolver.h
Normal file
116
src/shrpx_dns_resolver.h
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* 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 SHRPX_DNS_RESOLVER_H
|
||||
#define SHRPX_DNS_RESOLVER_H
|
||||
|
||||
#include "shrpx.h"
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <ev.h>
|
||||
#include <ares.h>
|
||||
|
||||
#include "template.h"
|
||||
#include "network.h"
|
||||
|
||||
using namespace nghttp2;
|
||||
|
||||
namespace shrpx {
|
||||
|
||||
enum DNSResolverStatus {
|
||||
// Resolver is in initial status
|
||||
DNS_STATUS_IDLE,
|
||||
// Resolver is currently resolving host name
|
||||
DNS_STATUS_RUNNING,
|
||||
// Resolver successfully resolved host name
|
||||
DNS_STATUS_OK,
|
||||
// Resolver failed to resolve host name
|
||||
DNS_STATUS_ERROR,
|
||||
};
|
||||
|
||||
// Callback function called when host name lookup is finished.
|
||||
// |status| is either DNS_STATUS_OK, or DNS_STATUS_ERROR. If |status|
|
||||
// is DNS_STATUS_OK, |result| points to the resolved address. Note
|
||||
// that port portion of |result| is undefined, and must be initialized
|
||||
// by application. This callback function is not called if name
|
||||
// lookup finishes in DNSResolver::resolve() completely. In this
|
||||
// case, application should call DNSResolver::get_status() to get
|
||||
// current status and result. In other words, callback is called if
|
||||
// get_status() returns DNS_STATUS_RUNNING.
|
||||
using CompleteCb = std::function<void(int status, const Address *result)>;
|
||||
|
||||
// DNSResolver is asynchronous name resolver, backed by c-ares
|
||||
// library.
|
||||
class DNSResolver {
|
||||
public:
|
||||
DNSResolver(struct ev_loop *loop);
|
||||
~DNSResolver();
|
||||
|
||||
// Starts resolving hostname |name|.
|
||||
int resolve(const StringRef &name, int family);
|
||||
// Returns status. If status_ is DNS_STATUS_SUCCESS && |result| is
|
||||
// not nullptr, |*result| is filled.
|
||||
int get_status(Address *result) const;
|
||||
// Sets callback function when name lookup finishes. The callback
|
||||
// function is called in a way that it can destroy this DNSResolver.
|
||||
void set_complete_cb(CompleteCb cb);
|
||||
CompleteCb get_complete_cb() const;
|
||||
|
||||
// Calls these functions when read/write event occurred respectively.
|
||||
int on_read(int fd);
|
||||
int on_write(int fd);
|
||||
int on_timeout();
|
||||
// Calls this function when DNS query finished.
|
||||
void on_result(int staus, hostent *hostent);
|
||||
void reset_timeout();
|
||||
|
||||
void start_rev(int fd);
|
||||
void stop_rev(int fd);
|
||||
void start_wev(int fd);
|
||||
void stop_wev(int fd);
|
||||
|
||||
private:
|
||||
int handle_event(int rfd, int wfd);
|
||||
|
||||
std::vector<std::unique_ptr<ev_io>> revs_, wevs_;
|
||||
Address result_;
|
||||
CompleteCb completeCb_;
|
||||
ev_timer timer_;
|
||||
StringRef name_;
|
||||
struct ev_loop *loop_;
|
||||
// ares_channel is pointer type
|
||||
ares_channel channel_;
|
||||
// AF_INET or AF_INET6. AF_INET for A record lookup, and AF_INET6
|
||||
// for AAAA record lookup.
|
||||
int family_;
|
||||
int status_;
|
||||
};
|
||||
|
||||
} // namespace shrpx
|
||||
|
||||
#endif // SHRPX_DNS_RESOLVER_H
|
||||
325
src/shrpx_dns_tracker.cc
Normal file
325
src/shrpx_dns_tracker.cc
Normal file
@@ -0,0 +1,325 @@
|
||||
/*
|
||||
* 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 "shrpx_dns_tracker.h"
|
||||
#include "shrpx_config.h"
|
||||
#include "util.h"
|
||||
|
||||
namespace shrpx {
|
||||
|
||||
namespace {
|
||||
void gccb(struct ev_loop *loop, ev_timer *w, int revents) {
|
||||
auto dns_tracker = static_cast<DNSTracker *>(w->data);
|
||||
dns_tracker->gc();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DNSTracker::DNSTracker(struct ev_loop *loop) : loop_(loop) {
|
||||
ev_timer_init(&gc_timer_, gccb, 0., 12_h);
|
||||
gc_timer_.data = this;
|
||||
}
|
||||
|
||||
DNSTracker::~DNSTracker() {
|
||||
ev_timer_stop(loop_, &gc_timer_);
|
||||
|
||||
for (auto &p : ents_) {
|
||||
auto &qlist = p.second.qlist;
|
||||
while (!qlist.empty()) {
|
||||
auto head = qlist.head;
|
||||
qlist.remove(head);
|
||||
head->status = DNS_STATUS_ERROR;
|
||||
head->in_qlist = false;
|
||||
// TODO Not sure we should call callback here, or it is even be
|
||||
// safe to do that.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ResolverEntry DNSTracker::make_entry(std::unique_ptr<DualDNSResolver> resolv,
|
||||
ImmutableString host, int status,
|
||||
const Address *result) {
|
||||
auto &dnsconf = get_config()->dns;
|
||||
|
||||
auto ent = ResolverEntry{};
|
||||
ent.resolv = std::move(resolv);
|
||||
ent.host = std::move(host);
|
||||
ent.status = status;
|
||||
switch (status) {
|
||||
case DNS_STATUS_ERROR:
|
||||
case DNS_STATUS_OK:
|
||||
ent.expiry = ev_now(loop_) + dnsconf.timeout.cache;
|
||||
break;
|
||||
}
|
||||
if (result) {
|
||||
ent.result = *result;
|
||||
}
|
||||
return ent;
|
||||
}
|
||||
|
||||
void DNSTracker::update_entry(ResolverEntry &ent,
|
||||
std::unique_ptr<DualDNSResolver> resolv,
|
||||
int status, const Address *result) {
|
||||
auto &dnsconf = get_config()->dns;
|
||||
|
||||
ent.resolv = std::move(resolv);
|
||||
ent.status = status;
|
||||
switch (status) {
|
||||
case DNS_STATUS_ERROR:
|
||||
case DNS_STATUS_OK:
|
||||
ent.expiry = ev_now(loop_) + dnsconf.timeout.cache;
|
||||
break;
|
||||
}
|
||||
if (result) {
|
||||
ent.result = *result;
|
||||
}
|
||||
}
|
||||
|
||||
int DNSTracker::resolve(Address *result, DNSQuery *dnsq) {
|
||||
int rv;
|
||||
|
||||
auto it = ents_.find(dnsq->host);
|
||||
|
||||
if (it == std::end(ents_)) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "DNS entry not found for " << dnsq->host;
|
||||
}
|
||||
|
||||
auto resolv = make_unique<DualDNSResolver>(loop_);
|
||||
auto host_copy =
|
||||
ImmutableString{std::begin(dnsq->host), std::end(dnsq->host)};
|
||||
auto host = StringRef{host_copy};
|
||||
|
||||
rv = resolv->resolve(host);
|
||||
if (rv != 0) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "Name lookup failed for " << host;
|
||||
}
|
||||
|
||||
ents_.emplace(host, make_entry(nullptr, std::move(host_copy),
|
||||
DNS_STATUS_ERROR, nullptr));
|
||||
|
||||
start_gc_timer();
|
||||
|
||||
return DNS_STATUS_ERROR;
|
||||
}
|
||||
|
||||
rv = resolv->get_status(result);
|
||||
switch (rv) {
|
||||
case DNS_STATUS_ERROR: {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "Name lookup failed for " << host;
|
||||
}
|
||||
|
||||
ents_.emplace(host, make_entry(nullptr, std::move(host_copy),
|
||||
DNS_STATUS_ERROR, nullptr));
|
||||
|
||||
start_gc_timer();
|
||||
|
||||
return DNS_STATUS_ERROR;
|
||||
}
|
||||
case DNS_STATUS_OK: {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "Name lookup succeeded: " << host << " -> "
|
||||
<< util::numeric_name(&result->su.sa, result->len);
|
||||
}
|
||||
|
||||
ents_.emplace(host, make_entry(nullptr, std::move(host_copy),
|
||||
DNS_STATUS_OK, result));
|
||||
|
||||
start_gc_timer();
|
||||
|
||||
return DNS_STATUS_OK;
|
||||
}
|
||||
case DNS_STATUS_RUNNING: {
|
||||
assert(rv == DNS_STATUS_RUNNING);
|
||||
|
||||
auto p = ents_.emplace(host,
|
||||
make_entry(std::move(resolv), std::move(host_copy),
|
||||
DNS_STATUS_RUNNING, nullptr));
|
||||
|
||||
start_gc_timer();
|
||||
|
||||
auto &ent = (*p.first).second;
|
||||
|
||||
add_to_qlist(ent, dnsq);
|
||||
|
||||
return DNS_STATUS_RUNNING;
|
||||
}
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
auto &ent = (*it).second;
|
||||
|
||||
if (ent.status != DNS_STATUS_RUNNING && ent.expiry < ev_now(loop_)) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "DNS entry found for " << dnsq->host
|
||||
<< ", but it has been expired";
|
||||
}
|
||||
|
||||
auto resolv = make_unique<DualDNSResolver>(loop_);
|
||||
auto host = StringRef{ent.host};
|
||||
|
||||
rv = resolv->resolve(host);
|
||||
if (rv != 0) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "Name lookup failed for " << host;
|
||||
}
|
||||
|
||||
update_entry(ent, nullptr, DNS_STATUS_ERROR, nullptr);
|
||||
|
||||
return DNS_STATUS_ERROR;
|
||||
}
|
||||
|
||||
rv = resolv->get_status(result);
|
||||
switch (rv) {
|
||||
case DNS_STATUS_ERROR: {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "Name lookup failed for " << host;
|
||||
}
|
||||
|
||||
update_entry(ent, nullptr, DNS_STATUS_ERROR, nullptr);
|
||||
|
||||
return DNS_STATUS_ERROR;
|
||||
}
|
||||
case DNS_STATUS_OK: {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "Name lookup succeeded: " << host << " -> "
|
||||
<< util::numeric_name(&result->su.sa, result->len);
|
||||
}
|
||||
|
||||
update_entry(ent, nullptr, DNS_STATUS_OK, result);
|
||||
|
||||
return DNS_STATUS_OK;
|
||||
}
|
||||
case DNS_STATUS_RUNNING: {
|
||||
update_entry(ent, std::move(resolv), DNS_STATUS_RUNNING, nullptr);
|
||||
add_to_qlist(ent, dnsq);
|
||||
|
||||
return DNS_STATUS_RUNNING;
|
||||
}
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
switch (ent.status) {
|
||||
case DNS_STATUS_RUNNING:
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "Waiting for name lookup complete for " << dnsq->host;
|
||||
}
|
||||
ent.qlist.append(dnsq);
|
||||
dnsq->in_qlist = true;
|
||||
return DNS_STATUS_RUNNING;
|
||||
case DNS_STATUS_ERROR:
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "Name lookup failed for " << dnsq->host << " (cached)";
|
||||
}
|
||||
return DNS_STATUS_ERROR;
|
||||
case DNS_STATUS_OK:
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "Name lookup succeeded (cached): " << dnsq->host << " -> "
|
||||
<< util::numeric_name(&ent.result.su.sa, ent.result.len);
|
||||
}
|
||||
if (result) {
|
||||
memcpy(result, &ent.result, sizeof(*result));
|
||||
}
|
||||
return DNS_STATUS_OK;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
void DNSTracker::add_to_qlist(ResolverEntry &ent, DNSQuery *dnsq) {
|
||||
auto loop = loop_;
|
||||
ent.resolv->set_complete_cb([&ent, loop](int status, const Address *result) {
|
||||
auto &qlist = ent.qlist;
|
||||
while (!qlist.empty()) {
|
||||
auto head = qlist.head;
|
||||
qlist.remove(head);
|
||||
head->status = status;
|
||||
head->in_qlist = false;
|
||||
auto cb = head->cb;
|
||||
cb(status, result);
|
||||
}
|
||||
|
||||
auto &dnsconf = get_config()->dns;
|
||||
|
||||
ent.resolv.reset();
|
||||
ent.status = status;
|
||||
ent.expiry = ev_now(loop) + dnsconf.timeout.cache;
|
||||
if (ent.status == DNS_STATUS_OK) {
|
||||
ent.result = *result;
|
||||
}
|
||||
});
|
||||
ent.qlist.append(dnsq);
|
||||
dnsq->in_qlist = true;
|
||||
}
|
||||
|
||||
void DNSTracker::cancel(DNSQuery *dnsq) {
|
||||
if (!dnsq->in_qlist) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto it = ents_.find(dnsq->host);
|
||||
if (it == std::end(ents_)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto &ent = (*it).second;
|
||||
ent.qlist.remove(dnsq);
|
||||
dnsq->in_qlist = false;
|
||||
}
|
||||
|
||||
void DNSTracker::start_gc_timer() {
|
||||
if (ev_is_active(&gc_timer_)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ev_timer_again(loop_, &gc_timer_);
|
||||
}
|
||||
|
||||
void DNSTracker::gc() {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "Starting removing expired DNS cache entries";
|
||||
}
|
||||
|
||||
auto now = ev_now(loop_);
|
||||
for (auto it = std::begin(ents_); it != std::end(ents_);) {
|
||||
auto &ent = (*it).second;
|
||||
if (ent.expiry >= now) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
|
||||
it = ents_.erase(it);
|
||||
}
|
||||
|
||||
if (ents_.empty()) {
|
||||
ev_timer_stop(loop_, &gc_timer_);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace shrpx
|
||||
116
src/shrpx_dns_tracker.h
Normal file
116
src/shrpx_dns_tracker.h
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* 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 SHRPX_DNS_TRACKER_H
|
||||
#define SHRPX_DNS_TRACKER_H
|
||||
|
||||
#include "shrpx.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "shrpx_dual_dns_resolver.h"
|
||||
|
||||
using namespace nghttp2;
|
||||
|
||||
namespace shrpx {
|
||||
|
||||
struct DNSQuery {
|
||||
DNSQuery(StringRef host, CompleteCb cb)
|
||||
: host(std::move(host)),
|
||||
cb(std::move(cb)),
|
||||
dlnext(nullptr),
|
||||
dlprev(nullptr),
|
||||
status(DNS_STATUS_IDLE),
|
||||
in_qlist(false) {}
|
||||
|
||||
// Host name we lookup for.
|
||||
StringRef host;
|
||||
// Callback function called when name lookup finished. This
|
||||
// callback is not called if name lookup finishes within
|
||||
// DNSTracker::resolve().
|
||||
CompleteCb cb;
|
||||
DNSQuery *dlnext, *dlprev;
|
||||
int status;
|
||||
// true if this object is in linked list ResolverEntry::qlist.
|
||||
bool in_qlist;
|
||||
};
|
||||
|
||||
struct ResolverEntry {
|
||||
// Host name this entry lookups for.
|
||||
ImmutableString host;
|
||||
// DNS resolver. Only non-nullptr if status is DNS_STATUS_RUNNING.
|
||||
std::unique_ptr<DualDNSResolver> resolv;
|
||||
// DNSQuery interested in this name lookup result. The result is
|
||||
// notified to them all.
|
||||
DList<DNSQuery> qlist;
|
||||
// Use the same enum with DNSResolverStatus
|
||||
int status;
|
||||
// result and its expiry time
|
||||
Address result;
|
||||
// time point when cached result expires.
|
||||
ev_tstamp expiry;
|
||||
};
|
||||
|
||||
class DNSTracker {
|
||||
public:
|
||||
DNSTracker(struct ev_loop *loop);
|
||||
~DNSTracker();
|
||||
|
||||
// Lookups host name described in |dnsq|. If name lookup finishes
|
||||
// within this function (either it came from /etc/hosts, host name
|
||||
// is numeric, lookup result is cached, etc), it returns
|
||||
// DNS_STATUS_OK or DNS_STATUS_ERROR. If lookup is successful,
|
||||
// DNS_STATUS_OK is returned, and |result| is filled. If lookup
|
||||
// failed, DNS_STATUS_ERROR is returned. If name lookup is being
|
||||
// done background, it returns DNS_STATUS_RUNNING. Its completion
|
||||
// is notified by calling dnsq->cb.
|
||||
int resolve(Address *result, DNSQuery *dnsq);
|
||||
// Cancels name lookup requested by |dnsq|.
|
||||
void cancel(DNSQuery *dnsq);
|
||||
// Removes expired entries from ents_.
|
||||
void gc();
|
||||
// Starts GC timer.
|
||||
void start_gc_timer();
|
||||
|
||||
private:
|
||||
ResolverEntry make_entry(std::unique_ptr<DualDNSResolver> resolv,
|
||||
ImmutableString host, int status,
|
||||
const Address *result);
|
||||
|
||||
void update_entry(ResolverEntry &ent, std::unique_ptr<DualDNSResolver> resolv,
|
||||
int status, const Address *result);
|
||||
|
||||
void add_to_qlist(ResolverEntry &ent, DNSQuery *dnsq);
|
||||
|
||||
std::map<StringRef, ResolverEntry> ents_;
|
||||
// Periodically iterates ents_, and removes expired entries to avoid
|
||||
// excessive use of memory. Since only backend API can potentially
|
||||
// increase memory consumption, interval could be very long.
|
||||
ev_timer gc_timer_;
|
||||
struct ev_loop *loop_;
|
||||
};
|
||||
|
||||
} // namespace shrpx
|
||||
|
||||
#endif // SHRPX_DNS_TRACKER_H
|
||||
@@ -138,7 +138,8 @@ Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool,
|
||||
chunked_response_(false),
|
||||
expect_final_response_(false),
|
||||
request_pending_(false),
|
||||
request_header_sent_(false) {
|
||||
request_header_sent_(false),
|
||||
accesslog_written_(false) {
|
||||
|
||||
auto &timeoutconf = get_config()->http2.timeout;
|
||||
|
||||
@@ -350,10 +351,8 @@ void Downstream::crumble_request_cookie(std::vector<nghttp2_nv> &nva) {
|
||||
}
|
||||
|
||||
namespace {
|
||||
void add_header(bool &key_prev, size_t &sum, HeaderRefs &headers,
|
||||
const StringRef &name, const StringRef &value, bool no_index,
|
||||
int32_t token) {
|
||||
key_prev = true;
|
||||
void add_header(size_t &sum, HeaderRefs &headers, const StringRef &name,
|
||||
const StringRef &value, bool no_index, int32_t token) {
|
||||
sum += name.size() + value.size();
|
||||
headers.emplace_back(name, value, no_index, token);
|
||||
}
|
||||
@@ -446,14 +445,14 @@ const HeaderRefs::value_type *FieldStore::header(const StringRef &name) const {
|
||||
|
||||
void FieldStore::add_header_token(const StringRef &name, const StringRef &value,
|
||||
bool no_index, int32_t token) {
|
||||
shrpx::add_header(header_key_prev_, buffer_size_, headers_, name, value,
|
||||
no_index, token);
|
||||
shrpx::add_header(buffer_size_, headers_, name, value, no_index, token);
|
||||
}
|
||||
|
||||
void FieldStore::alloc_add_header_name(const StringRef &name) {
|
||||
auto name_ref = alloc_header_name(balloc_, name);
|
||||
auto token = http2::lookup_token(name_ref);
|
||||
add_header_token(name_ref, StringRef{}, false, token);
|
||||
header_key_prev_ = true;
|
||||
}
|
||||
|
||||
void FieldStore::append_last_header_key(const char *data, size_t len) {
|
||||
@@ -476,14 +475,14 @@ void FieldStore::add_trailer_token(const StringRef &name,
|
||||
int32_t token) {
|
||||
// Header size limit should be applied to all header and trailer
|
||||
// fields combined.
|
||||
shrpx::add_header(trailer_key_prev_, buffer_size_, trailers_, name, value,
|
||||
no_index, token);
|
||||
shrpx::add_header(buffer_size_, trailers_, name, value, no_index, token);
|
||||
}
|
||||
|
||||
void FieldStore::alloc_add_trailer_name(const StringRef &name) {
|
||||
auto name_ref = alloc_header_name(balloc_, name);
|
||||
auto token = http2::lookup_token(name_ref);
|
||||
add_trailer_token(name_ref, StringRef{}, false, token);
|
||||
trailer_key_prev_ = true;
|
||||
}
|
||||
|
||||
void FieldStore::append_last_trailer_key(const char *data, size_t len) {
|
||||
@@ -908,7 +907,9 @@ void Downstream::disable_downstream_wtimer() {
|
||||
disable_timer(loop, &downstream_wtimer_);
|
||||
}
|
||||
|
||||
bool Downstream::accesslog_ready() const { return resp_.http_status > 0; }
|
||||
bool Downstream::accesslog_ready() const {
|
||||
return !accesslog_written_ && resp_.http_status > 0;
|
||||
}
|
||||
|
||||
void Downstream::add_retry() { ++num_retry_; }
|
||||
|
||||
@@ -924,6 +925,10 @@ bool Downstream::get_request_pending() const { return request_pending_; }
|
||||
|
||||
void Downstream::set_request_header_sent(bool f) { request_header_sent_ = f; }
|
||||
|
||||
bool Downstream::get_request_header_sent() const {
|
||||
return request_header_sent_;
|
||||
}
|
||||
|
||||
bool Downstream::request_submission_ready() const {
|
||||
return (request_state_ == Downstream::HEADER_COMPLETE ||
|
||||
request_state_ == Downstream::MSG_COMPLETE) &&
|
||||
@@ -983,4 +988,6 @@ void Downstream::set_addr(const DownstreamAddr *addr) { addr_ = addr; }
|
||||
|
||||
const DownstreamAddr *Downstream::get_addr() const { return addr_; }
|
||||
|
||||
void Downstream::set_accesslog_written(bool f) { accesslog_written_ = f; }
|
||||
|
||||
} // namespace shrpx
|
||||
|
||||
@@ -76,7 +76,6 @@ public:
|
||||
// Returns pointer to the header field with the name |name|. If
|
||||
// multiple header have |name| as name, return last occurrence from
|
||||
// the beginning. If no such header is found, returns nullptr.
|
||||
// This function must be called after headers are indexed
|
||||
const HeaderRefs::value_type *header(int32_t token) const;
|
||||
HeaderRefs::value_type *header(int32_t token);
|
||||
// Returns pointer to the header field with the name |name|. If no
|
||||
@@ -153,6 +152,8 @@ struct Request {
|
||||
}
|
||||
|
||||
FieldStore fs;
|
||||
// Timestamp when all request header fields are received.
|
||||
std::shared_ptr<Timestamp> tstamp;
|
||||
// Request scheme. For HTTP/2, this is :scheme header field value.
|
||||
// For HTTP/1.1, this is deduced from URI or connection.
|
||||
StringRef scheme;
|
||||
@@ -313,6 +314,7 @@ public:
|
||||
void set_request_pending(bool f);
|
||||
bool get_request_pending() const;
|
||||
void set_request_header_sent(bool f);
|
||||
bool get_request_header_sent() const;
|
||||
// Returns true if request is ready to be submitted to downstream.
|
||||
// When sending pending request, get_request_pending() should be
|
||||
// checked too because this function may return true when
|
||||
@@ -404,6 +406,8 @@ public:
|
||||
|
||||
const DownstreamAddr *get_addr() const;
|
||||
|
||||
void set_accesslog_written(bool f);
|
||||
|
||||
enum {
|
||||
EVENT_ERROR = 0x1,
|
||||
EVENT_TIMEOUT = 0x2,
|
||||
@@ -487,6 +491,8 @@ private:
|
||||
bool request_pending_;
|
||||
// true if downstream request header is considered to be sent.
|
||||
bool request_header_sent_;
|
||||
// true if access.log has been written.
|
||||
bool accesslog_written_;
|
||||
};
|
||||
|
||||
} // namespace shrpx
|
||||
|
||||
87
src/shrpx_dual_dns_resolver.cc
Normal file
87
src/shrpx_dual_dns_resolver.cc
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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 "shrpx_dual_dns_resolver.h"
|
||||
|
||||
namespace shrpx {
|
||||
|
||||
DualDNSResolver::DualDNSResolver(struct ev_loop *loop)
|
||||
: resolv4_(loop), resolv6_(loop) {
|
||||
auto cb = [this](int, const Address *) {
|
||||
int rv;
|
||||
Address result;
|
||||
|
||||
rv = this->get_status(&result);
|
||||
switch (rv) {
|
||||
case DNS_STATUS_ERROR:
|
||||
case DNS_STATUS_OK:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
auto cb = this->get_complete_cb();
|
||||
cb(rv, &result);
|
||||
};
|
||||
|
||||
resolv4_.set_complete_cb(cb);
|
||||
resolv6_.set_complete_cb(cb);
|
||||
}
|
||||
|
||||
int DualDNSResolver::resolve(const StringRef &host) {
|
||||
int rv4, rv6;
|
||||
rv4 = resolv4_.resolve(host, AF_INET);
|
||||
rv6 = resolv6_.resolve(host, AF_INET6);
|
||||
|
||||
if (rv4 != 0 && rv6 != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
CompleteCb DualDNSResolver::get_complete_cb() const { return complete_cb_; }
|
||||
|
||||
void DualDNSResolver::set_complete_cb(CompleteCb cb) { complete_cb_ = cb; }
|
||||
|
||||
int DualDNSResolver::get_status(Address *result) const {
|
||||
int rv4, rv6;
|
||||
rv6 = resolv6_.get_status(result);
|
||||
if (rv6 == DNS_STATUS_OK) {
|
||||
return DNS_STATUS_OK;
|
||||
}
|
||||
rv4 = resolv4_.get_status(result);
|
||||
if (rv4 == DNS_STATUS_OK) {
|
||||
return DNS_STATUS_OK;
|
||||
}
|
||||
if (rv4 == DNS_STATUS_RUNNING || rv6 == DNS_STATUS_RUNNING) {
|
||||
return DNS_STATUS_RUNNING;
|
||||
}
|
||||
if (rv4 == DNS_STATUS_ERROR || rv6 == DNS_STATUS_ERROR) {
|
||||
return DNS_STATUS_ERROR;
|
||||
}
|
||||
return DNS_STATUS_IDLE;
|
||||
}
|
||||
|
||||
} // namespace shrpx
|
||||
63
src/shrpx_dual_dns_resolver.h
Normal file
63
src/shrpx_dual_dns_resolver.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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 SHRPX_DUAL_DNS_RESOLVER_H
|
||||
#define SHRPX_DUAL_DNS_RESOLVER_H
|
||||
|
||||
#include "shrpx.h"
|
||||
|
||||
#include <ev.h>
|
||||
|
||||
#include "shrpx_dns_resolver.h"
|
||||
|
||||
using namespace nghttp2;
|
||||
|
||||
namespace shrpx {
|
||||
|
||||
// DualDNSResolver performs name resolution for both A and AAAA
|
||||
// records at the same time. The first successful return (or if we
|
||||
// have both successful results, prefer to AAAA) is chosen. This is
|
||||
// wrapper around 2 DNSResolver inside. resolve(), get_status(), and
|
||||
// how CompleteCb is called have the same semantics with DNSResolver.
|
||||
class DualDNSResolver {
|
||||
public:
|
||||
DualDNSResolver(struct ev_loop *loop);
|
||||
|
||||
// Resolves |host|. |host| must be NULL-terminated string.
|
||||
int resolve(const StringRef &host);
|
||||
CompleteCb get_complete_cb() const;
|
||||
void set_complete_cb(CompleteCb cb);
|
||||
int get_status(Address *result) const;
|
||||
|
||||
private:
|
||||
// For A record
|
||||
DNSResolver resolv4_;
|
||||
// For AAAA record
|
||||
DNSResolver resolv6_;
|
||||
CompleteCb complete_cb_;
|
||||
};
|
||||
|
||||
} // namespace shrpx
|
||||
|
||||
#endif // SHRPX_DUAL_DNS_RESOLVER_H
|
||||
@@ -37,6 +37,7 @@ enum ErrorCode {
|
||||
SHRPX_ERR_EOF = -101,
|
||||
SHRPX_ERR_INPROGRESS = -102,
|
||||
SHRPX_ERR_DCONN_CANCELED = -103,
|
||||
SHRPX_ERR_RETRY = -104,
|
||||
};
|
||||
|
||||
} // namespace shrpx
|
||||
|
||||
@@ -83,7 +83,7 @@ int exec_read_command(Process &proc, char *const argv[]) {
|
||||
auto error = errno;
|
||||
LOG(FATAL) << "Unblocking all signals failed: errno=" << error;
|
||||
|
||||
_Exit(EXIT_FAILURE);
|
||||
nghttp2_Exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
dup2(pfd[1], 1);
|
||||
@@ -94,7 +94,7 @@ int exec_read_command(Process &proc, char *const argv[]) {
|
||||
auto error = errno;
|
||||
LOG(ERROR) << "Could not execute command: " << argv[0]
|
||||
<< ", execve() faild, errno=" << error;
|
||||
_Exit(EXIT_FAILURE);
|
||||
nghttp2_Exit(EXIT_FAILURE);
|
||||
}
|
||||
// unreachable
|
||||
}
|
||||
@@ -111,7 +111,7 @@ int exec_read_command(Process &proc, char *const argv[]) {
|
||||
auto error = errno;
|
||||
LOG(FATAL) << "Restoring all signals failed: errno=" << error;
|
||||
|
||||
_Exit(EXIT_FAILURE);
|
||||
nghttp2_Exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (pid == -1) {
|
||||
|
||||
@@ -45,11 +45,14 @@ StringRef create_error_html(BlockAllocator &balloc, unsigned int http_status) {
|
||||
}
|
||||
}
|
||||
|
||||
auto status_string = http2::get_status_string(balloc, http_status);
|
||||
auto status_string = http2::stringify_status(balloc, http_status);
|
||||
auto reason_phrase = http2::get_reason_phrase(http_status);
|
||||
|
||||
return concat_string_ref(
|
||||
balloc, StringRef::from_lit(R"(<!DOCTYPE html><html lang="en"><title>)"),
|
||||
status_string, StringRef::from_lit("</title><body><h1>"), status_string,
|
||||
status_string, StringRef::from_lit(" "), reason_phrase,
|
||||
StringRef::from_lit("</title><body><h1>"), status_string,
|
||||
StringRef::from_lit(" "), reason_phrase,
|
||||
StringRef::from_lit("</h1><footer>"), httpconf.server_name,
|
||||
StringRef::from_lit("</footer></body></html>"));
|
||||
}
|
||||
|
||||
@@ -402,7 +402,7 @@ int Http2DownstreamConnection::push_request_headers() {
|
||||
// HTTP/1 upstream request can contain keyword other than
|
||||
// "trailers". We just forward "trailers".
|
||||
// TODO more strict handling required here.
|
||||
if (te && util::strifind(te->value, StringRef::from_lit("trailers"))) {
|
||||
if (te && http2::contains_trailers(te->value)) {
|
||||
nva.push_back(http2::make_nv_ll("te", "trailers"));
|
||||
}
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ void settings_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
|
||||
SSLOG(INFO, http2session) << "SETTINGS timeout";
|
||||
}
|
||||
|
||||
downstream_failure(http2session->get_addr());
|
||||
downstream_failure(http2session->get_addr(), http2session->get_raddr());
|
||||
|
||||
if (http2session->terminate_session(NGHTTP2_SETTINGS_TIMEOUT) != 0) {
|
||||
delete http2session;
|
||||
@@ -111,6 +111,10 @@ void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
|
||||
auto conn = static_cast<Connection *>(w->data);
|
||||
auto http2session = static_cast<Http2Session *>(conn->data);
|
||||
|
||||
if (w == &conn->rt && !conn->expired_rt()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
SSLOG(INFO, http2session) << "Timeout";
|
||||
}
|
||||
@@ -198,6 +202,7 @@ Http2Session::Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx,
|
||||
group_(group),
|
||||
addr_(addr),
|
||||
session_(nullptr),
|
||||
raddr_(nullptr),
|
||||
state_(DISCONNECTED),
|
||||
connection_check_state_(CONNECTION_CHECK_NONE),
|
||||
freelist_zone_(FREELIST_ZONE_NONE) {
|
||||
@@ -240,6 +245,11 @@ int Http2Session::disconnect(bool hard) {
|
||||
|
||||
wb_.reset();
|
||||
|
||||
if (dns_query_) {
|
||||
auto dns_tracker = worker_->get_dns_tracker();
|
||||
dns_tracker->cancel(dns_query_.get());
|
||||
}
|
||||
|
||||
conn_.rlimit.stopw();
|
||||
conn_.wlimit.stopw();
|
||||
|
||||
@@ -298,12 +308,47 @@ int Http2Session::disconnect(bool hard) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Http2Session::resolve_name() {
|
||||
int rv;
|
||||
|
||||
auto dns_query = make_unique<DNSQuery>(
|
||||
addr_->host, [this](int status, const Address *result) {
|
||||
int rv;
|
||||
|
||||
if (status == DNS_STATUS_OK) {
|
||||
*resolved_addr_ = *result;
|
||||
util::set_port(*this->resolved_addr_, this->addr_->port);
|
||||
}
|
||||
|
||||
rv = this->initiate_connection();
|
||||
if (rv != 0) {
|
||||
delete this;
|
||||
}
|
||||
});
|
||||
resolved_addr_ = make_unique<Address>();
|
||||
auto dns_tracker = worker_->get_dns_tracker();
|
||||
rv = dns_tracker->resolve(resolved_addr_.get(), dns_query.get());
|
||||
switch (rv) {
|
||||
case DNS_STATUS_ERROR:
|
||||
return -1;
|
||||
case DNS_STATUS_RUNNING:
|
||||
dns_query_ = std::move(dns_query);
|
||||
state_ = RESOLVING_NAME;
|
||||
return 0;
|
||||
case DNS_STATUS_OK:
|
||||
util::set_port(*resolved_addr_, addr_->port);
|
||||
return 0;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
int Http2Session::initiate_connection() {
|
||||
int rv = 0;
|
||||
|
||||
auto worker_blocker = worker_->get_connect_blocker();
|
||||
|
||||
if (state_ == DISCONNECTED) {
|
||||
if (state_ == DISCONNECTED || state_ == RESOLVING_NAME) {
|
||||
if (worker_blocker->blocked()) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
SSLOG(INFO, this)
|
||||
@@ -346,6 +391,8 @@ int Http2Session::initiate_connection() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
raddr_ = &proxy.addr;
|
||||
|
||||
worker_blocker->on_success();
|
||||
|
||||
ev_io_set(&conn_.rev, conn_.fd, EV_READ);
|
||||
@@ -370,13 +417,17 @@ int Http2Session::initiate_connection() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (state_ == DISCONNECTED || state_ == PROXY_CONNECTED) {
|
||||
if (state_ == DISCONNECTED || state_ == PROXY_CONNECTED ||
|
||||
state_ == RESOLVING_NAME) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
if (state_ != RESOLVING_NAME) {
|
||||
SSLOG(INFO, this) << "Connecting to downstream server";
|
||||
}
|
||||
}
|
||||
if (addr_->tls) {
|
||||
assert(ssl_ctx_);
|
||||
|
||||
if (state_ != RESOLVING_NAME) {
|
||||
auto ssl = ssl::create_ssl(ssl_ctx_);
|
||||
if (!ssl) {
|
||||
return -1;
|
||||
@@ -401,18 +452,45 @@ int Http2Session::initiate_connection() {
|
||||
SSL_set_session(conn_.tls.ssl, tls_session);
|
||||
SSL_SESSION_free(tls_session);
|
||||
}
|
||||
}
|
||||
|
||||
if (state_ == DISCONNECTED) {
|
||||
if (addr_->dns) {
|
||||
rv = resolve_name();
|
||||
if (rv != 0) {
|
||||
downstream_failure(addr_, nullptr);
|
||||
return -1;
|
||||
}
|
||||
if (state_ == RESOLVING_NAME) {
|
||||
return 0;
|
||||
}
|
||||
raddr_ = resolved_addr_.get();
|
||||
} else {
|
||||
raddr_ = &addr_->addr;
|
||||
}
|
||||
}
|
||||
|
||||
if (state_ == RESOLVING_NAME) {
|
||||
if (dns_query_->status == DNS_STATUS_ERROR) {
|
||||
downstream_failure(addr_, nullptr);
|
||||
return -1;
|
||||
}
|
||||
assert(dns_query_->status == DNS_STATUS_OK);
|
||||
state_ = DISCONNECTED;
|
||||
dns_query_.reset();
|
||||
raddr_ = resolved_addr_.get();
|
||||
}
|
||||
|
||||
// If state_ == PROXY_CONNECTED, we has connected to the proxy
|
||||
// using conn_.fd and tunnel has been established.
|
||||
if (state_ == DISCONNECTED) {
|
||||
assert(conn_.fd == -1);
|
||||
|
||||
conn_.fd =
|
||||
util::create_nonblock_socket(addr_->addr.su.storage.ss_family);
|
||||
conn_.fd = util::create_nonblock_socket(raddr_->su.storage.ss_family);
|
||||
if (conn_.fd == -1) {
|
||||
auto error = errno;
|
||||
SSLOG(WARN, this)
|
||||
<< "socket() failed; addr=" << util::to_numeric_addr(&addr_->addr)
|
||||
<< "socket() failed; addr=" << util::to_numeric_addr(raddr_)
|
||||
<< ", errno=" << error;
|
||||
|
||||
worker_blocker->on_failure();
|
||||
@@ -423,15 +501,14 @@ int Http2Session::initiate_connection() {
|
||||
|
||||
rv = connect(conn_.fd,
|
||||
// TODO maybe not thread-safe?
|
||||
const_cast<sockaddr *>(&addr_->addr.su.sa),
|
||||
addr_->addr.len);
|
||||
const_cast<sockaddr *>(&raddr_->su.sa), raddr_->len);
|
||||
if (rv != 0 && errno != EINPROGRESS) {
|
||||
auto error = errno;
|
||||
SSLOG(WARN, this) << "connect() failed; addr="
|
||||
<< util::to_numeric_addr(&addr_->addr)
|
||||
SSLOG(WARN, this)
|
||||
<< "connect() failed; addr=" << util::to_numeric_addr(raddr_)
|
||||
<< ", errno=" << error;
|
||||
|
||||
downstream_failure(addr_);
|
||||
downstream_failure(addr_, raddr_);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -441,17 +518,44 @@ int Http2Session::initiate_connection() {
|
||||
|
||||
conn_.prepare_client_handshake();
|
||||
} else {
|
||||
if (state_ == DISCONNECTED) {
|
||||
// Without TLS and proxy.
|
||||
if (addr_->dns) {
|
||||
rv = resolve_name();
|
||||
if (rv != 0) {
|
||||
downstream_failure(addr_, nullptr);
|
||||
return -1;
|
||||
}
|
||||
if (state_ == RESOLVING_NAME) {
|
||||
return 0;
|
||||
}
|
||||
raddr_ = resolved_addr_.get();
|
||||
} else {
|
||||
raddr_ = &addr_->addr;
|
||||
}
|
||||
}
|
||||
|
||||
if (state_ == RESOLVING_NAME) {
|
||||
if (dns_query_->status == DNS_STATUS_ERROR) {
|
||||
downstream_failure(addr_, nullptr);
|
||||
return -1;
|
||||
}
|
||||
assert(dns_query_->status == DNS_STATUS_OK);
|
||||
state_ = DISCONNECTED;
|
||||
dns_query_.reset();
|
||||
raddr_ = resolved_addr_.get();
|
||||
}
|
||||
|
||||
if (state_ == DISCONNECTED) {
|
||||
// Without TLS and proxy.
|
||||
assert(conn_.fd == -1);
|
||||
|
||||
conn_.fd =
|
||||
util::create_nonblock_socket(addr_->addr.su.storage.ss_family);
|
||||
conn_.fd = util::create_nonblock_socket(raddr_->su.storage.ss_family);
|
||||
|
||||
if (conn_.fd == -1) {
|
||||
auto error = errno;
|
||||
SSLOG(WARN, this)
|
||||
<< "socket() failed; addr=" << util::to_numeric_addr(&addr_->addr)
|
||||
<< "socket() failed; addr=" << util::to_numeric_addr(raddr_)
|
||||
<< ", errno=" << error;
|
||||
|
||||
worker_blocker->on_failure();
|
||||
@@ -460,15 +564,15 @@ int Http2Session::initiate_connection() {
|
||||
|
||||
worker_blocker->on_success();
|
||||
|
||||
rv = connect(conn_.fd, const_cast<sockaddr *>(&addr_->addr.su.sa),
|
||||
addr_->addr.len);
|
||||
rv = connect(conn_.fd, const_cast<sockaddr *>(&raddr_->su.sa),
|
||||
raddr_->len);
|
||||
if (rv != 0 && errno != EINPROGRESS) {
|
||||
auto error = errno;
|
||||
SSLOG(WARN, this) << "connect() failed; addr="
|
||||
<< util::to_numeric_addr(&addr_->addr)
|
||||
SSLOG(WARN, this)
|
||||
<< "connect() failed; addr=" << util::to_numeric_addr(raddr_)
|
||||
<< ", errno=" << error;
|
||||
|
||||
downstream_failure(addr_);
|
||||
downstream_failure(addr_, raddr_);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -477,25 +581,21 @@ int Http2Session::initiate_connection() {
|
||||
}
|
||||
}
|
||||
|
||||
write_ = &Http2Session::connected;
|
||||
|
||||
on_write_ = &Http2Session::downstream_write;
|
||||
on_read_ = &Http2Session::downstream_read;
|
||||
|
||||
// We have been already connected when no TLS and proxy is used.
|
||||
if (state_ != CONNECTED) {
|
||||
if (state_ == PROXY_CONNECTED) {
|
||||
return connected();
|
||||
}
|
||||
|
||||
write_ = &Http2Session::connected;
|
||||
|
||||
state_ = CONNECTING;
|
||||
conn_.wlimit.startw();
|
||||
|
||||
conn_.wt.repeat = downstreamconf.timeout.connect;
|
||||
ev_timer_again(conn_.loop, &conn_.wt);
|
||||
} else {
|
||||
conn_.rlimit.startw();
|
||||
|
||||
if (addr_->num_dconn == 0) {
|
||||
ev_timer_again(conn_.loop, &conn_.rt);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -538,7 +638,7 @@ int htp_hdrs_completecb(http_parser *htp) {
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
http_parser_settings htp_hooks = {
|
||||
constexpr http_parser_settings htp_hooks = {
|
||||
nullptr, // http_cb on_message_begin;
|
||||
nullptr, // http_data_cb on_url;
|
||||
nullptr, // http_data_cb on_status;
|
||||
@@ -615,8 +715,6 @@ int Http2Session::downstream_connect_proxy() {
|
||||
void Http2Session::add_downstream_connection(Http2DownstreamConnection *dconn) {
|
||||
dconns_.append(dconn);
|
||||
++addr_->num_dconn;
|
||||
|
||||
stop_read_timer();
|
||||
}
|
||||
|
||||
void Http2Session::remove_downstream_connection(
|
||||
@@ -625,10 +723,6 @@ void Http2Session::remove_downstream_connection(
|
||||
dconns_.remove(dconn);
|
||||
dconn->detach_stream_data();
|
||||
|
||||
if (addr_->num_dconn == 0) {
|
||||
repeat_read_timer();
|
||||
}
|
||||
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
SSLOG(INFO, this) << "Remove downstream";
|
||||
}
|
||||
@@ -996,11 +1090,15 @@ int on_response_headers(Http2Session *http2session, Downstream *downstream,
|
||||
int rv;
|
||||
|
||||
auto upstream = downstream->get_upstream();
|
||||
auto handler = upstream->get_client_handler();
|
||||
const auto &req = downstream->request();
|
||||
auto &resp = downstream->response();
|
||||
|
||||
auto &nva = resp.fs.headers();
|
||||
|
||||
auto config = get_config();
|
||||
auto &loggingconf = config->logging;
|
||||
|
||||
downstream->set_expect_final_response(false);
|
||||
|
||||
auto status = resp.fs.header(http2::HD__STATUS);
|
||||
@@ -1053,7 +1151,7 @@ int on_response_headers(Http2Session *http2session, Downstream *downstream,
|
||||
// On upgrade sucess, both ends can send data
|
||||
if (upstream->resume_read(SHRPX_NO_BUFFER, downstream, 0) != 0) {
|
||||
// If resume_read fails, just drop connection. Not ideal.
|
||||
delete upstream->get_client_handler();
|
||||
delete handler;
|
||||
return -1;
|
||||
}
|
||||
downstream->set_request_state(Downstream::HEADER_COMPLETE);
|
||||
@@ -1090,6 +1188,11 @@ int on_response_headers(Http2Session *http2session, Downstream *downstream,
|
||||
resp.headers_only = true;
|
||||
}
|
||||
|
||||
if (loggingconf.access.write_early && downstream->accesslog_ready()) {
|
||||
handler->write_accesslog(downstream);
|
||||
downstream->set_accesslog_written(true);
|
||||
}
|
||||
|
||||
rv = upstream->on_downstream_header_complete(downstream);
|
||||
if (rv != 0) {
|
||||
// Handling early return (in other words, response was hijacked by
|
||||
@@ -1550,7 +1653,7 @@ int Http2Session::connection_made() {
|
||||
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||
|
||||
if (!next_proto) {
|
||||
downstream_failure(addr_);
|
||||
downstream_failure(addr_, raddr_);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1559,7 +1662,7 @@ int Http2Session::connection_made() {
|
||||
SSLOG(INFO, this) << "Negotiated next protocol: " << proto;
|
||||
}
|
||||
if (!util::check_h2_is_selected(proto)) {
|
||||
downstream_failure(addr_);
|
||||
downstream_failure(addr_, raddr_);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -1608,24 +1711,9 @@ int Http2Session::connection_made() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto must_terminate =
|
||||
addr_->tls && !nghttp2::ssl::check_http2_requirement(conn_.tls.ssl);
|
||||
|
||||
reset_connection_check_timer(CONNCHK_TIMEOUT);
|
||||
|
||||
if (must_terminate) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "TLSv1.2 was not negotiated. HTTP/2 must not be negotiated.";
|
||||
}
|
||||
|
||||
rv = terminate_session(NGHTTP2_INADEQUATE_SECURITY);
|
||||
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
submit_pending_requests();
|
||||
}
|
||||
|
||||
signal_write();
|
||||
return 0;
|
||||
@@ -1848,10 +1936,10 @@ int Http2Session::connected() {
|
||||
auto sock_error = util::get_socket_error(conn_.fd);
|
||||
if (sock_error != 0) {
|
||||
SSLOG(WARN, this) << "Backend connect failed; addr="
|
||||
<< util::to_numeric_addr(&addr_->addr)
|
||||
<< util::to_numeric_addr(raddr_)
|
||||
<< ": errno=" << sock_error;
|
||||
|
||||
downstream_failure(addr_);
|
||||
downstream_failure(addr_, raddr_);
|
||||
|
||||
return -1;
|
||||
}
|
||||
@@ -1867,6 +1955,7 @@ int Http2Session::connected() {
|
||||
ev_timer_again(conn_.loop, &conn_.wt);
|
||||
|
||||
conn_.rlimit.startw();
|
||||
conn_.again_rt();
|
||||
|
||||
read_ = &Http2Session::read_clear;
|
||||
write_ = &Http2Session::write_clear;
|
||||
@@ -1891,6 +1980,8 @@ int Http2Session::connected() {
|
||||
}
|
||||
|
||||
int Http2Session::read_clear() {
|
||||
conn_.last_read = ev_now(conn_.loop);
|
||||
|
||||
std::array<uint8_t, 16_k> buf;
|
||||
|
||||
for (;;) {
|
||||
@@ -1911,6 +2002,8 @@ int Http2Session::read_clear() {
|
||||
}
|
||||
|
||||
int Http2Session::write_clear() {
|
||||
conn_.last_read = ev_now(conn_.loop);
|
||||
|
||||
std::array<struct iovec, MAX_WR_IOVCNT> iov;
|
||||
|
||||
for (;;) {
|
||||
@@ -1923,7 +2016,12 @@ int Http2Session::write_clear() {
|
||||
}
|
||||
|
||||
if (nwrite < 0) {
|
||||
return nwrite;
|
||||
// We may have pending data in receive buffer which may
|
||||
// contain part of response body. So keep reading. Invoke
|
||||
// read event to get read(2) error just in case.
|
||||
ev_feed_event(conn_.loop, &conn_.rev, EV_READ);
|
||||
write_ = &Http2Session::write_void;
|
||||
break;
|
||||
}
|
||||
|
||||
wb_.drain(nwrite);
|
||||
@@ -1945,7 +2043,7 @@ int Http2Session::write_clear() {
|
||||
}
|
||||
|
||||
int Http2Session::tls_handshake() {
|
||||
ev_timer_again(conn_.loop, &conn_.rt);
|
||||
conn_.last_read = ev_now(conn_.loop);
|
||||
|
||||
ERR_clear_error();
|
||||
|
||||
@@ -1956,7 +2054,7 @@ int Http2Session::tls_handshake() {
|
||||
}
|
||||
|
||||
if (rv < 0) {
|
||||
downstream_failure(addr_);
|
||||
downstream_failure(addr_, raddr_);
|
||||
|
||||
return rv;
|
||||
}
|
||||
@@ -1966,8 +2064,8 @@ int Http2Session::tls_handshake() {
|
||||
}
|
||||
|
||||
if (!get_config()->tls.insecure &&
|
||||
ssl::check_cert(conn_.tls.ssl, addr_) != 0) {
|
||||
downstream_failure(addr_);
|
||||
ssl::check_cert(conn_.tls.ssl, addr_, raddr_) != 0) {
|
||||
downstream_failure(addr_, raddr_);
|
||||
|
||||
return -1;
|
||||
}
|
||||
@@ -1975,8 +2073,8 @@ int Http2Session::tls_handshake() {
|
||||
if (!SSL_session_reused(conn_.tls.ssl)) {
|
||||
auto tls_session = SSL_get0_session(conn_.tls.ssl);
|
||||
if (tls_session) {
|
||||
ssl::try_cache_tls_session(addr_->tls_session_cache, addr_->addr,
|
||||
tls_session, ev_now(conn_.loop));
|
||||
ssl::try_cache_tls_session(addr_->tls_session_cache, *raddr_, tls_session,
|
||||
ev_now(conn_.loop));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1992,6 +2090,8 @@ int Http2Session::tls_handshake() {
|
||||
}
|
||||
|
||||
int Http2Session::read_tls() {
|
||||
conn_.last_read = ev_now(conn_.loop);
|
||||
|
||||
std::array<uint8_t, 16_k> buf;
|
||||
|
||||
ERR_clear_error();
|
||||
@@ -2014,6 +2114,8 @@ int Http2Session::read_tls() {
|
||||
}
|
||||
|
||||
int Http2Session::write_tls() {
|
||||
conn_.last_read = ev_now(conn_.loop);
|
||||
|
||||
ERR_clear_error();
|
||||
|
||||
struct iovec iov;
|
||||
@@ -2029,7 +2131,12 @@ int Http2Session::write_tls() {
|
||||
}
|
||||
|
||||
if (nwrite < 0) {
|
||||
return nwrite;
|
||||
// We may have pending data in receive buffer which may
|
||||
// contain part of response body. So keep reading. Invoke
|
||||
// read event to get read(2) error just in case.
|
||||
ev_feed_event(conn_.loop, &conn_.rev, EV_READ);
|
||||
write_ = &Http2Session::write_void;
|
||||
break;
|
||||
}
|
||||
|
||||
wb_.drain(nwrite);
|
||||
@@ -2052,6 +2159,11 @@ int Http2Session::write_tls() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Http2Session::write_void() {
|
||||
conn_.wlimit.stopw();
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Http2Session::should_hard_fail() const {
|
||||
switch (state_) {
|
||||
case PROXY_CONNECTING:
|
||||
@@ -2259,9 +2371,9 @@ void Http2Session::on_timeout() {
|
||||
}
|
||||
case CONNECTING: {
|
||||
SSLOG(WARN, this) << "Connect time out; addr="
|
||||
<< util::to_numeric_addr(&addr_->addr);
|
||||
<< util::to_numeric_addr(raddr_);
|
||||
|
||||
downstream_failure(addr_);
|
||||
downstream_failure(addr_, raddr_);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2281,10 +2393,6 @@ void Http2Session::check_retire() {
|
||||
signal_write();
|
||||
}
|
||||
|
||||
void Http2Session::repeat_read_timer() {
|
||||
ev_timer_again(conn_.loop, &conn_.rt);
|
||||
}
|
||||
|
||||
void Http2Session::stop_read_timer() { ev_timer_stop(conn_.loop, &conn_.rt); }
|
||||
const Address *Http2Session::get_raddr() const { return raddr_; }
|
||||
|
||||
} // namespace shrpx
|
||||
|
||||
@@ -50,6 +50,7 @@ class Http2DownstreamConnection;
|
||||
class Worker;
|
||||
struct DownstreamAddrGroup;
|
||||
struct DownstreamAddr;
|
||||
struct DNSQuery;
|
||||
|
||||
struct StreamData {
|
||||
StreamData *dlnext, *dlprev;
|
||||
@@ -81,6 +82,7 @@ public:
|
||||
// associated ClientHandlers will be deleted.
|
||||
int disconnect(bool hard = false);
|
||||
int initiate_connection();
|
||||
int resolve_name();
|
||||
|
||||
void add_downstream_connection(Http2DownstreamConnection *dconn);
|
||||
void remove_downstream_connection(Http2DownstreamConnection *dconn);
|
||||
@@ -112,6 +114,9 @@ public:
|
||||
int tls_handshake();
|
||||
int read_tls();
|
||||
int write_tls();
|
||||
// This is a special write function which just stop write event
|
||||
// watcher.
|
||||
int write_void();
|
||||
|
||||
int downstream_read_proxy(const uint8_t *data, size_t datalen);
|
||||
int downstream_connect_proxy();
|
||||
@@ -203,8 +208,8 @@ public:
|
||||
// shutdown the connection.
|
||||
void check_retire();
|
||||
|
||||
void repeat_read_timer();
|
||||
void stop_read_timer();
|
||||
// Returns address used to connect to backend. Could be nullptr.
|
||||
const Address *get_raddr() const;
|
||||
|
||||
enum {
|
||||
// Disconnected
|
||||
@@ -221,6 +226,8 @@ public:
|
||||
CONNECTED,
|
||||
// Connection is started to fail
|
||||
CONNECT_FAILING,
|
||||
// Resolving host name
|
||||
RESOLVING_NAME,
|
||||
};
|
||||
|
||||
enum {
|
||||
@@ -262,6 +269,13 @@ private:
|
||||
// Address of remote endpoint
|
||||
DownstreamAddr *addr_;
|
||||
nghttp2_session *session_;
|
||||
// Actual remote address used to contact backend. This is initially
|
||||
// nullptr, and may point to either &addr_->addr,
|
||||
// resolved_addr_.get(), or HTTP proxy's address structure.
|
||||
const Address *raddr_;
|
||||
// Resolved IP address if dns parameter is used
|
||||
std::unique_ptr<Address> resolved_addr_;
|
||||
std::unique_ptr<DNSQuery> dns_query_;
|
||||
int state_;
|
||||
int connection_check_state_;
|
||||
int freelist_zone_;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user