mirror of
https://github.com/nghttp2/nghttp2.git
synced 2026-03-28 00:39:18 +08:00
Compare commits
281 Commits
v1.21.0
...
tls13-earl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
66eba46c8e | ||
|
|
abcdca91ba | ||
|
|
5e59577e93 | ||
|
|
8c6612d338 | ||
|
|
b71d9ea58e | ||
|
|
aca99d42f1 | ||
|
|
90a9a804d0 | ||
|
|
97f1735cf5 | ||
|
|
939ad5ddbe | ||
|
|
24d92b979d | ||
|
|
4c92ff1843 | ||
|
|
280db5c6ba | ||
|
|
7fbcb2d005 | ||
|
|
53aeb2c3d7 | ||
|
|
ff200bfcf3 | ||
|
|
fee3151fd2 | ||
|
|
99a85159ae | ||
|
|
2a981a3f56 | ||
|
|
0028275d7b | ||
|
|
ee8bfddfc9 | ||
|
|
194acb1f2c | ||
|
|
43a2a70ae7 | ||
|
|
73344ae9aa | ||
|
|
c479f6122f | ||
|
|
eca0a3025b | ||
|
|
4720c5cb3d | ||
|
|
cd55ab28ab | ||
|
|
d402cfdf16 | ||
|
|
22502182d0 | ||
|
|
05e1fd5e77 | ||
|
|
943d7923f9 | ||
|
|
568ecbfb28 | ||
|
|
f5ddd7f43b | ||
|
|
88abbce7e7 | ||
|
|
16e9036568 | ||
|
|
fa7945c627 | ||
|
|
daca43f0dd | ||
|
|
16bc11e670 | ||
|
|
6f7e94cdba | ||
|
|
61efa15a14 | ||
|
|
8c0ea56bb8 | ||
|
|
549053710b | ||
|
|
6010d39325 | ||
|
|
be5c39a1cf | ||
|
|
b8fda6808b | ||
|
|
e29b9c1261 | ||
|
|
539e27812b | ||
|
|
7008afd40e | ||
|
|
77a41756db | ||
|
|
b15045d60e | ||
|
|
03084f7517 | ||
|
|
60baca27e4 | ||
|
|
86990db236 | ||
|
|
cb376bcd80 | ||
|
|
f2b8edd1e2 | ||
|
|
c4f8afcfde | ||
|
|
1a1a216d5a | ||
|
|
9f80a82c1a | ||
|
|
c573c80bd3 | ||
|
|
3cd6817e21 | ||
|
|
d4a69658a1 | ||
|
|
8e06fe4971 | ||
|
|
aaeeec8f1c | ||
|
|
66d5e24606 | ||
|
|
e8907a073f | ||
|
|
9656ac0254 | ||
|
|
75624617ce | ||
|
|
1a8f6578b3 | ||
|
|
4f0548b018 | ||
|
|
5119e82b93 | ||
|
|
3be5856c82 | ||
|
|
a319143901 | ||
|
|
17c88d60c7 | ||
|
|
7601511fdf | ||
|
|
f507b5eee4 | ||
|
|
93821165be | ||
|
|
aaa0b858e4 | ||
|
|
5fa1938691 | ||
|
|
56ee3d4820 | ||
|
|
c2d9a1ed6f | ||
|
|
fcf9ab2798 | ||
|
|
35e445bd04 | ||
|
|
88ce3c31b7 | ||
|
|
16320a0f81 | ||
|
|
8c72fb3539 | ||
|
|
8ffe389daa | ||
|
|
189a4516a1 | ||
|
|
2576855ded | ||
|
|
7d4d48a35e | ||
|
|
cc6f759190 | ||
|
|
c23fc86a23 | ||
|
|
d2324bdda1 | ||
|
|
6f0ae9d49a | ||
|
|
0389af5724 | ||
|
|
1766e25f45 | ||
|
|
323001238a | ||
|
|
91f062f873 | ||
|
|
650a0cfbff | ||
|
|
e6b8b3d1d3 | ||
|
|
a170023f23 | ||
|
|
4be4c0cddc | ||
|
|
0de9d374df | ||
|
|
0df199198a | ||
|
|
7646e376e0 | ||
|
|
5996798a34 | ||
|
|
6fec532012 | ||
|
|
15713e0b7c | ||
|
|
a6a561af47 | ||
|
|
09c468a4b4 | ||
|
|
bcda1c2409 | ||
|
|
afcd8d9ab1 | ||
|
|
c9b1c91944 | ||
|
|
5d9434eb09 | ||
|
|
1a44b5d52a | ||
|
|
6635ca5e26 | ||
|
|
9c6c78833b | ||
|
|
9a9ab0813c | ||
|
|
0ccaaa48ce | ||
|
|
3f2fe98dd1 | ||
|
|
0d91e9c255 | ||
|
|
af926fbe1f | ||
|
|
83039ae2d4 | ||
|
|
4c53da6961 | ||
|
|
eb306f463e | ||
|
|
788835c5fd | ||
|
|
4d76606fa2 | ||
|
|
1baf7d34b3 | ||
|
|
c78159469a | ||
|
|
b72ca0289c | ||
|
|
46f670f8a2 | ||
|
|
4b44362b9f | ||
|
|
d068a29798 | ||
|
|
0836a51408 | ||
|
|
566cee8fe7 | ||
|
|
e85698e131 | ||
|
|
5f3c541c4c | ||
|
|
3c43e00d8a | ||
|
|
92d686d356 | ||
|
|
0f69e9c825 | ||
|
|
217d979458 | ||
|
|
cc289972fc | ||
|
|
c601e603c2 | ||
|
|
1002c6da1c | ||
|
|
0911337689 | ||
|
|
3bcc416e13 | ||
|
|
65837806f5 | ||
|
|
b0772dcc66 | ||
|
|
c6d65aad3b | ||
|
|
18dd20ce55 | ||
|
|
0f6d76a501 | ||
|
|
0f1320109f | ||
|
|
defa28c618 | ||
|
|
b7c95be47c | ||
|
|
a18d154e0e | ||
|
|
52195a12ee | ||
|
|
59c78d5809 | ||
|
|
be164fc8f9 | ||
|
|
5833ef1efc | ||
|
|
28f88d46f3 | ||
|
|
6ec7683991 | ||
|
|
fb2d8f79d6 | ||
|
|
8f7fa1b1bf | ||
|
|
e5889ce622 | ||
|
|
3a6f83394c | ||
|
|
acf36f3d1a | ||
|
|
63e6a8bab2 | ||
|
|
5361cc6bd1 | ||
|
|
cabac55394 | ||
|
|
db7483ef10 | ||
|
|
4b51ccbefe | ||
|
|
74c2f1257a | ||
|
|
1428a5e3ae | ||
|
|
fe021c1524 | ||
|
|
c57bf21306 | ||
|
|
1743b7d92d | ||
|
|
7f31278c4c | ||
|
|
8401e16a15 | ||
|
|
07fb5854f3 | ||
|
|
b56a99bfba | ||
|
|
b91e4e4df1 | ||
|
|
52a4d6ac31 | ||
|
|
796ab87b14 | ||
|
|
ed1fad3bd4 | ||
|
|
9c1876f542 | ||
|
|
7d111d9963 | ||
|
|
8c2ce0cf3f | ||
|
|
1b442cb16f | ||
|
|
2bf3680d87 | ||
|
|
0d4f0f0db5 | ||
|
|
e17ff8fd32 | ||
|
|
14edd12304 | ||
|
|
e6ffdb23a4 | ||
|
|
98fdedac06 | ||
|
|
255037264a | ||
|
|
d3fcbe9a02 | ||
|
|
bcdd588c6e | ||
|
|
b5007d45f7 | ||
|
|
a584cf5a4f | ||
|
|
77f7a2fa7f | ||
|
|
f2c539dc70 | ||
|
|
78d7160a99 | ||
|
|
196673bbce | ||
|
|
794d13082c | ||
|
|
5f5cf4107e | ||
|
|
6f3ec54b9f | ||
|
|
58043a6b04 | ||
|
|
a885315ef5 | ||
|
|
d7581525ac | ||
|
|
385068eb91 | ||
|
|
1085f68018 | ||
|
|
21af775ce0 | ||
|
|
bf16fee6e9 | ||
|
|
2358a2137a | ||
|
|
66baa7dc25 | ||
|
|
d63b4c1034 | ||
|
|
963e220a1c | ||
|
|
2f146e4d4c | ||
|
|
f796eede5a | ||
|
|
c89453be95 | ||
|
|
c3f5f5ca36 | ||
|
|
911d12f7c4 | ||
|
|
34d3c45d35 | ||
|
|
17614312e0 | ||
|
|
977779ae8d | ||
|
|
a2e35a0757 | ||
|
|
a4a2b6403b | ||
|
|
8ce8e289c9 | ||
|
|
03be97e437 | ||
|
|
2c5cf5a82a | ||
|
|
0a2d1965df | ||
|
|
c8a5f1e335 | ||
|
|
5e00cf9620 | ||
|
|
ce6370e25c | ||
|
|
3f8c1e4b34 | ||
|
|
25cda200be | ||
|
|
a1bc83a2ba | ||
|
|
bc3949db9e | ||
|
|
6cfa885207 | ||
|
|
899588e0b5 | ||
|
|
49af52a68d | ||
|
|
ec908af19c | ||
|
|
e61ac4682e | ||
|
|
4d10dce61d | ||
|
|
c569830dfc | ||
|
|
2d9fd87029 | ||
|
|
2670bfb8ba | ||
|
|
cc9190ab37 | ||
|
|
980570de71 | ||
|
|
ef92b54db3 | ||
|
|
0130124cea | ||
|
|
e2a7e867f9 | ||
|
|
32ce0ce5d9 | ||
|
|
28082ff5f5 | ||
|
|
46ccc4332c | ||
|
|
3a1217e667 | ||
|
|
39fd0c1278 | ||
|
|
4e6bd54dd1 | ||
|
|
5c9f46a6b0 | ||
|
|
7d53866157 | ||
|
|
9a2e948c42 | ||
|
|
223e971c7e | ||
|
|
df814223ff | ||
|
|
82b326e684 | ||
|
|
6aa581d2f0 | ||
|
|
8c0b2c684a | ||
|
|
62324781bd | ||
|
|
7ae0b2dc09 | ||
|
|
058122b804 | ||
|
|
69f63c529d | ||
|
|
e17a6b29b6 | ||
|
|
b12c2a13c0 | ||
|
|
236c835abc | ||
|
|
b41a5afe04 | ||
|
|
ad338bfa44 | ||
|
|
a899522679 | ||
|
|
b9b58c781e | ||
|
|
aa1eec4642 | ||
|
|
0c8d9469ea | ||
|
|
079e1bdffc | ||
|
|
b4337d1b54 | ||
|
|
e6a11c5e12 |
@@ -4,7 +4,7 @@ AccessModifierOffset: -2
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: false
|
||||
AlignEscapedNewlinesLeft: false
|
||||
AlignEscapedNewlines: Right
|
||||
AlignOperands: true
|
||||
AlignTrailingComments: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
@@ -31,14 +31,20 @@ BraceWrapping:
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: true
|
||||
SplitEmptyRecord: true
|
||||
SplitEmptyNamespace: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeBraces: Attach
|
||||
BreakBeforeInheritanceComma: false
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakStringLiterals: true
|
||||
ColumnLimit: 80
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
CompactNamespaces: false
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
@@ -46,7 +52,11 @@ Cpp11BracedListStyle: true
|
||||
DerivePointerAlignment: false
|
||||
DisableFormat: false
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
|
||||
FixNamespaceComments: true
|
||||
ForEachMacros:
|
||||
- foreach
|
||||
- Q_FOREACH
|
||||
- BOOST_FOREACH
|
||||
IncludeCategories:
|
||||
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
|
||||
Priority: 2
|
||||
@@ -68,6 +78,7 @@ NamespaceIndentation: None
|
||||
ObjCBlockIndentWidth: 2
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PenaltyBreakAssignment: 2
|
||||
PenaltyBreakBeforeFirstCallParameter: 19
|
||||
PenaltyBreakComment: 300
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
@@ -77,7 +88,9 @@ PenaltyReturnTypeOnItsOwnLine: 60
|
||||
PointerAlignment: Right
|
||||
ReflowComments: true
|
||||
SortIncludes: false
|
||||
SortUsingDeclarations: true
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceInEmptyParentheses: false
|
||||
|
||||
26
.travis.yml
26
.travis.yml
@@ -1,3 +1,4 @@
|
||||
dist: trusty
|
||||
env:
|
||||
matrix:
|
||||
- CI_BUILD=cmake
|
||||
@@ -6,15 +7,13 @@ language: cpp
|
||||
compiler:
|
||||
- clang
|
||||
- gcc
|
||||
sudo: false
|
||||
sudo: required
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- george-edison55-precise-backports
|
||||
packages:
|
||||
- g++-4.9
|
||||
- libstdc++-4.9-dev
|
||||
- g++-7
|
||||
- autoconf
|
||||
- automake
|
||||
- autotools-dev
|
||||
@@ -33,29 +32,18 @@ addons:
|
||||
- cmake-data
|
||||
before_install:
|
||||
- $CC --version
|
||||
- if [ "$CXX" = "g++" ]; then export CXX="g++-4.9" CC="gcc-4.9"; fi
|
||||
- if [ "$CXX" = "g++" ]; then export CXX="g++-7" CC="gcc-7"; fi
|
||||
- $CC --version
|
||||
- go version
|
||||
- cmake --version
|
||||
before_script:
|
||||
# First build spdylay, since integration tests require it.
|
||||
# spdylay is going to be built under third-party/spdylay
|
||||
- cd third-party
|
||||
- git clone https://github.com/tatsuhiro-t/spdylay.git
|
||||
- cd spdylay
|
||||
- autoreconf -i
|
||||
# Don't use ASAN for spdylay since failmalloc does not work with it.
|
||||
- ./configure --disable-src --disable-examples
|
||||
- make check
|
||||
- export SPDYLAY_HOME=$PWD
|
||||
- cd ../..
|
||||
# Now build nghttp2
|
||||
- if [ "$CI_BUILD" = "autotools" ]; then autoreconf -i; fi
|
||||
- git submodule update --init
|
||||
- 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
|
||||
- if [ "$CI_BUILD" = "autotools" ]; then ./configure --with-mruby; fi
|
||||
- if [ "$CI_BUILD" = "cmake" ]; then cmake -DENABLE_WERROR=1 -DWITH_MRUBY=1 -DWITH_NEVERBLEED=1; fi
|
||||
script:
|
||||
- if [ "$CI_BUILD" = "autotools" ]; then make distcheck; fi
|
||||
- if [ "$CI_BUILD" = "autotools" ]; then make distcheck DISTCHECK_CONFIGURE_FLAGS="--with-mruby --with-neverbleed --enable-werror CPPFLAGS=-fsanitize=address LDFLAGS=\"-fsanitize=address -fuse-ld=gold\""; 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.
|
||||
|
||||
19
AUTHORS
19
AUTHORS
@@ -21,21 +21,25 @@ Amir Pakdel
|
||||
Anders Bakken
|
||||
Andreas Pohl
|
||||
Andy Davies
|
||||
Angus Gratton
|
||||
Anna Henningsen
|
||||
Ant Bryan
|
||||
Benedikt Christoph Wolters
|
||||
Benedikt Christoph Wolters
|
||||
Bernard Spil
|
||||
Benjamin Peterson
|
||||
Bernard Spil
|
||||
Brian Card
|
||||
Brian Suh
|
||||
Daniel Evers
|
||||
Daniel Stenberg
|
||||
Dave Reisner
|
||||
David Beitey
|
||||
David Weekly
|
||||
Dmitriy Vetutnev
|
||||
Etienne Cimon
|
||||
Fabian Möller
|
||||
Fabian Wiesel
|
||||
Gabi Davar
|
||||
Gitai
|
||||
Google Inc.
|
||||
Jacob Champion
|
||||
Jan-E
|
||||
@@ -50,11 +54,15 @@ Kenny (kang-yen) Peng
|
||||
Kenny Peng
|
||||
Kit Chan
|
||||
Kyle Schomp
|
||||
LazyHamster
|
||||
Lucas Pardue
|
||||
MATSUMOTO Ryosuke
|
||||
Marc Bachmann
|
||||
Matt Rudary
|
||||
Matt Way
|
||||
Mike Conlen
|
||||
Mike Frysinger
|
||||
Mike Lothian
|
||||
Nicholas Hurley
|
||||
Nora Shoemaker
|
||||
Peeyush Aggarwal
|
||||
@@ -63,15 +71,21 @@ Piotr Sikora
|
||||
Raul Gutierrez Segales
|
||||
Remo E
|
||||
Reza Tavakoli
|
||||
Rick Lei
|
||||
Ross Smith II
|
||||
Scott Mitchell
|
||||
Sebastiaan Deckers
|
||||
Simone Basso
|
||||
Soham Sinha
|
||||
Stefan Eissing
|
||||
Stephen Ludin
|
||||
Sunpoet Po-Chuan Hsieh
|
||||
Svante Signell
|
||||
Syohei YOSHIDA
|
||||
Tapanito
|
||||
Tatsuhiko Kubo
|
||||
Tatsuhiro Tsujikawa
|
||||
Tobias Geerinckx-Rice
|
||||
Tom Harwood
|
||||
Tomasz Buchert
|
||||
Tomasz Torcz
|
||||
@@ -89,6 +103,7 @@ dalf
|
||||
es
|
||||
fangdingjun
|
||||
kumagi
|
||||
lstefani
|
||||
makovich
|
||||
mod-h2-dev
|
||||
moparisthebest
|
||||
|
||||
@@ -24,13 +24,13 @@
|
||||
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
# XXX using 1.8.90 instead of 1.9.0-DEV
|
||||
project(nghttp2 VERSION 1.21.0)
|
||||
project(nghttp2 VERSION 1.28.90)
|
||||
|
||||
# See versioning rule:
|
||||
# http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
||||
set(LT_CURRENT 27)
|
||||
set(LT_REVISION 1)
|
||||
set(LT_AGE 13)
|
||||
set(LT_CURRENT 29)
|
||||
set(LT_REVISION 0)
|
||||
set(LT_AGE 15)
|
||||
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
include(Version)
|
||||
@@ -79,7 +79,7 @@ else()
|
||||
set(ENABLE_PYTHON_BINDINGS_DEFAULT OFF)
|
||||
endif()
|
||||
|
||||
find_package(LibXml2 2.7.7)
|
||||
find_package(LibXml2 2.6.26)
|
||||
set(WITH_LIBXML2_DEFAULT ${LIBXML2_FOUND})
|
||||
find_package(Jemalloc)
|
||||
set(WITH_JEMALLOC_DEFAULT ${JEMALLOC_FOUND})
|
||||
@@ -106,7 +106,7 @@ endif()
|
||||
foreach(_build_type "Release" "MinSizeRel" "RelWithDebInfo")
|
||||
foreach(_lang C CXX)
|
||||
string(TOUPPER "CMAKE_${_lang}_FLAGS_${_build_type}" _var)
|
||||
string(REGEX REPLACE "(^| )[/-]D *NDEBUG($| )" "" ${_var} "${${_var}}")
|
||||
string(REGEX REPLACE "(^| )[/-]D *NDEBUG($| )" " " ${_var} "${${_var}}")
|
||||
endforeach()
|
||||
endforeach()
|
||||
|
||||
|
||||
@@ -10,39 +10,47 @@
|
||||
#
|
||||
# $ sudo docker run -v /path/to/dest:/out nghttp2-android cp /root/build/nghttp2/src/nghttpx /out
|
||||
|
||||
FROM ubuntu:vivid
|
||||
|
||||
# Only use standalone-toolchain for reduce size
|
||||
FROM ubuntu:xenial
|
||||
MAINTAINER Tatsuhiro Tsujikawa
|
||||
|
||||
ENV ANDROID_HOME /root/android
|
||||
ENV PREFIX $ANDROID_HOME/usr/local
|
||||
ENV ANDROID_HOME /root
|
||||
ENV TOOLCHAIN $ANDROID_HOME/toolchain
|
||||
ENV PATH $TOOLCHAIN/bin:$PATH
|
||||
|
||||
# It would be better to use nearest ubuntu archive mirror for faster
|
||||
# downloads.
|
||||
# RUN sed -ie 's/archive\.ubuntu/jp.archive.ubuntu/g' /etc/apt/sources.list
|
||||
ENV NDK_VERSION r14b
|
||||
|
||||
RUN apt-get update
|
||||
# genisoimage, libc6-i386 and lib32stdc++6 are required to decompress ndk.
|
||||
RUN apt-get install -y make binutils autoconf automake autotools-dev libtool \
|
||||
pkg-config git curl dpkg-dev libxml2-dev \
|
||||
genisoimage libc6-i386 lib32stdc++6
|
||||
WORKDIR /root
|
||||
RUN apt-get update && \
|
||||
apt-get install -y unzip make binutils autoconf \
|
||||
automake autotools-dev libtool pkg-config git \
|
||||
curl dpkg-dev libxml2-dev genisoimage libc6-i386 \
|
||||
lib32stdc++6 python&& \
|
||||
rm -rf /var/cache/apk/*
|
||||
|
||||
# Install toolchain
|
||||
RUN curl -L -O https://dl.google.com/android/repository/android-ndk-$NDK_VERSION-linux-x86_64.zip && \
|
||||
unzip -q android-ndk-$NDK_VERSION-linux-x86_64.zip && \
|
||||
rm android-ndk-$NDK_VERSION-linux-x86_64.zip && \
|
||||
mkdir -p $ANDROID_HOME/toolchain && \
|
||||
$ANDROID_HOME/android-ndk-$NDK_VERSION/build/tools/make-standalone-toolchain.sh \
|
||||
--install-dir=$ANDROID_HOME/toolchain \
|
||||
--toolchain=arm-linux-androideabi-4.9 \
|
||||
--force && \
|
||||
rm -r android-ndk-$NDK_VERSION
|
||||
|
||||
ENV PREFIX /root/usr/local
|
||||
|
||||
# Setup version of libraries
|
||||
ENV OPENSSL_VERSION 1.0.2d
|
||||
ENV SPDYLAY_VERSION v1.4.0
|
||||
ENV LIBEV_VERSION 4.19
|
||||
ENV ZLIB_VERSION 1.2.8
|
||||
ENV CARES_VERSION 1.13.0
|
||||
ENV NGHTTP2_VERSION v1.24.0
|
||||
|
||||
WORKDIR /root/build
|
||||
RUN curl -L -O http://dl.google.com/android/ndk/android-ndk-r10d-linux-x86_64.bin && \
|
||||
chmod a+x android-ndk-r10d-linux-x86_64.bin && \
|
||||
./android-ndk-r10d-linux-x86_64.bin && \
|
||||
rm android-ndk-r10d-linux-x86_64.bin
|
||||
|
||||
WORKDIR /root/build/android-ndk-r10d
|
||||
RUN /bin/bash build/tools/make-standalone-toolchain.sh \
|
||||
--install-dir=$ANDROID_HOME/toolchain \
|
||||
--toolchain=arm-linux-androideabi-4.9 --llvm-version=3.5 \
|
||||
--system=linux-x86_64
|
||||
|
||||
WORKDIR /root/build
|
||||
RUN git clone https://github.com/tatsuhiro-t/spdylay
|
||||
RUN git clone https://github.com/tatsuhiro-t/spdylay -b $SPDYLAY_VERSION --depth 1
|
||||
WORKDIR /root/build/spdylay
|
||||
RUN autoreconf -i && \
|
||||
./configure \
|
||||
@@ -59,22 +67,22 @@ RUN autoreconf -i && \
|
||||
make install
|
||||
|
||||
WORKDIR /root/build
|
||||
RUN curl -L -O https://www.openssl.org/source/openssl-1.0.2d.tar.gz && \
|
||||
tar xf openssl-1.0.2d.tar.gz && \
|
||||
rm openssl-1.0.2d.tar.gz
|
||||
RUN curl -L -O https://www.openssl.org/source/openssl-$OPENSSL_VERSION.tar.gz && \
|
||||
tar xf openssl-$OPENSSL_VERSION.tar.gz && \
|
||||
rm openssl-$OPENSSL_VERSION.tar.gz
|
||||
|
||||
WORKDIR /root/build/openssl-1.0.2d
|
||||
WORKDIR /root/build/openssl-$OPENSSL_VERSION
|
||||
RUN export CROSS_COMPILE=$TOOLCHAIN/bin/arm-linux-androideabi- && \
|
||||
./Configure --prefix=$PREFIX android && \
|
||||
make && make install_sw
|
||||
|
||||
WORKDIR /root/build
|
||||
RUN curl -L -O http://dist.schmorp.de/libev/libev-4.19.tar.gz && \
|
||||
RUN curl -L -O http://dist.schmorp.de/libev/Attic/libev-$LIBEV_VERSION.tar.gz && \
|
||||
curl -L -O https://gist.github.com/tatsuhiro-t/48c45f08950f587180ed/raw/80a8f003b5d1091eae497c5995bbaa68096e739b/libev-4.19-android.patch && \
|
||||
tar xf libev-4.19.tar.gz && \
|
||||
rm libev-4.19.tar.gz
|
||||
tar xf libev-$LIBEV_VERSION.tar.gz && \
|
||||
rm libev-$LIBEV_VERSION.tar.gz
|
||||
|
||||
WORKDIR /root/build/libev-4.19
|
||||
WORKDIR /root/build/libev-$LIBEV_VERSION
|
||||
RUN patch -p1 < ../libev-4.19-android.patch && \
|
||||
./configure \
|
||||
--host=arm-linux-androideabi \
|
||||
@@ -87,11 +95,11 @@ RUN patch -p1 < ../libev-4.19-android.patch && \
|
||||
make install
|
||||
|
||||
WORKDIR /root/build
|
||||
RUN curl -L -O http://zlib.net/zlib-1.2.8.tar.gz && \
|
||||
tar xf zlib-1.2.8.tar.gz && \
|
||||
rm zlib-1.2.8.tar.gz
|
||||
RUN curl -L -O https://downloads.sourceforge.net/project/libpng/zlib/$ZLIB_VERSION/zlib-$ZLIB_VERSION.tar.gz && \
|
||||
tar xf zlib-$ZLIB_VERSION.tar.gz && \
|
||||
rm zlib-$ZLIB_VERSION.tar.gz
|
||||
|
||||
WORKDIR /root/build/zlib-1.2.8
|
||||
WORKDIR /root/build/zlib-$ZLIB_VERSION
|
||||
RUN HOST=arm-linux-androideabi \
|
||||
CC=$HOST-gcc \
|
||||
AR=$HOST-ar \
|
||||
@@ -105,11 +113,26 @@ RUN HOST=arm-linux-androideabi \
|
||||
--static && \
|
||||
make install
|
||||
|
||||
|
||||
WORKDIR /root/build
|
||||
RUN git clone https://github.com/nghttp2/nghttp2
|
||||
RUN curl -L -O https://c-ares.haxx.se/download/c-ares-$CARES_VERSION.tar.gz && \
|
||||
tar xf c-ares-$CARES_VERSION.tar.gz && \
|
||||
rm c-ares-$CARES_VERSION.tar.gz
|
||||
|
||||
WORKDIR /root/build/c-ares-$CARES_VERSION
|
||||
RUN ./configure \
|
||||
--host=arm-linux-androideabi \
|
||||
--build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \
|
||||
--prefix=$PREFIX \
|
||||
--disable-shared && \
|
||||
make install
|
||||
|
||||
WORKDIR /root/build
|
||||
RUN git clone https://github.com/nghttp2/nghttp2 -b $NGHTTP2_VERSION --depth 1
|
||||
WORKDIR /root/build/nghttp2
|
||||
RUN autoreconf -i && \
|
||||
./configure \
|
||||
--enable-app \
|
||||
--disable-shared \
|
||||
--host=arm-linux-androideabi \
|
||||
--build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \
|
||||
@@ -118,11 +141,10 @@ RUN autoreconf -i && \
|
||||
--disable-python-bindings \
|
||||
--disable-examples \
|
||||
--disable-threads \
|
||||
LIBSPDYLAY_CFLAGS=-I$PREFIX/usr/local/include \
|
||||
LIBSPDYLAY_LIBS="-L$PREFIX/usr/local/lib -lspdylay" \
|
||||
CPPFLAGS="-fPIE -I$PREFIX/include" \
|
||||
CXXFLAGS="-fno-strict-aliasing" \
|
||||
PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \
|
||||
LDFLAGS="-fPIE -pie -L$PREFIX/lib" && \
|
||||
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" && \
|
||||
make && \
|
||||
arm-linux-androideabi-strip src/nghttpx src/nghttpd src/nghttp
|
||||
|
||||
66
README.rst
66
README.rst
@@ -87,7 +87,7 @@ enabled. SPDY support will be removed soon.
|
||||
To enable ``-a`` option (getting linked assets from the downloaded
|
||||
resource) in ``nghttp``, the following package is required:
|
||||
|
||||
* libxml2 >= 2.7.7
|
||||
* libxml2 >= 2.6.26
|
||||
|
||||
To enable systemd support in nghttpx, the following package is
|
||||
required:
|
||||
@@ -157,22 +157,8 @@ minimizes the risk of private key leakage when serious bug like
|
||||
Heartbleed is exploited. The neverbleed is disabled by default. To
|
||||
enable it, use ``--with-neverbleed`` configure option.
|
||||
|
||||
Building from git
|
||||
-----------------
|
||||
|
||||
Building from git is easy, but please be sure that at least autoconf 2.68 is
|
||||
used:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ git submodule update --init
|
||||
$ autoreconf -i
|
||||
$ automake
|
||||
$ autoconf
|
||||
$ ./configure
|
||||
$ make
|
||||
|
||||
To compile the source code, gcc >= 4.8.3 or clang >= 3.4 is required.
|
||||
In order to compile the source code, gcc >= 4.8.3 or clang >= 3.4 is
|
||||
required.
|
||||
|
||||
.. note::
|
||||
|
||||
@@ -219,6 +205,40 @@ To compile the source code, gcc >= 4.8.3 or clang >= 3.4 is required.
|
||||
responsible to specify the correct values to these variables. For
|
||||
complete list of these variables, run ``./configure -h``.
|
||||
|
||||
Building nghttp2 from release tar archive
|
||||
-----------------------------------------
|
||||
|
||||
The nghttp2 project regularly releases tar archives which includes
|
||||
nghttp2 source code, and generated build files. They can be
|
||||
downloaded from `Releases
|
||||
<https://github.com/nghttp2/nghttp2/releases>`_ page.
|
||||
|
||||
Building nghttp2 from git requires autotools development packages.
|
||||
Building from tar archives does not require them, and thus it is much
|
||||
easier. The usual build step is as follows:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ tar xf nghttp2-X.Y.Z.tar.bz2
|
||||
$ cd nghttp2-X.Y.Z
|
||||
$ ./configure
|
||||
$ make
|
||||
|
||||
Building from git
|
||||
-----------------
|
||||
|
||||
Building from git is easy, but please be sure that at least autoconf 2.68 is
|
||||
used:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ git submodule update --init
|
||||
$ autoreconf -i
|
||||
$ automake
|
||||
$ autoconf
|
||||
$ ./configure
|
||||
$ make
|
||||
|
||||
Notes for building on Windows (MSVC)
|
||||
------------------------------------
|
||||
|
||||
@@ -265,6 +285,18 @@ If you want to compile the applications under ``examples/``, you need
|
||||
to remove or rename the ``event.h`` from libev's installation, because
|
||||
it conflicts with libevent's installation.
|
||||
|
||||
Notes for installation on Linux systems
|
||||
--------------------------------------------
|
||||
After installing nghttp2 tool suite with ``make install`` one might experience a similar error:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
nghttpx: error while loading shared libraries: libnghttp2.so.14: cannot open shared object file: No such file or directory
|
||||
|
||||
This means that the tool is unable to locate the ``libnghttp2.so`` shared library.
|
||||
|
||||
To update the shared library cache run ``sudo ldconfig``.
|
||||
|
||||
Building the documentation
|
||||
--------------------------
|
||||
|
||||
|
||||
19
configure.ac
19
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.21.0], [t-tujikawa@users.sourceforge.net])
|
||||
AC_INIT([nghttp2], [1.29.0-DEV], [t-tujikawa@users.sourceforge.net])
|
||||
AC_CONFIG_AUX_DIR([.])
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
@@ -44,9 +44,9 @@ m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
|
||||
dnl See versioning rule:
|
||||
dnl http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
||||
AC_SUBST(LT_CURRENT, 27)
|
||||
AC_SUBST(LT_REVISION, 1)
|
||||
AC_SUBST(LT_AGE, 13)
|
||||
AC_SUBST(LT_CURRENT, 29)
|
||||
AC_SUBST(LT_REVISION, 0)
|
||||
AC_SUBST(LT_AGE, 15)
|
||||
|
||||
major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"`
|
||||
minor=`echo $PACKAGE_VERSION |cut -d. -f2 | sed -e "s/[^0-9]//g"`
|
||||
@@ -119,7 +119,7 @@ AC_ARG_WITH([jemalloc],
|
||||
|
||||
AC_ARG_WITH([spdylay],
|
||||
[AS_HELP_STRING([--with-spdylay],
|
||||
[Use spdylay [default=no]])],
|
||||
[(Deprecated) Use spdylay [default=no]])],
|
||||
[request_spdylay=$withval], [request_spdylay=no])
|
||||
|
||||
AC_ARG_WITH([systemd],
|
||||
@@ -410,7 +410,7 @@ if test "x${request_systemd}" = "xyes" &&
|
||||
fi
|
||||
|
||||
# libxml2 (for src/nghttp)
|
||||
PKG_CHECK_MODULES([LIBXML2], [libxml-2.0 >= 2.7.7],
|
||||
PKG_CHECK_MODULES([LIBXML2], [libxml-2.0 >= 2.6.26],
|
||||
[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.])
|
||||
@@ -804,6 +804,9 @@ if test "x$werror" != "xno"; then
|
||||
AX_CHECK_COMPILE_FLAG([-Werror], [CXXFLAGS="$CXXFLAGS -Werror"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wformat-security], [CXXFLAGS="$CXXFLAGS -Wformat-security"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wsometimes-uninitialized], [CXXFLAGS="$CXXFLAGS -Wsometimes-uninitialized"])
|
||||
# Disable noexcept-type warning of g++-7. This is not harmful as
|
||||
# long as all source files are compiled with the same compiler.
|
||||
AX_CHECK_COMPILE_FLAG([-Wno-noexcept-type], [CXXFLAGS="$CXXFLAGS -Wno-noexcept-type"])
|
||||
AC_LANG_POP()
|
||||
fi
|
||||
|
||||
@@ -946,3 +949,7 @@ AC_MSG_NOTICE([summary of build options:
|
||||
Python bindings:${enable_python_bindings}
|
||||
Threading: ${enable_threads}
|
||||
])
|
||||
|
||||
if test "x${have_spdylay}" == "xyes"; then
|
||||
AC_MSG_WARN([spdylay support was deprecated, and will be removed in v1.29.0.])
|
||||
fi
|
||||
|
||||
@@ -49,6 +49,7 @@ set(APIDOCS
|
||||
nghttp2_rcbuf_decref.rst
|
||||
nghttp2_rcbuf_get_buf.rst
|
||||
nghttp2_rcbuf_incref.rst
|
||||
nghttp2_rcbuf_is_static.rst
|
||||
nghttp2_select_next_protocol.rst
|
||||
nghttp2_session_callbacks_del.rst
|
||||
nghttp2_session_callbacks_new.rst
|
||||
|
||||
@@ -74,12 +74,14 @@ APIDOCS= \
|
||||
nghttp2_rcbuf_decref.rst \
|
||||
nghttp2_rcbuf_get_buf.rst \
|
||||
nghttp2_rcbuf_incref.rst \
|
||||
nghttp2_rcbuf_is_static.rst \
|
||||
nghttp2_select_next_protocol.rst \
|
||||
nghttp2_session_callbacks_del.rst \
|
||||
nghttp2_session_callbacks_new.rst \
|
||||
nghttp2_session_callbacks_set_before_frame_send_callback.rst \
|
||||
nghttp2_session_callbacks_set_data_source_read_length_callback.rst \
|
||||
nghttp2_session_callbacks_set_error_callback.rst \
|
||||
nghttp2_session_callbacks_set_error_callback2.rst \
|
||||
nghttp2_session_callbacks_set_on_begin_frame_callback.rst \
|
||||
nghttp2_session_callbacks_set_on_begin_headers_callback.rst \
|
||||
nghttp2_session_callbacks_set_on_data_chunk_recv_callback.rst \
|
||||
@@ -267,7 +269,7 @@ apiref.rst: \
|
||||
$(APIDOCS): apiref.rst
|
||||
|
||||
clean-local:
|
||||
[ $(srcdir) = $(builddir) ] || for i in $(RST_FILES); do [ -e $(builddir)/$$i ] && rm -f $(builddir)/$$i; done
|
||||
if [ $(srcdir) != $(builddir) ]; then for i in $(RST_FILES); do rm -f $(builddir)/$$i; done fi
|
||||
-rm -f apiref.rst
|
||||
-rm -f $(APIDOCS)
|
||||
-rm -rf $(BUILDDIR)/*
|
||||
|
||||
@@ -13,6 +13,7 @@ import re
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.parsers.rst import directives
|
||||
from docutils.parsers.rst import Directive
|
||||
|
||||
from sphinx import addnodes
|
||||
from sphinx import version_info
|
||||
@@ -21,10 +22,8 @@ from sphinx.locale import l_, _
|
||||
from sphinx.domains import Domain, ObjType, Index
|
||||
from sphinx.directives import ObjectDescription
|
||||
from sphinx.util.nodes import make_refnode
|
||||
from sphinx.util.compat import Directive
|
||||
from sphinx.util.docfields import Field, GroupedField, TypedField
|
||||
|
||||
|
||||
# REs for Ruby signatures
|
||||
rb_sig_re = re.compile(
|
||||
r'''^ ([\w.]*\.)? # class name(s)
|
||||
|
||||
@@ -8,7 +8,7 @@ _h2load()
|
||||
_get_comp_words_by_ref cur prev
|
||||
case $cur in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W '--connection-window-bits --clients --verbose --ciphers --rate --no-tls-proto --header-table-size --requests --base-uri --h1 --threads --npn-list --rate-period --data --version --connection-inactivity-timeout --timing-script-file --encoder-header-table-size --max-concurrent-streams --connection-active-timeout --input-file --help --window-bits --header ' -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W '--connection-window-bits --clients --verbose --ciphers --rate --no-tls-proto --header-table-size --requests --base-uri --h1 --threads --npn-list --rate-period --data --version --connection-inactivity-timeout --timing-script-file --encoder-header-table-size --max-concurrent-streams --connection-active-timeout --input-file --help --window-bits --warm-up-time --duration --header ' -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
_filedir
|
||||
|
||||
@@ -8,7 +8,7 @@ _nghttp()
|
||||
_get_comp_words_by_ref cur prev
|
||||
case $cur in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W '--no-push --verbose --no-dep --get-assets --har --header-table-size --multiply --encoder-header-table-size --padding --hexdump --max-concurrent-streams --continuation --connection-window-bits --peer-max-concurrent-streams --timeout --data --no-content-length --version --color --cert --upgrade --remote-name --trailer --weight --help --key --null-out --window-bits --expect-continue --stat --header ' -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W '--no-push --verbose --no-dep --get-assets --har --header-table-size --multiply --encoder-header-table-size --padding --hexdump --max-concurrent-streams --continuation --connection-window-bits --peer-max-concurrent-streams --timeout --data --no-content-length --version --color --cert --upgrade --remote-name --trailer --weight --help --key --null-out --window-bits --expect-continue --stat --no-verify-peer --header ' -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
_filedir
|
||||
|
||||
@@ -8,7 +8,7 @@ _nghttpx()
|
||||
_get_comp_words_by_ref cur prev
|
||||
case $cur in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W '--worker-read-rate --include --frontend-http2-dump-response-header --tls-ticket-key-file --verify-client-cacert --max-response-header-fields --backend-http2-window-size --frontend-keep-alive-timeout --backend-request-buffer --max-request-header-fields --fastopen --backend-connect-timeout --tls-max-proto-version --conf --dns-lookup-timeout --backend-http2-max-concurrent-streams --worker-write-burst --npn-list --dns-max-try --fetch-ocsp-response-file --no-via --tls-session-cache-memcached-cert-file --no-http2-cipher-black-list --mruby-file --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 --frontend-max-requests --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 --redirect-https-port --request-header-field-buffer --api-max-request-body --frontend-http2-decoder-dynamic-table-size --errorlog-file --frontend-http2-max-concurrent-streams --psk-secrets --frontend-write-timeout --tls-ticket-key-cipher --read-burst --backend --server-name --insecure --backend-max-backoff --log-level --host-rewrite --tls-ticket-key-memcached-interval --frontend-http2-setting-timeout --frontend-http2-connection-window-size --worker-frontend-connections --syslog-facility --no-server-push --no-location-rewrite --single-thread --tls-session-cache-memcached --no-ocsp --backend-response-buffer --tls-min-proto-version --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 --frontend-http2-encoder-dynamic-table-size --read-rate --backend-connections-per-frontend --strip-incoming-forwarded ' -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W '--worker-read-rate --include --frontend-http2-dump-response-header --tls-ticket-key-file --verify-client-cacert --max-response-header-fields --backend-http2-window-size --frontend-keep-alive-timeout --backend-request-buffer --max-request-header-fields --fastopen --backend-connect-timeout --tls-max-proto-version --conf --dns-lookup-timeout --backend-http2-max-concurrent-streams --worker-write-burst --npn-list --dns-max-try --fetch-ocsp-response-file --no-via --tls-session-cache-memcached-cert-file --no-http2-cipher-black-list --mruby-file --client-no-http2-cipher-black-list --stream-read-timeout --client-ciphers --ocsp-update-interval --forwarded-for --accesslog-syslog --dns-cache-timeout --frontend-http2-read-timeout --listener-disable-timeout --ciphers --client-psk-secrets --strip-incoming-x-forwarded-for --no-server-rewrite --private-key-passwd-file --backend-keep-alive-timeout --backend-http-proxy-uri --frontend-max-requests --rlimit-nofile --no-strip-incoming-x-forwarded-proto --tls-ticket-key-memcached-cert-file --no-verify-ocsp --forwarded-by --tls-session-cache-memcached-private-key-file --error-page --ocsp-startup --backend-write-timeout --tls-dyn-rec-warmup-threshold --tls-ticket-key-memcached-max-retry --frontend-http2-window-size --http2-no-cookie-crumbling --worker-read-burst --dh-param-file --accesslog-format --errorlog-syslog --redirect-https-port --request-header-field-buffer --api-max-request-body --frontend-http2-decoder-dynamic-table-size --errorlog-file --frontend-http2-max-concurrent-streams --psk-secrets --frontend-write-timeout --tls-ticket-key-cipher --read-burst --no-add-x-forwarded-proto --backend --server-name --insecure --backend-max-backoff --log-level --host-rewrite --tls-ticket-key-memcached-interval --frontend-http2-setting-timeout --frontend-http2-connection-window-size --worker-frontend-connections --syslog-facility --no-server-push --no-location-rewrite --single-thread --tls-session-cache-memcached --no-ocsp --backend-response-buffer --tls-min-proto-version --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 --single-process --client-cert-file --tls-ticket-key-memcached --tls-dyn-rec-idle-timeout --frontend-http2-optimize-write-buffer-size --verify-client --frontend-http2-encoder-dynamic-table-size --read-rate --backend-connections-per-frontend --strip-incoming-forwarded ' -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
_filedir
|
||||
|
||||
@@ -157,7 +157,7 @@ html_theme_path = ['@top_srcdir@/doc/_themes']
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
html_use_smartypants = False
|
||||
#html_use_smartypants = False
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
html_sidebars = {
|
||||
|
||||
2
doc/docutils.conf
Normal file
2
doc/docutils.conf
Normal file
@@ -0,0 +1,2 @@
|
||||
[parsers]
|
||||
smart_quotes=no
|
||||
19
doc/h2load.1
19
doc/h2load.1
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "H2LOAD" "1" "Mar 27, 2017" "1.21.0" "nghttp2"
|
||||
.TH "H2LOAD" "1" "Nov 25, 2017" "1.28.0" "nghttp2"
|
||||
.SH NAME
|
||||
h2load \- HTTP/2 benchmarking tool
|
||||
.
|
||||
@@ -54,7 +54,9 @@ scheme, host or port values.
|
||||
Number of requests across all clients. If it is used
|
||||
with \fI\%\-\-timing\-script\-file\fP option, this option specifies
|
||||
the number of requests each client performs rather than
|
||||
the number of requests across all clients.
|
||||
the number of requests across all clients. This option
|
||||
is ignored if timing\-based benchmarking is enabled (see
|
||||
\fI\%\-\-duration\fP option).
|
||||
.sp
|
||||
Default: \fB1\fP
|
||||
.UNINDENT
|
||||
@@ -170,6 +172,19 @@ option is 1s.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-D, \-\-duration=<N>
|
||||
Specifies the main duration for the measurements in case
|
||||
of timing\-based benchmarking.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-warm\-up\-time=<DURATION>
|
||||
Specifies the time period before starting the actual
|
||||
measurements, in case of timing\-based benchmarking.
|
||||
Needs to provided along with \fI\%\-D\fP option.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-T, \-\-connection\-active\-timeout=<DURATION>
|
||||
Specifies the maximum time that h2load is willing to
|
||||
keep a connection open, regardless of the activity on
|
||||
|
||||
@@ -34,7 +34,9 @@ OPTIONS
|
||||
Number of requests across all clients. If it is used
|
||||
with :option:`--timing-script-file` option, this option specifies
|
||||
the number of requests each client performs rather than
|
||||
the number of requests across all clients.
|
||||
the number of requests across all clients. This option
|
||||
is ignored if timing-based benchmarking is enabled (see
|
||||
:option:`--duration` option).
|
||||
|
||||
Default: ``1``
|
||||
|
||||
@@ -136,6 +138,17 @@ OPTIONS
|
||||
the rate option is not used. The default value for this
|
||||
option is 1s.
|
||||
|
||||
.. option:: -D, --duration=<N>
|
||||
|
||||
Specifies the main duration for the measurements in case
|
||||
of timing-based benchmarking.
|
||||
|
||||
.. option:: --warm-up-time=<DURATION>
|
||||
|
||||
Specifies the time period before starting the actual
|
||||
measurements, in case of timing-based benchmarking.
|
||||
Needs to provided along with :option:`-D` option.
|
||||
|
||||
.. option:: -T, --connection-active-timeout=<DURATION>
|
||||
|
||||
Specifies the maximum time that h2load is willing to
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTP" "1" "Mar 27, 2017" "1.21.0" "nghttp2"
|
||||
.TH "NGHTTP" "1" "Nov 25, 2017" "1.28.0" "nghttp2"
|
||||
.SH NAME
|
||||
nghttp \- HTTP/2 client
|
||||
.
|
||||
@@ -236,6 +236,12 @@ combined with the \fI\%\-d\fP option.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-y, \-\-no\-verify\-peer
|
||||
Suppress warning on server certificate verification
|
||||
failure.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-version
|
||||
Display version information and exit.
|
||||
.UNINDENT
|
||||
|
||||
@@ -186,6 +186,11 @@ OPTIONS
|
||||
Continue interim response. This option is ignored unless
|
||||
combined with the :option:`-d` option.
|
||||
|
||||
.. option:: -y, --no-verify-peer
|
||||
|
||||
Suppress warning on server certificate verification
|
||||
failure.
|
||||
|
||||
.. option:: --version
|
||||
|
||||
Display version information and exit.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTPD" "1" "Mar 27, 2017" "1.21.0" "nghttp2"
|
||||
.TH "NGHTTPD" "1" "Nov 25, 2017" "1.28.0" "nghttp2"
|
||||
.SH NAME
|
||||
nghttpd \- HTTP/2 server
|
||||
.
|
||||
|
||||
260
doc/nghttpx.1
260
doc/nghttpx.1
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTPX" "1" "Mar 27, 2017" "1.21.0" "nghttp2"
|
||||
.TH "NGHTTPX" "1" "Nov 25, 2017" "1.28.0" "nghttp2"
|
||||
.SH NAME
|
||||
nghttpx \- HTTP/2 proxy
|
||||
.
|
||||
@@ -73,14 +73,16 @@ path which ends with "\fI/\fP" also matches the request path
|
||||
which only lacks trailing \(aq\fI/\fP\(aq (e.g., path "\fI/foo/\fP"
|
||||
matches request path "\fI/foo\fP"). If it does not end with
|
||||
"\fI/\fP", it performs exact match against the request path.
|
||||
If host is given, it performs exact match against the
|
||||
request host. If host alone is given, "\fI/\fP" is appended
|
||||
to it, so that it matches all request paths under the
|
||||
host (e.g., specifying "nghttp2.org" equals to
|
||||
"nghttp2.org/"). CONNECT method is treated specially.
|
||||
It does not have path, and we don\(aqt allow empty path.
|
||||
To workaround this, we assume that CONNECT method has
|
||||
"\fI/\fP" as path.
|
||||
If host is given, it performs a match against the
|
||||
request host. For a request received on the frontend
|
||||
listener with "sni\-fwd" parameter enabled, SNI host is
|
||||
used instead of a request host. If host alone is given,
|
||||
"\fI/\fP" is appended to it, so that it matches all request
|
||||
paths under the host (e.g., specifying "nghttp2.org"
|
||||
equals to "nghttp2.org/"). CONNECT method is treated
|
||||
specially. It does not have path, and we don\(aqt allow
|
||||
empty path. To workaround this, we assume that CONNECT
|
||||
method has "\fI/\fP" as path.
|
||||
.sp
|
||||
Patterns with host take precedence over patterns with
|
||||
just path. Then, longer patterns take precedence over
|
||||
@@ -94,6 +96,18 @@ host pattern "*.nghttp2.org" matches against
|
||||
match against "nghttp2.org". The exact hosts match
|
||||
takes precedence over the wildcard hosts match.
|
||||
.sp
|
||||
If path part ends with "*", it is treated as wildcard
|
||||
path. The wildcard path behaves differently from the
|
||||
normal path. For normal path, match is made around the
|
||||
boundary of path component separator,"\fI/\fP". On the other
|
||||
hand, the wildcard path does not take into account the
|
||||
path component separator. All paths which include the
|
||||
wildcard path without last "*" as prefix, and are
|
||||
strictly longer than wildcard path without last "*" are
|
||||
matched. "*" must match at least one character. For
|
||||
example, the pattern "\fI/foo*\fP" matches "\fI/foo/\fP" and
|
||||
"\fI/foobar\fP". But it does not match "\fI/foo\fP", or "\fI/fo\fP".
|
||||
.sp
|
||||
If <PATTERN> is omitted or empty string, "\fI/\fP" is used as
|
||||
pattern, which matches all request paths (catch\-all
|
||||
pattern). The catch\-all backend must be given.
|
||||
@@ -166,16 +180,32 @@ state, and this is the default behaviour.
|
||||
The session affinity is enabled using
|
||||
"affinity=<METHOD>" parameter. If "ip" is given in
|
||||
<METHOD>, client IP based session affinity is enabled.
|
||||
If "none" is given in <METHOD>, session affinity is
|
||||
disabled, and this is the default. The session affinity
|
||||
is enabled per <PATTERN>. If at least one backend has
|
||||
"affinity" parameter, and its <METHOD> is not "none",
|
||||
session affinity is enabled for all backend servers
|
||||
sharing the same <PATTERN>. It is advised to set
|
||||
"affinity" parameter to all backend explicitly if
|
||||
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.
|
||||
If "cookie" is given in <METHOD>, cookie based session
|
||||
affinity is enabled. If "none" is given in <METHOD>,
|
||||
session affinity is disabled, and this is the default.
|
||||
The session affinity is enabled per <PATTERN>. If at
|
||||
least one backend has "affinity" parameter, and its
|
||||
<METHOD> is not "none", session affinity is enabled for
|
||||
all backend servers sharing the same <PATTERN>. It is
|
||||
advised to set "affinity" parameter to all backend
|
||||
explicitly if 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
|
||||
If "affinity=cookie" is used, the additional
|
||||
configuration is required.
|
||||
"affinity\-cookie\-name=<NAME>" must be used to specify a
|
||||
name of cookie to use. Optionally,
|
||||
"affinity\-cookie\-path=<PATH>" can be used to specify a
|
||||
path which cookie is applied. The optional
|
||||
"affinity\-cookie\-secure=<SECURE>" controls the Secure
|
||||
attribute of a cookie. The default value is "auto", and
|
||||
the Secure attribute is determined by a request scheme.
|
||||
If a request scheme is "https", then Secure attribute is
|
||||
set. Otherwise, it is not set. If <SECURE> is "yes",
|
||||
the Secure attribute is always set. If <SECURE> is
|
||||
"no", the Secure attribute is always omitted.
|
||||
.sp
|
||||
By default, name resolution of backend host name is done
|
||||
at start up, or reloading configuration. If "dns"
|
||||
@@ -221,6 +251,11 @@ parameters are mutually exclusive.
|
||||
Optionally, TLS can be disabled by specifying "no\-tls"
|
||||
parameter. TLS is enabled by default.
|
||||
.sp
|
||||
If "sni\-fwd" parameter is used, when performing a match
|
||||
to select a backend server, SNI host name received from
|
||||
the client is used instead of the request host. See
|
||||
\fI\%\-\-backend\fP option about the pattern match.
|
||||
.sp
|
||||
To make this frontend as API endpoint, specify "api"
|
||||
parameter. This is disabled by default. It is
|
||||
important to limit the access to the API frontend.
|
||||
@@ -585,11 +620,14 @@ enabled for backend connections.
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-cacert=<PATH>
|
||||
Set path to trusted CA certificate file used in backend
|
||||
TLS connections. The file must be in PEM format. It
|
||||
can contain multiple certificates. If the linked
|
||||
OpenSSL is configured to load system wide certificates,
|
||||
they are loaded at startup regardless of this option.
|
||||
Set path to trusted CA certificate file. It is used in
|
||||
backend TLS connections to verify peer\(aqs certificate.
|
||||
It is also used to verify OCSP response from the script
|
||||
set by \fI\%\-\-fetch\-ocsp\-response\-file\fP\&. The file must be in
|
||||
PEM format. It can contain multiple certificates. If
|
||||
the linked OpenSSL is configured to load system wide
|
||||
certificates, they are loaded at startup regardless of
|
||||
this option.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -604,12 +642,12 @@ password protected it\(aqll be requested interactively.
|
||||
Specify additional certificate and private key file.
|
||||
nghttpx will choose certificates based on the hostname
|
||||
indicated by client using TLS SNI extension. If nghttpx
|
||||
is built with OpenSSL >= 1.0.2, signature algorithms
|
||||
(e.g., ECDSA+SHA256, RSA+SHA256) presented by client are
|
||||
also taken into consideration. This allows nghttpx to
|
||||
send ECDSA certificate to modern clients, while sending
|
||||
RSA based certificate to older clients. This option can
|
||||
be used multiple times. To make OCSP stapling work,
|
||||
is built with OpenSSL >= 1.0.2, the shared elliptic
|
||||
curves (e.g., P\-256) between client and server are also
|
||||
taken into consideration. This allows nghttpx to send
|
||||
ECDSA certificate to modern clients, while sending RSA
|
||||
based certificate to older clients. This option can be
|
||||
used multiple times. To make OCSP stapling work,
|
||||
<CERTPATH> must be absolute path.
|
||||
.sp
|
||||
Additional parameter can be specified in <PARAM>. The
|
||||
@@ -672,10 +710,14 @@ done in case\-insensitive manner. The versions between
|
||||
\fI\%\-\-tls\-min\-proto\-version\fP and \fI\%\-\-tls\-max\-proto\-version\fP are
|
||||
enabled. If the protocol list advertised by client does
|
||||
not overlap this range, you will receive the error
|
||||
message "unknown protocol". The available versions are:
|
||||
message "unknown protocol". If a protocol version lower
|
||||
than TLSv1.2 is specified, make sure that the compatible
|
||||
ciphers are included in \fI\%\-\-ciphers\fP option. The default
|
||||
cipher list only includes ciphers compatible with
|
||||
TLSv1.2 or above. The available versions are:
|
||||
TLSv1.2, TLSv1.1, and TLSv1.0
|
||||
.sp
|
||||
Default: \fBTLSv1.1\fP
|
||||
Default: \fBTLSv1.2\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -805,6 +847,20 @@ Default: \fB4h\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-ocsp\-startup
|
||||
Start accepting connections after initial attempts to
|
||||
get OCSP responses finish. It does not matter some of
|
||||
the attempts fail. This feature is useful if OCSP
|
||||
responses must be available before accepting
|
||||
connections.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-no\-verify\-ocsp
|
||||
nghttpx does not verify OCSP response.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-no\-ocsp
|
||||
Disable OCSP stapling.
|
||||
.UNINDENT
|
||||
@@ -1142,15 +1198,32 @@ $alpn: ALPN identifier of the protocol which generates
|
||||
the response. For HTTP/1, ALPN is always http/1.1,
|
||||
regardless of minor version.
|
||||
.IP \(bu 2
|
||||
$ssl_cipher: cipher used for SSL/TLS connection.
|
||||
$tls_cipher: cipher used for SSL/TLS connection.
|
||||
.IP \(bu 2
|
||||
$ssl_protocol: protocol for SSL/TLS connection.
|
||||
$tls_client_fingerprint_sha256: SHA\-256 fingerprint of
|
||||
client certificate.
|
||||
.IP \(bu 2
|
||||
$ssl_session_id: session ID for SSL/TLS connection.
|
||||
$tls_client_fingerprint_sha1: SHA\-1 fingerprint of
|
||||
client certificate.
|
||||
.IP \(bu 2
|
||||
$ssl_session_reused: "r" if SSL/TLS session was
|
||||
$tls_client_subject_name: subject name in client
|
||||
certificate.
|
||||
.IP \(bu 2
|
||||
$tls_client_issuer_name: issuer name in client
|
||||
certificate.
|
||||
.IP \(bu 2
|
||||
$tls_client_serial: serial number in client
|
||||
certificate.
|
||||
.IP \(bu 2
|
||||
$tls_protocol: protocol for SSL/TLS connection.
|
||||
.IP \(bu 2
|
||||
$tls_session_id: session ID for SSL/TLS connection.
|
||||
.IP \(bu 2
|
||||
$tls_session_reused: "r" if SSL/TLS session was
|
||||
reused. Otherwise, "."
|
||||
.IP \(bu 2
|
||||
$tls_sni: SNI server name for SSL/TLS connection.
|
||||
.IP \(bu 2
|
||||
$backend_host: backend host used to fulfill the
|
||||
request. "\-" if backend host is not available.
|
||||
.IP \(bu 2
|
||||
@@ -1207,6 +1280,21 @@ requests.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-no\-add\-x\-forwarded\-proto
|
||||
Don\(aqt append additional X\-Forwarded\-Proto header field
|
||||
to the backend request. If inbound client sets
|
||||
X\-Forwarded\-Proto, and
|
||||
\fI\%\-\-no\-strip\-incoming\-x\-forwarded\-proto\fP option is used,
|
||||
they are passed to the backend.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-no\-strip\-incoming\-x\-forwarded\-proto
|
||||
Don\(aqt strip X\-Forwarded\-Proto header field from inbound
|
||||
client requests.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-add\-forwarded=<LIST>
|
||||
Append RFC 7239 Forwarded header field with parameters
|
||||
specified in comma delimited list <LIST>. The supported
|
||||
@@ -1460,6 +1548,17 @@ Set path to save PID of this program.
|
||||
Run this program as <USER>. This option is intended to
|
||||
be used to drop root privileges.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-single\-process
|
||||
Run this program in a single process mode for debugging
|
||||
purpose. Without this option, nghttpx creates at least
|
||||
2 processes: master and worker processes. If this
|
||||
option is used, master and worker are unified into a
|
||||
single process. nghttpx still spawns additional process
|
||||
if neverbleed is used. In the single process mode, the
|
||||
signal handling feature is disabled.
|
||||
.UNINDENT
|
||||
.SS Scripting
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -1470,7 +1569,9 @@ Set mruby script file
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-conf=<PATH>
|
||||
Load configuration from <PATH>.
|
||||
Load configuration from <PATH>. Please note that
|
||||
nghttpx always tries to read the default configuration
|
||||
file if \fI\%\-\-conf\fP is not given.
|
||||
.sp
|
||||
Default: \fB/etc/nghttpx/nghttpx.conf\fP
|
||||
.UNINDENT
|
||||
@@ -1564,7 +1665,7 @@ follows:
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B <datetime>
|
||||
It is a conbination of date and time when the log is written. It
|
||||
It is a combination of date and time when the log is written. It
|
||||
is in ISO 8601 format.
|
||||
.TP
|
||||
.B <master\-pid>
|
||||
@@ -1693,6 +1794,22 @@ be customized using \fI\%\-\-fetch\-ocsp\-response\-file\fP option.
|
||||
.sp
|
||||
If OCSP query is failed, previous OCSP response, if any, is continued
|
||||
to be used.
|
||||
.sp
|
||||
\fI\%\-\-fetch\-ocsp\-response\-file\fP option provides wide range of
|
||||
possibility to manage OCSP response. It can take an arbitrary script
|
||||
or executable. The requirement is that it supports the command\-line
|
||||
interface of \fBfetch\-ocsp\-response\fP script, and it must return a
|
||||
valid DER encoded OCSP response on success. It must return exit code
|
||||
0 on success, and 75 for temporary error, and the other error code for
|
||||
generic failure. For large cluster of servers, it is not efficient
|
||||
for each server to perform OCSP query using \fBfetch\-ocsp\-response\fP\&.
|
||||
Instead, you can retrieve OCSP response in some way, and store it in a
|
||||
disk or a shared database. Then specify a program in
|
||||
\fI\%\-\-fetch\-ocsp\-response\-file\fP to fetch it from those stores.
|
||||
This could provide a way to share the OCSP response between fleet of
|
||||
servers, and also any OCSP query strategy can be applied which may be
|
||||
beyond the ability of nghttpx itself or \fBfetch\-ocsp\-response\fP
|
||||
script.
|
||||
.SH TLS SESSION RESUMPTION
|
||||
.sp
|
||||
nghttpx supports TLS session resumption through both session ID and
|
||||
@@ -1703,7 +1820,7 @@ By default, session ID is shared by all worker threads.
|
||||
.sp
|
||||
If \fI\%\-\-tls\-session\-cache\-memcached\fP is given, nghttpx will
|
||||
insert serialized session data to memcached with
|
||||
\fBnghttpx:tls\-session\-cache:\fP + lowercased hex string of session ID
|
||||
\fBnghttpx:tls\-session\-cache:\fP + lowercase hex string of session ID
|
||||
as a memcached entry key, with expiry time 12 hours. Session timeout
|
||||
is set to 12 hours.
|
||||
.sp
|
||||
@@ -1785,6 +1902,17 @@ API is subject to change in the future release.
|
||||
.UNINDENT
|
||||
.UNINDENT
|
||||
.sp
|
||||
\fBWARNING:\fP
|
||||
.INDENT 0.0
|
||||
.INDENT 3.5
|
||||
Almost all string value returned from method, or attribute is a
|
||||
fresh new mruby string, which involves memory allocation, and
|
||||
copies. Therefore, it is strongly recommended to store a return
|
||||
value in a local variable, and use it, instead of calling method or
|
||||
accessing attribute repeatedly.
|
||||
.UNINDENT
|
||||
.UNINDENT
|
||||
.sp
|
||||
nghttpx allows users to extend its capability using mruby scripts.
|
||||
nghttpx has 2 hook points to execute mruby script: request phase and
|
||||
response phase. The request phase hook is invoked after all request
|
||||
@@ -1833,7 +1961,7 @@ Return \fI\%Response\fP object.
|
||||
.TP
|
||||
.B attribute [R] ctx
|
||||
Return Ruby hash object. It persists until request finishes.
|
||||
So values set in request phase hoo can be retrieved in
|
||||
So values set in request phase hook can be retrieved in
|
||||
response phase hook.
|
||||
.UNINDENT
|
||||
.INDENT 7.0
|
||||
@@ -1871,6 +1999,56 @@ Return true if TLS is used on the connection.
|
||||
.B attribute [R] tls_sni
|
||||
Return the TLS SNI value which client sent in this connection.
|
||||
.UNINDENT
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B attribute [R] tls_client_fingerprint_sha256
|
||||
Return the SHA\-256 fingerprint of a client certificate.
|
||||
.UNINDENT
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B attribute [R] tls_client_fingerprint_sha1
|
||||
Return the SHA\-1 fingerprint of a client certificate.
|
||||
.UNINDENT
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B attribute [R] tls_client_issuer_name
|
||||
Return the issuer name of a client certificate.
|
||||
.UNINDENT
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B attribute [R] tls_client_subject_name
|
||||
Return the subject name of a client certificate.
|
||||
.UNINDENT
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B attribute [R] tls_client_serial
|
||||
Return the serial number of a client certificate.
|
||||
.UNINDENT
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B attribute [R] tls_cipher
|
||||
Return a TLS cipher negotiated in this connection.
|
||||
.UNINDENT
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B attribute [R] tls_protocol
|
||||
Return a TLS protocol version negotiated in this connection.
|
||||
.UNINDENT
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B attribute [R] tls_session_id
|
||||
Return a session ID for this connection in hex string.
|
||||
.UNINDENT
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B attribute [R] tls_session_reused
|
||||
Return true if, and only if a SSL/TLS session is reused.
|
||||
.UNINDENT
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B attribute [R] alpn
|
||||
Return ALPN identifier negotiated in this connection.
|
||||
.UNINDENT
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -2148,7 +2326,7 @@ The replacement is done instantly without breaking existing
|
||||
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
|
||||
The one limitation is that only numeric IP address is allowed in
|
||||
\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\&.
|
||||
|
||||
@@ -57,14 +57,16 @@ Connections
|
||||
which only lacks trailing '*/*' (e.g., path "*/foo/*"
|
||||
matches request path "*/foo*"). If it does not end with
|
||||
"*/*", it performs exact match against the request path.
|
||||
If host is given, it performs exact match against the
|
||||
request host. If host alone is given, "*/*" is appended
|
||||
to it, so that it matches all request paths under the
|
||||
host (e.g., specifying "nghttp2.org" equals to
|
||||
"nghttp2.org/"). CONNECT method is treated specially.
|
||||
It does not have path, and we don't allow empty path.
|
||||
To workaround this, we assume that CONNECT method has
|
||||
"*/*" as path.
|
||||
If host is given, it performs a match against the
|
||||
request host. For a request received on the frontend
|
||||
listener with "sni-fwd" parameter enabled, SNI host is
|
||||
used instead of a request host. If host alone is given,
|
||||
"*/*" is appended to it, so that it matches all request
|
||||
paths under the host (e.g., specifying "nghttp2.org"
|
||||
equals to "nghttp2.org/"). CONNECT method is treated
|
||||
specially. It does not have path, and we don't allow
|
||||
empty path. To workaround this, we assume that CONNECT
|
||||
method has "*/*" as path.
|
||||
|
||||
Patterns with host take precedence over patterns with
|
||||
just path. Then, longer patterns take precedence over
|
||||
@@ -78,6 +80,18 @@ Connections
|
||||
match against "nghttp2.org". The exact hosts match
|
||||
takes precedence over the wildcard hosts match.
|
||||
|
||||
If path part ends with "\*", it is treated as wildcard
|
||||
path. The wildcard path behaves differently from the
|
||||
normal path. For normal path, match is made around the
|
||||
boundary of path component separator,"*/*". On the other
|
||||
hand, the wildcard path does not take into account the
|
||||
path component separator. All paths which include the
|
||||
wildcard path without last "\*" as prefix, and are
|
||||
strictly longer than wildcard path without last "\*" are
|
||||
matched. "\*" must match at least one character. For
|
||||
example, the pattern "*/foo\**" matches "*/foo/*" and
|
||||
"*/foobar*". But it does not match "*/foo*", or "*/fo*".
|
||||
|
||||
If <PATTERN> is omitted or empty string, "*/*" is used as
|
||||
pattern, which matches all request paths (catch-all
|
||||
pattern). The catch-all backend must be given.
|
||||
@@ -150,16 +164,32 @@ Connections
|
||||
The session affinity is enabled using
|
||||
"affinity=<METHOD>" parameter. If "ip" is given in
|
||||
<METHOD>, client IP based session affinity is enabled.
|
||||
If "none" is given in <METHOD>, session affinity is
|
||||
disabled, and this is the default. The session affinity
|
||||
is enabled per <PATTERN>. If at least one backend has
|
||||
"affinity" parameter, and its <METHOD> is not "none",
|
||||
session affinity is enabled for all backend servers
|
||||
sharing the same <PATTERN>. It is advised to set
|
||||
"affinity" parameter to all backend explicitly if
|
||||
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.
|
||||
If "cookie" is given in <METHOD>, cookie based session
|
||||
affinity is enabled. If "none" is given in <METHOD>,
|
||||
session affinity is disabled, and this is the default.
|
||||
The session affinity is enabled per <PATTERN>. If at
|
||||
least one backend has "affinity" parameter, and its
|
||||
<METHOD> is not "none", session affinity is enabled for
|
||||
all backend servers sharing the same <PATTERN>. It is
|
||||
advised to set "affinity" parameter to all backend
|
||||
explicitly if 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.
|
||||
|
||||
If "affinity=cookie" is used, the additional
|
||||
configuration is required.
|
||||
"affinity-cookie-name=<NAME>" must be used to specify a
|
||||
name of cookie to use. Optionally,
|
||||
"affinity-cookie-path=<PATH>" can be used to specify a
|
||||
path which cookie is applied. The optional
|
||||
"affinity-cookie-secure=<SECURE>" controls the Secure
|
||||
attribute of a cookie. The default value is "auto", and
|
||||
the Secure attribute is determined by a request scheme.
|
||||
If a request scheme is "https", then Secure attribute is
|
||||
set. Otherwise, it is not set. If <SECURE> is "yes",
|
||||
the Secure attribute is always set. If <SECURE> is
|
||||
"no", the Secure attribute is always omitted.
|
||||
|
||||
By default, name resolution of backend host name is done
|
||||
at start up, or reloading configuration. If "dns"
|
||||
@@ -205,6 +235,11 @@ Connections
|
||||
Optionally, TLS can be disabled by specifying "no-tls"
|
||||
parameter. TLS is enabled by default.
|
||||
|
||||
If "sni-fwd" parameter is used, when performing a match
|
||||
to select a backend server, SNI host name received from
|
||||
the client is used instead of the request host. See
|
||||
:option:`--backend` option about the pattern match.
|
||||
|
||||
To make this frontend as API endpoint, specify "api"
|
||||
parameter. This is disabled by default. It is
|
||||
important to limit the access to the API frontend.
|
||||
@@ -539,11 +574,14 @@ SSL/TLS
|
||||
|
||||
.. option:: --cacert=<PATH>
|
||||
|
||||
Set path to trusted CA certificate file used in backend
|
||||
TLS connections. The file must be in PEM format. It
|
||||
can contain multiple certificates. If the linked
|
||||
OpenSSL is configured to load system wide certificates,
|
||||
they are loaded at startup regardless of this option.
|
||||
Set path to trusted CA certificate file. It is used in
|
||||
backend TLS connections to verify peer's certificate.
|
||||
It is also used to verify OCSP response from the script
|
||||
set by :option:`--fetch-ocsp-response-file`\. The file must be in
|
||||
PEM format. It can contain multiple certificates. If
|
||||
the linked OpenSSL is configured to load system wide
|
||||
certificates, they are loaded at startup regardless of
|
||||
this option.
|
||||
|
||||
.. option:: --private-key-passwd-file=<PATH>
|
||||
|
||||
@@ -556,12 +594,12 @@ SSL/TLS
|
||||
Specify additional certificate and private key file.
|
||||
nghttpx will choose certificates based on the hostname
|
||||
indicated by client using TLS SNI extension. If nghttpx
|
||||
is built with OpenSSL >= 1.0.2, signature algorithms
|
||||
(e.g., ECDSA+SHA256, RSA+SHA256) presented by client are
|
||||
also taken into consideration. This allows nghttpx to
|
||||
send ECDSA certificate to modern clients, while sending
|
||||
RSA based certificate to older clients. This option can
|
||||
be used multiple times. To make OCSP stapling work,
|
||||
is built with OpenSSL >= 1.0.2, the shared elliptic
|
||||
curves (e.g., P-256) between client and server are also
|
||||
taken into consideration. This allows nghttpx to send
|
||||
ECDSA certificate to modern clients, while sending RSA
|
||||
based certificate to older clients. This option can be
|
||||
used multiple times. To make OCSP stapling work,
|
||||
<CERTPATH> must be absolute path.
|
||||
|
||||
Additional parameter can be specified in <PARAM>. The
|
||||
@@ -617,10 +655,14 @@ SSL/TLS
|
||||
:option:`--tls-min-proto-version` and :option:`\--tls-max-proto-version` are
|
||||
enabled. If the protocol list advertised by client does
|
||||
not overlap this range, you will receive the error
|
||||
message "unknown protocol". The available versions are:
|
||||
message "unknown protocol". If a protocol version lower
|
||||
than TLSv1.2 is specified, make sure that the compatible
|
||||
ciphers are included in :option:`--ciphers` option. The default
|
||||
cipher list only includes ciphers compatible with
|
||||
TLSv1.2 or above. The available versions are:
|
||||
TLSv1.2, TLSv1.1, and TLSv1.0
|
||||
|
||||
Default: ``TLSv1.1``
|
||||
Default: ``TLSv1.2``
|
||||
|
||||
.. option:: --tls-max-proto-version=<VER>
|
||||
|
||||
@@ -736,6 +778,18 @@ SSL/TLS
|
||||
|
||||
Default: ``4h``
|
||||
|
||||
.. option:: --ocsp-startup
|
||||
|
||||
Start accepting connections after initial attempts to
|
||||
get OCSP responses finish. It does not matter some of
|
||||
the attempts fail. This feature is useful if OCSP
|
||||
responses must be available before accepting
|
||||
connections.
|
||||
|
||||
.. option:: --no-verify-ocsp
|
||||
|
||||
nghttpx does not verify OCSP response.
|
||||
|
||||
.. option:: --no-ocsp
|
||||
|
||||
Disable OCSP stapling.
|
||||
@@ -1039,11 +1093,22 @@ Logging
|
||||
* $alpn: ALPN identifier of the protocol which generates
|
||||
the response. For HTTP/1, ALPN is always http/1.1,
|
||||
regardless of minor version.
|
||||
* $ssl_cipher: cipher used for SSL/TLS connection.
|
||||
* $ssl_protocol: protocol for SSL/TLS connection.
|
||||
* $ssl_session_id: session ID for SSL/TLS connection.
|
||||
* $ssl_session_reused: "r" if SSL/TLS session was
|
||||
* $tls_cipher: cipher used for SSL/TLS connection.
|
||||
* $tls_client_fingerprint_sha256: SHA-256 fingerprint of
|
||||
client certificate.
|
||||
* $tls_client_fingerprint_sha1: SHA-1 fingerprint of
|
||||
client certificate.
|
||||
* $tls_client_subject_name: subject name in client
|
||||
certificate.
|
||||
* $tls_client_issuer_name: issuer name in client
|
||||
certificate.
|
||||
* $tls_client_serial: serial number in client
|
||||
certificate.
|
||||
* $tls_protocol: protocol for SSL/TLS connection.
|
||||
* $tls_session_id: session ID for SSL/TLS connection.
|
||||
* $tls_session_reused: "r" if SSL/TLS session was
|
||||
reused. Otherwise, "."
|
||||
* $tls_sni: SNI server name for SSL/TLS connection.
|
||||
* $backend_host: backend host used to fulfill the
|
||||
request. "-" if backend host is not available.
|
||||
* $backend_port: backend port used to fulfill the
|
||||
@@ -1094,6 +1159,19 @@ HTTP
|
||||
Strip X-Forwarded-For header field from inbound client
|
||||
requests.
|
||||
|
||||
.. option:: --no-add-x-forwarded-proto
|
||||
|
||||
Don't append additional X-Forwarded-Proto header field
|
||||
to the backend request. If inbound client sets
|
||||
X-Forwarded-Proto, and
|
||||
:option:`--no-strip-incoming-x-forwarded-proto` option is used,
|
||||
they are passed to the backend.
|
||||
|
||||
.. option:: --no-strip-incoming-x-forwarded-proto
|
||||
|
||||
Don't strip X-Forwarded-Proto header field from inbound
|
||||
client requests.
|
||||
|
||||
.. option:: --add-forwarded=<LIST>
|
||||
|
||||
Append RFC 7239 Forwarded header field with parameters
|
||||
@@ -1332,6 +1410,16 @@ Process
|
||||
Run this program as <USER>. This option is intended to
|
||||
be used to drop root privileges.
|
||||
|
||||
.. option:: --single-process
|
||||
|
||||
Run this program in a single process mode for debugging
|
||||
purpose. Without this option, nghttpx creates at least
|
||||
2 processes: master and worker processes. If this
|
||||
option is used, master and worker are unified into a
|
||||
single process. nghttpx still spawns additional process
|
||||
if neverbleed is used. In the single process mode, the
|
||||
signal handling feature is disabled.
|
||||
|
||||
|
||||
Scripting
|
||||
~~~~~~~~~
|
||||
@@ -1346,7 +1434,9 @@ Misc
|
||||
|
||||
.. option:: --conf=<PATH>
|
||||
|
||||
Load configuration from <PATH>.
|
||||
Load configuration from <PATH>. Please note that
|
||||
nghttpx always tries to read the default configuration
|
||||
file if :option:`--conf` is not given.
|
||||
|
||||
Default: ``/etc/nghttpx/nghttpx.conf``
|
||||
|
||||
@@ -1426,7 +1516,7 @@ Error log
|
||||
<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
|
||||
It is a combination of date and time when the log is written. It
|
||||
is in ISO 8601 format.
|
||||
|
||||
<master-pid>
|
||||
@@ -1548,6 +1638,22 @@ be customized using :option:`--fetch-ocsp-response-file` option.
|
||||
If OCSP query is failed, previous OCSP response, if any, is continued
|
||||
to be used.
|
||||
|
||||
:option:`--fetch-ocsp-response-file` option provides wide range of
|
||||
possibility to manage OCSP response. It can take an arbitrary script
|
||||
or executable. The requirement is that it supports the command-line
|
||||
interface of ``fetch-ocsp-response`` script, and it must return a
|
||||
valid DER encoded OCSP response on success. It must return exit code
|
||||
0 on success, and 75 for temporary error, and the other error code for
|
||||
generic failure. For large cluster of servers, it is not efficient
|
||||
for each server to perform OCSP query using ``fetch-ocsp-response``.
|
||||
Instead, you can retrieve OCSP response in some way, and store it in a
|
||||
disk or a shared database. Then specify a program in
|
||||
:option:`--fetch-ocsp-response-file` to fetch it from those stores.
|
||||
This could provide a way to share the OCSP response between fleet of
|
||||
servers, and also any OCSP query strategy can be applied which may be
|
||||
beyond the ability of nghttpx itself or ``fetch-ocsp-response``
|
||||
script.
|
||||
|
||||
TLS SESSION RESUMPTION
|
||||
----------------------
|
||||
|
||||
@@ -1561,7 +1667,7 @@ By default, session ID is shared by all worker threads.
|
||||
|
||||
If :option:`--tls-session-cache-memcached` is given, nghttpx will
|
||||
insert serialized session data to memcached with
|
||||
``nghttpx:tls-session-cache:`` + lowercased hex string of session ID
|
||||
``nghttpx:tls-session-cache:`` + lowercase hex string of session ID
|
||||
as a memcached entry key, with expiry time 12 hours. Session timeout
|
||||
is set to 12 hours.
|
||||
|
||||
@@ -1643,6 +1749,14 @@ MRUBY SCRIPTING
|
||||
The current mruby extension API is experimental and not frozen. The
|
||||
API is subject to change in the future release.
|
||||
|
||||
.. warning::
|
||||
|
||||
Almost all string value returned from method, or attribute is a
|
||||
fresh new mruby string, which involves memory allocation, and
|
||||
copies. Therefore, it is strongly recommended to store a return
|
||||
value in a local variable, and use it, instead of calling method or
|
||||
accessing attribute repeatedly.
|
||||
|
||||
nghttpx allows users to extend its capability using mruby scripts.
|
||||
nghttpx has 2 hook points to execute mruby script: request phase and
|
||||
response phase. The request phase hook is invoked after all request
|
||||
@@ -1689,7 +1803,7 @@ respectively.
|
||||
.. rb:attr_reader:: ctx
|
||||
|
||||
Return Ruby hash object. It persists until request finishes.
|
||||
So values set in request phase hoo can be retrieved in
|
||||
So values set in request phase hook can be retrieved in
|
||||
response phase hook.
|
||||
|
||||
.. rb:attr_reader:: phase
|
||||
@@ -1721,6 +1835,46 @@ respectively.
|
||||
|
||||
Return the TLS SNI value which client sent in this connection.
|
||||
|
||||
.. rb:attr_reader:: tls_client_fingerprint_sha256
|
||||
|
||||
Return the SHA-256 fingerprint of a client certificate.
|
||||
|
||||
.. rb:attr_reader:: tls_client_fingerprint_sha1
|
||||
|
||||
Return the SHA-1 fingerprint of a client certificate.
|
||||
|
||||
.. rb:attr_reader:: tls_client_issuer_name
|
||||
|
||||
Return the issuer name of a client certificate.
|
||||
|
||||
.. rb:attr_reader:: tls_client_subject_name
|
||||
|
||||
Return the subject name of a client certificate.
|
||||
|
||||
.. rb:attr_reader:: tls_client_serial
|
||||
|
||||
Return the serial number of a client certificate.
|
||||
|
||||
.. rb:attr_reader:: tls_cipher
|
||||
|
||||
Return a TLS cipher negotiated in this connection.
|
||||
|
||||
.. rb:attr_reader:: tls_protocol
|
||||
|
||||
Return a TLS protocol version negotiated in this connection.
|
||||
|
||||
.. rb:attr_reader:: tls_session_id
|
||||
|
||||
Return a session ID for this connection in hex string.
|
||||
|
||||
.. rb:attr_reader:: tls_session_reused
|
||||
|
||||
Return true if, and only if a SSL/TLS session is reused.
|
||||
|
||||
.. rb:attr_reader:: alpn
|
||||
|
||||
Return ALPN identifier negotiated in this connection.
|
||||
|
||||
.. rb:class:: Request
|
||||
|
||||
Object to represent request from client. The modification to
|
||||
@@ -1965,7 +2119,7 @@ The replacement is done instantly without breaking existing
|
||||
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
|
||||
The one limitation is that only numeric IP address is allowed in
|
||||
: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`.
|
||||
|
||||
@@ -49,7 +49,7 @@ Error log
|
||||
<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
|
||||
It is a combination of date and time when the log is written. It
|
||||
is in ISO 8601 format.
|
||||
|
||||
<master-pid>
|
||||
@@ -171,6 +171,22 @@ be customized using :option:`--fetch-ocsp-response-file` option.
|
||||
If OCSP query is failed, previous OCSP response, if any, is continued
|
||||
to be used.
|
||||
|
||||
:option:`--fetch-ocsp-response-file` option provides wide range of
|
||||
possibility to manage OCSP response. It can take an arbitrary script
|
||||
or executable. The requirement is that it supports the command-line
|
||||
interface of ``fetch-ocsp-response`` script, and it must return a
|
||||
valid DER encoded OCSP response on success. It must return exit code
|
||||
0 on success, and 75 for temporary error, and the other error code for
|
||||
generic failure. For large cluster of servers, it is not efficient
|
||||
for each server to perform OCSP query using ``fetch-ocsp-response``.
|
||||
Instead, you can retrieve OCSP response in some way, and store it in a
|
||||
disk or a shared database. Then specify a program in
|
||||
:option:`--fetch-ocsp-response-file` to fetch it from those stores.
|
||||
This could provide a way to share the OCSP response between fleet of
|
||||
servers, and also any OCSP query strategy can be applied which may be
|
||||
beyond the ability of nghttpx itself or ``fetch-ocsp-response``
|
||||
script.
|
||||
|
||||
TLS SESSION RESUMPTION
|
||||
----------------------
|
||||
|
||||
@@ -184,7 +200,7 @@ By default, session ID is shared by all worker threads.
|
||||
|
||||
If :option:`--tls-session-cache-memcached` is given, nghttpx will
|
||||
insert serialized session data to memcached with
|
||||
``nghttpx:tls-session-cache:`` + lowercased hex string of session ID
|
||||
``nghttpx:tls-session-cache:`` + lowercase hex string of session ID
|
||||
as a memcached entry key, with expiry time 12 hours. Session timeout
|
||||
is set to 12 hours.
|
||||
|
||||
@@ -266,6 +282,14 @@ MRUBY SCRIPTING
|
||||
The current mruby extension API is experimental and not frozen. The
|
||||
API is subject to change in the future release.
|
||||
|
||||
.. warning::
|
||||
|
||||
Almost all string value returned from method, or attribute is a
|
||||
fresh new mruby string, which involves memory allocation, and
|
||||
copies. Therefore, it is strongly recommended to store a return
|
||||
value in a local variable, and use it, instead of calling method or
|
||||
accessing attribute repeatedly.
|
||||
|
||||
nghttpx allows users to extend its capability using mruby scripts.
|
||||
nghttpx has 2 hook points to execute mruby script: request phase and
|
||||
response phase. The request phase hook is invoked after all request
|
||||
@@ -312,7 +336,7 @@ respectively.
|
||||
.. rb:attr_reader:: ctx
|
||||
|
||||
Return Ruby hash object. It persists until request finishes.
|
||||
So values set in request phase hoo can be retrieved in
|
||||
So values set in request phase hook can be retrieved in
|
||||
response phase hook.
|
||||
|
||||
.. rb:attr_reader:: phase
|
||||
@@ -344,6 +368,46 @@ respectively.
|
||||
|
||||
Return the TLS SNI value which client sent in this connection.
|
||||
|
||||
.. rb:attr_reader:: tls_client_fingerprint_sha256
|
||||
|
||||
Return the SHA-256 fingerprint of a client certificate.
|
||||
|
||||
.. rb:attr_reader:: tls_client_fingerprint_sha1
|
||||
|
||||
Return the SHA-1 fingerprint of a client certificate.
|
||||
|
||||
.. rb:attr_reader:: tls_client_issuer_name
|
||||
|
||||
Return the issuer name of a client certificate.
|
||||
|
||||
.. rb:attr_reader:: tls_client_subject_name
|
||||
|
||||
Return the subject name of a client certificate.
|
||||
|
||||
.. rb:attr_reader:: tls_client_serial
|
||||
|
||||
Return the serial number of a client certificate.
|
||||
|
||||
.. rb:attr_reader:: tls_cipher
|
||||
|
||||
Return a TLS cipher negotiated in this connection.
|
||||
|
||||
.. rb:attr_reader:: tls_protocol
|
||||
|
||||
Return a TLS protocol version negotiated in this connection.
|
||||
|
||||
.. rb:attr_reader:: tls_session_id
|
||||
|
||||
Return a session ID for this connection in hex string.
|
||||
|
||||
.. rb:attr_reader:: tls_session_reused
|
||||
|
||||
Return true if, and only if a SSL/TLS session is reused.
|
||||
|
||||
.. rb:attr_reader:: alpn
|
||||
|
||||
Return ALPN identifier negotiated in this connection.
|
||||
|
||||
.. rb:class:: Request
|
||||
|
||||
Object to represent request from client. The modification to
|
||||
@@ -588,7 +652,7 @@ The replacement is done instantly without breaking existing
|
||||
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
|
||||
The one limitation is that only numeric IP address is allowed in
|
||||
: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`.
|
||||
|
||||
@@ -116,7 +116,10 @@ briefly describe what the library does in this area. In the following
|
||||
description, without loss of generality we omit CONTINUATION frame
|
||||
since they must follow HEADERS frame and are processed atomically. In
|
||||
other words, they are just one big HEADERS frame. To disable these
|
||||
validations, use `nghttp2_option_set_no_http_messaging()`.
|
||||
validations, use `nghttp2_option_set_no_http_messaging()`. Please
|
||||
note that disabling this feature does not change the fundamental
|
||||
client and server model of HTTP. That is, even if the validation is
|
||||
disabled, only client can send requests.
|
||||
|
||||
For HTTP request, including those carried by PUSH_PROMISE, HTTP
|
||||
message starts with one HEADERS frame containing request headers. It
|
||||
@@ -149,13 +152,11 @@ header fields must not appear: "Connection", "Keep-Alive",
|
||||
Each header field name and value must obey the field-name and
|
||||
field-value production rules described in `RFC 7230, section
|
||||
3.2. <https://tools.ietf.org/html/rfc7230#section-3.2>`_.
|
||||
Additionally, all field name must be lower cased. While the pseudo
|
||||
header fields must satisfy these rules, we just ignore illegal regular
|
||||
headers (this means that these header fields are not passed to
|
||||
application callback). This is because these illegal header fields
|
||||
are floating around in existing internet and resetting stream just
|
||||
because of this may break many web sites. This is especially true if
|
||||
we forward to or translate from HTTP/1 traffic.
|
||||
Additionally, all field name must be lower cased. The invalid header
|
||||
fields are treated as stream error, and that stream is reset. If
|
||||
application wants to treat these headers in their own way, use
|
||||
`nghttp2_on_invalid_header_callback
|
||||
<https://nghttp2.org/documentation/types.html#c.nghttp2_on_invalid_header_callback>`_.
|
||||
|
||||
For "http" or "https" URIs, ":path" pseudo header fields must start
|
||||
with "/". The only exception is OPTIONS request, in that case, "*" is
|
||||
|
||||
@@ -2,7 +2,7 @@ Building Android binary
|
||||
=======================
|
||||
|
||||
In this article, we briefly describe how to build Android binary using
|
||||
`Android NDK <http://developer.android.com/tools/sdk/ndk/index.html>`_
|
||||
`Android NDK <https://developer.android.com/ndk/index.html>`_
|
||||
cross-compiler on Debian Linux.
|
||||
|
||||
The easiest way to build android binary is use Dockerfile.android.
|
||||
|
||||
@@ -26,8 +26,7 @@ Coding style
|
||||
We use clang-format to format source code consistently. The
|
||||
clang-format configuration file .clang-format is located at the root
|
||||
directory. Since clang-format produces slightly different results
|
||||
between versions, we currently use clang-format which comes with
|
||||
clang-3.9.
|
||||
between versions, we currently use clang-format-5.0.
|
||||
|
||||
To detect any violation to the coding style, we recommend to setup git
|
||||
pre-commit hook to check coding style of the changes you introduced.
|
||||
@@ -35,7 +34,7 @@ The pre-commit file is located at the root directory. Copy it under
|
||||
.git/hooks and make sure that it is executable. The pre-commit script
|
||||
uses clang-format-diff.py to detect any style errors. If it is not in
|
||||
your PATH or it exists under different name (e.g.,
|
||||
clang-format-diff-3.9 in debian), either add it to PATH variable or
|
||||
clang-format-diff-5.0 in debian), either add it to PATH variable or
|
||||
add git option ``clangformatdiff.binary`` to point to the script.
|
||||
|
||||
For emacs users, integrating clang-format to emacs is very easy.
|
||||
|
||||
@@ -12,7 +12,7 @@ Compiling from source
|
||||
---------------------
|
||||
|
||||
h2load is compiled alongside nghttp2 and requires that the
|
||||
``--enable-apps`` flag is passed to ``./configure`` and `required
|
||||
``--enable-app`` flag is passed to ``./configure`` and `required
|
||||
dependencies <https://github.com/nghttp2/nghttp2#requirements>`_ are
|
||||
available during compilation. For details on compiling, see `nghttp2:
|
||||
Building from Git
|
||||
@@ -64,6 +64,25 @@ The benchmarking result looks like this:
|
||||
See the h2load manual page :ref:`h2load-1-output` section for the
|
||||
explanation of the above numbers.
|
||||
|
||||
Timing-based load-testing
|
||||
-------------------------
|
||||
|
||||
As of v1.26.0, h2load supports timing-based load-testing. This method
|
||||
performs load-testing in terms of a given duration instead of a
|
||||
pre-defined number of requests. The new option :option:`--duration`
|
||||
specifies how long the load-testing takes. For example,
|
||||
``--duration=10`` makes h2load perform load-testing against a server
|
||||
for 10 seconds. You can also specify a “warming-up” period with
|
||||
:option:`--warm-up-time`. If :option:`--duration` is used,
|
||||
:option:`-n` option is ignored.
|
||||
|
||||
The following command performs load-testing for 10 seconds after 5
|
||||
seconds warming up period:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ h2load -c100 -m100 --duration=10 --warm-up-time=5 https://localhost
|
||||
|
||||
Flow Control
|
||||
------------
|
||||
|
||||
|
||||
@@ -290,7 +290,7 @@ Normally, client does not stop even after all requests are done unless
|
||||
connection is lost. To stop client, call
|
||||
``nghttp2::asio_http2::server::session::shutdown()``.
|
||||
|
||||
Recieve server push and enable SSL/TLS
|
||||
Receive server push and enable SSL/TLS
|
||||
++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
@@ -26,7 +26,7 @@ protocol selection will be done via ALPN or NPN.
|
||||
|
||||
To turn off encryption on frontend connection, use ``no-tls`` keyword
|
||||
in :option:`--frontend` option. In this case, SPDY protocol is not
|
||||
available even if spdylay library is liked to nghttpx. HTTP/2 and
|
||||
available even if spdylay library is linked to nghttpx. HTTP/2 and
|
||||
HTTP/1 are available on the frontend, and an HTTP/1 connection can be
|
||||
upgraded to HTTP/2 using HTTP Upgrade. Starting HTTP/2 connection by
|
||||
sending HTTP/2 connection preface is also supported.
|
||||
@@ -45,17 +45,17 @@ that default backend protocol is HTTP/1.1. To use HTTP/2 in backend,
|
||||
you have to specify ``h2`` in ``proto`` keyword in :option:`--backend`
|
||||
explicitly.
|
||||
|
||||
The backend is supposed to be Web server. For example, to make
|
||||
The backend is supposed to be a Web server. For example, to make
|
||||
nghttpx listen to encrypted HTTP/2 requests at port 8443, and a
|
||||
backend Web server is configured to listen to HTTP request at port
|
||||
8080 in the same host, run nghttpx command-line like this:
|
||||
backend Web server is configured to listen to HTTP requests at port
|
||||
8080 on the same host, run nghttpx command-line like this:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ nghttpx -f0.0.0.0,8443 -b127.0.0.1,8080 /path/to/server.key /path/to/server.crt
|
||||
|
||||
Then HTTP/2 enabled client can access to the nghttpx in HTTP/2. For
|
||||
example, you can send GET request to the server using nghttp:
|
||||
Then an HTTP/2 enabled client can access the nghttpx server using HTTP/2. For
|
||||
example, you can send a GET request using nghttp:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
@@ -66,19 +66,19 @@ HTTP/2 proxy mode
|
||||
|
||||
If nghttpx is invoked with :option:`--http2-proxy` (or its shorthand
|
||||
:option:`-s`) option, it operates in HTTP/2 proxy mode. The supported
|
||||
protocols in frontend and backend connections are the same in `default
|
||||
mode`_. The difference is that this mode acts like forward proxy and
|
||||
assumes the backend is HTTP proxy server (e.g., Squid, Apache Traffic
|
||||
Server). HTTP/1 request must include absolute URI in request line.
|
||||
protocols in frontend and backend connections are the same as in `default
|
||||
mode`_. The difference is that this mode acts like a forward proxy and
|
||||
assumes the backend is an HTTP proxy server (e.g., Squid, Apache Traffic
|
||||
Server). HTTP/1 requests must include an absolute URI in request line.
|
||||
|
||||
By default, frontend connection is encrypted. So this mode is also
|
||||
By default, the frontend connection is encrypted. So this mode is also
|
||||
called secure proxy. If nghttpx is linked with spdylay, it supports
|
||||
SPDY protocols and it works as so called SPDY proxy.
|
||||
|
||||
To turn off encryption on frontend connection, use ``no-tls`` keyword
|
||||
To turn off encryption on the frontend connection, use ``no-tls`` keyword
|
||||
in :option:`--frontend` option.
|
||||
|
||||
The backend must be HTTP proxy server. nghttpx supports multiple
|
||||
The backend must be an HTTP proxy server. nghttpx supports multiple
|
||||
backend server addresses. It translates incoming requests to HTTP
|
||||
request to backend server. The backend server performs real proxy
|
||||
work for each request, for example, dispatching requests to the origin
|
||||
@@ -92,7 +92,7 @@ connection, use :option:`--backend` option, and specify ``h2`` in
|
||||
|
||||
For example, to make nghttpx listen to encrypted HTTP/2 requests at
|
||||
port 8443, and a backend HTTP proxy server is configured to listen to
|
||||
HTTP/1 request at port 8080 in the same host, run nghttpx command-line
|
||||
HTTP/1 requests at port 8080 on the same host, run nghttpx command-line
|
||||
like this:
|
||||
|
||||
.. code-block:: text
|
||||
@@ -297,13 +297,31 @@ When you write this option in command-line, you should enclose
|
||||
argument with single or double quotes, since the character ``;`` has a
|
||||
special meaning in shell.
|
||||
|
||||
To route, request to request path whose prefix is ``/foo`` to backend
|
||||
server ``[::1]:8080``, you can write like so:
|
||||
To route, request to request path ``/foo`` to backend server
|
||||
``[::1]:8080``, you can write like so:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
backend=::1,8080;/foo
|
||||
|
||||
If the last character of path pattern is ``/``, all request paths
|
||||
which start with that pattern match:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
backend=::1,8080;/bar/
|
||||
|
||||
The request path ``/bar/buzz`` matches the ``/bar/``.
|
||||
|
||||
You can use ``*`` at the end of the path pattern to make it wildcard
|
||||
pattern. ``*`` must match at least one character:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
backend=::1,8080;/sample*
|
||||
|
||||
The request path ``/sample1/foo`` matches the ``/sample*`` pattern.
|
||||
|
||||
Of course, you can specify both host and request path at the same
|
||||
time:
|
||||
|
||||
@@ -371,7 +389,7 @@ parameter in :option:`--backend` option, like so:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
backend=foo.example.com;;dns
|
||||
backend=foo.example.com,80;;dns
|
||||
|
||||
nghttpx will cache resolved addresses for certain period of time. To
|
||||
change this cache period, use :option:`--dns-cache-timeout`.
|
||||
@@ -387,6 +405,28 @@ like so:
|
||||
|
||||
frontend=*,443;proxyproto
|
||||
|
||||
Session affinity
|
||||
----------------
|
||||
|
||||
Two kinds of session affinity are available: client IP, and HTTP
|
||||
Cookie.
|
||||
|
||||
To enable client IP based affinity, specify ``affinity=ip`` parameter
|
||||
in :option:`--backend` option. If PROXY protocol is enabled, then an
|
||||
address obtained from PROXY protocol is taken into consideration.
|
||||
|
||||
To enable HTTP Cookie based affinity, specify ``affinity=cookie``
|
||||
parameter, and specify a name of cookie in ``affinity-cookie-name``
|
||||
parameter. Optionally, a Path attribute can be specified in
|
||||
``affinity-cookie-path`` parameter:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
backend=127.0.0.1,3000;;affinity=cookie;affinity-cookie-name=nghttpxlb;affinity-cookie-path=/
|
||||
|
||||
Secure attribute of cookie is set if client connection is protected by
|
||||
TLS.
|
||||
|
||||
PSK cipher suites
|
||||
-----------------
|
||||
|
||||
|
||||
@@ -124,6 +124,7 @@ remote server. It's defined as::
|
||||
bev = bufferevent_openssl_socket_new(
|
||||
evbase, -1, ssl, BUFFEREVENT_SSL_CONNECTING,
|
||||
BEV_OPT_DEFER_CALLBACKS | BEV_OPT_CLOSE_ON_FREE);
|
||||
bufferevent_enable(bev, EV_READ | EV_WRITE);
|
||||
bufferevent_setcb(bev, readcb, writecb, eventcb, session_data);
|
||||
rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase,
|
||||
AF_UNSPEC, host, port);
|
||||
|
||||
@@ -7,11 +7,8 @@ if(ENABLE_EXAMPLES)
|
||||
COMPILE_FLAGS "${WARNCXXFLAGS} ${CXX1XCXXFLAGS}")
|
||||
|
||||
include_directories(
|
||||
${CMAKE_SOURCE_DIR}
|
||||
${CMAKE_SOURCE_DIR}/lib/includes
|
||||
${CMAKE_BINARY_DIR}/lib/includes
|
||||
${CMAKE_SOURCE_DIR}/src/includes
|
||||
${CMAKE_SOURCE_DIR}/third-party
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../third-party"
|
||||
|
||||
${LIBEVENT_INCLUDE_DIRS}
|
||||
${OPENSSL_INCLUDE_DIRS}
|
||||
|
||||
@@ -62,11 +62,11 @@ ASIOCPPFLAGS = ${AM_CPPFLAGS} ${BOOST_CPPFLAGS}
|
||||
ASIOLDADD = $(top_builddir)/lib/libnghttp2.la \
|
||||
$(top_builddir)/src/libnghttp2_asio.la @JEMALLOC_LIBS@ \
|
||||
$(top_builddir)/third-party/libhttp-parser.la \
|
||||
@OPENSSL_LIBS@ \
|
||||
${BOOST_LDFLAGS} \
|
||||
${BOOST_ASIO_LIB} \
|
||||
${BOOST_THREAD_LIB} \
|
||||
${BOOST_SYSTEM_LIB} \
|
||||
@OPENSSL_LIBS@ \
|
||||
@APPLDFLAGS@
|
||||
|
||||
asio_sv_SOURCES = asio-sv.cc
|
||||
|
||||
@@ -548,6 +548,7 @@ static void initiate_connection(struct event_base *evbase, SSL_CTX *ssl_ctx,
|
||||
bev = bufferevent_openssl_socket_new(
|
||||
evbase, -1, ssl, BUFFEREVENT_SSL_CONNECTING,
|
||||
BEV_OPT_DEFER_CALLBACKS | BEV_OPT_CLOSE_ON_FREE);
|
||||
bufferevent_enable(bev, EV_READ | EV_WRITE);
|
||||
bufferevent_setcb(bev, readcb, writecb, eventcb, session_data);
|
||||
rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase,
|
||||
AF_UNSPEC, host, port);
|
||||
|
||||
@@ -250,6 +250,7 @@ static http2_session_data *create_http2_session_data(app_context *app_ctx,
|
||||
session_data->bev = bufferevent_openssl_socket_new(
|
||||
app_ctx->evbase, fd, ssl, BUFFEREVENT_SSL_ACCEPTING,
|
||||
BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
|
||||
bufferevent_enable(session_data->bev, EV_READ | EV_WRITE);
|
||||
rv = getnameinfo(addr, (socklen_t)addrlen, host, sizeof(host), NULL, 0,
|
||||
NI_NUMERICHOST);
|
||||
if (rv != 0) {
|
||||
|
||||
@@ -23,8 +23,8 @@ following compiler/linker flags:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
CPPFLAGS="-fsanitize-coverage=edge -fsanitize=addres"
|
||||
LDFLAGS="-fsanitize-coverage=edge -fsanitize=addres"
|
||||
CPPFLAGS="-fsanitize-coverage=edge -fsanitize=address"
|
||||
LDFLAGS="-fsanitize-coverage=edge -fsanitize=address"
|
||||
|
||||
Then, fuzz_target.cc can be built using the following command:
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ HEADERS = [
|
||||
"user-agent",
|
||||
"date",
|
||||
"content-type",
|
||||
"nghttpx-0rtt-uniq",
|
||||
# disallowed h1 headers
|
||||
'connection',
|
||||
'keep-alive',
|
||||
|
||||
@@ -163,6 +163,16 @@ OPTIONS = [
|
||||
"redirect-https-port",
|
||||
"frontend-max-requests",
|
||||
"single-thread",
|
||||
"single-process",
|
||||
"no-add-x-forwarded-proto",
|
||||
"no-strip-incoming-x-forwarded-proto",
|
||||
"ocsp-startup",
|
||||
"no-verify-ocsp",
|
||||
"tls-anti-replay-memcached",
|
||||
"tls-anti-replay-memcached-cert-file",
|
||||
"tls-anti-replay-memcached-private-key-file",
|
||||
"tls-anti-replay-memcached-address-family",
|
||||
"no-strip-incoming-nghttpx-0rtt-uniq",
|
||||
]
|
||||
|
||||
LOGVARS = [
|
||||
@@ -181,6 +191,16 @@ LOGVARS = [
|
||||
"ssl_protocol",
|
||||
"ssl_session_id",
|
||||
"ssl_session_reused",
|
||||
"tls_cipher",
|
||||
"tls_protocol",
|
||||
"tls_session_id",
|
||||
"tls_session_reused",
|
||||
"tls_sni",
|
||||
"tls_client_fingerprint_sha256",
|
||||
"tls_client_fingerprint_sha1",
|
||||
"tls_client_subject_name",
|
||||
"tls_client_issuer_name",
|
||||
"tls_client_serial",
|
||||
"backend_host",
|
||||
"backend_port",
|
||||
]
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"golang.org/x/net/websocket"
|
||||
"io"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -125,6 +126,54 @@ Content-Length: 0
|
||||
// }
|
||||
// }
|
||||
|
||||
// TestH1H1AffinityCookie tests that affinity cookie is sent back in
|
||||
// cleartext http.
|
||||
func TestH1H1AffinityCookie(t *testing.T) {
|
||||
st := newServerTester([]string{"--affinity-cookie"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http1(requestParam{
|
||||
name: "TestH1H1AffinityCookie",
|
||||
})
|
||||
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)
|
||||
}
|
||||
|
||||
const pattern = `affinity=[0-9a-f]{8}; Path=/foo/bar`
|
||||
validCookie := regexp.MustCompile(pattern)
|
||||
if got := res.header.Get("Set-Cookie"); !validCookie.MatchString(got) {
|
||||
t.Errorf("Set-Cookie: %v; want pattern %v", got, pattern)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H1AffinityCookieTLS tests that affinity cookie is sent back
|
||||
// in https.
|
||||
func TestH1H1AffinityCookieTLS(t *testing.T) {
|
||||
st := newServerTesterTLS([]string{"--alpn-h1", "--affinity-cookie"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http1(requestParam{
|
||||
name: "TestH1H1AffinityCookieTLS",
|
||||
})
|
||||
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)
|
||||
}
|
||||
|
||||
const pattern = `affinity=[0-9a-f]{8}; Path=/foo/bar; Secure`
|
||||
validCookie := regexp.MustCompile(pattern)
|
||||
if got := res.header.Get("Set-Cookie"); !validCookie.MatchString(got) {
|
||||
t.Errorf("Set-Cookie: %v; want pattern %v", got, pattern)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H1GracefulShutdown tests graceful shutdown.
|
||||
func TestH1H1GracefulShutdown(t *testing.T) {
|
||||
st := newServerTester(nil, t, noopHandler)
|
||||
@@ -162,7 +211,7 @@ func TestH1H1GracefulShutdown(t *testing.T) {
|
||||
want := io.EOF
|
||||
b := make([]byte, 256)
|
||||
if _, err := st.conn.Read(b); err == nil || err != want {
|
||||
t.Errorf("st.conn.Read(): %v; want %v, %v", err, want)
|
||||
t.Errorf("st.conn.Read(): %v; want %v", err, want)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,105 @@ func TestH2H1PlainGET(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1AddXfp tests that server appends :scheme to the existing
|
||||
// x-forwarded-proto header field.
|
||||
func TestH2H1AddXfp(t *testing.T) {
|
||||
st := newServerTester([]string{"--no-strip-incoming-x-forwarded-proto"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
xfp := r.Header.Get("X-Forwarded-Proto")
|
||||
if got, want := xfp, "foo, http"; got != want {
|
||||
t.Errorf("X-Forwarded-Proto = %q; want %q", got, want)
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1AddXfp",
|
||||
header: []hpack.HeaderField{
|
||||
pair("x-forwarded-proto", "foo"),
|
||||
},
|
||||
})
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1NoAddXfp tests that server does not append :scheme to the
|
||||
// existing x-forwarded-proto header field.
|
||||
func TestH2H1NoAddXfp(t *testing.T) {
|
||||
st := newServerTester([]string{"--no-add-x-forwarded-proto", "--no-strip-incoming-x-forwarded-proto"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
xfp := r.Header.Get("X-Forwarded-Proto")
|
||||
if got, want := xfp, "foo"; got != want {
|
||||
t.Errorf("X-Forwarded-Proto = %q; want %q", got, want)
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1NoAddXfp",
|
||||
header: []hpack.HeaderField{
|
||||
pair("x-forwarded-proto", "foo"),
|
||||
},
|
||||
})
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1StripXfp tests that server strips incoming
|
||||
// x-forwarded-proto header field.
|
||||
func TestH2H1StripXfp(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
xfp := r.Header.Get("X-Forwarded-Proto")
|
||||
if got, want := xfp, "http"; got != want {
|
||||
t.Errorf("X-Forwarded-Proto = %q; want %q", got, want)
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1StripXfp",
|
||||
header: []hpack.HeaderField{
|
||||
pair("x-forwarded-proto", "foo"),
|
||||
},
|
||||
})
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1StripNoAddXfp tests that server strips incoming
|
||||
// x-forwarded-proto header field, and does not add another.
|
||||
func TestH2H1StripNoAddXfp(t *testing.T) {
|
||||
st := newServerTester([]string{"--no-add-x-forwarded-proto"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if got, found := r.Header["X-Forwarded-Proto"]; found {
|
||||
t.Errorf("X-Forwarded-Proto = %q; want nothing", got)
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1StripNoAddXfp",
|
||||
header: []hpack.HeaderField{
|
||||
pair("x-forwarded-proto", "foo"),
|
||||
},
|
||||
})
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1AddXff tests that server generates X-Forwarded-For header
|
||||
// field when forwarding request to backend.
|
||||
func TestH2H1AddXff(t *testing.T) {
|
||||
@@ -1014,14 +1113,45 @@ func TestH2H1Upgrade(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1ProxyProtocolV1ForwardedForObfuscated tests that Forwarded
|
||||
// header field includes obfuscated address even if PROXY protocol
|
||||
// version 1 containing TCP4 entry is accepted.
|
||||
func TestH2H1ProxyProtocolV1ForwardedForObfuscated(t *testing.T) {
|
||||
pattern := fmt.Sprintf(`^for=_[^;]+$`)
|
||||
validFwd := regexp.MustCompile(pattern)
|
||||
st := newServerTester([]string{"--accept-proxy-protocol", "--add-x-forwarded-for", "--add-forwarded=for", "--forwarded-for=obfuscated"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if got := r.Header.Get("Forwarded"); !validFwd.MatchString(got) {
|
||||
t.Errorf("Forwarded: %v; want pattern %v", got, pattern)
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
st.conn.Write([]byte("PROXY TCP4 192.168.0.2 192.168.0.100 12345 8080\r\n"))
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1ProxyProtocolV1ForwardedForObfuscated",
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("res.status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1ProxyProtocolV1TCP4 tests PROXY protocol version 1
|
||||
// containing TCP4 entry is accepted and X-Forwarded-For contains
|
||||
// advertised src address.
|
||||
func TestH2H1ProxyProtocolV1TCP4(t *testing.T) {
|
||||
st := newServerTester([]string{"--accept-proxy-protocol", "--add-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
st := newServerTester([]string{"--accept-proxy-protocol", "--add-x-forwarded-for", "--add-forwarded=for", "--forwarded-for=ip"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if got, want := r.Header.Get("X-Forwarded-For"), "192.168.0.2"; got != want {
|
||||
t.Errorf("X-Forwarded-For: %v; want %v", got, want)
|
||||
}
|
||||
if got, want := r.Header.Get("Forwarded"), "for=192.168.0.2"; got != want {
|
||||
t.Errorf("Forwarded: %v; want %v", got, want)
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
@@ -1044,10 +1174,13 @@ func TestH2H1ProxyProtocolV1TCP4(t *testing.T) {
|
||||
// containing TCP6 entry is accepted and X-Forwarded-For contains
|
||||
// advertised src address.
|
||||
func TestH2H1ProxyProtocolV1TCP6(t *testing.T) {
|
||||
st := newServerTester([]string{"--accept-proxy-protocol", "--add-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
st := newServerTester([]string{"--accept-proxy-protocol", "--add-x-forwarded-for", "--add-forwarded=for", "--forwarded-for=ip"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if got, want := r.Header.Get("X-Forwarded-For"), "2001:0db8:85a3:0000:0000:8a2e:0370:7334"; got != want {
|
||||
t.Errorf("X-Forwarded-For: %v; want %v", got, want)
|
||||
}
|
||||
if got, want := r.Header.Get("Forwarded"), `for="[2001:0db8:85a3:0000:0000:8a2e:0370:7334]"`; got != want {
|
||||
t.Errorf("Forwarded: %v; want %v", got, want)
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
@@ -1069,9 +1202,12 @@ func TestH2H1ProxyProtocolV1TCP6(t *testing.T) {
|
||||
// TestH2H1ProxyProtocolV1Unknown tests PROXY protocol version 1
|
||||
// containing UNKNOWN entry is accepted.
|
||||
func TestH2H1ProxyProtocolV1Unknown(t *testing.T) {
|
||||
st := newServerTester([]string{"--accept-proxy-protocol", "--add-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
st := newServerTester([]string{"--accept-proxy-protocol", "--add-x-forwarded-for", "--add-forwarded=for", "--forwarded-for=ip"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if got, notWant := r.Header.Get("X-Forwarded-For"), "192.168.0.2"; got == notWant {
|
||||
t.Errorf("X-Forwarded-For: %v")
|
||||
t.Errorf("X-Forwarded-For: %v; want something else", got)
|
||||
}
|
||||
if got, notWant := r.Header.Get("Forwarded"), "for=192.168.0.2"; got == notWant {
|
||||
t.Errorf("Forwarded: %v; want something else", got)
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
@@ -1449,6 +1585,175 @@ func TestH2H1HTTPSRedirectPort(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1Code204 tests that 204 response without content-length, and
|
||||
// transfer-encoding is valid.
|
||||
func TestH2H1Code204(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1Code204",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.status, 204; got != want {
|
||||
t.Errorf("status = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1Code204CL0 tests that 204 response with content-length: 0
|
||||
// is allowed.
|
||||
func TestH2H1Code204CL0(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
hj, ok := w.(http.Hijacker)
|
||||
if !ok {
|
||||
http.Error(w, "Could not hijack the connection", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
conn, bufrw, err := hj.Hijack()
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
bufrw.WriteString("HTTP/1.1 204\r\nContent-Length: 0\r\n\r\n")
|
||||
bufrw.Flush()
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1Code204CL0",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.status, 204; got != want {
|
||||
t.Errorf("status = %v; want %v", got, want)
|
||||
}
|
||||
|
||||
if got, found := res.header["Content-Length"]; found {
|
||||
t.Errorf("Content-Length = %v, want nothing", got)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1Code204CLNonzero tests that 204 response with nonzero
|
||||
// content-length is not allowed.
|
||||
func TestH2H1Code204CLNonzero(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
hj, ok := w.(http.Hijacker)
|
||||
if !ok {
|
||||
http.Error(w, "Could not hijack the connection", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
conn, bufrw, err := hj.Hijack()
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
bufrw.WriteString("HTTP/1.1 204\r\nContent-Length: 1\r\n\r\n")
|
||||
bufrw.Flush()
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1Code204CLNonzero",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.status, 502; got != want {
|
||||
t.Errorf("status = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1Code204TE tests that 204 response with transfer-encoding is
|
||||
// not allowed.
|
||||
func TestH2H1Code204TE(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
hj, ok := w.(http.Hijacker)
|
||||
if !ok {
|
||||
http.Error(w, "Could not hijack the connection", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
conn, bufrw, err := hj.Hijack()
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
bufrw.WriteString("HTTP/1.1 204\r\nTransfer-Encoding: chunked\r\n\r\n")
|
||||
bufrw.Flush()
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1Code204TE",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.status, 502; got != want {
|
||||
t.Errorf("status = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1AffinityCookie tests that affinity cookie is sent back in
|
||||
// cleartext http.
|
||||
func TestH2H1AffinityCookie(t *testing.T) {
|
||||
st := newServerTester([]string{"--affinity-cookie"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1AffinityCookie",
|
||||
})
|
||||
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)
|
||||
}
|
||||
|
||||
const pattern = `affinity=[0-9a-f]{8}; Path=/foo/bar`
|
||||
validCookie := regexp.MustCompile(pattern)
|
||||
if got := res.header.Get("Set-Cookie"); !validCookie.MatchString(got) {
|
||||
t.Errorf("Set-Cookie: %v; want pattern %v", got, pattern)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1AffinityCookieTLS tests that affinity cookie is sent back
|
||||
// in https.
|
||||
func TestH2H1AffinityCookieTLS(t *testing.T) {
|
||||
st := newServerTesterTLS([]string{"--affinity-cookie"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1AffinityCookieTLS",
|
||||
scheme: "https",
|
||||
})
|
||||
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)
|
||||
}
|
||||
|
||||
const pattern = `affinity=[0-9a-f]{8}; Path=/foo/bar; Secure`
|
||||
validCookie := regexp.MustCompile(pattern)
|
||||
if got := res.header.Get("Set-Cookie"); !validCookie.MatchString(got) {
|
||||
t.Errorf("Set-Cookie: %v; want pattern %v", got, pattern)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1GracefulShutdown tests graceful shutdown.
|
||||
func TestH2H1GracefulShutdown(t *testing.T) {
|
||||
st := newServerTester(nil, t, noopHandler)
|
||||
@@ -1653,6 +1958,105 @@ func TestH2H2TLSXfp(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H2AddXfp tests that server appends :scheme to the existing
|
||||
// x-forwarded-proto header field.
|
||||
func TestH2H2AddXfp(t *testing.T) {
|
||||
st := newServerTesterTLS([]string{"--http2-bridge", "--no-strip-incoming-x-forwarded-proto"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
xfp := r.Header.Get("X-Forwarded-Proto")
|
||||
if got, want := xfp, "foo, http"; got != want {
|
||||
t.Errorf("X-Forwarded-Proto = %q; want %q", got, want)
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H2AddXfp",
|
||||
header: []hpack.HeaderField{
|
||||
pair("x-forwarded-proto", "foo"),
|
||||
},
|
||||
})
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H2NoAddXfp tests that server does not append :scheme to the
|
||||
// existing x-forwarded-proto header field.
|
||||
func TestH2H2NoAddXfp(t *testing.T) {
|
||||
st := newServerTesterTLS([]string{"--http2-bridge", "--no-add-x-forwarded-proto", "--no-strip-incoming-x-forwarded-proto"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
xfp := r.Header.Get("X-Forwarded-Proto")
|
||||
if got, want := xfp, "foo"; got != want {
|
||||
t.Errorf("X-Forwarded-Proto = %q; want %q", got, want)
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H2NoAddXfp",
|
||||
header: []hpack.HeaderField{
|
||||
pair("x-forwarded-proto", "foo"),
|
||||
},
|
||||
})
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H2StripXfp tests that server strips incoming
|
||||
// x-forwarded-proto header field.
|
||||
func TestH2H2StripXfp(t *testing.T) {
|
||||
st := newServerTesterTLS([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
xfp := r.Header.Get("X-Forwarded-Proto")
|
||||
if got, want := xfp, "http"; got != want {
|
||||
t.Errorf("X-Forwarded-Proto = %q; want %q", got, want)
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H2StripXfp",
|
||||
header: []hpack.HeaderField{
|
||||
pair("x-forwarded-proto", "foo"),
|
||||
},
|
||||
})
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H2StripNoAddXfp tests that server strips incoming
|
||||
// x-forwarded-proto header field, and does not add another.
|
||||
func TestH2H2StripNoAddXfp(t *testing.T) {
|
||||
st := newServerTesterTLS([]string{"--http2-bridge", "--no-add-x-forwarded-proto"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if got, found := r.Header["X-Forwarded-Proto"]; found {
|
||||
t.Errorf("X-Forwarded-Proto = %q; want nothing", got)
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H2StripNoAddXfp",
|
||||
header: []hpack.HeaderField{
|
||||
pair("x-forwarded-proto", "foo"),
|
||||
},
|
||||
})
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H2AddXff tests that server generates X-Forwarded-For header
|
||||
// field when forwarding request to backend.
|
||||
func TestH2H2AddXff(t *testing.T) {
|
||||
@@ -1961,6 +2365,26 @@ func TestH2H2DNS(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H2Code204 tests that 204 response without content-length, and
|
||||
// transfer-encoding is valid.
|
||||
func TestH2H2Code204(t *testing.T) {
|
||||
st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H2Code204",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.status, 204; 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,7 +101,7 @@ func newServerTesterInternal(src_args []string, t *testing.T, handler http.Handl
|
||||
|
||||
args := []string{}
|
||||
|
||||
var backendTLS, dns, externalDNS, acceptProxyProtocol, redirectIfNotTLS bool
|
||||
var backendTLS, dns, externalDNS, acceptProxyProtocol, redirectIfNotTLS, affinityCookie, alpnH1 bool
|
||||
|
||||
for _, k := range src_args {
|
||||
switch k {
|
||||
@@ -116,6 +116,10 @@ func newServerTesterInternal(src_args []string, t *testing.T, handler http.Handl
|
||||
acceptProxyProtocol = true
|
||||
case "--redirect-if-not-tls":
|
||||
redirectIfNotTLS = true
|
||||
case "--affinity-cookie":
|
||||
affinityCookie = true
|
||||
case "--alpn-h1":
|
||||
alpnH1 = true
|
||||
default:
|
||||
args = append(args, k)
|
||||
}
|
||||
@@ -153,8 +157,8 @@ func newServerTesterInternal(src_args []string, t *testing.T, handler http.Handl
|
||||
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:])
|
||||
// We use awesome service nip.io.
|
||||
b += fmt.Sprintf("%v.nip.io,%v;", backendURL.Host[:sep], backendURL.Host[sep+1:])
|
||||
}
|
||||
|
||||
if backendTLS {
|
||||
@@ -168,6 +172,10 @@ func newServerTesterInternal(src_args []string, t *testing.T, handler http.Handl
|
||||
b += ";redirect-if-not-tls"
|
||||
}
|
||||
|
||||
if affinityCookie {
|
||||
b += ";affinity=cookie;affinity-cookie-name=affinity;affinity-cookie-path=/foo/bar"
|
||||
}
|
||||
|
||||
noTLS := ";no-tls"
|
||||
if frontendTLS {
|
||||
noTLS = ""
|
||||
@@ -218,7 +226,11 @@ func newServerTesterInternal(src_args []string, t *testing.T, handler http.Handl
|
||||
tlsConfig = clientConfig
|
||||
}
|
||||
tlsConfig.InsecureSkipVerify = true
|
||||
tlsConfig.NextProtos = []string{"h2", "spdy/3.1"}
|
||||
if alpnH1 {
|
||||
tlsConfig.NextProtos = []string{"http/1.1"}
|
||||
} else {
|
||||
tlsConfig.NextProtos = []string{"h2", "spdy/3.1"}
|
||||
}
|
||||
conn, err = tls.Dial("tcp", authority, tlsConfig)
|
||||
} else {
|
||||
conn, err = net.Dial("tcp", authority)
|
||||
@@ -266,7 +278,7 @@ func (st *serverTester) Close() {
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
st.cmd.Wait()
|
||||
done <- struct{}{}
|
||||
close(done)
|
||||
}()
|
||||
|
||||
st.cmd.Process.Signal(syscall.SIGQUIT)
|
||||
@@ -769,7 +781,7 @@ type serverResponse struct {
|
||||
connErr bool // true if HTTP/2 connection error
|
||||
spdyGoAwayErrCode spdy.GoAwayStatus // status code received in SPDY RST_STREAM
|
||||
spdyRstErrCode spdy.RstStreamStatus // status code received in SPDY GOAWAY
|
||||
connClose bool // Conection: close is included in response header in HTTP/1 test
|
||||
connClose bool // Connection: close is included in response header in HTTP/1 test
|
||||
reqHeader http.Header // http request header, currently only sotres pushed request header
|
||||
pushResponse []*serverResponse // pushed response
|
||||
}
|
||||
|
||||
@@ -44,6 +44,10 @@ set_target_properties(nghttp2 PROPERTIES
|
||||
VERSION ${LT_VERSION} SOVERSION ${LT_SOVERSION}
|
||||
C_VISIBILITY_PRESET hidden
|
||||
)
|
||||
target_include_directories(nghttp2 INTERFACE
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/includes"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/includes"
|
||||
)
|
||||
|
||||
if(HAVE_CUNIT)
|
||||
# Static library (for unittests because of symbol visibility)
|
||||
|
||||
@@ -387,6 +387,11 @@ typedef enum {
|
||||
* Indicates that a processing was canceled.
|
||||
*/
|
||||
NGHTTP2_ERR_CANCEL = -535,
|
||||
/**
|
||||
* When a local endpoint expects to receive SETTINGS frame, it
|
||||
* receives an other type of frame.
|
||||
*/
|
||||
NGHTTP2_ERR_SETTINGS_EXPECTED = -536,
|
||||
/**
|
||||
* The errors < :enum:`NGHTTP2_ERR_FATAL` mean that the library is
|
||||
* under unexpected condition and processing was terminated (e.g.,
|
||||
@@ -469,6 +474,15 @@ NGHTTP2_EXTERN void nghttp2_rcbuf_decref(nghttp2_rcbuf *rcbuf);
|
||||
*/
|
||||
NGHTTP2_EXTERN nghttp2_vec nghttp2_rcbuf_get_buf(nghttp2_rcbuf *rcbuf);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* Returns nonzero if the underlying buffer is statically allocated,
|
||||
* and 0 otherwise. This can be useful for language bindings that wish
|
||||
* to avoid creating duplicate strings for these buffers.
|
||||
*/
|
||||
NGHTTP2_EXTERN int nghttp2_rcbuf_is_static(const nghttp2_rcbuf *rcbuf);
|
||||
|
||||
/**
|
||||
* @enum
|
||||
*
|
||||
@@ -1741,11 +1755,12 @@ typedef int (*nghttp2_on_header_callback2)(nghttp2_session *session,
|
||||
* The parameter and behaviour are similar to
|
||||
* :type:`nghttp2_on_header_callback`. The difference is that this
|
||||
* callback is only invoked when a invalid header name/value pair is
|
||||
* received which is silently ignored if this callback is not set.
|
||||
* Only invalid regular header field are passed to this callback. In
|
||||
* other words, invalid pseudo header field is not passed to this
|
||||
* callback. Also header fields which includes upper cased latter are
|
||||
* also treated as error without passing them to this callback.
|
||||
* received which is treated as stream error if this callback is not
|
||||
* set. Only invalid regular header field are passed to this
|
||||
* callback. In other words, invalid pseudo header field is not
|
||||
* passed to this callback. Also header fields which includes upper
|
||||
* cased latter are also treated as error without passing them to this
|
||||
* callback.
|
||||
*
|
||||
* This callback is only considered if HTTP messaging validation is
|
||||
* turned on (which is on by default, see
|
||||
@@ -1754,10 +1769,13 @@ typedef int (*nghttp2_on_header_callback2)(nghttp2_session *session,
|
||||
* With this callback, application inspects the incoming invalid
|
||||
* field, and it also can reset stream from this callback by returning
|
||||
* :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. By default, the
|
||||
* error code is :enum:`NGHTTP2_INTERNAL_ERROR`. To change the error
|
||||
* error code is :enum:`NGHTTP2_PROTOCOL_ERROR`. To change the error
|
||||
* code, call `nghttp2_submit_rst_stream()` with the error code of
|
||||
* choice in addition to returning
|
||||
* :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`.
|
||||
*
|
||||
* If 0 is returned, the header field is ignored, and the stream is
|
||||
* not reset.
|
||||
*/
|
||||
typedef int (*nghttp2_on_invalid_header_callback)(
|
||||
nghttp2_session *session, const nghttp2_frame *frame, const uint8_t *name,
|
||||
@@ -1974,6 +1992,9 @@ typedef ssize_t (*nghttp2_pack_extension_callback)(nghttp2_session *session,
|
||||
* of length |len|. |len| does not include the sentinel NULL
|
||||
* character.
|
||||
*
|
||||
* This function is deprecated. The new application should use
|
||||
* :type:`nghttp2_error_callback2`.
|
||||
*
|
||||
* The format of error message may change between nghttp2 library
|
||||
* versions. The application should not depend on the particular
|
||||
* format.
|
||||
@@ -1990,6 +2011,33 @@ typedef ssize_t (*nghttp2_pack_extension_callback)(nghttp2_session *session,
|
||||
typedef int (*nghttp2_error_callback)(nghttp2_session *session, const char *msg,
|
||||
size_t len, void *user_data);
|
||||
|
||||
/**
|
||||
* @functypedef
|
||||
*
|
||||
* Callback function invoked when library provides the error code, and
|
||||
* message. This callback is solely for debugging purpose.
|
||||
* |lib_error_code| is one of error code defined in
|
||||
* :enum:`nghttp2_error`. The |msg| is typically NULL-terminated
|
||||
* string of length |len|, and intended for human consumption. |len|
|
||||
* does not include the sentinel NULL character.
|
||||
*
|
||||
* The format of error message may change between nghttp2 library
|
||||
* versions. The application should not depend on the particular
|
||||
* format.
|
||||
*
|
||||
* Normally, application should return 0 from this callback. If fatal
|
||||
* error occurred while doing something in this callback, application
|
||||
* should return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. In this case,
|
||||
* library will return immediately with return value
|
||||
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. Currently, if nonzero value
|
||||
* is returned from this callback, they are treated as
|
||||
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`, but application should not
|
||||
* rely on this details.
|
||||
*/
|
||||
typedef int (*nghttp2_error_callback2)(nghttp2_session *session,
|
||||
int lib_error_code, const char *msg,
|
||||
size_t len, void *user_data);
|
||||
|
||||
struct nghttp2_session_callbacks;
|
||||
|
||||
/**
|
||||
@@ -2254,10 +2302,30 @@ nghttp2_session_callbacks_set_on_extension_chunk_recv_callback(
|
||||
*
|
||||
* Sets callback function invoked when library tells error message to
|
||||
* the application.
|
||||
*
|
||||
* This function is deprecated. The new application should use
|
||||
* `nghttp2_session_callbacks_set_error_callback2()`.
|
||||
*
|
||||
* If both :type:`nghttp2_error_callback` and
|
||||
* :type:`nghttp2_error_callback2` are set, the latter takes
|
||||
* precedence.
|
||||
*/
|
||||
NGHTTP2_EXTERN void nghttp2_session_callbacks_set_error_callback(
|
||||
nghttp2_session_callbacks *cbs, nghttp2_error_callback error_callback);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* Sets callback function invoked when library tells error code, and
|
||||
* message to the application.
|
||||
*
|
||||
* If both :type:`nghttp2_error_callback` and
|
||||
* :type:`nghttp2_error_callback2` are set, the latter takes
|
||||
* precedence.
|
||||
*/
|
||||
NGHTTP2_EXTERN void nghttp2_session_callbacks_set_error_callback2(
|
||||
nghttp2_session_callbacks *cbs, nghttp2_error_callback2 error_callback2);
|
||||
|
||||
/**
|
||||
* @functypedef
|
||||
*
|
||||
@@ -2448,7 +2516,10 @@ nghttp2_option_set_no_recv_client_magic(nghttp2_option *option, int val);
|
||||
* <https://tools.ietf.org/html/rfc7540#section-8>`_. See
|
||||
* :ref:`http-messaging` section for details. For those applications
|
||||
* who use nghttp2 library as non-HTTP use, give nonzero to |val| to
|
||||
* disable this enforcement.
|
||||
* disable this enforcement. Please note that disabling this feature
|
||||
* does not change the fundamental client and server model of HTTP.
|
||||
* That is, even if the validation is disabled, only client can send
|
||||
* requests.
|
||||
*/
|
||||
NGHTTP2_EXTERN void nghttp2_option_set_no_http_messaging(nghttp2_option *option,
|
||||
int val);
|
||||
@@ -3802,9 +3873,8 @@ nghttp2_submit_response(nghttp2_session *session, int32_t stream_id,
|
||||
* Submits trailer fields HEADERS against the stream |stream_id|.
|
||||
*
|
||||
* The |nva| is an array of name/value pair :type:`nghttp2_nv` with
|
||||
* |nvlen| elements. The application is responsible not to include
|
||||
* pseudo-header fields (header field whose name starts with ":") in
|
||||
* |nva|.
|
||||
* |nvlen| elements. The application must not include pseudo-header
|
||||
* fields (headers whose names starts with ":") in |nva|.
|
||||
*
|
||||
* This function creates copies of all name/value pairs in |nva|. It
|
||||
* also lower-cases all names in |nva|. The order of elements in
|
||||
@@ -4687,8 +4757,8 @@ nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater,
|
||||
*
|
||||
* After this function returns, it is safe to delete the |nva|.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
* This function returns the number of bytes written to |buf| if it
|
||||
* succeeds, or one of the following negative error codes:
|
||||
*
|
||||
* :enum:`NGHTTP2_ERR_NOMEM`
|
||||
* Out of memory.
|
||||
@@ -4719,8 +4789,8 @@ NGHTTP2_EXTERN ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater,
|
||||
*
|
||||
* After this function returns, it is safe to delete the |nva|.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
* This function returns the number of bytes written to |vec| if it
|
||||
* succeeds, or one of the following negative error codes:
|
||||
*
|
||||
* :enum:`NGHTTP2_ERR_NOMEM`
|
||||
* Out of memory.
|
||||
|
||||
@@ -398,7 +398,7 @@ int nghttp2_bufs_advance(nghttp2_bufs *bufs);
|
||||
void nghttp2_bufs_seek_last_present(nghttp2_bufs *bufs);
|
||||
|
||||
/*
|
||||
* Returns nonzero if bufs->cur->next is not emtpy.
|
||||
* Returns nonzero if bufs->cur->next is not empty.
|
||||
*/
|
||||
int nghttp2_bufs_next_present(nghttp2_bufs *bufs);
|
||||
|
||||
|
||||
@@ -168,3 +168,8 @@ void nghttp2_session_callbacks_set_error_callback(
|
||||
nghttp2_session_callbacks *cbs, nghttp2_error_callback error_callback) {
|
||||
cbs->error_callback = error_callback;
|
||||
}
|
||||
|
||||
void nghttp2_session_callbacks_set_error_callback2(
|
||||
nghttp2_session_callbacks *cbs, nghttp2_error_callback2 error_callback2) {
|
||||
cbs->error_callback2 = error_callback2;
|
||||
}
|
||||
|
||||
@@ -119,6 +119,7 @@ struct nghttp2_session_callbacks {
|
||||
nghttp2_unpack_extension_callback unpack_extension_callback;
|
||||
nghttp2_on_extension_chunk_recv_callback on_extension_chunk_recv_callback;
|
||||
nghttp2_error_callback error_callback;
|
||||
nghttp2_error_callback2 error_callback2;
|
||||
};
|
||||
|
||||
#endif /* NGHTTP2_CALLBACKS_H */
|
||||
|
||||
@@ -672,6 +672,9 @@ int nghttp2_frame_pack_altsvc(nghttp2_bufs *bufs, nghttp2_extension *frame) {
|
||||
nghttp2_buf *buf;
|
||||
nghttp2_ext_altsvc *altsvc;
|
||||
|
||||
/* This is required with --disable-assert. */
|
||||
(void)rv;
|
||||
|
||||
altsvc = frame->payload;
|
||||
|
||||
buf = &bufs->head->buf;
|
||||
|
||||
@@ -70,7 +70,9 @@
|
||||
#define NGHTTP2_MAX_PADLEN 256
|
||||
|
||||
/* Union of extension frame payload */
|
||||
typedef union { nghttp2_ext_altsvc altsvc; } nghttp2_ext_frame_payload;
|
||||
typedef union {
|
||||
nghttp2_ext_altsvc altsvc;
|
||||
} nghttp2_ext_frame_payload;
|
||||
|
||||
void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd);
|
||||
|
||||
|
||||
@@ -662,9 +662,9 @@ static int hd_context_init(nghttp2_hd_context *context, nghttp2_mem *mem) {
|
||||
context->mem = mem;
|
||||
context->bad = 0;
|
||||
context->hd_table_bufsize_max = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE;
|
||||
rv = hd_ringbuf_init(&context->hd_table, context->hd_table_bufsize_max /
|
||||
NGHTTP2_HD_ENTRY_OVERHEAD,
|
||||
mem);
|
||||
rv = hd_ringbuf_init(
|
||||
&context->hd_table,
|
||||
context->hd_table_bufsize_max / NGHTTP2_HD_ENTRY_OVERHEAD, mem);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
@@ -211,7 +211,9 @@ typedef struct {
|
||||
|
||||
#define HD_MAP_SIZE 128
|
||||
|
||||
typedef struct { nghttp2_hd_entry *table[HD_MAP_SIZE]; } nghttp2_hd_map;
|
||||
typedef struct {
|
||||
nghttp2_hd_entry *table[HD_MAP_SIZE];
|
||||
} nghttp2_hd_map;
|
||||
|
||||
struct nghttp2_hd_deflater {
|
||||
nghttp2_hd_context ctx;
|
||||
@@ -313,7 +315,7 @@ void nghttp2_hd_deflate_free(nghttp2_hd_deflater *deflater);
|
||||
*
|
||||
* This function expands |bufs| as necessary to store the result. If
|
||||
* buffers is full and the process still requires more space, this
|
||||
* funtion fails and returns NGHTTP2_ERR_HEADER_COMP.
|
||||
* function fails and returns NGHTTP2_ERR_HEADER_COMP.
|
||||
*
|
||||
* After this function returns, it is safe to delete the |nva|.
|
||||
*
|
||||
|
||||
@@ -322,6 +322,9 @@ const char *nghttp2_strerror(int error_code) {
|
||||
return "Internal error";
|
||||
case NGHTTP2_ERR_CANCEL:
|
||||
return "Cancel";
|
||||
case NGHTTP2_ERR_SETTINGS_EXPECTED:
|
||||
return "When a local endpoint expects to receive SETTINGS frame, it "
|
||||
"receives an other type of frame";
|
||||
case NGHTTP2_ERR_NOMEM:
|
||||
return "Out of memory";
|
||||
case NGHTTP2_ERR_CALLBACK_FAILURE:
|
||||
|
||||
@@ -112,7 +112,7 @@ struct nghttp2_outbound_item {
|
||||
nghttp2_ext_frame_payload ext_frame_payload;
|
||||
nghttp2_aux_data aux_data;
|
||||
/* The priority used in priority comparion. Smaller is served
|
||||
ealier. For PING, SETTINGS and non-DATA frames (excluding
|
||||
earlier. For PING, SETTINGS and non-DATA frames (excluding
|
||||
response HEADERS frame) have dedicated cycle value defined above.
|
||||
For DATA frame, cycle is computed by taking into account of
|
||||
effective weight and frame payload length previously sent, so
|
||||
|
||||
@@ -35,14 +35,16 @@
|
||||
|
||||
/* Implementation of priority queue */
|
||||
|
||||
typedef struct { size_t index; } nghttp2_pq_entry;
|
||||
typedef struct {
|
||||
size_t index;
|
||||
} nghttp2_pq_entry;
|
||||
|
||||
typedef struct {
|
||||
/* The pointer to the pointer to the item stored */
|
||||
nghttp2_pq_entry **q;
|
||||
/* Memory allocator */
|
||||
nghttp2_mem *mem;
|
||||
/* The number of items sotred */
|
||||
/* The number of items stored */
|
||||
size_t length;
|
||||
/* The maximum number of items this pq can store. This is
|
||||
automatically extended when length is reached to this value. */
|
||||
@@ -71,7 +73,7 @@ void nghttp2_pq_free(nghttp2_pq *pq);
|
||||
/*
|
||||
* Adds |item| to the priority queue |pq|.
|
||||
*
|
||||
* This function returns 0 if it succeds, or one of the following
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
|
||||
@@ -36,7 +36,9 @@ typedef struct nghttp2_queue_cell {
|
||||
struct nghttp2_queue_cell *next;
|
||||
} nghttp2_queue_cell;
|
||||
|
||||
typedef struct { nghttp2_queue_cell *front, *back; } nghttp2_queue;
|
||||
typedef struct {
|
||||
nghttp2_queue_cell *front, *back;
|
||||
} nghttp2_queue;
|
||||
|
||||
void nghttp2_queue_init(nghttp2_queue *queue);
|
||||
void nghttp2_queue_free(nghttp2_queue *queue);
|
||||
|
||||
@@ -96,3 +96,7 @@ nghttp2_vec nghttp2_rcbuf_get_buf(nghttp2_rcbuf *rcbuf) {
|
||||
nghttp2_vec res = {rcbuf->base, rcbuf->len};
|
||||
return res;
|
||||
}
|
||||
|
||||
int nghttp2_rcbuf_is_static(const nghttp2_rcbuf *rcbuf) {
|
||||
return rcbuf->ref == -1;
|
||||
}
|
||||
|
||||
@@ -148,14 +148,16 @@ static int check_ext_type_set(const uint8_t *ext_types, uint8_t type) {
|
||||
}
|
||||
|
||||
static int session_call_error_callback(nghttp2_session *session,
|
||||
const char *fmt, ...) {
|
||||
int lib_error_code, const char *fmt,
|
||||
...) {
|
||||
size_t bufsize;
|
||||
va_list ap;
|
||||
char *buf;
|
||||
int rv;
|
||||
nghttp2_mem *mem;
|
||||
|
||||
if (!session->callbacks.error_callback) {
|
||||
if (!session->callbacks.error_callback &&
|
||||
!session->callbacks.error_callback2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -189,8 +191,13 @@ static int session_call_error_callback(nghttp2_session *session,
|
||||
return 0;
|
||||
}
|
||||
|
||||
rv = session->callbacks.error_callback(session, buf, (size_t)rv,
|
||||
session->user_data);
|
||||
if (session->callbacks.error_callback2) {
|
||||
rv = session->callbacks.error_callback2(session, lib_error_code, buf,
|
||||
(size_t)rv, session->user_data);
|
||||
} else {
|
||||
rv = session->callbacks.error_callback(session, buf, (size_t)rv,
|
||||
session->user_data);
|
||||
}
|
||||
|
||||
nghttp2_mem_free(mem, buf);
|
||||
|
||||
@@ -541,9 +548,8 @@ static int session_new(nghttp2_session **session_ptr,
|
||||
if (nghttp2_enable_strict_preface) {
|
||||
nghttp2_inbound_frame *iframe = &(*session_ptr)->iframe;
|
||||
|
||||
if (server &&
|
||||
((*session_ptr)->opt_flags & NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC) ==
|
||||
0) {
|
||||
if (server && ((*session_ptr)->opt_flags &
|
||||
NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC) == 0) {
|
||||
iframe->state = NGHTTP2_IB_READ_CLIENT_MAGIC;
|
||||
iframe->payloadleft = NGHTTP2_CLIENT_MAGIC_LEN;
|
||||
} else {
|
||||
@@ -682,6 +688,7 @@ static int inflight_settings_new(nghttp2_inflight_settings **settings_ptr,
|
||||
if (niv > 0) {
|
||||
(*settings_ptr)->iv = nghttp2_frame_iv_copy(iv, niv, mem);
|
||||
if (!(*settings_ptr)->iv) {
|
||||
nghttp2_mem_free(mem, *settings_ptr);
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
} else {
|
||||
@@ -1523,13 +1530,14 @@ static int session_predicate_response_headers_send(nghttp2_session *session,
|
||||
if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) {
|
||||
return NGHTTP2_ERR_INVALID_STREAM_ID;
|
||||
}
|
||||
if (stream->state == NGHTTP2_STREAM_OPENING) {
|
||||
switch (stream->state) {
|
||||
case NGHTTP2_STREAM_OPENING:
|
||||
return 0;
|
||||
}
|
||||
if (stream->state == NGHTTP2_STREAM_CLOSING) {
|
||||
case NGHTTP2_STREAM_CLOSING:
|
||||
return NGHTTP2_ERR_STREAM_CLOSING;
|
||||
default:
|
||||
return NGHTTP2_ERR_INVALID_STREAM_STATE;
|
||||
}
|
||||
return NGHTTP2_ERR_INVALID_STREAM_STATE;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1572,9 +1580,6 @@ session_predicate_push_response_headers_send(nghttp2_session *session,
|
||||
if (stream->state != NGHTTP2_STREAM_RESERVED) {
|
||||
return NGHTTP2_ERR_PROTO;
|
||||
}
|
||||
if (stream->state == NGHTTP2_STREAM_CLOSING) {
|
||||
return NGHTTP2_ERR_STREAM_CLOSING;
|
||||
}
|
||||
if (session->goaway_flags & NGHTTP2_GOAWAY_RECV) {
|
||||
return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED;
|
||||
}
|
||||
@@ -1609,19 +1614,18 @@ static int session_predicate_headers_send(nghttp2_session *session,
|
||||
return rv;
|
||||
}
|
||||
assert(stream);
|
||||
if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) {
|
||||
if (stream->state == NGHTTP2_STREAM_CLOSING) {
|
||||
return NGHTTP2_ERR_STREAM_CLOSING;
|
||||
}
|
||||
|
||||
switch (stream->state) {
|
||||
case NGHTTP2_STREAM_OPENED:
|
||||
return 0;
|
||||
}
|
||||
if (stream->state == NGHTTP2_STREAM_OPENED) {
|
||||
return 0;
|
||||
}
|
||||
if (stream->state == NGHTTP2_STREAM_CLOSING) {
|
||||
case NGHTTP2_STREAM_CLOSING:
|
||||
return NGHTTP2_ERR_STREAM_CLOSING;
|
||||
default:
|
||||
if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) {
|
||||
return 0;
|
||||
}
|
||||
return NGHTTP2_ERR_INVALID_STREAM_STATE;
|
||||
}
|
||||
return NGHTTP2_ERR_INVALID_STREAM_STATE;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2067,14 +2071,6 @@ static int session_prep_frame(nghttp2_session *session,
|
||||
/* We don't call nghttp2_session_adjust_closed_stream() here,
|
||||
since we don't keep closed stream in client side */
|
||||
|
||||
estimated_payloadlen = session_estimate_headers_payload(
|
||||
session, frame->headers.nva, frame->headers.nvlen,
|
||||
NGHTTP2_PRIORITY_SPECLEN);
|
||||
|
||||
if (estimated_payloadlen > session->max_send_header_block_length) {
|
||||
return NGHTTP2_ERR_FRAME_SIZE_ERROR;
|
||||
}
|
||||
|
||||
rv = session_predicate_request_headers_send(session, item);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
@@ -2086,14 +2082,6 @@ static int session_prep_frame(nghttp2_session *session,
|
||||
} else {
|
||||
nghttp2_stream *stream;
|
||||
|
||||
estimated_payloadlen = session_estimate_headers_payload(
|
||||
session, frame->headers.nva, frame->headers.nvlen,
|
||||
NGHTTP2_PRIORITY_SPECLEN);
|
||||
|
||||
if (estimated_payloadlen > session->max_send_header_block_length) {
|
||||
return NGHTTP2_ERR_FRAME_SIZE_ERROR;
|
||||
}
|
||||
|
||||
stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
|
||||
|
||||
if (stream && stream->state == NGHTTP2_STREAM_RESERVED) {
|
||||
@@ -2120,6 +2108,14 @@ static int session_prep_frame(nghttp2_session *session,
|
||||
}
|
||||
}
|
||||
|
||||
estimated_payloadlen = session_estimate_headers_payload(
|
||||
session, frame->headers.nva, frame->headers.nvlen,
|
||||
NGHTTP2_PRIORITY_SPECLEN);
|
||||
|
||||
if (estimated_payloadlen > session->max_send_header_block_length) {
|
||||
return NGHTTP2_ERR_FRAME_SIZE_ERROR;
|
||||
}
|
||||
|
||||
rv = nghttp2_frame_pack_headers(&session->aob.framebufs, &frame->headers,
|
||||
&session->hd_deflater);
|
||||
|
||||
@@ -2189,18 +2185,11 @@ static int session_prep_frame(nghttp2_session *session,
|
||||
nghttp2_stream *stream;
|
||||
size_t estimated_payloadlen;
|
||||
|
||||
estimated_payloadlen = session_estimate_headers_payload(
|
||||
session, frame->push_promise.nva, frame->push_promise.nvlen, 0);
|
||||
|
||||
if (estimated_payloadlen > session->max_send_header_block_length) {
|
||||
return NGHTTP2_ERR_FRAME_SIZE_ERROR;
|
||||
}
|
||||
|
||||
/* stream could be NULL if associated stream was already
|
||||
closed. */
|
||||
stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
|
||||
|
||||
/* predicte should fail if stream is NULL. */
|
||||
/* predicate should fail if stream is NULL. */
|
||||
rv = session_predicate_push_promise_send(session, stream);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
@@ -2208,6 +2197,13 @@ static int session_prep_frame(nghttp2_session *session,
|
||||
|
||||
assert(stream);
|
||||
|
||||
estimated_payloadlen = session_estimate_headers_payload(
|
||||
session, frame->push_promise.nva, frame->push_promise.nvlen, 0);
|
||||
|
||||
if (estimated_payloadlen > session->max_send_header_block_length) {
|
||||
return NGHTTP2_ERR_FRAME_SIZE_ERROR;
|
||||
}
|
||||
|
||||
rv = nghttp2_frame_pack_push_promise(
|
||||
&session->aob.framebufs, &frame->push_promise, &session->hd_deflater);
|
||||
if (rv != 0) {
|
||||
@@ -3331,7 +3327,7 @@ static int session_call_on_invalid_header(nghttp2_session *session,
|
||||
session, frame, nv->name->base, nv->name->len, nv->value->base,
|
||||
nv->value->len, nv->flags, session->user_data);
|
||||
} else {
|
||||
return 0;
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
if (rv == NGHTTP2_ERR_PAUSE || rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
|
||||
@@ -3421,6 +3417,27 @@ static uint32_t get_error_code_from_lib_error_code(int lib_error_code) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Calls on_invalid_frame_recv_callback if it is set to |session|.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_CALLBACK_FAILURE
|
||||
* User defined callback function fails.
|
||||
*/
|
||||
static int session_call_on_invalid_frame_recv_callback(nghttp2_session *session,
|
||||
nghttp2_frame *frame,
|
||||
int lib_error_code) {
|
||||
if (session->callbacks.on_invalid_frame_recv_callback) {
|
||||
if (session->callbacks.on_invalid_frame_recv_callback(
|
||||
session, frame, lib_error_code, session->user_data) != 0) {
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int session_handle_invalid_stream2(nghttp2_session *session,
|
||||
int32_t stream_id,
|
||||
nghttp2_frame *frame,
|
||||
@@ -3578,14 +3595,46 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame,
|
||||
if (subject_stream && session_enforce_http_messaging(session)) {
|
||||
rv = nghttp2_http_on_header(session, subject_stream, frame, &nv,
|
||||
trailer);
|
||||
|
||||
if (rv == NGHTTP2_ERR_IGN_HTTP_HEADER) {
|
||||
/* Don't overwrite rv here */
|
||||
int rv2;
|
||||
|
||||
rv2 = session_call_on_invalid_header(session, frame, &nv);
|
||||
if (rv2 == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
|
||||
rv = NGHTTP2_ERR_HTTP_HEADER;
|
||||
} else {
|
||||
if (rv2 != 0) {
|
||||
return rv2;
|
||||
}
|
||||
|
||||
/* header is ignored */
|
||||
DEBUGF("recv: HTTP ignored: type=%u, id=%d, header %.*s: %.*s\n",
|
||||
frame->hd.type, frame->hd.stream_id, (int)nv.name->len,
|
||||
nv.name->base, (int)nv.value->len, nv.value->base);
|
||||
|
||||
rv2 = session_call_error_callback(
|
||||
session, NGHTTP2_ERR_HTTP_HEADER,
|
||||
"Ignoring received invalid HTTP header field: frame type: "
|
||||
"%u, stream: %d, name: [%.*s], value: [%.*s]",
|
||||
frame->hd.type, frame->hd.stream_id, (int)nv.name->len,
|
||||
nv.name->base, (int)nv.value->len, nv.value->base);
|
||||
|
||||
if (nghttp2_is_fatal(rv2)) {
|
||||
return rv2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rv == NGHTTP2_ERR_HTTP_HEADER) {
|
||||
DEBUGF("recv: HTTP error: type=%u, id=%d, header %.*s: %.*s\n",
|
||||
frame->hd.type, frame->hd.stream_id, (int)nv.name->len,
|
||||
nv.name->base, (int)nv.value->len, nv.value->base);
|
||||
|
||||
rv = session_call_error_callback(
|
||||
session, "Invalid HTTP header field was received: frame type: "
|
||||
"%u, stream: %d, name: [%.*s], value: [%.*s]",
|
||||
session, NGHTTP2_ERR_HTTP_HEADER,
|
||||
"Invalid HTTP header field was received: frame type: "
|
||||
"%u, stream: %d, name: [%.*s], value: [%.*s]",
|
||||
frame->hd.type, frame->hd.stream_id, (int)nv.name->len,
|
||||
nv.name->base, (int)nv.value->len, nv.value->base);
|
||||
|
||||
@@ -3601,34 +3650,6 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame,
|
||||
}
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
if (rv == NGHTTP2_ERR_IGN_HTTP_HEADER) {
|
||||
/* Don't overwrite rv here */
|
||||
int rv2;
|
||||
|
||||
rv2 = session_call_on_invalid_header(session, frame, &nv);
|
||||
/* This handles NGHTTP2_ERR_PAUSE and
|
||||
NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE as well */
|
||||
if (rv2 != 0) {
|
||||
return rv2;
|
||||
}
|
||||
|
||||
/* header is ignored */
|
||||
DEBUGF("recv: HTTP ignored: type=%u, id=%d, header %.*s: %.*s\n",
|
||||
frame->hd.type, frame->hd.stream_id, (int)nv.name->len,
|
||||
nv.name->base, (int)nv.value->len, nv.value->base);
|
||||
|
||||
rv2 = session_call_error_callback(
|
||||
session,
|
||||
"Ignoring received invalid HTTP header field: frame type: "
|
||||
"%u, stream: %d, name: [%.*s], value: [%.*s]",
|
||||
frame->hd.type, frame->hd.stream_id, (int)nv.name->len,
|
||||
nv.name->base, (int)nv.value->len, nv.value->base);
|
||||
|
||||
if (nghttp2_is_fatal(rv2)) {
|
||||
return rv2;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (rv == 0) {
|
||||
rv = session_call_on_header(session, frame, &nv);
|
||||
@@ -3767,7 +3788,7 @@ int nghttp2_session_on_request_headers_received(nghttp2_session *session,
|
||||
session, frame, NGHTTP2_ERR_PROTO, "request HEADERS: stream_id == 0");
|
||||
}
|
||||
|
||||
/* If client recieves idle stream from server, it is invalid
|
||||
/* If client receives idle stream from server, it is invalid
|
||||
regardless stream ID is even or odd. This is because client is
|
||||
not expected to receive request from server. */
|
||||
if (!session->server) {
|
||||
@@ -4771,11 +4792,13 @@ int nghttp2_session_on_altsvc_received(nghttp2_session *session,
|
||||
|
||||
if (frame->hd.stream_id == 0) {
|
||||
if (altsvc->origin_len == 0) {
|
||||
return 0;
|
||||
return session_call_on_invalid_frame_recv_callback(session, frame,
|
||||
NGHTTP2_ERR_PROTO);
|
||||
}
|
||||
} else {
|
||||
if (altsvc->origin_len > 0) {
|
||||
return 0;
|
||||
return session_call_on_invalid_frame_recv_callback(session, frame,
|
||||
NGHTTP2_ERR_PROTO);
|
||||
}
|
||||
|
||||
stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
|
||||
@@ -4788,6 +4811,11 @@ int nghttp2_session_on_altsvc_received(nghttp2_session *session,
|
||||
}
|
||||
}
|
||||
|
||||
if (altsvc->field_value_len == 0) {
|
||||
return session_call_on_invalid_frame_recv_callback(session, frame,
|
||||
NGHTTP2_ERR_PROTO);
|
||||
}
|
||||
|
||||
return session_call_on_frame_received(session, frame);
|
||||
}
|
||||
|
||||
@@ -5324,9 +5352,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
|
||||
iframe->state = NGHTTP2_IB_IGN_ALL;
|
||||
|
||||
rv = session_call_error_callback(
|
||||
session, "Remote peer returned unexpected data while we expected "
|
||||
"SETTINGS frame. Perhaps, peer does not support HTTP/2 "
|
||||
"properly.");
|
||||
session, NGHTTP2_ERR_SETTINGS_EXPECTED,
|
||||
"Remote peer returned unexpected data while we expected "
|
||||
"SETTINGS frame. Perhaps, peer does not support HTTP/2 "
|
||||
"properly.");
|
||||
|
||||
if (nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
@@ -5567,7 +5596,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
|
||||
if (iframe->payloadleft) {
|
||||
nghttp2_settings_entry *min_header_table_size_entry;
|
||||
|
||||
/* We allocate iv with addtional one entry, to store the
|
||||
/* We allocate iv with additional one entry, to store the
|
||||
minimum header table size. */
|
||||
iframe->max_niv =
|
||||
iframe->frame.hd.length / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH + 1;
|
||||
@@ -5950,7 +5979,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
|
||||
|
||||
DEBUGF("recv: origin_len=%zu\n", origin_len);
|
||||
|
||||
if (2 + origin_len > iframe->payloadleft) {
|
||||
if (origin_len > iframe->payloadleft) {
|
||||
busy = 1;
|
||||
iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
|
||||
break;
|
||||
@@ -6036,9 +6065,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
|
||||
|
||||
/* Use promised stream ID for PUSH_PROMISE */
|
||||
rv = nghttp2_session_add_rst_stream(
|
||||
session, iframe->frame.hd.type == NGHTTP2_PUSH_PROMISE
|
||||
? iframe->frame.push_promise.promised_stream_id
|
||||
: iframe->frame.hd.stream_id,
|
||||
session,
|
||||
iframe->frame.hd.type == NGHTTP2_PUSH_PROMISE
|
||||
? iframe->frame.push_promise.promised_stream_id
|
||||
: iframe->frame.hd.stream_id,
|
||||
NGHTTP2_INTERNAL_ERROR);
|
||||
if (nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
@@ -7128,6 +7158,7 @@ uint32_t nghttp2_session_get_remote_settings(nghttp2_session *session,
|
||||
}
|
||||
|
||||
assert(0);
|
||||
abort(); /* if NDEBUG is set */
|
||||
}
|
||||
|
||||
uint32_t nghttp2_session_get_local_settings(nghttp2_session *session,
|
||||
@@ -7148,6 +7179,7 @@ uint32_t nghttp2_session_get_local_settings(nghttp2_session *session,
|
||||
}
|
||||
|
||||
assert(0);
|
||||
abort(); /* if NDEBUG is set */
|
||||
}
|
||||
|
||||
static int nghttp2_session_upgrade_internal(nghttp2_session *session,
|
||||
|
||||
@@ -311,7 +311,7 @@ struct nghttp2_session {
|
||||
/* Unacked local SETTINGS_MAX_CONCURRENT_STREAMS value. We use this
|
||||
to refuse the incoming stream if it exceeds this value. */
|
||||
uint32_t pending_local_max_concurrent_stream;
|
||||
/* The bitwose OR of zero or more of nghttp2_typemask to indicate
|
||||
/* The bitwise OR of zero or more of nghttp2_typemask to indicate
|
||||
that the default handling of extension frame is enabled. */
|
||||
uint32_t builtin_recv_ext_types;
|
||||
/* Unacked local ENABLE_PUSH value. We use this to refuse
|
||||
@@ -319,7 +319,7 @@ struct nghttp2_session {
|
||||
uint8_t pending_enable_push;
|
||||
/* Nonzero if the session is server side. */
|
||||
uint8_t server;
|
||||
/* Flags indicating GOAWAY is sent and/or recieved. The flags are
|
||||
/* Flags indicating GOAWAY is sent and/or received. The flags are
|
||||
composed by bitwise OR-ing nghttp2_goaway_flag. */
|
||||
uint8_t goaway_flags;
|
||||
/* This flag is used to reduce excessive queuing of WINDOW_UPDATE to
|
||||
@@ -722,7 +722,7 @@ int nghttp2_session_on_goaway_received(nghttp2_session *session,
|
||||
nghttp2_frame *frame);
|
||||
|
||||
/*
|
||||
* Called when WINDOW_UPDATE is recieved, assuming |frame| is properly
|
||||
* Called when WINDOW_UPDATE is received, assuming |frame| is properly
|
||||
* initialized.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
@@ -737,7 +737,7 @@ int nghttp2_session_on_window_update_received(nghttp2_session *session,
|
||||
nghttp2_frame *frame);
|
||||
|
||||
/*
|
||||
* Called when ALTSVC is recieved, assuming |frame| is properly
|
||||
* Called when ALTSVC is received, assuming |frame| is properly
|
||||
* initialized.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
|
||||
@@ -366,8 +366,9 @@ static void check_queued(nghttp2_stream *stream) {
|
||||
}
|
||||
}
|
||||
if (queued == 0) {
|
||||
fprintf(stderr, "stream(%p)=%d, stream->queued == 1, and "
|
||||
"!stream_active(), but no descendants is queued\n",
|
||||
fprintf(stderr,
|
||||
"stream(%p)=%d, stream->queued == 1, and "
|
||||
"!stream_active(), but no descendants is queued\n",
|
||||
stream, stream->stream_id);
|
||||
assert(0);
|
||||
}
|
||||
@@ -378,9 +379,10 @@ static void check_queued(nghttp2_stream *stream) {
|
||||
}
|
||||
} else {
|
||||
if (stream_active(stream) || !nghttp2_pq_empty(&stream->obq)) {
|
||||
fprintf(stderr, "stream(%p) = %d, stream->queued == 0, but "
|
||||
"stream_active(stream) == %d and "
|
||||
"nghttp2_pq_size(&stream->obq) = %zu\n",
|
||||
fprintf(stderr,
|
||||
"stream(%p) = %d, stream->queued == 0, but "
|
||||
"stream_active(stream) == %d and "
|
||||
"nghttp2_pq_size(&stream->obq) = %zu\n",
|
||||
stream, stream->stream_id, stream_active(stream),
|
||||
nghttp2_pq_size(&stream->obq));
|
||||
assert(0);
|
||||
|
||||
@@ -8,11 +8,8 @@ set_source_files_properties(${cxx_sources} PROPERTIES
|
||||
COMPILE_FLAGS "${WARNCXXFLAGS} ${CXX1XCXXFLAGS}")
|
||||
|
||||
include_directories(
|
||||
"${CMAKE_SOURCE_DIR}/lib/includes"
|
||||
"${CMAKE_BINARY_DIR}/lib/includes"
|
||||
"${CMAKE_SOURCE_DIR}/lib"
|
||||
"${CMAKE_SOURCE_DIR}/src/includes"
|
||||
"${CMAKE_SOURCE_DIR}/third-party"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/includes"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../third-party"
|
||||
|
||||
${JEMALLOC_INCLUDE_DIRS}
|
||||
${SPDYLAY_INCLUDE_DIRS}
|
||||
@@ -48,7 +45,7 @@ if(ENABLE_APP)
|
||||
set(NGHTTP_SOURCES
|
||||
${HELPER_OBJECTS}
|
||||
nghttp.cc
|
||||
ssl.cc
|
||||
tls.cc
|
||||
)
|
||||
if(HAVE_LIBXML2)
|
||||
list(APPEND NGHTTP_SOURCES HtmlParser.cc)
|
||||
@@ -58,7 +55,7 @@ if(ENABLE_APP)
|
||||
set(NGHTTPD_SOURCES
|
||||
${HELPER_OBJECTS}
|
||||
nghttpd.cc
|
||||
ssl.cc
|
||||
tls.cc
|
||||
HttpServer.cc
|
||||
)
|
||||
|
||||
@@ -67,7 +64,7 @@ if(ENABLE_APP)
|
||||
util.cc
|
||||
http2.cc h2load.cc
|
||||
timegm.c
|
||||
ssl.cc
|
||||
tls.cc
|
||||
h2load_http2_session.cc
|
||||
h2load_http1_session.cc
|
||||
)
|
||||
@@ -82,7 +79,7 @@ if(ENABLE_APP)
|
||||
set(NGHTTPX_SRCS
|
||||
util.cc http2.cc timegm.c
|
||||
app_helper.cc
|
||||
ssl.cc
|
||||
tls.cc
|
||||
shrpx_config.cc
|
||||
shrpx_accept_handler.cc
|
||||
shrpx_connection_handler.cc
|
||||
@@ -98,7 +95,7 @@ if(ENABLE_APP)
|
||||
shrpx_log.cc
|
||||
shrpx_http.cc
|
||||
shrpx_io_control.cc
|
||||
shrpx_ssl.cc
|
||||
shrpx_tls.cc
|
||||
shrpx_worker.cc
|
||||
shrpx_log_config.cc
|
||||
shrpx_connect_blocker.cc
|
||||
@@ -152,7 +149,7 @@ if(ENABLE_APP)
|
||||
if(HAVE_CUNIT)
|
||||
set(NGHTTPX_UNITTEST_SOURCES
|
||||
shrpx-unittest.cc
|
||||
shrpx_ssl_test.cc
|
||||
shrpx_tls_test.cc
|
||||
shrpx_downstream_test.cc
|
||||
shrpx_config_test.cc
|
||||
shrpx_worker_test.cc
|
||||
@@ -216,7 +213,7 @@ endif()
|
||||
if(ENABLE_ASIO_LIB)
|
||||
set(NGHTTP2_ASIO_SOURCES
|
||||
util.cc http2.cc
|
||||
ssl.cc
|
||||
tls.cc
|
||||
timegm.c
|
||||
asio_common.cc
|
||||
asio_io_service_pool.cc
|
||||
@@ -252,6 +249,11 @@ if(ENABLE_ASIO_LIB)
|
||||
${OPENSSL_INCLUDE_DIRS}
|
||||
${Boost_INCLUDE_DIRS}
|
||||
)
|
||||
target_include_directories(nghttp2_asio INTERFACE
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/../lib/includes"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../lib/includes"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/includes"
|
||||
)
|
||||
target_link_libraries(nghttp2_asio
|
||||
nghttp2
|
||||
${OPENSSL_LIBRARIES}
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
#include "app_helper.h"
|
||||
#include "http2.h"
|
||||
#include "util.h"
|
||||
#include "ssl.h"
|
||||
#include "tls.h"
|
||||
#include "template.h"
|
||||
|
||||
#ifndef O_BINARY
|
||||
@@ -877,7 +877,7 @@ int Http2Handler::connection_made() {
|
||||
}
|
||||
}
|
||||
|
||||
if (ssl_ && !nghttp2::ssl::check_http2_requirement(ssl_)) {
|
||||
if (ssl_ && !nghttp2::tls::check_http2_requirement(ssl_)) {
|
||||
terminate_session(NGHTTP2_INADEQUATE_SECURITY);
|
||||
}
|
||||
|
||||
@@ -1749,8 +1749,8 @@ void fill_callback(nghttp2_session_callbacks *callbacks, const Config *config) {
|
||||
nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(
|
||||
callbacks, verbose_on_invalid_frame_recv_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_error_callback(callbacks,
|
||||
verbose_error_callback);
|
||||
nghttp2_session_callbacks_set_error_callback2(callbacks,
|
||||
verbose_error_callback);
|
||||
}
|
||||
|
||||
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
|
||||
@@ -1779,7 +1779,7 @@ struct ClientInfo {
|
||||
struct Worker {
|
||||
std::unique_ptr<Sessions> sessions;
|
||||
ev_async w;
|
||||
// protectes q
|
||||
// protects q
|
||||
std::mutex m;
|
||||
std::deque<ClientInfo> q;
|
||||
};
|
||||
@@ -2122,14 +2122,14 @@ int HttpServer::run() {
|
||||
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
|
||||
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
|
||||
|
||||
if (nghttp2::ssl::ssl_ctx_set_proto_versions(
|
||||
ssl_ctx, nghttp2::ssl::NGHTTP2_TLS_MIN_VERSION,
|
||||
nghttp2::ssl::NGHTTP2_TLS_MAX_VERSION) != 0) {
|
||||
if (nghttp2::tls::ssl_ctx_set_proto_versions(
|
||||
ssl_ctx, nghttp2::tls::NGHTTP2_TLS_MIN_VERSION,
|
||||
nghttp2::tls::NGHTTP2_TLS_MAX_VERSION) != 0) {
|
||||
std::cerr << "Could not set TLS versions" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (SSL_CTX_set_cipher_list(ssl_ctx, ssl::DEFAULT_CIPHER_LIST) == 0) {
|
||||
if (SSL_CTX_set_cipher_list(ssl_ctx, tls::DEFAULT_CIPHER_LIST) == 0) {
|
||||
std::cerr << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
|
||||
return -1;
|
||||
}
|
||||
@@ -2156,7 +2156,7 @@ int HttpServer::run() {
|
||||
}
|
||||
SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh);
|
||||
EC_KEY_free(ecdh);
|
||||
// #endif // OPENSSL_VERSION_NUBMER < 0x10002000L
|
||||
// #endif // OPENSSL_VERSION_NUBMER < 0x10002000L
|
||||
|
||||
#endif // OPENSSL_NO_EC
|
||||
|
||||
@@ -2197,8 +2197,9 @@ int HttpServer::run() {
|
||||
return -1;
|
||||
}
|
||||
if (config_->verify_client) {
|
||||
SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE |
|
||||
SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
|
||||
SSL_CTX_set_verify(ssl_ctx,
|
||||
SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE |
|
||||
SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
|
||||
verify_callback);
|
||||
}
|
||||
|
||||
|
||||
@@ -81,10 +81,10 @@ endif # HAVE_LIBXML2
|
||||
|
||||
nghttp_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} nghttp.cc nghttp.h \
|
||||
${HTML_PARSER_OBJECTS} ${HTML_PARSER_HFILES} \
|
||||
ssl.cc ssl.h
|
||||
tls.cc tls.h
|
||||
|
||||
nghttpd_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} nghttpd.cc \
|
||||
ssl.cc ssl.h \
|
||||
tls.cc tls.h \
|
||||
HttpServer.cc HttpServer.h
|
||||
|
||||
bin_PROGRAMS += h2load
|
||||
@@ -92,7 +92,7 @@ bin_PROGRAMS += h2load
|
||||
h2load_SOURCES = util.cc util.h \
|
||||
http2.cc http2.h h2load.cc h2load.h \
|
||||
timegm.c timegm.h \
|
||||
ssl.cc ssl.h \
|
||||
tls.cc tls.h \
|
||||
h2load_session.h \
|
||||
h2load_http2_session.cc h2load_http2_session.h \
|
||||
h2load_http1_session.cc h2load_http1_session.h
|
||||
@@ -104,7 +104,7 @@ endif # HAVE_SPDYLAY
|
||||
NGHTTPX_SRCS = \
|
||||
util.cc util.h http2.cc http2.h timegm.c timegm.h base64.h \
|
||||
app_helper.cc app_helper.h \
|
||||
ssl.cc ssl.h \
|
||||
tls.cc tls.h \
|
||||
shrpx_config.cc shrpx_config.h \
|
||||
shrpx_error.h \
|
||||
shrpx_accept_handler.cc shrpx_accept_handler.h \
|
||||
@@ -122,7 +122,7 @@ NGHTTPX_SRCS = \
|
||||
shrpx_log.cc shrpx_log.h \
|
||||
shrpx_http.cc shrpx_http.h \
|
||||
shrpx_io_control.cc shrpx_io_control.h \
|
||||
shrpx_ssl.cc shrpx_ssl.h \
|
||||
shrpx_tls.cc shrpx_tls.h \
|
||||
shrpx_worker.cc shrpx_worker.h \
|
||||
shrpx_log_config.cc shrpx_log_config.h \
|
||||
shrpx_connect_blocker.cc shrpx_connect_blocker.h \
|
||||
@@ -183,7 +183,7 @@ endif # HAVE_NEVERBLEED
|
||||
if HAVE_CUNIT
|
||||
check_PROGRAMS += nghttpx-unittest
|
||||
nghttpx_unittest_SOURCES = shrpx-unittest.cc \
|
||||
shrpx_ssl_test.cc shrpx_ssl_test.h \
|
||||
shrpx_tls_test.cc shrpx_tls_test.h \
|
||||
shrpx_downstream_test.cc shrpx_downstream_test.h \
|
||||
shrpx_config_test.cc shrpx_config_test.h \
|
||||
shrpx_worker_test.cc shrpx_worker_test.h \
|
||||
@@ -240,7 +240,7 @@ lib_LTLIBRARIES = libnghttp2_asio.la
|
||||
|
||||
libnghttp2_asio_la_SOURCES = \
|
||||
util.cc util.h http2.cc http2.h \
|
||||
ssl.cc ssl.h \
|
||||
tls.cc tls.h \
|
||||
ssl_compat.h \
|
||||
timegm.c timegm.h \
|
||||
asio_common.cc asio_common.h \
|
||||
|
||||
@@ -27,7 +27,9 @@
|
||||
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <sys/uio.h>
|
||||
#endif // !_WIN32
|
||||
|
||||
#include <cassert>
|
||||
|
||||
@@ -271,6 +273,6 @@ ByteRef make_byte_ref(BlockAllocator &alloc, size_t size) {
|
||||
return {dst, size};
|
||||
}
|
||||
|
||||
} // namespace aria2
|
||||
} // namespace nghttp2
|
||||
|
||||
#endif // ALLOCATOR_H
|
||||
|
||||
@@ -155,7 +155,7 @@ void print_nv(nghttp2_nv *nva, size_t nvlen) {
|
||||
print_nv(nva);
|
||||
}
|
||||
}
|
||||
} // namelen
|
||||
} // namespace
|
||||
|
||||
void print_timer() {
|
||||
auto millis = get_timer();
|
||||
@@ -327,8 +327,9 @@ void print_frame(print_type ptype, const nghttp2_frame *frame) {
|
||||
break;
|
||||
case NGHTTP2_GOAWAY:
|
||||
print_frame_attr_indent();
|
||||
fprintf(outfile, "(last_stream_id=%d, error_code=%s(0x%02x), "
|
||||
"opaque_data(%u)=[%s])\n",
|
||||
fprintf(outfile,
|
||||
"(last_stream_id=%d, error_code=%s(0x%02x), "
|
||||
"opaque_data(%u)=[%s])\n",
|
||||
frame->goaway.last_stream_id,
|
||||
nghttp2_http2_strerror(frame->goaway.error_code),
|
||||
frame->goaway.error_code,
|
||||
@@ -425,8 +426,8 @@ int verbose_on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int verbose_error_callback(nghttp2_session *session, const char *msg,
|
||||
size_t len, void *user_data) {
|
||||
int verbose_error_callback(nghttp2_session *session, int lib_error_code,
|
||||
const char *msg, size_t len, void *user_data) {
|
||||
print_timer();
|
||||
fprintf(outfile, " [ERROR] %.*s\n", (int)len, msg);
|
||||
fflush(outfile);
|
||||
|
||||
@@ -60,8 +60,8 @@ int verbose_on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
||||
int32_t stream_id, const uint8_t *data,
|
||||
size_t len, void *user_data);
|
||||
|
||||
int verbose_error_callback(nghttp2_session *session, const char *msg,
|
||||
size_t len, void *user_data);
|
||||
int verbose_error_callback(nghttp2_session *session, int lib_error_code,
|
||||
const char *msg, size_t len, void *user_data);
|
||||
|
||||
// Returns difference between |a| and |b| in milliseconds, assuming
|
||||
// |a| is more recent than |b|.
|
||||
|
||||
@@ -96,29 +96,48 @@ boost::asio::io_service &session::io_service() const {
|
||||
|
||||
const request *session::submit(boost::system::error_code &ec,
|
||||
const std::string &method,
|
||||
const std::string &uri, header_map h) const {
|
||||
return impl_->submit(ec, method, uri, generator_cb(), std::move(h));
|
||||
const std::string &uri, header_map h,
|
||||
priority_spec prio) const {
|
||||
return impl_->submit(ec, method, uri, generator_cb(), std::move(h),
|
||||
std::move(prio));
|
||||
}
|
||||
|
||||
const request *session::submit(boost::system::error_code &ec,
|
||||
const std::string &method,
|
||||
const std::string &uri, std::string data,
|
||||
header_map h) const {
|
||||
header_map h, priority_spec prio) const {
|
||||
return impl_->submit(ec, method, uri, string_generator(std::move(data)),
|
||||
std::move(h));
|
||||
std::move(h), std::move(prio));
|
||||
}
|
||||
|
||||
const request *session::submit(boost::system::error_code &ec,
|
||||
const std::string &method,
|
||||
const std::string &uri, generator_cb cb,
|
||||
header_map h) const {
|
||||
return impl_->submit(ec, method, uri, std::move(cb), std::move(h));
|
||||
header_map h, priority_spec prio) const {
|
||||
return impl_->submit(ec, method, uri, std::move(cb), std::move(h),
|
||||
std::move(prio));
|
||||
}
|
||||
|
||||
void session::read_timeout(const boost::posix_time::time_duration &t) {
|
||||
impl_->read_timeout(t);
|
||||
}
|
||||
|
||||
priority_spec::priority_spec(const int32_t stream_id, const int32_t weight,
|
||||
const bool exclusive)
|
||||
: valid_(true) {
|
||||
nghttp2_priority_spec_init(&spec_, stream_id, weight, exclusive);
|
||||
}
|
||||
|
||||
const nghttp2_priority_spec *priority_spec::get() const {
|
||||
if (!valid_) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &spec_;
|
||||
}
|
||||
|
||||
const bool priority_spec::valid() const { return valid_; }
|
||||
|
||||
} // namespace client
|
||||
} // namespace asio_http2
|
||||
} // nghttp2
|
||||
} // namespace nghttp2
|
||||
|
||||
@@ -45,9 +45,9 @@ session_impl::session_impl(
|
||||
io_service_(io_service),
|
||||
resolver_(io_service),
|
||||
deadline_(io_service),
|
||||
ping_(io_service),
|
||||
connect_timeout_(connect_timeout),
|
||||
read_timeout_(boost::posix_time::seconds(60)),
|
||||
ping_(io_service),
|
||||
session_(nullptr),
|
||||
data_pending_(nullptr),
|
||||
data_pendinglen_(0),
|
||||
@@ -84,7 +84,6 @@ void session_impl::start_resolve(const std::string &host,
|
||||
});
|
||||
|
||||
deadline_.async_wait(std::bind(&session_impl::handle_deadline, self));
|
||||
start_ping();
|
||||
}
|
||||
|
||||
void session_impl::handle_deadline() {
|
||||
@@ -135,6 +134,8 @@ void session_impl::connected(tcp::resolver::iterator endpoint_it) {
|
||||
do_write();
|
||||
do_read();
|
||||
|
||||
start_ping();
|
||||
|
||||
auto &connect_cb = on_connect();
|
||||
if (connect_cb) {
|
||||
connect_cb(endpoint_it);
|
||||
@@ -478,7 +479,7 @@ std::unique_ptr<stream> session_impl::create_stream() {
|
||||
const request *session_impl::submit(boost::system::error_code &ec,
|
||||
const std::string &method,
|
||||
const std::string &uri, generator_cb cb,
|
||||
header_map h) {
|
||||
header_map h, priority_spec prio) {
|
||||
ec.clear();
|
||||
|
||||
if (stopped_) {
|
||||
@@ -558,7 +559,7 @@ const request *session_impl::submit(boost::system::error_code &ec,
|
||||
prdptr = &prd;
|
||||
}
|
||||
|
||||
auto stream_id = nghttp2_submit_request(session_, nullptr, nva.data(),
|
||||
auto stream_id = nghttp2_submit_request(session_, prio.get(), nva.data(),
|
||||
nva.size(), prdptr, strm.get());
|
||||
if (stream_id < 0) {
|
||||
ec = make_error_code(static_cast<nghttp2_error>(stream_id));
|
||||
@@ -755,4 +756,4 @@ void session_impl::read_timeout(const boost::posix_time::time_duration &t) {
|
||||
|
||||
} // namespace client
|
||||
} // namespace asio_http2
|
||||
} // nghttp2
|
||||
} // namespace nghttp2
|
||||
|
||||
@@ -70,7 +70,7 @@ public:
|
||||
|
||||
const request *submit(boost::system::error_code &ec,
|
||||
const std::string &method, const std::string &uri,
|
||||
generator_cb cb, header_map h);
|
||||
generator_cb cb, header_map h, priority_spec spec);
|
||||
|
||||
virtual void start_connect(tcp::resolver::iterator endpoint_it) = 0;
|
||||
virtual tcp::socket &socket() = 0;
|
||||
|
||||
@@ -45,8 +45,9 @@ 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, [self](const boost::system::error_code &ec,
|
||||
tcp::resolver::iterator endpoint_it) {
|
||||
socket(), endpoint_it,
|
||||
[self](const boost::system::error_code &ec,
|
||||
tcp::resolver::iterator endpoint_it) {
|
||||
if (self->stopped()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
#include <boost/asio/ssl.hpp>
|
||||
|
||||
#include "ssl.h"
|
||||
#include "tls.h"
|
||||
#include "util.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
*/
|
||||
#include "asio_common.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <memory>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
@@ -169,6 +169,6 @@ private:
|
||||
|
||||
} // namespace server
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp
|
||||
} // namespace nghttp2
|
||||
|
||||
#endif // ASIO_SERVER_HTTP2_HANDLER_H
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
#include "asio_server.h"
|
||||
#include "util.h"
|
||||
#include "ssl.h"
|
||||
#include "tls.h"
|
||||
#include "template.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
#include <boost/asio/ssl.hpp>
|
||||
|
||||
#include "ssl.h"
|
||||
#include "tls.h"
|
||||
#include "util.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
@@ -72,7 +72,7 @@ configure_tls_context_easy(boost::system::error_code &ec,
|
||||
SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
|
||||
SSL_CTX_set_mode(ctx, SSL_MODE_RELEASE_BUFFERS);
|
||||
|
||||
SSL_CTX_set_cipher_list(ctx, ssl::DEFAULT_CIPHER_LIST);
|
||||
SSL_CTX_set_cipher_list(ctx, tls::DEFAULT_CIPHER_LIST);
|
||||
|
||||
#ifndef OPENSSL_NO_EC
|
||||
auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
|
||||
|
||||
492
src/h2load.cc
492
src/h2load.cc
@@ -59,7 +59,7 @@
|
||||
#ifdef HAVE_SPDYLAY
|
||||
#include "h2load_spdy_session.h"
|
||||
#endif // HAVE_SPDYLAY
|
||||
#include "ssl.h"
|
||||
#include "tls.h"
|
||||
#include "http2.h"
|
||||
#include "util.h"
|
||||
#include "template.h"
|
||||
@@ -79,7 +79,7 @@ bool recorded(const std::chrono::steady_clock::time_point &t) {
|
||||
} // namespace
|
||||
|
||||
Config::Config()
|
||||
: ciphers(ssl::DEFAULT_CIPHER_LIST),
|
||||
: ciphers(tls::DEFAULT_CIPHER_LIST),
|
||||
data_length(-1),
|
||||
addrs(nullptr),
|
||||
nreqs(1),
|
||||
@@ -90,6 +90,8 @@ Config::Config()
|
||||
connection_window_bits(30),
|
||||
rate(0),
|
||||
rate_period(1.0),
|
||||
duration(0.0),
|
||||
warm_up_time(0.0),
|
||||
conn_active_timeout(0.),
|
||||
conn_inactivity_timeout(0.),
|
||||
no_tls_proto(PROTO_HTTP2),
|
||||
@@ -118,6 +120,7 @@ Config::~Config() {
|
||||
}
|
||||
|
||||
bool Config::is_rate_mode() const { return (this->rate != 0); }
|
||||
bool Config::is_timing_based_mode() const { return (this->duration > 0); }
|
||||
bool Config::has_base_uri() const { return (!this->base_uri.empty()); }
|
||||
Config config;
|
||||
|
||||
@@ -151,33 +154,12 @@ std::mt19937 gen(rd());
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void sampling_init(Sampling &smp, size_t total, size_t max_samples) {
|
||||
void sampling_init(Sampling &smp, size_t max_samples) {
|
||||
smp.n = 0;
|
||||
|
||||
if (total <= max_samples) {
|
||||
smp.interval = 0.;
|
||||
smp.point = 0.;
|
||||
return;
|
||||
}
|
||||
|
||||
smp.interval = static_cast<double>(total) / max_samples;
|
||||
|
||||
std::uniform_real_distribution<> dis(0., smp.interval);
|
||||
|
||||
smp.point = dis(gen);
|
||||
smp.max_samples = max_samples;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
bool sampling_should_pick(Sampling &smp) {
|
||||
return smp.interval == 0. || smp.n == ceil(smp.point);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void sampling_advance_point(Sampling &smp) { smp.point += smp.interval; }
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void writecb(struct ev_loop *loop, ev_io *w, int revents) {
|
||||
auto client = static_cast<Client *>(w->data);
|
||||
@@ -190,6 +172,7 @@ void writecb(struct ev_loop *loop, ev_io *w, int revents) {
|
||||
rv = client->connect();
|
||||
if (rv != 0) {
|
||||
client->fail();
|
||||
client->worker->free_client(client);
|
||||
delete client;
|
||||
return;
|
||||
}
|
||||
@@ -197,6 +180,7 @@ void writecb(struct ev_loop *loop, ev_io *w, int revents) {
|
||||
}
|
||||
if (rv != 0) {
|
||||
client->fail();
|
||||
client->worker->free_client(client);
|
||||
delete client;
|
||||
}
|
||||
}
|
||||
@@ -210,6 +194,7 @@ void readcb(struct ev_loop *loop, ev_io *w, int revents) {
|
||||
if (client->try_again_or_fail() == 0) {
|
||||
return;
|
||||
}
|
||||
client->worker->free_client(client);
|
||||
delete client;
|
||||
return;
|
||||
}
|
||||
@@ -241,16 +226,71 @@ void rate_period_timeout_w_cb(struct ev_loop *loop, ev_timer *w, int revents) {
|
||||
std::cerr << "client could not connect to host" << std::endl;
|
||||
client->fail();
|
||||
} else {
|
||||
client.release();
|
||||
if (worker->config->is_timing_based_mode()) {
|
||||
worker->clients.push_back(client.release());
|
||||
} else {
|
||||
client.release();
|
||||
}
|
||||
}
|
||||
worker->report_rate_progress();
|
||||
}
|
||||
if (worker->nconns_made >= worker->nclients) {
|
||||
ev_timer_stop(worker->loop, w);
|
||||
if (!worker->config->is_timing_based_mode()) {
|
||||
if (worker->nconns_made >= worker->nclients) {
|
||||
ev_timer_stop(worker->loop, w);
|
||||
}
|
||||
} else {
|
||||
// To check whether all created clients are pushed correctly
|
||||
assert(worker->nclients == worker->clients.size());
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
// Called when the duration for infinite number of requests are over
|
||||
void duration_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
|
||||
auto worker = static_cast<Worker *>(w->data);
|
||||
|
||||
worker->current_phase = Phase::DURATION_OVER;
|
||||
|
||||
std::cout << "Main benchmark duration is over for thread #" << worker->id
|
||||
<< ". Stopping all clients." << std::endl;
|
||||
worker->stop_all_clients();
|
||||
std::cout << "Stopped all clients for thread #" << worker->id << std::endl;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
// Called when the warmup duration for infinite number of requests are over
|
||||
void warmup_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
|
||||
auto worker = static_cast<Worker *>(w->data);
|
||||
|
||||
std::cout << "Warm-up phase is over for thread #" << worker->id << "."
|
||||
<< std::endl;
|
||||
std::cout << "Main benchmark duration is started for thread #" << worker->id
|
||||
<< "." << std::endl;
|
||||
assert(worker->stats.req_started == 0);
|
||||
assert(worker->stats.req_done == 0);
|
||||
|
||||
for (auto client : worker->clients) {
|
||||
if (client) {
|
||||
assert(client->req_todo == 0);
|
||||
assert(client->req_left == 1);
|
||||
assert(client->req_inflight == 0);
|
||||
assert(client->req_started == 0);
|
||||
assert(client->req_done == 0);
|
||||
|
||||
client->record_client_start_time();
|
||||
client->clear_connect_times();
|
||||
client->record_connect_start_time();
|
||||
}
|
||||
}
|
||||
|
||||
worker->current_phase = Phase::MAIN_DURATION;
|
||||
|
||||
ev_timer_start(worker->loop, &worker->duration_watcher);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
// Called when an a connection has been inactive for a set period of time
|
||||
// or a fixed amount of time after all requests have been made on a
|
||||
@@ -269,8 +309,7 @@ void conn_timeout_cb(EV_P_ ev_timer *w, int revents) {
|
||||
|
||||
namespace {
|
||||
bool check_stop_client_request_timeout(Client *client, ev_timer *w) {
|
||||
if (client->req_left == 0 ||
|
||||
client->streams.size() >= client->session->max_concurrent_streams()) {
|
||||
if (client->req_left == 0) {
|
||||
// no more requests to make, stop timer
|
||||
ev_timer_stop(client->worker->loop, w);
|
||||
return true;
|
||||
@@ -284,6 +323,11 @@ namespace {
|
||||
void client_request_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
|
||||
auto client = static_cast<Client *>(w->data);
|
||||
|
||||
if (client->streams.size() >= (size_t)config.max_concurrent_streams) {
|
||||
ev_timer_stop(client->worker->loop, w);
|
||||
return;
|
||||
}
|
||||
|
||||
if (client->submit_request() != 0) {
|
||||
ev_timer_stop(client->worker->loop, w);
|
||||
client->process_request_failure();
|
||||
@@ -336,6 +380,11 @@ Client::Client(uint32_t id, Worker *worker, size_t req_todo)
|
||||
fd(-1),
|
||||
new_connection_requested(false),
|
||||
final(false) {
|
||||
if (req_todo == 0) { // this means infinite number of requests are to be made
|
||||
// This ensures that number of requests are unbounded
|
||||
// Just a positive number is fine, we chose the first positive number
|
||||
req_left = 1;
|
||||
}
|
||||
ev_io_init(&wev, writecb, 0, EV_WRITE);
|
||||
ev_io_init(&rev, readcb, 0, EV_READ);
|
||||
|
||||
@@ -361,10 +410,7 @@ Client::~Client() {
|
||||
SSL_free(ssl);
|
||||
}
|
||||
|
||||
if (sampling_should_pick(worker->client_smp)) {
|
||||
sampling_advance_point(worker->client_smp);
|
||||
worker->sample_client_stat(&cstat);
|
||||
}
|
||||
worker->sample_client_stat(&cstat);
|
||||
++worker->client_smp.n;
|
||||
}
|
||||
|
||||
@@ -407,9 +453,17 @@ int Client::make_socket(addrinfo *addr) {
|
||||
int Client::connect() {
|
||||
int rv;
|
||||
|
||||
record_client_start_time();
|
||||
clear_connect_times();
|
||||
record_connect_start_time();
|
||||
if (!worker->config->is_timing_based_mode() ||
|
||||
worker->current_phase == Phase::MAIN_DURATION) {
|
||||
record_client_start_time();
|
||||
clear_connect_times();
|
||||
record_connect_start_time();
|
||||
} else if (worker->current_phase == Phase::INITIAL_IDLE) {
|
||||
worker->current_phase = Phase::WARM_UP;
|
||||
std::cout << "Warm-up started for thread #" << worker->id << "."
|
||||
<< std::endl;
|
||||
ev_timer_start(worker->loop, &worker->warmup_watcher);
|
||||
}
|
||||
|
||||
if (worker->config->conn_inactivity_timeout > 0.) {
|
||||
ev_timer_again(worker->loop, &conn_inactivity_watcher);
|
||||
@@ -467,13 +521,17 @@ int Client::try_again_or_fail() {
|
||||
|
||||
if (new_connection_requested) {
|
||||
new_connection_requested = false;
|
||||
if (req_left) {
|
||||
// At the moment, we don't have a facility to re-start request
|
||||
// already in in-flight. Make them fail.
|
||||
worker->stats.req_failed += req_inflight;
|
||||
worker->stats.req_error += req_inflight;
|
||||
|
||||
req_inflight = 0;
|
||||
if (req_left) {
|
||||
|
||||
if (worker->current_phase == Phase::MAIN_DURATION) {
|
||||
// At the moment, we don't have a facility to re-start request
|
||||
// already in in-flight. Make them fail.
|
||||
worker->stats.req_failed += req_inflight;
|
||||
worker->stats.req_error += req_inflight;
|
||||
|
||||
req_inflight = 0;
|
||||
}
|
||||
|
||||
// Keep using current address
|
||||
if (connect() == 0) {
|
||||
@@ -529,11 +587,16 @@ int Client::submit_request() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (worker->current_phase != Phase::MAIN_DURATION) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
++worker->stats.req_started;
|
||||
--req_left;
|
||||
++req_started;
|
||||
++req_inflight;
|
||||
|
||||
if (!worker->config->is_timing_based_mode()) {
|
||||
--req_left;
|
||||
}
|
||||
// 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_left == 0) {
|
||||
@@ -544,6 +607,10 @@ int Client::submit_request() {
|
||||
}
|
||||
|
||||
void Client::process_timedout_streams() {
|
||||
if (worker->current_phase != Phase::MAIN_DURATION) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto &p : streams) {
|
||||
auto &req_stat = p.second.req_stat;
|
||||
if (!req_stat.completed) {
|
||||
@@ -557,6 +624,10 @@ void Client::process_timedout_streams() {
|
||||
}
|
||||
|
||||
void Client::process_abandoned_streams() {
|
||||
if (worker->current_phase != Phase::MAIN_DURATION) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto req_abandoned = req_inflight + req_left;
|
||||
|
||||
worker->stats.req_failed += req_abandoned;
|
||||
@@ -567,6 +638,10 @@ void Client::process_abandoned_streams() {
|
||||
}
|
||||
|
||||
void Client::process_request_failure() {
|
||||
if (worker->current_phase != Phase::MAIN_DURATION) {
|
||||
return;
|
||||
}
|
||||
|
||||
worker->stats.req_failed += req_left;
|
||||
worker->stats.req_error += req_left;
|
||||
|
||||
@@ -575,6 +650,8 @@ void Client::process_request_failure() {
|
||||
if (req_inflight == 0) {
|
||||
terminate_session();
|
||||
}
|
||||
std::cout << "Process Request Failure:" << worker->stats.req_failed
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
namespace {
|
||||
@@ -625,7 +702,7 @@ void Client::report_tls_info() {
|
||||
if (worker->id == 0 && !worker->tls_info_report_done) {
|
||||
worker->tls_info_report_done = true;
|
||||
auto cipher = SSL_get_current_cipher(ssl);
|
||||
std::cout << "TLS Protocol: " << ssl::get_tls_protocol(ssl) << "\n"
|
||||
std::cout << "TLS Protocol: " << tls::get_tls_protocol(ssl) << "\n"
|
||||
<< "Cipher: " << SSL_CIPHER_get_name(cipher) << std::endl;
|
||||
print_server_tmp_key(ssl);
|
||||
}
|
||||
@@ -653,6 +730,15 @@ void Client::on_header(int32_t stream_id, const uint8_t *name, size_t namelen,
|
||||
return;
|
||||
}
|
||||
auto &stream = (*itr).second;
|
||||
|
||||
if (worker->current_phase != Phase::MAIN_DURATION) {
|
||||
// If the stream is for warm-up phase, then mark as a success
|
||||
// But we do not update the count for 2xx, 3xx, etc status codes
|
||||
// Same has been done in on_status_code function
|
||||
stream.status_success = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (stream.status_success == -1 && namelen == 7 &&
|
||||
util::streq_l(":status", name, namelen)) {
|
||||
int status = 0;
|
||||
@@ -691,6 +777,11 @@ void Client::on_status_code(int32_t stream_id, uint16_t status) {
|
||||
}
|
||||
auto &stream = (*itr).second;
|
||||
|
||||
if (worker->current_phase != Phase::MAIN_DURATION) {
|
||||
stream.status_success = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (status >= 200 && status < 300) {
|
||||
++worker->stats.status[2];
|
||||
stream.status_success = 1;
|
||||
@@ -706,40 +797,39 @@ 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;
|
||||
if (worker->current_phase == Phase::MAIN_DURATION) {
|
||||
if (req_inflight > 0) {
|
||||
--req_inflight;
|
||||
}
|
||||
auto req_stat = get_req_stat(stream_id);
|
||||
if (!req_stat) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto req_stat = get_req_stat(stream_id);
|
||||
if (!req_stat) {
|
||||
return;
|
||||
}
|
||||
req_stat->stream_close_time = std::chrono::steady_clock::now();
|
||||
if (success) {
|
||||
req_stat->completed = true;
|
||||
++worker->stats.req_success;
|
||||
++cstat.req_success;
|
||||
|
||||
req_stat->stream_close_time = std::chrono::steady_clock::now();
|
||||
if (success) {
|
||||
req_stat->completed = true;
|
||||
++worker->stats.req_success;
|
||||
++cstat.req_success;
|
||||
if (streams[stream_id].status_success == 1) {
|
||||
++worker->stats.req_status_success;
|
||||
} else {
|
||||
++worker->stats.req_failed;
|
||||
}
|
||||
|
||||
if (streams[stream_id].status_success == 1) {
|
||||
++worker->stats.req_status_success;
|
||||
worker->sample_req_stat(req_stat);
|
||||
|
||||
// Count up in successful cases only
|
||||
++worker->request_times_smp.n;
|
||||
} else {
|
||||
++worker->stats.req_failed;
|
||||
++worker->stats.req_error;
|
||||
}
|
||||
|
||||
if (sampling_should_pick(worker->request_times_smp)) {
|
||||
sampling_advance_point(worker->request_times_smp);
|
||||
worker->sample_req_stat(req_stat);
|
||||
}
|
||||
|
||||
// Count up in successful cases only
|
||||
++worker->request_times_smp.n;
|
||||
} else {
|
||||
++worker->stats.req_failed;
|
||||
++worker->stats.req_error;
|
||||
++worker->stats.req_done;
|
||||
++req_done;
|
||||
}
|
||||
|
||||
++worker->stats.req_done;
|
||||
|
||||
worker->report_progress();
|
||||
streams.erase(stream_id);
|
||||
if (req_left == 0 && req_inflight == 0) {
|
||||
@@ -747,10 +837,14 @@ void Client::on_stream_close(int32_t stream_id, bool success, bool final) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!config.timing_script && !final && req_left > 0 &&
|
||||
submit_request() != 0) {
|
||||
process_request_failure();
|
||||
return;
|
||||
if (!final && req_left > 0) {
|
||||
if (config.timing_script) {
|
||||
if (!ev_is_active(&request_timeout_watcher)) {
|
||||
ev_feed_event(worker->loop, &request_timeout_watcher, EV_TIMER);
|
||||
}
|
||||
} else if (submit_request() != 0) {
|
||||
process_request_failure();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -865,7 +959,9 @@ int Client::connection_made() {
|
||||
record_connect_time();
|
||||
|
||||
if (!config.timing_script) {
|
||||
auto nreq = std::min(req_left, session->max_concurrent_streams());
|
||||
auto nreq = config.is_timing_based_mode()
|
||||
? std::max(req_left, session->max_concurrent_streams())
|
||||
: std::min(req_left, session->max_concurrent_streams());
|
||||
for (; nreq > 0; --nreq) {
|
||||
if (submit_request() != 0) {
|
||||
process_request_failure();
|
||||
@@ -906,7 +1002,9 @@ int Client::on_read(const uint8_t *data, size_t len) {
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
worker->stats.bytes_total += len;
|
||||
if (worker->current_phase == Phase::MAIN_DURATION) {
|
||||
worker->stats.bytes_total += len;
|
||||
}
|
||||
signal_write();
|
||||
return 0;
|
||||
}
|
||||
@@ -1176,37 +1274,78 @@ Worker::Worker(uint32_t id, SSL_CTX *ssl_ctx, size_t req_todo, size_t nclients,
|
||||
rate(rate),
|
||||
max_samples(max_samples),
|
||||
next_client_id(0) {
|
||||
if (!config->is_rate_mode()) {
|
||||
if (!config->is_rate_mode() && !config->is_timing_based_mode()) {
|
||||
progress_interval = std::max(static_cast<size_t>(1), req_todo / 10);
|
||||
} else {
|
||||
progress_interval = std::max(static_cast<size_t>(1), nclients / 10);
|
||||
}
|
||||
|
||||
// Below timeout is not needed in case of timing-based benchmarking
|
||||
// create timer that will go off every rate_period
|
||||
ev_timer_init(&timeout_watcher, rate_period_timeout_w_cb, 0.,
|
||||
config->rate_period);
|
||||
timeout_watcher.data = this;
|
||||
|
||||
stats.req_stats.reserve(std::min(req_todo, max_samples));
|
||||
stats.client_stats.reserve(std::min(nclients, max_samples));
|
||||
if (config->is_timing_based_mode()) {
|
||||
stats.req_stats.reserve(std::max(req_todo, max_samples));
|
||||
stats.client_stats.reserve(std::max(nclients, max_samples));
|
||||
} else {
|
||||
stats.req_stats.reserve(std::min(req_todo, max_samples));
|
||||
stats.client_stats.reserve(std::min(nclients, max_samples));
|
||||
}
|
||||
|
||||
sampling_init(request_times_smp, req_todo, max_samples);
|
||||
sampling_init(client_smp, nclients, max_samples);
|
||||
sampling_init(request_times_smp, max_samples);
|
||||
sampling_init(client_smp, max_samples);
|
||||
|
||||
ev_timer_init(&duration_watcher, duration_timeout_cb, config->duration, 0.);
|
||||
duration_watcher.data = this;
|
||||
|
||||
ev_timer_init(&warmup_watcher, warmup_timeout_cb, config->warm_up_time, 0.);
|
||||
warmup_watcher.data = this;
|
||||
|
||||
if (config->is_timing_based_mode()) {
|
||||
current_phase = Phase::INITIAL_IDLE;
|
||||
} else {
|
||||
current_phase = Phase::MAIN_DURATION;
|
||||
}
|
||||
}
|
||||
|
||||
Worker::~Worker() {
|
||||
ev_timer_stop(loop, &timeout_watcher);
|
||||
ev_timer_stop(loop, &duration_watcher);
|
||||
ev_timer_stop(loop, &warmup_watcher);
|
||||
ev_loop_destroy(loop);
|
||||
}
|
||||
|
||||
void Worker::stop_all_clients() {
|
||||
for (auto client : clients) {
|
||||
if (client && client->session) {
|
||||
client->terminate_session();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Worker::free_client(Client *deleted_client) {
|
||||
for (auto &client : clients) {
|
||||
if (client == deleted_client) {
|
||||
client->req_todo = client->req_done;
|
||||
stats.req_todo += client->req_todo;
|
||||
auto index = &client - &clients[0];
|
||||
clients[index] = NULL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Worker::run() {
|
||||
if (!config->is_rate_mode()) {
|
||||
if (!config->is_rate_mode() && !config->is_timing_based_mode()) {
|
||||
for (size_t i = 0; i < nclients; ++i) {
|
||||
auto req_todo = nreqs_per_client;
|
||||
if (nreqs_rem > 0) {
|
||||
++req_todo;
|
||||
--nreqs_rem;
|
||||
}
|
||||
|
||||
auto client = make_unique<Client>(next_client_id++, this, req_todo);
|
||||
if (client->connect() != 0) {
|
||||
std::cerr << "client could not connect to host" << std::endl;
|
||||
@@ -1215,27 +1354,45 @@ void Worker::run() {
|
||||
client.release();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
} else if (config->is_rate_mode()) {
|
||||
ev_timer_again(loop, &timeout_watcher);
|
||||
|
||||
// call callback so that we don't waste the first rate_period
|
||||
rate_period_timeout_w_cb(loop, &timeout_watcher, 0);
|
||||
} else {
|
||||
// call the callback to start for one single time
|
||||
rate_period_timeout_w_cb(loop, &timeout_watcher, 0);
|
||||
}
|
||||
ev_run(loop, 0);
|
||||
}
|
||||
|
||||
namespace {
|
||||
template <typename Stats, typename Stat>
|
||||
void sample(Sampling &smp, Stats &stats, Stat *s) {
|
||||
++smp.n;
|
||||
if (stats.size() < smp.max_samples) {
|
||||
stats.push_back(*s);
|
||||
return;
|
||||
}
|
||||
auto d = std::uniform_int_distribution<unsigned long>(0, smp.n - 1);
|
||||
auto i = d(gen);
|
||||
if (i < smp.max_samples) {
|
||||
stats[i] = *s;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void Worker::sample_req_stat(RequestStat *req_stat) {
|
||||
stats.req_stats.push_back(*req_stat);
|
||||
assert(stats.req_stats.size() <= max_samples);
|
||||
sample(request_times_smp, stats.req_stats, req_stat);
|
||||
}
|
||||
|
||||
void Worker::sample_client_stat(ClientStat *cstat) {
|
||||
stats.client_stats.push_back(*cstat);
|
||||
assert(stats.client_stats.size() <= max_samples);
|
||||
sample(client_smp, stats.client_stats, cstat);
|
||||
}
|
||||
|
||||
void Worker::report_progress() {
|
||||
if (id != 0 || config->is_rate_mode() || stats.req_done % progress_interval) {
|
||||
if (id != 0 || config->is_rate_mode() || stats.req_done % progress_interval ||
|
||||
config->is_timing_based_mode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1313,14 +1470,10 @@ process_time_stats(const std::vector<std::unique_ptr<Worker>> &workers) {
|
||||
size_t nclient_times = 0;
|
||||
for (const auto &w : workers) {
|
||||
nrequest_times += w->stats.req_stats.size();
|
||||
if (w->request_times_smp.interval != 0.) {
|
||||
request_times_sampling = true;
|
||||
}
|
||||
request_times_sampling = w->request_times_smp.n > w->stats.req_stats.size();
|
||||
|
||||
nclient_times += w->stats.client_stats.size();
|
||||
if (w->client_smp.interval != 0.) {
|
||||
client_times_sampling = true;
|
||||
}
|
||||
client_times_sampling = w->client_smp.n > w->stats.client_stats.size();
|
||||
}
|
||||
|
||||
std::vector<double> request_times;
|
||||
@@ -1581,12 +1734,27 @@ std::unique_ptr<Worker> create_worker(uint32_t id, SSL_CTX *ssl_ctx,
|
||||
<< util::duration_str(config.rate_period) << " ";
|
||||
}
|
||||
|
||||
std::cout << "spawning thread #" << id << ": " << nclients
|
||||
<< " total client(s). " << rate_report.str() << nreqs
|
||||
<< " total requests" << std::endl;
|
||||
if (config.is_timing_based_mode()) {
|
||||
std::cout << "spawning thread #" << id << ": " << nclients
|
||||
<< " total client(s). Timing-based test with "
|
||||
<< config.warm_up_time << "s of warm-up time and "
|
||||
<< config.duration << "s of main duration for measurements."
|
||||
<< std::endl;
|
||||
} else {
|
||||
std::cout << "spawning thread #" << id << ": " << nclients
|
||||
<< " total client(s). " << rate_report.str() << nreqs
|
||||
<< " total requests" << std::endl;
|
||||
}
|
||||
|
||||
return make_unique<Worker>(id, ssl_ctx, nreqs, nclients, rate, max_samples,
|
||||
&config);
|
||||
if (config.is_rate_mode()) {
|
||||
return make_unique<Worker>(id, ssl_ctx, nreqs, nclients, rate, max_samples,
|
||||
&config);
|
||||
} else {
|
||||
// Here rate is same as client because the rate_timeout callback
|
||||
// will be called only once
|
||||
return make_unique<Worker>(id, ssl_ctx, nreqs, nclients, nclients,
|
||||
max_samples, &config);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -1652,18 +1820,18 @@ Options:
|
||||
Number of requests across all clients. If it is used
|
||||
with --timing-script-file option, this option specifies
|
||||
the number of requests each client performs rather than
|
||||
the number of requests across all clients.
|
||||
the number of requests across all clients. This option
|
||||
is ignored if timing-based benchmarking is enabled (see
|
||||
--duration option).
|
||||
Default: )"
|
||||
<< config.nreqs << R"(
|
||||
-c, --clients=<N>
|
||||
Number of concurrent clients. With -r option, this
|
||||
specifies the maximum number of connections to be made.
|
||||
Default: )"
|
||||
<< config.nclients << R"(
|
||||
Default: )" << config.nclients << R"(
|
||||
-t, --threads=<N>
|
||||
Number of native threads.
|
||||
Default: )"
|
||||
<< config.nthreads << R"(
|
||||
Default: )" << config.nthreads << R"(
|
||||
-i, --input-file=<PATH>
|
||||
Path of a file with multiple URIs are separated by EOLs.
|
||||
This option will disable URIs getting from command-line.
|
||||
@@ -1689,8 +1857,7 @@ Options:
|
||||
(2**<N>)-1. For SPDY, if <N> is strictly less than 16,
|
||||
this option is ignored. Otherwise 2**<N> is used for
|
||||
SPDY.
|
||||
Default: )"
|
||||
<< config.connection_window_bits << R"(
|
||||
Default: )" << config.connection_window_bits << R"(
|
||||
-H, --header=<HEADER>
|
||||
Add/Override a header to the requests.
|
||||
--ciphers=<SUITE>
|
||||
@@ -1710,8 +1877,7 @@ Options:
|
||||
Available protocols: )";
|
||||
#endif // !HAVE_SPDYLAY
|
||||
out << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"( and
|
||||
)"
|
||||
<< NGHTTP2_H1_1 << R"(
|
||||
)" << NGHTTP2_H1_1 << R"(
|
||||
Default: )"
|
||||
<< NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"(
|
||||
-d, --data=<PATH>
|
||||
@@ -1737,6 +1903,13 @@ Options:
|
||||
length of the period in time. This option is ignored if
|
||||
the rate option is not used. The default value for this
|
||||
option is 1s.
|
||||
-D, --duration=<N>
|
||||
Specifies the main duration for the measurements in case
|
||||
of timing-based benchmarking.
|
||||
--warm-up-time=<DURATION>
|
||||
Specifies the time period before starting the actual
|
||||
measurements, in case of timing-based benchmarking.
|
||||
Needs to provided along with -D option.
|
||||
-T, --connection-active-timeout=<DURATION>
|
||||
Specifies the maximum time that h2load is willing to
|
||||
keep a connection open, regardless of the activity on
|
||||
@@ -1787,8 +1960,7 @@ Options:
|
||||
NPN. The parameter must be delimited by a single comma
|
||||
only and any white spaces are treated as a part of
|
||||
protocol string.
|
||||
Default: )"
|
||||
<< DEFAULT_NPN_LIST << R"(
|
||||
Default: )" << DEFAULT_NPN_LIST << R"(
|
||||
--h1 Short hand for --npn-list=http/1.1
|
||||
--no-tls-proto=http/1.1, which effectively force
|
||||
http/1.1 for both http and https URI.
|
||||
@@ -1816,16 +1988,15 @@ Options:
|
||||
The <DURATION> argument is an integer and an optional unit (e.g., 1s
|
||||
is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms
|
||||
(hours, minutes, seconds and milliseconds, respectively). If a unit
|
||||
is omitted, a second is used as unit.)"
|
||||
<< std::endl;
|
||||
is omitted, a second is used as unit.)" << std::endl;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
ssl::libssl_init();
|
||||
tls::libssl_init();
|
||||
|
||||
#ifndef NOTHREADS
|
||||
ssl::LibsslGlobalLock lock;
|
||||
tls::LibsslGlobalLock lock;
|
||||
#endif // NOTHREADS
|
||||
|
||||
std::string datafile;
|
||||
@@ -1850,6 +2021,7 @@ int main(int argc, char **argv) {
|
||||
{"rate", required_argument, nullptr, 'r'},
|
||||
{"connection-active-timeout", required_argument, nullptr, 'T'},
|
||||
{"connection-inactivity-timeout", required_argument, nullptr, 'N'},
|
||||
{"duration", required_argument, nullptr, 'D'},
|
||||
{"timing-script-file", required_argument, &flag, 3},
|
||||
{"base-uri", required_argument, nullptr, 'B'},
|
||||
{"npn-list", required_argument, &flag, 4},
|
||||
@@ -1857,10 +2029,12 @@ int main(int argc, char **argv) {
|
||||
{"h1", no_argument, &flag, 6},
|
||||
{"header-table-size", required_argument, &flag, 7},
|
||||
{"encoder-header-table-size", required_argument, &flag, 8},
|
||||
{"warm-up-time", required_argument, &flag, 9},
|
||||
{nullptr, 0, nullptr, 0}};
|
||||
int option_index = 0;
|
||||
auto c = getopt_long(argc, argv, "hvW:c:d:m:n:p:t:w:H:i:r:T:N:B:",
|
||||
long_options, &option_index);
|
||||
auto c = getopt_long(argc, argv,
|
||||
"hvW:c:d:m:n:p:t:w:H:i:r:T:N:D:B:", long_options,
|
||||
&option_index);
|
||||
if (c == -1) {
|
||||
break;
|
||||
}
|
||||
@@ -2015,6 +2189,14 @@ int main(int argc, char **argv) {
|
||||
config.base_uri = arg.str();
|
||||
break;
|
||||
}
|
||||
case 'D':
|
||||
config.duration = strtoul(optarg, nullptr, 10);
|
||||
if (config.duration == 0) {
|
||||
std::cerr << "-D: the main duration for timing-based benchmarking "
|
||||
<< "must be positive." << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
case 'v':
|
||||
config.verbose = true;
|
||||
break;
|
||||
@@ -2071,6 +2253,14 @@ int main(int argc, char **argv) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
case 9:
|
||||
// --warm-up-time
|
||||
config.warm_up_time = util::parse_duration_with_unit(optarg);
|
||||
if (!std::isfinite(config.warm_up_time)) {
|
||||
std::cerr << "--warm-up-time: value error " << optarg << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -2156,8 +2346,9 @@ int main(int argc, char **argv) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (config.nreqs == 0) {
|
||||
std::cerr << "-n: the number of requests must be strictly greater than 0."
|
||||
if (config.nreqs == 0 && !config.is_timing_based_mode()) {
|
||||
std::cerr << "-n: the number of requests must be strictly greater than 0 "
|
||||
"if timing-based test is not being run."
|
||||
<< std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
@@ -2181,7 +2372,8 @@ int main(int argc, char **argv) {
|
||||
|
||||
// With timing script, we don't distribute config.nreqs to each
|
||||
// client or thread.
|
||||
if (!config.timing_script && config.nreqs < config.nclients) {
|
||||
if (!config.timing_script && config.nreqs < config.nclients &&
|
||||
!config.is_timing_based_mode()) {
|
||||
std::cerr << "-n, -c: the number of requests must be greater than or "
|
||||
<< "equal to the clients." << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
@@ -2189,11 +2381,14 @@ int main(int argc, char **argv) {
|
||||
|
||||
if (config.nclients < config.nthreads) {
|
||||
std::cerr << "-c, -t: the number of clients must be greater than or equal "
|
||||
"to the number of threads."
|
||||
<< std::endl;
|
||||
<< "to the number of threads." << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (config.is_timing_based_mode()) {
|
||||
config.nreqs = 0;
|
||||
}
|
||||
|
||||
if (config.is_rate_mode()) {
|
||||
if (config.rate < config.nthreads) {
|
||||
std::cerr << "-r, -t: the connection rate must be greater than or equal "
|
||||
@@ -2242,9 +2437,9 @@ 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);
|
||||
|
||||
if (nghttp2::ssl::ssl_ctx_set_proto_versions(
|
||||
ssl_ctx, nghttp2::ssl::NGHTTP2_TLS_MIN_VERSION,
|
||||
nghttp2::ssl::NGHTTP2_TLS_MAX_VERSION) != 0) {
|
||||
if (nghttp2::tls::ssl_ctx_set_proto_versions(
|
||||
ssl_ctx, nghttp2::tls::NGHTTP2_TLS_MIN_VERSION,
|
||||
nghttp2::tls::NGHTTP2_TLS_MAX_VERSION) != 0) {
|
||||
std::cerr << "Could not set TLS versions" << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
@@ -2527,7 +2722,7 @@ int main(int argc, char **argv) {
|
||||
// Requests which have not been issued due to connection errors, are
|
||||
// counted towards req_failed and req_error.
|
||||
auto req_not_issued =
|
||||
stats.req_todo - stats.req_status_success - stats.req_failed;
|
||||
(stats.req_todo - stats.req_status_success - stats.req_failed);
|
||||
stats.req_failed += req_not_issued;
|
||||
stats.req_error += req_not_issued;
|
||||
|
||||
@@ -2538,10 +2733,17 @@ int main(int argc, char **argv) {
|
||||
double rps = 0;
|
||||
int64_t bps = 0;
|
||||
if (duration.count() > 0) {
|
||||
auto secd = std::chrono::duration_cast<
|
||||
std::chrono::duration<double, std::chrono::seconds::period>>(duration);
|
||||
rps = stats.req_success / secd.count();
|
||||
bps = stats.bytes_total / secd.count();
|
||||
if (config.is_timing_based_mode()) {
|
||||
// we only want to consider the main duration if warm-up is given
|
||||
rps = stats.req_success / config.duration;
|
||||
bps = stats.bytes_total / config.duration;
|
||||
} else {
|
||||
auto secd = std::chrono::duration_cast<
|
||||
std::chrono::duration<double, std::chrono::seconds::period>>(
|
||||
duration);
|
||||
rps = stats.req_success / secd.count();
|
||||
bps = stats.bytes_total / secd.count();
|
||||
}
|
||||
}
|
||||
|
||||
double header_space_savings = 0.;
|
||||
@@ -2554,14 +2756,14 @@ int main(int argc, char **argv) {
|
||||
finished in )"
|
||||
<< util::format_duration(duration) << ", " << rps << " req/s, "
|
||||
<< util::utos_funit(bps) << R"(B/s
|
||||
requests: )" << stats.req_todo
|
||||
<< " total, " << stats.req_started << " started, " << stats.req_done
|
||||
<< " done, " << stats.req_status_success << " succeeded, "
|
||||
<< stats.req_failed << " failed, " << stats.req_error
|
||||
<< " errored, " << stats.req_timedout << R"( timeout
|
||||
status codes: )"
|
||||
<< stats.status[2] << " 2xx, " << stats.status[3] << " 3xx, "
|
||||
<< stats.status[4] << " 4xx, " << stats.status[5] << R"( 5xx
|
||||
requests: )" << stats.req_todo << " total, "
|
||||
<< stats.req_started << " started, " << stats.req_done << " done, "
|
||||
<< stats.req_status_success << " succeeded, " << stats.req_failed
|
||||
<< " failed, " << stats.req_error << " errored, "
|
||||
<< stats.req_timedout << R"( timeout
|
||||
status codes: )" << stats.status[2] << " 2xx, "
|
||||
<< stats.status[3] << " 3xx, " << stats.status[4] << " 4xx, "
|
||||
<< stats.status[5] << R"( 5xx
|
||||
traffic: )" << util::utos_funit(stats.bytes_total)
|
||||
<< "B (" << stats.bytes_total << ") total, "
|
||||
<< util::utos_funit(stats.bytes_head) << "B (" << stats.bytes_head
|
||||
@@ -2569,12 +2771,12 @@ traffic: )" << util::utos_funit(stats.bytes_total)
|
||||
<< "%), " << util::utos_funit(stats.bytes_body) << "B ("
|
||||
<< stats.bytes_body << R"() data
|
||||
min max mean sd +/- sd
|
||||
time for request: )"
|
||||
<< std::setw(10) << util::format_duration(ts.request.min) << " "
|
||||
<< std::setw(10) << util::format_duration(ts.request.max) << " "
|
||||
<< std::setw(10) << util::format_duration(ts.request.mean) << " "
|
||||
<< std::setw(10) << util::format_duration(ts.request.sd)
|
||||
<< std::setw(9) << util::dtos(ts.request.within_sd) << "%"
|
||||
time for request: )" << std::setw(10)
|
||||
<< util::format_duration(ts.request.min) << " " << std::setw(10)
|
||||
<< util::format_duration(ts.request.max) << " " << std::setw(10)
|
||||
<< util::format_duration(ts.request.mean) << " " << std::setw(10)
|
||||
<< util::format_duration(ts.request.sd) << std::setw(9)
|
||||
<< util::dtos(ts.request.within_sd) << "%"
|
||||
<< "\ntime for connect: " << std::setw(10)
|
||||
<< util::format_duration(ts.connect.min) << " " << std::setw(10)
|
||||
<< util::format_duration(ts.connect.max) << " " << std::setw(10)
|
||||
|
||||
40
src/h2load.h
40
src/h2load.h
@@ -85,6 +85,10 @@ struct Config {
|
||||
// rate at which connections should be made
|
||||
size_t rate;
|
||||
ev_tstamp rate_period;
|
||||
// amount of time for main measurements in timing-based test
|
||||
ev_tstamp duration;
|
||||
// amount of time to wait before starting measurements in timing-based test
|
||||
ev_tstamp warm_up_time;
|
||||
// amount of time to wait for activity on a given connection
|
||||
ev_tstamp conn_active_timeout;
|
||||
// amount of time to wait after the last request is made on a connection
|
||||
@@ -118,6 +122,7 @@ struct Config {
|
||||
~Config();
|
||||
|
||||
bool is_rate_mode() const;
|
||||
bool is_timing_based_mode() const;
|
||||
bool has_base_uri() const;
|
||||
};
|
||||
|
||||
@@ -139,7 +144,7 @@ struct ClientStat {
|
||||
// time client end (i.e., client somehow processed all requests it
|
||||
// is responsible for, and disconnected)
|
||||
std::chrono::steady_clock::time_point client_end_time;
|
||||
// The number of requests completed successfull, but not necessarily
|
||||
// The number of requests completed successful, but not necessarily
|
||||
// means successful HTTP status code.
|
||||
size_t req_success;
|
||||
|
||||
@@ -180,7 +185,7 @@ struct Stats {
|
||||
size_t req_started;
|
||||
// The number of requests finished
|
||||
size_t req_done;
|
||||
// The number of requests completed successfull, but not necessarily
|
||||
// The number of requests completed successful, but not necessarily
|
||||
// means successful HTTP status code.
|
||||
size_t req_success;
|
||||
// The number of requests marked as success. HTTP status code is
|
||||
@@ -215,15 +220,21 @@ struct Stats {
|
||||
|
||||
enum ClientState { CLIENT_IDLE, CLIENT_CONNECTED };
|
||||
|
||||
// This type tells whether the client is in warmup phase or not or is over
|
||||
enum class Phase {
|
||||
INITIAL_IDLE, // Initial idle state before warm-up phase
|
||||
WARM_UP, // Warm up phase when no measurements are done
|
||||
MAIN_DURATION, // Main measurement phase; if timing-based
|
||||
// test is not run, this is the default phase
|
||||
DURATION_OVER // This phase occurs after the measurements are over
|
||||
};
|
||||
|
||||
struct Client;
|
||||
|
||||
// We use systematic sampling method
|
||||
// We use reservoir sampling method
|
||||
struct Sampling {
|
||||
// sampling interval
|
||||
double interval;
|
||||
// cumulative value of interval, and the next point is the integer
|
||||
// rounded up from this value.
|
||||
double point;
|
||||
// maximum number of samples
|
||||
size_t max_samples;
|
||||
// number of samples seen, including discarded samples.
|
||||
size_t n;
|
||||
};
|
||||
@@ -253,6 +264,15 @@ struct Worker {
|
||||
ev_timer timeout_watcher;
|
||||
// The next client ID this worker assigns
|
||||
uint32_t next_client_id;
|
||||
// Keeps track of the current phase (for timing-based experiment) for the
|
||||
// worker
|
||||
Phase current_phase;
|
||||
// We need to keep track of the clients in order to stop them when needed
|
||||
std::vector<Client *> clients;
|
||||
// This is only active when there is not a bounded number of requests
|
||||
// specified
|
||||
ev_timer duration_watcher;
|
||||
ev_timer warmup_watcher;
|
||||
|
||||
Worker(uint32_t id, SSL_CTX *ssl_ctx, size_t nreq_todo, size_t nclients,
|
||||
size_t rate, size_t max_samples, Config *config);
|
||||
@@ -263,6 +283,10 @@ struct Worker {
|
||||
void sample_client_stat(ClientStat *cstat);
|
||||
void report_progress();
|
||||
void report_rate_progress();
|
||||
// This function calls the destructors of all the clients.
|
||||
void stop_all_clients();
|
||||
// This function frees a client from the list of clients for this Worker.
|
||||
void free_client(Client *);
|
||||
};
|
||||
|
||||
struct Stream {
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#include <cassert>
|
||||
#include <cerrno>
|
||||
#include <iostream>
|
||||
|
||||
#include "h2load.h"
|
||||
#include "util.h"
|
||||
@@ -52,6 +53,15 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||
}
|
||||
client->on_header(frame->hd.stream_id, name, namelen, value, valuelen);
|
||||
client->worker->stats.bytes_head_decomp += namelen + valuelen;
|
||||
|
||||
if (client->worker->config->verbose) {
|
||||
std::cout << "[stream_id=" << frame->hd.stream_id << "] ";
|
||||
std::cout.write(reinterpret_cast<const char *>(name), namelen);
|
||||
std::cout << ": ";
|
||||
std::cout.write(reinterpret_cast<const char *>(value), valuelen);
|
||||
std::cout << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
@@ -180,6 +190,9 @@ ssize_t send_callback(nghttp2_session *session, const uint8_t *data,
|
||||
void Http2Session::on_connect() {
|
||||
int rv;
|
||||
|
||||
// This is required with --disable-assert.
|
||||
(void)rv;
|
||||
|
||||
nghttp2_session_callbacks *callbacks;
|
||||
|
||||
nghttp2_session_callbacks_new(&callbacks);
|
||||
|
||||
172
src/http2.cc
172
src/http2.cc
@@ -358,15 +358,21 @@ nghttp2_nv make_nv_nocopy(const StringRef &name, const StringRef &value,
|
||||
|
||||
namespace {
|
||||
void copy_headers_to_nva_internal(std::vector<nghttp2_nv> &nva,
|
||||
const HeaderRefs &headers, uint8_t nv_flags) {
|
||||
for (auto &kv : headers) {
|
||||
if (kv.name.empty() || kv.name[0] == ':') {
|
||||
const HeaderRefs &headers, uint8_t nv_flags,
|
||||
uint32_t flags) {
|
||||
auto it_forwarded = std::end(headers);
|
||||
auto it_xff = std::end(headers);
|
||||
auto it_xfp = std::end(headers);
|
||||
auto it_via = std::end(headers);
|
||||
|
||||
for (auto it = std::begin(headers); it != std::end(headers); ++it) {
|
||||
auto kv = &(*it);
|
||||
if (kv->name.empty() || kv->name[0] == ':') {
|
||||
continue;
|
||||
}
|
||||
switch (kv.token) {
|
||||
switch (kv->token) {
|
||||
case HD_COOKIE:
|
||||
case HD_CONNECTION:
|
||||
case HD_FORWARDED:
|
||||
case HD_HOST:
|
||||
case HD_HTTP2_SETTINGS:
|
||||
case HD_KEEP_ALIVE:
|
||||
@@ -375,51 +381,162 @@ void copy_headers_to_nva_internal(std::vector<nghttp2_nv> &nva,
|
||||
case HD_TE:
|
||||
case HD_TRANSFER_ENCODING:
|
||||
case HD_UPGRADE:
|
||||
case HD_VIA:
|
||||
case HD_X_FORWARDED_FOR:
|
||||
case HD_X_FORWARDED_PROTO:
|
||||
continue;
|
||||
case HD_FORWARDED:
|
||||
if (flags & HDOP_STRIP_FORWARDED) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (it_forwarded == std::end(headers)) {
|
||||
it_forwarded = it;
|
||||
continue;
|
||||
}
|
||||
|
||||
kv = &(*it_forwarded);
|
||||
it_forwarded = it;
|
||||
break;
|
||||
case HD_X_FORWARDED_FOR:
|
||||
if (flags & HDOP_STRIP_X_FORWARDED_FOR) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (it_xff == std::end(headers)) {
|
||||
it_xff = it;
|
||||
continue;
|
||||
}
|
||||
|
||||
kv = &(*it_xff);
|
||||
it_xff = it;
|
||||
break;
|
||||
case HD_X_FORWARDED_PROTO:
|
||||
if (flags & HDOP_STRIP_X_FORWARDED_PROTO) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (it_xfp == std::end(headers)) {
|
||||
it_xfp = it;
|
||||
continue;
|
||||
}
|
||||
|
||||
kv = &(*it_xfp);
|
||||
it_xfp = it;
|
||||
break;
|
||||
case HD_VIA:
|
||||
if (flags & HDOP_STRIP_VIA) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (it_via == std::end(headers)) {
|
||||
it_via = it;
|
||||
continue;
|
||||
}
|
||||
|
||||
kv = &(*it_via);
|
||||
it_via = it;
|
||||
break;
|
||||
case HD_NGHTTPX_0RTT_UNIQ:
|
||||
if (flags & HDOP_STRIP_NGHTTPX_ZERO_RTT_UNIQ) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
nva.push_back(make_nv_internal(kv.name, kv.value, kv.no_index, nv_flags));
|
||||
nva.push_back(
|
||||
make_nv_internal(kv->name, kv->value, kv->no_index, nv_flags));
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void copy_headers_to_nva(std::vector<nghttp2_nv> &nva,
|
||||
const HeaderRefs &headers) {
|
||||
copy_headers_to_nva_internal(nva, headers, NGHTTP2_NV_FLAG_NONE);
|
||||
const HeaderRefs &headers, uint32_t flags) {
|
||||
copy_headers_to_nva_internal(nva, headers, NGHTTP2_NV_FLAG_NONE, flags);
|
||||
}
|
||||
|
||||
void copy_headers_to_nva_nocopy(std::vector<nghttp2_nv> &nva,
|
||||
const HeaderRefs &headers) {
|
||||
copy_headers_to_nva_internal(nva, headers, NGHTTP2_NV_FLAG_NO_COPY_NAME |
|
||||
NGHTTP2_NV_FLAG_NO_COPY_VALUE);
|
||||
const HeaderRefs &headers, uint32_t flags) {
|
||||
copy_headers_to_nva_internal(
|
||||
nva, headers,
|
||||
NGHTTP2_NV_FLAG_NO_COPY_NAME | NGHTTP2_NV_FLAG_NO_COPY_VALUE, flags);
|
||||
}
|
||||
|
||||
void build_http1_headers_from_headers(DefaultMemchunks *buf,
|
||||
const HeaderRefs &headers) {
|
||||
for (auto &kv : headers) {
|
||||
if (kv.name.empty() || kv.name[0] == ':') {
|
||||
const HeaderRefs &headers,
|
||||
uint32_t flags) {
|
||||
auto it_forwarded = std::end(headers);
|
||||
auto it_xff = std::end(headers);
|
||||
auto it_xfp = std::end(headers);
|
||||
auto it_via = std::end(headers);
|
||||
|
||||
for (auto it = std::begin(headers); it != std::end(headers); ++it) {
|
||||
auto kv = &(*it);
|
||||
if (kv->name.empty() || kv->name[0] == ':') {
|
||||
continue;
|
||||
}
|
||||
switch (kv.token) {
|
||||
switch (kv->token) {
|
||||
case HD_CONNECTION:
|
||||
case HD_COOKIE:
|
||||
case HD_FORWARDED:
|
||||
case HD_HOST:
|
||||
case HD_HTTP2_SETTINGS:
|
||||
case HD_KEEP_ALIVE:
|
||||
case HD_PROXY_CONNECTION:
|
||||
case HD_SERVER:
|
||||
case HD_UPGRADE:
|
||||
case HD_VIA:
|
||||
case HD_X_FORWARDED_FOR:
|
||||
case HD_X_FORWARDED_PROTO:
|
||||
continue;
|
||||
case HD_FORWARDED:
|
||||
if (flags & HDOP_STRIP_FORWARDED) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (it_forwarded == std::end(headers)) {
|
||||
it_forwarded = it;
|
||||
continue;
|
||||
}
|
||||
|
||||
kv = &(*it_forwarded);
|
||||
it_forwarded = it;
|
||||
break;
|
||||
case HD_X_FORWARDED_FOR:
|
||||
if (flags & HDOP_STRIP_X_FORWARDED_FOR) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (it_xff == std::end(headers)) {
|
||||
it_xff = it;
|
||||
continue;
|
||||
}
|
||||
|
||||
kv = &(*it_xff);
|
||||
it_xff = it;
|
||||
break;
|
||||
case HD_X_FORWARDED_PROTO:
|
||||
if (flags & HDOP_STRIP_X_FORWARDED_PROTO) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (it_xfp == std::end(headers)) {
|
||||
it_xfp = it;
|
||||
continue;
|
||||
}
|
||||
|
||||
kv = &(*it_xfp);
|
||||
it_xfp = it;
|
||||
break;
|
||||
case HD_VIA:
|
||||
if (flags & HDOP_STRIP_VIA) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (it_via == std::end(headers)) {
|
||||
it_via = it;
|
||||
continue;
|
||||
}
|
||||
|
||||
kv = &(*it_via);
|
||||
it_via = it;
|
||||
break;
|
||||
}
|
||||
capitalize(buf, kv.name);
|
||||
capitalize(buf, kv->name);
|
||||
buf->append(": ");
|
||||
buf->append(kv.value);
|
||||
buf->append(kv->value);
|
||||
buf->append("\r\n");
|
||||
}
|
||||
}
|
||||
@@ -808,6 +925,11 @@ int lookup_token(const uint8_t *name, size_t namelen) {
|
||||
return HD_X_FORWARDED_PROTO;
|
||||
}
|
||||
break;
|
||||
case 'q':
|
||||
if (util::streq_l("nghttpx-0rtt-uni", name, 16)) {
|
||||
return HD_NGHTTPX_0RTT_UNIQ;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1486,7 +1608,7 @@ template <typename InputIt> InputIt eat_file(InputIt first, InputIt last) {
|
||||
for (; p != first && *(p - 1) != '/'; --p)
|
||||
;
|
||||
if (p == first) {
|
||||
// this should not happend in normal case, where we expect path
|
||||
// this should not happened in normal case, where we expect path
|
||||
// starts with '/'
|
||||
*first++ = '/';
|
||||
return first;
|
||||
|
||||
41
src/http2.h
41
src/http2.h
@@ -187,24 +187,54 @@ nghttp2_nv make_nv_ls_nocopy(const char (&name)[N], const StringRef &value) {
|
||||
NGHTTP2_NV_FLAG_NO_COPY_NAME | NGHTTP2_NV_FLAG_NO_COPY_VALUE};
|
||||
}
|
||||
|
||||
enum HeaderBuildOp {
|
||||
HDOP_NONE,
|
||||
// Forwarded header fields must be stripped. If this flag is not
|
||||
// set, all Forwarded header fields other than last one are added.
|
||||
HDOP_STRIP_FORWARDED = 1,
|
||||
// X-Forwarded-For header fields must be stripped. If this flag is
|
||||
// not set, all X-Forwarded-For header fields other than last one
|
||||
// are added.
|
||||
HDOP_STRIP_X_FORWARDED_FOR = 1 << 1,
|
||||
// X-Forwarded-Proto header fields must be stripped. If this flag
|
||||
// is not set, all X-Forwarded-Proto header fields other than last
|
||||
// one are added.
|
||||
HDOP_STRIP_X_FORWARDED_PROTO = 1 << 2,
|
||||
// Via header fields must be stripped. If this flag is not set, all
|
||||
// Via header fields other than last one are added.
|
||||
HDOP_STRIP_VIA = 1 << 3,
|
||||
// nghttpx-0rtt-uniq header fields must be stripped. If this flag
|
||||
// is not set, all nghttpx-0rtt-uniq header fields are added.
|
||||
HDOP_STRIP_NGHTTPX_ZERO_RTT_UNIQ = 1 << 4,
|
||||
// Strip above all header fields.
|
||||
HDOP_STRIP_ALL = HDOP_STRIP_FORWARDED | HDOP_STRIP_X_FORWARDED_FOR |
|
||||
HDOP_STRIP_X_FORWARDED_PROTO | HDOP_STRIP_VIA |
|
||||
HDOP_STRIP_NGHTTPX_ZERO_RTT_UNIQ,
|
||||
};
|
||||
|
||||
// Appends headers in |headers| to |nv|. |headers| must be indexed
|
||||
// before this call (its element's token field is assigned). Certain
|
||||
// headers, including disallowed headers in HTTP/2 spec and headers
|
||||
// which require special handling (i.e. via), are not copied.
|
||||
// which require special handling (i.e. via), are not copied. |flags|
|
||||
// is one or more of HeaderBuildOp flags. They tell function that
|
||||
// certain header fields should not be added.
|
||||
void copy_headers_to_nva(std::vector<nghttp2_nv> &nva,
|
||||
const HeaderRefs &headers);
|
||||
const HeaderRefs &headers, uint32_t flags);
|
||||
|
||||
// Just like copy_headers_to_nva(), but this adds
|
||||
// NGHTTP2_NV_FLAG_NO_COPY_NAME and NGHTTP2_NV_FLAG_NO_COPY_VALUE.
|
||||
void copy_headers_to_nva_nocopy(std::vector<nghttp2_nv> &nva,
|
||||
const HeaderRefs &headers);
|
||||
const HeaderRefs &headers, uint32_t flags);
|
||||
|
||||
// Appends HTTP/1.1 style header lines to |buf| from headers in
|
||||
// |headers|. |headers| must be indexed before this call (its
|
||||
// element's token field is assigned). Certain headers, which
|
||||
// requires special handling (i.e. via and cookie), are not appended.
|
||||
// |flags| is one or more of HeaderBuildOp flags. They tell function
|
||||
// that certain header fields should not be added.
|
||||
void build_http1_headers_from_headers(DefaultMemchunks *buf,
|
||||
const HeaderRefs &headers);
|
||||
const HeaderRefs &headers,
|
||||
uint32_t flags);
|
||||
|
||||
// Return positive window_size_increment if WINDOW_UPDATE should be
|
||||
// sent for the stream |stream_id|. If |stream_id| == 0, this function
|
||||
@@ -242,7 +272,7 @@ void erase_header(HeaderRef *hd);
|
||||
//
|
||||
// This function returns the new rewritten URI on success. If the
|
||||
// location URI is not subject to the rewrite, this function returns
|
||||
// emtpy string.
|
||||
// empty string.
|
||||
StringRef rewrite_location_uri(BlockAllocator &balloc, const StringRef &uri,
|
||||
const http_parser_url &u,
|
||||
const StringRef &match_host,
|
||||
@@ -286,6 +316,7 @@ enum {
|
||||
HD_KEEP_ALIVE,
|
||||
HD_LINK,
|
||||
HD_LOCATION,
|
||||
HD_NGHTTPX_0RTT_UNIQ,
|
||||
HD_PROXY_CONNECTION,
|
||||
HD_SERVER,
|
||||
HD_TE,
|
||||
|
||||
@@ -150,11 +150,33 @@ auto headers = HeaderRefs{
|
||||
{StringRef::from_lit("zulu"), StringRef::from_lit("12")}};
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
auto headers2 = HeaderRefs{
|
||||
{StringRef::from_lit("x-forwarded-for"), StringRef::from_lit("xff1"), false,
|
||||
http2::HD_X_FORWARDED_FOR},
|
||||
{StringRef::from_lit("x-forwarded-for"), StringRef::from_lit("xff2"), false,
|
||||
http2::HD_X_FORWARDED_FOR},
|
||||
{StringRef::from_lit("x-forwarded-proto"), StringRef::from_lit("xfp1"),
|
||||
false, http2::HD_X_FORWARDED_PROTO},
|
||||
{StringRef::from_lit("x-forwarded-proto"), StringRef::from_lit("xfp2"),
|
||||
false, http2::HD_X_FORWARDED_PROTO},
|
||||
{StringRef::from_lit("forwarded"), StringRef::from_lit("fwd1"), false,
|
||||
http2::HD_FORWARDED},
|
||||
{StringRef::from_lit("forwarded"), StringRef::from_lit("fwd2"), false,
|
||||
http2::HD_FORWARDED},
|
||||
{StringRef::from_lit("via"), StringRef::from_lit("via1"), false,
|
||||
http2::HD_VIA},
|
||||
{StringRef::from_lit("via"), StringRef::from_lit("via2"), false,
|
||||
http2::HD_VIA},
|
||||
};
|
||||
} // namespace
|
||||
|
||||
void test_http2_copy_headers_to_nva(void) {
|
||||
auto ans = std::vector<int>{0, 1, 4, 5, 6, 7, 12};
|
||||
std::vector<nghttp2_nv> nva;
|
||||
|
||||
http2::copy_headers_to_nva_nocopy(nva, headers);
|
||||
http2::copy_headers_to_nva_nocopy(nva, headers,
|
||||
http2::HDOP_STRIP_X_FORWARDED_FOR);
|
||||
CU_ASSERT(7 == nva.size());
|
||||
for (size_t i = 0; i < ans.size(); ++i) {
|
||||
check_nv(headers[ans[i]], &nva[i]);
|
||||
@@ -169,7 +191,7 @@ void test_http2_copy_headers_to_nva(void) {
|
||||
}
|
||||
|
||||
nva.clear();
|
||||
http2::copy_headers_to_nva(nva, headers);
|
||||
http2::copy_headers_to_nva(nva, headers, http2::HDOP_STRIP_X_FORWARDED_FOR);
|
||||
CU_ASSERT(7 == nva.size());
|
||||
for (size_t i = 0; i < ans.size(); ++i) {
|
||||
check_nv(headers[ans[i]], &nva[i]);
|
||||
@@ -180,12 +202,27 @@ void test_http2_copy_headers_to_nva(void) {
|
||||
CU_ASSERT(NGHTTP2_NV_FLAG_NONE == nva[i].flags);
|
||||
}
|
||||
}
|
||||
|
||||
nva.clear();
|
||||
|
||||
auto ans2 = std::vector<int>{0, 2, 4, 6};
|
||||
http2::copy_headers_to_nva(nva, headers2, http2::HDOP_NONE);
|
||||
CU_ASSERT(ans2.size() == nva.size());
|
||||
for (size_t i = 0; i < ans2.size(); ++i) {
|
||||
check_nv(headers2[ans2[i]], &nva[i]);
|
||||
}
|
||||
|
||||
nva.clear();
|
||||
|
||||
http2::copy_headers_to_nva(nva, headers2, http2::HDOP_STRIP_ALL);
|
||||
CU_ASSERT(nva.empty());
|
||||
}
|
||||
|
||||
void test_http2_build_http1_headers_from_headers(void) {
|
||||
MemchunkPool pool;
|
||||
DefaultMemchunks buf(&pool);
|
||||
http2::build_http1_headers_from_headers(&buf, headers);
|
||||
http2::build_http1_headers_from_headers(&buf, headers,
|
||||
http2::HDOP_STRIP_X_FORWARDED_FOR);
|
||||
auto hdrs = std::string(buf.head->pos, buf.head->last);
|
||||
CU_ASSERT("Alpha: 0\r\n"
|
||||
"Bravo: 1\r\n"
|
||||
@@ -196,6 +233,21 @@ void test_http2_build_http1_headers_from_headers(void) {
|
||||
"Te: 8\r\n"
|
||||
"Te: 9\r\n"
|
||||
"Zulu: 12\r\n" == hdrs);
|
||||
|
||||
buf.reset();
|
||||
|
||||
http2::build_http1_headers_from_headers(&buf, headers2, http2::HDOP_NONE);
|
||||
hdrs = std::string(buf.head->pos, buf.head->last);
|
||||
CU_ASSERT("X-Forwarded-For: xff1\r\n"
|
||||
"X-Forwarded-Proto: xfp1\r\n"
|
||||
"Forwarded: fwd1\r\n"
|
||||
"Via: via1\r\n" == hdrs);
|
||||
|
||||
buf.reset();
|
||||
|
||||
http2::build_http1_headers_from_headers(&buf, headers2,
|
||||
http2::HDOP_STRIP_ALL);
|
||||
CU_ASSERT(0 == buf.rleft());
|
||||
}
|
||||
|
||||
void test_http2_lws(void) {
|
||||
|
||||
@@ -118,6 +118,28 @@ private:
|
||||
std::unique_ptr<request_impl> impl_;
|
||||
};
|
||||
|
||||
// Wrapper around an nghttp2_priority_spec.
|
||||
class priority_spec {
|
||||
public:
|
||||
// The default ctor is used only by sentinel values.
|
||||
priority_spec() = default;
|
||||
|
||||
// Create a priority spec with the given priority settings.
|
||||
explicit priority_spec(const int32_t stream_id, const int32_t weight,
|
||||
const bool exclusive = false);
|
||||
|
||||
// Return a pointer to a valid nghttp2 priority spec, or null.
|
||||
const nghttp2_priority_spec *get() const;
|
||||
|
||||
// Indicates whether or not this spec is valid (i.e. was constructed with
|
||||
// values).
|
||||
const bool valid() const;
|
||||
|
||||
private:
|
||||
nghttp2_priority_spec spec_;
|
||||
bool valid_ = false;
|
||||
};
|
||||
|
||||
class session_impl;
|
||||
|
||||
class session {
|
||||
@@ -177,7 +199,8 @@ public:
|
||||
// succeeds, or nullptr and |ec| contains error message.
|
||||
const request *submit(boost::system::error_code &ec,
|
||||
const std::string &method, const std::string &uri,
|
||||
header_map h = header_map{}) const;
|
||||
header_map h = header_map{},
|
||||
priority_spec prio = priority_spec()) const;
|
||||
|
||||
// Submits request to server using |method| (e.g., "GET"), |uri|
|
||||
// (e.g., "http://localhost/") and optionally additional header
|
||||
@@ -186,7 +209,8 @@ public:
|
||||
// contains error message.
|
||||
const request *submit(boost::system::error_code &ec,
|
||||
const std::string &method, const std::string &uri,
|
||||
std::string data, header_map h = header_map{}) const;
|
||||
std::string data, header_map h = header_map{},
|
||||
priority_spec prio = priority_spec()) const;
|
||||
|
||||
// Submits request to server using |method| (e.g., "GET"), |uri|
|
||||
// (e.g., "http://localhost/") and optionally additional header
|
||||
@@ -195,7 +219,8 @@ public:
|
||||
// nullptr and |ec| contains error message.
|
||||
const request *submit(boost::system::error_code &ec,
|
||||
const std::string &method, const std::string &uri,
|
||||
generator_cb cb, header_map h = header_map{}) const;
|
||||
generator_cb cb, header_map h = header_map{},
|
||||
priority_spec prio = priority_spec()) const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<session_impl> impl_;
|
||||
|
||||
@@ -48,7 +48,9 @@
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
typedef struct { int dump_header_table; } inflate_config;
|
||||
typedef struct {
|
||||
int dump_header_table;
|
||||
} inflate_config;
|
||||
|
||||
static inflate_config config;
|
||||
|
||||
|
||||
@@ -28,7 +28,15 @@
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <limits.h>
|
||||
#ifdef _WIN32
|
||||
/* Structure for scatter/gather I/O. */
|
||||
struct iovec {
|
||||
void *iov_base; /* Pointer to data. */
|
||||
size_t iov_len; /* Length of data. */
|
||||
};
|
||||
#else // !_WIN32
|
||||
#include <sys/uio.h>
|
||||
#endif // !_WIN32
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
@@ -50,23 +58,21 @@ namespace nghttp2 {
|
||||
#endif // !defined(IOV_MAX) || IOV_MAX >= DEFAULT_WR_IOVCNT
|
||||
|
||||
template <size_t N> struct Memchunk {
|
||||
Memchunk(std::unique_ptr<Memchunk> next_chunk)
|
||||
: pos(std::begin(buf)),
|
||||
last(pos),
|
||||
knext(std::move(next_chunk)),
|
||||
next(nullptr) {}
|
||||
Memchunk(Memchunk *next_chunk)
|
||||
: pos(std::begin(buf)), last(pos), knext(next_chunk), next(nullptr) {}
|
||||
size_t len() const { return last - pos; }
|
||||
size_t left() const { return std::end(buf) - last; }
|
||||
void reset() { pos = last = std::begin(buf); }
|
||||
std::array<uint8_t, N> buf;
|
||||
uint8_t *pos, *last;
|
||||
std::unique_ptr<Memchunk> knext;
|
||||
Memchunk *knext;
|
||||
Memchunk *next;
|
||||
static const size_t size = N;
|
||||
};
|
||||
|
||||
template <typename T> struct Pool {
|
||||
Pool() : pool(nullptr), freelist(nullptr), poolsize(0) {}
|
||||
~Pool() { clear(); }
|
||||
T *get() {
|
||||
if (freelist) {
|
||||
auto m = freelist;
|
||||
@@ -76,9 +82,9 @@ template <typename T> struct Pool {
|
||||
return m;
|
||||
}
|
||||
|
||||
pool = make_unique<T>(std::move(pool));
|
||||
pool = new T{pool};
|
||||
poolsize += T::size;
|
||||
return pool.get();
|
||||
return pool;
|
||||
}
|
||||
void recycle(T *m) {
|
||||
m->next = freelist;
|
||||
@@ -86,11 +92,16 @@ template <typename T> struct Pool {
|
||||
}
|
||||
void clear() {
|
||||
freelist = nullptr;
|
||||
for (auto p = pool; p;) {
|
||||
auto knext = p->knext;
|
||||
delete p;
|
||||
p = knext;
|
||||
}
|
||||
pool = nullptr;
|
||||
poolsize = 0;
|
||||
}
|
||||
using value_type = T;
|
||||
std::unique_ptr<T> pool;
|
||||
T *pool;
|
||||
T *freelist;
|
||||
size_t poolsize;
|
||||
};
|
||||
|
||||
@@ -42,34 +42,34 @@ void test_pool_recycle(void) {
|
||||
|
||||
auto m1 = pool.get();
|
||||
|
||||
CU_ASSERT(m1 == pool.pool.get());
|
||||
CU_ASSERT(m1 == pool.pool);
|
||||
CU_ASSERT(MemchunkPool::value_type::size == pool.poolsize);
|
||||
CU_ASSERT(nullptr == pool.freelist);
|
||||
|
||||
auto m2 = pool.get();
|
||||
|
||||
CU_ASSERT(m2 == pool.pool.get());
|
||||
CU_ASSERT(m2 == pool.pool);
|
||||
CU_ASSERT(2 * MemchunkPool::value_type::size == pool.poolsize);
|
||||
CU_ASSERT(nullptr == pool.freelist);
|
||||
CU_ASSERT(m1 == m2->knext.get());
|
||||
CU_ASSERT(nullptr == m1->knext.get());
|
||||
CU_ASSERT(m1 == m2->knext);
|
||||
CU_ASSERT(nullptr == m1->knext);
|
||||
|
||||
auto m3 = pool.get();
|
||||
|
||||
CU_ASSERT(m3 == pool.pool.get());
|
||||
CU_ASSERT(m3 == pool.pool);
|
||||
CU_ASSERT(3 * MemchunkPool::value_type::size == pool.poolsize);
|
||||
CU_ASSERT(nullptr == pool.freelist);
|
||||
|
||||
pool.recycle(m3);
|
||||
|
||||
CU_ASSERT(m3 == pool.pool.get());
|
||||
CU_ASSERT(m3 == pool.pool);
|
||||
CU_ASSERT(3 * MemchunkPool::value_type::size == pool.poolsize);
|
||||
CU_ASSERT(m3 == pool.freelist);
|
||||
|
||||
auto m4 = pool.get();
|
||||
|
||||
CU_ASSERT(m3 == m4);
|
||||
CU_ASSERT(m4 == pool.pool.get());
|
||||
CU_ASSERT(m4 == pool.pool);
|
||||
CU_ASSERT(3 * MemchunkPool::value_type::size == pool.poolsize);
|
||||
CU_ASSERT(nullptr == pool.freelist);
|
||||
|
||||
|
||||
@@ -33,7 +33,11 @@
|
||||
#ifdef HAVE_SYS_SOCKET_H
|
||||
#include <sys/socket.h>
|
||||
#endif // HAVE_SYS_SOCKET_H
|
||||
#ifdef _WIN32
|
||||
#include <ws2tcpip.h>
|
||||
#else // !_WIN32
|
||||
#include <sys/un.h>
|
||||
#endif // !_WIN32
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
#include <netinet/in.h>
|
||||
#endif // HAVE_NETINET_IN_H
|
||||
@@ -48,7 +52,9 @@ union sockaddr_union {
|
||||
sockaddr sa;
|
||||
sockaddr_in6 in6;
|
||||
sockaddr_in in;
|
||||
#ifndef _WIN32
|
||||
sockaddr_un un;
|
||||
#endif // !_WIN32
|
||||
};
|
||||
|
||||
struct Address {
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
#include "HtmlParser.h"
|
||||
#include "util.h"
|
||||
#include "base64.h"
|
||||
#include "ssl.h"
|
||||
#include "tls.h"
|
||||
#include "template.h"
|
||||
|
||||
#ifndef O_BINARY
|
||||
@@ -89,7 +89,11 @@ enum {
|
||||
|
||||
namespace {
|
||||
constexpr auto anchors = std::array<Anchor, 5>{{
|
||||
{3, 0, 201}, {5, 0, 101}, {7, 0, 1}, {9, 7, 1}, {11, 3, 1},
|
||||
{3, 0, 201},
|
||||
{5, 0, 101},
|
||||
{7, 0, 1},
|
||||
{9, 7, 1},
|
||||
{11, 3, 1},
|
||||
}};
|
||||
} // namespace
|
||||
|
||||
@@ -116,7 +120,8 @@ Config::Config()
|
||||
no_dep(false),
|
||||
hexdump(false),
|
||||
no_push(false),
|
||||
expect_continue(false) {
|
||||
expect_continue(false),
|
||||
verify_peer(true) {
|
||||
nghttp2_option_new(&http2_option);
|
||||
nghttp2_option_set_peer_max_concurrent_streams(http2_option,
|
||||
peer_max_concurrent_streams);
|
||||
@@ -171,6 +176,8 @@ Request::~Request() { nghttp2_gzip_inflate_del(inflater); }
|
||||
|
||||
void Request::init_inflater() {
|
||||
int rv;
|
||||
// This is required with --disable-assert.
|
||||
(void)rv;
|
||||
rv = nghttp2_gzip_inflate_new(&inflater);
|
||||
assert(rv == 0);
|
||||
}
|
||||
@@ -401,17 +408,10 @@ int htp_msg_begincb(http_parser *htp) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int htp_statuscb(http_parser *htp, const char *at, size_t length) {
|
||||
auto client = static_cast<HttpClient *>(htp->data);
|
||||
client->upgrade_response_status_code = htp->status_code;
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int htp_msg_completecb(http_parser *htp) {
|
||||
auto client = static_cast<HttpClient *>(htp->data);
|
||||
client->upgrade_response_status_code = htp->status_code;
|
||||
client->upgrade_response_complete = true;
|
||||
return 0;
|
||||
}
|
||||
@@ -421,7 +421,7 @@ namespace {
|
||||
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;
|
||||
nullptr, // http_data_cb on_status;
|
||||
nullptr, // http_data_cb on_header_field;
|
||||
nullptr, // http_data_cb on_header_value;
|
||||
nullptr, // http_cb on_headers_complete;
|
||||
@@ -646,6 +646,11 @@ int HttpClient::resolve_host(const std::string &host, uint16_t port) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Just returns 1 to continue handshake.
|
||||
int verify_cb(int preverify_ok, X509_STORE_CTX *ctx) { return 1; }
|
||||
} // namespace
|
||||
|
||||
int HttpClient::initiate_connection() {
|
||||
int rv;
|
||||
|
||||
@@ -675,6 +680,17 @@ int HttpClient::initiate_connection() {
|
||||
const auto &host_string =
|
||||
config.host_override.empty() ? host : config.host_override;
|
||||
|
||||
#if (!defined(LIBRESSL_VERSION_NUMBER) && \
|
||||
OPENSSL_VERSION_NUMBER >= 0x10002000L) || \
|
||||
defined(OPENSSL_IS_BORINGSSL)
|
||||
auto param = SSL_get0_param(ssl);
|
||||
X509_VERIFY_PARAM_set_hostflags(param, 0);
|
||||
X509_VERIFY_PARAM_set1_host(param, host_string.c_str(),
|
||||
host_string.size());
|
||||
#endif // (!defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >=
|
||||
// 0x10002000L) || defined(OPENSSL_IS_BORINGSSL)
|
||||
SSL_set_verify(ssl, SSL_VERIFY_PEER, verify_cb);
|
||||
|
||||
if (!util::numeric_host(host_string.c_str())) {
|
||||
SSL_set_tlsext_host_name(ssl, host_string.c_str());
|
||||
}
|
||||
@@ -1295,6 +1311,14 @@ int HttpClient::tls_handshake() {
|
||||
readfn = &HttpClient::read_tls;
|
||||
writefn = &HttpClient::write_tls;
|
||||
|
||||
if (config.verify_peer) {
|
||||
auto verify_res = SSL_get_verify_result(ssl);
|
||||
if (verify_res != X509_V_OK) {
|
||||
std::cerr << "[WARNING] Certificate verification failed: "
|
||||
<< X509_verify_cert_error_string(verify_res) << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
if (connection_made() != 0) {
|
||||
return -1;
|
||||
}
|
||||
@@ -2247,15 +2271,20 @@ int communicate(
|
||||
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
|
||||
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
|
||||
|
||||
if (nghttp2::ssl::ssl_ctx_set_proto_versions(
|
||||
ssl_ctx, nghttp2::ssl::NGHTTP2_TLS_MIN_VERSION,
|
||||
nghttp2::ssl::NGHTTP2_TLS_MAX_VERSION) != 0) {
|
||||
if (SSL_CTX_set_default_verify_paths(ssl_ctx) != 1) {
|
||||
std::cerr << "[WARNING] Could not load system trusted CA certificates: "
|
||||
<< ERR_error_string(ERR_get_error(), nullptr) << std::endl;
|
||||
}
|
||||
|
||||
if (nghttp2::tls::ssl_ctx_set_proto_versions(
|
||||
ssl_ctx, nghttp2::tls::NGHTTP2_TLS_MIN_VERSION,
|
||||
nghttp2::tls::NGHTTP2_TLS_MAX_VERSION) != 0) {
|
||||
std::cerr << "[ERROR] Could not set TLS versions" << std::endl;
|
||||
result = -1;
|
||||
goto fin;
|
||||
}
|
||||
|
||||
if (SSL_CTX_set_cipher_list(ssl_ctx, ssl::DEFAULT_CIPHER_LIST) == 0) {
|
||||
if (SSL_CTX_set_cipher_list(ssl_ctx, tls::DEFAULT_CIPHER_LIST) == 0) {
|
||||
std::cerr << "[ERROR] " << ERR_error_string(ERR_get_error(), nullptr)
|
||||
<< std::endl;
|
||||
result = -1;
|
||||
@@ -2433,8 +2462,8 @@ int run(char **uris, int n) {
|
||||
nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(
|
||||
callbacks, verbose_on_invalid_frame_recv_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_error_callback(callbacks,
|
||||
verbose_error_callback);
|
||||
nghttp2_session_callbacks_set_error_callback2(callbacks,
|
||||
verbose_error_callback);
|
||||
}
|
||||
|
||||
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
|
||||
@@ -2701,6 +2730,9 @@ Options:
|
||||
(up to a short timeout) until the server sends a 100
|
||||
Continue interim response. This option is ignored unless
|
||||
combined with the -d option.
|
||||
-y, --no-verify-peer
|
||||
Suppress warning on server certificate verification
|
||||
failure.
|
||||
--version Display version information and exit.
|
||||
-h, --help Display this help and exit.
|
||||
|
||||
@@ -2718,7 +2750,7 @@ Options:
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
ssl::libssl_init();
|
||||
tls::libssl_init();
|
||||
|
||||
bool color = false;
|
||||
while (1) {
|
||||
@@ -2742,6 +2774,7 @@ int main(int argc, char **argv) {
|
||||
{"header-table-size", required_argument, nullptr, 'c'},
|
||||
{"padding", required_argument, nullptr, 'b'},
|
||||
{"har", required_argument, nullptr, 'r'},
|
||||
{"no-verify-peer", no_argument, nullptr, 'y'},
|
||||
{"cert", required_argument, &flag, 1},
|
||||
{"key", required_argument, &flag, 2},
|
||||
{"color", no_argument, &flag, 3},
|
||||
@@ -2757,8 +2790,9 @@ int main(int argc, char **argv) {
|
||||
{"encoder-header-table-size", required_argument, &flag, 14},
|
||||
{nullptr, 0, nullptr, 0}};
|
||||
int option_index = 0;
|
||||
int c = getopt_long(argc, argv, "M:Oab:c:d:gm:np:r:hH:vst:uw:W:",
|
||||
long_options, &option_index);
|
||||
int c =
|
||||
getopt_long(argc, argv, "M:Oab:c:d:m:np:r:hH:vst:uw:yW:", long_options,
|
||||
&option_index);
|
||||
if (c == -1) {
|
||||
break;
|
||||
}
|
||||
@@ -2888,6 +2922,9 @@ int main(int argc, char **argv) {
|
||||
config.min_header_table_size = std::min(config.min_header_table_size, n);
|
||||
break;
|
||||
}
|
||||
case 'y':
|
||||
config.verify_peer = false;
|
||||
break;
|
||||
case '?':
|
||||
util::show_candidates(argv[optind - 1], long_options);
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
@@ -96,6 +96,7 @@ struct Config {
|
||||
bool hexdump;
|
||||
bool no_push;
|
||||
bool expect_continue;
|
||||
bool verify_peer;
|
||||
};
|
||||
|
||||
enum class RequestState { INITIAL, ON_REQUEST, ON_RESPONSE, ON_COMPLETE };
|
||||
|
||||
@@ -43,14 +43,14 @@ static size_t deflate_data(uint8_t *out, size_t outlen, const uint8_t *in,
|
||||
zst.opaque = Z_NULL;
|
||||
|
||||
rv = deflateInit(&zst, Z_DEFAULT_COMPRESSION);
|
||||
assert(rv == Z_OK);
|
||||
CU_ASSERT(rv == Z_OK);
|
||||
|
||||
zst.avail_in = (unsigned int)inlen;
|
||||
zst.next_in = (uint8_t *)in;
|
||||
zst.avail_out = (unsigned int)outlen;
|
||||
zst.next_out = out;
|
||||
rv = deflate(&zst, Z_SYNC_FLUSH);
|
||||
assert(rv == Z_OK);
|
||||
CU_ASSERT(rv == Z_OK);
|
||||
|
||||
deflateEnd(&zst);
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
#include "app_helper.h"
|
||||
#include "HttpServer.h"
|
||||
#include "util.h"
|
||||
#include "ssl.h"
|
||||
#include "tls.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
@@ -174,8 +174,7 @@ Options:
|
||||
--mime-types-file=<PATH>
|
||||
Path to file that contains MIME media types and the
|
||||
extensions that represent them.
|
||||
Default: )"
|
||||
<< config.mime_types_file << R"(
|
||||
Default: )" << config.mime_types_file << R"(
|
||||
--no-content-length
|
||||
Don't send content-length header field.
|
||||
--version Display version information and exit.
|
||||
@@ -190,10 +189,10 @@ Options:
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
ssl::libssl_init();
|
||||
tls::libssl_init();
|
||||
|
||||
#ifndef NOTHREADS
|
||||
ssl::LibsslGlobalLock lock;
|
||||
tls::LibsslGlobalLock lock;
|
||||
#endif // NOTHREADS
|
||||
|
||||
Config config;
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
#include <string.h>
|
||||
#include <CUnit/Basic.h>
|
||||
// include test cases' include files here
|
||||
#include "shrpx_ssl_test.h"
|
||||
#include "shrpx_tls_test.h"
|
||||
#include "shrpx_downstream_test.h"
|
||||
#include "shrpx_config_test.h"
|
||||
#include "shrpx_worker_test.h"
|
||||
@@ -43,7 +43,7 @@
|
||||
#include "shrpx_http_test.h"
|
||||
#include "base64_test.h"
|
||||
#include "shrpx_config.h"
|
||||
#include "ssl.h"
|
||||
#include "tls.h"
|
||||
#include "shrpx_router_test.h"
|
||||
#include "shrpx_log.h"
|
||||
|
||||
@@ -55,7 +55,7 @@ int main(int argc, char *argv[]) {
|
||||
CU_pSuite pSuite = NULL;
|
||||
unsigned int num_tests_failed;
|
||||
|
||||
nghttp2::ssl::libssl_init();
|
||||
nghttp2::tls::libssl_init();
|
||||
|
||||
shrpx::create_config();
|
||||
|
||||
@@ -71,12 +71,12 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
// add the tests to the suite
|
||||
if (!CU_add_test(pSuite, "ssl_create_lookup_tree",
|
||||
shrpx::test_shrpx_ssl_create_lookup_tree) ||
|
||||
!CU_add_test(pSuite, "ssl_cert_lookup_tree_add_ssl_ctx",
|
||||
shrpx::test_shrpx_ssl_cert_lookup_tree_add_ssl_ctx) ||
|
||||
!CU_add_test(pSuite, "ssl_tls_hostname_match",
|
||||
shrpx::test_shrpx_ssl_tls_hostname_match) ||
|
||||
if (!CU_add_test(pSuite, "tls_create_lookup_tree",
|
||||
shrpx::test_shrpx_tls_create_lookup_tree) ||
|
||||
!CU_add_test(pSuite, "tls_cert_lookup_tree_add_ssl_ctx",
|
||||
shrpx::test_shrpx_tls_cert_lookup_tree_add_ssl_ctx) ||
|
||||
!CU_add_test(pSuite, "tls_tls_hostname_match",
|
||||
shrpx::test_shrpx_tls_tls_hostname_match) ||
|
||||
!CU_add_test(pSuite, "http2_add_header", shrpx::test_http2_add_header) ||
|
||||
!CU_add_test(pSuite, "http2_get_header", shrpx::test_http2_get_header) ||
|
||||
!CU_add_test(pSuite, "http2_copy_headers_to_nva",
|
||||
@@ -115,6 +115,10 @@ int main(int argc, char *argv[]) {
|
||||
shrpx::test_downstream_assemble_request_cookie) ||
|
||||
!CU_add_test(pSuite, "downstream_rewrite_location_response_header",
|
||||
shrpx::test_downstream_rewrite_location_response_header) ||
|
||||
!CU_add_test(pSuite, "downstream_supports_non_final_response",
|
||||
shrpx::test_downstream_supports_non_final_response) ||
|
||||
!CU_add_test(pSuite, "downstream_find_affinity_cookie",
|
||||
shrpx::test_downstream_find_affinity_cookie) ||
|
||||
!CU_add_test(pSuite, "config_parse_header",
|
||||
shrpx::test_shrpx_config_parse_header) ||
|
||||
!CU_add_test(pSuite, "config_parse_log_format",
|
||||
@@ -129,7 +133,11 @@ int main(int argc, char *argv[]) {
|
||||
shrpx::test_shrpx_http_create_forwarded) ||
|
||||
!CU_add_test(pSuite, "http_create_via_header_value",
|
||||
shrpx::test_shrpx_http_create_via_header_value) ||
|
||||
!CU_add_test(pSuite, "http_create_affinity_cookie",
|
||||
shrpx::test_shrpx_http_create_affinity_cookie) ||
|
||||
!CU_add_test(pSuite, "router_match", shrpx::test_shrpx_router_match) ||
|
||||
!CU_add_test(pSuite, "router_match_wildcard",
|
||||
shrpx::test_shrpx_router_match_wildcard) ||
|
||||
!CU_add_test(pSuite, "router_match_prefix",
|
||||
shrpx::test_shrpx_router_match_prefix) ||
|
||||
!CU_add_test(pSuite, "util_streq", shrpx::test_util_streq) ||
|
||||
|
||||
370
src/shrpx.cc
370
src/shrpx.cc
@@ -76,7 +76,7 @@
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
#include "shrpx_config.h"
|
||||
#include "shrpx_ssl.h"
|
||||
#include "shrpx_tls.h"
|
||||
#include "shrpx_log_config.h"
|
||||
#include "shrpx_worker.h"
|
||||
#include "shrpx_http2_upstream.h"
|
||||
@@ -88,7 +88,7 @@
|
||||
#include "shrpx_log.h"
|
||||
#include "util.h"
|
||||
#include "app_helper.h"
|
||||
#include "ssl.h"
|
||||
#include "tls.h"
|
||||
#include "template.h"
|
||||
#include "allocator.h"
|
||||
#include "ssl_compat.h"
|
||||
@@ -1205,13 +1205,17 @@ pid_t fork_worker_process(int &main_ipc_fd,
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto pid = fork();
|
||||
auto config = get_config();
|
||||
|
||||
pid_t pid = 0;
|
||||
|
||||
if (!config->single_process) {
|
||||
pid = fork();
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
ev_loop_fork(EV_DEFAULT);
|
||||
|
||||
auto config = get_config();
|
||||
|
||||
for (auto &addr : config->conn.listener.addrs) {
|
||||
util::make_socket_closeonexec(addr.fd);
|
||||
}
|
||||
@@ -1230,22 +1234,37 @@ pid_t fork_worker_process(int &main_ipc_fd,
|
||||
LOG(FATAL) << "Unblocking all signals failed: "
|
||||
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
||||
|
||||
nghttp2_Exit(EXIT_FAILURE);
|
||||
if (config->single_process) {
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
nghttp2_Exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
if (!config->single_process) {
|
||||
close(ipc_fd[1]);
|
||||
}
|
||||
|
||||
close(ipc_fd[1]);
|
||||
WorkerProcessConfig wpconf{ipc_fd[0]};
|
||||
rv = worker_process_event_loop(&wpconf);
|
||||
if (rv != 0) {
|
||||
LOG(FATAL) << "Worker process returned error";
|
||||
|
||||
nghttp2_Exit(EXIT_FAILURE);
|
||||
if (config->single_process) {
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
nghttp2_Exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
LOG(NOTICE) << "Worker process shutting down momentarily";
|
||||
|
||||
// call exit(...) instead of nghttp2_Exit to get leak sanitizer report
|
||||
nghttp2_Exit(EXIT_SUCCESS);
|
||||
if (config->single_process) {
|
||||
exit(EXIT_SUCCESS);
|
||||
} else {
|
||||
nghttp2_Exit(EXIT_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
// parent process
|
||||
@@ -1322,7 +1341,7 @@ int event_loop() {
|
||||
|
||||
auto loop = ev_default_loop(config->ev_loop_flags);
|
||||
|
||||
int ipc_fd;
|
||||
int ipc_fd = 0;
|
||||
|
||||
auto pid = fork_worker_process(ipc_fd, {});
|
||||
|
||||
@@ -1373,7 +1392,7 @@ constexpr auto DEFAULT_NPN_LIST = StringRef::from_lit("h2,h2-16,h2-14,"
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
constexpr auto DEFAULT_TLS_MIN_PROTO_VERSION = StringRef::from_lit("TLSv1.1");
|
||||
constexpr auto DEFAULT_TLS_MIN_PROTO_VERSION = StringRef::from_lit("TLSv1.2");
|
||||
#ifdef TLS1_3_VERSION
|
||||
constexpr auto DEFAULT_TLS_MAX_PROTO_VERSION = StringRef::from_lit("TLSv1.3");
|
||||
#else // !TLS1_3_VERSION
|
||||
@@ -1419,6 +1438,12 @@ void fill_default_config(Config *config) {
|
||||
memcachedconf.family = AF_UNSPEC;
|
||||
}
|
||||
|
||||
auto &anti_replayconf = tlsconf.anti_replay;
|
||||
{
|
||||
auto &memcachedconf = anti_replayconf.memcached;
|
||||
memcachedconf.family = AF_UNSPEC;
|
||||
}
|
||||
|
||||
ticketconf.cipher = EVP_aes_128_cbc();
|
||||
}
|
||||
|
||||
@@ -1437,13 +1462,13 @@ void fill_default_config(Config *config) {
|
||||
}
|
||||
|
||||
tlsconf.session_timeout = std::chrono::hours(12);
|
||||
tlsconf.ciphers = StringRef::from_lit(nghttp2::ssl::DEFAULT_CIPHER_LIST);
|
||||
tlsconf.ciphers = StringRef::from_lit(nghttp2::tls::DEFAULT_CIPHER_LIST);
|
||||
tlsconf.client.ciphers =
|
||||
StringRef::from_lit(nghttp2::ssl::DEFAULT_CIPHER_LIST);
|
||||
StringRef::from_lit(nghttp2::tls::DEFAULT_CIPHER_LIST);
|
||||
tlsconf.min_proto_version =
|
||||
ssl::proto_version_from_string(DEFAULT_TLS_MIN_PROTO_VERSION);
|
||||
tls::proto_version_from_string(DEFAULT_TLS_MIN_PROTO_VERSION);
|
||||
tlsconf.max_proto_version =
|
||||
ssl::proto_version_from_string(DEFAULT_TLS_MAX_PROTO_VERSION);
|
||||
tls::proto_version_from_string(DEFAULT_TLS_MAX_PROTO_VERSION);
|
||||
#if OPENSSL_1_1_API || defined(OPENSSL_IS_BORINGSSL)
|
||||
tlsconf.ecdh_curves = StringRef::from_lit("X25519:P-256:P-384:P-521");
|
||||
#else // !OPENSSL_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
|
||||
@@ -1459,6 +1484,9 @@ void fill_default_config(Config *config) {
|
||||
httpconf.max_response_header_fields = 500;
|
||||
httpconf.redirect_https_port = StringRef::from_lit("443");
|
||||
httpconf.max_requests = std::numeric_limits<size_t>::max();
|
||||
httpconf.xfp.add = true;
|
||||
httpconf.xfp.strip_incoming = true;
|
||||
httpconf.zero_rtt_uniq.strip_incoming = true;
|
||||
|
||||
auto &http2conf = config->http2;
|
||||
{
|
||||
@@ -1644,14 +1672,16 @@ Connections:
|
||||
which only lacks trailing '/' (e.g., path "/foo/"
|
||||
matches request path "/foo"). If it does not end with
|
||||
"/", it performs exact match against the request path.
|
||||
If host is given, it performs exact match against the
|
||||
request host. If host alone is given, "/" is appended
|
||||
to it, so that it matches all request paths under the
|
||||
host (e.g., specifying "nghttp2.org" equals to
|
||||
"nghttp2.org/"). CONNECT method is treated specially.
|
||||
It does not have path, and we don't allow empty path.
|
||||
To workaround this, we assume that CONNECT method has
|
||||
"/" as path.
|
||||
If host is given, it performs a match against the
|
||||
request host. For a request received on the frontend
|
||||
listener with "sni-fwd" parameter enabled, SNI host is
|
||||
used instead of a request host. If host alone is given,
|
||||
"/" is appended to it, so that it matches all request
|
||||
paths under the host (e.g., specifying "nghttp2.org"
|
||||
equals to "nghttp2.org/"). CONNECT method is treated
|
||||
specially. It does not have path, and we don't allow
|
||||
empty path. To workaround this, we assume that CONNECT
|
||||
method has "/" as path.
|
||||
|
||||
Patterns with host take precedence over patterns with
|
||||
just path. Then, longer patterns take precedence over
|
||||
@@ -1665,6 +1695,18 @@ Connections:
|
||||
match against "nghttp2.org". The exact hosts match
|
||||
takes precedence over the wildcard hosts match.
|
||||
|
||||
If path part ends with "*", it is treated as wildcard
|
||||
path. The wildcard path behaves differently from the
|
||||
normal path. For normal path, match is made around the
|
||||
boundary of path component separator,"/". On the other
|
||||
hand, the wildcard path does not take into account the
|
||||
path component separator. All paths which include the
|
||||
wildcard path without last "*" as prefix, and are
|
||||
strictly longer than wildcard path without last "*" are
|
||||
matched. "*" must match at least one character. For
|
||||
example, the pattern "/foo*" matches "/foo/" and
|
||||
"/foobar". But it does not match "/foo", or "/fo".
|
||||
|
||||
If <PATTERN> is omitted or empty string, "/" is used as
|
||||
pattern, which matches all request paths (catch-all
|
||||
pattern). The catch-all backend must be given.
|
||||
@@ -1737,16 +1779,32 @@ Connections:
|
||||
The session affinity is enabled using
|
||||
"affinity=<METHOD>" parameter. If "ip" is given in
|
||||
<METHOD>, client IP based session affinity is enabled.
|
||||
If "none" is given in <METHOD>, session affinity is
|
||||
disabled, and this is the default. The session affinity
|
||||
is enabled per <PATTERN>. If at least one backend has
|
||||
"affinity" parameter, and its <METHOD> is not "none",
|
||||
session affinity is enabled for all backend servers
|
||||
sharing the same <PATTERN>. It is advised to set
|
||||
"affinity" parameter to all backend explicitly if
|
||||
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.
|
||||
If "cookie" is given in <METHOD>, cookie based session
|
||||
affinity is enabled. If "none" is given in <METHOD>,
|
||||
session affinity is disabled, and this is the default.
|
||||
The session affinity is enabled per <PATTERN>. If at
|
||||
least one backend has "affinity" parameter, and its
|
||||
<METHOD> is not "none", session affinity is enabled for
|
||||
all backend servers sharing the same <PATTERN>. It is
|
||||
advised to set "affinity" parameter to all backend
|
||||
explicitly if 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.
|
||||
|
||||
If "affinity=cookie" is used, the additional
|
||||
configuration is required.
|
||||
"affinity-cookie-name=<NAME>" must be used to specify a
|
||||
name of cookie to use. Optionally,
|
||||
"affinity-cookie-path=<PATH>" can be used to specify a
|
||||
path which cookie is applied. The optional
|
||||
"affinity-cookie-secure=<SECURE>" controls the Secure
|
||||
attribute of a cookie. The default value is "auto", and
|
||||
the Secure attribute is determined by a request scheme.
|
||||
If a request scheme is "https", then Secure attribute is
|
||||
set. Otherwise, it is not set. If <SECURE> is "yes",
|
||||
the Secure attribute is always set. If <SECURE> is
|
||||
"no", the Secure attribute is always omitted.
|
||||
|
||||
By default, name resolution of backend host name is done
|
||||
at start up, or reloading configuration. If "dns"
|
||||
@@ -1790,6 +1848,11 @@ Connections:
|
||||
Optionally, TLS can be disabled by specifying "no-tls"
|
||||
parameter. TLS is enabled by default.
|
||||
|
||||
If "sni-fwd" parameter is used, when performing a match
|
||||
to select a backend server, SNI host name received from
|
||||
the client is used instead of the request host. See
|
||||
--backend option about the pattern match.
|
||||
|
||||
To make this frontend as API endpoint, specify "api"
|
||||
parameter. This is disabled by default. It is
|
||||
important to limit the access to the API frontend.
|
||||
@@ -1834,8 +1897,7 @@ Connections:
|
||||
Performance:
|
||||
-n, --workers=<N>
|
||||
Set the number of worker threads.
|
||||
Default: )"
|
||||
<< config->num_worker << R"(
|
||||
Default: )" << config->num_worker << R"(
|
||||
--single-thread
|
||||
Run everything in one thread inside the worker process.
|
||||
This feature is provided for better debugging
|
||||
@@ -2017,8 +2079,7 @@ SSL/TLS:
|
||||
--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"(
|
||||
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
|
||||
@@ -2031,11 +2092,14 @@ SSL/TLS:
|
||||
Don't verify backend server's certificate if TLS is
|
||||
enabled for backend connections.
|
||||
--cacert=<PATH>
|
||||
Set path to trusted CA certificate file used in backend
|
||||
TLS connections. The file must be in PEM format. It
|
||||
can contain multiple certificates. If the linked
|
||||
OpenSSL is configured to load system wide certificates,
|
||||
they are loaded at startup regardless of this option.
|
||||
Set path to trusted CA certificate file. It is used in
|
||||
backend TLS connections to verify peer's certificate.
|
||||
It is also used to verify OCSP response from the script
|
||||
set by --fetch-ocsp-response-file. The file must be in
|
||||
PEM format. It can contain multiple certificates. If
|
||||
the linked OpenSSL is configured to load system wide
|
||||
certificates, they are loaded at startup regardless of
|
||||
this option.
|
||||
--private-key-passwd-file=<PATH>
|
||||
Path to file that contains password for the server's
|
||||
private key. If none is given and the private key is
|
||||
@@ -2044,12 +2108,12 @@ SSL/TLS:
|
||||
Specify additional certificate and private key file.
|
||||
nghttpx will choose certificates based on the hostname
|
||||
indicated by client using TLS SNI extension. If nghttpx
|
||||
is built with OpenSSL >= 1.0.2, signature algorithms
|
||||
(e.g., ECDSA+SHA256, RSA+SHA256) presented by client are
|
||||
also taken into consideration. This allows nghttpx to
|
||||
send ECDSA certificate to modern clients, while sending
|
||||
RSA based certificate to older clients. This option can
|
||||
be used multiple times. To make OCSP stapling work,
|
||||
is built with OpenSSL >= 1.0.2, the shared elliptic
|
||||
curves (e.g., P-256) between client and server are also
|
||||
taken into consideration. This allows nghttpx to send
|
||||
ECDSA certificate to modern clients, while sending RSA
|
||||
based certificate to older clients. This option can be
|
||||
used multiple times. To make OCSP stapling work,
|
||||
<CERTPATH> must be absolute path.
|
||||
|
||||
Additional parameter can be specified in <PARAM>. The
|
||||
@@ -2071,8 +2135,8 @@ SSL/TLS:
|
||||
NPN. The parameter must be delimited by a single comma
|
||||
only and any white spaces are treated as a part of
|
||||
protocol string.
|
||||
Default: )"
|
||||
<< DEFAULT_NPN_LIST << R"(
|
||||
Default: )" << DEFAULT_NPN_LIST
|
||||
<< R"(
|
||||
--verify-client
|
||||
Require and verify client certificate.
|
||||
--verify-client-cacert=<PATH>
|
||||
@@ -2091,15 +2155,20 @@ SSL/TLS:
|
||||
--tls-min-proto-version and --tls-max-proto-version are
|
||||
enabled. If the protocol list advertised by client does
|
||||
not overlap this range, you will receive the error
|
||||
message "unknown protocol". The available versions are:
|
||||
message "unknown protocol". If a protocol version lower
|
||||
than TLSv1.2 is specified, make sure that the compatible
|
||||
ciphers are included in --ciphers option. The default
|
||||
cipher list only includes ciphers compatible with
|
||||
TLSv1.2 or above. The available versions are:
|
||||
)"
|
||||
#ifdef TLS1_3_VERSION
|
||||
"TLSv1.3, "
|
||||
"TLSv1.3, "
|
||||
#endif // TLS1_3_VERSION
|
||||
"TLSv1.2, TLSv1.1, and TLSv1.0"
|
||||
R"(
|
||||
"TLSv1.2, TLSv1.1, and TLSv1.0"
|
||||
R"(
|
||||
Default: )"
|
||||
<< DEFAULT_TLS_MIN_PROTO_VERSION << R"(
|
||||
<< DEFAULT_TLS_MIN_PROTO_VERSION
|
||||
<< R"(
|
||||
--tls-max-proto-version=<VER>
|
||||
Specify maximum SSL/TLS protocol. The name matching is
|
||||
done in case-insensitive manner. The versions between
|
||||
@@ -2109,10 +2178,10 @@ SSL/TLS:
|
||||
message "unknown protocol". The available versions are:
|
||||
)"
|
||||
#ifdef TLS1_3_VERSION
|
||||
"TLSv1.3, "
|
||||
"TLSv1.3, "
|
||||
#endif // TLS1_3_VERSION
|
||||
"TLSv1.2, TLSv1.1, and TLSv1.0"
|
||||
R"(
|
||||
"TLSv1.2, TLSv1.1, and TLSv1.0"
|
||||
R"(
|
||||
Default: )"
|
||||
<< DEFAULT_TLS_MAX_PROTO_VERSION << R"(
|
||||
--tls-ticket-key-file=<PATH>
|
||||
@@ -2194,6 +2263,14 @@ SSL/TLS:
|
||||
Set interval to update OCSP response cache.
|
||||
Default: )"
|
||||
<< util::duration_str(config->tls.ocsp.update_interval) << R"(
|
||||
--ocsp-startup
|
||||
Start accepting connections after initial attempts to
|
||||
get OCSP responses finish. It does not matter some of
|
||||
the attempts fail. This feature is useful if OCSP
|
||||
responses must be available before accepting
|
||||
connections.
|
||||
--no-verify-ocsp
|
||||
nghttpx does not verify OCSP response.
|
||||
--no-ocsp Disable OCSP stapling.
|
||||
--tls-session-cache-memcached=<HOST>,<PORT>[;tls]
|
||||
Specify address of memcached server to store session
|
||||
@@ -2214,6 +2291,25 @@ SSL/TLS:
|
||||
--tls-session-cache-memcached-private-key-file=<PATH>
|
||||
Path to client private key for memcached connections to
|
||||
store session cache.
|
||||
--tls-anti-replay-memcached=<HOST>,<PORT>[;tls]
|
||||
Specify address of memcached server to store ClientHello
|
||||
to avoid 0-RTT early data replay. This enables shared
|
||||
storage between multiple nghttpx instances. Optionally,
|
||||
memcached connection can be encrypted with TLS by
|
||||
specifying "tls" parameter.
|
||||
--tls-anti-replay-memcached-address-family=(auto|IPv4|IPv6)
|
||||
Specify address family of memcached connections to store
|
||||
ClientHello to avoid 0-RTT early data replay. If "auto"
|
||||
is given, both IPv4 and IPv6 are considered. If "IPv4"
|
||||
is given, only IPv4 address is considered. If "IPv6" is
|
||||
given, only IPv6 address is considered.
|
||||
Default: auto
|
||||
--tls-anti-replay-memcached-cert-file=<PATH>
|
||||
Path to client certificate for memcached connections to
|
||||
store ClientHello to avoid 0-RTT early data replay.
|
||||
--tls-anti-replay-memcached-private-key-file=<PATH>
|
||||
Path to client private key for memcached connections to
|
||||
store ClientHello to avoid 0-RTT early data replay.
|
||||
--tls-dyn-rec-warmup-threshold=<SIZE>
|
||||
Specify the threshold size for TLS dynamic record size
|
||||
behaviour. During a TLS session, after the threshold
|
||||
@@ -2426,11 +2522,22 @@ Logging:
|
||||
* $alpn: ALPN identifier of the protocol which generates
|
||||
the response. For HTTP/1, ALPN is always http/1.1,
|
||||
regardless of minor version.
|
||||
* $ssl_cipher: cipher used for SSL/TLS connection.
|
||||
* $ssl_protocol: protocol for SSL/TLS connection.
|
||||
* $ssl_session_id: session ID for SSL/TLS connection.
|
||||
* $ssl_session_reused: "r" if SSL/TLS session was
|
||||
* $tls_cipher: cipher used for SSL/TLS connection.
|
||||
* $tls_client_fingerprint_sha256: SHA-256 fingerprint of
|
||||
client certificate.
|
||||
* $tls_client_fingerprint_sha1: SHA-1 fingerprint of
|
||||
client certificate.
|
||||
* $tls_client_subject_name: subject name in client
|
||||
certificate.
|
||||
* $tls_client_issuer_name: issuer name in client
|
||||
certificate.
|
||||
* $tls_client_serial: serial number in client
|
||||
certificate.
|
||||
* $tls_protocol: protocol for SSL/TLS connection.
|
||||
* $tls_session_id: session ID for SSL/TLS connection.
|
||||
* $tls_session_reused: "r" if SSL/TLS session was
|
||||
reused. Otherwise, "."
|
||||
* $tls_sni: SNI server name for SSL/TLS connection.
|
||||
* $backend_host: backend host used to fulfill the
|
||||
request. "-" if backend host is not available.
|
||||
* $backend_port: backend port used to fulfill the
|
||||
@@ -2449,8 +2556,7 @@ Logging:
|
||||
Set path to write error log. To reopen file, send USR1
|
||||
signal to nghttpx. stderr will be redirected to the
|
||||
error log file unless --errorlog-syslog is used.
|
||||
Default: )"
|
||||
<< config->logging.error.file << R"(
|
||||
Default: )" << config->logging.error.file << R"(
|
||||
--errorlog-syslog
|
||||
Send error log to syslog. If this option is used,
|
||||
--errorlog-file option is ignored.
|
||||
@@ -2466,6 +2572,15 @@ HTTP:
|
||||
--strip-incoming-x-forwarded-for
|
||||
Strip X-Forwarded-For header field from inbound client
|
||||
requests.
|
||||
--no-add-x-forwarded-proto
|
||||
Don't append additional X-Forwarded-Proto header field
|
||||
to the backend request. If inbound client sets
|
||||
X-Forwarded-Proto, and
|
||||
--no-strip-incoming-x-forwarded-proto option is used,
|
||||
they are passed to the backend.
|
||||
--no-strip-incoming-x-forwarded-proto
|
||||
Don't strip X-Forwarded-Proto header field from inbound
|
||||
client requests.
|
||||
--add-forwarded=<LIST>
|
||||
Append RFC 7239 Forwarded header field with parameters
|
||||
specified in comma delimited list <LIST>. The supported
|
||||
@@ -2501,6 +2616,9 @@ HTTP:
|
||||
Default: obfuscated
|
||||
--no-via Don't append to Via header field. If Via header field
|
||||
is received, it is left unaltered.
|
||||
--no-strip-incoming-nghttpx-0rtt-uniq
|
||||
Don't strip nghttpx-0rtt-uniq header field from inbound
|
||||
client requests.
|
||||
--no-location-rewrite
|
||||
Don't rewrite location header field in default mode.
|
||||
When --http2-proxy is used, location header field will
|
||||
@@ -2573,8 +2691,8 @@ HTTP:
|
||||
Specify the port number which appears in Location header
|
||||
field when redirect to HTTPS URI is made due to
|
||||
"redirect-if-not-tls" parameter in --backend option.
|
||||
Default: )"
|
||||
<< config->http.redirect_https_port << R"(
|
||||
Default: )" << config->http.redirect_https_port
|
||||
<< R"(
|
||||
|
||||
API:
|
||||
--api-max-request-body=<SIZE>
|
||||
@@ -2635,6 +2753,14 @@ Process:
|
||||
--user=<USER>
|
||||
Run this program as <USER>. This option is intended to
|
||||
be used to drop root privileges.
|
||||
--single-process
|
||||
Run this program in a single process mode for debugging
|
||||
purpose. Without this option, nghttpx creates at least
|
||||
2 processes: master and worker processes. If this
|
||||
option is used, master and worker are unified into a
|
||||
single process. nghttpx still spawns additional process
|
||||
if neverbleed is used. In the single process mode, the
|
||||
signal handling feature is disabled.
|
||||
|
||||
Scripting:
|
||||
--mruby-file=<PATH>
|
||||
@@ -2642,9 +2768,10 @@ Scripting:
|
||||
|
||||
Misc:
|
||||
--conf=<PATH>
|
||||
Load configuration from <PATH>.
|
||||
Default: )"
|
||||
<< config->conf_path << R"(
|
||||
Load configuration from <PATH>. Please note that
|
||||
nghttpx always tries to read the default configuration
|
||||
file if --conf is not given.
|
||||
Default: )" << config->conf_path << R"(
|
||||
--include=<PATH>
|
||||
Load additional configurations from <PATH>. File <PATH>
|
||||
is read when configuration parser encountered this
|
||||
@@ -2662,8 +2789,7 @@ Misc:
|
||||
The <DURATION> argument is an integer and an optional unit (e.g., 1s
|
||||
is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms
|
||||
(hours, minutes, seconds and milliseconds, respectively). If a unit
|
||||
is omitted, a second is used as unit.)"
|
||||
<< std::endl;
|
||||
is omitted, a second is used as unit.)" << std::endl;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -2672,6 +2798,7 @@ int process_options(Config *config,
|
||||
std::vector<std::pair<StringRef, StringRef>> &cmdcfgs) {
|
||||
std::array<char, STRERROR_BUFSIZE> errbuf;
|
||||
if (conf_exists(config->conf_path.c_str())) {
|
||||
LOG(NOTICE) << "Loading configuration from " << config->conf_path;
|
||||
std::set<StringRef> include_set;
|
||||
if (load_config(config, config->conf_path.c_str(), include_set) == -1) {
|
||||
LOG(FATAL) << "Failed to load configuration from " << config->conf_path;
|
||||
@@ -2786,7 +2913,7 @@ int process_options(Config *config,
|
||||
}
|
||||
|
||||
if (!tlsconf.tls_proto_list.empty()) {
|
||||
tlsconf.tls_proto_mask = ssl::create_tls_proto_mask(tlsconf.tls_proto_list);
|
||||
tlsconf.tls_proto_mask = tls::create_tls_proto_mask(tlsconf.tls_proto_list);
|
||||
}
|
||||
|
||||
// TODO We depends on the ordering of protocol version macro in
|
||||
@@ -2797,7 +2924,7 @@ int process_options(Config *config,
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ssl::set_alpn_prefs(tlsconf.alpn_prefs, tlsconf.npn_list) != 0) {
|
||||
if (tls::set_alpn_prefs(tlsconf.alpn_prefs, tlsconf.npn_list) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -2821,7 +2948,7 @@ int process_options(Config *config,
|
||||
upstreamconf.worker_connections = std::numeric_limits<size_t>::max();
|
||||
}
|
||||
|
||||
if (ssl::upstream_tls_enabled(config->conn) &&
|
||||
if (tls::upstream_tls_enabled(config->conn) &&
|
||||
(tlsconf.private_key_file.empty() || tlsconf.cert_file.empty())) {
|
||||
LOG(FATAL) << "TLS private key and certificate files are required. "
|
||||
"Specify them in command-line, or in configuration file "
|
||||
@@ -2829,7 +2956,7 @@ int process_options(Config *config,
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ssl::upstream_tls_enabled(config->conn) && !tlsconf.ocsp.disabled) {
|
||||
if (tls::upstream_tls_enabled(config->conn) && !tlsconf.ocsp.disabled) {
|
||||
struct stat buf;
|
||||
if (stat(tlsconf.ocsp.fetch_ocsp_response_file.c_str(), &buf) != 0) {
|
||||
tlsconf.ocsp.disabled = true;
|
||||
@@ -2897,6 +3024,26 @@ int process_options(Config *config,
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto &memcachedconf = tlsconf.anti_replay.memcached;
|
||||
if (!memcachedconf.host.empty()) {
|
||||
auto hostport = util::make_hostport(StringRef{memcachedconf.host},
|
||||
memcachedconf.port);
|
||||
if (resolve_hostname(&memcachedconf.addr, memcachedconf.host.c_str(),
|
||||
memcachedconf.port, memcachedconf.family) == -1) {
|
||||
LOG(FATAL) << "Resolving memcached address for TLS anti-replay failed: "
|
||||
<< hostport;
|
||||
return -1;
|
||||
}
|
||||
LOG(NOTICE) << "Memcached address for TLS anti-replay: " << hostport
|
||||
<< " -> " << util::to_numeric_addr(&memcachedconf.addr);
|
||||
if (memcachedconf.tls) {
|
||||
LOG(NOTICE) << "Connection to memcached for TLS anti-replay will be "
|
||||
"encrypted by TLS";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (config->rlimit_nofile) {
|
||||
struct rlimit lim = {static_cast<rlim_t>(config->rlimit_nofile),
|
||||
static_cast<rlim_t>(config->rlimit_nofile)};
|
||||
@@ -2994,7 +3141,7 @@ void reload_config(WorkerProcess *wp) {
|
||||
// already created first default loop.
|
||||
auto loop = ev_default_loop(new_config->ev_loop_flags);
|
||||
|
||||
int ipc_fd;
|
||||
int ipc_fd = 0;
|
||||
|
||||
// fork_worker_process and forked child process assumes new
|
||||
// configuration can be obtained from get_config().
|
||||
@@ -3032,10 +3179,10 @@ int main(int argc, char **argv) {
|
||||
int rv;
|
||||
std::array<char, STRERROR_BUFSIZE> errbuf;
|
||||
|
||||
nghttp2::ssl::libssl_init();
|
||||
nghttp2::tls::libssl_init();
|
||||
|
||||
#ifndef NOTHREADS
|
||||
nghttp2::ssl::LibsslGlobalLock lock;
|
||||
nghttp2::tls::LibsslGlobalLock lock;
|
||||
#endif // NOTHREADS
|
||||
|
||||
Log::set_severity_level(NOTICE);
|
||||
@@ -3122,7 +3269,9 @@ int main(int argc, char **argv) {
|
||||
{SHRPX_OPT_BACKEND_HTTP_PROXY_URI.c_str(), required_argument, &flag,
|
||||
26},
|
||||
{SHRPX_OPT_BACKEND_NO_TLS.c_str(), no_argument, &flag, 27},
|
||||
{SHRPX_OPT_OCSP_STARTUP.c_str(), no_argument, &flag, 28},
|
||||
{SHRPX_OPT_FRONTEND_NO_TLS.c_str(), no_argument, &flag, 29},
|
||||
{SHRPX_OPT_NO_VERIFY_OCSP.c_str(), no_argument, &flag, 30},
|
||||
{SHRPX_OPT_BACKEND_TLS_SNI_FIELD.c_str(), required_argument, &flag, 31},
|
||||
{SHRPX_OPT_DH_PARAM_FILE.c_str(), required_argument, &flag, 33},
|
||||
{SHRPX_OPT_READ_RATE.c_str(), required_argument, &flag, 34},
|
||||
@@ -3300,6 +3449,20 @@ int main(int argc, char **argv) {
|
||||
{SHRPX_OPT_FRONTEND_MAX_REQUESTS.c_str(), required_argument, &flag,
|
||||
155},
|
||||
{SHRPX_OPT_SINGLE_THREAD.c_str(), no_argument, &flag, 156},
|
||||
{SHRPX_OPT_NO_ADD_X_FORWARDED_PROTO.c_str(), no_argument, &flag, 157},
|
||||
{SHRPX_OPT_NO_STRIP_INCOMING_X_FORWARDED_PROTO.c_str(), no_argument,
|
||||
&flag, 158},
|
||||
{SHRPX_OPT_SINGLE_PROCESS.c_str(), no_argument, &flag, 159},
|
||||
{SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED.c_str(), required_argument, &flag,
|
||||
160},
|
||||
{SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED_ADDRESS_FAMILY.c_str(),
|
||||
required_argument, &flag, 161},
|
||||
{SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED_CERT_FILE.c_str(),
|
||||
required_argument, &flag, 162},
|
||||
{SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED_PRIVATE_KEY_FILE.c_str(),
|
||||
required_argument, &flag, 163},
|
||||
{SHRPX_OPT_NO_STRIP_INCOMING_NGHTTPX_0RTT_UNIQ.c_str(), no_argument,
|
||||
&flag, 164},
|
||||
{nullptr, 0, nullptr, 0}};
|
||||
|
||||
int option_index = 0;
|
||||
@@ -3468,11 +3631,21 @@ int main(int argc, char **argv) {
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_NO_TLS,
|
||||
StringRef::from_lit("yes"));
|
||||
break;
|
||||
case 28:
|
||||
// --ocsp-startup
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_OCSP_STARTUP,
|
||||
StringRef::from_lit("yes"));
|
||||
break;
|
||||
case 29:
|
||||
// --frontend-no-tls
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_NO_TLS,
|
||||
StringRef::from_lit("yes"));
|
||||
break;
|
||||
case 30:
|
||||
// --no-verify-ocsp
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_NO_VERIFY_OCSP,
|
||||
StringRef::from_lit("yes"));
|
||||
break;
|
||||
case 31:
|
||||
// --backend-tls-sni-field
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_TLS_SNI_FIELD,
|
||||
@@ -4036,6 +4209,47 @@ int main(int argc, char **argv) {
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_SINGLE_THREAD,
|
||||
StringRef::from_lit("yes"));
|
||||
break;
|
||||
case 157:
|
||||
// --no-add-x-forwarded-proto
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_NO_ADD_X_FORWARDED_PROTO,
|
||||
StringRef::from_lit("yes"));
|
||||
break;
|
||||
case 158:
|
||||
// --no-strip-incoming-x-forwarded-proto
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_NO_STRIP_INCOMING_X_FORWARDED_PROTO,
|
||||
StringRef::from_lit("yes"));
|
||||
break;
|
||||
case 159:
|
||||
// --single-process
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_SINGLE_PROCESS,
|
||||
StringRef::from_lit("yes"));
|
||||
break;
|
||||
case 160:
|
||||
// --tls-anti-replay-memcached
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED,
|
||||
StringRef{optarg});
|
||||
break;
|
||||
case 161:
|
||||
// --tls-anti-replay-memcached-address-family
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED_ADDRESS_FAMILY,
|
||||
StringRef{optarg});
|
||||
break;
|
||||
case 162:
|
||||
// --tls-anti-replay-memcached-cert-file
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED_CERT_FILE,
|
||||
StringRef{optarg});
|
||||
break;
|
||||
case 163:
|
||||
// --tls-anti-replay-memcached-private-key-file
|
||||
cmdcfgs.emplace_back(
|
||||
SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED_PRIVATE_KEY_FILE,
|
||||
StringRef{optarg});
|
||||
break;
|
||||
case 164:
|
||||
// --no-strip-incoming-nghttpx-0rtt-uniq
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_NO_STRIP_INCOMING_NGHTTPX_0RTT_UNIQ,
|
||||
StringRef::from_lit("yes"));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -99,8 +99,6 @@ void AcceptHandler::accept_connection() {
|
||||
util::make_socket_closeonexec(cfd);
|
||||
#endif // !HAVE_ACCEPT4
|
||||
|
||||
util::make_socket_nodelay(cfd);
|
||||
|
||||
conn_hnr_->handle_connection(cfd, &sockaddr.sa, addrlen, faddr_);
|
||||
}
|
||||
|
||||
|
||||
@@ -38,12 +38,14 @@ namespace {
|
||||
const std::array<APIEndpoint, 2> &apis() {
|
||||
static const auto apis = new std::array<APIEndpoint, 2>{{
|
||||
APIEndpoint{
|
||||
StringRef::from_lit("/api/v1beta1/backendconfig"), true,
|
||||
StringRef::from_lit("/api/v1beta1/backendconfig"),
|
||||
true,
|
||||
(1 << API_METHOD_POST) | (1 << API_METHOD_PUT),
|
||||
&APIDownstreamConnection::handle_backendconfig,
|
||||
},
|
||||
APIEndpoint{
|
||||
StringRef::from_lit("/api/v1beta1/configrevision"), true,
|
||||
StringRef::from_lit("/api/v1beta1/configrevision"),
|
||||
true,
|
||||
(1 << API_METHOD_GET),
|
||||
&APIDownstreamConnection::handle_configrevision,
|
||||
},
|
||||
@@ -56,7 +58,8 @@ const std::array<APIEndpoint, 2> &apis() {
|
||||
namespace {
|
||||
// The method string. This must be same order of APIMethod.
|
||||
constexpr StringRef API_METHOD_STRING[] = {
|
||||
StringRef::from_lit("GET"), StringRef::from_lit("POST"),
|
||||
StringRef::from_lit("GET"),
|
||||
StringRef::from_lit("POST"),
|
||||
StringRef::from_lit("PUT"),
|
||||
};
|
||||
} // namespace
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
#include "shrpx_config.h"
|
||||
#include "shrpx_http_downstream_connection.h"
|
||||
#include "shrpx_http2_downstream_connection.h"
|
||||
#include "shrpx_ssl.h"
|
||||
#include "shrpx_tls.h"
|
||||
#include "shrpx_worker.h"
|
||||
#include "shrpx_downstream_connection_pool.h"
|
||||
#include "shrpx_downstream.h"
|
||||
@@ -56,7 +56,7 @@
|
||||
#endif // HAVE_SPDYLAY
|
||||
#include "util.h"
|
||||
#include "template.h"
|
||||
#include "ssl.h"
|
||||
#include "tls.h"
|
||||
|
||||
using namespace nghttp2;
|
||||
|
||||
@@ -96,10 +96,6 @@ void readcb(struct ev_loop *loop, ev_io *w, int revents) {
|
||||
delete handler;
|
||||
return;
|
||||
}
|
||||
if (handler->do_write() != 0) {
|
||||
delete handler;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -449,25 +445,32 @@ ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl,
|
||||
*p = '\0';
|
||||
|
||||
forwarded_for_ = StringRef{buf.base, p};
|
||||
} else if (family == AF_INET6) {
|
||||
// 2 for '[' and ']'
|
||||
auto len = 2 + ipaddr_.size();
|
||||
// 1 for terminating NUL.
|
||||
auto buf = make_byte_ref(balloc_, len + 1);
|
||||
auto p = buf.base;
|
||||
*p++ = '[';
|
||||
p = std::copy(std::begin(ipaddr_), std::end(ipaddr_), p);
|
||||
*p++ = ']';
|
||||
*p = '\0';
|
||||
|
||||
forwarded_for_ = StringRef{buf.base, p};
|
||||
} else {
|
||||
// family == AF_INET or family == AF_UNIX
|
||||
forwarded_for_ = ipaddr_;
|
||||
} else if (!faddr_->accept_proxy_protocol &&
|
||||
!config->conn.upstream.accept_proxy_protocol) {
|
||||
init_forwarded_for(family, ipaddr_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClientHandler::init_forwarded_for(int family, const StringRef &ipaddr) {
|
||||
if (family == AF_INET6) {
|
||||
// 2 for '[' and ']'
|
||||
auto len = 2 + ipaddr.size();
|
||||
// 1 for terminating NUL.
|
||||
auto buf = make_byte_ref(balloc_, len + 1);
|
||||
auto p = buf.base;
|
||||
*p++ = '[';
|
||||
p = std::copy(std::begin(ipaddr), std::end(ipaddr), p);
|
||||
*p++ = ']';
|
||||
*p = '\0';
|
||||
|
||||
forwarded_for_ = StringRef{buf.base, p};
|
||||
} else {
|
||||
// family == AF_INET or family == AF_UNIX
|
||||
forwarded_for_ = ipaddr;
|
||||
}
|
||||
}
|
||||
|
||||
void ClientHandler::setup_upstream_io_callback() {
|
||||
if (conn_.tls.ssl) {
|
||||
conn_.prepare_server_handshake();
|
||||
@@ -580,7 +583,7 @@ int ClientHandler::validate_next_proto() {
|
||||
CLOG(INFO, this) << "The negotiated next protocol: " << proto;
|
||||
}
|
||||
|
||||
if (!ssl::in_proto_list(get_config()->tls.npn_list, proto)) {
|
||||
if (!tls::in_proto_list(get_config()->tls.npn_list, proto)) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this) << "The negotiated protocol is not supported: " << proto;
|
||||
}
|
||||
@@ -696,7 +699,7 @@ void ClientHandler::pool_downstream_connection(
|
||||
|
||||
auto &shared_addr = group->shared_addr;
|
||||
|
||||
if (shared_addr->affinity == AFFINITY_NONE) {
|
||||
if (shared_addr->affinity.type == AFFINITY_NONE) {
|
||||
auto &dconn_pool = group->shared_addr->dconn_pool;
|
||||
dconn_pool.add_downstream_connection(std::move(dconn));
|
||||
|
||||
@@ -801,61 +804,40 @@ bool load_lighter(const DownstreamAddr *lhs, const DownstreamAddr *rhs) {
|
||||
Http2Session *ClientHandler::select_http2_session(
|
||||
const std::shared_ptr<DownstreamAddrGroup> &group) {
|
||||
auto &shared_addr = group->shared_addr;
|
||||
auto &http2_avail_freelist = shared_addr->http2_avail_freelist;
|
||||
|
||||
for (auto session = http2_avail_freelist.head; session;) {
|
||||
auto next = session->dlnext;
|
||||
|
||||
session->remove_from_freelist();
|
||||
|
||||
// session may be in graceful shutdown period now.
|
||||
if (session->max_concurrency_reached(0)) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this)
|
||||
<< "Maximum streams have been reached for Http2Session(" << session
|
||||
<< "). Skip it";
|
||||
}
|
||||
|
||||
session = next;
|
||||
|
||||
// First count the working backend addresses.
|
||||
size_t min = 0;
|
||||
for (const auto &addr : shared_addr->addrs) {
|
||||
if (addr.proto != PROTO_HTTP2 || addr.connect_blocker->blocked()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
++min;
|
||||
}
|
||||
|
||||
if (min == 0) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this) << "No working backend address found";
|
||||
CLOG(INFO, this) << "Use Http2Session " << session
|
||||
<< " from http2_avail_freelist";
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto &http2_avail_freelist = shared_addr->http2_avail_freelist;
|
||||
|
||||
if (http2_avail_freelist.size() >= min) {
|
||||
for (auto session = http2_avail_freelist.head; session;) {
|
||||
auto next = session->dlnext;
|
||||
|
||||
session->remove_from_freelist();
|
||||
|
||||
// session may be in graceful shutdown period now.
|
||||
if (session->max_concurrency_reached(0)) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this)
|
||||
<< "Maximum streams have been reached for Http2Session("
|
||||
<< session << "). Skip it";
|
||||
}
|
||||
|
||||
session = next;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (session->max_concurrency_reached(1)) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this) << "Use Http2Session " << session
|
||||
<< " from http2_avail_freelist";
|
||||
CLOG(INFO, this) << "Maximum streams are reached for Http2Session("
|
||||
<< session << ").";
|
||||
}
|
||||
|
||||
if (session->max_concurrency_reached(1)) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this) << "Maximum streams are reached for Http2Session("
|
||||
<< session << ").";
|
||||
}
|
||||
} else {
|
||||
session->add_to_avail_freelist();
|
||||
}
|
||||
return session;
|
||||
} else {
|
||||
session->add_to_avail_freelist();
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
DownstreamAddr *selected_addr = nullptr;
|
||||
@@ -898,7 +880,12 @@ Http2Session *ClientHandler::select_http2_session(
|
||||
}
|
||||
}
|
||||
|
||||
assert(selected_addr);
|
||||
if (selected_addr == nullptr) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this) << "No working backend address found";
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this) << "Selected DownstreamAddr=" << selected_addr
|
||||
@@ -960,6 +947,24 @@ uint32_t next_cycle(const WeightedPri &pri) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
uint32_t ClientHandler::get_affinity_cookie(Downstream *downstream,
|
||||
const StringRef &cookie_name) {
|
||||
auto h = downstream->find_affinity_cookie(cookie_name);
|
||||
if (h) {
|
||||
return h;
|
||||
}
|
||||
|
||||
auto d = std::uniform_int_distribution<uint32_t>(
|
||||
1, std::numeric_limits<uint32_t>::max());
|
||||
auto rh = d(worker_->get_randgen());
|
||||
h = util::hash32(StringRef{reinterpret_cast<uint8_t *>(&rh),
|
||||
reinterpret_cast<uint8_t *>(&rh) + sizeof(rh)});
|
||||
|
||||
downstream->renew_affinity_cookie(h);
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
std::unique_ptr<DownstreamConnection>
|
||||
ClientHandler::get_downstream_connection(int &err, Downstream *downstream) {
|
||||
size_t group_idx;
|
||||
@@ -983,29 +988,30 @@ ClientHandler::get_downstream_connection(int &err, Downstream *downstream) {
|
||||
auto &balloc = downstream->get_block_allocator();
|
||||
|
||||
// Fast path. If we have one group, it must be catch-all group.
|
||||
// proxy mode falls in this case.
|
||||
if (groups.size() == 1) {
|
||||
group_idx = 0;
|
||||
} else if (req.method == HTTP_CONNECT) {
|
||||
// CONNECT method does not have path. But we requires path in
|
||||
// host-path mapping. As workaround, we assume that path is "/".
|
||||
group_idx = match_downstream_addr_group(routerconf, req.authority,
|
||||
StringRef::from_lit("/"), groups,
|
||||
catch_all, balloc);
|
||||
} else {
|
||||
if (!req.authority.empty()) {
|
||||
group_idx = match_downstream_addr_group(
|
||||
routerconf, req.authority, req.path, groups, catch_all, balloc);
|
||||
StringRef authority;
|
||||
if (faddr_->sni_fwd) {
|
||||
authority = sni_;
|
||||
} else if (!req.authority.empty()) {
|
||||
authority = req.authority;
|
||||
} else {
|
||||
auto h = req.fs.header(http2::HD_HOST);
|
||||
if (h) {
|
||||
group_idx = match_downstream_addr_group(routerconf, h->value, req.path,
|
||||
groups, catch_all, balloc);
|
||||
} else {
|
||||
group_idx = match_downstream_addr_group(
|
||||
routerconf, StringRef{}, req.path, groups, catch_all, balloc);
|
||||
authority = h->value;
|
||||
}
|
||||
}
|
||||
|
||||
StringRef path;
|
||||
// CONNECT method does not have path. But we requires path in
|
||||
// host-path mapping. As workaround, we assume that path is "/".
|
||||
if (req.method != HTTP_CONNECT) {
|
||||
path = req.path;
|
||||
}
|
||||
|
||||
group_idx = match_downstream_addr_group(routerconf, authority, path, groups,
|
||||
catch_all, balloc);
|
||||
}
|
||||
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
@@ -1024,27 +1030,59 @@ ClientHandler::get_downstream_connection(int &err, Downstream *downstream) {
|
||||
auto &group = groups[group_idx];
|
||||
auto &shared_addr = group->shared_addr;
|
||||
|
||||
if (shared_addr->affinity == AFFINITY_IP) {
|
||||
if (!affinity_hash_computed_) {
|
||||
affinity_hash_ = compute_affinity_from_ip(ipaddr_);
|
||||
affinity_hash_computed_ = true;
|
||||
if (shared_addr->affinity.type != AFFINITY_NONE) {
|
||||
uint32_t hash;
|
||||
switch (shared_addr->affinity.type) {
|
||||
case AFFINITY_IP:
|
||||
if (!affinity_hash_computed_) {
|
||||
affinity_hash_ = compute_affinity_from_ip(ipaddr_);
|
||||
affinity_hash_computed_ = true;
|
||||
}
|
||||
hash = affinity_hash_;
|
||||
break;
|
||||
case AFFINITY_COOKIE:
|
||||
hash = get_affinity_cookie(downstream, shared_addr->affinity.cookie.name);
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
const auto &affinity_hash = shared_addr->affinity_hash;
|
||||
|
||||
auto it = std::lower_bound(
|
||||
std::begin(affinity_hash), std::end(affinity_hash), affinity_hash_,
|
||||
std::begin(affinity_hash), std::end(affinity_hash), hash,
|
||||
[](const AffinityHash &lhs, uint32_t rhs) { return lhs.hash < rhs; });
|
||||
|
||||
if (it == std::end(affinity_hash)) {
|
||||
it = std::begin(affinity_hash);
|
||||
}
|
||||
|
||||
auto aff_idx =
|
||||
static_cast<size_t>(std::distance(std::begin(affinity_hash), it));
|
||||
auto idx = (*it).idx;
|
||||
auto addr = &shared_addr->addrs[idx];
|
||||
|
||||
auto &addr = shared_addr->addrs[idx];
|
||||
if (addr.proto == PROTO_HTTP2) {
|
||||
auto http2session = select_http2_session_with_affinity(group, &addr);
|
||||
if (addr->connect_blocker->blocked()) {
|
||||
size_t i;
|
||||
for (i = aff_idx + 1; i != aff_idx; ++i) {
|
||||
if (i == shared_addr->affinity_hash.size()) {
|
||||
i = 0;
|
||||
}
|
||||
addr = &shared_addr->addrs[shared_addr->affinity_hash[i].idx];
|
||||
if (addr->connect_blocker->blocked()) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (i == aff_idx) {
|
||||
err = -1;
|
||||
return nullptr;
|
||||
}
|
||||
aff_idx = i;
|
||||
}
|
||||
|
||||
if (addr->proto == PROTO_HTTP2) {
|
||||
auto http2session = select_http2_session_with_affinity(group, addr);
|
||||
|
||||
auto dconn = make_unique<Http2DownstreamConnection>(http2session);
|
||||
|
||||
@@ -1053,11 +1091,11 @@ ClientHandler::get_downstream_connection(int &err, Downstream *downstream) {
|
||||
return std::move(dconn);
|
||||
}
|
||||
|
||||
auto &dconn_pool = addr.dconn_pool;
|
||||
auto &dconn_pool = addr->dconn_pool;
|
||||
auto dconn = dconn_pool->pop_downstream_connection();
|
||||
|
||||
if (!dconn) {
|
||||
dconn = make_unique<HttpDownstreamConnection>(group, idx, conn_.loop,
|
||||
dconn = make_unique<HttpDownstreamConnection>(group, aff_idx, conn_.loop,
|
||||
worker_);
|
||||
}
|
||||
|
||||
@@ -1133,7 +1171,7 @@ ClientHandler::get_downstream_connection(int &err, Downstream *downstream) {
|
||||
}
|
||||
|
||||
dconn =
|
||||
make_unique<HttpDownstreamConnection>(group, -1, conn_.loop, worker_);
|
||||
make_unique<HttpDownstreamConnection>(group, 0, conn_.loop, worker_);
|
||||
}
|
||||
|
||||
dconn->set_client_handler(this);
|
||||
@@ -1206,7 +1244,6 @@ void ClientHandler::start_immediate_shutdown() {
|
||||
}
|
||||
|
||||
void ClientHandler::write_accesslog(Downstream *downstream) {
|
||||
nghttp2::ssl::TLSSessionInfo tls_info;
|
||||
auto &req = downstream->request();
|
||||
|
||||
auto config = get_config();
|
||||
@@ -1220,10 +1257,15 @@ void ClientHandler::write_accesslog(Downstream *downstream) {
|
||||
upstream_accesslog(
|
||||
config->logging.access.format,
|
||||
LogSpec{
|
||||
downstream, ipaddr_, alpn_,
|
||||
nghttp2::ssl::get_tls_session_info(&tls_info, conn_.tls.ssl),
|
||||
downstream,
|
||||
ipaddr_,
|
||||
alpn_,
|
||||
sni_,
|
||||
conn_.tls.ssl,
|
||||
std::chrono::high_resolution_clock::now(), // request_end_time
|
||||
port_, faddr_->port, config->pid,
|
||||
port_,
|
||||
faddr_->port,
|
||||
config->pid,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1462,6 +1504,14 @@ int ClientHandler::proxy_protocol_read() {
|
||||
<< " bytes read";
|
||||
}
|
||||
|
||||
auto config = get_config();
|
||||
auto &fwdconf = config->http.forwarded;
|
||||
|
||||
if ((fwdconf.params & FORWARDED_FOR) &&
|
||||
fwdconf.for_node_type == FORWARDED_NODE_IP) {
|
||||
init_forwarded_for(family, ipaddr_);
|
||||
}
|
||||
|
||||
return on_proxy_protocol_finish();
|
||||
}
|
||||
|
||||
@@ -1487,6 +1537,8 @@ void ClientHandler::set_tls_sni(const StringRef &sni) {
|
||||
|
||||
StringRef ClientHandler::get_tls_sni() const { return sni_; }
|
||||
|
||||
StringRef ClientHandler::get_alpn() const { return alpn_; }
|
||||
|
||||
BlockAllocator &ClientHandler::get_block_allocator() { return balloc_; }
|
||||
|
||||
} // namespace shrpx
|
||||
|
||||
@@ -125,6 +125,9 @@ public:
|
||||
|
||||
Worker *get_worker() const;
|
||||
|
||||
// Initializes forwarded_for_.
|
||||
void init_forwarded_for(int family, const StringRef &ipaddr);
|
||||
|
||||
using ReadBuf = DefaultMemchunkBuffer;
|
||||
|
||||
ReadBuf *get_rb();
|
||||
@@ -150,6 +153,11 @@ public:
|
||||
Http2Session *select_http2_session_with_affinity(
|
||||
const std::shared_ptr<DownstreamAddrGroup> &group, DownstreamAddr *addr);
|
||||
|
||||
// Returns an affinity cookie value for |downstream|. |cookie_name|
|
||||
// is used to inspect cookie header field in request header fields.
|
||||
uint32_t get_affinity_cookie(Downstream *downstream,
|
||||
const StringRef &cookie_name);
|
||||
|
||||
const UpstreamAddr *get_upstream_addr() const;
|
||||
|
||||
void repeat_read_timer();
|
||||
@@ -163,6 +171,9 @@ public:
|
||||
// Returns TLS SNI extension value client sent in this connection.
|
||||
StringRef get_tls_sni() const;
|
||||
|
||||
// Returns ALPN negotiated in this connection.
|
||||
StringRef get_alpn() const;
|
||||
|
||||
BlockAllocator &get_block_allocator();
|
||||
|
||||
private:
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
#include "http-parser/http_parser.h"
|
||||
|
||||
#include "shrpx_log.h"
|
||||
#include "shrpx_ssl.h"
|
||||
#include "shrpx_tls.h"
|
||||
#include "shrpx_http.h"
|
||||
#include "util.h"
|
||||
#include "base64.h"
|
||||
@@ -401,6 +401,11 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
|
||||
break;
|
||||
case 7:
|
||||
switch (name[6]) {
|
||||
case 'i':
|
||||
if (util::strieq_l("tls_sn", name, 6)) {
|
||||
return SHRPX_LOGF_TLS_SNI;
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
if (util::strieq_l("reques", name, 6)) {
|
||||
return SHRPX_LOGF_REQUEST;
|
||||
@@ -419,6 +424,9 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
|
||||
if (util::strieq_l("ssl_ciphe", name, 9)) {
|
||||
return SHRPX_LOGF_SSL_CIPHER;
|
||||
}
|
||||
if (util::strieq_l("tls_ciphe", name, 9)) {
|
||||
return SHRPX_LOGF_TLS_CIPHER;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@@ -455,6 +463,9 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
|
||||
if (util::strieq_l("ssl_protoco", name, 11)) {
|
||||
return SHRPX_LOGF_SSL_PROTOCOL;
|
||||
}
|
||||
if (util::strieq_l("tls_protoco", name, 11)) {
|
||||
return SHRPX_LOGF_TLS_PROTOCOL;
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
if (util::strieq_l("backend_hos", name, 11)) {
|
||||
@@ -472,6 +483,9 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
|
||||
if (util::strieq_l("ssl_session_i", name, 13)) {
|
||||
return SHRPX_LOGF_SSL_SESSION_ID;
|
||||
}
|
||||
if (util::strieq_l("tls_session_i", name, 13)) {
|
||||
return SHRPX_LOGF_TLS_SESSION_ID;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@@ -484,12 +498,60 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 17:
|
||||
switch (name[16]) {
|
||||
case 'l':
|
||||
if (util::strieq_l("tls_client_seria", name, 16)) {
|
||||
return SHRPX_LOGF_TLS_CLIENT_SERIAL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 18:
|
||||
switch (name[17]) {
|
||||
case 'd':
|
||||
if (util::strieq_l("ssl_session_reuse", name, 17)) {
|
||||
return SHRPX_LOGF_SSL_SESSION_REUSED;
|
||||
}
|
||||
if (util::strieq_l("tls_session_reuse", name, 17)) {
|
||||
return SHRPX_LOGF_TLS_SESSION_REUSED;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 22:
|
||||
switch (name[21]) {
|
||||
case 'e':
|
||||
if (util::strieq_l("tls_client_issuer_nam", name, 21)) {
|
||||
return SHRPX_LOGF_TLS_CLIENT_ISSUER_NAME;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 23:
|
||||
switch (name[22]) {
|
||||
case 'e':
|
||||
if (util::strieq_l("tls_client_subject_nam", name, 22)) {
|
||||
return SHRPX_LOGF_TLS_CLIENT_SUBJECT_NAME;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 27:
|
||||
switch (name[26]) {
|
||||
case '1':
|
||||
if (util::strieq_l("tls_client_fingerprint_sha", name, 26)) {
|
||||
return SHRPX_LOGF_TLS_CLIENT_FINGERPRINT_SHA1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 29:
|
||||
switch (name[28]) {
|
||||
case '6':
|
||||
if (util::strieq_l("tls_client_fingerprint_sha25", name, 28)) {
|
||||
return SHRPX_LOGF_TLS_CLIENT_FINGERPRINT_SHA256;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@@ -642,7 +704,7 @@ int parse_duration(ev_tstamp *dest, const StringRef &opt,
|
||||
namespace {
|
||||
int parse_tls_proto_version(int &dest, const StringRef &opt,
|
||||
const StringRef &optarg) {
|
||||
auto v = ssl::proto_version_from_string(optarg);
|
||||
auto v = tls::proto_version_from_string(optarg);
|
||||
if (v == -1) {
|
||||
LOG(ERROR) << opt << ": invalid TLS protocol version: " << optarg;
|
||||
return -1;
|
||||
@@ -693,6 +755,7 @@ int parse_memcached_connection_params(MemcachedConnectionParams &out,
|
||||
struct UpstreamParams {
|
||||
int alt_mode;
|
||||
bool tls;
|
||||
bool sni_fwd;
|
||||
bool proxyproto;
|
||||
};
|
||||
|
||||
@@ -708,6 +771,8 @@ int parse_upstream_params(UpstreamParams &out, const StringRef &src_params) {
|
||||
|
||||
if (util::strieq_l("tls", param)) {
|
||||
out.tls = true;
|
||||
} else if (util::strieq_l("sni-fwd", param)) {
|
||||
out.sni_fwd = true;
|
||||
} else if (util::strieq_l("no-tls", param)) {
|
||||
out.tls = false;
|
||||
} else if (util::strieq_l("api", param)) {
|
||||
@@ -742,10 +807,10 @@ int parse_upstream_params(UpstreamParams &out, const StringRef &src_params) {
|
||||
|
||||
struct DownstreamParams {
|
||||
StringRef sni;
|
||||
AffinityConfig affinity;
|
||||
size_t fall;
|
||||
size_t rise;
|
||||
shrpx_proto proto;
|
||||
shrpx_session_affinity affinity;
|
||||
bool tls;
|
||||
bool dns;
|
||||
bool redirect_if_not_tls;
|
||||
@@ -815,11 +880,38 @@ int parse_downstream_params(DownstreamParams &out,
|
||||
} else if (util::istarts_with_l(param, "affinity=")) {
|
||||
auto valstr = StringRef{first + str_size("affinity="), end};
|
||||
if (util::strieq_l("none", valstr)) {
|
||||
out.affinity = AFFINITY_NONE;
|
||||
out.affinity.type = AFFINITY_NONE;
|
||||
} else if (util::strieq_l("ip", valstr)) {
|
||||
out.affinity = AFFINITY_IP;
|
||||
out.affinity.type = AFFINITY_IP;
|
||||
} else if (util::strieq_l("cookie", valstr)) {
|
||||
out.affinity.type = AFFINITY_COOKIE;
|
||||
} else {
|
||||
LOG(ERROR) << "backend: affinity: value must be either none or ip";
|
||||
LOG(ERROR)
|
||||
<< "backend: affinity: value must be one of none, ip, and cookie";
|
||||
return -1;
|
||||
}
|
||||
} else if (util::istarts_with_l(param, "affinity-cookie-name=")) {
|
||||
auto val = StringRef{first + str_size("affinity-cookie-name="), end};
|
||||
if (val.empty()) {
|
||||
LOG(ERROR)
|
||||
<< "backend: affinity-cookie-name: non empty string is expected";
|
||||
return -1;
|
||||
}
|
||||
out.affinity.cookie.name = val;
|
||||
} else if (util::istarts_with_l(param, "affinity-cookie-path=")) {
|
||||
out.affinity.cookie.path =
|
||||
StringRef{first + str_size("affinity-cookie-path="), end};
|
||||
} else if (util::istarts_with_l(param, "affinity-cookie-secure=")) {
|
||||
auto valstr = StringRef{first + str_size("affinity-cookie-secure="), end};
|
||||
if (util::strieq_l("auto", valstr)) {
|
||||
out.affinity.cookie.secure = COOKIE_SECURE_AUTO;
|
||||
} else if (util::strieq_l("yes", valstr)) {
|
||||
out.affinity.cookie.secure = COOKIE_SECURE_YES;
|
||||
} else if (util::strieq_l("no", valstr)) {
|
||||
out.affinity.cookie.secure = COOKIE_SECURE_NO;
|
||||
} else {
|
||||
LOG(ERROR) << "backend: affinity-cookie-secure: value must be one of "
|
||||
"auto, yes, and no";
|
||||
return -1;
|
||||
}
|
||||
} else if (util::strieq_l("dns", param)) {
|
||||
@@ -871,6 +963,13 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (params.affinity.type == AFFINITY_COOKIE &&
|
||||
params.affinity.cookie.name.empty()) {
|
||||
LOG(ERROR) << "backend: affinity-cookie-name is mandatory if "
|
||||
"affinity=cookie is specified";
|
||||
return -1;
|
||||
}
|
||||
|
||||
addr.fall = params.fall;
|
||||
addr.rise = params.rise;
|
||||
addr.proto = params.proto;
|
||||
@@ -915,8 +1014,27 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
|
||||
if (g.pattern == pattern) {
|
||||
// Last value wins if we have multiple different affinity
|
||||
// value under one group.
|
||||
if (params.affinity != AFFINITY_NONE) {
|
||||
g.affinity = params.affinity;
|
||||
if (params.affinity.type != AFFINITY_NONE) {
|
||||
if (g.affinity.type == AFFINITY_NONE) {
|
||||
g.affinity.type = params.affinity.type;
|
||||
if (params.affinity.type == AFFINITY_COOKIE) {
|
||||
g.affinity.cookie.name = make_string_ref(
|
||||
downstreamconf.balloc, params.affinity.cookie.name);
|
||||
if (!params.affinity.cookie.path.empty()) {
|
||||
g.affinity.cookie.path = make_string_ref(
|
||||
downstreamconf.balloc, params.affinity.cookie.path);
|
||||
}
|
||||
g.affinity.cookie.secure = params.affinity.cookie.secure;
|
||||
}
|
||||
} else if (g.affinity.type != params.affinity.type ||
|
||||
g.affinity.cookie.name != params.affinity.cookie.name ||
|
||||
g.affinity.cookie.path != params.affinity.cookie.path ||
|
||||
g.affinity.cookie.secure !=
|
||||
params.affinity.cookie.secure) {
|
||||
LOG(ERROR) << "backend: affinity: multiple different affinity "
|
||||
"configurations found in a single group";
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
// If at least one backend requires frontend TLS connection,
|
||||
// enable it for all backends sharing the same pattern.
|
||||
@@ -936,7 +1054,16 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
|
||||
addr_groups.emplace_back(pattern);
|
||||
auto &g = addr_groups.back();
|
||||
g.addrs.push_back(addr);
|
||||
g.affinity = params.affinity;
|
||||
g.affinity.type = params.affinity.type;
|
||||
if (params.affinity.type == AFFINITY_COOKIE) {
|
||||
g.affinity.cookie.name =
|
||||
make_string_ref(downstreamconf.balloc, params.affinity.cookie.name);
|
||||
if (!params.affinity.cookie.path.empty()) {
|
||||
g.affinity.cookie.path =
|
||||
make_string_ref(downstreamconf.balloc, params.affinity.cookie.path);
|
||||
}
|
||||
g.affinity.cookie.secure = params.affinity.cookie.secure;
|
||||
}
|
||||
g.redirect_if_not_tls = params.redirect_if_not_tls;
|
||||
|
||||
if (pattern[0] == '*') {
|
||||
@@ -947,6 +1074,12 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
|
||||
auto host = StringRef{std::begin(g.pattern) + 1, path_first};
|
||||
auto path = StringRef{path_first, std::end(g.pattern)};
|
||||
|
||||
auto path_is_wildcard = false;
|
||||
if (path[path.size() - 1] == '*') {
|
||||
path = StringRef{std::begin(path), std::begin(path) + path.size() - 1};
|
||||
path_is_wildcard = true;
|
||||
}
|
||||
|
||||
auto it = std::find_if(
|
||||
std::begin(wildcard_patterns), std::end(wildcard_patterns),
|
||||
[&host](const WildcardPattern &wp) { return wp.host == host; });
|
||||
@@ -955,7 +1088,7 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
|
||||
wildcard_patterns.emplace_back(host);
|
||||
|
||||
auto &router = wildcard_patterns.back().router;
|
||||
router.add_route(path, idx);
|
||||
router.add_route(path, idx, path_is_wildcard);
|
||||
|
||||
auto iov = make_byte_ref(downstreamconf.balloc, host.size() + 1);
|
||||
auto p = iov.base;
|
||||
@@ -965,13 +1098,20 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
|
||||
|
||||
rw_router.add_route(rev_host, wildcard_patterns.size() - 1);
|
||||
} else {
|
||||
(*it).router.add_route(path, idx);
|
||||
(*it).router.add_route(path, idx, path_is_wildcard);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
router.add_route(g.pattern, idx);
|
||||
auto path_is_wildcard = false;
|
||||
if (pattern[pattern.size() - 1] == '*') {
|
||||
pattern = StringRef{std::begin(pattern),
|
||||
std::begin(pattern) + pattern.size() - 1};
|
||||
path_is_wildcard = true;
|
||||
}
|
||||
|
||||
router.add_route(pattern, idx, path_is_wildcard);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -1558,6 +1698,11 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
return SHRPX_OPTID_HTTP2_BRIDGE;
|
||||
}
|
||||
break;
|
||||
case 'p':
|
||||
if (util::strieq_l("ocsp-startu", name, 11)) {
|
||||
return SHRPX_OPTID_OCSP_STARTUP;
|
||||
}
|
||||
break;
|
||||
case 'y':
|
||||
if (util::strieq_l("client-prox", name, 11)) {
|
||||
return SHRPX_OPTID_CLIENT_PROXY;
|
||||
@@ -1613,6 +1758,11 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
return SHRPX_OPTID_NO_SERVER_PUSH;
|
||||
}
|
||||
break;
|
||||
case 'p':
|
||||
if (util::strieq_l("no-verify-ocs", name, 13)) {
|
||||
return SHRPX_OPTID_NO_VERIFY_OCSP;
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
if (util::strieq_l("backend-no-tl", name, 13)) {
|
||||
return SHRPX_OPTID_BACKEND_NO_TLS;
|
||||
@@ -1620,6 +1770,9 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
if (util::strieq_l("client-cipher", name, 13)) {
|
||||
return SHRPX_OPTID_CLIENT_CIPHERS;
|
||||
}
|
||||
if (util::strieq_l("single-proces", name, 13)) {
|
||||
return SHRPX_OPTID_SINGLE_PROCESS;
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
if (util::strieq_l("tls-proto-lis", name, 13)) {
|
||||
@@ -1902,6 +2055,11 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
return SHRPX_OPTID_FETCH_OCSP_RESPONSE_FILE;
|
||||
}
|
||||
break;
|
||||
case 'o':
|
||||
if (util::strieq_l("no-add-x-forwarded-prot", name, 23)) {
|
||||
return SHRPX_OPTID_NO_ADD_X_FORWARDED_PROTO;
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
if (util::strieq_l("listener-disable-timeou", name, 23)) {
|
||||
return SHRPX_OPTID_LISTENER_DISABLE_TIMEOUT;
|
||||
@@ -1914,6 +2072,11 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
break;
|
||||
case 25:
|
||||
switch (name[24]) {
|
||||
case 'd':
|
||||
if (util::strieq_l("tls-anti-replay-memcache", name, 24)) {
|
||||
return SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED;
|
||||
}
|
||||
break;
|
||||
case 'e':
|
||||
if (util::strieq_l("backend-http2-window-siz", name, 24)) {
|
||||
return SHRPX_OPTID_BACKEND_HTTP2_WINDOW_SIZE;
|
||||
@@ -2097,6 +2260,19 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
if (util::strieq_l("frontend-http2-optimize-window-siz", name, 34)) {
|
||||
return SHRPX_OPTID_FRONTEND_HTTP2_OPTIMIZE_WINDOW_SIZE;
|
||||
}
|
||||
if (util::strieq_l("tls-anti-replay-memcached-cert-fil", name, 34)) {
|
||||
return SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED_CERT_FILE;
|
||||
}
|
||||
break;
|
||||
case 'o':
|
||||
if (util::strieq_l("no-strip-incoming-x-forwarded-prot", name, 34)) {
|
||||
return SHRPX_OPTID_NO_STRIP_INCOMING_X_FORWARDED_PROTO;
|
||||
}
|
||||
break;
|
||||
case 'q':
|
||||
if (util::strieq_l("no-strip-incoming-nghttpx-0rtt-uni", name, 34)) {
|
||||
return SHRPX_OPTID_NO_STRIP_INCOMING_NGHTTPX_0RTT_UNIQ;
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
if (util::strieq_l("frontend-http2-dump-response-heade", name, 34)) {
|
||||
@@ -2175,6 +2351,11 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
return SHRPX_OPTID_BACKEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE;
|
||||
}
|
||||
break;
|
||||
case 'y':
|
||||
if (util::strieq_l("tls-anti-replay-memcached-address-famil", name, 39)) {
|
||||
return SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED_ADDRESS_FAMILY;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 41:
|
||||
@@ -2201,6 +2382,12 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
break;
|
||||
case 42:
|
||||
switch (name[41]) {
|
||||
case 'e':
|
||||
if (util::strieq_l("tls-anti-replay-memcached-private-key-fil", name,
|
||||
41)) {
|
||||
return SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED_PRIVATE_KEY_FILE;
|
||||
}
|
||||
break;
|
||||
case 'y':
|
||||
if (util::strieq_l("tls-session-cache-memcached-address-famil", name,
|
||||
41)) {
|
||||
@@ -2283,9 +2470,15 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (params.sni_fwd && !params.tls) {
|
||||
LOG(ERROR) << "frontend: sni_fwd requires tls";
|
||||
return -1;
|
||||
}
|
||||
|
||||
UpstreamAddr addr{};
|
||||
addr.fd = -1;
|
||||
addr.tls = params.tls;
|
||||
addr.sni_fwd = params.sni_fwd;
|
||||
addr.alt_mode = params.alt_mode;
|
||||
addr.accept_proxy_protocol = params.proxyproto;
|
||||
|
||||
@@ -2504,14 +2697,16 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
||||
<< SHRPX_OPT_FRONTEND;
|
||||
return 0;
|
||||
case SHRPX_OPTID_BACKEND_NO_TLS:
|
||||
LOG(WARN) << opt << ": deprecated. backend connection is not encrypted by "
|
||||
"default. See also "
|
||||
LOG(WARN) << opt
|
||||
<< ": deprecated. backend connection is not encrypted by "
|
||||
"default. See also "
|
||||
<< SHRPX_OPT_BACKEND_TLS;
|
||||
return 0;
|
||||
case SHRPX_OPTID_BACKEND_TLS_SNI_FIELD:
|
||||
LOG(WARN) << opt << ": deprecated. Use sni keyword in --backend option. "
|
||||
"For now, all sni values of all backends are "
|
||||
"overridden by the given value "
|
||||
LOG(WARN) << opt
|
||||
<< ": deprecated. Use sni keyword in --backend option. "
|
||||
"For now, all sni values of all backends are "
|
||||
"overridden by the given value "
|
||||
<< optarg;
|
||||
config->tls.backend_sni_name = make_string_ref(config->balloc, optarg);
|
||||
|
||||
@@ -2622,8 +2817,9 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_CLIENT:
|
||||
LOG(ERROR) << opt << ": deprecated. Use frontend=<addr>,<port>;no-tls, "
|
||||
"backend=<addr>,<port>;;proto=h2;tls";
|
||||
LOG(ERROR) << opt
|
||||
<< ": deprecated. Use frontend=<addr>,<port>;no-tls, "
|
||||
"backend=<addr>,<port>;;proto=h2;tls";
|
||||
return -1;
|
||||
case SHRPX_OPTID_INSECURE:
|
||||
config->tls.insecure = util::strieq_l("yes", optarg);
|
||||
@@ -2718,8 +2914,9 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
||||
return 0;
|
||||
}
|
||||
case SHRPX_OPTID_TLS_PROTO_LIST: {
|
||||
LOG(WARN) << opt << ": deprecated. Use tls-min-proto-version and "
|
||||
"tls-max-proto-version instead.";
|
||||
LOG(WARN) << opt
|
||||
<< ": deprecated. Use tls-min-proto-version and "
|
||||
"tls-max-proto-version instead.";
|
||||
auto list = util::split_str(optarg, ',');
|
||||
config->tls.tls_proto_list.resize(list.size());
|
||||
for (size_t i = 0; i < list.size(); ++i) {
|
||||
@@ -2980,7 +3177,8 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED:
|
||||
case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED: {
|
||||
case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED:
|
||||
case SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED: {
|
||||
auto addr_end = std::find(std::begin(optarg), std::end(optarg), ';');
|
||||
auto src_params = StringRef{addr_end, std::end(optarg)};
|
||||
|
||||
@@ -3010,6 +3208,13 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
||||
memcachedconf.tls = params.tls;
|
||||
break;
|
||||
}
|
||||
case SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED: {
|
||||
auto &memcachedconf = config->tls.anti_replay.memcached;
|
||||
memcachedconf.host = make_string_ref(config->balloc, StringRef{host});
|
||||
memcachedconf.port = port;
|
||||
memcachedconf.tls = params.tls;
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
return 0;
|
||||
@@ -3157,6 +3362,16 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
||||
config->tls.ticket.memcached.private_key_file =
|
||||
make_string_ref(config->balloc, optarg);
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED_CERT_FILE:
|
||||
config->tls.anti_replay.memcached.cert_file =
|
||||
make_string_ref(config->balloc, optarg);
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED_PRIVATE_KEY_FILE:
|
||||
config->tls.anti_replay.memcached.private_key_file =
|
||||
make_string_ref(config->balloc, optarg);
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_ADDRESS_FAMILY:
|
||||
return parse_address_family(&config->tls.ticket.memcached.family, opt,
|
||||
@@ -3164,6 +3379,9 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
||||
case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_ADDRESS_FAMILY:
|
||||
return parse_address_family(&config->tls.session_cache.memcached.family,
|
||||
opt, optarg);
|
||||
case SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED_ADDRESS_FAMILY:
|
||||
return parse_address_family(&config->tls.anti_replay.memcached.family, opt,
|
||||
optarg);
|
||||
case SHRPX_OPTID_BACKEND_ADDRESS_FAMILY:
|
||||
return parse_address_family(&config->conn.downstream->family, opt, optarg);
|
||||
case SHRPX_OPTID_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS:
|
||||
@@ -3344,8 +3562,9 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
||||
case SHRPX_OPTID_REDIRECT_HTTPS_PORT: {
|
||||
auto n = util::parse_uint(optarg);
|
||||
if (n == -1 || n < 0 || n > 65535) {
|
||||
LOG(ERROR) << opt << ": bad value. Specify an integer in the range [0, "
|
||||
"65535], inclusive";
|
||||
LOG(ERROR) << opt
|
||||
<< ": bad value. Specify an integer in the range [0, "
|
||||
"65535], inclusive";
|
||||
return -1;
|
||||
}
|
||||
config->http.redirect_https_port = optarg;
|
||||
@@ -3356,6 +3575,30 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
||||
case SHRPX_OPTID_SINGLE_THREAD:
|
||||
config->single_thread = util::strieq_l("yes", optarg);
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_SINGLE_PROCESS:
|
||||
config->single_process = util::strieq_l("yes", optarg);
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_NO_ADD_X_FORWARDED_PROTO:
|
||||
config->http.xfp.add = !util::strieq_l("yes", optarg);
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_NO_STRIP_INCOMING_X_FORWARDED_PROTO:
|
||||
config->http.xfp.strip_incoming = !util::strieq_l("yes", optarg);
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_OCSP_STARTUP:
|
||||
config->tls.ocsp.startup = util::strieq_l("yes", optarg);
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_NO_VERIFY_OCSP:
|
||||
config->tls.ocsp.no_verify = util::strieq_l("yes", optarg);
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_NO_STRIP_INCOMING_NGHTTPX_0RTT_UNIQ:
|
||||
config->http.zero_rtt_uniq.strip_incoming = !util::strieq_l("yes", optarg);
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_CONF:
|
||||
LOG(WARN) << "conf: ignored";
|
||||
@@ -3549,6 +3792,7 @@ StringRef strproto(shrpx_proto proto) {
|
||||
|
||||
// gcc needs this.
|
||||
assert(0);
|
||||
abort();
|
||||
}
|
||||
|
||||
namespace {
|
||||
@@ -3713,7 +3957,7 @@ int configure_downstream_group(Config *config, bool http2_proxy,
|
||||
}
|
||||
}
|
||||
|
||||
if (g.affinity == AFFINITY_IP) {
|
||||
if (g.affinity.type != AFFINITY_NONE) {
|
||||
size_t idx = 0;
|
||||
for (auto &addr : g.addrs) {
|
||||
StringRef key;
|
||||
|
||||
@@ -64,11 +64,11 @@ struct LogFragment;
|
||||
class ConnectBlocker;
|
||||
class Http2Session;
|
||||
|
||||
namespace ssl {
|
||||
namespace tls {
|
||||
|
||||
class CertLookupTree;
|
||||
|
||||
} // namespace ssl
|
||||
} // namespace tls
|
||||
|
||||
constexpr auto SHRPX_OPT_PRIVATE_KEY_FILE =
|
||||
StringRef::from_lit("private-key-file");
|
||||
@@ -336,6 +336,23 @@ constexpr auto SHRPX_OPT_REDIRECT_HTTPS_PORT =
|
||||
constexpr auto SHRPX_OPT_FRONTEND_MAX_REQUESTS =
|
||||
StringRef::from_lit("frontend-max-requests");
|
||||
constexpr auto SHRPX_OPT_SINGLE_THREAD = StringRef::from_lit("single-thread");
|
||||
constexpr auto SHRPX_OPT_SINGLE_PROCESS = StringRef::from_lit("single-process");
|
||||
constexpr auto SHRPX_OPT_NO_ADD_X_FORWARDED_PROTO =
|
||||
StringRef::from_lit("no-add-x-forwarded-proto");
|
||||
constexpr auto SHRPX_OPT_NO_STRIP_INCOMING_X_FORWARDED_PROTO =
|
||||
StringRef::from_lit("no-strip-incoming-x-forwarded-proto");
|
||||
constexpr auto SHRPX_OPT_OCSP_STARTUP = StringRef::from_lit("ocsp-startup");
|
||||
constexpr auto SHRPX_OPT_NO_VERIFY_OCSP = StringRef::from_lit("no-verify-ocsp");
|
||||
constexpr auto SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED =
|
||||
StringRef::from_lit("tls-anti-replay-memcached");
|
||||
constexpr auto SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED_CERT_FILE =
|
||||
StringRef::from_lit("tls-anti-replay-memcached-cert-file");
|
||||
constexpr auto SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED_PRIVATE_KEY_FILE =
|
||||
StringRef::from_lit("tls-anti-replay-memcached-private-key-file");
|
||||
constexpr auto SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED_ADDRESS_FAMILY =
|
||||
StringRef::from_lit("tls-anti-replay-memcached-address-family");
|
||||
constexpr auto SHRPX_OPT_NO_STRIP_INCOMING_NGHTTPX_0RTT_UNIQ =
|
||||
StringRef::from_lit("no-strip-incoming-nghttpx-0rtt-uniq");
|
||||
|
||||
constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8;
|
||||
|
||||
@@ -349,6 +366,31 @@ enum shrpx_session_affinity {
|
||||
AFFINITY_NONE,
|
||||
// Client IP affinity
|
||||
AFFINITY_IP,
|
||||
// Cookie based affinity
|
||||
AFFINITY_COOKIE,
|
||||
};
|
||||
|
||||
enum shrpx_cookie_secure {
|
||||
// Secure attribute of session affinity cookie is determined by the
|
||||
// request scheme.
|
||||
COOKIE_SECURE_AUTO,
|
||||
// Secure attribute of session affinity cookie is always set.
|
||||
COOKIE_SECURE_YES,
|
||||
// Secure attribute of session affinity cookie is always unset.
|
||||
COOKIE_SECURE_NO,
|
||||
};
|
||||
|
||||
struct AffinityConfig {
|
||||
// Type of session affinity.
|
||||
shrpx_session_affinity type;
|
||||
struct {
|
||||
// Name of a cookie to use.
|
||||
StringRef name;
|
||||
// Path which a cookie is applied to.
|
||||
StringRef path;
|
||||
// Secure attribute
|
||||
shrpx_cookie_secure secure;
|
||||
} cookie;
|
||||
};
|
||||
|
||||
enum shrpx_forwarded_param {
|
||||
@@ -399,6 +441,9 @@ struct UpstreamAddr {
|
||||
bool host_unix;
|
||||
// true if TLS is enabled.
|
||||
bool tls;
|
||||
// true if SNI host should be used as a host when selecting backend
|
||||
// server.
|
||||
bool sni_fwd;
|
||||
// true if client is supposed to send PROXY protocol v1 header.
|
||||
bool accept_proxy_protocol;
|
||||
int fd;
|
||||
@@ -439,15 +484,15 @@ struct AffinityHash {
|
||||
|
||||
struct DownstreamAddrGroupConfig {
|
||||
DownstreamAddrGroupConfig(const StringRef &pattern)
|
||||
: pattern(pattern), affinity(AFFINITY_NONE), redirect_if_not_tls(false) {}
|
||||
: pattern(pattern), affinity{AFFINITY_NONE}, redirect_if_not_tls(false) {}
|
||||
|
||||
StringRef pattern;
|
||||
std::vector<DownstreamAddrConfig> addrs;
|
||||
// Bunch of session affinity hash. Only used if affinity ==
|
||||
// AFFINITY_IP.
|
||||
std::vector<AffinityHash> affinity_hash;
|
||||
// Session affinity
|
||||
shrpx_session_affinity affinity;
|
||||
// Cookie based session affinity configuration.
|
||||
AffinityConfig affinity;
|
||||
// true if this group requires that client connection must be TLS,
|
||||
// and the request must be redirected to https URI.
|
||||
bool redirect_if_not_tls;
|
||||
@@ -542,6 +587,23 @@ struct TLSConfig {
|
||||
} memcached;
|
||||
} session_cache;
|
||||
|
||||
struct {
|
||||
struct {
|
||||
Address addr;
|
||||
uint16_t port;
|
||||
// Hostname of memcached server. This is also used as SNI field
|
||||
// if TLS is enabled.
|
||||
StringRef host;
|
||||
// Client private key and certificate for authentication
|
||||
StringRef private_key_file;
|
||||
StringRef cert_file;
|
||||
// Address family of memcached connection. One of either
|
||||
// AF_INET, AF_INET6 or AF_UNSPEC.
|
||||
int family;
|
||||
bool tls;
|
||||
} memcached;
|
||||
} anti_replay;
|
||||
|
||||
// Dynamic record sizing configurations
|
||||
struct {
|
||||
size_t warmup_threshold;
|
||||
@@ -553,6 +615,8 @@ struct TLSConfig {
|
||||
ev_tstamp update_interval;
|
||||
StringRef fetch_ocsp_response_file;
|
||||
bool disabled;
|
||||
bool startup;
|
||||
bool no_verify;
|
||||
} ocsp;
|
||||
|
||||
// Client verification configurations
|
||||
@@ -638,6 +702,13 @@ struct HttpConfig {
|
||||
bool add;
|
||||
bool strip_incoming;
|
||||
} xff;
|
||||
struct {
|
||||
bool add;
|
||||
bool strip_incoming;
|
||||
} xfp;
|
||||
struct {
|
||||
bool strip_incoming;
|
||||
} zero_rtt_uniq;
|
||||
std::vector<AltSvc> altsvcs;
|
||||
std::vector<ErrorPage> error_pages;
|
||||
HeaderRefs add_request_headers;
|
||||
@@ -864,6 +935,7 @@ struct Config {
|
||||
verbose{false},
|
||||
daemon{false},
|
||||
http2_proxy{false},
|
||||
single_process{false},
|
||||
single_thread{false},
|
||||
ev_loop_flags{0} {}
|
||||
~Config();
|
||||
@@ -905,6 +977,9 @@ struct Config {
|
||||
bool verbose;
|
||||
bool daemon;
|
||||
bool http2_proxy;
|
||||
// Run nghttpx in single process mode. With this mode, signal
|
||||
// handling is omitted.
|
||||
bool single_process;
|
||||
bool single_thread;
|
||||
// flags passed to ev_default_loop() and ev_loop_new()
|
||||
int ev_loop_flags;
|
||||
@@ -1018,6 +1093,7 @@ enum {
|
||||
SHRPX_OPTID_MAX_REQUEST_HEADER_FIELDS,
|
||||
SHRPX_OPTID_MAX_RESPONSE_HEADER_FIELDS,
|
||||
SHRPX_OPTID_MRUBY_FILE,
|
||||
SHRPX_OPTID_NO_ADD_X_FORWARDED_PROTO,
|
||||
SHRPX_OPTID_NO_HOST_REWRITE,
|
||||
SHRPX_OPTID_NO_HTTP2_CIPHER_BLACK_LIST,
|
||||
SHRPX_OPTID_NO_KQUEUE,
|
||||
@@ -1025,8 +1101,12 @@ enum {
|
||||
SHRPX_OPTID_NO_OCSP,
|
||||
SHRPX_OPTID_NO_SERVER_PUSH,
|
||||
SHRPX_OPTID_NO_SERVER_REWRITE,
|
||||
SHRPX_OPTID_NO_STRIP_INCOMING_NGHTTPX_0RTT_UNIQ,
|
||||
SHRPX_OPTID_NO_STRIP_INCOMING_X_FORWARDED_PROTO,
|
||||
SHRPX_OPTID_NO_VERIFY_OCSP,
|
||||
SHRPX_OPTID_NO_VIA,
|
||||
SHRPX_OPTID_NPN_LIST,
|
||||
SHRPX_OPTID_OCSP_STARTUP,
|
||||
SHRPX_OPTID_OCSP_UPDATE_INTERVAL,
|
||||
SHRPX_OPTID_PADDING,
|
||||
SHRPX_OPTID_PID_FILE,
|
||||
@@ -1040,6 +1120,7 @@ enum {
|
||||
SHRPX_OPTID_RESPONSE_HEADER_FIELD_BUFFER,
|
||||
SHRPX_OPTID_RLIMIT_NOFILE,
|
||||
SHRPX_OPTID_SERVER_NAME,
|
||||
SHRPX_OPTID_SINGLE_PROCESS,
|
||||
SHRPX_OPTID_SINGLE_THREAD,
|
||||
SHRPX_OPTID_STREAM_READ_TIMEOUT,
|
||||
SHRPX_OPTID_STREAM_WRITE_TIMEOUT,
|
||||
@@ -1047,6 +1128,10 @@ enum {
|
||||
SHRPX_OPTID_STRIP_INCOMING_X_FORWARDED_FOR,
|
||||
SHRPX_OPTID_SUBCERT,
|
||||
SHRPX_OPTID_SYSLOG_FACILITY,
|
||||
SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED,
|
||||
SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED_ADDRESS_FAMILY,
|
||||
SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED_CERT_FILE,
|
||||
SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED_PRIVATE_KEY_FILE,
|
||||
SHRPX_OPTID_TLS_DYN_REC_IDLE_TIMEOUT,
|
||||
SHRPX_OPTID_TLS_DYN_REC_WARMUP_THRESHOLD,
|
||||
SHRPX_OPTID_TLS_MAX_PROTO_VERSION,
|
||||
|
||||
@@ -163,14 +163,14 @@ void test_shrpx_config_parse_log_format(void) {
|
||||
void test_shrpx_config_read_tls_ticket_key_file(void) {
|
||||
char file1[] = "/tmp/nghttpx-unittest.XXXXXX";
|
||||
auto fd1 = mkstemp(file1);
|
||||
assert(fd1 != -1);
|
||||
assert(48 ==
|
||||
write(fd1, "0..............12..............34..............5", 48));
|
||||
CU_ASSERT(fd1 != -1);
|
||||
CU_ASSERT(48 ==
|
||||
write(fd1, "0..............12..............34..............5", 48));
|
||||
char file2[] = "/tmp/nghttpx-unittest.XXXXXX";
|
||||
auto fd2 = mkstemp(file2);
|
||||
assert(fd2 != -1);
|
||||
assert(48 ==
|
||||
write(fd2, "6..............78..............9a..............b", 48));
|
||||
CU_ASSERT(fd2 != -1);
|
||||
CU_ASSERT(48 ==
|
||||
write(fd2, "6..............78..............9a..............b", 48));
|
||||
|
||||
close(fd1);
|
||||
close(fd2);
|
||||
@@ -204,16 +204,18 @@ void test_shrpx_config_read_tls_ticket_key_file(void) {
|
||||
void test_shrpx_config_read_tls_ticket_key_file_aes_256(void) {
|
||||
char file1[] = "/tmp/nghttpx-unittest.XXXXXX";
|
||||
auto fd1 = mkstemp(file1);
|
||||
assert(fd1 != -1);
|
||||
assert(80 == write(fd1, "0..............12..............................34..."
|
||||
"...........................5",
|
||||
80));
|
||||
CU_ASSERT(fd1 != -1);
|
||||
CU_ASSERT(80 == write(fd1,
|
||||
"0..............12..............................34..."
|
||||
"...........................5",
|
||||
80));
|
||||
char file2[] = "/tmp/nghttpx-unittest.XXXXXX";
|
||||
auto fd2 = mkstemp(file2);
|
||||
assert(fd2 != -1);
|
||||
assert(80 == write(fd2, "6..............78..............................9a..."
|
||||
"...........................b",
|
||||
80));
|
||||
CU_ASSERT(fd2 != -1);
|
||||
CU_ASSERT(80 == write(fd2,
|
||||
"6..............78..............................9a..."
|
||||
"...........................b",
|
||||
80));
|
||||
|
||||
close(fd1);
|
||||
close(fd2);
|
||||
|
||||
@@ -81,6 +81,6 @@ private:
|
||||
bool offline_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
} // namespace shrpx
|
||||
|
||||
#endif // SHRPX_CONNECT_BLOCKER_H
|
||||
|
||||
@@ -33,12 +33,11 @@
|
||||
|
||||
#include <openssl/err.h>
|
||||
|
||||
#include "shrpx_ssl.h"
|
||||
#include "shrpx_tls.h"
|
||||
#include "shrpx_memcached_request.h"
|
||||
#include "shrpx_log.h"
|
||||
#include "memchunk.h"
|
||||
#include "util.h"
|
||||
#include "ssl_compat.h"
|
||||
|
||||
using namespace nghttp2;
|
||||
|
||||
@@ -60,7 +59,8 @@ Connection::Connection(struct ev_loop *loop, int fd, SSL *ssl,
|
||||
IOCb readcb, TimerCb timeoutcb, void *data,
|
||||
size_t tls_dyn_rec_warmup_threshold,
|
||||
ev_tstamp tls_dyn_rec_idle_timeout, shrpx_proto proto)
|
||||
: tls{DefaultMemchunks(mcpool), DefaultPeekMemchunks(mcpool)},
|
||||
: tls{DefaultMemchunks(mcpool), DefaultPeekMemchunks(mcpool),
|
||||
DefaultMemchunks(mcpool)},
|
||||
wlimit(loop, &wev, write_limit.rate, write_limit.burst),
|
||||
rlimit(loop, &rev, read_limit.rate, read_limit.burst, this),
|
||||
loop(loop),
|
||||
@@ -92,7 +92,15 @@ Connection::Connection(struct ev_loop *loop, int fd, SSL *ssl,
|
||||
}
|
||||
}
|
||||
|
||||
Connection::~Connection() { disconnect(); }
|
||||
Connection::~Connection() {
|
||||
disconnect();
|
||||
|
||||
#if OPENSSL_1_1_1_API
|
||||
if (tls.ch_md_ctx) {
|
||||
EVP_MD_CTX_free(tls.ch_md_ctx);
|
||||
}
|
||||
#endif // OPENSSL_1_1_1_API
|
||||
}
|
||||
|
||||
void Connection::disconnect() {
|
||||
if (tls.ssl) {
|
||||
@@ -110,19 +118,34 @@ void Connection::disconnect() {
|
||||
tls.cached_session_lookup_req = nullptr;
|
||||
}
|
||||
|
||||
if (tls.anti_replay_req) {
|
||||
tls.anti_replay_req->canceled = true;
|
||||
tls.anti_replay_req = nullptr;
|
||||
}
|
||||
|
||||
SSL_shutdown(tls.ssl);
|
||||
SSL_free(tls.ssl);
|
||||
tls.ssl = nullptr;
|
||||
|
||||
#if OPENSSL_1_1_1_API
|
||||
if (tls.ch_md_ctx) {
|
||||
EVP_MD_CTX_reset(tls.ch_md_ctx);
|
||||
}
|
||||
#endif // OPENSSL_1_1_1_API
|
||||
|
||||
tls.wbuf.reset();
|
||||
tls.rbuf.reset();
|
||||
tls.last_write_idle = 0.;
|
||||
tls.warmup_writelen = 0;
|
||||
tls.last_writelen = 0;
|
||||
tls.last_readlen = 0;
|
||||
tls.handshake_state = 0;
|
||||
tls.handshake_state = TLS_CONN_NORMAL;
|
||||
tls.initial_handshake_done = false;
|
||||
tls.reneg_started = false;
|
||||
tls.sct_requested = false;
|
||||
tls.early_data_finish = false;
|
||||
tls.early_cb_called = false;
|
||||
tls.postpone_early_data = false;
|
||||
}
|
||||
|
||||
if (fd != -1) {
|
||||
@@ -140,11 +163,23 @@ void Connection::disconnect() {
|
||||
wlimit.stopw();
|
||||
}
|
||||
|
||||
void Connection::prepare_client_handshake() { SSL_set_connect_state(tls.ssl); }
|
||||
void Connection::prepare_client_handshake() {
|
||||
SSL_set_connect_state(tls.ssl);
|
||||
// This prevents SSL_read_early_data from being called.
|
||||
tls.early_data_finish = true;
|
||||
}
|
||||
|
||||
void Connection::prepare_server_handshake() {
|
||||
SSL_set_accept_state(tls.ssl);
|
||||
tls.server_handshake = true;
|
||||
|
||||
#if OPENSSL_1_1_1_API
|
||||
if (!tls.ch_md_ctx) {
|
||||
tls.ch_md_ctx = EVP_MD_CTX_new();
|
||||
}
|
||||
|
||||
EVP_DigestInit_ex(tls.ch_md_ctx, EVP_sha256(), nullptr);
|
||||
#endif // OPENSSL_1_1_1_API
|
||||
}
|
||||
|
||||
// BIO implementation is inspired by openldap implementation:
|
||||
@@ -218,7 +253,19 @@ int shrpx_bio_read(BIO *b, char *buf, int len) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return rbuf.remove(buf, len);
|
||||
len = rbuf.remove(buf, len);
|
||||
|
||||
if (conn->tls.early_cb_called) {
|
||||
return len;
|
||||
}
|
||||
|
||||
#if OPENSSL_1_1_1_API
|
||||
if (EVP_DigestUpdate(conn->tls.ch_md_ctx, buf, len) == 0) {
|
||||
return -1;
|
||||
}
|
||||
#endif // OPENSSL_1_1_1_API
|
||||
|
||||
return len;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -326,8 +373,9 @@ int Connection::tls_handshake() {
|
||||
wlimit.stopw();
|
||||
ev_timer_stop(loop, &wt);
|
||||
|
||||
std::array<uint8_t, 16_k> buf;
|
||||
|
||||
if (ev_is_active(&rev)) {
|
||||
std::array<uint8_t, 8_k> buf;
|
||||
auto nread = read_clear(buf.data(), buf.size());
|
||||
if (nread < 0) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
@@ -347,6 +395,7 @@ int Connection::tls_handshake() {
|
||||
|
||||
switch (tls.handshake_state) {
|
||||
case TLS_CONN_WAIT_FOR_SESSION_CACHE:
|
||||
case TLS_CONN_WAIT_FOR_ANTI_REPLAY:
|
||||
return SHRPX_ERR_INPROGRESS;
|
||||
case TLS_CONN_GOT_SESSION_CACHE: {
|
||||
// Use the same trick invented by @kazuho in h2o project.
|
||||
@@ -360,7 +409,7 @@ int Connection::tls_handshake() {
|
||||
auto ssl_opts = SSL_get_options(tls.ssl);
|
||||
SSL_free(tls.ssl);
|
||||
|
||||
auto ssl = ssl::create_ssl(ssl_ctx);
|
||||
auto ssl = tls::create_ssl(ssl_ctx);
|
||||
if (!ssl) {
|
||||
return -1;
|
||||
}
|
||||
@@ -380,7 +429,73 @@ int Connection::tls_handshake() {
|
||||
break;
|
||||
}
|
||||
|
||||
auto rv = SSL_do_handshake(tls.ssl);
|
||||
int rv;
|
||||
|
||||
ERR_clear_error();
|
||||
|
||||
#if OPENSSL_1_1_1_API
|
||||
if (!tls.server_handshake || tls.early_data_finish) {
|
||||
rv = SSL_do_handshake(tls.ssl);
|
||||
} else {
|
||||
for (;;) {
|
||||
size_t nread;
|
||||
|
||||
rv = SSL_read_early_data(tls.ssl, buf.data(), buf.size(), &nread);
|
||||
if (rv == SSL_READ_EARLY_DATA_ERROR) {
|
||||
if (SSL_get_error(tls.ssl, rv) == SSL_ERROR_WANT_CLIENT_HELLO_CB) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO)
|
||||
<< "tls: early_cb returns negative return value; handshake "
|
||||
"interrupted";
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// If we have early data, and server sends ServerHello, assume
|
||||
// that handshake is completed in server side, and start
|
||||
// processing request. If we don't exit handshake code here,
|
||||
// server waits for EndOfEarlyData and Finished message from
|
||||
// client, which voids the purpose of 0-RTT data. The left
|
||||
// over of handshake is done through write_tls or read_tls.
|
||||
if (!tls.postpone_early_data &&
|
||||
(tls.handshake_state == TLS_CONN_WRITE_STARTED ||
|
||||
tls.wbuf.rleft()) &&
|
||||
tls.earlybuf.rleft()) {
|
||||
rv = 1;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "tls: read early data " << nread << " bytes";
|
||||
}
|
||||
|
||||
tls.earlybuf.append(buf.data(), nread);
|
||||
|
||||
if (rv == SSL_READ_EARLY_DATA_FINISH) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "tls: read all early data; total "
|
||||
<< tls.earlybuf.rleft() << " bytes";
|
||||
}
|
||||
tls.early_data_finish = true;
|
||||
// The same reason stated above.
|
||||
if (!tls.postpone_early_data &&
|
||||
(tls.handshake_state == TLS_CONN_WRITE_STARTED ||
|
||||
tls.wbuf.rleft()) &&
|
||||
tls.earlybuf.rleft()) {
|
||||
rv = 1;
|
||||
} else {
|
||||
ERR_clear_error();
|
||||
rv = SSL_do_handshake(tls.ssl);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else // !OPENSSL_1_1_1_API
|
||||
rv = SSL_do_handshake(tls.ssl);
|
||||
#endif // !OPENSSL_1_1_1_API
|
||||
|
||||
if (rv <= 0) {
|
||||
auto err = SSL_get_error(tls.ssl, rv);
|
||||
@@ -394,6 +509,9 @@ int Connection::tls_handshake() {
|
||||
}
|
||||
break;
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
#if OPENSSL_1_1_1_API
|
||||
case SSL_ERROR_WANT_CLIENT_HELLO_CB:
|
||||
#endif // OPENSSL_1_1_1_API
|
||||
break;
|
||||
case SSL_ERROR_SSL:
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
@@ -409,7 +527,8 @@ int Connection::tls_handshake() {
|
||||
}
|
||||
}
|
||||
|
||||
if (tls.handshake_state == TLS_CONN_WAIT_FOR_SESSION_CACHE) {
|
||||
if (tls.handshake_state == TLS_CONN_WAIT_FOR_SESSION_CACHE ||
|
||||
tls.handshake_state == TLS_CONN_WAIT_FOR_ANTI_REPLAY) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "tls: handshake is still in progress";
|
||||
}
|
||||
@@ -502,8 +621,8 @@ int Connection::write_tls_pending_handshake() {
|
||||
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "SSL/TLS handshake completed";
|
||||
nghttp2::ssl::TLSSessionInfo tls_info{};
|
||||
if (nghttp2::ssl::get_tls_session_info(&tls_info, tls.ssl)) {
|
||||
nghttp2::tls::TLSSessionInfo tls_info{};
|
||||
if (nghttp2::tls::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")
|
||||
@@ -530,7 +649,7 @@ int Connection::check_http2_requirement() {
|
||||
!util::check_h2_is_selected(StringRef{next_proto, next_proto_len})) {
|
||||
return 0;
|
||||
}
|
||||
if (!nghttp2::ssl::check_http2_tls_version(tls.ssl)) {
|
||||
if (!nghttp2::tls::check_http2_tls_version(tls.ssl)) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "TLSv1.2 was not negotiated. HTTP/2 must not be used.";
|
||||
}
|
||||
@@ -545,7 +664,7 @@ int Connection::check_http2_requirement() {
|
||||
}
|
||||
|
||||
if (check_black_list &&
|
||||
nghttp2::ssl::check_http2_cipher_black_list(tls.ssl)) {
|
||||
nghttp2::tls::check_http2_cipher_black_list(tls.ssl)) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "The negotiated cipher suite is in HTTP/2 cipher suite "
|
||||
"black list. HTTP/2 must not be used.";
|
||||
@@ -614,7 +733,23 @@ ssize_t Connection::write_tls(const void *data, size_t len) {
|
||||
|
||||
tls.last_write_idle = -1.;
|
||||
|
||||
ERR_clear_error();
|
||||
|
||||
#if OPENSSL_1_1_1_API
|
||||
int rv;
|
||||
if (SSL_is_init_finished(tls.ssl)) {
|
||||
rv = SSL_write(tls.ssl, data, len);
|
||||
} else {
|
||||
size_t nwrite;
|
||||
rv = SSL_write_early_data(tls.ssl, data, len, &nwrite);
|
||||
// Use the same semantics with SSL_write.
|
||||
if (rv == 1) {
|
||||
rv = nwrite;
|
||||
}
|
||||
}
|
||||
#else // !OPENSSL_1_1_1_API
|
||||
auto rv = SSL_write(tls.ssl, data, len);
|
||||
#endif // !OPENSSL_1_1_1_API
|
||||
|
||||
if (rv <= 0) {
|
||||
auto err = SSL_get_error(tls.ssl, rv);
|
||||
@@ -649,6 +784,14 @@ ssize_t Connection::write_tls(const void *data, size_t len) {
|
||||
}
|
||||
|
||||
ssize_t Connection::read_tls(void *data, size_t len) {
|
||||
ERR_clear_error();
|
||||
|
||||
#if OPENSSL_1_1_1_API
|
||||
if (tls.earlybuf.rleft()) {
|
||||
return tls.earlybuf.remove(data, len);
|
||||
}
|
||||
#endif // OPENSSL_1_1_1_API
|
||||
|
||||
// SSL_read requires the same arguments (buf pointer and its
|
||||
// length) on SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE.
|
||||
// rlimit_.avail() or rlimit_.avail() may return different length
|
||||
@@ -666,6 +809,47 @@ ssize_t Connection::read_tls(void *data, size_t len) {
|
||||
tls.last_readlen = 0;
|
||||
}
|
||||
|
||||
#if OPENSSL_1_1_1_API
|
||||
if (!tls.early_data_finish) {
|
||||
// TLSv1.3 handshake is still going on.
|
||||
size_t nread;
|
||||
auto rv = SSL_read_early_data(tls.ssl, data, len, &nread);
|
||||
if (rv == SSL_READ_EARLY_DATA_ERROR) {
|
||||
auto err = SSL_get_error(tls.ssl, rv);
|
||||
switch (err) {
|
||||
case SSL_ERROR_WANT_READ:
|
||||
tls.last_readlen = len;
|
||||
return 0;
|
||||
case SSL_ERROR_SSL:
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "SSL_read: "
|
||||
<< ERR_error_string(ERR_get_error(), nullptr);
|
||||
}
|
||||
return SHRPX_ERR_NETWORK;
|
||||
default:
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "SSL_read: SSL_get_error returned " << err;
|
||||
}
|
||||
return SHRPX_ERR_NETWORK;
|
||||
}
|
||||
}
|
||||
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "tls: read early data " << nread << " bytes";
|
||||
}
|
||||
|
||||
if (rv == SSL_READ_EARLY_DATA_FINISH) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "tls: read all early data";
|
||||
}
|
||||
tls.early_data_finish = true;
|
||||
// We may have stopped write watcher in write_tls.
|
||||
wlimit.startw();
|
||||
}
|
||||
return nread;
|
||||
}
|
||||
#endif // OPENSSL_1_1_1_API
|
||||
|
||||
auto rv = SSL_read(tls.ssl, data, len);
|
||||
|
||||
if (rv <= 0) {
|
||||
@@ -800,11 +984,25 @@ int Connection::get_tcp_hint(TCPHint *hint) const {
|
||||
: 0;
|
||||
|
||||
// http://www.slideshare.net/kazuho/programming-tcp-for-responsiveness
|
||||
|
||||
// TODO 29 (5 (header) + 8 (explicit nonce) + 16 (tag)) is TLS
|
||||
// overhead for AES-GCM. For CHACHA20_POLY1305, it is 21 since it
|
||||
// does not need 8 bytes explicit nonce.
|
||||
//
|
||||
// TODO 29 (5 + 8 + 16) is TLS overhead for AES-GCM. For
|
||||
// CHACHA20_POLY1305, it is 21 since it does not need 8 bytes
|
||||
// explicit nonce.
|
||||
auto writable_size = (avail_packets + 2) * (tcp_info.tcpi_snd_mss - 29);
|
||||
// For TLSv1.3, AES-GCM and CHACHA20_POLY1305 overhead are now 22
|
||||
// bytes (5 (header) + 1 (ContentType) + 16 (tag)).
|
||||
size_t tls_overhead;
|
||||
#ifdef TLS1_3_VERSION
|
||||
if (SSL_version(tls.ssl) == TLS1_3_VERSION) {
|
||||
tls_overhead = 22;
|
||||
} else
|
||||
#endif // TLS1_3_VERSION
|
||||
{
|
||||
tls_overhead = 29;
|
||||
}
|
||||
|
||||
auto writable_size =
|
||||
(avail_packets + 2) * (tcp_info.tcpi_snd_mss - tls_overhead);
|
||||
if (writable_size > 16_k) {
|
||||
writable_size = writable_size & ~(16_k - 1);
|
||||
} else {
|
||||
@@ -812,7 +1010,7 @@ int Connection::get_tcp_hint(TCPHint *hint) const {
|
||||
LOG(INFO) << "writable_size is too small: " << writable_size;
|
||||
}
|
||||
// TODO is this required?
|
||||
writable_size = std::max(writable_size, static_cast<uint32_t>(536 * 2));
|
||||
writable_size = std::max(writable_size, static_cast<size_t>(536 * 2));
|
||||
}
|
||||
|
||||
// if (LOG_ENABLED(INFO)) {
|
||||
|
||||
@@ -32,29 +32,46 @@
|
||||
#include <ev.h>
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/evp.h>
|
||||
|
||||
#include "shrpx_rate_limit.h"
|
||||
#include "shrpx_error.h"
|
||||
#include "memchunk.h"
|
||||
#include "ssl_compat.h"
|
||||
|
||||
namespace shrpx {
|
||||
|
||||
struct MemcachedRequest;
|
||||
|
||||
namespace tls {
|
||||
struct TLSSessionCache;
|
||||
} // namespace tls
|
||||
|
||||
enum {
|
||||
TLS_CONN_NORMAL,
|
||||
TLS_CONN_WAIT_FOR_SESSION_CACHE,
|
||||
TLS_CONN_GOT_SESSION_CACHE,
|
||||
TLS_CONN_CANCEL_SESSION_CACHE,
|
||||
TLS_CONN_WAIT_FOR_ANTI_REPLAY,
|
||||
TLS_CONN_WRITE_STARTED,
|
||||
};
|
||||
|
||||
struct TLSConnection {
|
||||
DefaultMemchunks wbuf;
|
||||
DefaultPeekMemchunks rbuf;
|
||||
// Stores TLSv1.3 early data.
|
||||
DefaultMemchunks earlybuf;
|
||||
// Message digest of ClientHello in hex string.
|
||||
StringRef ch_hex_md;
|
||||
SSL *ssl;
|
||||
SSL_SESSION *cached_session;
|
||||
MemcachedRequest *cached_session_lookup_req;
|
||||
tls::TLSSessionCache *client_session_cache;
|
||||
#if OPENSSL_1_1_1_API
|
||||
// Message digest context to calculate ClientHello for anti-replay.
|
||||
EVP_MD_CTX *ch_md_ctx;
|
||||
#endif // !OPENSSL_1_1_1_API
|
||||
MemcachedRequest *anti_replay_req;
|
||||
ev_tstamp last_write_idle;
|
||||
size_t warmup_writelen;
|
||||
// length passed to SSL_write and SSL_read last time. This is
|
||||
@@ -66,6 +83,20 @@ struct TLSConnection {
|
||||
bool reneg_started;
|
||||
// true if ssl is prepared to do handshake as server.
|
||||
bool server_handshake;
|
||||
// true if ssl is initialized as server, and client requested
|
||||
// signed_certificate_timestamp extension.
|
||||
bool sct_requested;
|
||||
// true if TLSv1.3 early data has been completely received. Since
|
||||
// SSL_read_early_data acts like SSL_do_handshake, this field may be
|
||||
// true even if the negotiated TLS version is TLSv1.2 or earlier.
|
||||
// This value is also true if this is client side connection for
|
||||
// convenience.
|
||||
bool early_data_finish;
|
||||
// true if early_cb gets called.
|
||||
bool early_cb_called;
|
||||
// true if processing early data should be postponed until handshake
|
||||
// finishes.
|
||||
bool postpone_early_data;
|
||||
};
|
||||
|
||||
struct TCPHint {
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
#include <random>
|
||||
|
||||
#include "shrpx_client_handler.h"
|
||||
#include "shrpx_ssl.h"
|
||||
#include "shrpx_tls.h"
|
||||
#include "shrpx_worker.h"
|
||||
#include "shrpx_config.h"
|
||||
#include "shrpx_http2_session.h"
|
||||
@@ -118,7 +118,8 @@ ConnectionHandler::ConnectionHandler(struct ev_loop *loop, std::mt19937 &gen)
|
||||
tls_ticket_key_memcached_get_retry_count_(0),
|
||||
tls_ticket_key_memcached_fail_count_(0),
|
||||
worker_round_robin_cnt_(get_config()->api.enabled ? 1 : 0),
|
||||
graceful_shutdown_(false) {
|
||||
graceful_shutdown_(false),
|
||||
enable_acceptor_on_ocsp_completion_(false) {
|
||||
ev_timer_init(&disable_acceptor_timer_, acceptor_disable_cb, 0., 0.);
|
||||
disable_acceptor_timer_.data = this;
|
||||
|
||||
@@ -154,7 +155,7 @@ ConnectionHandler::~ConnectionHandler() {
|
||||
|
||||
for (auto ssl_ctx : all_ssl_ctx_) {
|
||||
auto tls_ctx_data =
|
||||
static_cast<ssl::TLSContextData *>(SSL_CTX_get_app_data(ssl_ctx));
|
||||
static_cast<tls::TLSContextData *>(SSL_CTX_get_app_data(ssl_ctx));
|
||||
if (tls_ctx_data) {
|
||||
delete tls_ctx_data;
|
||||
}
|
||||
@@ -199,19 +200,19 @@ void ConnectionHandler::worker_replace_downstream(
|
||||
}
|
||||
|
||||
int ConnectionHandler::create_single_worker() {
|
||||
cert_tree_ = ssl::create_cert_lookup_tree();
|
||||
auto sv_ssl_ctx = ssl::setup_server_ssl_context(
|
||||
cert_tree_ = tls::create_cert_lookup_tree();
|
||||
auto sv_ssl_ctx = tls::setup_server_ssl_context(
|
||||
all_ssl_ctx_, indexed_ssl_ctx_, cert_tree_.get()
|
||||
#ifdef HAVE_NEVERBLEED
|
||||
,
|
||||
nb_.get()
|
||||
#endif // HAVE_NEVERBLEED
|
||||
);
|
||||
auto cl_ssl_ctx = ssl::setup_downstream_client_ssl_context(
|
||||
);
|
||||
auto cl_ssl_ctx = tls::setup_downstream_client_ssl_context(
|
||||
#ifdef HAVE_NEVERBLEED
|
||||
nb_.get()
|
||||
#endif // HAVE_NEVERBLEED
|
||||
);
|
||||
);
|
||||
|
||||
if (cl_ssl_ctx) {
|
||||
all_ssl_ctx_.push_back(cl_ssl_ctx);
|
||||
@@ -219,22 +220,39 @@ int ConnectionHandler::create_single_worker() {
|
||||
|
||||
auto config = get_config();
|
||||
auto &tlsconf = config->tls;
|
||||
auto &memcachedconf = config->tls.session_cache.memcached;
|
||||
|
||||
SSL_CTX *session_cache_ssl_ctx = nullptr;
|
||||
if (memcachedconf.tls) {
|
||||
session_cache_ssl_ctx = ssl::create_ssl_client_context(
|
||||
{
|
||||
auto &memcachedconf = config->tls.session_cache.memcached;
|
||||
if (memcachedconf.tls) {
|
||||
session_cache_ssl_ctx = tls::create_ssl_client_context(
|
||||
#ifdef HAVE_NEVERBLEED
|
||||
nb_.get(),
|
||||
nb_.get(),
|
||||
#endif // HAVE_NEVERBLEED
|
||||
tlsconf.cacert, memcachedconf.cert_file, memcachedconf.private_key_file,
|
||||
nullptr);
|
||||
all_ssl_ctx_.push_back(session_cache_ssl_ctx);
|
||||
tlsconf.cacert, memcachedconf.cert_file,
|
||||
memcachedconf.private_key_file, nullptr);
|
||||
all_ssl_ctx_.push_back(session_cache_ssl_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
SSL_CTX *anti_replay_ssl_ctx = nullptr;
|
||||
{
|
||||
auto &memcachedconf = config->tls.anti_replay.memcached;
|
||||
|
||||
if (memcachedconf.tls) {
|
||||
anti_replay_ssl_ctx = tls::create_ssl_client_context(
|
||||
#ifdef HAVE_NEVERBLEED
|
||||
nb_.get(),
|
||||
#endif // HAVE_NEVERBLEED
|
||||
tlsconf.cacert, memcachedconf.cert_file,
|
||||
memcachedconf.private_key_file, nullptr);
|
||||
all_ssl_ctx_.push_back(anti_replay_ssl_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
single_worker_ = make_unique<Worker>(
|
||||
loop_, sv_ssl_ctx, cl_ssl_ctx, session_cache_ssl_ctx, cert_tree_.get(),
|
||||
ticket_keys_, this, config->conn.downstream);
|
||||
loop_, sv_ssl_ctx, cl_ssl_ctx, session_cache_ssl_ctx, anti_replay_ssl_ctx,
|
||||
cert_tree_.get(), ticket_keys_, this, config->conn.downstream);
|
||||
#ifdef HAVE_MRUBY
|
||||
if (single_worker_->create_mruby_context() != 0) {
|
||||
return -1;
|
||||
@@ -248,19 +266,19 @@ int ConnectionHandler::create_worker_thread(size_t num) {
|
||||
#ifndef NOTHREADS
|
||||
assert(workers_.size() == 0);
|
||||
|
||||
cert_tree_ = ssl::create_cert_lookup_tree();
|
||||
auto sv_ssl_ctx = ssl::setup_server_ssl_context(
|
||||
cert_tree_ = tls::create_cert_lookup_tree();
|
||||
auto sv_ssl_ctx = tls::setup_server_ssl_context(
|
||||
all_ssl_ctx_, indexed_ssl_ctx_, cert_tree_.get()
|
||||
#ifdef HAVE_NEVERBLEED
|
||||
,
|
||||
nb_.get()
|
||||
#endif // HAVE_NEVERBLEED
|
||||
);
|
||||
auto cl_ssl_ctx = ssl::setup_downstream_client_ssl_context(
|
||||
);
|
||||
auto cl_ssl_ctx = tls::setup_downstream_client_ssl_context(
|
||||
#ifdef HAVE_NEVERBLEED
|
||||
nb_.get()
|
||||
#endif // HAVE_NEVERBLEED
|
||||
);
|
||||
);
|
||||
|
||||
if (cl_ssl_ctx) {
|
||||
all_ssl_ctx_.push_back(cl_ssl_ctx);
|
||||
@@ -268,7 +286,6 @@ int ConnectionHandler::create_worker_thread(size_t num) {
|
||||
|
||||
auto config = get_config();
|
||||
auto &tlsconf = config->tls;
|
||||
auto &memcachedconf = config->tls.session_cache.memcached;
|
||||
auto &apiconf = config->api;
|
||||
|
||||
// We have dedicated worker for API request processing.
|
||||
@@ -276,12 +293,12 @@ int ConnectionHandler::create_worker_thread(size_t num) {
|
||||
++num;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < num; ++i) {
|
||||
auto loop = ev_loop_new(config->ev_loop_flags);
|
||||
SSL_CTX *session_cache_ssl_ctx = nullptr;
|
||||
{
|
||||
auto &memcachedconf = config->tls.session_cache.memcached;
|
||||
|
||||
SSL_CTX *session_cache_ssl_ctx = nullptr;
|
||||
if (memcachedconf.tls) {
|
||||
session_cache_ssl_ctx = ssl::create_ssl_client_context(
|
||||
session_cache_ssl_ctx = tls::create_ssl_client_context(
|
||||
#ifdef HAVE_NEVERBLEED
|
||||
nb_.get(),
|
||||
#endif // HAVE_NEVERBLEED
|
||||
@@ -289,9 +306,30 @@ int ConnectionHandler::create_worker_thread(size_t num) {
|
||||
memcachedconf.private_key_file, nullptr);
|
||||
all_ssl_ctx_.push_back(session_cache_ssl_ctx);
|
||||
}
|
||||
auto worker = make_unique<Worker>(
|
||||
loop, sv_ssl_ctx, cl_ssl_ctx, session_cache_ssl_ctx, cert_tree_.get(),
|
||||
ticket_keys_, this, config->conn.downstream);
|
||||
}
|
||||
|
||||
SSL_CTX *anti_replay_ssl_ctx = nullptr;
|
||||
{
|
||||
auto &memcachedconf = config->tls.anti_replay.memcached;
|
||||
|
||||
if (memcachedconf.tls) {
|
||||
anti_replay_ssl_ctx = tls::create_ssl_client_context(
|
||||
#ifdef HAVE_NEVERBLEED
|
||||
nb_.get(),
|
||||
#endif // HAVE_NEVERBLEED
|
||||
tlsconf.cacert, memcachedconf.cert_file,
|
||||
memcachedconf.private_key_file, nullptr);
|
||||
all_ssl_ctx_.push_back(anti_replay_ssl_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < num; ++i) {
|
||||
auto loop = ev_loop_new(config->ev_loop_flags);
|
||||
|
||||
auto worker =
|
||||
make_unique<Worker>(loop, sv_ssl_ctx, cl_ssl_ctx, session_cache_ssl_ctx,
|
||||
anti_replay_ssl_ctx, cert_tree_.get(), ticket_keys_,
|
||||
this, config->conn.downstream);
|
||||
#ifdef HAVE_MRUBY
|
||||
if (worker->create_mruby_context() != 0) {
|
||||
return -1;
|
||||
@@ -384,7 +422,7 @@ int ConnectionHandler::handle_connection(int fd, sockaddr *addr, int addrlen,
|
||||
}
|
||||
|
||||
auto client =
|
||||
ssl::accept_connection(single_worker_.get(), fd, addr, addrlen, faddr);
|
||||
tls::accept_connection(single_worker_.get(), fd, addr, addrlen, faddr);
|
||||
if (!client) {
|
||||
LLOG(ERROR, this) << "ClientHandler creation failed";
|
||||
|
||||
@@ -498,6 +536,9 @@ bool ConnectionHandler::get_graceful_shutdown() const {
|
||||
}
|
||||
|
||||
void ConnectionHandler::cancel_ocsp_update() {
|
||||
enable_acceptor_on_ocsp_completion_ = false;
|
||||
ev_timer_stop(loop_, &ocsp_timer_);
|
||||
|
||||
if (ocsp_.proc.pid == 0) {
|
||||
return;
|
||||
}
|
||||
@@ -592,7 +633,7 @@ void ConnectionHandler::handle_ocsp_complete() {
|
||||
|
||||
auto ssl_ctx = all_ssl_ctx_[ocsp_.next];
|
||||
auto tls_ctx_data =
|
||||
static_cast<ssl::TLSContextData *>(SSL_CTX_get_app_data(ssl_ctx));
|
||||
static_cast<tls::TLSContextData *>(SSL_CTX_get_app_data(ssl_ctx));
|
||||
|
||||
auto rstatus = ocsp_.chldev.rstatus;
|
||||
auto status = WEXITSTATUS(rstatus);
|
||||
@@ -610,8 +651,13 @@ void ConnectionHandler::handle_ocsp_complete() {
|
||||
<< " finished successfully";
|
||||
}
|
||||
|
||||
auto config = get_config();
|
||||
auto &tlsconf = config->tls;
|
||||
|
||||
if (tlsconf.ocsp.no_verify ||
|
||||
tls::verify_ocsp_response(ssl_ctx, ocsp_.resp.data(),
|
||||
ocsp_.resp.size()) == 0) {
|
||||
#ifndef OPENSSL_IS_BORINGSSL
|
||||
{
|
||||
#ifdef HAVE_ATOMIC_STD_SHARED_PTR
|
||||
std::atomic_store_explicit(
|
||||
&tls_ctx_data->ocsp_data,
|
||||
@@ -622,10 +668,10 @@ void ConnectionHandler::handle_ocsp_complete() {
|
||||
tls_ctx_data->ocsp_data =
|
||||
std::make_shared<std::vector<uint8_t>>(std::move(ocsp_.resp));
|
||||
#endif // !HAVE_ATOMIC_STD_SHARED_PTR
|
||||
}
|
||||
#else // OPENSSL_IS_BORINGSSL
|
||||
SSL_CTX_set_ocsp_response(ssl_ctx, ocsp_.resp.data(), ocsp_.resp.size());
|
||||
SSL_CTX_set_ocsp_response(ssl_ctx, ocsp_.resp.data(), ocsp_.resp.size());
|
||||
#endif // OPENSSL_IS_BORINGSSL
|
||||
}
|
||||
|
||||
++ocsp_.next;
|
||||
proceed_next_cert_ocsp();
|
||||
@@ -650,12 +696,18 @@ void ConnectionHandler::proceed_next_cert_ocsp() {
|
||||
// We have updated all ocsp response, and schedule next update.
|
||||
ev_timer_set(&ocsp_timer_, get_config()->tls.ocsp.update_interval, 0.);
|
||||
ev_timer_start(loop_, &ocsp_timer_);
|
||||
|
||||
if (enable_acceptor_on_ocsp_completion_) {
|
||||
enable_acceptor_on_ocsp_completion_ = false;
|
||||
enable_acceptor();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto ssl_ctx = all_ssl_ctx_[ocsp_.next];
|
||||
auto tls_ctx_data =
|
||||
static_cast<ssl::TLSContextData *>(SSL_CTX_get_app_data(ssl_ctx));
|
||||
static_cast<tls::TLSContextData *>(SSL_CTX_get_app_data(ssl_ctx));
|
||||
|
||||
// client SSL_CTX is also included in all_ssl_ctx_, but has no
|
||||
// tls_ctx_data.
|
||||
@@ -777,7 +829,7 @@ SSL_CTX *ConnectionHandler::create_tls_ticket_key_memcached_ssl_ctx() {
|
||||
auto &tlsconf = config->tls;
|
||||
auto &memcachedconf = config->tls.ticket.memcached;
|
||||
|
||||
auto ssl_ctx = ssl::create_ssl_client_context(
|
||||
auto ssl_ctx = tls::create_ssl_client_context(
|
||||
#ifdef HAVE_NEVERBLEED
|
||||
nb_.get(),
|
||||
#endif // HAVE_NEVERBLEED
|
||||
@@ -849,4 +901,8 @@ ConnectionHandler::get_indexed_ssl_ctx(size_t idx) const {
|
||||
return indexed_ssl_ctx_[idx];
|
||||
}
|
||||
|
||||
void ConnectionHandler::set_enable_acceptor_on_ocsp_completion(bool f) {
|
||||
enable_acceptor_on_ocsp_completion_ = f;
|
||||
}
|
||||
|
||||
} // namespace shrpx
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user