mirror of
https://github.com/nghttp2/nghttp2.git
synced 2025-12-07 02:28:53 +08:00
Compare commits
342 Commits
gh-actions
...
v1.45.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
53dfaad925 | ||
|
|
2f2da110a1 | ||
|
|
8a14435aa8 | ||
|
|
dbaa59908e | ||
|
|
ca41f2faab | ||
|
|
daae20d4a5 | ||
|
|
6d9667dc74 | ||
|
|
58499f256b | ||
|
|
afb455ef80 | ||
|
|
f4515e9034 | ||
|
|
aab07d00d7 | ||
|
|
32ecfc6a86 | ||
|
|
e866f9fae7 | ||
|
|
a029f6ed2c | ||
|
|
5b6e2cb5e0 | ||
|
|
0264847a37 | ||
|
|
d276ca0adc | ||
|
|
6a099ee50a | ||
|
|
be88846972 | ||
|
|
9a6b623c25 | ||
|
|
97b36b8c74 | ||
|
|
0df332e7b8 | ||
|
|
2d7e6fbb11 | ||
|
|
fd107ab47c | ||
|
|
1320d7efab | ||
|
|
7cdc6cfa6d | ||
|
|
095ee9683d | ||
|
|
1e2081a1c5 | ||
|
|
e167e07a9a | ||
|
|
f3b9cd8404 | ||
|
|
8f9744c07b | ||
|
|
684a219e39 | ||
|
|
e2e6d827c7 | ||
|
|
f0108ece6f | ||
|
|
789b7a5ff1 | ||
|
|
0961295a82 | ||
|
|
fd060eb9f1 | ||
|
|
1feeda4514 | ||
|
|
6d29de0f1e | ||
|
|
74162850f0 | ||
|
|
8903bd1e8a | ||
|
|
4b79a4a10d | ||
|
|
8f419a4869 | ||
|
|
fcdac50f79 | ||
|
|
4541134c88 | ||
|
|
b5e5972c2a | ||
|
|
525d59fdf6 | ||
|
|
00f65afe20 | ||
|
|
fc402f5804 | ||
|
|
f74b6d9a43 | ||
|
|
ccaf2333ca | ||
|
|
0066bf8eed | ||
|
|
bc8f88f5fa | ||
|
|
10c9d917ad | ||
|
|
cc5f752f2d | ||
|
|
39b1a51ff4 | ||
|
|
a2e2e46af3 | ||
|
|
9d53a7e0a6 | ||
|
|
7ea57eaa18 | ||
|
|
1657a425c1 | ||
|
|
e929e92245 | ||
|
|
5994e48b28 | ||
|
|
50662c9c9e | ||
|
|
addd614e94 | ||
|
|
fbb228050a | ||
|
|
9bda8e266e | ||
|
|
d977005126 | ||
|
|
8b579bc7d0 | ||
|
|
ab16a11aa3 | ||
|
|
85347e12de | ||
|
|
67afbbbaa6 | ||
|
|
b743ee21f0 | ||
|
|
72702a042e | ||
|
|
649c69fa9e | ||
|
|
9fd0b87925 | ||
|
|
1c7a001489 | ||
|
|
47edc33b0d | ||
|
|
2afad0c650 | ||
|
|
fb53a6a686 | ||
|
|
31b5b78dc1 | ||
|
|
2f941c7fb3 | ||
|
|
ba483b4032 | ||
|
|
977b0ceee4 | ||
|
|
fcc20334da | ||
|
|
83c063346d | ||
|
|
c2e29ad06f | ||
|
|
9194d40da7 | ||
|
|
002073ef57 | ||
|
|
ef3066a1bd | ||
|
|
65db5b94e4 | ||
|
|
3122038c48 | ||
|
|
54fd0efdfe | ||
|
|
f0d1e50d5a | ||
|
|
a87ea20b7c | ||
|
|
8e7e40d0cc | ||
|
|
de4d4f6609 | ||
|
|
e01d61484d | ||
|
|
51f83087f2 | ||
|
|
17012654e1 | ||
|
|
e998d125ab | ||
|
|
95601d3179 | ||
|
|
0566a5833b | ||
|
|
c50459b81a | ||
|
|
0e52cf76eb | ||
|
|
0baf725073 | ||
|
|
e77fd7ddb9 | ||
|
|
e5cb5dca61 | ||
|
|
7941b559c5 | ||
|
|
58d81dbc52 | ||
|
|
2b4dc4496f | ||
|
|
c5e9d0096a | ||
|
|
c6f9780b1b | ||
|
|
ef694923f7 | ||
|
|
8d02203bb6 | ||
|
|
1e75be3b5d | ||
|
|
7d13891066 | ||
|
|
4292bd7ad9 | ||
|
|
82cd110dbe | ||
|
|
d2729193c7 | ||
|
|
87fb325357 | ||
|
|
fb8ff7b892 | ||
|
|
5aeae7444f | ||
|
|
c9b11e9fbf | ||
|
|
0005efa508 | ||
|
|
6931cb9d65 | ||
|
|
c1bcf0f11a | ||
|
|
717e7ae8b2 | ||
|
|
bed00fb8e1 | ||
|
|
2010401b81 | ||
|
|
23e09e3b3c | ||
|
|
80c9d46b70 | ||
|
|
0aa107426c | ||
|
|
1517c77d9c | ||
|
|
51bf79bb8c | ||
|
|
d88eadff13 | ||
|
|
0d35e8e15e | ||
|
|
a0066a1ccf | ||
|
|
7a5082e8c4 | ||
|
|
dfc345756c | ||
|
|
137da6adf6 | ||
|
|
8563ec5a7a | ||
|
|
8ac4bee3bc | ||
|
|
579fb478b5 | ||
|
|
33c580ebbf | ||
|
|
ff389b3e97 | ||
|
|
50fe8e7852 | ||
|
|
cdb6d19989 | ||
|
|
29694e2945 | ||
|
|
9fe08d3913 | ||
|
|
c07a0d9005 | ||
|
|
cbd45478e0 | ||
|
|
6f243108e9 | ||
|
|
0dcdf7ae21 | ||
|
|
e7ef2bec8b | ||
|
|
4f4dce82c6 | ||
|
|
a619e7a88c | ||
|
|
102d960106 | ||
|
|
7de71b29a0 | ||
|
|
4eced8a393 | ||
|
|
710b9c35e5 | ||
|
|
f46984d218 | ||
|
|
44663a7e6e | ||
|
|
446124f378 | ||
|
|
c45f2085d5 | ||
|
|
3abf62b41a | ||
|
|
9b2982510e | ||
|
|
48bb1ebe01 | ||
|
|
fe4c6e4c56 | ||
|
|
37bd9ffc48 | ||
|
|
b0548b4944 | ||
|
|
12425556c1 | ||
|
|
3ed2da562b | ||
|
|
354f46d8c5 | ||
|
|
e70f0db83c | ||
|
|
49b8c56fde | ||
|
|
940fdd5573 | ||
|
|
ef53db201e | ||
|
|
aeb0b0728d | ||
|
|
8b2746abf1 | ||
|
|
01da060496 | ||
|
|
20cbd269c4 | ||
|
|
7c2cd43dfa | ||
|
|
de5feff720 | ||
|
|
7342de837d | ||
|
|
aa2c648918 | ||
|
|
e914b50d16 | ||
|
|
f79554f918 | ||
|
|
213cc9c4b5 | ||
|
|
05f3b8fa0f | ||
|
|
bc53624133 | ||
|
|
5944d034da | ||
|
|
df400feb61 | ||
|
|
48e10c57da | ||
|
|
1eb818b64c | ||
|
|
0954932091 | ||
|
|
e584d9cd2e | ||
|
|
4d140ea6bd | ||
|
|
09a2e50fc2 | ||
|
|
35d8ef33ef | ||
|
|
f1ff2af47a | ||
|
|
d2d2c31ec7 | ||
|
|
95102c1c6c | ||
|
|
fa8c16ae01 | ||
|
|
7ca2a8213d | ||
|
|
1c8e5046e5 | ||
|
|
68a5652733 | ||
|
|
6b4be30c64 | ||
|
|
6ce952ad4a | ||
|
|
5ae62dd9d7 | ||
|
|
51987107a2 | ||
|
|
e4a8c4813c | ||
|
|
3d708f7dc4 | ||
|
|
4b5bcb56bc | ||
|
|
10ec8c9558 | ||
|
|
3900f758ea | ||
|
|
a3346fbad8 | ||
|
|
f73d58d74e | ||
|
|
813d5e1ddf | ||
|
|
acb661df72 | ||
|
|
4bc7710de9 | ||
|
|
b8c1f4f138 | ||
|
|
387b67472c | ||
|
|
b2c099bac6 | ||
|
|
1acebb1cc4 | ||
|
|
8d89a8dcb0 | ||
|
|
a60a34331b | ||
|
|
749015eb86 | ||
|
|
4b45142e72 | ||
|
|
76009ce7b9 | ||
|
|
2722119776 | ||
|
|
c724585bce | ||
|
|
0b61e46f95 | ||
|
|
5c0da486b9 | ||
|
|
9701e5e6e4 | ||
|
|
1684091234 | ||
|
|
a93eb8b8f5 | ||
|
|
c591ab5e6f | ||
|
|
b3fbebed55 | ||
|
|
4621f88441 | ||
|
|
747edb3a99 | ||
|
|
558970e281 | ||
|
|
73fd20a608 | ||
|
|
78c2c33b9e | ||
|
|
610add1f59 | ||
|
|
655510ce28 | ||
|
|
f7414700f4 | ||
|
|
53a860a5bf | ||
|
|
1aae450303 | ||
|
|
b3a2f8837c | ||
|
|
33d2a93294 | ||
|
|
2da0db70de | ||
|
|
8b5cbf8066 | ||
|
|
9668563801 | ||
|
|
ff7067f3a3 | ||
|
|
6b8b152444 | ||
|
|
3dbe3b3e7f | ||
|
|
7aa4bff97b | ||
|
|
6002fac9f1 | ||
|
|
231c6ac862 | ||
|
|
c3eb7e1634 | ||
|
|
05a6ee2b49 | ||
|
|
94d76c042d | ||
|
|
23ccaa6191 | ||
|
|
476e9d0a48 | ||
|
|
7cd5ed6fc6 | ||
|
|
750c23f319 | ||
|
|
bb36df8b2e | ||
|
|
470c43a986 | ||
|
|
8ea78e8361 | ||
|
|
9c748d20d5 | ||
|
|
af15b22b03 | ||
|
|
80c9c705b8 | ||
|
|
138419d232 | ||
|
|
8cee15bc5a | ||
|
|
8113974b26 | ||
|
|
2b70cefd48 | ||
|
|
16054d4bfd | ||
|
|
c2d4a53b67 | ||
|
|
29cbf8b83f | ||
|
|
3448b1c78c | ||
|
|
31253f400d | ||
|
|
1b6f547948 | ||
|
|
deb68b414a | ||
|
|
b799b063f8 | ||
|
|
368014b8dd | ||
|
|
fa16e66a6d | ||
|
|
40af31da4c | ||
|
|
9e6c0685a2 | ||
|
|
ebad3d4755 | ||
|
|
d4fd0681ef | ||
|
|
43a47aa08b | ||
|
|
20079b4c2f | ||
|
|
2aeec7703e | ||
|
|
cef458c31c | ||
|
|
617a5766a2 | ||
|
|
f1d6733554 | ||
|
|
5f3bcb1f58 | ||
|
|
b419bfd95f | ||
|
|
e406a2c15e | ||
|
|
962a75c45e | ||
|
|
6cdc13d6c6 | ||
|
|
92944f7847 | ||
|
|
276792a812 | ||
|
|
579fa6ea93 | ||
|
|
2f2b211766 | ||
|
|
88a3cb51af | ||
|
|
40679cf638 | ||
|
|
5b587e8578 | ||
|
|
50a1121d81 | ||
|
|
3239c5efcc | ||
|
|
fb0bd22979 | ||
|
|
3dc6c0afa2 | ||
|
|
e8762781a7 | ||
|
|
2bf841e22a | ||
|
|
5b9892a902 | ||
|
|
7ebab98e91 | ||
|
|
23fc6cc900 | ||
|
|
2e35cdea6c | ||
|
|
22af8e782b | ||
|
|
c88e910009 | ||
|
|
43ba312593 | ||
|
|
3c17299a92 | ||
|
|
a7ecff657c | ||
|
|
79a4f789a1 | ||
|
|
28ba0b37e1 | ||
|
|
6b7ade9f3f | ||
|
|
465367294f | ||
|
|
563c117303 | ||
|
|
1c04ca8032 | ||
|
|
d32e20bcaa | ||
|
|
8b8ba6b0a6 | ||
|
|
81fb015391 | ||
|
|
d8c71d5fdb | ||
|
|
fb5b5aef0a | ||
|
|
6787423edc | ||
|
|
ffcdf5dfbc | ||
|
|
0cdb173846 | ||
|
|
c9d5472ffb | ||
|
|
15bd71ed94 | ||
|
|
a76b7a37fd | ||
|
|
5cdf9ce19b | ||
|
|
0fba09246b |
@@ -2,16 +2,18 @@
|
||||
Language: Cpp
|
||||
AccessModifierOffset: -2
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveMacros: false
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: false
|
||||
AlignConsecutiveMacros: None
|
||||
AlignConsecutiveAssignments: None
|
||||
AlignConsecutiveBitFields: None
|
||||
AlignConsecutiveDeclarations: None
|
||||
AlignEscapedNewlines: Right
|
||||
AlignOperands: true
|
||||
AlignOperands: Align
|
||||
AlignTrailingComments: true
|
||||
AllowAllArgumentsOnNextLine: true
|
||||
AllowAllConstructorInitializersOnNextLine: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortEnumsOnASingleLine: true
|
||||
AllowShortBlocksOnASingleLine: Never
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: All
|
||||
AllowShortLambdasOnASingleLine: All
|
||||
@@ -21,12 +23,14 @@ AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: MultiLine
|
||||
AttributeMacros:
|
||||
- __capability
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: false
|
||||
AfterClass: false
|
||||
AfterControlStatement: false
|
||||
AfterControlStatement: Never
|
||||
AfterEnum: false
|
||||
AfterFunction: false
|
||||
AfterNamespace: false
|
||||
@@ -36,11 +40,14 @@ BraceWrapping:
|
||||
AfterExternBlock: false
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
BeforeLambdaBody: false
|
||||
BeforeWhile: false
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: true
|
||||
SplitEmptyRecord: true
|
||||
SplitEmptyNamespace: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeConceptDeclarations: true
|
||||
BreakBeforeBraces: Attach
|
||||
BreakBeforeInheritanceComma: false
|
||||
BreakInheritanceList: BeforeColon
|
||||
@@ -56,27 +63,43 @@ ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: true
|
||||
DeriveLineEnding: true
|
||||
DerivePointerAlignment: false
|
||||
DisableFormat: false
|
||||
EmptyLineBeforeAccessModifier: LogicalBlock
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
FixNamespaceComments: true
|
||||
ForEachMacros:
|
||||
- foreach
|
||||
- Q_FOREACH
|
||||
- BOOST_FOREACH
|
||||
StatementAttributeLikeMacros:
|
||||
- Q_EMIT
|
||||
IncludeBlocks: Preserve
|
||||
IncludeCategories:
|
||||
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
|
||||
Priority: 2
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
- Regex: '^(<|"(gtest|isl|json)/)'
|
||||
Priority: 3
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
- Regex: '.*'
|
||||
Priority: 1
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
IncludeIsMainRegex: '$'
|
||||
IncludeIsMainSourceRegex: ''
|
||||
IndentCaseLabels: false
|
||||
IndentCaseBlocks: false
|
||||
IndentGotoLabels: true
|
||||
IndentPPDirectives: AfterHash
|
||||
IndentExternBlock: AfterExternBlock
|
||||
IndentRequires: false
|
||||
IndentWidth: 2
|
||||
IndentWrappedFunctionNames: false
|
||||
InsertTrailingCommas: None
|
||||
JavaScriptQuotes: Leave
|
||||
JavaScriptWrapImports: true
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
@@ -86,6 +109,7 @@ MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: None
|
||||
ObjCBinPackProtocolList: Auto
|
||||
ObjCBlockIndentWidth: 2
|
||||
ObjCBreakBeforeNestedBlockParam: true
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PenaltyBreakAssignment: 2
|
||||
@@ -96,31 +120,46 @@ PenaltyBreakString: 1000
|
||||
PenaltyBreakTemplateDeclaration: 10
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 60
|
||||
PenaltyIndentedWhitespace: 0
|
||||
PointerAlignment: Right
|
||||
ReflowComments: true
|
||||
SortIncludes: false
|
||||
SortJavaStaticImport: Before
|
||||
SortUsingDeclarations: true
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterLogicalNot: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCaseColon: false
|
||||
SpaceBeforeCpp11BracedList: false
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceAroundPointerQualifiers: Default
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceInEmptyBlock: false
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInAngles: false
|
||||
SpacesInConditionalStatement: false
|
||||
SpacesInContainerLiterals: true
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
Standard: Cpp11
|
||||
SpaceBeforeSquareBrackets: false
|
||||
BitFieldColonSpacing: Both
|
||||
Standard: Latest
|
||||
StatementMacros:
|
||||
- Q_UNUSED
|
||||
- QT_REQUIRE_VERSION
|
||||
TabWidth: 8
|
||||
UseCRLF: false
|
||||
UseTab: Never
|
||||
WhitespaceSensitiveMacros:
|
||||
- STRINGIZE
|
||||
- PP_STRINGIZE
|
||||
- BOOST_PP_STRINGIZE
|
||||
- NS_SWIFT_NAME
|
||||
- CF_SWIFT_NAME
|
||||
...
|
||||
|
||||
|
||||
93
.github/workflows/build.yml
vendored
93
.github/workflows/build.yml
vendored
@@ -8,9 +8,16 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-18.04, macos-10.15]
|
||||
os: [ubuntu-20.04, macos-10.15]
|
||||
compiler: [gcc, clang]
|
||||
buildtool: [autotools, cmake]
|
||||
http3: [http3, no-http3]
|
||||
openssl: [openssl1, openssl3]
|
||||
exclude:
|
||||
- os: macos-10.15
|
||||
openssl: openssl3
|
||||
- http3: no-http3
|
||||
openssl: openssl3
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@@ -34,10 +41,11 @@ jobs:
|
||||
libjansson-dev \
|
||||
libjemalloc-dev \
|
||||
libc-ares-dev \
|
||||
libelf-dev \
|
||||
cmake \
|
||||
cmake-data
|
||||
echo 'CPPFLAGS=-fsanitize=address' >> $GITHUB_ENV
|
||||
echo 'LDFLAGS=-fsanitize=address' >> $GITHUB_ENV
|
||||
echo 'CPPFLAGS=-fsanitize=address,undefined -fno-sanitize-recover=undefined -g' >> $GITHUB_ENV
|
||||
echo 'LDFLAGS=-fsanitize=address,undefined -fno-sanitize-recover=undefined' >> $GITHUB_ENV
|
||||
- name: MacOS setup
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
@@ -52,8 +60,6 @@ jobs:
|
||||
pkg-config \
|
||||
libtool
|
||||
echo 'PKG_CONFIG_PATH=/usr/local/opt/libressl/lib/pkgconfig:/usr/local/opt/libxml2/lib/pkgconfig' >> $GITHUB_ENV
|
||||
# This fixes infamous 'stdio.h not found' error.
|
||||
echo 'SDKROOT='"$(xcrun --sdk macosx --show-sdk-path)" >> $GITHUB_ENV
|
||||
- name: Setup clang (Linux)
|
||||
if: runner.os == 'Linux' && matrix.compiler == 'clang'
|
||||
run: |
|
||||
@@ -74,26 +80,93 @@ jobs:
|
||||
run: |
|
||||
echo 'CC=gcc' >> $GITHUB_ENV
|
||||
echo 'CXX=g++' >> $GITHUB_ENV
|
||||
- name: Build libbpf
|
||||
if: matrix.http3 == 'http3' && matrix.compiler == 'clang' && runner.os == 'Linux'
|
||||
run: |
|
||||
git clone -b v0.4.0 https://github.com/libbpf/libbpf
|
||||
cd libbpf
|
||||
PREFIX=$PWD/build make -C src install
|
||||
|
||||
EXTRA_AUTOTOOLS_OPTS="--with-libbpf"
|
||||
|
||||
echo 'EXTRA_AUTOTOOLS_OPTS='"$EXTRA_AUTOTOOLS_OPTS" >> $GITHUB_ENV
|
||||
- name: Build quictls/openssl v1.1.1
|
||||
if: matrix.http3 == 'http3' && matrix.openssl == 'openssl1'
|
||||
run: |
|
||||
git clone --depth 1 -b OpenSSL_1_1_1l+quic https://github.com/quictls/openssl
|
||||
cd openssl
|
||||
./config enable-tls1_3 --prefix=$PWD/build
|
||||
make -j$(nproc)
|
||||
make install_sw
|
||||
- name: Build quictls/openssl v3.0.0
|
||||
if: matrix.http3 == 'http3' && matrix.openssl == 'openssl3'
|
||||
run: |
|
||||
unset CPPFLAGS
|
||||
unset LDFLAGS
|
||||
|
||||
git clone --depth 1 -b openssl-3.0.0+quic https://github.com/quictls/openssl
|
||||
cd openssl
|
||||
./config enable-tls1_3 --prefix=$PWD/build --libdir=$PWD/build/lib
|
||||
make -j$(nproc)
|
||||
make install_sw
|
||||
- name: Build nghttp3
|
||||
if: matrix.http3 == 'http3'
|
||||
run: |
|
||||
git clone https://github.com/ngtcp2/nghttp3
|
||||
cd nghttp3
|
||||
autoreconf -i
|
||||
./configure --prefix=$PWD/build --enable-lib-only
|
||||
make -j$(nproc) check
|
||||
make install
|
||||
- name: Build ngtcp2
|
||||
if: matrix.http3 == 'http3'
|
||||
run: |
|
||||
git clone https://github.com/ngtcp2/ngtcp2
|
||||
cd ngtcp2
|
||||
autoreconf -i
|
||||
./configure --prefix=$PWD/build --enable-lib-only PKG_CONFIG_PATH="../openssl/build/lib/pkgconfig"
|
||||
make -j$(nproc) check
|
||||
make install
|
||||
- name: Setup extra environment variables for HTTP/3
|
||||
if: matrix.http3 == 'http3'
|
||||
run: |
|
||||
PKG_CONFIG_PATH="$PWD/openssl/build/lib/pkgconfig:$PWD/nghttp3/build/lib/pkgconfig:$PWD/ngtcp2/build/lib/pkgconfig:$PWD/libbpf/build/lib64/pkgconfig:$PKG_CONFIG_PATH"
|
||||
LDFLAGS="$LDFLAGS -Wl,-rpath,$PWD/openssl/build/lib -Wl,-rpath,$PWD/libbpf/build/lib64"
|
||||
EXTRA_AUTOTOOLS_OPTS="--enable-http3 $EXTRA_AUTOTOOLS_OPTS"
|
||||
|
||||
echo 'PKG_CONFIG_PATH='"$PKG_CONFIG_PATH" >> $GITHUB_ENV
|
||||
echo 'LDFLAGS='"$LDFLAGS" >> $GITHUB_ENV
|
||||
echo 'EXTRA_AUTOTOOLS_OPTS='"$EXTRA_AUTOTOOLS_OPTS" >> $GITHUB_ENV
|
||||
echo 'EXTRA_CMAKE_OPTS=-DENABLE_HTTP3=ON' >> $GITHUB_ENV
|
||||
- name: Setup git submodules
|
||||
run: |
|
||||
git submodule update --init
|
||||
- name: Configure autotools
|
||||
if: matrix.buildtool == 'autotools'
|
||||
run: |
|
||||
autoreconf -i
|
||||
./configure --enable-werror --with-mruby
|
||||
./configure
|
||||
- name: Configure cmake
|
||||
if: matrix.buildtool == 'cmake'
|
||||
run: |
|
||||
cmake -DENABLE_WERROR=1 -DWITH_MRUBY=1 -DWITH_NEVERBLEED=1 -DCPPFLAGS="$CPPFLAGS" -DLDFLAGS="$LDFLAGS" .
|
||||
make dist
|
||||
VERSION=$(grep PACKAGE_VERSION config.h | cut -d' ' -f3 | tr -d '"')
|
||||
tar xf nghttp2-$VERSION.tar.gz
|
||||
cd nghttp2-$VERSION
|
||||
echo 'NGHTTP2_CMAKE_DIR='"$PWD" >> $GITHUB_ENV
|
||||
|
||||
# This fixes infamous 'stdio.h not found' error.
|
||||
echo 'SDKROOT='"$(xcrun --sdk macosx --show-sdk-path)" >> $GITHUB_ENV
|
||||
|
||||
cmake -DENABLE_WERROR=1 -DWITH_MRUBY=1 -DWITH_NEVERBLEED=1 -DENABLE_APP=1 $EXTRA_CMAKE_OPTS -DCPPFLAGS="$CPPFLAGS" -DLDFLAGS="$LDFLAGS" .
|
||||
- name: Build nghttp2 with autotools
|
||||
if: matrix.buildtool == 'autotools'
|
||||
run: |
|
||||
make distcheck \
|
||||
DISTCHECK_CONFIGURE_FLAGS="--with-mruby --with-neverbleed --enable-werror CPPFLAGS=\"$CPPFLAGS\" LDFLAGS=\"$LDFLAGS\""
|
||||
DISTCHECK_CONFIGURE_FLAGS="--with-mruby --with-neverbleed --with-libev --enable-werror $EXTRA_AUTOTOOLS_OPTS CPPFLAGS=\"$CPPFLAGS\" LDFLAGS=\"$LDFLAGS\""
|
||||
- name: Build nghttp2 with cmake
|
||||
if: matrix.buildtool == 'cmake'
|
||||
run: |
|
||||
cd $NGHTTP2_CMAKE_DIR
|
||||
make
|
||||
make check
|
||||
- name: Integration test
|
||||
@@ -101,5 +174,5 @@ jobs:
|
||||
# artifacts.
|
||||
if: matrix.buildtool == 'cmake'
|
||||
run: |
|
||||
cd integration-tests
|
||||
cd $NGHTTP2_CMAKE_DIR/integration-tests
|
||||
make itprep it
|
||||
|
||||
11
AUTHORS
11
AUTHORS
@@ -19,6 +19,7 @@ Alek Storm
|
||||
Alex Nalivko
|
||||
Alexandros Konstantinakis-Karmis
|
||||
Alexis La Goutte
|
||||
Amir Livneh
|
||||
Amir Pakdel
|
||||
Anders Bakken
|
||||
Andreas Pohl
|
||||
@@ -27,17 +28,20 @@ Andy Davies
|
||||
Angus Gratton
|
||||
Anna Henningsen
|
||||
Ant Bryan
|
||||
Asra Ali
|
||||
Benedikt Christoph Wolters
|
||||
Benjamin Peterson
|
||||
Bernard Spil
|
||||
Brendan Heinonen
|
||||
Brian Card
|
||||
Brian Suh
|
||||
Daniel Bevenius
|
||||
Daniel Evers
|
||||
Daniel Stenberg
|
||||
Dave Reisner
|
||||
David Beitey
|
||||
David Weekly
|
||||
Dmitri Tikhonov
|
||||
Dmitriy Vetutnev
|
||||
Don
|
||||
Dylan Plecki
|
||||
@@ -47,9 +51,12 @@ Fabian Wiesel
|
||||
Gabi Davar
|
||||
Gaël PORTAY
|
||||
Geoff Hill
|
||||
George Liu
|
||||
Gitai
|
||||
Google Inc.
|
||||
Hajime Fujita
|
||||
Jacky Tian
|
||||
Jacky_Yin
|
||||
Jacob Champion
|
||||
James M Snell
|
||||
Jan Kundrát
|
||||
@@ -69,11 +76,13 @@ Kit Chan
|
||||
Kyle Schomp
|
||||
LazyHamster
|
||||
Leo Neat
|
||||
Lorenz Nickel
|
||||
Lucas Pardue
|
||||
MATSUMOTO Ryosuke
|
||||
Marc Bachmann
|
||||
Matt Rudary
|
||||
Matt Way
|
||||
Michael Kaufmann
|
||||
Mike Conlen
|
||||
Mike Frysinger
|
||||
Mike Lothian
|
||||
@@ -104,6 +113,7 @@ Tatsuhiko Kubo
|
||||
Tatsuhiro Tsujikawa
|
||||
Tobias Geerinckx-Rice
|
||||
Tom Harwood
|
||||
Tomas Krizek
|
||||
Tomasz Buchert
|
||||
Tomasz Torcz
|
||||
Vernon Tang
|
||||
@@ -124,6 +134,7 @@ es
|
||||
fangdingjun
|
||||
jwchoi
|
||||
kumagi
|
||||
lhuang04
|
||||
lstefani
|
||||
makovich
|
||||
mod-h2-dev
|
||||
|
||||
@@ -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.41.90)
|
||||
project(nghttp2 VERSION 1.45.1)
|
||||
|
||||
# See versioning rule:
|
||||
# http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
||||
set(LT_CURRENT 34)
|
||||
set(LT_CURRENT 35)
|
||||
set(LT_REVISION 0)
|
||||
set(LT_AGE 20)
|
||||
set(LT_AGE 21)
|
||||
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
|
||||
include(Version)
|
||||
@@ -61,6 +61,10 @@ find_package(OpenSSL 1.0.1)
|
||||
find_package(Libev 4.11)
|
||||
find_package(Libcares 1.7.5)
|
||||
find_package(ZLIB 1.2.3)
|
||||
find_package(Libngtcp2 0.0.0)
|
||||
find_package(Libngtcp2_crypto_openssl 0.0.0)
|
||||
find_package(Libnghttp3 0.0.0)
|
||||
find_package(Libbpf 0.4.0)
|
||||
if(OPENSSL_FOUND AND LIBEV_FOUND AND ZLIB_FOUND)
|
||||
set(ENABLE_APP_DEFAULT ON)
|
||||
else()
|
||||
@@ -182,9 +186,18 @@ if(HAVE_CUNIT)
|
||||
endif()
|
||||
|
||||
# openssl (for src)
|
||||
include(CheckSymbolExists)
|
||||
set(HAVE_OPENSSL ${OPENSSL_FOUND})
|
||||
if(OPENSSL_FOUND)
|
||||
set(OPENSSL_INCLUDE_DIRS ${OPENSSL_INCLUDE_DIR})
|
||||
cmake_push_check_state()
|
||||
set(CMAKE_REQUIRED_INCLUDES "${OPENSSL_INCLUDE_DIR}")
|
||||
set(CMAKE_REQUIRED_LIBRARIES "${OPENSSL_LIBRARIES}")
|
||||
check_symbol_exists(SSL_is_quic "openssl/ssl.h" HAVE_SSL_IS_QUIC)
|
||||
if(NOT HAVE_SSL_IS_QUIC)
|
||||
message(WARNING "OpenSSL in ${OPENSSL_LIBRARIES} dose not have SSL_is_quic. HTTP/3 support cannot be enabled")
|
||||
endif()
|
||||
cmake_pop_check_state()
|
||||
else()
|
||||
set(OPENSSL_INCLUDE_DIRS "")
|
||||
set(OPENSSL_LIBRARIES "")
|
||||
@@ -223,11 +236,31 @@ if(ENABLE_ASIO_LIB)
|
||||
find_package(Boost 1.54.0 REQUIRED system thread)
|
||||
endif()
|
||||
|
||||
# libbpf (for bpf)
|
||||
set(HAVE_LIBBPF ${LIBBPF_FOUND})
|
||||
if(LIBBPF_FOUND)
|
||||
set(BPFCFLAGS -Wall -O2 -g)
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
# For Debian/Ubuntu
|
||||
set(EXTRABPFCFLAGS -I/usr/include/${CMAKE_SYSTEM_PROCESSOR}-linux-gnu)
|
||||
endif()
|
||||
|
||||
check_c_source_compiles("
|
||||
#include <linux/bpf.h>
|
||||
int main() { enum bpf_stats_type foo; (void)foo; }" HAVE_BPF_STATS_TYPE)
|
||||
endif()
|
||||
|
||||
# The nghttp, nghttpd and nghttpx under src depend on zlib, OpenSSL and libev
|
||||
if(ENABLE_APP AND NOT (ZLIB_FOUND AND OPENSSL_FOUND AND LIBEV_FOUND))
|
||||
message(FATAL_ERROR "Applications were requested (ENABLE_APP=1) but dependencies are not met.")
|
||||
endif()
|
||||
|
||||
# HTTP/3 requires quictls/openssl, libngtcp2, libngtcp2_crypto_openssl
|
||||
# and libnghttp3.
|
||||
if(ENABLE_HTTP3 AND NOT (HAVE_SSL_IS_QUIC AND LIBNGTCP2_FOUND AND LIBNGTCP2_CRYPTO_OPENSSL_FOUND AND LIBNGHTTP3_FOUND))
|
||||
message(FATAL_ERROR "HTTP/3 was requested (ENABLE_HTTP3=1) but dependencies are not met.")
|
||||
endif()
|
||||
|
||||
# HPACK tools requires jansson
|
||||
if(ENABLE_HPACK_TOOLS AND NOT HAVE_JANSSON)
|
||||
message(FATAL_ERROR "HPACK tools were requested (ENABLE_HPACK_TOOLS=1) but dependencies are not met.")
|
||||
@@ -448,11 +481,16 @@ foreach(name
|
||||
configure_file("${name}.in" "${name}" @ONLY)
|
||||
endforeach()
|
||||
|
||||
if(APPLE)
|
||||
add_definitions(-D__APPLE_USE_RFC_3542)
|
||||
endif()
|
||||
|
||||
include_directories(
|
||||
"${CMAKE_CURRENT_BINARY_DIR}" # for config.h
|
||||
)
|
||||
# For use in src/CMakeLists.txt
|
||||
set(PKGDATADIR "${CMAKE_INSTALL_FULL_DATADIR}/${CMAKE_PROJECT_NAME}")
|
||||
set(PKGLIBDIR "${CMAKE_INSTALL_FULL_LIBDIR}/${CMAKE_PROJECT_NAME}")
|
||||
|
||||
install(FILES README.rst DESTINATION "${CMAKE_INSTALL_DOCDIR}")
|
||||
|
||||
@@ -469,6 +507,7 @@ add_subdirectory(integration-tests)
|
||||
add_subdirectory(doc)
|
||||
add_subdirectory(contrib)
|
||||
add_subdirectory(script)
|
||||
add_subdirectory(bpf)
|
||||
|
||||
|
||||
string(TOUPPER "${CMAKE_BUILD_TYPE}" _build_type)
|
||||
@@ -499,6 +538,10 @@ message(STATUS "summary of build options:
|
||||
Libxml2: ${HAVE_LIBXML2} (LIBS='${LIBXML2_LIBRARIES}')
|
||||
Libev: ${HAVE_LIBEV} (LIBS='${LIBEV_LIBRARIES}')
|
||||
Libc-ares: ${HAVE_LIBCARES} (LIBS='${LIBCARES_LIBRARIES}')
|
||||
Libngtcp2: ${HAVE_LIBNGTCP2} (LIBS='${LIBNGTCP2_LIBRARIES}')
|
||||
Libngtcp2_crypto_openssl: ${HAVE_LIBNGTCP2_CRYPTO_OPENSSL} (LIBS='${LIBNGTCP2_CRYPTO_OPENSSL_LIBRARIES}')
|
||||
Libnghttp3: ${HAVE_LIBNGHTTP3} (LIBS='${LIBNGHTTP3_LIBRARIES}')
|
||||
Libbpf: ${HAVE_LIBBPF} (LIBS='${LIBBPF_LIBRARIES}')
|
||||
Libevent(SSL): ${HAVE_LIBEVENT_OPENSSL} (LIBS='${LIBEVENT_OPENSSL_LIBRARIES}')
|
||||
Jansson: ${HAVE_JANSSON} (LIBS='${JANSSON_LIBRARIES}')
|
||||
Jemalloc: ${HAVE_JEMALLOC} (LIBS='${JEMALLOC_LIBRARIES}')
|
||||
@@ -517,6 +560,7 @@ message(STATUS "summary of build options:
|
||||
Examples: ${ENABLE_EXAMPLES}
|
||||
Python bindings:${ENABLE_PYTHON_BINDINGS}
|
||||
Threading: ${ENABLE_THREADS}
|
||||
HTTP/3(EXPERIMENTAL): ${ENABLE_HTTP3}
|
||||
")
|
||||
if(ENABLE_LIB_ONLY_DISABLED_OTHERS)
|
||||
message("Only the library will be built. To build other components "
|
||||
|
||||
@@ -17,6 +17,7 @@ option(ENABLE_LIB_ONLY "Build libnghttp2 only. This is a short hand for -DENAB
|
||||
option(ENABLE_STATIC_LIB "Build libnghttp2 in static mode also")
|
||||
option(ENABLE_SHARED_LIB "Build libnghttp2 as a shared library" ON)
|
||||
option(ENABLE_STATIC_CRT "Build libnghttp2 against the MS LIBCMT[d]")
|
||||
option(ENABLE_HTTP3 "Enable HTTP/3 support" OFF)
|
||||
|
||||
option(WITH_LIBXML2 "Use libxml2"
|
||||
${WITH_LIBXML2_DEFAULT})
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
SUBDIRS = lib third-party src examples python tests integration-tests \
|
||||
SUBDIRS = lib third-party src bpf examples python tests integration-tests \
|
||||
doc contrib script
|
||||
|
||||
# Now with python setuptools, make uninstall will leave many files we
|
||||
@@ -46,7 +46,11 @@ EXTRA_DIST = nghttpx.conf.sample proxy.pac.sample android-config android-make \
|
||||
cmake/FindLibevent.cmake \
|
||||
cmake/FindJansson.cmake \
|
||||
cmake/FindLibcares.cmake \
|
||||
cmake/FindSystemd.cmake
|
||||
cmake/FindSystemd.cmake \
|
||||
cmake/FindLibbpf.cmake \
|
||||
cmake/FindLibnghttp3.cmake \
|
||||
cmake/FindLibngtcp2.cmake \
|
||||
cmake/FindLibngtcp2_crypto_openssl.cmake
|
||||
|
||||
.PHONY: clang-format
|
||||
|
||||
|
||||
153
README.rst
153
README.rst
@@ -112,7 +112,7 @@ libnghttp2_asio C++ library requires the following packages:
|
||||
The Python bindings require the following packages:
|
||||
|
||||
* cython >= 0.19
|
||||
* python >= 2.7
|
||||
* python >= 3.8
|
||||
* python-setuptools
|
||||
|
||||
If you are using Ubuntu 16.04 LTS (Xenial Xerus) or Debian 8 (jessie)
|
||||
@@ -145,6 +145,31 @@ 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.
|
||||
|
||||
To enable the experimental HTTP/3 support for h2load and nghttpx, the
|
||||
following libraries are required:
|
||||
|
||||
* `OpenSSL with QUIC support
|
||||
<https://github.com/quictls/openssl/tree/OpenSSL_1_1_1k+quic>`_
|
||||
* `ngtcp2 <https://github.com/ngtcp2/ngtcp2>`_
|
||||
* `nghttp3 <https://github.com/ngtcp2/nghttp3>`_
|
||||
|
||||
Use ``--enable-http3`` configure option to enable HTTP/3 feature for
|
||||
h2load and nghttpx.
|
||||
|
||||
In order to build optional eBPF program to direct an incoming QUIC UDP
|
||||
datagram to a correct socket for nghttpx, the following libraries are
|
||||
required:
|
||||
|
||||
* libbpf-dev >= 0.4.0
|
||||
|
||||
Use ``--with-libbpf`` configure option to build eBPF program.
|
||||
libelf-dev is needed to build libbpf.
|
||||
|
||||
For Ubuntu 20.04, you can build libbpf from `the source code
|
||||
<https://github.com/libbpf/libbpf/releases/tag/v0.4.0>`_. nghttpx
|
||||
requires eBPF program for reloading its configuration and hot swapping
|
||||
its executable.
|
||||
|
||||
Compiling libnghttp2 C source code requires a C99 compiler. gcc 4.8
|
||||
is known to be adequate. In order to compile the C++ source code, gcc
|
||||
>= 6.0 or clang >= 6.0 is required. C++ source code requires C++14
|
||||
@@ -307,6 +332,88 @@ The generated documents will not be installed with ``make install``.
|
||||
The online documentation is available at
|
||||
https://nghttp2.org/documentation/
|
||||
|
||||
Build HTTP/3 enabled h2load and nghttpx
|
||||
---------------------------------------
|
||||
|
||||
To build h2load and nghttpx with HTTP/3 feature enabled, run the
|
||||
configure script with ``--enable-http3``.
|
||||
|
||||
For nghttpx to reload configurations and swapping its executable while
|
||||
gracefully terminating old worker processes, eBPF is required. Run
|
||||
the configure script with ``--enable-http3 --with-libbpf`` to build
|
||||
eBPF program. The Connection ID encryption key must be set with
|
||||
``--frontend-quic-connection-id-encryption-key`` and must not change
|
||||
in order to keep the existing connections alive during reload.
|
||||
|
||||
The detailed steps to build HTTP/3 enabled h2load and nghttpx follow.
|
||||
|
||||
Build custom OpenSSL:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ git clone --depth 1 -b OpenSSL_1_1_1l+quic https://github.com/quictls/openssl
|
||||
$ cd openssl
|
||||
$ ./config --prefix=$PWD/build --openssldir=/etc/ssl
|
||||
$ make -j$(nproc)
|
||||
$ make install_sw
|
||||
$ cd ..
|
||||
|
||||
Build nghttp3:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ git clone --depth 1 https://github.com/ngtcp2/nghttp3
|
||||
$ cd nghttp3
|
||||
$ autoreconf -i
|
||||
$ ./configure --prefix=$PWD/build --enable-lib-only
|
||||
$ make -j$(nproc)
|
||||
$ make install
|
||||
$ cd ..
|
||||
|
||||
Build ngtcp2:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ git clone --depth 1 https://github.com/ngtcp2/ngtcp2
|
||||
$ cd ngtcp2
|
||||
$ autoreconf -i
|
||||
$ ./configure --prefix=$PWD/build --enable-lib-only \
|
||||
PKG_CONFIG_PATH="$PWD/../openssl/build/lib/pkgconfig"
|
||||
$ make -j$(nproc)
|
||||
$ make install
|
||||
$ cd ..
|
||||
|
||||
If your Linux distribution does not have libbpf-dev >= 0.4.0, build
|
||||
from source:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ git clone --depth 1 -b v0.4.0 https://github.com/libbpf/libbpf
|
||||
$ cd libbpf
|
||||
$ PREFIX=$PWD/build make -C src install
|
||||
$ cd ..
|
||||
|
||||
Build nghttp2:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ git clone https://github.com/nghttp2/nghttp2
|
||||
$ cd nghttp2
|
||||
$ git submodule update --init
|
||||
$ autoreconf -i
|
||||
$ ./configure --with-mruby --with-neverbleed --enable-http3 --with-libbpf \
|
||||
--disable-python-bindings \
|
||||
CC=clang-12 CXX=clang++-12 \
|
||||
PKG_CONFIG_PATH="$PWD/../openssl/build/lib/pkgconfig:$PWD/../nghttp3/build/lib/pkgconfig:$PWD/../ngtcp2/build/lib/pkgconfig:$PWD/../libbpf/build/lib64/pkgconfig" \
|
||||
LDFLAGS="$LDFLAGS -Wl,-rpath,$PWD/../openssl/build/lib -Wl,-rpath,$PWD/../libbpf/build/lib64"
|
||||
$ make -j$(nproc)
|
||||
|
||||
The eBPF program ``reuseport_kern.o`` should be found under bpf
|
||||
directory. Pass ``--quic-bpf-program-file=bpf/reuseport_kern.o``
|
||||
option to nghttpx to load it. See also `HTTP/3 section in nghttpx -
|
||||
HTTP/2 proxy - HOW-TO
|
||||
<https://nghttp2.org/documentation/nghttpx-howto.html#http-3>`_.
|
||||
|
||||
Unit tests
|
||||
----------
|
||||
|
||||
@@ -734,7 +841,7 @@ information. Here is sample output from ``nghttpd``:
|
||||
nghttpx - proxy
|
||||
+++++++++++++++
|
||||
|
||||
``nghttpx`` is a multi-threaded reverse proxy for HTTP/2, and
|
||||
``nghttpx`` is a multi-threaded reverse proxy for HTTP/3, HTTP/2, and
|
||||
HTTP/1.1, and powers http://nghttp2.org and supports HTTP/2 server
|
||||
push.
|
||||
|
||||
@@ -755,16 +862,16 @@ ticket keys among multiple ``nghttpx`` instances via memcached.
|
||||
|
||||
``nghttpx`` has 2 operation modes:
|
||||
|
||||
================== ================ ================ =============
|
||||
Mode option Frontend Backend Note
|
||||
================== ================ ================ =============
|
||||
default mode HTTP/2, HTTP/1.1 HTTP/1.1, HTTP/2 Reverse proxy
|
||||
``--http2-proxy`` HTTP/2, HTTP/1.1 HTTP/1.1, HTTP/2 Forward proxy
|
||||
================== ================ ================ =============
|
||||
================== ======================== ================ =============
|
||||
Mode option Frontend Backend Note
|
||||
================== ======================== ================ =============
|
||||
default mode HTTP/3, HTTP/2, HTTP/1.1 HTTP/1.1, HTTP/2 Reverse proxy
|
||||
``--http2-proxy`` HTTP/3, HTTP/2, HTTP/1.1 HTTP/1.1, HTTP/2 Forward proxy
|
||||
================== ======================== ================ =============
|
||||
|
||||
The interesting mode at the moment is the default mode. It works like
|
||||
a reverse proxy and listens for HTTP/2, and HTTP/1.1 and can be
|
||||
deployed as a SSL/TLS terminator for existing web server.
|
||||
a reverse proxy and listens for HTTP/3, HTTP/2, and HTTP/1.1 and can
|
||||
be deployed as a SSL/TLS terminator for existing web server.
|
||||
|
||||
In all modes, the frontend connections are encrypted by SSL/TLS by
|
||||
default. To disable encryption, use the ``no-tls`` keyword in
|
||||
@@ -782,16 +889,16 @@ server:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
Client <-- (HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/1.1, HTTP/2) --> Web Server
|
||||
[reverse proxy]
|
||||
Client <-- (HTTP/3, HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/1.1, HTTP/2) --> Web Server
|
||||
[reverse proxy]
|
||||
|
||||
With the ``--http2-proxy`` option, it works as forward proxy, and it
|
||||
is so called secure HTTP/2 proxy:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
Client <-- (HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/1.1) --> Proxy
|
||||
[secure proxy] (e.g., Squid, ATS)
|
||||
Client <-- (HTTP/3, HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/1.1) --> Proxy
|
||||
[secure proxy] (e.g., Squid, ATS)
|
||||
|
||||
The ``Client`` in the above example needs to be configured to use
|
||||
``nghttpx`` as secure proxy.
|
||||
@@ -823,7 +930,7 @@ proxy through an HTTP proxy:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
Client <-- (HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/2) --
|
||||
Client <-- (HTTP/3, HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/2) --
|
||||
|
||||
--===================---> HTTP/2 Proxy
|
||||
(HTTP proxy tunnel) (e.g., nghttpx -s)
|
||||
@@ -831,8 +938,8 @@ proxy through an HTTP proxy:
|
||||
Benchmarking tool
|
||||
-----------------
|
||||
|
||||
The ``h2load`` program is a benchmarking tool for HTTP/2. The UI of
|
||||
``h2load`` is heavily inspired by ``weighttp``
|
||||
The ``h2load`` program is a benchmarking tool for HTTP/3, HTTP/2, and
|
||||
HTTP/1.1. The UI of ``h2load`` is heavily inspired by ``weighttp``
|
||||
(https://github.com/lighttpd/weighttp). The typical usage is as
|
||||
follows:
|
||||
|
||||
@@ -875,6 +982,14 @@ threads to avoid saturating a single core on client side.
|
||||
considered a DOS attack. Please only use it against your private
|
||||
servers.
|
||||
|
||||
If the experimental HTTP/3 is enabled, h2load can send requests to
|
||||
HTTP/3 server. To do this, specify ``h3`` to ``--npn-list`` option
|
||||
like so:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ h2load --npn-list h3 https://127.0.0.1:4433
|
||||
|
||||
HPACK tools
|
||||
-----------
|
||||
|
||||
@@ -1422,7 +1537,7 @@ The extension module is called ``nghttp2``.
|
||||
determined by the ``configure`` script. If the detected Python version is not
|
||||
what you expect, specify a path to Python executable in a ``PYTHON``
|
||||
variable as an argument to configure script (e.g., ``./configure
|
||||
PYTHON=/usr/bin/python3.5``).
|
||||
PYTHON=/usr/bin/python3.8``).
|
||||
|
||||
The following example code illustrates basic usage of the HPACK compressor
|
||||
and decompressor in Python:
|
||||
@@ -1494,7 +1609,7 @@ BaseRequestHandler usage:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import io, ssl
|
||||
import nghttp2
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# script to extract commit author's name from standard input. The
|
||||
# input should be <AUTHOR>:<EMAIL>, one per line.
|
||||
@@ -49,4 +49,4 @@ for name in names:
|
||||
ndict[lowname] = name
|
||||
|
||||
for name in sorted(ndict.values()):
|
||||
print name
|
||||
print(name)
|
||||
|
||||
13
bpf/CMakeLists.txt
Normal file
13
bpf/CMakeLists.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
if(LIBBPF_FOUND)
|
||||
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/reuseport_kern.o"
|
||||
COMMAND ${CMAKE_C_COMPILER} ${BPFCFLAGS} ${EXTRABPFCFLAGS} -I${LIBBPF_INCLUDE_DIRS} -target bpf -c reuseport_kern.c -o "${CMAKE_CURRENT_BINARY_DIR}/reuseport_kern.o"
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
VERBATIM)
|
||||
|
||||
add_custom_target(bpf ALL
|
||||
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/reuseport_kern.o"
|
||||
VERBATIM)
|
||||
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/reuseport_kern.o"
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}/${CMAKE_PROJECT_NAME}")
|
||||
endif()
|
||||
40
bpf/Makefile.am
Normal file
40
bpf/Makefile.am
Normal file
@@ -0,0 +1,40 @@
|
||||
# nghttp2 - HTTP/2 C Library
|
||||
|
||||
# Copyright (c) 2021 Tatsuhiro Tsujikawa
|
||||
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
EXTRA_DIST = CMakeLists.txt reuseport_kern.c
|
||||
|
||||
if HAVE_LIBBPF
|
||||
|
||||
bpf_pkglibdir = $(pkglibdir)
|
||||
bpf_pkglib_DATA = reuseport_kern.o
|
||||
|
||||
all: $(builddir)/reuseport_kern.o
|
||||
|
||||
$(builddir)/reuseport_kern.o: reuseport_kern.c
|
||||
$(CC) @LIBBPF_CFLAGS@ @BPFCFLAGS@ @EXTRABPFCFLAGS@ \
|
||||
-target bpf -c $< -o $@
|
||||
|
||||
clean-local:
|
||||
-rm -f reuseport_kern.o
|
||||
|
||||
endif # HAVE_LIBBPF
|
||||
659
bpf/reuseport_kern.c
Normal file
659
bpf/reuseport_kern.c
Normal file
@@ -0,0 +1,659 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2021 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include <linux/udp.h>
|
||||
#include <linux/bpf.h>
|
||||
|
||||
#include <bpf/bpf_helpers.h>
|
||||
|
||||
/*
|
||||
* How to compile:
|
||||
*
|
||||
* clang-12 -O2 -Wall -target bpf -g -c reuseport_kern.c -o reuseport_kern.o \
|
||||
* -I/path/to/kernel/include
|
||||
*
|
||||
* See
|
||||
* https://www.kernel.org/doc/Documentation/kbuild/headers_install.txt
|
||||
* how to install kernel header files.
|
||||
*/
|
||||
|
||||
/* AES_CBC_decrypt_buffer: https://github.com/kokke/tiny-AES-c
|
||||
License is Public Domain. Commit hash:
|
||||
12e7744b4919e9d55de75b7ab566326a1c8e7a67 */
|
||||
|
||||
#define AES_BLOCKLEN \
|
||||
16 /* Block length in bytes - AES is 128b block \
|
||||
only */
|
||||
|
||||
#define AES_KEYLEN 16 /* Key length in bytes */
|
||||
#define AES_keyExpSize 176
|
||||
|
||||
struct AES_ctx {
|
||||
__u8 RoundKey[AES_keyExpSize];
|
||||
};
|
||||
|
||||
/* The number of columns comprising a state in AES. This is a constant
|
||||
in AES. Value=4 */
|
||||
#define Nb 4
|
||||
|
||||
#define Nk 4 /* The number of 32 bit words in a key. */
|
||||
#define Nr 10 /* The number of rounds in AES Cipher. */
|
||||
|
||||
/* state - array holding the intermediate results during
|
||||
decryption. */
|
||||
typedef __u8 state_t[4][4];
|
||||
|
||||
/* The lookup-tables are marked const so they can be placed in
|
||||
read-only storage instead of RAM The numbers below can be computed
|
||||
dynamically trading ROM for RAM - This can be useful in (embedded)
|
||||
bootloader applications, where ROM is often limited. */
|
||||
static const __u8 sbox[256] = {
|
||||
/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
|
||||
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b,
|
||||
0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
|
||||
0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26,
|
||||
0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
|
||||
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2,
|
||||
0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
|
||||
0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed,
|
||||
0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
|
||||
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f,
|
||||
0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
|
||||
0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec,
|
||||
0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
|
||||
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14,
|
||||
0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
|
||||
0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d,
|
||||
0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
|
||||
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f,
|
||||
0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
|
||||
0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11,
|
||||
0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
|
||||
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f,
|
||||
0xb0, 0x54, 0xbb, 0x16};
|
||||
|
||||
static const __u8 rsbox[256] = {
|
||||
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e,
|
||||
0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87,
|
||||
0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32,
|
||||
0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
|
||||
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49,
|
||||
0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16,
|
||||
0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50,
|
||||
0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
|
||||
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05,
|
||||
0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02,
|
||||
0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41,
|
||||
0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
|
||||
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8,
|
||||
0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89,
|
||||
0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b,
|
||||
0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
|
||||
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59,
|
||||
0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d,
|
||||
0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d,
|
||||
0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
|
||||
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63,
|
||||
0x55, 0x21, 0x0c, 0x7d};
|
||||
|
||||
/* The round constant word array, Rcon[i], contains the values given
|
||||
by x to the power (i-1) being powers of x (x is denoted as {02}) in
|
||||
the field GF(2^8) */
|
||||
static const __u8 Rcon[11] = {0x8d, 0x01, 0x02, 0x04, 0x08, 0x10,
|
||||
0x20, 0x40, 0x80, 0x1b, 0x36};
|
||||
|
||||
#define getSBoxValue(num) (sbox[(num)])
|
||||
|
||||
/* This function produces Nb(Nr+1) round keys. The round keys are used
|
||||
in each round to decrypt the states. */
|
||||
static void KeyExpansion(__u8 *RoundKey, const __u8 *Key) {
|
||||
unsigned i, j, k;
|
||||
__u8 tempa[4]; /* Used for the column/row operations */
|
||||
|
||||
/* The first round key is the key itself. */
|
||||
for (i = 0; i < Nk; ++i) {
|
||||
RoundKey[(i * 4) + 0] = Key[(i * 4) + 0];
|
||||
RoundKey[(i * 4) + 1] = Key[(i * 4) + 1];
|
||||
RoundKey[(i * 4) + 2] = Key[(i * 4) + 2];
|
||||
RoundKey[(i * 4) + 3] = Key[(i * 4) + 3];
|
||||
}
|
||||
|
||||
/* All other round keys are found from the previous round keys. */
|
||||
for (i = Nk; i < Nb * (Nr + 1); ++i) {
|
||||
{
|
||||
k = (i - 1) * 4;
|
||||
tempa[0] = RoundKey[k + 0];
|
||||
tempa[1] = RoundKey[k + 1];
|
||||
tempa[2] = RoundKey[k + 2];
|
||||
tempa[3] = RoundKey[k + 3];
|
||||
}
|
||||
|
||||
if (i % Nk == 0) {
|
||||
/* This function shifts the 4 bytes in a word to the left once.
|
||||
[a0,a1,a2,a3] becomes [a1,a2,a3,a0] */
|
||||
|
||||
/* Function RotWord() */
|
||||
{
|
||||
const __u8 u8tmp = tempa[0];
|
||||
tempa[0] = tempa[1];
|
||||
tempa[1] = tempa[2];
|
||||
tempa[2] = tempa[3];
|
||||
tempa[3] = u8tmp;
|
||||
}
|
||||
|
||||
/* SubWord() is a function that takes a four-byte input word and
|
||||
applies the S-box to each of the four bytes to produce an
|
||||
output word. */
|
||||
|
||||
/* Function Subword() */
|
||||
{
|
||||
tempa[0] = getSBoxValue(tempa[0]);
|
||||
tempa[1] = getSBoxValue(tempa[1]);
|
||||
tempa[2] = getSBoxValue(tempa[2]);
|
||||
tempa[3] = getSBoxValue(tempa[3]);
|
||||
}
|
||||
|
||||
tempa[0] = tempa[0] ^ Rcon[i / Nk];
|
||||
}
|
||||
j = i * 4;
|
||||
k = (i - Nk) * 4;
|
||||
RoundKey[j + 0] = RoundKey[k + 0] ^ tempa[0];
|
||||
RoundKey[j + 1] = RoundKey[k + 1] ^ tempa[1];
|
||||
RoundKey[j + 2] = RoundKey[k + 2] ^ tempa[2];
|
||||
RoundKey[j + 3] = RoundKey[k + 3] ^ tempa[3];
|
||||
}
|
||||
}
|
||||
|
||||
static void AES_init_ctx(struct AES_ctx *ctx, const __u8 *key) {
|
||||
KeyExpansion(ctx->RoundKey, key);
|
||||
}
|
||||
|
||||
/* This function adds the round key to state. The round key is added
|
||||
to the state by an XOR function. */
|
||||
static void AddRoundKey(__u8 round, state_t *state, const __u8 *RoundKey) {
|
||||
__u8 i, j;
|
||||
for (i = 0; i < 4; ++i) {
|
||||
for (j = 0; j < 4; ++j) {
|
||||
(*state)[i][j] ^= RoundKey[(round * Nb * 4) + (i * Nb) + j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static __u8 xtime(__u8 x) { return ((x << 1) ^ (((x >> 7) & 1) * 0x1b)); }
|
||||
|
||||
#define Multiply(x, y) \
|
||||
(((y & 1) * x) ^ ((y >> 1 & 1) * xtime(x)) ^ \
|
||||
((y >> 2 & 1) * xtime(xtime(x))) ^ \
|
||||
((y >> 3 & 1) * xtime(xtime(xtime(x)))) ^ \
|
||||
((y >> 4 & 1) * xtime(xtime(xtime(xtime(x))))))
|
||||
|
||||
#define getSBoxInvert(num) (rsbox[(num)])
|
||||
|
||||
/* MixColumns function mixes the columns of the state matrix. The
|
||||
method used to multiply may be difficult to understand for the
|
||||
inexperienced. Please use the references to gain more
|
||||
information. */
|
||||
static void InvMixColumns(state_t *state) {
|
||||
int i;
|
||||
__u8 a, b, c, d;
|
||||
for (i = 0; i < 4; ++i) {
|
||||
a = (*state)[i][0];
|
||||
b = (*state)[i][1];
|
||||
c = (*state)[i][2];
|
||||
d = (*state)[i][3];
|
||||
|
||||
(*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^
|
||||
Multiply(d, 0x09);
|
||||
(*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^
|
||||
Multiply(d, 0x0d);
|
||||
(*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^
|
||||
Multiply(d, 0x0b);
|
||||
(*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^
|
||||
Multiply(d, 0x0e);
|
||||
}
|
||||
}
|
||||
|
||||
extern __u32 LINUX_KERNEL_VERSION __kconfig;
|
||||
|
||||
/* The SubBytes Function Substitutes the values in the state matrix
|
||||
with values in an S-box. */
|
||||
static void InvSubBytes(state_t *state) {
|
||||
__u8 i, j;
|
||||
if (LINUX_KERNEL_VERSION < KERNEL_VERSION(5, 10, 0)) {
|
||||
for (i = 0; i < 4; ++i) {
|
||||
for (j = 0; j < 4; ++j) {
|
||||
/* Ubuntu 20.04 LTS kernel 5.4.0 needs this workaround
|
||||
otherwise "math between map_value pointer and register with
|
||||
unbounded min value is not allowed". 5.10.0 is a kernel
|
||||
version that works but it might not be the minimum
|
||||
version. */
|
||||
__u8 k = (*state)[j][i];
|
||||
(*state)[j][i] = k ? getSBoxInvert(k) : getSBoxInvert(0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < 4; ++i) {
|
||||
for (j = 0; j < 4; ++j) {
|
||||
(*state)[j][i] = getSBoxInvert((*state)[j][i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void InvShiftRows(state_t *state) {
|
||||
__u8 temp;
|
||||
|
||||
/* Rotate first row 1 columns to right */
|
||||
temp = (*state)[3][1];
|
||||
(*state)[3][1] = (*state)[2][1];
|
||||
(*state)[2][1] = (*state)[1][1];
|
||||
(*state)[1][1] = (*state)[0][1];
|
||||
(*state)[0][1] = temp;
|
||||
|
||||
/* Rotate second row 2 columns to right */
|
||||
temp = (*state)[0][2];
|
||||
(*state)[0][2] = (*state)[2][2];
|
||||
(*state)[2][2] = temp;
|
||||
|
||||
temp = (*state)[1][2];
|
||||
(*state)[1][2] = (*state)[3][2];
|
||||
(*state)[3][2] = temp;
|
||||
|
||||
/* Rotate third row 3 columns to right */
|
||||
temp = (*state)[0][3];
|
||||
(*state)[0][3] = (*state)[1][3];
|
||||
(*state)[1][3] = (*state)[2][3];
|
||||
(*state)[2][3] = (*state)[3][3];
|
||||
(*state)[3][3] = temp;
|
||||
}
|
||||
|
||||
static void InvCipher(state_t *state, const __u8 *RoundKey) {
|
||||
/* Add the First round key to the state before starting the
|
||||
rounds. */
|
||||
AddRoundKey(Nr, state, RoundKey);
|
||||
|
||||
/* There will be Nr rounds. The first Nr-1 rounds are identical.
|
||||
These Nr rounds are executed in the loop below. Last one without
|
||||
InvMixColumn() */
|
||||
InvShiftRows(state);
|
||||
InvSubBytes(state);
|
||||
AddRoundKey(Nr - 1, state, RoundKey);
|
||||
InvMixColumns(state);
|
||||
|
||||
InvShiftRows(state);
|
||||
InvSubBytes(state);
|
||||
AddRoundKey(Nr - 2, state, RoundKey);
|
||||
InvMixColumns(state);
|
||||
|
||||
InvShiftRows(state);
|
||||
InvSubBytes(state);
|
||||
AddRoundKey(Nr - 3, state, RoundKey);
|
||||
InvMixColumns(state);
|
||||
|
||||
InvShiftRows(state);
|
||||
InvSubBytes(state);
|
||||
AddRoundKey(Nr - 4, state, RoundKey);
|
||||
InvMixColumns(state);
|
||||
|
||||
InvShiftRows(state);
|
||||
InvSubBytes(state);
|
||||
AddRoundKey(Nr - 5, state, RoundKey);
|
||||
InvMixColumns(state);
|
||||
|
||||
InvShiftRows(state);
|
||||
InvSubBytes(state);
|
||||
AddRoundKey(Nr - 6, state, RoundKey);
|
||||
InvMixColumns(state);
|
||||
|
||||
InvShiftRows(state);
|
||||
InvSubBytes(state);
|
||||
AddRoundKey(Nr - 7, state, RoundKey);
|
||||
InvMixColumns(state);
|
||||
|
||||
InvShiftRows(state);
|
||||
InvSubBytes(state);
|
||||
AddRoundKey(Nr - 8, state, RoundKey);
|
||||
InvMixColumns(state);
|
||||
|
||||
InvShiftRows(state);
|
||||
InvSubBytes(state);
|
||||
AddRoundKey(Nr - 9, state, RoundKey);
|
||||
InvMixColumns(state);
|
||||
|
||||
InvShiftRows(state);
|
||||
InvSubBytes(state);
|
||||
AddRoundKey(Nr - 10, state, RoundKey);
|
||||
}
|
||||
|
||||
static void AES_ECB_decrypt(const struct AES_ctx *ctx, __u8 *buf) {
|
||||
/* The next function call decrypts the PlainText with the Key using
|
||||
AES algorithm. */
|
||||
InvCipher((state_t *)buf, ctx->RoundKey);
|
||||
}
|
||||
|
||||
/* rol32: From linux kernel source code */
|
||||
|
||||
/**
|
||||
* rol32 - rotate a 32-bit value left
|
||||
* @word: value to rotate
|
||||
* @shift: bits to roll
|
||||
*/
|
||||
static inline __u32 rol32(__u32 word, unsigned int shift) {
|
||||
return (word << shift) | (word >> ((-shift) & 31));
|
||||
}
|
||||
|
||||
/* jhash.h: Jenkins hash support.
|
||||
*
|
||||
* Copyright (C) 2006. Bob Jenkins (bob_jenkins@burtleburtle.net)
|
||||
*
|
||||
* https://burtleburtle.net/bob/hash/
|
||||
*
|
||||
* These are the credits from Bob's sources:
|
||||
*
|
||||
* lookup3.c, by Bob Jenkins, May 2006, Public Domain.
|
||||
*
|
||||
* These are functions for producing 32-bit hashes for hash table lookup.
|
||||
* hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final()
|
||||
* are externally useful functions. Routines to test the hash are included
|
||||
* if SELF_TEST is defined. You can use this free for any purpose. It's in
|
||||
* the public domain. It has no warranty.
|
||||
*
|
||||
* Copyright (C) 2009-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
|
||||
*
|
||||
* I've modified Bob's hash to be useful in the Linux kernel, and
|
||||
* any bugs present are my fault.
|
||||
* Jozsef
|
||||
*/
|
||||
|
||||
/* __jhash_final - final mixing of 3 32-bit values (a,b,c) into c */
|
||||
#define __jhash_final(a, b, c) \
|
||||
{ \
|
||||
c ^= b; \
|
||||
c -= rol32(b, 14); \
|
||||
a ^= c; \
|
||||
a -= rol32(c, 11); \
|
||||
b ^= a; \
|
||||
b -= rol32(a, 25); \
|
||||
c ^= b; \
|
||||
c -= rol32(b, 16); \
|
||||
a ^= c; \
|
||||
a -= rol32(c, 4); \
|
||||
b ^= a; \
|
||||
b -= rol32(a, 14); \
|
||||
c ^= b; \
|
||||
c -= rol32(b, 24); \
|
||||
}
|
||||
|
||||
/* __jhash_nwords - hash exactly 3, 2 or 1 word(s) */
|
||||
static inline __u32 __jhash_nwords(__u32 a, __u32 b, __u32 c, __u32 initval) {
|
||||
a += initval;
|
||||
b += initval;
|
||||
c += initval;
|
||||
|
||||
__jhash_final(a, b, c);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/* An arbitrary initial parameter */
|
||||
#define JHASH_INITVAL 0xdeadbeef
|
||||
|
||||
static inline __u32 jhash_2words(__u32 a, __u32 b, __u32 initval) {
|
||||
return __jhash_nwords(a, b, 0, initval + JHASH_INITVAL + (2 << 2));
|
||||
}
|
||||
|
||||
struct bpf_map_def SEC("maps") cid_prefix_map = {
|
||||
.type = BPF_MAP_TYPE_HASH,
|
||||
.max_entries = 255,
|
||||
.key_size = sizeof(__u64),
|
||||
.value_size = sizeof(__u32),
|
||||
};
|
||||
|
||||
struct bpf_map_def SEC("maps") reuseport_array = {
|
||||
.type = BPF_MAP_TYPE_REUSEPORT_SOCKARRAY,
|
||||
.max_entries = 255,
|
||||
.key_size = sizeof(__u32),
|
||||
.value_size = sizeof(__u32),
|
||||
};
|
||||
|
||||
struct bpf_map_def SEC("maps") sk_info = {
|
||||
.type = BPF_MAP_TYPE_ARRAY,
|
||||
.max_entries = 3,
|
||||
.key_size = sizeof(__u32),
|
||||
.value_size = sizeof(__u64),
|
||||
};
|
||||
|
||||
typedef struct quic_hd {
|
||||
__u8 *dcid;
|
||||
__u32 dcidlen;
|
||||
__u32 dcid_offset;
|
||||
__u8 type;
|
||||
} quic_hd;
|
||||
|
||||
#define SV_DCIDLEN 20
|
||||
#define MAX_DCIDLEN 20
|
||||
#define MIN_DCIDLEN 8
|
||||
#define CID_PREFIXLEN 8
|
||||
|
||||
enum {
|
||||
NGTCP2_PKT_INITIAL = 0x0,
|
||||
NGTCP2_PKT_0RTT = 0x1,
|
||||
NGTCP2_PKT_HANDSHAKE = 0x2,
|
||||
NGTCP2_PKT_SHORT = 0x40,
|
||||
};
|
||||
|
||||
static inline int parse_quic(quic_hd *qhd, __u8 *data, __u8 *data_end) {
|
||||
__u8 *p;
|
||||
__u64 dcidlen;
|
||||
|
||||
if (*data & 0x80) {
|
||||
p = data + 1 + 4;
|
||||
|
||||
/* Do not check the actual DCID length because we might not buffer
|
||||
entire DCID here. */
|
||||
dcidlen = *p;
|
||||
|
||||
if (dcidlen > MAX_DCIDLEN || dcidlen < MIN_DCIDLEN) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
++p;
|
||||
|
||||
qhd->type = (*data & 0x30) >> 4;
|
||||
qhd->dcid = p;
|
||||
qhd->dcidlen = dcidlen;
|
||||
qhd->dcid_offset = 6;
|
||||
} else {
|
||||
qhd->type = NGTCP2_PKT_SHORT;
|
||||
qhd->dcid = data + 1;
|
||||
qhd->dcidlen = SV_DCIDLEN;
|
||||
qhd->dcid_offset = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __u32 hash(const __u8 *data, __u32 datalen, __u32 initval) {
|
||||
__u32 a, b;
|
||||
|
||||
a = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
|
||||
b = (data[4] << 24) | (data[5] << 16) | (data[6] << 8) | data[7];
|
||||
|
||||
return jhash_2words(a, b, initval);
|
||||
}
|
||||
|
||||
static __u32 sk_index_from_dcid(const quic_hd *qhd,
|
||||
const struct sk_reuseport_md *reuse_md,
|
||||
__u64 num_socks) {
|
||||
__u32 len = qhd->dcidlen;
|
||||
__u32 h = reuse_md->hash;
|
||||
__u8 hbuf[8];
|
||||
|
||||
if (len > 16) {
|
||||
__builtin_memset(hbuf, 0, sizeof(hbuf));
|
||||
|
||||
switch (len) {
|
||||
case 20:
|
||||
__builtin_memcpy(hbuf, qhd->dcid + 16, 4);
|
||||
break;
|
||||
case 19:
|
||||
__builtin_memcpy(hbuf, qhd->dcid + 16, 3);
|
||||
break;
|
||||
case 18:
|
||||
__builtin_memcpy(hbuf, qhd->dcid + 16, 2);
|
||||
break;
|
||||
case 17:
|
||||
__builtin_memcpy(hbuf, qhd->dcid + 16, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
h = hash(hbuf, sizeof(hbuf), h);
|
||||
len = 16;
|
||||
}
|
||||
|
||||
if (len > 8) {
|
||||
__builtin_memset(hbuf, 0, sizeof(hbuf));
|
||||
|
||||
switch (len) {
|
||||
case 16:
|
||||
__builtin_memcpy(hbuf, qhd->dcid + 8, 8);
|
||||
break;
|
||||
case 15:
|
||||
__builtin_memcpy(hbuf, qhd->dcid + 8, 7);
|
||||
break;
|
||||
case 14:
|
||||
__builtin_memcpy(hbuf, qhd->dcid + 8, 6);
|
||||
break;
|
||||
case 13:
|
||||
__builtin_memcpy(hbuf, qhd->dcid + 8, 5);
|
||||
break;
|
||||
case 12:
|
||||
__builtin_memcpy(hbuf, qhd->dcid + 8, 4);
|
||||
break;
|
||||
case 11:
|
||||
__builtin_memcpy(hbuf, qhd->dcid + 8, 3);
|
||||
break;
|
||||
case 10:
|
||||
__builtin_memcpy(hbuf, qhd->dcid + 8, 2);
|
||||
break;
|
||||
case 9:
|
||||
__builtin_memcpy(hbuf, qhd->dcid + 8, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
h = hash(hbuf, sizeof(hbuf), h);
|
||||
len = 8;
|
||||
}
|
||||
|
||||
return hash(qhd->dcid, len, h) % num_socks;
|
||||
}
|
||||
|
||||
SEC("sk_reuseport")
|
||||
int select_reuseport(struct sk_reuseport_md *reuse_md) {
|
||||
__u32 sk_index, *psk_index;
|
||||
__u64 *pnum_socks, *pkey;
|
||||
__u32 zero = 0, key_high_idx = 1, key_low_idx = 2;
|
||||
int rv;
|
||||
quic_hd qhd;
|
||||
__u8 qpktbuf[6 + MAX_DCIDLEN];
|
||||
struct AES_ctx aes_ctx;
|
||||
__u8 key[AES_KEYLEN];
|
||||
|
||||
if (bpf_skb_load_bytes(reuse_md, sizeof(struct udphdr), qpktbuf,
|
||||
sizeof(qpktbuf)) != 0) {
|
||||
return SK_DROP;
|
||||
}
|
||||
|
||||
pnum_socks = bpf_map_lookup_elem(&sk_info, &zero);
|
||||
if (pnum_socks == NULL) {
|
||||
return SK_DROP;
|
||||
}
|
||||
|
||||
pkey = bpf_map_lookup_elem(&sk_info, &key_high_idx);
|
||||
if (pkey == NULL) {
|
||||
return SK_DROP;
|
||||
}
|
||||
|
||||
__builtin_memcpy(key, pkey, sizeof(*pkey));
|
||||
|
||||
pkey = bpf_map_lookup_elem(&sk_info, &key_low_idx);
|
||||
if (pkey == NULL) {
|
||||
return SK_DROP;
|
||||
}
|
||||
|
||||
__builtin_memcpy(key + sizeof(*pkey), pkey, sizeof(*pkey));
|
||||
|
||||
rv = parse_quic(&qhd, qpktbuf, qpktbuf + sizeof(qpktbuf));
|
||||
if (rv != 0) {
|
||||
return SK_DROP;
|
||||
}
|
||||
|
||||
AES_init_ctx(&aes_ctx, key);
|
||||
|
||||
switch (qhd.type) {
|
||||
case NGTCP2_PKT_INITIAL:
|
||||
case NGTCP2_PKT_0RTT:
|
||||
if (qhd.dcidlen == SV_DCIDLEN) {
|
||||
AES_ECB_decrypt(&aes_ctx, qhd.dcid);
|
||||
|
||||
psk_index = bpf_map_lookup_elem(&cid_prefix_map, qhd.dcid);
|
||||
if (psk_index != NULL) {
|
||||
sk_index = *psk_index;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sk_index = sk_index_from_dcid(&qhd, reuse_md, *pnum_socks);
|
||||
|
||||
break;
|
||||
case NGTCP2_PKT_HANDSHAKE:
|
||||
case NGTCP2_PKT_SHORT:
|
||||
if (qhd.dcidlen != SV_DCIDLEN) {
|
||||
return SK_DROP;
|
||||
}
|
||||
|
||||
AES_ECB_decrypt(&aes_ctx, qhd.dcid);
|
||||
|
||||
psk_index = bpf_map_lookup_elem(&cid_prefix_map, qhd.dcid);
|
||||
if (psk_index == NULL) {
|
||||
sk_index = sk_index_from_dcid(&qhd, reuse_md, *pnum_socks);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
sk_index = *psk_index;
|
||||
|
||||
break;
|
||||
default:
|
||||
return SK_DROP;
|
||||
}
|
||||
|
||||
rv = bpf_sk_select_reuseport(reuse_md, &reuseport_array, &sk_index, 0);
|
||||
if (rv != 0) {
|
||||
return SK_DROP;
|
||||
}
|
||||
|
||||
return SK_PASS;
|
||||
}
|
||||
32
cmake/FindLibbpf.cmake
Normal file
32
cmake/FindLibbpf.cmake
Normal file
@@ -0,0 +1,32 @@
|
||||
# - Try to find libbpf
|
||||
# Once done this will define
|
||||
# LIBBPF_FOUND - System has libbpf
|
||||
# LIBBPF_INCLUDE_DIRS - The libbpf include directories
|
||||
# LIBBPF_LIBRARIES - The libraries needed to use libbpf
|
||||
|
||||
find_package(PkgConfig QUIET)
|
||||
pkg_check_modules(PC_LIBBPF QUIET libbpf)
|
||||
|
||||
find_path(LIBBPF_INCLUDE_DIR
|
||||
NAMES bpf/bpf.h
|
||||
HINTS ${PC_LIBBPF_INCLUDE_DIRS}
|
||||
)
|
||||
find_library(LIBBPF_LIBRARY
|
||||
NAMES bpf
|
||||
HINTS ${PC_LIBBPF_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
# handle the QUIETLY and REQUIRED arguments and set LIBBPF_FOUND
|
||||
# to TRUE if all listed variables are TRUE and the requested version
|
||||
# matches.
|
||||
find_package_handle_standard_args(Libbpf REQUIRED_VARS
|
||||
LIBBPF_LIBRARY LIBBPF_INCLUDE_DIR
|
||||
VERSION_VAR LIBBPF_VERSION)
|
||||
|
||||
if(LIBBPF_FOUND)
|
||||
set(LIBBPF_LIBRARIES ${LIBBPF_LIBRARY})
|
||||
set(LIBBPF_INCLUDE_DIRS ${LIBBPF_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
mark_as_advanced(LIBBPF_INCLUDE_DIR LIBBPF_LIBRARY)
|
||||
41
cmake/FindLibnghttp3.cmake
Normal file
41
cmake/FindLibnghttp3.cmake
Normal file
@@ -0,0 +1,41 @@
|
||||
# - Try to find libnghttp3
|
||||
# Once done this will define
|
||||
# LIBNGHTTP3_FOUND - System has libnghttp3
|
||||
# LIBNGHTTP3_INCLUDE_DIRS - The libnghttp3 include directories
|
||||
# LIBNGHTTP3_LIBRARIES - The libraries needed to use libnghttp3
|
||||
|
||||
find_package(PkgConfig QUIET)
|
||||
pkg_check_modules(PC_LIBNGHTTP3 QUIET libnghttp3)
|
||||
|
||||
find_path(LIBNGHTTP3_INCLUDE_DIR
|
||||
NAMES nghttp3/nghttp3.h
|
||||
HINTS ${PC_LIBNGHTTP3_INCLUDE_DIRS}
|
||||
)
|
||||
find_library(LIBNGHTTP3_LIBRARY
|
||||
NAMES nghttp3
|
||||
HINTS ${PC_LIBNGHTTP3_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
if(LIBNGHTTP3_INCLUDE_DIR)
|
||||
set(_version_regex "^#define[ \t]+NGHTTP3_VERSION[ \t]+\"([^\"]+)\".*")
|
||||
file(STRINGS "${LIBNGHTTP3_INCLUDE_DIR}/nghttp3/version.h"
|
||||
LIBNGHTTP3_VERSION REGEX "${_version_regex}")
|
||||
string(REGEX REPLACE "${_version_regex}" "\\1"
|
||||
LIBNGHTTP3_VERSION "${LIBNGHTTP3_VERSION}")
|
||||
unset(_version_regex)
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
# handle the QUIETLY and REQUIRED arguments and set LIBNGHTTP3_FOUND
|
||||
# to TRUE if all listed variables are TRUE and the requested version
|
||||
# matches.
|
||||
find_package_handle_standard_args(Libnghttp3 REQUIRED_VARS
|
||||
LIBNGHTTP3_LIBRARY LIBNGHTTP3_INCLUDE_DIR
|
||||
VERSION_VAR LIBNGHTTP3_VERSION)
|
||||
|
||||
if(LIBNGHTTP3_FOUND)
|
||||
set(LIBNGHTTP3_LIBRARIES ${LIBNGHTTP3_LIBRARY})
|
||||
set(LIBNGHTTP3_INCLUDE_DIRS ${LIBNGHTTP3_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
mark_as_advanced(LIBNGHTTP3_INCLUDE_DIR LIBNGHTTP3_LIBRARY)
|
||||
41
cmake/FindLibngtcp2.cmake
Normal file
41
cmake/FindLibngtcp2.cmake
Normal file
@@ -0,0 +1,41 @@
|
||||
# - Try to find libngtcp2
|
||||
# Once done this will define
|
||||
# LIBNGTCP2_FOUND - System has libngtcp2
|
||||
# LIBNGTCP2_INCLUDE_DIRS - The libngtcp2 include directories
|
||||
# LIBNGTCP2_LIBRARIES - The libraries needed to use libngtcp2
|
||||
|
||||
find_package(PkgConfig QUIET)
|
||||
pkg_check_modules(PC_LIBNGTCP2 QUIET libngtcp2)
|
||||
|
||||
find_path(LIBNGTCP2_INCLUDE_DIR
|
||||
NAMES ngtcp2/ngtcp2.h
|
||||
HINTS ${PC_LIBNGTCP2_INCLUDE_DIRS}
|
||||
)
|
||||
find_library(LIBNGTCP2_LIBRARY
|
||||
NAMES ngtcp2
|
||||
HINTS ${PC_LIBNGTCP2_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
if(LIBNGTCP2_INCLUDE_DIR)
|
||||
set(_version_regex "^#define[ \t]+NGTCP2_VERSION[ \t]+\"([^\"]+)\".*")
|
||||
file(STRINGS "${LIBNGTCP2_INCLUDE_DIR}/ngtcp2/version.h"
|
||||
LIBNGTCP2_VERSION REGEX "${_version_regex}")
|
||||
string(REGEX REPLACE "${_version_regex}" "\\1"
|
||||
LIBNGTCP2_VERSION "${LIBNGTCP2_VERSION}")
|
||||
unset(_version_regex)
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
# handle the QUIETLY and REQUIRED arguments and set LIBNGTCP2_FOUND
|
||||
# to TRUE if all listed variables are TRUE and the requested version
|
||||
# matches.
|
||||
find_package_handle_standard_args(Libngtcp2 REQUIRED_VARS
|
||||
LIBNGTCP2_LIBRARY LIBNGTCP2_INCLUDE_DIR
|
||||
VERSION_VAR LIBNGTCP2_VERSION)
|
||||
|
||||
if(LIBNGTCP2_FOUND)
|
||||
set(LIBNGTCP2_LIBRARIES ${LIBNGTCP2_LIBRARY})
|
||||
set(LIBNGTCP2_INCLUDE_DIRS ${LIBNGTCP2_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
mark_as_advanced(LIBNGTCP2_INCLUDE_DIR LIBNGTCP2_LIBRARY)
|
||||
43
cmake/FindLibngtcp2_crypto_openssl.cmake
Normal file
43
cmake/FindLibngtcp2_crypto_openssl.cmake
Normal file
@@ -0,0 +1,43 @@
|
||||
# - Try to find libngtcp2_crypto_openssl
|
||||
# Once done this will define
|
||||
# LIBNGTCP2_CRYPTO_OPENSSL_FOUND - System has libngtcp2_crypto_openssl
|
||||
# LIBNGTCP2_CRYPTO_OPENSSL_INCLUDE_DIRS - The libngtcp2_crypto_openssl include directories
|
||||
# LIBNGTCP2_CRYPTO_OPENSSL_LIBRARIES - The libraries needed to use libngtcp2_crypto_openssl
|
||||
|
||||
find_package(PkgConfig QUIET)
|
||||
pkg_check_modules(PC_LIBNGTCP2_CRYPTO_OPENSSL QUIET libngtcp2_crypto_openssl)
|
||||
|
||||
find_path(LIBNGTCP2_CRYPTO_OPENSSL_INCLUDE_DIR
|
||||
NAMES ngtcp2/ngtcp2_crypto_openssl.h
|
||||
HINTS ${PC_LIBNGTCP2_CRYPTO_OPENSSL_INCLUDE_DIRS}
|
||||
)
|
||||
find_library(LIBNGTCP2_CRYPTO_OPENSSL_LIBRARY
|
||||
NAMES ngtcp2_crypto_openssl
|
||||
HINTS ${PC_LIBNGTCP2_CRYPTO_OPENSSL_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
if(LIBNGTCP2_CRYPTO_OPENSSL_INCLUDE_DIR)
|
||||
set(_version_regex "^#define[ \t]+NGTCP2_VERSION[ \t]+\"([^\"]+)\".*")
|
||||
file(STRINGS "${LIBNGTCP2_CRYPTO_OPENSSL_INCLUDE_DIR}/ngtcp2/version.h"
|
||||
LIBNGTCP2_CRYPTO_OPENSSL_VERSION REGEX "${_version_regex}")
|
||||
string(REGEX REPLACE "${_version_regex}" "\\1"
|
||||
LIBNGTCP2_CRYPTO_OPENSSL_VERSION "${LIBNGTCP2_CRYPTO_OPENSSL_VERSION}")
|
||||
unset(_version_regex)
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
# handle the QUIETLY and REQUIRED arguments and set
|
||||
# LIBNGTCP2_CRYPTO_OPENSSL_FOUND to TRUE if all listed variables are
|
||||
# TRUE and the requested version matches.
|
||||
find_package_handle_standard_args(Libngtcp2_crypto_openssl REQUIRED_VARS
|
||||
LIBNGTCP2_CRYPTO_OPENSSL_LIBRARY
|
||||
LIBNGTCP2_CRYPTO_OPENSSL_INCLUDE_DIR
|
||||
VERSION_VAR LIBNGTCP2_CRYPTO_OPENSSL_VERSION)
|
||||
|
||||
if(LIBNGTCP2_CRYPTO_OPENSSL_FOUND)
|
||||
set(LIBNGTCP2_CRYPTO_OPENSSL_LIBRARIES ${LIBNGTCP2_CRYPTO_OPENSSL_LIBRARY})
|
||||
set(LIBNGTCP2_CRYPTO_OPENSSL_INCLUDE_DIRS ${LIBNGTCP2_CRYPTO_OPENSSL_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
mark_as_advanced(LIBNGTCP2_CRYPTO_OPENSSL_INCLUDE_DIR
|
||||
LIBNGTCP2_CRYPTO_OPENSSL_LIBRARY)
|
||||
@@ -15,5 +15,5 @@ find_library(SYSTEMD_LIBRARIES NAMES systemd ${PC_SYSTEMD_LIBRARY_DIRS})
|
||||
find_path(SYSTEMD_INCLUDE_DIRS systemd/sd-login.h HINTS ${PC_SYSTEMD_INCLUDE_DIRS})
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(SYSTEMD DEFAULT_MSG SYSTEMD_INCLUDE_DIRS SYSTEMD_LIBRARIES)
|
||||
find_package_handle_standard_args(Systemd DEFAULT_MSG SYSTEMD_INCLUDE_DIRS SYSTEMD_LIBRARIES)
|
||||
mark_as_advanced(SYSTEMD_INCLUDE_DIRS SYSTEMD_LIBRARIES)
|
||||
|
||||
@@ -78,3 +78,12 @@
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#cmakedefine HAVE_UNISTD_H 1
|
||||
|
||||
/* Define to 1 if HTTP/3 is enabled. */
|
||||
#cmakedefine ENABLE_HTTP3 1
|
||||
|
||||
/* Define to 1 if you have `libbpf` library. */
|
||||
#cmakedefine HAVE_LIBBPF 1
|
||||
|
||||
/* Define to 1 if you have enum bpf_stats_type in linux/bpf.h. */
|
||||
#cmakedefine HAVE_BPF_STATS_TYPE 1
|
||||
|
||||
482
configure.ac
482
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.42.0-DEV], [t-tujikawa@users.sourceforge.net])
|
||||
AC_INIT([nghttp2], [1.45.1], [t-tujikawa@users.sourceforge.net])
|
||||
AC_CONFIG_AUX_DIR([.])
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
@@ -43,10 +43,10 @@ AM_INIT_AUTOMAKE([subdir-objects])
|
||||
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, 34)
|
||||
dnl https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
||||
AC_SUBST(LT_CURRENT, 35)
|
||||
AC_SUBST(LT_REVISION, 0)
|
||||
AC_SUBST(LT_AGE, 20)
|
||||
AC_SUBST(LT_AGE, 21)
|
||||
|
||||
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"`
|
||||
@@ -107,11 +107,51 @@ AC_ARG_ENABLE([lib-only],
|
||||
[Build libnghttp2 only. This is a short hand for --disable-app --disable-examples --disable-hpack-tools --disable-python-bindings])],
|
||||
[request_lib_only=$enableval], [request_lib_only=no])
|
||||
|
||||
AC_ARG_ENABLE([http3],
|
||||
[AS_HELP_STRING([--enable-http3],
|
||||
[(EXPERIMENTAL) Enable HTTP/3. This requires ngtcp2, nghttp3, and a custom OpenSSL.])],
|
||||
[request_http3=$enableval], [request_http3=no])
|
||||
|
||||
AC_ARG_WITH([libxml2],
|
||||
[AS_HELP_STRING([--with-libxml2],
|
||||
[Use libxml2 [default=check]])],
|
||||
[request_libxml2=$withval], [request_libxml2=check])
|
||||
|
||||
AC_ARG_WITH([jansson],
|
||||
[AS_HELP_STRING([--with-jansson],
|
||||
[Use jansson [default=check]])],
|
||||
[request_jansson=$withval], [request_jansson=check])
|
||||
|
||||
AC_ARG_WITH([zlib],
|
||||
[AS_HELP_STRING([--with-zlib],
|
||||
[Use zlib [default=check]])],
|
||||
[request_zlib=$withval], [request_zlib=check])
|
||||
|
||||
AC_ARG_WITH([libevent-openssl],
|
||||
[AS_HELP_STRING([--with-libevent-openssl],
|
||||
[Use libevent_openssl [default=check]])],
|
||||
[request_libevent_openssl=$withval], [request_libevent_openssl=check])
|
||||
|
||||
AC_ARG_WITH([libcares],
|
||||
[AS_HELP_STRING([--with-libcares],
|
||||
[Use libc-ares [default=check]])],
|
||||
[request_libcares=$withval], [request_libcares=check])
|
||||
|
||||
AC_ARG_WITH([openssl],
|
||||
[AS_HELP_STRING([--with-openssl],
|
||||
[Use openssl [default=check]])],
|
||||
[request_openssl=$withval], [request_openssl=check])
|
||||
|
||||
AC_ARG_WITH([libev],
|
||||
[AS_HELP_STRING([--with-libev],
|
||||
[Use libev [default=check]])],
|
||||
[request_libev=$withval], [request_libev=check])
|
||||
|
||||
AC_ARG_WITH([cunit],
|
||||
[AS_HELP_STRING([--with-cunit],
|
||||
[Use cunit [default=check]])],
|
||||
[request_cunit=$withval], [request_cunit=check])
|
||||
|
||||
AC_ARG_WITH([jemalloc],
|
||||
[AS_HELP_STRING([--with-jemalloc],
|
||||
[Use jemalloc [default=check]])],
|
||||
@@ -137,9 +177,36 @@ AC_ARG_WITH([cython],
|
||||
[Use cython in given PATH])],
|
||||
[cython_path=$withval], [])
|
||||
|
||||
AC_ARG_WITH([libngtcp2],
|
||||
[AS_HELP_STRING([--with-libngtcp2],
|
||||
[Use libngtcp2 [default=check]])],
|
||||
[request_libngtcp2=$withval], [request_libngtcp2=check])
|
||||
|
||||
AC_ARG_WITH([libnghttp3],
|
||||
[AS_HELP_STRING([--with-libnghttp3],
|
||||
[Use libnghttp3 [default=check]])],
|
||||
[request_libnghttp3=$withval], [request_libnghttp3=check])
|
||||
|
||||
AC_ARG_WITH([libbpf],
|
||||
[AS_HELP_STRING([--with-libbpf],
|
||||
[Use libbpf [default=no]])],
|
||||
[request_libbpf=$withval], [request_libbpf=no])
|
||||
|
||||
dnl Define variables
|
||||
AC_ARG_VAR([CYTHON], [the Cython executable])
|
||||
|
||||
AC_ARG_VAR([LIBEV_CFLAGS], [C compiler flags for libev, skipping any checks])
|
||||
AC_ARG_VAR([LIBEV_LIBS], [linker flags for libev, skipping any checks])
|
||||
|
||||
AC_ARG_VAR([JEMALLOC_CFLAGS],
|
||||
[C compiler flags for jemalloc, skipping any checks])
|
||||
AC_ARG_VAR([JEMALLOC_LIBS], [linker flags for jemalloc, skipping any checks])
|
||||
|
||||
AC_ARG_VAR([LIBTOOL_LDFLAGS],
|
||||
[libtool specific flags (e.g., -static-libtool-libs)])
|
||||
|
||||
AC_ARG_VAR([BPFCFLAGS], [C compiler flags for bpf program])
|
||||
|
||||
dnl Checks for programs
|
||||
AC_PROG_CC
|
||||
AC_PROG_CXX
|
||||
@@ -151,7 +218,7 @@ AC_PROG_MKDIR_P
|
||||
|
||||
PKG_PROG_PKG_CONFIG([0.20])
|
||||
|
||||
AM_PATH_PYTHON([2.7],, [:])
|
||||
AM_PATH_PYTHON([3.8],, [:])
|
||||
|
||||
if [test "x$request_lib_only" = "xyes"]; then
|
||||
request_app=no
|
||||
@@ -161,7 +228,7 @@ if [test "x$request_lib_only" = "xyes"]; then
|
||||
fi
|
||||
|
||||
if [test "x$request_python_bindings" != "xno"]; then
|
||||
AX_PYTHON_DEVEL([>= '2.7'])
|
||||
AX_PYTHON_DEVEL([>= '3.8'])
|
||||
fi
|
||||
|
||||
if test "x${cython_path}" = "x"; then
|
||||
@@ -200,6 +267,7 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
|
||||
]],
|
||||
[[
|
||||
std::vector<std::future<int>> v;
|
||||
(void)v;
|
||||
]])],
|
||||
[AC_DEFINE([HAVE_STD_FUTURE], [1],
|
||||
[Define to 1 if you have the `std::future`.])
|
||||
@@ -289,11 +357,26 @@ case "$host_os" in
|
||||
;;
|
||||
esac
|
||||
|
||||
# zlib
|
||||
PKG_CHECK_MODULES([ZLIB], [zlib >= 1.2.3], [have_zlib=yes], [have_zlib=no])
|
||||
case "${build}" in
|
||||
*-apple-darwin*)
|
||||
EXTRA_DEFS="-D__APPLE_USE_RFC_3542"
|
||||
AC_SUBST([EXTRA_DEFS])
|
||||
;;
|
||||
esac
|
||||
|
||||
if test "x${have_zlib}" = "xno"; then
|
||||
AC_MSG_NOTICE($ZLIB_PKG_ERRORS)
|
||||
# zlib
|
||||
have_zlib=no
|
||||
if test "x${request_zlib}" != "xno"; then
|
||||
PKG_CHECK_MODULES([ZLIB], [zlib >= 1.2.3], [have_zlib=yes], [have_zlib=no])
|
||||
|
||||
if test "x${have_zlib}" = "xno"; then
|
||||
AC_MSG_NOTICE($ZLIB_PKG_ERRORS)
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "x${request_zlib}" = "xyes" &&
|
||||
test "x${have_zlib}" != "xyes"; then
|
||||
AC_MSG_ERROR([zlib was requested (--with-zlib) but not found])
|
||||
fi
|
||||
|
||||
# dl: openssl requires libdl when it is statically linked.
|
||||
@@ -309,82 +392,242 @@ case "${host_os}" in
|
||||
esac
|
||||
|
||||
# cunit
|
||||
PKG_CHECK_MODULES([CUNIT], [cunit >= 2.1], [have_cunit=yes], [have_cunit=no])
|
||||
# If pkg-config does not find cunit, check it using AC_CHECK_LIB. We
|
||||
# do this because Debian (Ubuntu) lacks pkg-config file for cunit.
|
||||
if test "x${have_cunit}" = "xno"; then
|
||||
AC_MSG_WARN([${CUNIT_PKG_ERRORS}])
|
||||
AC_CHECK_LIB([cunit], [CU_initialize_registry],
|
||||
[have_cunit=yes], [have_cunit=no])
|
||||
have_cunit=no
|
||||
if test "x${request_cunit}" != "xno"; then
|
||||
PKG_CHECK_MODULES([CUNIT], [cunit >= 2.1], [have_cunit=yes], [have_cunit=no])
|
||||
# If pkg-config does not find cunit, check it using AC_CHECK_LIB. We
|
||||
# do this because Debian (Ubuntu) lacks pkg-config file for cunit.
|
||||
if test "x${have_cunit}" = "xno"; then
|
||||
AC_MSG_WARN([${CUNIT_PKG_ERRORS}])
|
||||
AC_CHECK_LIB([cunit], [CU_initialize_registry],
|
||||
[have_cunit=yes], [have_cunit=no])
|
||||
if test "x${have_cunit}" = "xyes"; then
|
||||
CUNIT_LIBS="-lcunit"
|
||||
CUNIT_CFLAGS=""
|
||||
AC_SUBST([CUNIT_LIBS])
|
||||
AC_SUBST([CUNIT_CFLAGS])
|
||||
fi
|
||||
fi
|
||||
if test "x${have_cunit}" = "xyes"; then
|
||||
CUNIT_LIBS="-lcunit"
|
||||
CUNIT_CFLAGS=""
|
||||
AC_SUBST([CUNIT_LIBS])
|
||||
AC_SUBST([CUNIT_CFLAGS])
|
||||
# cunit in Mac OS X requires ncurses. Note that in Mac OS X, test
|
||||
# program can be built without -lncurses, but it emits runtime
|
||||
# error.
|
||||
case "${build}" in
|
||||
*-apple-darwin*)
|
||||
CUNIT_LIBS="$CUNIT_LIBS -lncurses"
|
||||
AC_SUBST([CUNIT_LIBS])
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
fi
|
||||
if test "x${have_cunit}" = "xyes"; then
|
||||
# cunit in Mac OS X requires ncurses. Note that in Mac OS X, test
|
||||
# program can be built without -lncurses, but it emits runtime
|
||||
# error.
|
||||
case "${build}" in
|
||||
*-apple-darwin*)
|
||||
CUNIT_LIBS="$CUNIT_LIBS -lncurses"
|
||||
AC_SUBST([CUNIT_LIBS])
|
||||
;;
|
||||
esac
|
||||
|
||||
if test "x${request_cunit}" = "xyes" &&
|
||||
test "x${have_cunit}" != "xyes"; then
|
||||
AC_MSG_ERROR([cunit was requested (--with-cunit) but not found])
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL([HAVE_CUNIT], [ test "x${have_cunit}" = "xyes" ])
|
||||
|
||||
# libev (for src)
|
||||
# libev does not have pkg-config file. Check it in an old way.
|
||||
save_LIBS=$LIBS
|
||||
# android requires -lm for floor
|
||||
AC_CHECK_LIB([ev], [ev_time], [have_libev=yes], [have_libev=no], [-lm])
|
||||
if test "x${have_libev}" = "xyes"; then
|
||||
AC_CHECK_HEADER([ev.h], [have_libev=yes], [have_libev=no])
|
||||
if test "x${have_libev}" = "xyes"; then
|
||||
LIBEV_LIBS=-lev
|
||||
LIBEV_CFLAGS=
|
||||
AC_SUBST([LIBEV_LIBS])
|
||||
AC_SUBST([LIBEV_CFLAGS])
|
||||
have_libev=no
|
||||
if test "x${request_libev}" != "xno"; then
|
||||
if test "x${LIBEV_LIBS}" = "x" && test "x${LIBEV_CFLAGS}" = "x"; then
|
||||
# libev does not have pkg-config file. Check it in an old way.
|
||||
save_LIBS=$LIBS
|
||||
# android requires -lm for floor
|
||||
AC_CHECK_LIB([ev], [ev_time], [have_libev=yes], [have_libev=no], [-lm])
|
||||
if test "x${have_libev}" = "xyes"; then
|
||||
AC_CHECK_HEADER([ev.h], [have_libev=yes], [have_libev=no])
|
||||
if test "x${have_libev}" = "xyes"; then
|
||||
LIBEV_LIBS=-lev
|
||||
LIBEV_CFLAGS=
|
||||
fi
|
||||
fi
|
||||
LIBS=$save_LIBS
|
||||
else
|
||||
have_libev=yes
|
||||
fi
|
||||
fi
|
||||
LIBS=$save_LIBS
|
||||
|
||||
if test "x${request_libev}" = "xyes" &&
|
||||
test "x${have_libev}" != "xyes"; then
|
||||
AC_MSG_ERROR([libev was requested (--with-libev) but not found])
|
||||
fi
|
||||
|
||||
# openssl (for src)
|
||||
PKG_CHECK_MODULES([OPENSSL], [openssl >= 1.0.1],
|
||||
[have_openssl=yes], [have_openssl=no])
|
||||
if test "x${have_openssl}" = "xno"; then
|
||||
AC_MSG_NOTICE($OPENSSL_PKG_ERRORS)
|
||||
have_openssl=no
|
||||
if test "x${request_openssl}" != "xno"; then
|
||||
PKG_CHECK_MODULES([OPENSSL], [openssl >= 1.0.1],
|
||||
[have_openssl=yes], [have_openssl=no])
|
||||
if test "x${have_openssl}" = "xno"; then
|
||||
AC_MSG_NOTICE($OPENSSL_PKG_ERRORS)
|
||||
else
|
||||
save_CFLAGS="$CFLAGS"
|
||||
save_LIBS="$LIBS"
|
||||
CFLAGS="$OPENSSL_CFLAGS $CFLAGS"
|
||||
LIBS="$OPENSSL_LIBS $LIBS"
|
||||
|
||||
have_ssl_is_quic=no
|
||||
AC_MSG_CHECKING([for SSL_is_quic])
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
|
||||
#include <openssl/ssl.h>
|
||||
]], [[
|
||||
SSL *ssl = NULL;
|
||||
SSL_is_quic(ssl);
|
||||
]])],
|
||||
[AC_MSG_RESULT([yes]); have_ssl_is_quic=yes],
|
||||
[AC_MSG_RESULT([no]); have_ssl_is_quic=no])
|
||||
|
||||
CFLAGS="$save_CFLAGS"
|
||||
LIBS="$save_LIBS"
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "x${request_openssl}" = "xyes" &&
|
||||
test "x${have_openssl}" != "xyes"; then
|
||||
AC_MSG_ERROR([openssl was requested (--with-openssl) but not found])
|
||||
fi
|
||||
|
||||
# c-ares (for src)
|
||||
PKG_CHECK_MODULES([LIBCARES], [libcares >= 1.7.5], [have_libcares=yes],
|
||||
[have_libcares=no])
|
||||
if test "x${have_libcares}" = "xno"; then
|
||||
AC_MSG_NOTICE($LIBCARES_PKG_ERRORS)
|
||||
have_libcares=no
|
||||
if test "x${request_libcares}" != "xno"; then
|
||||
PKG_CHECK_MODULES([LIBCARES], [libcares >= 1.7.5], [have_libcares=yes],
|
||||
[have_libcares=no])
|
||||
if test "x${have_libcares}" = "xno"; then
|
||||
AC_MSG_NOTICE($LIBCARES_PKG_ERRORS)
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "x${request_libcares}" = "xyes" &&
|
||||
test "x${have_libcares}" != "xyes"; then
|
||||
AC_MSG_ERROR([libcares was requested (--with-libcares) but not found])
|
||||
fi
|
||||
|
||||
# ngtcp2 (for src)
|
||||
have_libngtcp2=no
|
||||
if test "x${request_libngtcp2}" != "xno"; then
|
||||
PKG_CHECK_MODULES([LIBNGTCP2], [libngtcp2 >= 0.0.0], [have_libngtcp2=yes],
|
||||
[have_libngtcp2=no])
|
||||
if test "x${have_libngtcp2}" = "xno"; then
|
||||
AC_MSG_NOTICE($LIBNGTCP2_PKG_ERRORS)
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "x${request_libngtcp2}" = "xyes" &&
|
||||
test "x${have_libngtcp2}" != "xyes"; then
|
||||
AC_MSG_ERROR([libngtcp2 was requested (--with-libngtcp2) but not found])
|
||||
fi
|
||||
|
||||
# ngtcp2_crypto_openssl (for src)
|
||||
have_libngtcp2_crypto_openssl=no
|
||||
if test "x${request_libngtcp2}" != "xno"; then
|
||||
PKG_CHECK_MODULES([LIBNGTCP2_CRYPTO_OPENSSL],
|
||||
[libngtcp2_crypto_openssl >= 0.0.0],
|
||||
[have_libngtcp2_crypto_openssl=yes],
|
||||
[have_libngtcp2_crypto_openssl=no])
|
||||
if test "x${have_libngtcp2_crypto_openssl}" = "xno"; then
|
||||
AC_MSG_NOTICE($LIBNGTCP2_CRYPTO_OPENSSL_PKG_ERRORS)
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "x${request_libngtcp2}" = "xyes" &&
|
||||
test "x${have_libngtcp2_crypto_openssl}" != "xyes"; then
|
||||
AC_MSG_ERROR([libngtcp2_crypto_openssl was requested (--with-libngtcp2) but not found])
|
||||
fi
|
||||
|
||||
# nghttp3 (for src)
|
||||
have_libnghttp3=no
|
||||
if test "x${request_libnghttp3}" != "xno"; then
|
||||
PKG_CHECK_MODULES([LIBNGHTTP3], [libnghttp3 >= 0.0.0], [have_libnghttp3=yes],
|
||||
[have_libnghttp3=no])
|
||||
if test "x${have_libnghttp3}" = "xno"; then
|
||||
AC_MSG_NOTICE($LIBNGHTTP3_PKG_ERRORS)
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "x${request_libnghttp3}" = "xyes" &&
|
||||
test "x${have_libnghttp3}" != "xyes"; then
|
||||
AC_MSG_ERROR([libnghttp3 was requested (--with-libnghttp3) but not found])
|
||||
fi
|
||||
|
||||
# libbpf (for src)
|
||||
have_libbpf=no
|
||||
if test "x${request_libbpf}" != "xno"; then
|
||||
PKG_CHECK_MODULES([LIBBPF], [libbpf >= 0.4.0], [have_libbpf=yes],
|
||||
[have_libbpf=no])
|
||||
if test "x${have_libbpf}" = "xyes"; then
|
||||
AC_DEFINE([HAVE_LIBBPF], [1], [Define to 1 if you have `libbpf` library.])
|
||||
if test "x${BPFCFLAGS}" = "x"; then
|
||||
BPFCFLAGS="-Wall -O2 -g"
|
||||
fi
|
||||
# Add the include path for Debian
|
||||
EXTRABPFCFLAGS="-I/usr/include/$host_cpu-$host_os"
|
||||
AC_SUBST([EXTRABPFCFLAGS])
|
||||
|
||||
AC_MSG_CHECKING([whether enum bpf_stats_type is defined in linux/bpf.h])
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
|
||||
[[
|
||||
#include <linux/bpf.h>
|
||||
]],
|
||||
[[
|
||||
enum bpf_stats_type foo;
|
||||
(void)foo;
|
||||
]])],
|
||||
[have_bpf_stats_type=yes],
|
||||
[have_bpf_stats_type=no])
|
||||
|
||||
if test "x${have_bpf_stats_type}" = "xyes"; then
|
||||
AC_MSG_RESULT([yes])
|
||||
AC_DEFINE([HAVE_BPF_STATS_TYPE], [1],
|
||||
[Define to 1 if you have enum bpf_stats_type in linux/bpf.h.])
|
||||
else
|
||||
AC_MSG_RESULT([no])
|
||||
fi
|
||||
else
|
||||
AC_MSG_NOTICE($LIBBPF_PKG_ERRORS)
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "x${request_libbpf}" = "xyes" &&
|
||||
test "x${have_libbpf}" != "xyes"; then
|
||||
AC_MSG_ERROR([libbpf was requested (--with-libbpf) but not found])
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL([HAVE_LIBBPF], [ test "x${have_libbpf}" = "xyes" ])
|
||||
|
||||
# libevent_openssl (for examples)
|
||||
# 2.0.8 is required because we use evconnlistener_set_error_cb()
|
||||
PKG_CHECK_MODULES([LIBEVENT_OPENSSL], [libevent_openssl >= 2.0.8],
|
||||
[have_libevent_openssl=yes], [have_libevent_openssl=no])
|
||||
if test "x${have_libevent_openssl}" = "xno"; then
|
||||
AC_MSG_NOTICE($LIBEVENT_OPENSSL_PKG_ERRORS)
|
||||
have_libevent_openssl=no
|
||||
if test "x${request_libevent_openssl}" != "xno"; then
|
||||
PKG_CHECK_MODULES([LIBEVENT_OPENSSL], [libevent_openssl >= 2.0.8],
|
||||
[have_libevent_openssl=yes], [have_libevent_openssl=no])
|
||||
if test "x${have_libevent_openssl}" = "xno"; then
|
||||
AC_MSG_NOTICE($LIBEVENT_OPENSSL_PKG_ERRORS)
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "x${request_libevent_openssl}" = "xyes" &&
|
||||
test "x${have_libevent_openssl}" != "xyes"; then
|
||||
AC_MSG_ERROR([libevent_openssl was requested (--with-libevent) but not found])
|
||||
fi
|
||||
|
||||
# jansson (for src/nghttp, src/deflatehd and src/inflatehd)
|
||||
PKG_CHECK_MODULES([JANSSON], [jansson >= 2.5],
|
||||
[have_jansson=yes], [have_jansson=no])
|
||||
if test "x${have_jansson}" = "xyes"; then
|
||||
AC_DEFINE([HAVE_JANSSON], [1],
|
||||
[Define to 1 if you have `libjansson` library.])
|
||||
else
|
||||
AC_MSG_NOTICE($JANSSON_PKG_ERRORS)
|
||||
have_jansson=no
|
||||
if test "x${request_jansson}" != "xno"; then
|
||||
PKG_CHECK_MODULES([JANSSON], [jansson >= 2.5],
|
||||
[have_jansson=yes], [have_jansson=no])
|
||||
if test "x${have_jansson}" = "xyes"; then
|
||||
AC_DEFINE([HAVE_JANSSON], [1],
|
||||
[Define to 1 if you have `libjansson` library.])
|
||||
else
|
||||
AC_MSG_NOTICE($JANSSON_PKG_ERRORS)
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "x${request_jansson}" = "xyes" &&
|
||||
test "x${have_jansson}" != "xyes"; then
|
||||
AC_MSG_ERROR([jansson was requested (--with-jansson) but not found])
|
||||
fi
|
||||
|
||||
# libsystemd (for src/nghttpx)
|
||||
have_libsystemd=no
|
||||
@@ -405,12 +648,15 @@ if test "x${request_systemd}" = "xyes" &&
|
||||
fi
|
||||
|
||||
# libxml2 (for src/nghttp)
|
||||
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.])
|
||||
else
|
||||
AC_MSG_NOTICE($LIBXML2_PKG_ERRORS)
|
||||
have_libxml2=no
|
||||
if test "x${request_libxml2}" != "xno"; then
|
||||
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.])
|
||||
else
|
||||
AC_MSG_NOTICE($LIBXML2_PKG_ERRORS)
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "x${request_libxml2}" = "xyes" &&
|
||||
@@ -423,28 +669,31 @@ AM_CONDITIONAL([HAVE_LIBXML2], [ test "x${have_libxml2}" = "xyes" ])
|
||||
# jemalloc
|
||||
have_jemalloc=no
|
||||
if test "x${request_jemalloc}" != "xno"; then
|
||||
save_LIBS=$LIBS
|
||||
AC_SEARCH_LIBS([malloc_stats_print], [jemalloc], [have_jemalloc=yes], [],
|
||||
[$PTHREAD_LDFLAGS])
|
||||
|
||||
if test "x${have_jemalloc}" = "xyes"; then
|
||||
jemalloc_libs=${ac_cv_search_malloc_stats_print}
|
||||
else
|
||||
# On Darwin, malloc_stats_print is je_malloc_stats_print
|
||||
AC_SEARCH_LIBS([je_malloc_stats_print], [jemalloc], [have_jemalloc=yes], [],
|
||||
if test "x${JEMALLOC_LIBS}" = "x" && test "x${JEMALLOC_CFLAGS}" = "x"; then
|
||||
save_LIBS=$LIBS
|
||||
AC_SEARCH_LIBS([malloc_stats_print], [jemalloc], [have_jemalloc=yes], [],
|
||||
[$PTHREAD_LDFLAGS])
|
||||
|
||||
if test "x${have_jemalloc}" = "xyes"; then
|
||||
jemalloc_libs=${ac_cv_search_je_malloc_stats_print}
|
||||
jemalloc_libs=${ac_cv_search_malloc_stats_print}
|
||||
else
|
||||
# On Darwin, malloc_stats_print is je_malloc_stats_print
|
||||
AC_SEARCH_LIBS([je_malloc_stats_print], [jemalloc], [have_jemalloc=yes], [],
|
||||
[$PTHREAD_LDFLAGS])
|
||||
|
||||
if test "x${have_jemalloc}" = "xyes"; then
|
||||
jemalloc_libs=${ac_cv_search_je_malloc_stats_print}
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
LIBS=$save_LIBS
|
||||
LIBS=$save_LIBS
|
||||
|
||||
if test "x${have_jemalloc}" = "xyes" &&
|
||||
test "x${jemalloc_libs}" != "xnone required"; then
|
||||
JEMALLOC_LIBS=${jemalloc_libs}
|
||||
AC_SUBST([JEMALLOC_LIBS])
|
||||
if test "x${have_jemalloc}" = "xyes" &&
|
||||
test "x${jemalloc_libs}" != "xnone required"; then
|
||||
JEMALLOC_LIBS=${jemalloc_libs}
|
||||
fi
|
||||
else
|
||||
have_jemalloc=yes
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -490,6 +739,24 @@ fi
|
||||
|
||||
AM_CONDITIONAL([ENABLE_APP], [ test "x${enable_app}" = "xyes" ])
|
||||
|
||||
# Check HTTP/3 support
|
||||
enable_http3=no
|
||||
if test "x${request_http3}" != "xno" &&
|
||||
test "x${have_ssl_is_quic}" = "xyes" &&
|
||||
test "x${have_libngtcp2}" = "xyes" &&
|
||||
test "x${have_libngtcp2_crypto_openssl}" = "xyes" &&
|
||||
test "x${have_libnghttp3}" = "xyes"; then
|
||||
enable_http3=yes
|
||||
AC_DEFINE([ENABLE_HTTP3], [1], [Define to 1 if HTTP/3 is enabled.])
|
||||
fi
|
||||
|
||||
if test "x${request_http3}" = "xyes" &&
|
||||
test "x${enable_http3}" != "xyes"; then
|
||||
AC_MSG_ERROR([HTTP/3 was requested (--enable-http3) but dependencies are not met.])
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL([ENABLE_HTTP3], [ test "x${enable_http3}" = "xyes" ])
|
||||
|
||||
enable_hpack_tools=no
|
||||
# HPACK tools requires jansson
|
||||
if test "x${request_hpack_tools}" != "xno" &&
|
||||
@@ -567,7 +834,7 @@ enable_python_bindings=no
|
||||
if test "x${request_python_bindings}" != "xno" &&
|
||||
test "x${CYTHON}" != "x" &&
|
||||
test "x${PYTHON}" != "x:" &&
|
||||
test "x${have_python_dev}" = "xyes"; then
|
||||
test "x${PYTHON_VERSION}" != "x"; then
|
||||
enable_python_bindings=yes
|
||||
fi
|
||||
|
||||
@@ -716,6 +983,34 @@ AC_CHECK_DECLS([initgroups], [], [], [[
|
||||
#include <grp.h>
|
||||
]])
|
||||
|
||||
have_netinet_udp_h_udp_segment=no
|
||||
AC_CHECK_DECL([UDP_SEGMENT], [have_netinet_udp_h_udp_segment=yes],
|
||||
[have_netinet_udp_h_udp_segment=no], [[
|
||||
#include <netinet/udp.h>
|
||||
]])
|
||||
|
||||
if test "x$have_netinet_udp_h_udp_segment" = "xno"; then
|
||||
have_linux_udp_h_udp_segment=no
|
||||
AC_MSG_CHECKING([whether UDP_SEGMENT is defined as 103 in linux/udp.h])
|
||||
AC_RUN_IFELSE([AC_LANG_PROGRAM(
|
||||
[[
|
||||
#include <linux/udp.h>
|
||||
]],
|
||||
[[
|
||||
#if UDP_SEGMENT != 103
|
||||
exit(1)
|
||||
#endif
|
||||
]])],
|
||||
[have_linux_udp_h_udp_segment=yes
|
||||
AC_MSG_RESULT([yes])],
|
||||
[have_linux_udp_h_udp_segment=no
|
||||
AC_MSG_RESULT([no])])
|
||||
|
||||
if test "x$have_linux_udp_h_udp_segment" = "xyes"; then
|
||||
EXTRA_DEFS="$EXTRA_DEFS -DUDP_SEGMENT=103"
|
||||
fi
|
||||
fi
|
||||
|
||||
save_CFLAGS=$CFLAGS
|
||||
save_CXXFLAGS=$CXXFLAGS
|
||||
|
||||
@@ -831,6 +1126,7 @@ AC_CONFIG_FILES([
|
||||
src/Makefile
|
||||
src/includes/Makefile
|
||||
src/libnghttp2_asio.pc
|
||||
bpf/Makefile
|
||||
examples/Makefile
|
||||
python/Makefile
|
||||
python/setup.py
|
||||
@@ -882,17 +1178,22 @@ AC_MSG_NOTICE([summary of build options:
|
||||
WARNCXXFLAGS: ${WARNCXXFLAGS}
|
||||
CXX1XCXXFLAGS: ${CXX1XCXXFLAGS}
|
||||
EXTRACFLAG: ${EXTRACFLAG}
|
||||
BPFCFLAGS: ${BPFCFLAGS}
|
||||
EXTRABPFCFLAGS: ${EXTRABPFCFLAGS}
|
||||
LIBS: ${LIBS}
|
||||
DEFS: ${DEFS}
|
||||
EXTRA_DEFS: ${EXTRA_DEFS}
|
||||
Library:
|
||||
Shared: ${enable_shared}
|
||||
Static: ${enable_static}
|
||||
Libtool:
|
||||
LIBTOOL_LDFLAGS: ${LIBTOOL_LDFLAGS}
|
||||
Python:
|
||||
Python: ${PYTHON}
|
||||
PYTHON_VERSION: ${PYTHON_VERSION}
|
||||
pyexecdir: ${pyexecdir}
|
||||
Python-dev: ${have_python_dev}
|
||||
PYTHON_CPPFLAGS:${PYTHON_CPPFLAGS}
|
||||
PYTHON_LDFLAGS: ${PYTHON_LDFLAGS}
|
||||
PYTHON_LIBS: ${PYTHON_LIBS}
|
||||
Cython: ${CYTHON}
|
||||
Test:
|
||||
CUnit: ${have_cunit} (CFLAGS='${CUNIT_CFLAGS}' LIBS='${CUNIT_LIBS}')
|
||||
@@ -902,9 +1203,13 @@ AC_MSG_NOTICE([summary of build options:
|
||||
Libxml2: ${have_libxml2} (CFLAGS='${LIBXML2_CFLAGS}' LIBS='${LIBXML2_LIBS}')
|
||||
Libev: ${have_libev} (CFLAGS='${LIBEV_CFLAGS}' LIBS='${LIBEV_LIBS}')
|
||||
Libc-ares: ${have_libcares} (CFLAGS='${LIBCARES_CFLAGS}' LIBS='${LIBCARES_LIBS}')
|
||||
libngtcp2: ${have_libngtcp2} (CFLAGS='${LIBNGTCP2_CFLAGS}' LIBS='${LIBNGTCP2_LIBS}')
|
||||
libngtcp2_crypto_openssl: ${have_libngtcp2_crypto_openssl} (CFLAGS='${LIBNGTCP2_CRYPTO_OPENSSL_CFLAGS}' LIBS='${LIBNGTCP2_CRYPTO_OPENSSL_LIBS}')
|
||||
libnghttp3: ${have_libnghttp3} (CFLAGS='${LIBNGHTTP3_CFLAGS}' LIBS='${LIBNGHTTP3_LIBS}')
|
||||
libbpf: ${have_libbpf} (CFLAGS='${LIBBPF_CFLAGS}' LIBS='${LIBBPF_LIBS}')
|
||||
Libevent(SSL): ${have_libevent_openssl} (CFLAGS='${LIBEVENT_OPENSSL_CFLAGS}' LIBS='${LIBEVENT_OPENSSL_LIBS}')
|
||||
Jansson: ${have_jansson} (CFLAGS='${JANSSON_CFLAGS}' LIBS='${JANSSON_LIBS}')
|
||||
Jemalloc: ${have_jemalloc} (LIBS='${JEMALLOC_LIBS}')
|
||||
Jemalloc: ${have_jemalloc} (CFLAGS='${JEMALLOC_CFLAGS}' LIBS='${JEMALLOC_LIBS}')
|
||||
Zlib: ${have_zlib} (CFLAGS='${ZLIB_CFLAGS}' LIBS='${ZLIB_LIBS}')
|
||||
Systemd: ${have_libsystemd} (CFLAGS='${SYSTEMD_CFLAGS}' LIBS='${SYSTEMD_LIBS}')
|
||||
Boost CPPFLAGS: ${BOOST_CPPFLAGS}
|
||||
@@ -923,4 +1228,5 @@ AC_MSG_NOTICE([summary of build options:
|
||||
Examples: ${enable_examples}
|
||||
Python bindings:${enable_python_bindings}
|
||||
Threading: ${enable_threads}
|
||||
HTTP/3 (EXPERIMENTAL): ${enable_http3}
|
||||
])
|
||||
|
||||
@@ -184,9 +184,9 @@ set(EXTRA_DIST
|
||||
sources/python-apiref.rst
|
||||
sources/building-android-binary.rst
|
||||
sources/contribute.rst
|
||||
_exts/sphinxcontrib/LICENSE.rubydomain
|
||||
_exts/sphinxcontrib/__init__.py
|
||||
_exts/sphinxcontrib/rubydomain.py
|
||||
_exts/rubydomain/LICENSE.rubydomain
|
||||
_exts/rubydomain/__init__.py
|
||||
_exts/rubydomain/rubydomain.py
|
||||
_themes/sphinx_rtd_theme/__init__.py
|
||||
_themes/sphinx_rtd_theme/breadcrumbs.html
|
||||
_themes/sphinx_rtd_theme/footer.html
|
||||
|
||||
@@ -30,6 +30,8 @@ APIDOCS= \
|
||||
nghttp2_check_authority.rst \
|
||||
nghttp2_check_header_name.rst \
|
||||
nghttp2_check_header_value.rst \
|
||||
nghttp2_check_method.rst \
|
||||
nghttp2_check_path.rst \
|
||||
nghttp2_hd_deflate_bound.rst \
|
||||
nghttp2_hd_deflate_change_table_size.rst \
|
||||
nghttp2_hd_deflate_del.rst \
|
||||
@@ -204,9 +206,9 @@ EXTRA_DIST = \
|
||||
sources/building-android-binary.rst \
|
||||
sources/contribute.rst \
|
||||
sources/security.rst \
|
||||
_exts/sphinxcontrib/LICENSE.rubydomain \
|
||||
_exts/sphinxcontrib/__init__.py \
|
||||
_exts/sphinxcontrib/rubydomain.py \
|
||||
_exts/rubydomain/LICENSE.rubydomain \
|
||||
_exts/rubydomain/__init__.py \
|
||||
_exts/rubydomain/rubydomain.py \
|
||||
_themes/sphinx_rtd_theme/__init__.py \
|
||||
_themes/sphinx_rtd_theme/breadcrumbs.html \
|
||||
_themes/sphinx_rtd_theme/footer.html \
|
||||
@@ -270,7 +272,7 @@ EXTRA_DIST = \
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
SPHINXBUILD ?= sphinx-build
|
||||
PAPER =
|
||||
BUILDDIR = manual
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ from docutils.parsers.rst import Directive
|
||||
from sphinx import addnodes
|
||||
from sphinx import version_info
|
||||
from sphinx.roles import XRefRole
|
||||
from sphinx.locale import l_, _
|
||||
from sphinx.locale import _
|
||||
from sphinx.domains import Domain, ObjType, Index
|
||||
from sphinx.directives import ObjectDescription
|
||||
from sphinx.util.nodes import make_refnode
|
||||
@@ -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 --connect-to --header-table-size --requests --log-file --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" ) )
|
||||
COMPREPLY=( $( compgen -W '--requests --clients --threads --input-file --max-concurrent-streams --window-bits --connection-window-bits --header --ciphers --tls13-ciphers --no-tls-proto --data --rate --rate-period --duration --warm-up-time --connection-active-timeout --connection-inactivity-timeout --timing-script-file --base-uri --npn-list --h1 --header-table-size --encoder-header-table-size --log-file --qlog-file-base --connect-to --rps --groups --no-udp-gso --max-udp-payload-size --verbose --version --help ' -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
_filedir
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from __future__ import print_function
|
||||
import subprocess
|
||||
import io
|
||||
import re
|
||||
|
||||
@@ -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 --no-verify-peer --header ' -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W '--verbose --null-out --remote-name --timeout --window-bits --connection-window-bits --get-assets --stat --header --trailer --cert --key --data --multiply --upgrade --weight --peer-max-concurrent-streams --header-table-size --encoder-header-table-size --padding --har --color --continuation --no-content-length --no-dep --hexdump --no-push --max-concurrent-streams --expect-continue --no-verify-peer --version --help ' -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
_filedir
|
||||
|
||||
@@ -8,7 +8,7 @@ _nghttpd()
|
||||
_get_comp_words_by_ref cur prev
|
||||
case $cur in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W '--htdocs --verbose --daemon --echo-upload --error-gzip --push --header-table-size --encoder-header-table-size --padding --hexdump --max-concurrent-streams --no-tls --connection-window-bits --mime-types-file --no-content-length --workers --version --color --early-response --dh-param-file --trailer --address --window-bits --verify-client --help ' -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W '--address --daemon --verify-client --htdocs --verbose --no-tls --header-table-size --encoder-header-table-size --color --push --padding --max-concurrent-streams --workers --error-gzip --window-bits --connection-window-bits --dh-param-file --early-response --trailer --hexdump --echo-upload --mime-types-file --no-content-length --version --help ' -- "$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 --tls13-client-ciphers --frontend-keep-alive-timeout --backend-request-buffer --max-request-header-fields --backend-connect-timeout --tls-max-proto-version --conf --dns-lookup-timeout --backend-http2-max-concurrent-streams --worker-write-burst --npn-list --dns-max-try --fetch-ocsp-response-file --no-via --tls-session-cache-memcached-cert-file --no-http2-cipher-black-list --mruby-file --add-forwarded --client-no-http2-cipher-black-list --stream-read-timeout --client-ciphers --ocsp-update-interval --forwarded-for --accesslog-syslog --dns-cache-timeout --frontend-http2-read-timeout --listener-disable-timeout --ciphers --client-psk-secrets --strip-incoming-x-forwarded-for --no-server-rewrite --private-key-passwd-file --backend-keep-alive-timeout --backend-http-proxy-uri --frontend-max-requests --tls-no-postpone-early-data --rlimit-nofile --no-strip-incoming-x-forwarded-proto --tls-ticket-key-memcached-cert-file --no-verify-ocsp --forwarded-by --tls-session-cache-memcached-private-key-file --error-page --ocsp-startup --backend-write-timeout --tls-dyn-rec-warmup-threshold --tls-ticket-key-memcached-max-retry --frontend-http2-window-size --http2-no-cookie-crumbling --worker-read-burst --dh-param-file --accesslog-format --errorlog-syslog --redirect-https-port --request-header-field-buffer --api-max-request-body --frontend-http2-decoder-dynamic-table-size --errorlog-file --frontend-http2-max-concurrent-streams --psk-secrets --frontend-write-timeout --tls-ticket-key-cipher --read-burst --no-add-x-forwarded-proto --backend --server-name --insecure --backend-max-backoff --log-level --host-rewrite --tls-ticket-key-memcached-interval --frontend-http2-setting-timeout --frontend-http2-connection-window-size --worker-frontend-connections --syslog-facility --fastopen --no-location-rewrite --single-thread --tls-session-cache-memcached --no-ocsp --backend-response-buffer --tls-min-proto-version --workers --add-x-forwarded-for --no-server-push --worker-write-rate --add-request-header --backend-http2-settings-timeout --subcert --ignore-per-pattern-mruby-error --ecdh-curves --no-kqueue --help --frontend-frame-debug --tls-sct-dir --pid-file --frontend-http2-dump-request-header --daemon --write-rate --altsvc --backend-http2-decoder-dynamic-table-size --no-strip-incoming-early-data --user --verify-client-tolerate-expired --frontend-read-timeout --tls-ticket-key-memcached-max-fail --backlog --write-burst --backend-connections-per-host --tls-max-early-data --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 --tls13-ciphers --version --add-response-header --backend-read-timeout --frontend-http2-optimize-window-size --frontend --accesslog-file --http2-proxy --backend-http2-encoder-dynamic-table-size --client-private-key-file --single-process --client-cert-file --tls-ticket-key-memcached --tls-dyn-rec-idle-timeout --frontend-http2-optimize-write-buffer-size --verify-client --frontend-http2-encoder-dynamic-table-size --read-rate --backend-connections-per-frontend --strip-incoming-forwarded ' -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W '--backend --frontend --backlog --backend-address-family --backend-http-proxy-uri --workers --single-thread --read-rate --read-burst --write-rate --write-burst --worker-read-rate --worker-read-burst --worker-write-rate --worker-write-burst --worker-frontend-connections --backend-connections-per-host --backend-connections-per-frontend --rlimit-nofile --backend-request-buffer --backend-response-buffer --fastopen --no-kqueue --frontend-http2-read-timeout --frontend-http3-read-timeout --frontend-read-timeout --frontend-write-timeout --frontend-keep-alive-timeout --stream-read-timeout --stream-write-timeout --backend-read-timeout --backend-write-timeout --backend-connect-timeout --backend-keep-alive-timeout --listener-disable-timeout --frontend-http2-setting-timeout --backend-http2-settings-timeout --backend-max-backoff --ciphers --tls13-ciphers --client-ciphers --tls13-client-ciphers --ecdh-curves --insecure --cacert --private-key-passwd-file --subcert --dh-param-file --npn-list --verify-client --verify-client-cacert --verify-client-tolerate-expired --client-private-key-file --client-cert-file --tls-min-proto-version --tls-max-proto-version --tls-ticket-key-file --tls-ticket-key-memcached --tls-ticket-key-memcached-address-family --tls-ticket-key-memcached-interval --tls-ticket-key-memcached-max-retry --tls-ticket-key-memcached-max-fail --tls-ticket-key-cipher --tls-ticket-key-memcached-cert-file --tls-ticket-key-memcached-private-key-file --fetch-ocsp-response-file --ocsp-update-interval --ocsp-startup --no-verify-ocsp --no-ocsp --tls-session-cache-memcached --tls-session-cache-memcached-address-family --tls-session-cache-memcached-cert-file --tls-session-cache-memcached-private-key-file --tls-dyn-rec-warmup-threshold --tls-dyn-rec-idle-timeout --no-http2-cipher-block-list --client-no-http2-cipher-block-list --tls-sct-dir --psk-secrets --client-psk-secrets --tls-no-postpone-early-data --tls-max-early-data --frontend-http2-max-concurrent-streams --backend-http2-max-concurrent-streams --frontend-http2-window-size --frontend-http2-connection-window-size --backend-http2-window-size --backend-http2-connection-window-size --http2-no-cookie-crumbling --padding --no-server-push --frontend-http2-optimize-write-buffer-size --frontend-http2-optimize-window-size --frontend-http2-encoder-dynamic-table-size --frontend-http2-decoder-dynamic-table-size --backend-http2-encoder-dynamic-table-size --backend-http2-decoder-dynamic-table-size --http2-proxy --log-level --accesslog-file --accesslog-syslog --accesslog-format --accesslog-write-early --errorlog-file --errorlog-syslog --syslog-facility --add-x-forwarded-for --strip-incoming-x-forwarded-for --no-add-x-forwarded-proto --no-strip-incoming-x-forwarded-proto --add-forwarded --strip-incoming-forwarded --forwarded-by --forwarded-for --no-via --no-strip-incoming-early-data --no-location-rewrite --host-rewrite --altsvc --http2-altsvc --add-request-header --add-response-header --request-header-field-buffer --max-request-header-fields --response-header-field-buffer --max-response-header-fields --error-page --server-name --no-server-rewrite --redirect-https-port --api-max-request-body --dns-cache-timeout --dns-lookup-timeout --dns-max-try --frontend-max-requests --frontend-http2-dump-request-header --frontend-http2-dump-response-header --frontend-frame-debug --daemon --pid-file --user --single-process --mruby-file --ignore-per-pattern-mruby-error --frontend-quic-idle-timeout --frontend-quic-debug-log --quic-bpf-program-file --frontend-quic-early-data --frontend-quic-qlog-dir --frontend-quic-require-token --frontend-quic-congestion-controller --frontend-quic-connection-id-encryption-key --no-quic-bpf --frontend-http3-window-size --frontend-http3-connection-window-size --frontend-http3-max-window-size --frontend-http3-max-connection-window-size --frontend-http3-max-concurrent-streams --conf --include --version --help ' -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
_filedir
|
||||
|
||||
@@ -41,7 +41,7 @@ import sys, os
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
sys.path.append(os.path.abspath('@top_srcdir@/doc/_exts'))
|
||||
sys.path.insert(0, os.path.abspath('@top_srcdir@/doc/_exts'))
|
||||
|
||||
# -- General configuration -----------------------------------------------------
|
||||
|
||||
@@ -50,7 +50,7 @@ sys.path.append(os.path.abspath('@top_srcdir@/doc/_exts'))
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = ['sphinxcontrib.rubydomain']
|
||||
extensions = ['rubydomain.rubydomain']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['@top_srcdir@/_templates']
|
||||
|
||||
53
doc/h2load.1
53
doc/h2load.1
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "H2LOAD" "1" "Jun 02, 2020" "1.41.0" "nghttp2"
|
||||
.TH "H2LOAD" "1" "Sep 21, 2021" "1.45.1" "nghttp2"
|
||||
.SH NAME
|
||||
h2load \- HTTP/2 benchmarking tool
|
||||
.
|
||||
@@ -101,6 +101,7 @@ Default: \fB1\fP
|
||||
.TP
|
||||
.B \-w, \-\-window\-bits=<N>
|
||||
Sets the stream level initial window size to (2**<N>)\-1.
|
||||
For QUIC, <N> is capped to 26 (roughly 64MiB).
|
||||
.sp
|
||||
Default: \fB30\fP
|
||||
.UNINDENT
|
||||
@@ -120,13 +121,21 @@ Add/Override a header to the requests.
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-ciphers=<SUITE>
|
||||
Set allowed cipher list. The format of the string is
|
||||
described in OpenSSL ciphers(1).
|
||||
Set allowed cipher list for TLSv1.2 or ealier. The
|
||||
format of the string is described in OpenSSL ciphers(1).
|
||||
.sp
|
||||
Default: \fBECDHE\-ECDSA\-AES256\-GCM\-SHA384:ECDHE\-RSA\-AES256\-GCM\-SHA384:ECDHE\-ECDSA\-CHACHA20\-POLY1305:ECDHE\-RSA\-CHACHA20\-POLY1305:ECDHE\-ECDSA\-AES128\-GCM\-SHA256:ECDHE\-RSA\-AES128\-GCM\-SHA256:ECDHE\-ECDSA\-AES256\-SHA384:ECDHE\-RSA\-AES256\-SHA384:ECDHE\-ECDSA\-AES128\-SHA256:ECDHE\-RSA\-AES128\-SHA256\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-tls13\-ciphers=<SUITE>
|
||||
Set allowed cipher list for TLSv1.3. The format of the
|
||||
string is described in OpenSSL ciphers(1).
|
||||
.sp
|
||||
Default: \fBTLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_CCM_SHA256\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-p, \-\-no\-tls\-proto=<PROTOID>
|
||||
Specify ALPN identifier of the protocol to be used when
|
||||
accessing http URI without SSL/TLS.
|
||||
@@ -168,7 +177,7 @@ option is 1s.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-D, \-\-duration=<N>
|
||||
.B \-D, \-\-duration=<DURATION>
|
||||
Specifies the main duration for the measurements in case
|
||||
of timing\-based benchmarking. \fI\%\-D\fP and \fI\%\-r\fP are mutually
|
||||
exclusive.
|
||||
@@ -220,7 +229,8 @@ to the number of script lines. The scheme, host and
|
||||
port defined in the first URI are used solely. Values
|
||||
contained in other URIs, if present, are ignored.
|
||||
Definition of a base URI overrides all scheme, host or
|
||||
port values.
|
||||
port values. \fI\%\-\-timing\-script\-file\fP and \fI\%\-\-rps\fP are
|
||||
mutually exclusive.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -284,12 +294,45 @@ to buffering. Status code is \-1 for failed streams.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-qlog\-file\-base=<PATH>
|
||||
Enable qlog output and specify base file name for qlogs.
|
||||
Qlog is emitted for each connection.
|
||||
For a given base name "base", each output file name
|
||||
becomes "base.M.N.qlog" where M is worker ID and N is
|
||||
client ID (e.g. "base.0.3.qlog").
|
||||
Only effective in QUIC runs.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-connect\-to=<HOST>[:<PORT>]
|
||||
Host and port to connect instead of using the authority
|
||||
in <URI>.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-rps=<N>
|
||||
Specify request per second for each client. \fI\%\-\-rps\fP and
|
||||
\fI\%\-\-timing\-script\-file\fP are mutually exclusive.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-groups=<GROUPS>
|
||||
Specify the supported groups.
|
||||
.sp
|
||||
Default: \fBX25519:P\-256:P\-384:P\-521\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-no\-udp\-gso
|
||||
Disable UDP GSO.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-max\-udp\-payload\-size=<SIZE>
|
||||
Specify the maximum outgoing UDP datagram payload size.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-v, \-\-verbose
|
||||
Output debug information.
|
||||
.UNINDENT
|
||||
|
||||
@@ -76,6 +76,7 @@ OPTIONS
|
||||
.. option:: -w, --window-bits=<N>
|
||||
|
||||
Sets the stream level initial window size to (2\*\*<N>)-1.
|
||||
For QUIC, <N> is capped to 26 (roughly 64MiB).
|
||||
|
||||
Default: ``30``
|
||||
|
||||
@@ -92,11 +93,18 @@ OPTIONS
|
||||
|
||||
.. option:: --ciphers=<SUITE>
|
||||
|
||||
Set allowed cipher list. The format of the string is
|
||||
described in OpenSSL ciphers(1).
|
||||
Set allowed cipher list for TLSv1.2 or ealier. The
|
||||
format of the string is described in OpenSSL ciphers(1).
|
||||
|
||||
Default: ``ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256``
|
||||
|
||||
.. option:: --tls13-ciphers=<SUITE>
|
||||
|
||||
Set allowed cipher list for TLSv1.3. The format of the
|
||||
string is described in OpenSSL ciphers(1).
|
||||
|
||||
Default: ``TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_CCM_SHA256``
|
||||
|
||||
.. option:: -p, --no-tls-proto=<PROTOID>
|
||||
|
||||
Specify ALPN identifier of the protocol to be used when
|
||||
@@ -134,7 +142,7 @@ OPTIONS
|
||||
the rate option is not used. The default value for this
|
||||
option is 1s.
|
||||
|
||||
.. option:: -D, --duration=<N>
|
||||
.. option:: -D, --duration=<DURATION>
|
||||
|
||||
Specifies the main duration for the measurements in case
|
||||
of timing-based benchmarking. :option:`-D` and :option:`\-r` are mutually
|
||||
@@ -183,7 +191,8 @@ OPTIONS
|
||||
port defined in the first URI are used solely. Values
|
||||
contained in other URIs, if present, are ignored.
|
||||
Definition of a base URI overrides all scheme, host or
|
||||
port values.
|
||||
port values. :option:`--timing-script-file` and :option:`\--rps` are
|
||||
mutually exclusive.
|
||||
|
||||
.. option:: -B, --base-uri=(<URI>|unix:<PATH>)
|
||||
|
||||
@@ -239,11 +248,39 @@ OPTIONS
|
||||
appear slightly out of order with multiple threads due
|
||||
to buffering. Status code is -1 for failed streams.
|
||||
|
||||
.. option:: --qlog-file-base=<PATH>
|
||||
|
||||
Enable qlog output and specify base file name for qlogs.
|
||||
Qlog is emitted for each connection.
|
||||
For a given base name "base", each output file name
|
||||
becomes "base.M.N.qlog" where M is worker ID and N is
|
||||
client ID (e.g. "base.0.3.qlog").
|
||||
Only effective in QUIC runs.
|
||||
|
||||
.. option:: --connect-to=<HOST>[:<PORT>]
|
||||
|
||||
Host and port to connect instead of using the authority
|
||||
in <URI>.
|
||||
|
||||
.. option:: --rps=<N>
|
||||
|
||||
Specify request per second for each client. :option:`--rps` and
|
||||
:option:`--timing-script-file` are mutually exclusive.
|
||||
|
||||
.. option:: --groups=<GROUPS>
|
||||
|
||||
Specify the supported groups.
|
||||
|
||||
Default: ``X25519:P-256:P-384:P-521``
|
||||
|
||||
.. option:: --no-udp-gso
|
||||
|
||||
Disable UDP GSO.
|
||||
|
||||
.. option:: --max-udp-payload-size=<SIZE>
|
||||
|
||||
Specify the maximum outgoing UDP datagram payload size.
|
||||
|
||||
.. option:: -v, --verbose
|
||||
|
||||
Output debug information.
|
||||
|
||||
148
doc/mkapiref.py
148
doc/mkapiref.py
@@ -1,9 +1,11 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# nghttp2 - HTTP/2 C Library
|
||||
|
||||
#
|
||||
# Copyright (c) 2020 nghttp2 contributors
|
||||
# Copyright (c) 2020 ngtcp2 contributors
|
||||
# Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
@@ -25,17 +27,16 @@
|
||||
|
||||
# Generates API reference from C source code.
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from __future__ import print_function # At least python 2.6 is required
|
||||
import re, sys, argparse, os.path
|
||||
|
||||
class FunctionDoc:
|
||||
def __init__(self, name, content, domain):
|
||||
def __init__(self, name, content, domain, filename):
|
||||
self.name = name
|
||||
self.content = content
|
||||
self.domain = domain
|
||||
if self.domain == 'function':
|
||||
self.funcname = re.search(r'(nghttp2_[^ )]+)\(', self.name).group(1)
|
||||
self.filename = filename
|
||||
|
||||
def write(self, out):
|
||||
out.write('.. {}:: {}\n'.format(self.domain, self.name))
|
||||
@@ -64,6 +65,26 @@ class StructDoc:
|
||||
out.write(' {}\n'.format(line))
|
||||
out.write('\n')
|
||||
|
||||
class EnumDoc:
|
||||
def __init__(self, name, content, members):
|
||||
self.name = name
|
||||
self.content = content
|
||||
self.members = members
|
||||
|
||||
def write(self, out):
|
||||
if self.name:
|
||||
out.write('.. type:: {}\n'.format(self.name))
|
||||
out.write('\n')
|
||||
for line in self.content:
|
||||
out.write(' {}\n'.format(line))
|
||||
out.write('\n')
|
||||
for name, content in self.members:
|
||||
out.write(' .. enum:: {}\n'.format(name))
|
||||
out.write('\n')
|
||||
for line in content:
|
||||
out.write(' {}\n'.format(line))
|
||||
out.write('\n')
|
||||
|
||||
class MacroDoc:
|
||||
def __init__(self, name, content):
|
||||
self.name = name
|
||||
@@ -75,50 +96,76 @@ class MacroDoc:
|
||||
for line in self.content:
|
||||
out.write(' {}\n'.format(line))
|
||||
|
||||
def make_api_ref(infiles):
|
||||
class MacroSectionDoc:
|
||||
def __init__(self, content):
|
||||
self.content = content
|
||||
|
||||
def write(self, out):
|
||||
out.write('\n')
|
||||
c = ' '.join(self.content).strip()
|
||||
out.write(c)
|
||||
out.write('\n')
|
||||
out.write('-' * len(c))
|
||||
out.write('\n\n')
|
||||
|
||||
class TypedefDoc:
|
||||
def __init__(self, name, content):
|
||||
self.name = name
|
||||
self.content = content
|
||||
|
||||
def write(self, out):
|
||||
out.write('''.. type:: {}\n'''.format(self.name))
|
||||
out.write('\n')
|
||||
for line in self.content:
|
||||
out.write(' {}\n'.format(line))
|
||||
|
||||
def make_api_ref(infile):
|
||||
macros = []
|
||||
enums = []
|
||||
types = []
|
||||
functions = []
|
||||
for infile in infiles:
|
||||
while True:
|
||||
while True:
|
||||
line = infile.readline()
|
||||
if not line:
|
||||
break
|
||||
elif line == '/**\n':
|
||||
line = infile.readline()
|
||||
if not line:
|
||||
break
|
||||
elif line == '/**\n':
|
||||
line = infile.readline()
|
||||
doctype = line.split()[1]
|
||||
if doctype == '@function':
|
||||
functions.append(process_function('function', infile))
|
||||
elif doctype == '@functypedef':
|
||||
types.append(process_function('type', infile))
|
||||
elif doctype == '@struct' or doctype == '@union':
|
||||
types.append(process_struct(infile))
|
||||
elif doctype == '@enum':
|
||||
enums.append(process_enum(infile))
|
||||
elif doctype == '@macro':
|
||||
macros.append(process_macro(infile))
|
||||
doctype = line.split()[1]
|
||||
if doctype == '@function':
|
||||
functions.append(process_function('function', infile))
|
||||
elif doctype == '@functypedef':
|
||||
types.append(process_function('type', infile))
|
||||
elif doctype == '@struct' or doctype == '@union':
|
||||
types.append(process_struct(infile))
|
||||
elif doctype == '@enum':
|
||||
enums.append(process_enum(infile))
|
||||
elif doctype == '@macro':
|
||||
macros.append(process_macro(infile))
|
||||
elif doctype == '@macrosection':
|
||||
macros.append(process_macrosection(infile))
|
||||
elif doctype == '@typedef':
|
||||
types.append(process_typedef(infile))
|
||||
return macros, enums, types, functions
|
||||
|
||||
alldocs = [('Macros', macros),
|
||||
('Enums', enums),
|
||||
('Types (structs, unions and typedefs)', types),
|
||||
('Functions', functions)]
|
||||
|
||||
def output(
|
||||
indexfile, macrosfile, enumsfile, typesfile, funcsdir,
|
||||
title, indexfile, macrosfile, enumsfile, typesfile, funcsdir,
|
||||
macros, enums, types, functions):
|
||||
indexfile.write('''
|
||||
API Reference
|
||||
=============
|
||||
{title}
|
||||
{titledecoration}
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
macros
|
||||
enums
|
||||
types
|
||||
''')
|
||||
{macros}
|
||||
{enums}
|
||||
{types}
|
||||
'''.format(
|
||||
title=title, titledecoration='='*len(title),
|
||||
macros=os.path.splitext(os.path.basename(macrosfile.name))[0],
|
||||
enums=os.path.splitext(os.path.basename(enumsfile.name))[0],
|
||||
types=os.path.splitext(os.path.basename(typesfile.name))[0],
|
||||
))
|
||||
|
||||
for doc in functions:
|
||||
indexfile.write(' {}\n'.format(doc.funcname))
|
||||
@@ -153,9 +200,10 @@ Types (structs, unions and typedefs)
|
||||
Synopsis
|
||||
--------
|
||||
|
||||
*#include <nghttp2/nghttp2.h>*
|
||||
*#include <nghttp2/{filename}>*
|
||||
|
||||
'''.format(funcname=doc.funcname, secul='='*len(doc.funcname)))
|
||||
'''.format(funcname=doc.funcname, secul='='*len(doc.funcname),
|
||||
filename=doc.filename))
|
||||
doc.write(f)
|
||||
|
||||
def process_macro(infile):
|
||||
@@ -164,6 +212,17 @@ def process_macro(infile):
|
||||
macro_name = line.split()[1]
|
||||
return MacroDoc(macro_name, content)
|
||||
|
||||
def process_macrosection(infile):
|
||||
content = read_content(infile)
|
||||
return MacroSectionDoc(content)
|
||||
|
||||
def process_typedef(infile):
|
||||
content = read_content(infile)
|
||||
typedef = infile.readline()
|
||||
typedef = re.sub(r';\n$', '', typedef)
|
||||
typedef = re.sub(r'typedef ', '', typedef)
|
||||
return TypedefDoc(typedef, content)
|
||||
|
||||
def process_enum(infile):
|
||||
members = []
|
||||
enum_name = None
|
||||
@@ -176,7 +235,7 @@ def process_enum(infile):
|
||||
member_content = read_content(infile)
|
||||
line = infile.readline()
|
||||
items = line.split()
|
||||
member_name = items[0]
|
||||
member_name = items[0].rstrip(',')
|
||||
if len(items) >= 3:
|
||||
member_content.insert(0, '(``{}``) '\
|
||||
.format(' '.join(items[2:]).rstrip(',')))
|
||||
@@ -185,7 +244,7 @@ def process_enum(infile):
|
||||
enum_name = line.rstrip().split()[1]
|
||||
enum_name = re.sub(r';$', '', enum_name)
|
||||
break
|
||||
return StructDoc(enum_name, content, members, 'macro')
|
||||
return EnumDoc(enum_name, content, members)
|
||||
|
||||
def process_struct(infile):
|
||||
members = []
|
||||
@@ -226,7 +285,9 @@ def process_function(domain, infile):
|
||||
func_proto = re.sub(r';\n$', '', func_proto)
|
||||
func_proto = re.sub(r'\s+', ' ', func_proto)
|
||||
func_proto = re.sub(r'NGHTTP2_EXTERN ', '', func_proto)
|
||||
return FunctionDoc(func_proto, content, domain)
|
||||
func_proto = re.sub(r'typedef ', '', func_proto)
|
||||
filename = os.path.basename(infile.name)
|
||||
return FunctionDoc(func_proto, content, domain, filename)
|
||||
|
||||
def read_content(infile):
|
||||
content = []
|
||||
@@ -251,6 +312,8 @@ def transform_content(content):
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description="Generate API reference")
|
||||
parser.add_argument('--title', default='API Reference',
|
||||
help='title of index page')
|
||||
parser.add_argument('index', type=argparse.FileType('w'),
|
||||
help='index output file')
|
||||
parser.add_argument('macros', type=argparse.FileType('w'),
|
||||
@@ -269,12 +332,13 @@ if __name__ == '__main__':
|
||||
types = []
|
||||
funcs = []
|
||||
for infile in args.files:
|
||||
m, e, t, f = make_api_ref(args.files)
|
||||
m, e, t, f = make_api_ref(infile)
|
||||
macros.extend(m)
|
||||
enums.extend(e)
|
||||
types.extend(t)
|
||||
funcs.extend(f)
|
||||
funcs.sort(key=lambda x: x.funcname)
|
||||
output(
|
||||
args.title,
|
||||
args.index, args.macros, args.enums, args.types, args.funcsdir,
|
||||
macros, enums, types, funcs)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTP" "1" "Jun 02, 2020" "1.41.0" "nghttp2"
|
||||
.TH "NGHTTP" "1" "Sep 21, 2021" "1.45.1" "nghttp2"
|
||||
.SH NAME
|
||||
nghttp \- HTTP/2 client
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTPD" "1" "Jun 02, 2020" "1.41.0" "nghttp2"
|
||||
.TH "NGHTTPD" "1" "Sep 21, 2021" "1.45.1" "nghttp2"
|
||||
.SH NAME
|
||||
nghttpd \- HTTP/2 server
|
||||
.
|
||||
|
||||
266
doc/nghttpx.1
266
doc/nghttpx.1
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTPX" "1" "Jun 02, 2020" "1.41.0" "nghttp2"
|
||||
.TH "NGHTTPX" "1" "Sep 21, 2021" "1.45.1" "nghttp2"
|
||||
.SH NAME
|
||||
nghttpx \- HTTP/2 proxy
|
||||
.
|
||||
@@ -35,7 +35,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
\fBnghttpx\fP [OPTIONS]... [<PRIVATE_KEY> <CERT>]
|
||||
.SH DESCRIPTION
|
||||
.sp
|
||||
A reverse proxy for HTTP/2, and HTTP/1.
|
||||
A reverse proxy for HTTP/3, HTTP/2, and HTTP/1.
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B <PRIVATE_KEY>
|
||||
@@ -140,12 +140,13 @@ parameters are: "proto=<PROTO>", "tls",
|
||||
"affinity=<METHOD>", "dns", "redirect\-if\-not\-tls",
|
||||
"upgrade\-scheme", "mruby=<PATH>",
|
||||
"read\-timeout=<DURATION>", "write\-timeout=<DURATION>",
|
||||
"group=<GROUP>", "group\-weight=<N>", and "weight=<N>".
|
||||
The parameter consists of keyword, and optionally
|
||||
followed by "=" and value. For example, the parameter
|
||||
"proto=h2" consists of the keyword "proto" and value
|
||||
"h2". The parameter "tls" consists of the keyword "tls"
|
||||
without value. Each parameter is described as follows.
|
||||
"group=<GROUP>", "group\-weight=<N>", "weight=<N>", and
|
||||
"dnf". The parameter consists of keyword, and
|
||||
optionally followed by "=" and value. For example, the
|
||||
parameter "proto=h2" consists of the keyword "proto" and
|
||||
value "h2". The parameter "tls" consists of the keyword
|
||||
"tls" without value. Each parameter is described as
|
||||
follows.
|
||||
.sp
|
||||
The backend application protocol can be specified using
|
||||
optional "proto" parameter, and in the form of
|
||||
@@ -276,9 +277,19 @@ weight than weight 2. If this parameter is omitted,
|
||||
weight becomes 1. "weight" is ignored if session
|
||||
affinity is enabled.
|
||||
.sp
|
||||
If "dnf" parameter is specified, an incoming request is
|
||||
not forwarded to a backend and just consumed along with
|
||||
the request body (actually a backend server never be
|
||||
contacted). It is expected that the HTTP response is
|
||||
generated by mruby script (see "mruby=<PATH>" parameter
|
||||
above). "dnf" is an abbreviation of "do not forward".
|
||||
.sp
|
||||
Since ";" and ":" are used as delimiter, <PATTERN> must
|
||||
not contain these characters. Since ";" has special
|
||||
meaning in shell, the option value must be quoted.
|
||||
not contain these characters. In order to include ":"
|
||||
in <PATTERN>, one has to specify "%3A" (which is
|
||||
percent\-encoded from of ":") instead. Since ";" has
|
||||
special meaning in shell, the option value must be
|
||||
quoted.
|
||||
.sp
|
||||
Default: \fB127.0.0.1,80\fP
|
||||
.UNINDENT
|
||||
@@ -320,6 +331,12 @@ To accept PROXY protocol version 1 and 2 on frontend
|
||||
connection, specify "proxyproto" parameter. This is
|
||||
disabled by default.
|
||||
.sp
|
||||
To receive HTTP/3 (QUIC) traffic, specify "quic"
|
||||
parameter. It makes nghttpx listen on UDP port rather
|
||||
than TCP port. UNIX domain socket, "api", and
|
||||
"healthmon" parameters cannot be used with "quic"
|
||||
parameter.
|
||||
.sp
|
||||
Default: \fB*,3000\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
@@ -525,6 +542,13 @@ Default: \fB3m\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-frontend\-http3\-read\-timeout=<DURATION>
|
||||
Specify read timeout for HTTP/3 frontend connection.
|
||||
.sp
|
||||
Default: \fB3m\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-frontend\-read\-timeout=<DURATION>
|
||||
Specify read timeout for HTTP/1.1 frontend connection.
|
||||
.sp
|
||||
@@ -1003,19 +1027,19 @@ Default: \fB1s\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-no\-http2\-cipher\-black\-list
|
||||
Allow black listed cipher suite on frontend HTTP/2
|
||||
.B \-\-no\-http2\-cipher\-block\-list
|
||||
Allow block listed cipher suite on frontend HTTP/2
|
||||
connection. See
|
||||
\fI\%https://tools.ietf.org/html/rfc7540#appendix\-A\fP for the
|
||||
complete HTTP/2 cipher suites black list.
|
||||
complete HTTP/2 cipher suites block list.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-client\-no\-http2\-cipher\-black\-list
|
||||
Allow black listed cipher suite on backend HTTP/2
|
||||
.B \-\-client\-no\-http2\-cipher\-block\-list
|
||||
Allow block listed cipher suite on backend HTTP/2
|
||||
connection. See
|
||||
\fI\%https://tools.ietf.org/html/rfc7540#appendix\-A\fP for the
|
||||
complete HTTP/2 cipher suites black list.
|
||||
complete HTTP/2 cipher suites block list.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -1040,9 +1064,9 @@ in hex. An empty line, and line which starts with \(aq#\(aq
|
||||
are skipped. The default enabled cipher list might not
|
||||
contain any PSK cipher suite. In that case, desired PSK
|
||||
cipher suites must be enabled using \fI\%\-\-ciphers\fP option.
|
||||
The desired PSK cipher suite may be black listed by
|
||||
The desired PSK cipher suite may be block listed by
|
||||
HTTP/2. To use those cipher suites with HTTP/2,
|
||||
consider to use \fI\%\-\-no\-http2\-cipher\-black\-list\fP option.
|
||||
consider to use \fI\%\-\-no\-http2\-cipher\-block\-list\fP option.
|
||||
But be aware its implications.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
@@ -1057,20 +1081,21 @@ The first identity and secret pair encountered is used.
|
||||
The default enabled cipher list might not contain any
|
||||
PSK cipher suite. In that case, desired PSK cipher
|
||||
suites must be enabled using \fI\%\-\-client\-ciphers\fP option.
|
||||
The desired PSK cipher suite may be black listed by
|
||||
The desired PSK cipher suite may be block listed by
|
||||
HTTP/2. To use those cipher suites with HTTP/2,
|
||||
consider to use \fI\%\-\-client\-no\-http2\-cipher\-black\-list\fP
|
||||
consider to use \fI\%\-\-client\-no\-http2\-cipher\-block\-list\fP
|
||||
option. But be aware its implications.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-tls\-no\-postpone\-early\-data
|
||||
By default, nghttpx postpones forwarding HTTP requests
|
||||
sent in early data, including those sent in partially in
|
||||
it, until TLS handshake finishes. If all backend server
|
||||
recognizes "Early\-Data" header field, using this option
|
||||
makes nghttpx not postpone forwarding request and get
|
||||
full potential of 0\-RTT data.
|
||||
By default, except for QUIC connections, nghttpx
|
||||
postpones forwarding HTTP requests sent in early data,
|
||||
including those sent in partially in it, until TLS
|
||||
handshake finishes. If all backend server recognizes
|
||||
"Early\-Data" header field, using this option makes
|
||||
nghttpx not postpone forwarding request and get full
|
||||
potential of 0\-RTT data.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -1324,6 +1349,18 @@ request. "\-" if backend host is not available.
|
||||
.IP \(bu 2
|
||||
$backend_port: backend port used to fulfill the
|
||||
request. "\-" if backend host is not available.
|
||||
.IP \(bu 2
|
||||
$method: HTTP method
|
||||
.IP \(bu 2
|
||||
$path: Request path including query. For CONNECT
|
||||
request, authority is recorded.
|
||||
.IP \(bu 2
|
||||
$path_without_query: $path up to the first \(aq?\(aq
|
||||
character. For CONNECT request, authority is
|
||||
recorded.
|
||||
.IP \(bu 2
|
||||
$protocol_version: HTTP version (e.g., HTTP/1.1,
|
||||
HTTP/2)
|
||||
.UNINDENT
|
||||
.sp
|
||||
The variable can be enclosed by "{" and "}" for
|
||||
@@ -1463,13 +1500,21 @@ not be altered regardless of this option.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-altsvc=<PROTOID,PORT[,HOST,[ORIGIN]]>
|
||||
.B \-\-altsvc=<PROTOID,PORT[,HOST,[ORIGIN[,PARAMS]]]>
|
||||
Specify protocol ID, port, host and origin of
|
||||
alternative service. <HOST> and <ORIGIN> are optional.
|
||||
They are advertised in alt\-svc header field only in
|
||||
HTTP/1.1 frontend. This option can be used multiple
|
||||
times to specify multiple alternative services.
|
||||
Example: \fI\%\-\-altsvc\fP=h2,443
|
||||
alternative service. <HOST>, <ORIGIN> and <PARAMS> are
|
||||
optional. Empty <HOST> and <ORIGIN> are allowed and
|
||||
they are treated as nothing is specified. They are
|
||||
advertised in alt\-svc header field only in HTTP/1.1
|
||||
frontend. This option can be used multiple times to
|
||||
specify multiple alternative services.
|
||||
Example: \fI\%\-\-altsvc\fP="h2,443,,,ma=3600; persist=1\(aq
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-http2\-altsvc=<PROTOID,PORT[,HOST,[ORIGIN[,PARAMS]]]>
|
||||
Just like \fI\%\-\-altsvc\fP option, but this altsvc is only sent
|
||||
in HTTP/2 frontend.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -1654,10 +1699,10 @@ be used to drop root privileges.
|
||||
.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
|
||||
2 processes: main and worker processes. If this option
|
||||
is used, main 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
|
||||
@@ -1673,6 +1718,125 @@ Ignore mruby compile error for per\-pattern mruby script
|
||||
file. If error occurred, it is treated as if no mruby
|
||||
file were specified for the pattern.
|
||||
.UNINDENT
|
||||
.SS HTTP/3 and QUIC
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-frontend\-quic\-idle\-timeout=<DURATION>
|
||||
Specify an idle timeout for QUIC connection.
|
||||
.sp
|
||||
Default: \fB30s\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-frontend\-quic\-debug\-log
|
||||
Output QUIC debug log to \fI/dev/stderr.\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-quic\-bpf\-program\-file=<PATH>
|
||||
Specify a path to eBPF program file reuseport_kern.o to
|
||||
direct an incoming QUIC UDP datagram to a correct
|
||||
socket.
|
||||
.sp
|
||||
Default: \fB/usr/local/lib/nghttp2/reuseport_kern.o\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-frontend\-quic\-early\-data
|
||||
Enable early data on frontend QUIC connections. nghttpx
|
||||
sends "Early\-Data" header field to a backend server if a
|
||||
request is received in early data and handshake has not
|
||||
finished. All backend servers should deal with possibly
|
||||
replayed requests.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-frontend\-quic\-qlog\-dir=<DIR>
|
||||
Specify a directory where a qlog file is written for
|
||||
frontend QUIC connections. A qlog file is created per
|
||||
each QUIC connection. The file name is ISO8601 basic
|
||||
format, followed by "\-", server Source Connection ID and
|
||||
".qlog".
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-frontend\-quic\-require\-token
|
||||
Require an address validation token for a frontend QUIC
|
||||
connection. Server sends a token in Retry packet or
|
||||
NEW_TOKEN frame in the previous connection.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-frontend\-quic\-congestion\-controller=<CC>
|
||||
Specify a congestion controller algorithm for a frontend
|
||||
QUIC connection. <CC> should be either "cubic" or
|
||||
"bbr".
|
||||
.sp
|
||||
Default: \fBcubic\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-frontend\-quic\-connection\-id\-encryption\-key=<HEXSTRING>
|
||||
Specify Connection ID encryption key. The encryption
|
||||
key must be 16 bytes, and it must be encoded in hex
|
||||
string (which is 32 bytes long). If this option is
|
||||
omitted, new key is generated. In order to survive QUIC
|
||||
connection in a configuration reload event, old and new
|
||||
configuration must have this option and share the same
|
||||
key.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-no\-quic\-bpf
|
||||
Disable eBPF.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-frontend\-http3\-window\-size=<SIZE>
|
||||
Sets the per\-stream initial window size of HTTP/3
|
||||
frontend connection.
|
||||
.sp
|
||||
Default: \fB256K\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-frontend\-http3\-connection\-window\-size=<SIZE>
|
||||
Sets the per\-connection window size of HTTP/3 frontend
|
||||
connection.
|
||||
.sp
|
||||
Default: \fB1M\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-frontend\-http3\-max\-window\-size=<SIZE>
|
||||
Sets the maximum per\-stream window size of HTTP/3
|
||||
frontend connection. The window size is adjusted based
|
||||
on the receiving rate of stream data. The initial value
|
||||
is the value specified by \fI\%\-\-frontend\-http3\-window\-size\fP
|
||||
and the window size grows up to <SIZE> bytes.
|
||||
.sp
|
||||
Default: \fB6M\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-frontend\-http3\-max\-connection\-window\-size=<SIZE>
|
||||
Sets the maximum per\-connection window size of HTTP/3
|
||||
frontend connection. The window size is adjusted based
|
||||
on the receiving rate of stream data. The initial value
|
||||
is the value specified by
|
||||
\fI\%\-\-frontend\-http3\-connection\-window\-size\fP and the window
|
||||
size grows up to <SIZE> bytes.
|
||||
.sp
|
||||
Default: \fB8M\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-frontend\-http3\-max\-concurrent\-streams=<N>
|
||||
Set the maximum number of the concurrent streams in one
|
||||
frontend HTTP/3 connection.
|
||||
.sp
|
||||
Default: \fB100\fP
|
||||
.UNINDENT
|
||||
.SS Misc
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -1769,15 +1933,15 @@ Error log is written to stderr by default. It can be configured
|
||||
using \fI\%\-\-errorlog\-file\fP\&. The format of log message is as
|
||||
follows:
|
||||
.sp
|
||||
<datetime> <master\-pid> <current\-pid> <thread\-id> <level> (<filename>:<line>) <msg>
|
||||
<datetime> <main\-pid> <current\-pid> <thread\-id> <level> (<filename>:<line>) <msg>
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B <datetime>
|
||||
It is a combination of date and time when the log is written. It
|
||||
is in ISO 8601 format.
|
||||
.TP
|
||||
.B <master\-pid>
|
||||
It is a master process ID.
|
||||
.B <main\-pid>
|
||||
It is a main process ID.
|
||||
.TP
|
||||
.B <current\-pid>
|
||||
It is a process ID which writes this log.
|
||||
@@ -1813,15 +1977,15 @@ SIGUSR2
|
||||
.INDENT 3.5
|
||||
Fork and execute nghttpx. It will execute the binary in the same
|
||||
path with same command\-line arguments and environment variables. As
|
||||
of nghttpx version 1.20.0, the new master process sends SIGQUIT to
|
||||
the original master process when it is ready to serve requests. For
|
||||
the earlier versions of nghttpx, user has to send SIGQUIT to the
|
||||
original master process.
|
||||
of nghttpx version 1.20.0, the new main process sends SIGQUIT to the
|
||||
original main process when it is ready to serve requests. For the
|
||||
earlier versions of nghttpx, user has to send SIGQUIT to the
|
||||
original main process.
|
||||
.sp
|
||||
The difference between SIGUSR2 (+ SIGQUIT) and SIGHUP is that former
|
||||
is usually used to execute new binary, and the master process is
|
||||
newly spawned. On the other hand, the latter just reloads
|
||||
configuration file, and the same master process continues to exist.
|
||||
is usually used to execute new binary, and the main process is newly
|
||||
spawned. On the other hand, the latter just reloads configuration
|
||||
file, and the same main process continues to exist.
|
||||
.UNINDENT
|
||||
.UNINDENT
|
||||
.sp
|
||||
@@ -1830,16 +1994,16 @@ configuration file, and the same master process continues to exist.
|
||||
.INDENT 3.5
|
||||
nghttpx consists of multiple processes: one process for processing
|
||||
these signals, and another one for processing requests. The former
|
||||
spawns the latter. The former is called master process, and the
|
||||
spawns the latter. The former is called main process, and the
|
||||
latter is called worker process. If neverbleed is enabled, the
|
||||
worker process spawns neverbleed daemon process which does RSA key
|
||||
processing. The above signal must be sent to the master process.
|
||||
If the other processes received one of them, it is ignored. This
|
||||
processing. The above signal must be sent to the main process. If
|
||||
the other processes received one of them, it is ignored. This
|
||||
behaviour of these processes may change in the future release. In
|
||||
other words, in the future release, the processes other than master
|
||||
other words, in the future release, the processes other than main
|
||||
process may terminate upon the reception of these signals.
|
||||
Therefore these signals should not be sent to the processes other
|
||||
than master process.
|
||||
than main process.
|
||||
.UNINDENT
|
||||
.UNINDENT
|
||||
.SH SERVER PUSH
|
||||
|
||||
@@ -14,7 +14,7 @@ SYNOPSIS
|
||||
DESCRIPTION
|
||||
-----------
|
||||
|
||||
A reverse proxy for HTTP/2, and HTTP/1.
|
||||
A reverse proxy for HTTP/3, HTTP/2, and HTTP/1.
|
||||
|
||||
.. describe:: <PRIVATE_KEY>
|
||||
|
||||
@@ -124,12 +124,13 @@ Connections
|
||||
"affinity=<METHOD>", "dns", "redirect-if-not-tls",
|
||||
"upgrade-scheme", "mruby=<PATH>",
|
||||
"read-timeout=<DURATION>", "write-timeout=<DURATION>",
|
||||
"group=<GROUP>", "group-weight=<N>", and "weight=<N>".
|
||||
The parameter consists of keyword, and optionally
|
||||
followed by "=" and value. For example, the parameter
|
||||
"proto=h2" consists of the keyword "proto" and value
|
||||
"h2". The parameter "tls" consists of the keyword "tls"
|
||||
without value. Each parameter is described as follows.
|
||||
"group=<GROUP>", "group-weight=<N>", "weight=<N>", and
|
||||
"dnf". The parameter consists of keyword, and
|
||||
optionally followed by "=" and value. For example, the
|
||||
parameter "proto=h2" consists of the keyword "proto" and
|
||||
value "h2". The parameter "tls" consists of the keyword
|
||||
"tls" without value. Each parameter is described as
|
||||
follows.
|
||||
|
||||
The backend application protocol can be specified using
|
||||
optional "proto" parameter, and in the form of
|
||||
@@ -260,9 +261,19 @@ Connections
|
||||
weight becomes 1. "weight" is ignored if session
|
||||
affinity is enabled.
|
||||
|
||||
If "dnf" parameter is specified, an incoming request is
|
||||
not forwarded to a backend and just consumed along with
|
||||
the request body (actually a backend server never be
|
||||
contacted). It is expected that the HTTP response is
|
||||
generated by mruby script (see "mruby=<PATH>" parameter
|
||||
above). "dnf" is an abbreviation of "do not forward".
|
||||
|
||||
Since ";" and ":" are used as delimiter, <PATTERN> must
|
||||
not contain these characters. Since ";" has special
|
||||
meaning in shell, the option value must be quoted.
|
||||
not contain these characters. In order to include ":"
|
||||
in <PATTERN>, one has to specify "%3A" (which is
|
||||
percent-encoded from of ":") instead. Since ";" has
|
||||
special meaning in shell, the option value must be
|
||||
quoted.
|
||||
|
||||
|
||||
Default: ``127.0.0.1,80``
|
||||
@@ -304,6 +315,12 @@ Connections
|
||||
connection, specify "proxyproto" parameter. This is
|
||||
disabled by default.
|
||||
|
||||
To receive HTTP/3 (QUIC) traffic, specify "quic"
|
||||
parameter. It makes nghttpx listen on UDP port rather
|
||||
than TCP port. UNIX domain socket, "api", and
|
||||
"healthmon" parameters cannot be used with "quic"
|
||||
parameter.
|
||||
|
||||
|
||||
Default: ``*,3000``
|
||||
|
||||
@@ -492,6 +509,12 @@ Timeout
|
||||
|
||||
Default: ``3m``
|
||||
|
||||
.. option:: --frontend-http3-read-timeout=<DURATION>
|
||||
|
||||
Specify read timeout for HTTP/3 frontend connection.
|
||||
|
||||
Default: ``3m``
|
||||
|
||||
.. option:: --frontend-read-timeout=<DURATION>
|
||||
|
||||
Specify read timeout for HTTP/1.1 frontend connection.
|
||||
@@ -922,19 +945,19 @@ SSL/TLS
|
||||
|
||||
Default: ``1s``
|
||||
|
||||
.. option:: --no-http2-cipher-black-list
|
||||
.. option:: --no-http2-cipher-block-list
|
||||
|
||||
Allow black listed cipher suite on frontend HTTP/2
|
||||
Allow block listed cipher suite on frontend HTTP/2
|
||||
connection. See
|
||||
https://tools.ietf.org/html/rfc7540#appendix-A for the
|
||||
complete HTTP/2 cipher suites black list.
|
||||
complete HTTP/2 cipher suites block list.
|
||||
|
||||
.. option:: --client-no-http2-cipher-black-list
|
||||
.. option:: --client-no-http2-cipher-block-list
|
||||
|
||||
Allow black listed cipher suite on backend HTTP/2
|
||||
Allow block listed cipher suite on backend HTTP/2
|
||||
connection. See
|
||||
https://tools.ietf.org/html/rfc7540#appendix-A for the
|
||||
complete HTTP/2 cipher suites black list.
|
||||
complete HTTP/2 cipher suites block list.
|
||||
|
||||
.. option:: --tls-sct-dir=<DIR>
|
||||
|
||||
@@ -957,9 +980,9 @@ SSL/TLS
|
||||
are skipped. The default enabled cipher list might not
|
||||
contain any PSK cipher suite. In that case, desired PSK
|
||||
cipher suites must be enabled using :option:`--ciphers` option.
|
||||
The desired PSK cipher suite may be black listed by
|
||||
The desired PSK cipher suite may be block listed by
|
||||
HTTP/2. To use those cipher suites with HTTP/2,
|
||||
consider to use :option:`--no-http2-cipher-black-list` option.
|
||||
consider to use :option:`--no-http2-cipher-block-list` option.
|
||||
But be aware its implications.
|
||||
|
||||
.. option:: --client-psk-secrets=<PATH>
|
||||
@@ -973,19 +996,20 @@ SSL/TLS
|
||||
The default enabled cipher list might not contain any
|
||||
PSK cipher suite. In that case, desired PSK cipher
|
||||
suites must be enabled using :option:`--client-ciphers` option.
|
||||
The desired PSK cipher suite may be black listed by
|
||||
The desired PSK cipher suite may be block listed by
|
||||
HTTP/2. To use those cipher suites with HTTP/2,
|
||||
consider to use :option:`--client-no-http2-cipher-black-list`
|
||||
consider to use :option:`--client-no-http2-cipher-block-list`
|
||||
option. But be aware its implications.
|
||||
|
||||
.. option:: --tls-no-postpone-early-data
|
||||
|
||||
By default, nghttpx postpones forwarding HTTP requests
|
||||
sent in early data, including those sent in partially in
|
||||
it, until TLS handshake finishes. If all backend server
|
||||
recognizes "Early-Data" header field, using this option
|
||||
makes nghttpx not postpone forwarding request and get
|
||||
full potential of 0-RTT data.
|
||||
By default, except for QUIC connections, nghttpx
|
||||
postpones forwarding HTTP requests sent in early data,
|
||||
including those sent in partially in it, until TLS
|
||||
handshake finishes. If all backend server recognizes
|
||||
"Early-Data" header field, using this option makes
|
||||
nghttpx not postpone forwarding request and get full
|
||||
potential of 0-RTT data.
|
||||
|
||||
.. option:: --tls-max-early-data=<SIZE>
|
||||
|
||||
@@ -1203,6 +1227,14 @@ Logging
|
||||
request. "-" if backend host is not available.
|
||||
* $backend_port: backend port used to fulfill the
|
||||
request. "-" if backend host is not available.
|
||||
* $method: HTTP method
|
||||
* $path: Request path including query. For CONNECT
|
||||
request, authority is recorded.
|
||||
* $path_without_query: $path up to the first '?'
|
||||
character. For CONNECT request, authority is
|
||||
recorded.
|
||||
* $protocol_version: HTTP version (e.g., HTTP/1.1,
|
||||
HTTP/2)
|
||||
|
||||
The variable can be enclosed by "{" and "}" for
|
||||
disambiguation (e.g., ${remote_addr}).
|
||||
@@ -1327,14 +1359,21 @@ HTTP
|
||||
mode. When :option:`--http2-proxy` is used, these headers will
|
||||
not be altered regardless of this option.
|
||||
|
||||
.. option:: --altsvc=<PROTOID,PORT[,HOST,[ORIGIN]]>
|
||||
.. option:: --altsvc=<PROTOID,PORT[,HOST,[ORIGIN[,PARAMS]]]>
|
||||
|
||||
Specify protocol ID, port, host and origin of
|
||||
alternative service. <HOST> and <ORIGIN> are optional.
|
||||
They are advertised in alt-svc header field only in
|
||||
HTTP/1.1 frontend. This option can be used multiple
|
||||
times to specify multiple alternative services.
|
||||
Example: :option:`--altsvc`\=h2,443
|
||||
alternative service. <HOST>, <ORIGIN> and <PARAMS> are
|
||||
optional. Empty <HOST> and <ORIGIN> are allowed and
|
||||
they are treated as nothing is specified. They are
|
||||
advertised in alt-svc header field only in HTTP/1.1
|
||||
frontend. This option can be used multiple times to
|
||||
specify multiple alternative services.
|
||||
Example: :option:`--altsvc`\="h2,443,,,ma=3600; persist=1'
|
||||
|
||||
.. option:: --http2-altsvc=<PROTOID,PORT[,HOST,[ORIGIN[,PARAMS]]]>
|
||||
|
||||
Just like :option:`--altsvc` option, but this altsvc is only sent
|
||||
in HTTP/2 frontend.
|
||||
|
||||
.. option:: --add-request-header=<HEADER>
|
||||
|
||||
@@ -1509,10 +1548,10 @@ 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
|
||||
2 processes: main and worker processes. If this option
|
||||
is used, main 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.
|
||||
|
||||
|
||||
@@ -1530,6 +1569,114 @@ Scripting
|
||||
file were specified for the pattern.
|
||||
|
||||
|
||||
HTTP/3 and QUIC
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
.. option:: --frontend-quic-idle-timeout=<DURATION>
|
||||
|
||||
Specify an idle timeout for QUIC connection.
|
||||
|
||||
Default: ``30s``
|
||||
|
||||
.. option:: --frontend-quic-debug-log
|
||||
|
||||
Output QUIC debug log to */dev/stderr.*
|
||||
|
||||
.. option:: --quic-bpf-program-file=<PATH>
|
||||
|
||||
Specify a path to eBPF program file reuseport_kern.o to
|
||||
direct an incoming QUIC UDP datagram to a correct
|
||||
socket.
|
||||
|
||||
Default: ``/usr/local/lib/nghttp2/reuseport_kern.o``
|
||||
|
||||
.. option:: --frontend-quic-early-data
|
||||
|
||||
Enable early data on frontend QUIC connections. nghttpx
|
||||
sends "Early-Data" header field to a backend server if a
|
||||
request is received in early data and handshake has not
|
||||
finished. All backend servers should deal with possibly
|
||||
replayed requests.
|
||||
|
||||
.. option:: --frontend-quic-qlog-dir=<DIR>
|
||||
|
||||
Specify a directory where a qlog file is written for
|
||||
frontend QUIC connections. A qlog file is created per
|
||||
each QUIC connection. The file name is ISO8601 basic
|
||||
format, followed by "-", server Source Connection ID and
|
||||
".qlog".
|
||||
|
||||
.. option:: --frontend-quic-require-token
|
||||
|
||||
Require an address validation token for a frontend QUIC
|
||||
connection. Server sends a token in Retry packet or
|
||||
NEW_TOKEN frame in the previous connection.
|
||||
|
||||
.. option:: --frontend-quic-congestion-controller=<CC>
|
||||
|
||||
Specify a congestion controller algorithm for a frontend
|
||||
QUIC connection. <CC> should be either "cubic" or
|
||||
"bbr".
|
||||
|
||||
Default: ``cubic``
|
||||
|
||||
.. option:: --frontend-quic-connection-id-encryption-key=<HEXSTRING>
|
||||
|
||||
Specify Connection ID encryption key. The encryption
|
||||
key must be 16 bytes, and it must be encoded in hex
|
||||
string (which is 32 bytes long). If this option is
|
||||
omitted, new key is generated. In order to survive QUIC
|
||||
connection in a configuration reload event, old and new
|
||||
configuration must have this option and share the same
|
||||
key.
|
||||
|
||||
.. option:: --no-quic-bpf
|
||||
|
||||
Disable eBPF.
|
||||
|
||||
.. option:: --frontend-http3-window-size=<SIZE>
|
||||
|
||||
Sets the per-stream initial window size of HTTP/3
|
||||
frontend connection.
|
||||
|
||||
Default: ``256K``
|
||||
|
||||
.. option:: --frontend-http3-connection-window-size=<SIZE>
|
||||
|
||||
Sets the per-connection window size of HTTP/3 frontend
|
||||
connection.
|
||||
|
||||
Default: ``1M``
|
||||
|
||||
.. option:: --frontend-http3-max-window-size=<SIZE>
|
||||
|
||||
Sets the maximum per-stream window size of HTTP/3
|
||||
frontend connection. The window size is adjusted based
|
||||
on the receiving rate of stream data. The initial value
|
||||
is the value specified by :option:`--frontend-http3-window-size`
|
||||
and the window size grows up to <SIZE> bytes.
|
||||
|
||||
Default: ``6M``
|
||||
|
||||
.. option:: --frontend-http3-max-connection-window-size=<SIZE>
|
||||
|
||||
Sets the maximum per-connection window size of HTTP/3
|
||||
frontend connection. The window size is adjusted based
|
||||
on the receiving rate of stream data. The initial value
|
||||
is the value specified by
|
||||
:option:`--frontend-http3-connection-window-size` and the window
|
||||
size grows up to <SIZE> bytes.
|
||||
|
||||
Default: ``8M``
|
||||
|
||||
.. option:: --frontend-http3-max-concurrent-streams=<N>
|
||||
|
||||
Set the maximum number of the concurrent streams in one
|
||||
frontend HTTP/3 connection.
|
||||
|
||||
Default: ``100``
|
||||
|
||||
|
||||
Misc
|
||||
~~~~
|
||||
|
||||
@@ -1614,14 +1761,14 @@ Error log
|
||||
using :option:`--errorlog-file`. The format of log message is as
|
||||
follows:
|
||||
|
||||
<datetime> <master-pid> <current-pid> <thread-id> <level> (<filename>:<line>) <msg>
|
||||
<datetime> <main-pid> <current-pid> <thread-id> <level> (<filename>:<line>) <msg>
|
||||
|
||||
<datetime>
|
||||
It is a combination of date and time when the log is written. It
|
||||
is in ISO 8601 format.
|
||||
|
||||
<master-pid>
|
||||
It is a master process ID.
|
||||
<main-pid>
|
||||
It is a main process ID.
|
||||
|
||||
<current-pid>
|
||||
It is a process ID which writes this log.
|
||||
@@ -1654,30 +1801,30 @@ SIGUSR2
|
||||
|
||||
Fork and execute nghttpx. It will execute the binary in the same
|
||||
path with same command-line arguments and environment variables. As
|
||||
of nghttpx version 1.20.0, the new master process sends SIGQUIT to
|
||||
the original master process when it is ready to serve requests. For
|
||||
the earlier versions of nghttpx, user has to send SIGQUIT to the
|
||||
original master process.
|
||||
of nghttpx version 1.20.0, the new main process sends SIGQUIT to the
|
||||
original main process when it is ready to serve requests. For the
|
||||
earlier versions of nghttpx, user has to send SIGQUIT to the
|
||||
original main process.
|
||||
|
||||
The difference between SIGUSR2 (+ SIGQUIT) and SIGHUP is that former
|
||||
is usually used to execute new binary, and the master process is
|
||||
newly spawned. On the other hand, the latter just reloads
|
||||
configuration file, and the same master process continues to exist.
|
||||
is usually used to execute new binary, and the main process is newly
|
||||
spawned. On the other hand, the latter just reloads configuration
|
||||
file, and the same main process continues to exist.
|
||||
|
||||
.. note::
|
||||
|
||||
nghttpx consists of multiple processes: one process for processing
|
||||
these signals, and another one for processing requests. The former
|
||||
spawns the latter. The former is called master process, and the
|
||||
spawns the latter. The former is called main process, and the
|
||||
latter is called worker process. If neverbleed is enabled, the
|
||||
worker process spawns neverbleed daemon process which does RSA key
|
||||
processing. The above signal must be sent to the master process.
|
||||
If the other processes received one of them, it is ignored. This
|
||||
processing. The above signal must be sent to the main process. If
|
||||
the other processes received one of them, it is ignored. This
|
||||
behaviour of these processes may change in the future release. In
|
||||
other words, in the future release, the processes other than master
|
||||
other words, in the future release, the processes other than main
|
||||
process may terminate upon the reception of these signals.
|
||||
Therefore these signals should not be sent to the processes other
|
||||
than master process.
|
||||
than main process.
|
||||
|
||||
SERVER PUSH
|
||||
-----------
|
||||
|
||||
@@ -46,14 +46,14 @@ Error log
|
||||
using :option:`--errorlog-file`. The format of log message is as
|
||||
follows:
|
||||
|
||||
<datetime> <master-pid> <current-pid> <thread-id> <level> (<filename>:<line>) <msg>
|
||||
<datetime> <main-pid> <current-pid> <thread-id> <level> (<filename>:<line>) <msg>
|
||||
|
||||
<datetime>
|
||||
It is a combination of date and time when the log is written. It
|
||||
is in ISO 8601 format.
|
||||
|
||||
<master-pid>
|
||||
It is a master process ID.
|
||||
<main-pid>
|
||||
It is a main process ID.
|
||||
|
||||
<current-pid>
|
||||
It is a process ID which writes this log.
|
||||
@@ -86,30 +86,30 @@ SIGUSR2
|
||||
|
||||
Fork and execute nghttpx. It will execute the binary in the same
|
||||
path with same command-line arguments and environment variables. As
|
||||
of nghttpx version 1.20.0, the new master process sends SIGQUIT to
|
||||
the original master process when it is ready to serve requests. For
|
||||
the earlier versions of nghttpx, user has to send SIGQUIT to the
|
||||
original master process.
|
||||
of nghttpx version 1.20.0, the new main process sends SIGQUIT to the
|
||||
original main process when it is ready to serve requests. For the
|
||||
earlier versions of nghttpx, user has to send SIGQUIT to the
|
||||
original main process.
|
||||
|
||||
The difference between SIGUSR2 (+ SIGQUIT) and SIGHUP is that former
|
||||
is usually used to execute new binary, and the master process is
|
||||
newly spawned. On the other hand, the latter just reloads
|
||||
configuration file, and the same master process continues to exist.
|
||||
is usually used to execute new binary, and the main process is newly
|
||||
spawned. On the other hand, the latter just reloads configuration
|
||||
file, and the same main process continues to exist.
|
||||
|
||||
.. note::
|
||||
|
||||
nghttpx consists of multiple processes: one process for processing
|
||||
these signals, and another one for processing requests. The former
|
||||
spawns the latter. The former is called master process, and the
|
||||
spawns the latter. The former is called main process, and the
|
||||
latter is called worker process. If neverbleed is enabled, the
|
||||
worker process spawns neverbleed daemon process which does RSA key
|
||||
processing. The above signal must be sent to the master process.
|
||||
If the other processes received one of them, it is ignored. This
|
||||
processing. The above signal must be sent to the main process. If
|
||||
the other processes received one of them, it is ignored. This
|
||||
behaviour of these processes may change in the future release. In
|
||||
other words, in the future release, the processes other than master
|
||||
other words, in the future release, the processes other than main
|
||||
process may terminate upon the reception of these signals.
|
||||
Therefore these signals should not be sent to the processes other
|
||||
than master process.
|
||||
than main process.
|
||||
|
||||
SERVER PUSH
|
||||
-----------
|
||||
|
||||
@@ -6,7 +6,7 @@ Architecture
|
||||
|
||||
The most notable point in nghttp2 library architecture is it does not
|
||||
perform any I/O. nghttp2 only performs HTTP/2 protocol stuff based on
|
||||
input byte strings. It will calls callback functions set by
|
||||
input byte strings. It will call callback functions set by
|
||||
applications while processing input. The output of nghttp2 is just
|
||||
byte string. An application is responsible to send these output to
|
||||
the remote peer. The callback functions may be called while producing
|
||||
|
||||
@@ -26,16 +26,16 @@ 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-9.
|
||||
between versions, we currently use clang-format-12.
|
||||
|
||||
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.
|
||||
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-9
|
||||
in debian), either add it to PATH variable or add git option
|
||||
``clangformatdiff.binary`` to point to the script.
|
||||
your PATH or it exists under different name (e.g.,
|
||||
clang-format-diff-12 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.
|
||||
clang-format.el should come with clang distribution. If it is not
|
||||
|
||||
@@ -131,3 +131,12 @@ specify ``unix:`` followed by the path to UNIX domain socket. For
|
||||
example, if UNIX domain socket is ``/tmp/nghttpx.sock``, use
|
||||
``--base-uri=unix:/tmp/nghttpx.sock``. h2load uses scheme, host and
|
||||
port in the first URI in command-line or input file.
|
||||
|
||||
HTTP/3
|
||||
------
|
||||
|
||||
h2load supports HTTP/3 if it is built with HTTP/3 enabled. HTTP/3
|
||||
support is experimental.
|
||||
|
||||
In order to send HTTP/3 request, specify ``h3`` to
|
||||
:option:`--npn-list`.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.. nghttp2 documentation master file, created by
|
||||
.. nghttp2 documentation main file, created by
|
||||
sphinx-quickstart on Sun Mar 11 22:57:49 2012.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
@@ -14,8 +14,8 @@ Default mode
|
||||
|
||||
If nghttpx is invoked without :option:`--http2-proxy`, it operates in
|
||||
default mode. In this mode, it works as reverse proxy (gateway) for
|
||||
both HTTP/2 and HTTP/1 clients to backend servers. This is also known
|
||||
as "HTTP/2 router".
|
||||
HTTP/3, HTTP/2 and HTTP/1 clients to backend servers. This is also
|
||||
known as "HTTP/2 router".
|
||||
|
||||
By default, frontend connection is encrypted using SSL/TLS. So
|
||||
server's private key and certificate must be supplied to the command
|
||||
@@ -28,6 +28,9 @@ 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.
|
||||
|
||||
In order to receive HTTP/3 traffic, use ``quic`` parameter in
|
||||
:option:`--frontend` option (.e.g, ``--frontend='*,443;quic'``)
|
||||
|
||||
nghttpx can listen on multiple frontend addresses. This is achieved
|
||||
by using multiple :option:`--frontend` options. For each frontend
|
||||
address, TLS can be enabled or disabled.
|
||||
@@ -228,7 +231,7 @@ process. It will do fork and execute new executable, using same
|
||||
command-line arguments and environment variables.
|
||||
|
||||
As of nghttpx version 1.20.0, that is all you have to do. The new
|
||||
master process sends QUIT signal to the original process, when it is
|
||||
main process sends QUIT signal to the original process, when it is
|
||||
ready to serve requests, to shut it down gracefully.
|
||||
|
||||
For earlier versions of nghttpx, you have to do one more thing. At
|
||||
@@ -239,7 +242,7 @@ current process will exit. At this point, only new nghttpx process
|
||||
exists and serves incoming requests.
|
||||
|
||||
If you want to just reload configuration file without executing new
|
||||
binary, send SIGHUP to nghttpx master process.
|
||||
binary, send SIGHUP to nghttpx main process.
|
||||
|
||||
Re-opening log files
|
||||
--------------------
|
||||
@@ -445,10 +448,10 @@ nghttpx server accepts any of the identity and secret pairs in the
|
||||
file. The default cipher suite list does not contain PSK cipher
|
||||
suites. In order to use PSK, PSK cipher suite must be enabled by
|
||||
using :option:`--ciphers` option. The desired PSK cipher suite may be
|
||||
listed in `HTTP/2 cipher black list
|
||||
listed in `HTTP/2 cipher block list
|
||||
<https://tools.ietf.org/html/rfc7540#appendix-A>`_. In order to use
|
||||
such PSK cipher suite with HTTP/2, disable HTTP/2 cipher black list by
|
||||
using :option:`--no-http2-cipher-black-list` option. But you should
|
||||
such PSK cipher suite with HTTP/2, disable HTTP/2 cipher block list by
|
||||
using :option:`--no-http2-cipher-block-list` option. But you should
|
||||
understand its implications.
|
||||
|
||||
At the time of writing, even if only PSK cipher suites are specified
|
||||
@@ -468,10 +471,10 @@ used, like so:
|
||||
The default cipher suite list does not contain PSK cipher suites. In
|
||||
order to use PSK, PSK cipher suite must be enabled by using
|
||||
:option:`--client-ciphers` option. The desired PSK cipher suite may
|
||||
be listed in `HTTP/2 cipher black list
|
||||
be listed in `HTTP/2 cipher block list
|
||||
<https://tools.ietf.org/html/rfc7540#appendix-A>`_. In order to use
|
||||
such PSK cipher suite with HTTP/2, disable HTTP/2 cipher black list by
|
||||
using :option:`--client-no-http2-cipher-black-list` option. But you
|
||||
such PSK cipher suite with HTTP/2, disable HTTP/2 cipher block list by
|
||||
using :option:`--client-no-http2-cipher-block-list` option. But you
|
||||
should understand its implications.
|
||||
|
||||
TLSv1.3
|
||||
@@ -509,6 +512,42 @@ Bootstrapping WebSockets with HTTP/2 for both frontend and backend
|
||||
connections. This feature is enabled by default and no configuration
|
||||
is required.
|
||||
|
||||
WebSockets over HTTP/3 is also supported.
|
||||
|
||||
HTTP/3
|
||||
------
|
||||
|
||||
nghttpx supports HTTP/3 if it is built with HTTP/3 support enabled.
|
||||
HTTP/3 support is experimental.
|
||||
|
||||
In order to listen UDP port to receive HTTP/3 traffic,
|
||||
:option:`--frontend` option must have ``quic`` parameter:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
frontend=*,443;quic
|
||||
|
||||
The above example makes nghttpx receive HTTP/3 traffic on UDP
|
||||
port 443.
|
||||
|
||||
nghttpx does not support HTTP/3 on backend connection.
|
||||
|
||||
Hot swapping (SIGUSR2) or configuration reload (SIGHUP) require eBPF
|
||||
program. Without eBPF, old worker processes keep getting HTTP/3
|
||||
traffic and do not work as intended. Connection ID encryption key
|
||||
must be set with
|
||||
:option:`--frontend-quic-connection-id-encryption-key` and must not
|
||||
change in order to keep the existing connections alive during reload.
|
||||
|
||||
In order announce that HTTP/3 endpoint is available, you should
|
||||
specify alt-svc header field. For example, the following options send
|
||||
alt-svc header field in HTTP/1.1 and HTTP/2 response:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
altsvc=h3,443,,,ma=3600
|
||||
http2-altsvc=h3,443,,,ma=3600
|
||||
|
||||
Migration from nghttpx v1.18.x or earlier
|
||||
-----------------------------------------
|
||||
|
||||
@@ -516,10 +555,10 @@ As of nghttpx v1.19.0, :option:`--ciphers` option only changes cipher
|
||||
list for frontend TLS connection. In order to change cipher list for
|
||||
backend connection, use :option:`--client-ciphers` option.
|
||||
|
||||
Similarly, :option:`--no-http2-cipher-black-list` option only disables
|
||||
HTTP/2 cipher black list for frontend connection. In order to disable
|
||||
HTTP/2 cipher black list for backend connection, use
|
||||
:option:`--client-no-http2-cipher-black-list` option.
|
||||
Similarly, :option:`--no-http2-cipher-block-list` option only disables
|
||||
HTTP/2 cipher block list for frontend connection. In order to disable
|
||||
HTTP/2 cipher block list for backend connection, use
|
||||
:option:`--client-no-http2-cipher-block-list` option.
|
||||
|
||||
``--accept-proxy-protocol`` option was deprecated. Instead, use
|
||||
``proxyproto`` parameter in :option:`--frontend` option to enable
|
||||
|
||||
@@ -13,7 +13,7 @@ The extension module is called ``nghttp2``.
|
||||
determined by configure script. If the detected Python version is not
|
||||
what you expect, specify a path to Python executable in ``PYTHON``
|
||||
variable as an argument to configure script (e.g., ``./configure
|
||||
PYTHON=/usr/bin/python3.5``).
|
||||
PYTHON=/usr/bin/python3.8``).
|
||||
|
||||
HPACK API
|
||||
---------
|
||||
@@ -137,14 +137,14 @@ HTTP/2 servers
|
||||
.. note::
|
||||
|
||||
We use :py:mod:`asyncio` for HTTP/2 server classes, and ALPN.
|
||||
Therefore, Python 3.5 or later is required to use these objects.
|
||||
To explicitly configure nghttp2 build to use Python 3.5, specify
|
||||
the ``PYTHON`` variable to the path to Python 3.5 executable when
|
||||
Therefore, Python 3.8 or later is required to use these objects.
|
||||
To explicitly configure nghttp2 build to use Python 3.8, specify
|
||||
the ``PYTHON`` variable to the path to Python 3.8 executable when
|
||||
invoking configure script like this:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ ./configure PYTHON=/usr/bin/python3.5
|
||||
$ ./configure PYTHON=/usr/bin/python3.8
|
||||
|
||||
.. py:class:: HTTP2Server(address, RequestHandlerClass, ssl=None)
|
||||
|
||||
@@ -336,7 +336,7 @@ The following example illustrates :py:class:`HTTP2Server` and
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import io, ssl
|
||||
|
||||
@@ -367,7 +367,7 @@ response body generation. This is simplified reverse proxy:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import ssl
|
||||
import os
|
||||
|
||||
@@ -33,6 +33,6 @@ attached to this kind of issue.
|
||||
Before few hours of new release, we merge the fixes to the master
|
||||
branch (and/or a release branch if necessary) and make a new release.
|
||||
Security advisory is disclosed on GitHub. We also post the
|
||||
vulnerability information to `oss-secirty
|
||||
vulnerability information to `oss-security
|
||||
<https://oss-security.openwall.org/wiki/mailing-lists/oss-security>`_
|
||||
mailing list.
|
||||
|
||||
75
docker/Dockerfile
Normal file
75
docker/Dockerfile
Normal file
@@ -0,0 +1,75 @@
|
||||
FROM debian:11 as build
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
git clang make binutils autoconf automake autotools-dev libtool \
|
||||
pkg-config \
|
||||
zlib1g-dev libev-dev libjemalloc-dev ruby-dev libc-ares-dev bison \
|
||||
libelf-dev
|
||||
|
||||
RUN git clone --depth 1 -b openssl-3.0.0+quic https://github.com/quictls/openssl && \
|
||||
cd openssl && \
|
||||
./config --openssldir=/etc/ssl && \
|
||||
make -j$(nproc) && \
|
||||
make install_sw && \
|
||||
cd .. && \
|
||||
rm -rf openssl
|
||||
|
||||
RUN git clone --depth 1 https://github.com/ngtcp2/nghttp3 && \
|
||||
cd nghttp3 && \
|
||||
autoreconf -i && \
|
||||
./configure --enable-lib-only && \
|
||||
make -j$(nproc) && \
|
||||
make install-strip && \
|
||||
cd .. && \
|
||||
rm -rf nghttp3
|
||||
|
||||
RUN git clone --depth 1 https://github.com/ngtcp2/ngtcp2 && \
|
||||
cd ngtcp2 && \
|
||||
autoreconf -i && \
|
||||
./configure --enable-lib-only \
|
||||
LIBTOOL_LDFLAGS="-static-libtool-libs" \
|
||||
OPENSSL_LIBS="-l:libssl.a -l:libcrypto.a -ldl -lpthread" \
|
||||
PKG_CONFIG_PATH="/usr/local/lib64/pkgconfig" && \
|
||||
make -j$(nproc) && \
|
||||
make install-strip && \
|
||||
cd .. && \
|
||||
rm -rf ngtcp2
|
||||
|
||||
RUN git clone --depth 1 -b v0.4.0 https://github.com/libbpf/libbpf && \
|
||||
cd libbpf && \
|
||||
PREFIX=/usr/local make -C src install && \
|
||||
cd .. && \
|
||||
rm -rf libbpf
|
||||
|
||||
RUN git clone --depth 1 https://github.com/nghttp2/nghttp2.git && \
|
||||
cd nghttp2 && \
|
||||
git submodule update --init && \
|
||||
autoreconf -i && \
|
||||
./configure --disable-examples --disable-hpack-tools \
|
||||
--disable-python-bindings --with-mruby --with-neverbleed \
|
||||
--enable-http3 --with-libbpf \
|
||||
CC=clang CXX=clang++ \
|
||||
LIBTOOL_LDFLAGS="-static-libtool-libs" \
|
||||
OPENSSL_LIBS="-l:libssl.a -l:libcrypto.a -ldl -pthread" \
|
||||
LIBEV_LIBS="-l:libev.a" \
|
||||
JEMALLOC_LIBS="-l:libjemalloc.a" \
|
||||
LIBCARES_LIBS="-l:libcares.a" \
|
||||
ZLIB_LIBS="-l:libz.a" \
|
||||
LIBBPF_LIBS="-L/usr/local/lib64 -l:libbpf.a -l:libelf.a" \
|
||||
LDFLAGS="-static-libgcc -static-libstdc++" \
|
||||
PKG_CONFIG_PATH="/usr/local/lib64/pkgconfig" && \
|
||||
make -j$(nproc) install-strip && \
|
||||
cd .. && \
|
||||
rm -rf nghttp2
|
||||
|
||||
FROM gcr.io/distroless/base-debian11
|
||||
|
||||
COPY --from=build \
|
||||
/usr/local/bin/h2load \
|
||||
/usr/local/bin/nghttpx \
|
||||
/usr/local/bin/nghttp \
|
||||
/usr/local/bin/nghttpd \
|
||||
/usr/local/bin/
|
||||
COPY --from=build /usr/local/lib/nghttp2/reuseport_kern.o \
|
||||
/usr/local/lib/nghttp2/
|
||||
25
docker/README.rst
Normal file
25
docker/README.rst
Normal file
@@ -0,0 +1,25 @@
|
||||
Dockerfile
|
||||
==========
|
||||
|
||||
Dockerfile creates the applications bundled with nghttp2.
|
||||
These applications are:
|
||||
|
||||
- nghttp
|
||||
- nghttpd
|
||||
- nghttpx
|
||||
- h2load
|
||||
|
||||
HTTP/3 and eBPF features are enabled.
|
||||
|
||||
In order to run nghttpx with HTTP/3 endpoint, you need to run the
|
||||
image with the escalated privilege and higher memlock value. Here is
|
||||
the example command-line to run nghttpx to listen to HTTP/3 on port
|
||||
443, assuming that the current directory contains a private key and a
|
||||
certificate in server.key and server.crt respectively :
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ docker run --rm -it -v $PWD:/shared --net=host --privileged \
|
||||
--ulimit memlock=2048000 nghttp2 nghttpx \
|
||||
/shared/server.key /shared/server.crt \
|
||||
-f'*,443;quic'
|
||||
@@ -35,6 +35,7 @@ AM_CPPFLAGS = \
|
||||
@LIBEVENT_OPENSSL_CFLAGS@ \
|
||||
@OPENSSL_CFLAGS@ \
|
||||
@DEFS@
|
||||
AM_LDFLAGS = @LIBTOOL_LDFLAGS@
|
||||
LDADD = $(top_builddir)/lib/libnghttp2.la \
|
||||
$(top_builddir)/third-party/liburl-parser.la \
|
||||
@LIBEVENT_OPENSSL_LIBS@ \
|
||||
|
||||
@@ -142,7 +142,6 @@ static int alpn_select_proto_cb(SSL *ssl, const unsigned char **out,
|
||||
/* Create SSL_CTX. */
|
||||
static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) {
|
||||
SSL_CTX *ssl_ctx;
|
||||
EC_KEY *ecdh;
|
||||
|
||||
ssl_ctx = SSL_CTX_new(SSLv23_server_method());
|
||||
if (!ssl_ctx) {
|
||||
@@ -153,14 +152,28 @@ static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) {
|
||||
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
|
||||
SSL_OP_NO_COMPRESSION |
|
||||
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
|
||||
|
||||
ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
|
||||
if (!ecdh) {
|
||||
errx(1, "EC_KEY_new_by_curv_name failed: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
||||
{
|
||||
EVP_PKEY *ecdh;
|
||||
ecdh = EVP_EC_gen("P-256");
|
||||
if (!ecdh) {
|
||||
errx(1, "EVP_EC_gen failed: %s", ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh);
|
||||
EVP_PKEY_free(ecdh);
|
||||
}
|
||||
SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh);
|
||||
EC_KEY_free(ecdh);
|
||||
#else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
|
||||
{
|
||||
EC_KEY *ecdh;
|
||||
ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
|
||||
if (!ecdh) {
|
||||
errx(1, "EC_KEY_new_by_curv_name failed: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh);
|
||||
EC_KEY_free(ecdh);
|
||||
}
|
||||
#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
|
||||
|
||||
if (SSL_CTX_use_PrivateKey_file(ssl_ctx, key_file, SSL_FILETYPE_PEM) != 1) {
|
||||
errx(1, "Could not read private key file %s", key_file);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
|
||||
def name(i):
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
|
||||
def name(i):
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from gentokenlookup import gentokenlookup
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
|
||||
HEADERS = [
|
||||
(':authority', 0),
|
||||
@@ -96,47 +96,47 @@ def build_header(headers):
|
||||
|
||||
def gen_enum():
|
||||
name = ''
|
||||
print 'typedef enum {'
|
||||
print('typedef enum {')
|
||||
for k, token in HEADERS:
|
||||
if token is None:
|
||||
print ' {},'.format(to_enum_hd(k))
|
||||
print(' {},'.format(to_enum_hd(k)))
|
||||
else:
|
||||
if name != k:
|
||||
name = k
|
||||
print ' {} = {},'.format(to_enum_hd(k), token)
|
||||
print '} nghttp2_token;'
|
||||
print(' {} = {},'.format(to_enum_hd(k), token))
|
||||
print('} nghttp2_token;')
|
||||
|
||||
def gen_index_header():
|
||||
print '''\
|
||||
print('''\
|
||||
static int32_t lookup_token(const uint8_t *name, size_t namelen) {
|
||||
switch (namelen) {'''
|
||||
switch (namelen) {''')
|
||||
b = build_header(HEADERS)
|
||||
for size in sorted(b.keys()):
|
||||
ents = b[size]
|
||||
print '''\
|
||||
case {}:'''.format(size)
|
||||
print '''\
|
||||
switch (name[{}]) {{'''.format(size - 1)
|
||||
print('''\
|
||||
case {}:'''.format(size))
|
||||
print('''\
|
||||
switch (name[{}]) {{'''.format(size - 1))
|
||||
for c in sorted(ents.keys()):
|
||||
headers = sorted(ents[c])
|
||||
print '''\
|
||||
case '{}':'''.format(c)
|
||||
print('''\
|
||||
case '{}':'''.format(c))
|
||||
for k in headers:
|
||||
print '''\
|
||||
print('''\
|
||||
if (memeq("{}", name, {})) {{
|
||||
return {};
|
||||
}}'''.format(k[:-1], size - 1, to_enum_hd(k))
|
||||
print '''\
|
||||
break;'''
|
||||
print '''\
|
||||
}}'''.format(k[:-1], size - 1, to_enum_hd(k)))
|
||||
print('''\
|
||||
break;''')
|
||||
print('''\
|
||||
}
|
||||
break;'''
|
||||
print '''\
|
||||
break;''')
|
||||
print('''\
|
||||
}
|
||||
return -1;
|
||||
}'''
|
||||
}''')
|
||||
|
||||
if __name__ == '__main__':
|
||||
gen_enum()
|
||||
print ''
|
||||
print()
|
||||
gen_index_header()
|
||||
|
||||
29
genmethodchartbl.py
Executable file
29
genmethodchartbl.py
Executable file
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
|
||||
def name(i):
|
||||
if i < 0x21:
|
||||
return \
|
||||
['NUL ', 'SOH ', 'STX ', 'ETX ', 'EOT ', 'ENQ ', 'ACK ', 'BEL ',
|
||||
'BS ', 'HT ', 'LF ', 'VT ', 'FF ', 'CR ', 'SO ', 'SI ',
|
||||
'DLE ', 'DC1 ', 'DC2 ', 'DC3 ', 'DC4 ', 'NAK ', 'SYN ', 'ETB ',
|
||||
'CAN ', 'EM ', 'SUB ', 'ESC ', 'FS ', 'GS ', 'RS ', 'US ',
|
||||
'SPC '][i]
|
||||
elif i == 0x7f:
|
||||
return 'DEL '
|
||||
|
||||
for i in range(256):
|
||||
if chr(i) in ["!" , "#" , "$" , "%" , "&" , "'" , "*",
|
||||
"+" , "-" , "." , "^" , "_" , "`" , "|" , "~"] or\
|
||||
('0' <= chr(i) and chr(i) <= '9') or \
|
||||
('A' <= chr(i) and chr(i) <= 'Z') or \
|
||||
('a' <= chr(i) and chr(i) <= 'z'):
|
||||
sys.stdout.write('1 /* {} */, '.format(chr(i)))
|
||||
elif (0x21 <= i and i < 0x7f):
|
||||
sys.stdout.write('0 /* {} */, '.format(chr(i)))
|
||||
elif 0x80 <= i:
|
||||
sys.stdout.write('0 /* {} */, '.format(hex(i)))
|
||||
else:
|
||||
sys.stdout.write('0 /* {} */, '.format(name(i)))
|
||||
if (i + 1)%4 == 0:
|
||||
sys.stdout.write('\n')
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
from __future__ import unicode_literals
|
||||
#!/usr/bin/env python3
|
||||
from io import StringIO
|
||||
|
||||
from gentokenlookup import gentokenlookup
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from gentokenlookup import gentokenlookup
|
||||
|
||||
@@ -113,6 +113,7 @@ OPTIONS = [
|
||||
"max-request-header-fields",
|
||||
"header-field-buffer",
|
||||
"max-header-fields",
|
||||
"no-http2-cipher-block-list",
|
||||
"no-http2-cipher-black-list",
|
||||
"backend-http1-tls",
|
||||
"tls-session-cache-memcached-cert-file",
|
||||
@@ -155,6 +156,7 @@ OPTIONS = [
|
||||
"frontend-keep-alive-timeout",
|
||||
"psk-secrets",
|
||||
"client-psk-secrets",
|
||||
"client-no-http2-cipher-block-list",
|
||||
"client-no-http2-cipher-black-list",
|
||||
"client-ciphers",
|
||||
"accesslog-write-early",
|
||||
@@ -175,6 +177,22 @@ OPTIONS = [
|
||||
"tls13-ciphers",
|
||||
"tls13-client-ciphers",
|
||||
"no-strip-incoming-early-data",
|
||||
"quic-bpf-program-file",
|
||||
"no-quic-bpf",
|
||||
"http2-altsvc",
|
||||
"frontend-http3-read-timeout",
|
||||
"frontend-quic-idle-timeout",
|
||||
"frontend-quic-debug-log",
|
||||
"frontend-http3-window-size",
|
||||
"frontend-http3-connection-window-size",
|
||||
"frontend-http3-max-window-size",
|
||||
"frontend-http3-max-connection-window-size",
|
||||
"frontend-http3-max-concurrent-streams",
|
||||
"frontend-quic-early-data",
|
||||
"frontend-quic-qlog-dir",
|
||||
"frontend-quic-require-token",
|
||||
"frontend-quic-congestion-controller",
|
||||
"frontend-quic-connection-id-encryption-key",
|
||||
]
|
||||
|
||||
LOGVARS = [
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
|
||||
def name(i):
|
||||
|
||||
23
genpathchartbl.py
Executable file
23
genpathchartbl.py
Executable file
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
|
||||
def name(i):
|
||||
if i < 0x21:
|
||||
return \
|
||||
['NUL ', 'SOH ', 'STX ', 'ETX ', 'EOT ', 'ENQ ', 'ACK ', 'BEL ',
|
||||
'BS ', 'HT ', 'LF ', 'VT ', 'FF ', 'CR ', 'SO ', 'SI ',
|
||||
'DLE ', 'DC1 ', 'DC2 ', 'DC3 ', 'DC4 ', 'NAK ', 'SYN ', 'ETB ',
|
||||
'CAN ', 'EM ', 'SUB ', 'ESC ', 'FS ', 'GS ', 'RS ', 'US ',
|
||||
'SPC '][i]
|
||||
elif i == 0x7f:
|
||||
return 'DEL '
|
||||
|
||||
for i in range(256):
|
||||
if (0x21 <= i and i < 0x7f):
|
||||
sys.stdout.write('1 /* {} */, '.format(chr(i)))
|
||||
elif 0x80 <= i:
|
||||
sys.stdout.write('1 /* {} */, '.format(hex(i)))
|
||||
else:
|
||||
sys.stdout.write('0 /* {} */, '.format(name(i)))
|
||||
if (i + 1)%4 == 0:
|
||||
sys.stdout.write('\n')
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
|
||||
def to_enum_hd(k, prefix):
|
||||
res = prefix
|
||||
@@ -24,46 +24,46 @@ def build_header(headers):
|
||||
return res
|
||||
|
||||
def gen_enum(tokens, prefix):
|
||||
print '''\
|
||||
enum {'''
|
||||
print('''\
|
||||
enum {''')
|
||||
for k in sorted(tokens):
|
||||
print '''\
|
||||
{},'''.format(to_enum_hd(k, prefix))
|
||||
print '''\
|
||||
print('''\
|
||||
{},'''.format(to_enum_hd(k, prefix)))
|
||||
print('''\
|
||||
{}MAXIDX,
|
||||
}};'''.format(prefix)
|
||||
}};'''.format(prefix))
|
||||
|
||||
def gen_index_header(tokens, prefix, value_type, comp_fun, return_type, fail_value):
|
||||
print '''\
|
||||
print('''\
|
||||
{} lookup_token(const {} *name, size_t namelen) {{
|
||||
switch (namelen) {{'''.format(return_type, value_type)
|
||||
switch (namelen) {{'''.format(return_type, value_type))
|
||||
b = build_header(tokens)
|
||||
for size in sorted(b.keys()):
|
||||
ents = b[size]
|
||||
print '''\
|
||||
case {}:'''.format(size)
|
||||
print '''\
|
||||
switch (name[{}]) {{'''.format(size - 1)
|
||||
print('''\
|
||||
case {}:'''.format(size))
|
||||
print('''\
|
||||
switch (name[{}]) {{'''.format(size - 1))
|
||||
for c in sorted(ents.keys()):
|
||||
headers = sorted(ents[c])
|
||||
print '''\
|
||||
case '{}':'''.format(c)
|
||||
print('''\
|
||||
case '{}':'''.format(c))
|
||||
for k in headers:
|
||||
print '''\
|
||||
print('''\
|
||||
if ({}("{}", name, {})) {{
|
||||
return {};
|
||||
}}'''.format(comp_fun, k[:-1], size - 1, to_enum_hd(k, prefix))
|
||||
print '''\
|
||||
break;'''
|
||||
print '''\
|
||||
}}'''.format(comp_fun, k[:-1], size - 1, to_enum_hd(k, prefix)))
|
||||
print('''\
|
||||
break;''')
|
||||
print('''\
|
||||
}
|
||||
break;'''
|
||||
print '''\
|
||||
break;''')
|
||||
print('''\
|
||||
}}
|
||||
return {};
|
||||
}}'''.format(fail_value)
|
||||
}}'''.format(fail_value))
|
||||
|
||||
def gentokenlookup(tokens, prefix, value_type='uint8_t', comp_fun='util::streq_l', return_type='int', fail_value='-1'):
|
||||
gen_enum(tokens, prefix)
|
||||
print ''
|
||||
print()
|
||||
gen_index_header(tokens, prefix, value_type, comp_fun, return_type, fail_value)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
|
||||
def name(i):
|
||||
@@ -20,8 +20,6 @@ for i in range(256):
|
||||
sys.stdout.write('1 /* {} */, '.format(chr(i)))
|
||||
elif 0x80 <= i:
|
||||
sys.stdout.write('1 /* {} */, '.format(hex(i)))
|
||||
elif 0 == i:
|
||||
sys.stdout.write('1 /* NUL */, ')
|
||||
else:
|
||||
sys.stdout.write('0 /* {} */, '.format(name(i)))
|
||||
if (i + 1)%4 == 0:
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# script to produce rst file from program's help output.
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from __future__ import print_function
|
||||
import sys
|
||||
import re
|
||||
import argparse
|
||||
|
||||
@@ -1171,3 +1171,31 @@ Content-Length: 1000000
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H1ChunkedEndsPrematurely tests that an HTTP/1.1 request fails
|
||||
// if the backend chunked encoded response ends prematurely.
|
||||
func TestH1H1ChunkedEndsPrematurely(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 200\r\nTransfer-Encoding: chunked\r\n\r\n")
|
||||
bufrw.Flush()
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
_, err := st.http1(requestParam{
|
||||
name: "TestH1H1ChunkedEndsPrematurely",
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("st.http1() should fail")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -565,7 +565,7 @@ func TestH2H1BadResponseCL(t *testing.T) {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
|
||||
want := http2.ErrCodeProtocol
|
||||
want := http2.ErrCodeInternal
|
||||
if res.errCode != want {
|
||||
t.Errorf("res.errCode = %v; want %v", res.errCode, want)
|
||||
}
|
||||
@@ -2838,3 +2838,35 @@ func TestH2ResponseBeforeRequestEnd(t *testing.T) {
|
||||
t.Errorf("res.status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1ChunkedEndsPrematurely tests that a stream is reset if the
|
||||
// backend chunked encoded response ends prematurely.
|
||||
func TestH2H1ChunkedEndsPrematurely(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 200\r\nTransfer-Encoding: chunked\r\n\r\n")
|
||||
bufrw.Flush()
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1ChunkedEndsPrematurely",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.errCode, http2.ErrCodeInternal; got != want {
|
||||
t.Errorf("res.errCode = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ set(NGHTTP2_SOURCES
|
||||
nghttp2_http.c
|
||||
nghttp2_rcbuf.c
|
||||
nghttp2_debug.c
|
||||
nghttp2_ksl.c
|
||||
)
|
||||
|
||||
set(NGHTTP2_RES "")
|
||||
|
||||
@@ -27,6 +27,7 @@ EXTRA_DIST = Makefile.msvc CMakeLists.txt version.rc.in
|
||||
AM_CFLAGS = $(WARNCFLAGS) $(EXTRACFLAG)
|
||||
AM_CPPFLAGS = -I$(srcdir)/includes -I$(builddir)/includes -DBUILDING_NGHTTP2 \
|
||||
@DEFS@
|
||||
AM_LDFLAGS = @LIBTOOL_LDFLAGS@
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
pkgconfig_DATA = libnghttp2.pc
|
||||
@@ -49,8 +50,7 @@ OBJECTS = nghttp2_pq.c nghttp2_map.c nghttp2_queue.c \
|
||||
nghttp2_mem.c \
|
||||
nghttp2_http.c \
|
||||
nghttp2_rcbuf.c \
|
||||
nghttp2_debug.c \
|
||||
nghttp2_ksl.c
|
||||
nghttp2_debug.c
|
||||
|
||||
HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \
|
||||
nghttp2_frame.h \
|
||||
@@ -66,9 +66,8 @@ HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \
|
||||
nghttp2_mem.h \
|
||||
nghttp2_http.h \
|
||||
nghttp2_rcbuf.h \
|
||||
nghttp2_debug.h \
|
||||
nghttp2_ksl.h
|
||||
nghttp2_debug.h
|
||||
|
||||
libnghttp2_la_SOURCES = $(HFILES) $(OBJECTS)
|
||||
libnghttp2_la_LDFLAGS = -no-undefined \
|
||||
libnghttp2_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined \
|
||||
-version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -57,7 +57,7 @@
|
||||
|
||||
/* Maximum headers block size to send, calculated using
|
||||
nghttp2_hd_deflate_bound(). This is the default value, and can be
|
||||
overridden by nghttp2_option_set_max_send_header_block_size(). */
|
||||
overridden by nghttp2_option_set_max_send_header_block_length(). */
|
||||
#define NGHTTP2_MAX_HEADERSLEN 65536
|
||||
|
||||
/* The number of bytes for each SETTINGS entry */
|
||||
|
||||
@@ -507,7 +507,166 @@ int nghttp2_check_header_value(const uint8_t *value, size_t len) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Generated by genauthroitychartbl.py */
|
||||
/* Generated by genmethodchartbl.py */
|
||||
static char VALID_METHOD_CHARS[] = {
|
||||
0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */,
|
||||
0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */,
|
||||
0 /* BS */, 0 /* HT */, 0 /* LF */, 0 /* VT */,
|
||||
0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */,
|
||||
0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
|
||||
0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */,
|
||||
0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */,
|
||||
0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */,
|
||||
0 /* SPC */, 1 /* ! */, 0 /* " */, 1 /* # */,
|
||||
1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
|
||||
0 /* ( */, 0 /* ) */, 1 /* * */, 1 /* + */,
|
||||
0 /* , */, 1 /* - */, 1 /* . */, 0 /* / */,
|
||||
1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */,
|
||||
1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */,
|
||||
1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */,
|
||||
0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */,
|
||||
0 /* @ */, 1 /* A */, 1 /* B */, 1 /* C */,
|
||||
1 /* D */, 1 /* E */, 1 /* F */, 1 /* G */,
|
||||
1 /* H */, 1 /* I */, 1 /* J */, 1 /* K */,
|
||||
1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */,
|
||||
1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */,
|
||||
1 /* T */, 1 /* U */, 1 /* V */, 1 /* W */,
|
||||
1 /* X */, 1 /* Y */, 1 /* Z */, 0 /* [ */,
|
||||
0 /* \ */, 0 /* ] */, 1 /* ^ */, 1 /* _ */,
|
||||
1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
|
||||
1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */,
|
||||
1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */,
|
||||
1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */,
|
||||
1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */,
|
||||
1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
|
||||
1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */,
|
||||
1 /* | */, 0 /* } */, 1 /* ~ */, 0 /* DEL */,
|
||||
0 /* 0x80 */, 0 /* 0x81 */, 0 /* 0x82 */, 0 /* 0x83 */,
|
||||
0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, 0 /* 0x87 */,
|
||||
0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
|
||||
0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */,
|
||||
0 /* 0x90 */, 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */,
|
||||
0 /* 0x94 */, 0 /* 0x95 */, 0 /* 0x96 */, 0 /* 0x97 */,
|
||||
0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, 0 /* 0x9b */,
|
||||
0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
|
||||
0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */,
|
||||
0 /* 0xa4 */, 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */,
|
||||
0 /* 0xa8 */, 0 /* 0xa9 */, 0 /* 0xaa */, 0 /* 0xab */,
|
||||
0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, 0 /* 0xaf */,
|
||||
0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
|
||||
0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */,
|
||||
0 /* 0xb8 */, 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */,
|
||||
0 /* 0xbc */, 0 /* 0xbd */, 0 /* 0xbe */, 0 /* 0xbf */,
|
||||
0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, 0 /* 0xc3 */,
|
||||
0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
|
||||
0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */,
|
||||
0 /* 0xcc */, 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */,
|
||||
0 /* 0xd0 */, 0 /* 0xd1 */, 0 /* 0xd2 */, 0 /* 0xd3 */,
|
||||
0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, 0 /* 0xd7 */,
|
||||
0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
|
||||
0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */,
|
||||
0 /* 0xe0 */, 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */,
|
||||
0 /* 0xe4 */, 0 /* 0xe5 */, 0 /* 0xe6 */, 0 /* 0xe7 */,
|
||||
0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, 0 /* 0xeb */,
|
||||
0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
|
||||
0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */,
|
||||
0 /* 0xf4 */, 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */,
|
||||
0 /* 0xf8 */, 0 /* 0xf9 */, 0 /* 0xfa */, 0 /* 0xfb */,
|
||||
0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, 0 /* 0xff */
|
||||
};
|
||||
|
||||
int nghttp2_check_method(const uint8_t *value, size_t len) {
|
||||
const uint8_t *last;
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
for (last = value + len; value != last; ++value) {
|
||||
if (!VALID_METHOD_CHARS[*value]) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Generated by genpathchartbl.py */
|
||||
static char VALID_PATH_CHARS[] = {
|
||||
0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */,
|
||||
0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */,
|
||||
0 /* BS */, 0 /* HT */, 0 /* LF */, 0 /* VT */,
|
||||
0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */,
|
||||
0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
|
||||
0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */,
|
||||
0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */,
|
||||
0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */,
|
||||
0 /* SPC */, 1 /* ! */, 1 /* " */, 1 /* # */,
|
||||
1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
|
||||
1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */,
|
||||
1 /* , */, 1 /* - */, 1 /* . */, 1 /* / */,
|
||||
1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */,
|
||||
1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */,
|
||||
1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */,
|
||||
1 /* < */, 1 /* = */, 1 /* > */, 1 /* ? */,
|
||||
1 /* @ */, 1 /* A */, 1 /* B */, 1 /* C */,
|
||||
1 /* D */, 1 /* E */, 1 /* F */, 1 /* G */,
|
||||
1 /* H */, 1 /* I */, 1 /* J */, 1 /* K */,
|
||||
1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */,
|
||||
1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */,
|
||||
1 /* T */, 1 /* U */, 1 /* V */, 1 /* W */,
|
||||
1 /* X */, 1 /* Y */, 1 /* Z */, 1 /* [ */,
|
||||
1 /* \ */, 1 /* ] */, 1 /* ^ */, 1 /* _ */,
|
||||
1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
|
||||
1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */,
|
||||
1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */,
|
||||
1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */,
|
||||
1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */,
|
||||
1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
|
||||
1 /* x */, 1 /* y */, 1 /* z */, 1 /* { */,
|
||||
1 /* | */, 1 /* } */, 1 /* ~ */, 0 /* DEL */,
|
||||
1 /* 0x80 */, 1 /* 0x81 */, 1 /* 0x82 */, 1 /* 0x83 */,
|
||||
1 /* 0x84 */, 1 /* 0x85 */, 1 /* 0x86 */, 1 /* 0x87 */,
|
||||
1 /* 0x88 */, 1 /* 0x89 */, 1 /* 0x8a */, 1 /* 0x8b */,
|
||||
1 /* 0x8c */, 1 /* 0x8d */, 1 /* 0x8e */, 1 /* 0x8f */,
|
||||
1 /* 0x90 */, 1 /* 0x91 */, 1 /* 0x92 */, 1 /* 0x93 */,
|
||||
1 /* 0x94 */, 1 /* 0x95 */, 1 /* 0x96 */, 1 /* 0x97 */,
|
||||
1 /* 0x98 */, 1 /* 0x99 */, 1 /* 0x9a */, 1 /* 0x9b */,
|
||||
1 /* 0x9c */, 1 /* 0x9d */, 1 /* 0x9e */, 1 /* 0x9f */,
|
||||
1 /* 0xa0 */, 1 /* 0xa1 */, 1 /* 0xa2 */, 1 /* 0xa3 */,
|
||||
1 /* 0xa4 */, 1 /* 0xa5 */, 1 /* 0xa6 */, 1 /* 0xa7 */,
|
||||
1 /* 0xa8 */, 1 /* 0xa9 */, 1 /* 0xaa */, 1 /* 0xab */,
|
||||
1 /* 0xac */, 1 /* 0xad */, 1 /* 0xae */, 1 /* 0xaf */,
|
||||
1 /* 0xb0 */, 1 /* 0xb1 */, 1 /* 0xb2 */, 1 /* 0xb3 */,
|
||||
1 /* 0xb4 */, 1 /* 0xb5 */, 1 /* 0xb6 */, 1 /* 0xb7 */,
|
||||
1 /* 0xb8 */, 1 /* 0xb9 */, 1 /* 0xba */, 1 /* 0xbb */,
|
||||
1 /* 0xbc */, 1 /* 0xbd */, 1 /* 0xbe */, 1 /* 0xbf */,
|
||||
1 /* 0xc0 */, 1 /* 0xc1 */, 1 /* 0xc2 */, 1 /* 0xc3 */,
|
||||
1 /* 0xc4 */, 1 /* 0xc5 */, 1 /* 0xc6 */, 1 /* 0xc7 */,
|
||||
1 /* 0xc8 */, 1 /* 0xc9 */, 1 /* 0xca */, 1 /* 0xcb */,
|
||||
1 /* 0xcc */, 1 /* 0xcd */, 1 /* 0xce */, 1 /* 0xcf */,
|
||||
1 /* 0xd0 */, 1 /* 0xd1 */, 1 /* 0xd2 */, 1 /* 0xd3 */,
|
||||
1 /* 0xd4 */, 1 /* 0xd5 */, 1 /* 0xd6 */, 1 /* 0xd7 */,
|
||||
1 /* 0xd8 */, 1 /* 0xd9 */, 1 /* 0xda */, 1 /* 0xdb */,
|
||||
1 /* 0xdc */, 1 /* 0xdd */, 1 /* 0xde */, 1 /* 0xdf */,
|
||||
1 /* 0xe0 */, 1 /* 0xe1 */, 1 /* 0xe2 */, 1 /* 0xe3 */,
|
||||
1 /* 0xe4 */, 1 /* 0xe5 */, 1 /* 0xe6 */, 1 /* 0xe7 */,
|
||||
1 /* 0xe8 */, 1 /* 0xe9 */, 1 /* 0xea */, 1 /* 0xeb */,
|
||||
1 /* 0xec */, 1 /* 0xed */, 1 /* 0xee */, 1 /* 0xef */,
|
||||
1 /* 0xf0 */, 1 /* 0xf1 */, 1 /* 0xf2 */, 1 /* 0xf3 */,
|
||||
1 /* 0xf4 */, 1 /* 0xf5 */, 1 /* 0xf6 */, 1 /* 0xf7 */,
|
||||
1 /* 0xf8 */, 1 /* 0xf9 */, 1 /* 0xfa */, 1 /* 0xfb */,
|
||||
1 /* 0xfc */, 1 /* 0xfd */, 1 /* 0xfe */, 1 /* 0xff */
|
||||
};
|
||||
|
||||
int nghttp2_check_path(const uint8_t *value, size_t len) {
|
||||
const uint8_t *last;
|
||||
for (last = value + len; value != last; ++value) {
|
||||
if (!VALID_PATH_CHARS[*value]) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Generated by genauthoritychartbl.py */
|
||||
static char VALID_AUTHORITY_CHARS[] = {
|
||||
0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */,
|
||||
0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */,
|
||||
|
||||
@@ -360,12 +360,21 @@ int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
|
||||
return NGHTTP2_ERR_IGN_HTTP_HEADER;
|
||||
}
|
||||
|
||||
if (nv->token == NGHTTP2_TOKEN__AUTHORITY ||
|
||||
nv->token == NGHTTP2_TOKEN_HOST) {
|
||||
switch (nv->token) {
|
||||
case NGHTTP2_TOKEN__METHOD:
|
||||
rv = nghttp2_check_method(nv->value->base, nv->value->len);
|
||||
break;
|
||||
case NGHTTP2_TOKEN__PATH:
|
||||
rv = nghttp2_check_path(nv->value->base, nv->value->len);
|
||||
break;
|
||||
case NGHTTP2_TOKEN__AUTHORITY:
|
||||
case NGHTTP2_TOKEN_HOST:
|
||||
rv = nghttp2_check_authority(nv->value->base, nv->value->len);
|
||||
} else if (nv->token == NGHTTP2_TOKEN__SCHEME) {
|
||||
break;
|
||||
case NGHTTP2_TOKEN__SCHEME:
|
||||
rv = check_scheme(nv->value->base, nv->value->len);
|
||||
} else {
|
||||
break;
|
||||
default:
|
||||
rv = nghttp2_check_header_value(nv->value->base, nv->value->len);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,707 +0,0 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2020 nghttp2 contributors
|
||||
* Copyright (c) 2018 ngtcp2 contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "nghttp2_ksl.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "nghttp2_mem.h"
|
||||
|
||||
static size_t ksl_nodelen(size_t keylen) {
|
||||
return (sizeof(nghttp2_ksl_node) + keylen - sizeof(uint64_t) + 0xf) &
|
||||
(size_t)~0xf;
|
||||
}
|
||||
|
||||
static size_t ksl_blklen(size_t nodelen) {
|
||||
return sizeof(nghttp2_ksl_blk) + nodelen * NGHTTP2_KSL_MAX_NBLK -
|
||||
sizeof(uint64_t);
|
||||
}
|
||||
|
||||
/*
|
||||
* ksl_node_set_key sets |key| to |node|.
|
||||
*/
|
||||
static void ksl_node_set_key(nghttp2_ksl *ksl, nghttp2_ksl_node *node,
|
||||
const void *key) {
|
||||
memcpy(node->key, key, ksl->keylen);
|
||||
}
|
||||
|
||||
int nghttp2_ksl_init(nghttp2_ksl *ksl, nghttp2_ksl_compar compar, size_t keylen,
|
||||
nghttp2_mem *mem) {
|
||||
size_t nodelen = ksl_nodelen(keylen);
|
||||
size_t blklen = ksl_blklen(nodelen);
|
||||
nghttp2_ksl_blk *head;
|
||||
|
||||
ksl->head = nghttp2_mem_malloc(mem, blklen);
|
||||
if (!ksl->head) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
ksl->front = ksl->back = ksl->head;
|
||||
ksl->compar = compar;
|
||||
ksl->keylen = keylen;
|
||||
ksl->nodelen = nodelen;
|
||||
ksl->n = 0;
|
||||
ksl->mem = mem;
|
||||
|
||||
head = ksl->head;
|
||||
head->next = head->prev = NULL;
|
||||
head->n = 0;
|
||||
head->leaf = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ksl_free_blk frees |blk| recursively.
|
||||
*/
|
||||
static void ksl_free_blk(nghttp2_ksl *ksl, nghttp2_ksl_blk *blk) {
|
||||
size_t i;
|
||||
|
||||
if (!blk->leaf) {
|
||||
for (i = 0; i < blk->n; ++i) {
|
||||
ksl_free_blk(ksl, nghttp2_ksl_nth_node(ksl, blk, i)->blk);
|
||||
}
|
||||
}
|
||||
|
||||
nghttp2_mem_free(ksl->mem, blk);
|
||||
}
|
||||
|
||||
void nghttp2_ksl_free(nghttp2_ksl *ksl) {
|
||||
if (!ksl) {
|
||||
return;
|
||||
}
|
||||
|
||||
ksl_free_blk(ksl, ksl->head);
|
||||
}
|
||||
|
||||
/*
|
||||
* ksl_split_blk splits |blk| into 2 nghttp2_ksl_blk objects. The new
|
||||
* nghttp2_ksl_blk is always the "right" block.
|
||||
*
|
||||
* It returns the pointer to the nghttp2_ksl_blk created which is the
|
||||
* located at the right of |blk|, or NULL which indicates out of
|
||||
* memory error.
|
||||
*/
|
||||
static nghttp2_ksl_blk *ksl_split_blk(nghttp2_ksl *ksl, nghttp2_ksl_blk *blk) {
|
||||
nghttp2_ksl_blk *rblk;
|
||||
|
||||
rblk = nghttp2_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen));
|
||||
if (rblk == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rblk->next = blk->next;
|
||||
blk->next = rblk;
|
||||
if (rblk->next) {
|
||||
rblk->next->prev = rblk;
|
||||
} else if (ksl->back == blk) {
|
||||
ksl->back = rblk;
|
||||
}
|
||||
rblk->prev = blk;
|
||||
rblk->leaf = blk->leaf;
|
||||
|
||||
rblk->n = blk->n / 2;
|
||||
|
||||
memcpy(rblk->nodes, blk->nodes + ksl->nodelen * (blk->n - rblk->n),
|
||||
ksl->nodelen * rblk->n);
|
||||
|
||||
blk->n -= rblk->n;
|
||||
|
||||
assert(blk->n >= NGHTTP2_KSL_MIN_NBLK);
|
||||
assert(rblk->n >= NGHTTP2_KSL_MIN_NBLK);
|
||||
|
||||
return rblk;
|
||||
}
|
||||
|
||||
/*
|
||||
* ksl_split_node splits a node included in |blk| at the position |i|
|
||||
* into 2 adjacent nodes. The new node is always inserted at the
|
||||
* position |i+1|.
|
||||
*
|
||||
* It returns 0 if it succeeds, or one of the following negative error
|
||||
* codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
static int ksl_split_node(nghttp2_ksl *ksl, nghttp2_ksl_blk *blk, size_t i) {
|
||||
nghttp2_ksl_node *node;
|
||||
nghttp2_ksl_blk *lblk = nghttp2_ksl_nth_node(ksl, blk, i)->blk, *rblk;
|
||||
|
||||
rblk = ksl_split_blk(ksl, lblk);
|
||||
if (rblk == NULL) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
memmove(blk->nodes + (i + 2) * ksl->nodelen,
|
||||
blk->nodes + (i + 1) * ksl->nodelen,
|
||||
ksl->nodelen * (blk->n - (i + 1)));
|
||||
|
||||
node = nghttp2_ksl_nth_node(ksl, blk, i + 1);
|
||||
node->blk = rblk;
|
||||
++blk->n;
|
||||
ksl_node_set_key(ksl, node,
|
||||
nghttp2_ksl_nth_node(ksl, rblk, rblk->n - 1)->key);
|
||||
|
||||
node = nghttp2_ksl_nth_node(ksl, blk, i);
|
||||
ksl_node_set_key(ksl, node,
|
||||
nghttp2_ksl_nth_node(ksl, lblk, lblk->n - 1)->key);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ksl_split_head splits a head (root) block. It increases the height
|
||||
* of skip list by 1.
|
||||
*
|
||||
* It returns 0 if it succeeds, or one of the following negative error
|
||||
* codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
static int ksl_split_head(nghttp2_ksl *ksl) {
|
||||
nghttp2_ksl_blk *rblk = NULL, *lblk, *nhead = NULL;
|
||||
nghttp2_ksl_node *node;
|
||||
|
||||
rblk = ksl_split_blk(ksl, ksl->head);
|
||||
if (rblk == NULL) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
lblk = ksl->head;
|
||||
|
||||
nhead = nghttp2_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen));
|
||||
if (nhead == NULL) {
|
||||
nghttp2_mem_free(ksl->mem, rblk);
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
nhead->next = nhead->prev = NULL;
|
||||
nhead->n = 2;
|
||||
nhead->leaf = 0;
|
||||
|
||||
node = nghttp2_ksl_nth_node(ksl, nhead, 0);
|
||||
ksl_node_set_key(ksl, node,
|
||||
nghttp2_ksl_nth_node(ksl, lblk, lblk->n - 1)->key);
|
||||
node->blk = lblk;
|
||||
|
||||
node = nghttp2_ksl_nth_node(ksl, nhead, 1);
|
||||
ksl_node_set_key(ksl, node,
|
||||
nghttp2_ksl_nth_node(ksl, rblk, rblk->n - 1)->key);
|
||||
node->blk = rblk;
|
||||
|
||||
ksl->head = nhead;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* insert_node inserts a node whose key is |key| with the associated
|
||||
* |data| at the index of |i|. This function assumes that the number
|
||||
* of nodes contained by |blk| is strictly less than
|
||||
* NGHTTP2_KSL_MAX_NBLK.
|
||||
*/
|
||||
static void ksl_insert_node(nghttp2_ksl *ksl, nghttp2_ksl_blk *blk, size_t i,
|
||||
const nghttp2_ksl_key *key, void *data) {
|
||||
nghttp2_ksl_node *node;
|
||||
|
||||
assert(blk->n < NGHTTP2_KSL_MAX_NBLK);
|
||||
|
||||
memmove(blk->nodes + (i + 1) * ksl->nodelen, blk->nodes + i * ksl->nodelen,
|
||||
ksl->nodelen * (blk->n - i));
|
||||
|
||||
node = nghttp2_ksl_nth_node(ksl, blk, i);
|
||||
ksl_node_set_key(ksl, node, key);
|
||||
node->data = data;
|
||||
|
||||
++blk->n;
|
||||
}
|
||||
|
||||
static size_t ksl_bsearch(nghttp2_ksl *ksl, nghttp2_ksl_blk *blk,
|
||||
const nghttp2_ksl_key *key,
|
||||
nghttp2_ksl_compar compar) {
|
||||
ssize_t left = -1, right = (ssize_t)blk->n, mid;
|
||||
nghttp2_ksl_node *node;
|
||||
|
||||
while (right - left > 1) {
|
||||
mid = (left + right) / 2;
|
||||
node = nghttp2_ksl_nth_node(ksl, blk, (size_t)mid);
|
||||
if (compar((nghttp2_ksl_key *)node->key, key)) {
|
||||
left = mid;
|
||||
} else {
|
||||
right = mid;
|
||||
}
|
||||
}
|
||||
|
||||
return (size_t)right;
|
||||
}
|
||||
|
||||
int nghttp2_ksl_insert(nghttp2_ksl *ksl, nghttp2_ksl_it *it,
|
||||
const nghttp2_ksl_key *key, void *data) {
|
||||
nghttp2_ksl_blk *blk = ksl->head;
|
||||
nghttp2_ksl_node *node;
|
||||
size_t i;
|
||||
int rv;
|
||||
|
||||
if (blk->n == NGHTTP2_KSL_MAX_NBLK) {
|
||||
rv = ksl_split_head(ksl);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
blk = ksl->head;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
i = ksl_bsearch(ksl, blk, key, ksl->compar);
|
||||
|
||||
if (blk->leaf) {
|
||||
if (i < blk->n &&
|
||||
!ksl->compar(key, nghttp2_ksl_nth_node(ksl, blk, i)->key)) {
|
||||
if (it) {
|
||||
*it = nghttp2_ksl_end(ksl);
|
||||
}
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
ksl_insert_node(ksl, blk, i, key, data);
|
||||
++ksl->n;
|
||||
if (it) {
|
||||
nghttp2_ksl_it_init(it, ksl, blk, i);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (i == blk->n) {
|
||||
/* This insertion extends the largest key in this subtree. */
|
||||
for (; !blk->leaf;) {
|
||||
node = nghttp2_ksl_nth_node(ksl, blk, blk->n - 1);
|
||||
if (node->blk->n == NGHTTP2_KSL_MAX_NBLK) {
|
||||
rv = ksl_split_node(ksl, blk, blk->n - 1);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
node = nghttp2_ksl_nth_node(ksl, blk, blk->n - 1);
|
||||
}
|
||||
ksl_node_set_key(ksl, node, key);
|
||||
blk = node->blk;
|
||||
}
|
||||
ksl_insert_node(ksl, blk, blk->n, key, data);
|
||||
++ksl->n;
|
||||
if (it) {
|
||||
nghttp2_ksl_it_init(it, ksl, blk, blk->n - 1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
node = nghttp2_ksl_nth_node(ksl, blk, i);
|
||||
|
||||
if (node->blk->n == NGHTTP2_KSL_MAX_NBLK) {
|
||||
rv = ksl_split_node(ksl, blk, i);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
if (ksl->compar((nghttp2_ksl_key *)node->key, key)) {
|
||||
node = nghttp2_ksl_nth_node(ksl, blk, i + 1);
|
||||
if (ksl->compar((nghttp2_ksl_key *)node->key, key)) {
|
||||
ksl_node_set_key(ksl, node, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
blk = node->blk;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ksl_remove_node removes the node included in |blk| at the index of
|
||||
* |i|.
|
||||
*/
|
||||
static void ksl_remove_node(nghttp2_ksl *ksl, nghttp2_ksl_blk *blk, size_t i) {
|
||||
memmove(blk->nodes + i * ksl->nodelen, blk->nodes + (i + 1) * ksl->nodelen,
|
||||
ksl->nodelen * (blk->n - (i + 1)));
|
||||
|
||||
--blk->n;
|
||||
}
|
||||
|
||||
/*
|
||||
* ksl_merge_node merges 2 nodes which are the nodes at the index of
|
||||
* |i| and |i + 1|.
|
||||
*
|
||||
* If |blk| is the direct descendant of head (root) block and the head
|
||||
* block contains just 2 nodes, the merged block becomes head block,
|
||||
* which decreases the height of |ksl| by 1.
|
||||
*
|
||||
* This function returns the pointer to the merged block.
|
||||
*/
|
||||
static nghttp2_ksl_blk *ksl_merge_node(nghttp2_ksl *ksl, nghttp2_ksl_blk *blk,
|
||||
size_t i) {
|
||||
nghttp2_ksl_blk *lblk, *rblk;
|
||||
|
||||
assert(i + 1 < blk->n);
|
||||
|
||||
lblk = nghttp2_ksl_nth_node(ksl, blk, i)->blk;
|
||||
rblk = nghttp2_ksl_nth_node(ksl, blk, i + 1)->blk;
|
||||
|
||||
assert(lblk->n + rblk->n < NGHTTP2_KSL_MAX_NBLK);
|
||||
|
||||
memcpy(lblk->nodes + ksl->nodelen * lblk->n, rblk->nodes,
|
||||
ksl->nodelen * rblk->n);
|
||||
|
||||
lblk->n += rblk->n;
|
||||
lblk->next = rblk->next;
|
||||
if (lblk->next) {
|
||||
lblk->next->prev = lblk;
|
||||
} else if (ksl->back == rblk) {
|
||||
ksl->back = lblk;
|
||||
}
|
||||
|
||||
nghttp2_mem_free(ksl->mem, rblk);
|
||||
|
||||
if (ksl->head == blk && blk->n == 2) {
|
||||
nghttp2_mem_free(ksl->mem, ksl->head);
|
||||
ksl->head = lblk;
|
||||
} else {
|
||||
ksl_remove_node(ksl, blk, i + 1);
|
||||
ksl_node_set_key(ksl, nghttp2_ksl_nth_node(ksl, blk, i),
|
||||
nghttp2_ksl_nth_node(ksl, lblk, lblk->n - 1)->key);
|
||||
}
|
||||
|
||||
return lblk;
|
||||
}
|
||||
|
||||
/*
|
||||
* ksl_shift_left moves the first node in blk->nodes[i]->blk->nodes to
|
||||
* blk->nodes[i - 1]->blk->nodes.
|
||||
*/
|
||||
static void ksl_shift_left(nghttp2_ksl *ksl, nghttp2_ksl_blk *blk, size_t i) {
|
||||
nghttp2_ksl_node *lnode, *rnode, *dest, *src;
|
||||
|
||||
assert(i > 0);
|
||||
|
||||
lnode = nghttp2_ksl_nth_node(ksl, blk, i - 1);
|
||||
rnode = nghttp2_ksl_nth_node(ksl, blk, i);
|
||||
|
||||
assert(lnode->blk->n < NGHTTP2_KSL_MAX_NBLK);
|
||||
assert(rnode->blk->n > NGHTTP2_KSL_MIN_NBLK);
|
||||
|
||||
dest = nghttp2_ksl_nth_node(ksl, lnode->blk, lnode->blk->n);
|
||||
src = nghttp2_ksl_nth_node(ksl, rnode->blk, 0);
|
||||
|
||||
memcpy(dest, src, ksl->nodelen);
|
||||
ksl_node_set_key(ksl, lnode, dest->key);
|
||||
++lnode->blk->n;
|
||||
|
||||
--rnode->blk->n;
|
||||
memmove(rnode->blk->nodes, rnode->blk->nodes + ksl->nodelen,
|
||||
ksl->nodelen * rnode->blk->n);
|
||||
}
|
||||
|
||||
/*
|
||||
* ksl_shift_right moves the last node in blk->nodes[i]->blk->nodes to
|
||||
* blk->nodes[i + 1]->blk->nodes.
|
||||
*/
|
||||
static void ksl_shift_right(nghttp2_ksl *ksl, nghttp2_ksl_blk *blk, size_t i) {
|
||||
nghttp2_ksl_node *lnode, *rnode, *dest, *src;
|
||||
|
||||
assert(i < blk->n - 1);
|
||||
|
||||
lnode = nghttp2_ksl_nth_node(ksl, blk, i);
|
||||
rnode = nghttp2_ksl_nth_node(ksl, blk, i + 1);
|
||||
|
||||
assert(lnode->blk->n > NGHTTP2_KSL_MIN_NBLK);
|
||||
assert(rnode->blk->n < NGHTTP2_KSL_MAX_NBLK);
|
||||
|
||||
memmove(rnode->blk->nodes + ksl->nodelen, rnode->blk->nodes,
|
||||
ksl->nodelen * rnode->blk->n);
|
||||
++rnode->blk->n;
|
||||
|
||||
dest = nghttp2_ksl_nth_node(ksl, rnode->blk, 0);
|
||||
src = nghttp2_ksl_nth_node(ksl, lnode->blk, lnode->blk->n - 1);
|
||||
|
||||
memcpy(dest, src, ksl->nodelen);
|
||||
|
||||
--lnode->blk->n;
|
||||
ksl_node_set_key(
|
||||
ksl, lnode,
|
||||
nghttp2_ksl_nth_node(ksl, lnode->blk, lnode->blk->n - 1)->key);
|
||||
}
|
||||
|
||||
/*
|
||||
* key_equal returns nonzero if |lhs| and |rhs| are equal using the
|
||||
* function |compar|.
|
||||
*/
|
||||
static int key_equal(nghttp2_ksl_compar compar, const nghttp2_ksl_key *lhs,
|
||||
const nghttp2_ksl_key *rhs) {
|
||||
return !compar(lhs, rhs) && !compar(rhs, lhs);
|
||||
}
|
||||
|
||||
int nghttp2_ksl_remove(nghttp2_ksl *ksl, nghttp2_ksl_it *it,
|
||||
const nghttp2_ksl_key *key) {
|
||||
nghttp2_ksl_blk *blk = ksl->head;
|
||||
nghttp2_ksl_node *node;
|
||||
size_t i;
|
||||
|
||||
if (!blk->leaf && blk->n == 2 &&
|
||||
nghttp2_ksl_nth_node(ksl, blk, 0)->blk->n == NGHTTP2_KSL_MIN_NBLK &&
|
||||
nghttp2_ksl_nth_node(ksl, blk, 1)->blk->n == NGHTTP2_KSL_MIN_NBLK) {
|
||||
blk = ksl_merge_node(ksl, ksl->head, 0);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
i = ksl_bsearch(ksl, blk, key, ksl->compar);
|
||||
|
||||
if (i == blk->n) {
|
||||
if (it) {
|
||||
*it = nghttp2_ksl_end(ksl);
|
||||
}
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (blk->leaf) {
|
||||
if (ksl->compar(key, nghttp2_ksl_nth_node(ksl, blk, i)->key)) {
|
||||
if (it) {
|
||||
*it = nghttp2_ksl_end(ksl);
|
||||
}
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
ksl_remove_node(ksl, blk, i);
|
||||
--ksl->n;
|
||||
if (it) {
|
||||
if (blk->n == i && blk->next) {
|
||||
nghttp2_ksl_it_init(it, ksl, blk->next, 0);
|
||||
} else {
|
||||
nghttp2_ksl_it_init(it, ksl, blk, i);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
node = nghttp2_ksl_nth_node(ksl, blk, i);
|
||||
|
||||
if (node->blk->n == NGHTTP2_KSL_MIN_NBLK) {
|
||||
if (i > 0 && nghttp2_ksl_nth_node(ksl, blk, i - 1)->blk->n >
|
||||
NGHTTP2_KSL_MIN_NBLK) {
|
||||
ksl_shift_right(ksl, blk, i - 1);
|
||||
blk = node->blk;
|
||||
} else if (i + 1 < blk->n &&
|
||||
nghttp2_ksl_nth_node(ksl, blk, i + 1)->blk->n >
|
||||
NGHTTP2_KSL_MIN_NBLK) {
|
||||
ksl_shift_left(ksl, blk, i + 1);
|
||||
blk = node->blk;
|
||||
} else if (i > 0) {
|
||||
blk = ksl_merge_node(ksl, blk, i - 1);
|
||||
} else {
|
||||
assert(i + 1 < blk->n);
|
||||
blk = ksl_merge_node(ksl, blk, i);
|
||||
}
|
||||
} else {
|
||||
blk = node->blk;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nghttp2_ksl_it nghttp2_ksl_lower_bound(nghttp2_ksl *ksl,
|
||||
const nghttp2_ksl_key *key) {
|
||||
nghttp2_ksl_blk *blk = ksl->head;
|
||||
nghttp2_ksl_it it;
|
||||
size_t i;
|
||||
|
||||
for (;;) {
|
||||
i = ksl_bsearch(ksl, blk, key, ksl->compar);
|
||||
|
||||
if (blk->leaf) {
|
||||
if (i == blk->n && blk->next) {
|
||||
blk = blk->next;
|
||||
i = 0;
|
||||
}
|
||||
nghttp2_ksl_it_init(&it, ksl, blk, i);
|
||||
return it;
|
||||
}
|
||||
|
||||
if (i == blk->n) {
|
||||
/* This happens if descendant has smaller key. Fast forward to
|
||||
find last node in this subtree. */
|
||||
for (; !blk->leaf; blk = nghttp2_ksl_nth_node(ksl, blk, blk->n - 1)->blk)
|
||||
;
|
||||
if (blk->next) {
|
||||
blk = blk->next;
|
||||
i = 0;
|
||||
} else {
|
||||
i = blk->n;
|
||||
}
|
||||
nghttp2_ksl_it_init(&it, ksl, blk, i);
|
||||
return it;
|
||||
}
|
||||
blk = nghttp2_ksl_nth_node(ksl, blk, i)->blk;
|
||||
}
|
||||
}
|
||||
|
||||
nghttp2_ksl_it nghttp2_ksl_lower_bound_compar(nghttp2_ksl *ksl,
|
||||
const nghttp2_ksl_key *key,
|
||||
nghttp2_ksl_compar compar) {
|
||||
nghttp2_ksl_blk *blk = ksl->head;
|
||||
nghttp2_ksl_it it;
|
||||
size_t i;
|
||||
|
||||
for (;;) {
|
||||
i = ksl_bsearch(ksl, blk, key, compar);
|
||||
|
||||
if (blk->leaf) {
|
||||
if (i == blk->n && blk->next) {
|
||||
blk = blk->next;
|
||||
i = 0;
|
||||
}
|
||||
nghttp2_ksl_it_init(&it, ksl, blk, i);
|
||||
return it;
|
||||
}
|
||||
|
||||
if (i == blk->n) {
|
||||
/* This happens if descendant has smaller key. Fast forward to
|
||||
find last node in this subtree. */
|
||||
for (; !blk->leaf; blk = nghttp2_ksl_nth_node(ksl, blk, blk->n - 1)->blk)
|
||||
;
|
||||
if (blk->next) {
|
||||
blk = blk->next;
|
||||
i = 0;
|
||||
} else {
|
||||
i = blk->n;
|
||||
}
|
||||
nghttp2_ksl_it_init(&it, ksl, blk, i);
|
||||
return it;
|
||||
}
|
||||
blk = nghttp2_ksl_nth_node(ksl, blk, i)->blk;
|
||||
}
|
||||
}
|
||||
|
||||
void nghttp2_ksl_update_key(nghttp2_ksl *ksl, const nghttp2_ksl_key *old_key,
|
||||
const nghttp2_ksl_key *new_key) {
|
||||
nghttp2_ksl_blk *blk = ksl->head;
|
||||
nghttp2_ksl_node *node;
|
||||
size_t i;
|
||||
|
||||
for (;;) {
|
||||
i = ksl_bsearch(ksl, blk, old_key, ksl->compar);
|
||||
|
||||
assert(i < blk->n);
|
||||
node = nghttp2_ksl_nth_node(ksl, blk, i);
|
||||
|
||||
if (blk->leaf) {
|
||||
assert(key_equal(ksl->compar, (nghttp2_ksl_key *)node->key, old_key));
|
||||
ksl_node_set_key(ksl, node, new_key);
|
||||
return;
|
||||
}
|
||||
|
||||
if (key_equal(ksl->compar, (nghttp2_ksl_key *)node->key, old_key) ||
|
||||
ksl->compar((nghttp2_ksl_key *)node->key, new_key)) {
|
||||
ksl_node_set_key(ksl, node, new_key);
|
||||
}
|
||||
|
||||
blk = node->blk;
|
||||
}
|
||||
}
|
||||
|
||||
static void ksl_print(nghttp2_ksl *ksl, nghttp2_ksl_blk *blk, size_t level) {
|
||||
size_t i;
|
||||
nghttp2_ksl_node *node;
|
||||
|
||||
fprintf(stderr, "LV=%zu n=%zu\n", level, blk->n);
|
||||
|
||||
if (blk->leaf) {
|
||||
for (i = 0; i < blk->n; ++i) {
|
||||
node = nghttp2_ksl_nth_node(ksl, blk, i);
|
||||
fprintf(stderr, " %" PRId64, *(int64_t *)(void *)node->key);
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < blk->n; ++i) {
|
||||
ksl_print(ksl, nghttp2_ksl_nth_node(ksl, blk, i)->blk, level + 1);
|
||||
}
|
||||
}
|
||||
|
||||
size_t nghttp2_ksl_len(nghttp2_ksl *ksl) { return ksl->n; }
|
||||
|
||||
void nghttp2_ksl_clear(nghttp2_ksl *ksl) {
|
||||
size_t i;
|
||||
nghttp2_ksl_blk *head;
|
||||
|
||||
if (!ksl->head->leaf) {
|
||||
for (i = 0; i < ksl->head->n; ++i) {
|
||||
ksl_free_blk(ksl, nghttp2_ksl_nth_node(ksl, ksl->head, i)->blk);
|
||||
}
|
||||
}
|
||||
|
||||
ksl->front = ksl->back = ksl->head;
|
||||
ksl->n = 0;
|
||||
|
||||
head = ksl->head;
|
||||
|
||||
head->next = head->prev = NULL;
|
||||
head->n = 0;
|
||||
head->leaf = 1;
|
||||
}
|
||||
|
||||
void nghttp2_ksl_print(nghttp2_ksl *ksl) { ksl_print(ksl, ksl->head, 0); }
|
||||
|
||||
nghttp2_ksl_it nghttp2_ksl_begin(const nghttp2_ksl *ksl) {
|
||||
nghttp2_ksl_it it;
|
||||
nghttp2_ksl_it_init(&it, ksl, ksl->front, 0);
|
||||
return it;
|
||||
}
|
||||
|
||||
nghttp2_ksl_it nghttp2_ksl_end(const nghttp2_ksl *ksl) {
|
||||
nghttp2_ksl_it it;
|
||||
nghttp2_ksl_it_init(&it, ksl, ksl->back, ksl->back->n);
|
||||
return it;
|
||||
}
|
||||
|
||||
void nghttp2_ksl_it_init(nghttp2_ksl_it *it, const nghttp2_ksl *ksl,
|
||||
nghttp2_ksl_blk *blk, size_t i) {
|
||||
it->ksl = ksl;
|
||||
it->blk = blk;
|
||||
it->i = i;
|
||||
}
|
||||
|
||||
void *nghttp2_ksl_it_get(const nghttp2_ksl_it *it) {
|
||||
assert(it->i < it->blk->n);
|
||||
return nghttp2_ksl_nth_node(it->ksl, it->blk, it->i)->data;
|
||||
}
|
||||
|
||||
void nghttp2_ksl_it_prev(nghttp2_ksl_it *it) {
|
||||
assert(!nghttp2_ksl_it_begin(it));
|
||||
|
||||
if (it->i == 0) {
|
||||
it->blk = it->blk->prev;
|
||||
it->i = it->blk->n - 1;
|
||||
} else {
|
||||
--it->i;
|
||||
}
|
||||
}
|
||||
|
||||
int nghttp2_ksl_it_begin(const nghttp2_ksl_it *it) {
|
||||
return it->i == 0 && it->blk->prev == NULL;
|
||||
}
|
||||
@@ -1,315 +0,0 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2020 nghttp2 contributors
|
||||
* Copyright (c) 2018 ngtcp2 contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef NGHTTP2_KSL_H
|
||||
#define NGHTTP2_KSL_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
/*
|
||||
* Skip List using single key instead of range.
|
||||
*/
|
||||
|
||||
#define NGHTTP2_KSL_DEGR 16
|
||||
/* NGHTTP2_KSL_MAX_NBLK is the maximum number of nodes which a single
|
||||
block can contain. */
|
||||
#define NGHTTP2_KSL_MAX_NBLK (2 * NGHTTP2_KSL_DEGR - 1)
|
||||
/* NGHTTP2_KSL_MIN_NBLK is the minimum number of nodes which a single
|
||||
block other than root must contains. */
|
||||
#define NGHTTP2_KSL_MIN_NBLK (NGHTTP2_KSL_DEGR - 1)
|
||||
|
||||
/*
|
||||
* nghttp2_ksl_key represents key in nghttp2_ksl.
|
||||
*/
|
||||
typedef void nghttp2_ksl_key;
|
||||
|
||||
struct nghttp2_ksl_node;
|
||||
typedef struct nghttp2_ksl_node nghttp2_ksl_node;
|
||||
|
||||
struct nghttp2_ksl_blk;
|
||||
typedef struct nghttp2_ksl_blk nghttp2_ksl_blk;
|
||||
|
||||
/*
|
||||
* nghttp2_ksl_node is a node which contains either nghttp2_ksl_blk or
|
||||
* opaque data. If a node is an internal node, it contains
|
||||
* nghttp2_ksl_blk. Otherwise, it has data. The key is stored at the
|
||||
* location starting at key.
|
||||
*/
|
||||
struct nghttp2_ksl_node {
|
||||
union {
|
||||
nghttp2_ksl_blk *blk;
|
||||
void *data;
|
||||
};
|
||||
union {
|
||||
uint64_t align;
|
||||
/* key is a buffer to include key associated to this node.
|
||||
Because the length of key is unknown until nghttp2_ksl_init is
|
||||
called, the actual buffer will be allocated after this
|
||||
field. */
|
||||
uint8_t key[1];
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
* nghttp2_ksl_blk contains nghttp2_ksl_node objects.
|
||||
*/
|
||||
struct nghttp2_ksl_blk {
|
||||
/* next points to the next block if leaf field is nonzero. */
|
||||
nghttp2_ksl_blk *next;
|
||||
/* prev points to the previous block if leaf field is nonzero. */
|
||||
nghttp2_ksl_blk *prev;
|
||||
/* n is the number of nodes this object contains in nodes. */
|
||||
size_t n;
|
||||
/* leaf is nonzero if this block contains leaf nodes. */
|
||||
int leaf;
|
||||
union {
|
||||
uint64_t align;
|
||||
/* nodes is a buffer to contain NGHTTP2_KSL_MAX_NBLK
|
||||
nghttp2_ksl_node objects. Because nghttp2_ksl_node object is
|
||||
allocated along with the additional variable length key
|
||||
storage, the size of buffer is unknown until nghttp2_ksl_init is
|
||||
called. */
|
||||
uint8_t nodes[1];
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
* nghttp2_ksl_compar is a function type which returns nonzero if key
|
||||
* |lhs| should be placed before |rhs|. It returns 0 otherwise.
|
||||
*/
|
||||
typedef int (*nghttp2_ksl_compar)(const nghttp2_ksl_key *lhs,
|
||||
const nghttp2_ksl_key *rhs);
|
||||
|
||||
struct nghttp2_ksl;
|
||||
typedef struct nghttp2_ksl nghttp2_ksl;
|
||||
|
||||
struct nghttp2_ksl_it;
|
||||
typedef struct nghttp2_ksl_it nghttp2_ksl_it;
|
||||
|
||||
/*
|
||||
* nghttp2_ksl_it is a forward iterator to iterate nodes.
|
||||
*/
|
||||
struct nghttp2_ksl_it {
|
||||
const nghttp2_ksl *ksl;
|
||||
nghttp2_ksl_blk *blk;
|
||||
size_t i;
|
||||
};
|
||||
|
||||
/*
|
||||
* nghttp2_ksl is a deterministic paged skip list.
|
||||
*/
|
||||
struct nghttp2_ksl {
|
||||
/* head points to the root block. */
|
||||
nghttp2_ksl_blk *head;
|
||||
/* front points to the first leaf block. */
|
||||
nghttp2_ksl_blk *front;
|
||||
/* back points to the last leaf block. */
|
||||
nghttp2_ksl_blk *back;
|
||||
nghttp2_ksl_compar compar;
|
||||
size_t n;
|
||||
/* keylen is the size of key */
|
||||
size_t keylen;
|
||||
/* nodelen is the actual size of nghttp2_ksl_node including key
|
||||
storage. */
|
||||
size_t nodelen;
|
||||
nghttp2_mem *mem;
|
||||
};
|
||||
|
||||
/*
|
||||
* nghttp2_ksl_init initializes |ksl|. |compar| specifies compare
|
||||
* function. |keylen| is the length of key.
|
||||
*
|
||||
* It returns 0 if it succeeds, or one of the following negative error
|
||||
* codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
int nghttp2_ksl_init(nghttp2_ksl *ksl, nghttp2_ksl_compar compar, size_t keylen,
|
||||
nghttp2_mem *mem);
|
||||
|
||||
/*
|
||||
* nghttp2_ksl_free frees resources allocated for |ksl|. If |ksl| is
|
||||
* NULL, this function does nothing. It does not free the memory
|
||||
* region pointed by |ksl| itself.
|
||||
*/
|
||||
void nghttp2_ksl_free(nghttp2_ksl *ksl);
|
||||
|
||||
/*
|
||||
* nghttp2_ksl_insert inserts |key| with its associated |data|. On
|
||||
* successful insertion, the iterator points to the inserted node is
|
||||
* stored in |*it|.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
* NGHTTP2_ERR_INVALID_ARGUMENT
|
||||
* |key| already exists.
|
||||
*/
|
||||
int nghttp2_ksl_insert(nghttp2_ksl *ksl, nghttp2_ksl_it *it,
|
||||
const nghttp2_ksl_key *key, void *data);
|
||||
|
||||
/*
|
||||
* nghttp2_ksl_remove removes the |key| from |ksl|.
|
||||
*
|
||||
* This function assigns the iterator to |*it|, which points to the
|
||||
* node which is located at the right next of the removed node if |it|
|
||||
* is not NULL. If |key| is not found, no deletion takes place and
|
||||
* the return value of nghttp2_ksl_end(ksl) is assigned to |*it|.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_INVALID_ARGUMENT
|
||||
* |key| does not exist.
|
||||
*/
|
||||
int nghttp2_ksl_remove(nghttp2_ksl *ksl, nghttp2_ksl_it *it,
|
||||
const nghttp2_ksl_key *key);
|
||||
|
||||
/*
|
||||
* nghttp2_ksl_lower_bound returns the iterator which points to the
|
||||
* first node which has the key which is equal to |key| or the last
|
||||
* node which satisfies !compar(&node->key, key). If there is no such
|
||||
* node, it returns the iterator which satisfies nghttp2_ksl_it_end(it)
|
||||
* != 0.
|
||||
*/
|
||||
nghttp2_ksl_it nghttp2_ksl_lower_bound(nghttp2_ksl *ksl,
|
||||
const nghttp2_ksl_key *key);
|
||||
|
||||
/*
|
||||
* nghttp2_ksl_lower_bound_compar works like nghttp2_ksl_lower_bound,
|
||||
* but it takes custom function |compar| to do lower bound search.
|
||||
*/
|
||||
nghttp2_ksl_it nghttp2_ksl_lower_bound_compar(nghttp2_ksl *ksl,
|
||||
const nghttp2_ksl_key *key,
|
||||
nghttp2_ksl_compar compar);
|
||||
|
||||
/*
|
||||
* nghttp2_ksl_update_key replaces the key of nodes which has |old_key|
|
||||
* with |new_key|. |new_key| must be strictly greater than the
|
||||
* previous node and strictly smaller than the next node.
|
||||
*/
|
||||
void nghttp2_ksl_update_key(nghttp2_ksl *ksl, const nghttp2_ksl_key *old_key,
|
||||
const nghttp2_ksl_key *new_key);
|
||||
|
||||
/*
|
||||
* nghttp2_ksl_begin returns the iterator which points to the first
|
||||
* node. If there is no node in |ksl|, it returns the iterator which
|
||||
* satisfies nghttp2_ksl_it_end(it) != 0.
|
||||
*/
|
||||
nghttp2_ksl_it nghttp2_ksl_begin(const nghttp2_ksl *ksl);
|
||||
|
||||
/*
|
||||
* nghttp2_ksl_end returns the iterator which points to the node
|
||||
* following the last node. The returned object satisfies
|
||||
* nghttp2_ksl_it_end(). If there is no node in |ksl|, it returns the
|
||||
* iterator which satisfies nghttp2_ksl_it_begin(it) != 0.
|
||||
*/
|
||||
nghttp2_ksl_it nghttp2_ksl_end(const nghttp2_ksl *ksl);
|
||||
|
||||
/*
|
||||
* nghttp2_ksl_len returns the number of elements stored in |ksl|.
|
||||
*/
|
||||
size_t nghttp2_ksl_len(nghttp2_ksl *ksl);
|
||||
|
||||
/*
|
||||
* nghttp2_ksl_clear removes all elements stored in |ksl|.
|
||||
*/
|
||||
void nghttp2_ksl_clear(nghttp2_ksl *ksl);
|
||||
|
||||
/*
|
||||
* nghttp2_ksl_nth_node returns the |n|th node under |blk|.
|
||||
*/
|
||||
#define nghttp2_ksl_nth_node(KSL, BLK, N) \
|
||||
((nghttp2_ksl_node *)(void *)((BLK)->nodes + (KSL)->nodelen * (N)))
|
||||
|
||||
/*
|
||||
* nghttp2_ksl_print prints its internal state in stderr. It assumes
|
||||
* that the key is of type int64_t. This function should be used for
|
||||
* the debugging purpose only.
|
||||
*/
|
||||
void nghttp2_ksl_print(nghttp2_ksl *ksl);
|
||||
|
||||
/*
|
||||
* nghttp2_ksl_it_init initializes |it|.
|
||||
*/
|
||||
void nghttp2_ksl_it_init(nghttp2_ksl_it *it, const nghttp2_ksl *ksl,
|
||||
nghttp2_ksl_blk *blk, size_t i);
|
||||
|
||||
/*
|
||||
* nghttp2_ksl_it_get returns the data associated to the node which
|
||||
* |it| points to. It is undefined to call this function when
|
||||
* nghttp2_ksl_it_end(it) returns nonzero.
|
||||
*/
|
||||
void *nghttp2_ksl_it_get(const nghttp2_ksl_it *it);
|
||||
|
||||
/*
|
||||
* nghttp2_ksl_it_next advances the iterator by one. It is undefined
|
||||
* if this function is called when nghttp2_ksl_it_end(it) returns
|
||||
* nonzero.
|
||||
*/
|
||||
#define nghttp2_ksl_it_next(IT) \
|
||||
(++(IT)->i == (IT)->blk->n && (IT)->blk->next \
|
||||
? ((IT)->blk = (IT)->blk->next, (IT)->i = 0) \
|
||||
: 0)
|
||||
|
||||
/*
|
||||
* nghttp2_ksl_it_prev moves backward the iterator by one. It is
|
||||
* undefined if this function is called when nghttp2_ksl_it_begin(it)
|
||||
* returns nonzero.
|
||||
*/
|
||||
void nghttp2_ksl_it_prev(nghttp2_ksl_it *it);
|
||||
|
||||
/*
|
||||
* nghttp2_ksl_it_end returns nonzero if |it| points to the beyond the
|
||||
* last node.
|
||||
*/
|
||||
#define nghttp2_ksl_it_end(IT) \
|
||||
((IT)->blk->n == (IT)->i && (IT)->blk->next == NULL)
|
||||
|
||||
/*
|
||||
* nghttp2_ksl_it_begin returns nonzero if |it| points to the first
|
||||
* node. |it| might satisfy both nghttp2_ksl_it_begin(&it) and
|
||||
* nghttp2_ksl_it_end(&it) if the skip list has no node.
|
||||
*/
|
||||
int nghttp2_ksl_it_begin(const nghttp2_ksl_it *it);
|
||||
|
||||
/*
|
||||
* nghttp2_ksl_key returns the key of the node which |it| points to.
|
||||
* It is undefined to call this function when nghttp2_ksl_it_end(it)
|
||||
* returns nonzero.
|
||||
*/
|
||||
#define nghttp2_ksl_it_key(IT) \
|
||||
((nghttp2_ksl_key *)nghttp2_ksl_nth_node((IT)->ksl, (IT)->blk, (IT)->i)->key)
|
||||
|
||||
#endif /* NGHTTP2_KSL_H */
|
||||
@@ -27,14 +27,16 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "nghttp2_helper.h"
|
||||
|
||||
#define INITIAL_TABLE_LENGTH 256
|
||||
#define NGHTTP2_INITIAL_TABLE_LENBITS 8
|
||||
|
||||
int nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem) {
|
||||
map->mem = mem;
|
||||
map->tablelen = INITIAL_TABLE_LENGTH;
|
||||
map->tablelen = 1 << NGHTTP2_INITIAL_TABLE_LENBITS;
|
||||
map->tablelenbits = NGHTTP2_INITIAL_TABLE_LENBITS;
|
||||
map->table =
|
||||
nghttp2_mem_calloc(mem, map->tablelen, sizeof(nghttp2_map_bucket));
|
||||
if (map->table == NULL) {
|
||||
@@ -47,151 +49,145 @@ int nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem) {
|
||||
}
|
||||
|
||||
void nghttp2_map_free(nghttp2_map *map) {
|
||||
size_t i;
|
||||
nghttp2_map_bucket *bkt;
|
||||
|
||||
if (!map) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < map->tablelen; ++i) {
|
||||
bkt = &map->table[i];
|
||||
if (bkt->ksl) {
|
||||
nghttp2_ksl_free(bkt->ksl);
|
||||
nghttp2_mem_free(map->mem, bkt->ksl);
|
||||
}
|
||||
}
|
||||
|
||||
nghttp2_mem_free(map->mem, map->table);
|
||||
}
|
||||
|
||||
void nghttp2_map_each_free(nghttp2_map *map,
|
||||
int (*func)(nghttp2_map_entry *entry, void *ptr),
|
||||
void nghttp2_map_each_free(nghttp2_map *map, int (*func)(void *data, void *ptr),
|
||||
void *ptr) {
|
||||
uint32_t i;
|
||||
nghttp2_map_bucket *bkt;
|
||||
nghttp2_ksl_it it;
|
||||
|
||||
for (i = 0; i < map->tablelen; ++i) {
|
||||
bkt = &map->table[i];
|
||||
|
||||
if (bkt->ptr) {
|
||||
func(bkt->ptr, ptr);
|
||||
bkt->ptr = NULL;
|
||||
assert(bkt->ksl == NULL || nghttp2_ksl_len(bkt->ksl) == 0);
|
||||
if (bkt->data == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bkt->ksl) {
|
||||
for (it = nghttp2_ksl_begin(bkt->ksl); !nghttp2_ksl_it_end(&it);
|
||||
nghttp2_ksl_it_next(&it)) {
|
||||
func(nghttp2_ksl_it_get(&it), ptr);
|
||||
}
|
||||
|
||||
nghttp2_ksl_free(bkt->ksl);
|
||||
nghttp2_mem_free(map->mem, bkt->ksl);
|
||||
bkt->ksl = NULL;
|
||||
}
|
||||
func(bkt->data, ptr);
|
||||
}
|
||||
}
|
||||
|
||||
int nghttp2_map_each(nghttp2_map *map,
|
||||
int (*func)(nghttp2_map_entry *entry, void *ptr),
|
||||
int nghttp2_map_each(nghttp2_map *map, int (*func)(void *data, void *ptr),
|
||||
void *ptr) {
|
||||
int rv;
|
||||
uint32_t i;
|
||||
nghttp2_map_bucket *bkt;
|
||||
nghttp2_ksl_it it;
|
||||
|
||||
for (i = 0; i < map->tablelen; ++i) {
|
||||
bkt = &map->table[i];
|
||||
|
||||
if (bkt->ptr) {
|
||||
rv = func(bkt->ptr, ptr);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
assert(bkt->ksl == NULL || nghttp2_ksl_len(bkt->ksl) == 0);
|
||||
if (bkt->data == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bkt->ksl) {
|
||||
for (it = nghttp2_ksl_begin(bkt->ksl); !nghttp2_ksl_it_end(&it);
|
||||
nghttp2_ksl_it_next(&it)) {
|
||||
rv = func(nghttp2_ksl_it_get(&it), ptr);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nghttp2_map_entry_init(nghttp2_map_entry *entry, key_type key) {
|
||||
entry->key = key;
|
||||
entry->next = NULL;
|
||||
}
|
||||
|
||||
/* FNV1a hash */
|
||||
static uint32_t hash(key_type key, uint32_t mod) {
|
||||
uint8_t *p, *end;
|
||||
uint32_t h = 0x811C9DC5u;
|
||||
|
||||
p = (uint8_t *)&key;
|
||||
end = p + sizeof(key_type);
|
||||
|
||||
for (; p != end;) {
|
||||
h ^= *p++;
|
||||
h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24);
|
||||
}
|
||||
|
||||
return h & (mod - 1);
|
||||
}
|
||||
|
||||
static int less(const nghttp2_ksl_key *lhs, const nghttp2_ksl_key *rhs) {
|
||||
return *(key_type *)lhs < *(key_type *)rhs;
|
||||
}
|
||||
|
||||
static int map_insert(nghttp2_map *map, nghttp2_map_bucket *table,
|
||||
uint32_t tablelen, nghttp2_map_entry *entry) {
|
||||
uint32_t h = hash(entry->key, tablelen);
|
||||
nghttp2_map_bucket *bkt = &table[h];
|
||||
nghttp2_mem *mem = map->mem;
|
||||
int rv;
|
||||
|
||||
if (bkt->ptr == NULL &&
|
||||
(bkt->ksl == NULL || nghttp2_ksl_len(bkt->ksl) == 0)) {
|
||||
bkt->ptr = entry;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!bkt->ksl) {
|
||||
bkt->ksl = nghttp2_mem_malloc(mem, sizeof(*bkt->ksl));
|
||||
if (bkt->ksl == NULL) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
nghttp2_ksl_init(bkt->ksl, less, sizeof(key_type), mem);
|
||||
}
|
||||
|
||||
if (bkt->ptr) {
|
||||
rv = nghttp2_ksl_insert(bkt->ksl, NULL, &bkt->ptr->key, bkt->ptr);
|
||||
rv = func(bkt->data, ptr);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
bkt->ptr = NULL;
|
||||
}
|
||||
|
||||
return nghttp2_ksl_insert(bkt->ksl, NULL, &entry->key, entry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* new_tablelen must be power of 2 */
|
||||
static int map_resize(nghttp2_map *map, uint32_t new_tablelen) {
|
||||
static uint32_t hash(nghttp2_map_key_type key) {
|
||||
return (uint32_t)key * 2654435769u;
|
||||
}
|
||||
|
||||
static size_t h2idx(uint32_t hash, uint32_t bits) {
|
||||
return hash >> (32 - bits);
|
||||
}
|
||||
|
||||
static size_t distance(uint32_t tablelen, uint32_t tablelenbits,
|
||||
nghttp2_map_bucket *bkt, size_t idx) {
|
||||
return (idx - h2idx(bkt->hash, tablelenbits)) & (tablelen - 1);
|
||||
}
|
||||
|
||||
static void map_bucket_swap(nghttp2_map_bucket *bkt, uint32_t *phash,
|
||||
nghttp2_map_key_type *pkey, void **pdata) {
|
||||
uint32_t h = bkt->hash;
|
||||
nghttp2_map_key_type key = bkt->key;
|
||||
void *data = bkt->data;
|
||||
|
||||
bkt->hash = *phash;
|
||||
bkt->key = *pkey;
|
||||
bkt->data = *pdata;
|
||||
|
||||
*phash = h;
|
||||
*pkey = key;
|
||||
*pdata = data;
|
||||
}
|
||||
|
||||
static void map_bucket_set_data(nghttp2_map_bucket *bkt, uint32_t hash,
|
||||
nghttp2_map_key_type key, void *data) {
|
||||
bkt->hash = hash;
|
||||
bkt->key = key;
|
||||
bkt->data = data;
|
||||
}
|
||||
|
||||
void nghttp2_map_print_distance(nghttp2_map *map) {
|
||||
uint32_t i;
|
||||
size_t idx;
|
||||
nghttp2_map_bucket *bkt;
|
||||
|
||||
for (i = 0; i < map->tablelen; ++i) {
|
||||
bkt = &map->table[i];
|
||||
|
||||
if (bkt->data == NULL) {
|
||||
fprintf(stderr, "@%u <EMPTY>\n", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
idx = h2idx(bkt->hash, map->tablelenbits);
|
||||
fprintf(stderr, "@%u hash=%08x key=%d base=%zu distance=%zu\n", i,
|
||||
bkt->hash, bkt->key, idx,
|
||||
distance(map->tablelen, map->tablelenbits, bkt, idx));
|
||||
}
|
||||
}
|
||||
|
||||
static int insert(nghttp2_map_bucket *table, uint32_t tablelen,
|
||||
uint32_t tablelenbits, uint32_t hash,
|
||||
nghttp2_map_key_type key, void *data) {
|
||||
size_t idx = h2idx(hash, tablelenbits);
|
||||
size_t d = 0, dd;
|
||||
nghttp2_map_bucket *bkt;
|
||||
|
||||
for (;;) {
|
||||
bkt = &table[idx];
|
||||
|
||||
if (bkt->data == NULL) {
|
||||
map_bucket_set_data(bkt, hash, key, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dd = distance(tablelen, tablelenbits, bkt, idx);
|
||||
if (d > dd) {
|
||||
map_bucket_swap(bkt, &hash, &key, &data);
|
||||
d = dd;
|
||||
} else if (bkt->key == key) {
|
||||
/* TODO This check is just a waste after first swap or if this
|
||||
function is called from map_resize. That said, there is no
|
||||
difference with or without this conditional in performance
|
||||
wise. */
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
++d;
|
||||
idx = (idx + 1) & (tablelen - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* new_tablelen must be power of 2 and new_tablelen == (1 <<
|
||||
new_tablelenbits) must hold. */
|
||||
static int map_resize(nghttp2_map *map, uint32_t new_tablelen,
|
||||
uint32_t new_tablelenbits) {
|
||||
uint32_t i;
|
||||
nghttp2_map_bucket *new_table;
|
||||
nghttp2_map_bucket *bkt;
|
||||
nghttp2_ksl_it it;
|
||||
int rv;
|
||||
|
||||
new_table =
|
||||
@@ -202,64 +198,38 @@ static int map_resize(nghttp2_map *map, uint32_t new_tablelen) {
|
||||
|
||||
for (i = 0; i < map->tablelen; ++i) {
|
||||
bkt = &map->table[i];
|
||||
|
||||
if (bkt->ptr) {
|
||||
rv = map_insert(map, new_table, new_tablelen, bkt->ptr);
|
||||
if (rv != 0) {
|
||||
goto fail;
|
||||
}
|
||||
assert(bkt->ksl == NULL || nghttp2_ksl_len(bkt->ksl) == 0);
|
||||
if (bkt->data == NULL) {
|
||||
continue;
|
||||
}
|
||||
rv = insert(new_table, new_tablelen, new_tablelenbits, bkt->hash, bkt->key,
|
||||
bkt->data);
|
||||
|
||||
if (bkt->ksl) {
|
||||
for (it = nghttp2_ksl_begin(bkt->ksl); !nghttp2_ksl_it_end(&it);
|
||||
nghttp2_ksl_it_next(&it)) {
|
||||
rv = map_insert(map, new_table, new_tablelen, nghttp2_ksl_it_get(&it));
|
||||
if (rv != 0) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < map->tablelen; ++i) {
|
||||
bkt = &map->table[i];
|
||||
if (bkt->ksl) {
|
||||
nghttp2_ksl_free(bkt->ksl);
|
||||
nghttp2_mem_free(map->mem, bkt->ksl);
|
||||
}
|
||||
assert(0 == rv);
|
||||
}
|
||||
|
||||
nghttp2_mem_free(map->mem, map->table);
|
||||
map->tablelen = new_tablelen;
|
||||
map->tablelenbits = new_tablelenbits;
|
||||
map->table = new_table;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
for (i = 0; i < new_tablelen; ++i) {
|
||||
bkt = &new_table[i];
|
||||
if (bkt->ksl) {
|
||||
nghttp2_ksl_free(bkt->ksl);
|
||||
nghttp2_mem_free(map->mem, bkt->ksl);
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_entry *new_entry) {
|
||||
int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_key_type key, void *data) {
|
||||
int rv;
|
||||
|
||||
assert(data);
|
||||
|
||||
/* Load factor is 0.75 */
|
||||
if ((map->size + 1) * 4 > map->tablelen * 3) {
|
||||
rv = map_resize(map, map->tablelen * 2);
|
||||
rv = map_resize(map, map->tablelen * 2, map->tablelenbits + 1);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
rv = map_insert(map, map->table, map->tablelen, new_entry);
|
||||
|
||||
rv = insert(map->table, map->tablelen, map->tablelenbits, hash(key), key,
|
||||
data);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
@@ -267,68 +237,75 @@ int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_entry *new_entry) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
nghttp2_map_entry *nghttp2_map_find(nghttp2_map *map, key_type key) {
|
||||
nghttp2_map_bucket *bkt = &map->table[hash(key, map->tablelen)];
|
||||
nghttp2_ksl_it it;
|
||||
void *nghttp2_map_find(nghttp2_map *map, nghttp2_map_key_type key) {
|
||||
uint32_t h = hash(key);
|
||||
size_t idx = h2idx(h, map->tablelenbits);
|
||||
nghttp2_map_bucket *bkt;
|
||||
size_t d = 0;
|
||||
|
||||
if (bkt->ptr) {
|
||||
if (bkt->ptr->key == key) {
|
||||
return bkt->ptr;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
for (;;) {
|
||||
bkt = &map->table[idx];
|
||||
|
||||
if (bkt->ksl) {
|
||||
it = nghttp2_ksl_lower_bound(bkt->ksl, &key);
|
||||
if (nghttp2_ksl_it_end(&it) ||
|
||||
*(key_type *)nghttp2_ksl_it_key(&it) != key) {
|
||||
if (bkt->data == NULL ||
|
||||
d > distance(map->tablelen, map->tablelenbits, bkt, idx)) {
|
||||
return NULL;
|
||||
}
|
||||
return nghttp2_ksl_it_get(&it);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
if (bkt->key == key) {
|
||||
return bkt->data;
|
||||
}
|
||||
|
||||
++d;
|
||||
idx = (idx + 1) & (map->tablelen - 1);
|
||||
}
|
||||
}
|
||||
|
||||
int nghttp2_map_remove(nghttp2_map *map, key_type key) {
|
||||
nghttp2_map_bucket *bkt = &map->table[hash(key, map->tablelen)];
|
||||
int rv;
|
||||
int nghttp2_map_remove(nghttp2_map *map, nghttp2_map_key_type key) {
|
||||
uint32_t h = hash(key);
|
||||
size_t idx = h2idx(h, map->tablelenbits), didx;
|
||||
nghttp2_map_bucket *bkt;
|
||||
size_t d = 0;
|
||||
|
||||
for (;;) {
|
||||
bkt = &map->table[idx];
|
||||
|
||||
if (bkt->data == NULL ||
|
||||
d > distance(map->tablelen, map->tablelenbits, bkt, idx)) {
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (bkt->key == key) {
|
||||
map_bucket_set_data(bkt, 0, 0, NULL);
|
||||
|
||||
didx = idx;
|
||||
idx = (idx + 1) & (map->tablelen - 1);
|
||||
|
||||
for (;;) {
|
||||
bkt = &map->table[idx];
|
||||
if (bkt->data == NULL ||
|
||||
distance(map->tablelen, map->tablelenbits, bkt, idx) == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
map->table[didx] = *bkt;
|
||||
map_bucket_set_data(bkt, 0, 0, NULL);
|
||||
didx = idx;
|
||||
|
||||
idx = (idx + 1) & (map->tablelen - 1);
|
||||
}
|
||||
|
||||
if (bkt->ptr) {
|
||||
if (bkt->ptr->key == key) {
|
||||
bkt->ptr = NULL;
|
||||
--map->size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (bkt->ksl) {
|
||||
rv = nghttp2_ksl_remove(bkt->ksl, NULL, &key);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
--map->size;
|
||||
return 0;
|
||||
++d;
|
||||
idx = (idx + 1) & (map->tablelen - 1);
|
||||
}
|
||||
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
void nghttp2_map_clear(nghttp2_map *map) {
|
||||
uint32_t i;
|
||||
nghttp2_map_bucket *bkt;
|
||||
|
||||
for (i = 0; i < map->tablelen; ++i) {
|
||||
bkt = &map->table[i];
|
||||
bkt->ptr = NULL;
|
||||
if (bkt->ksl) {
|
||||
nghttp2_ksl_free(bkt->ksl);
|
||||
nghttp2_mem_free(map->mem, bkt->ksl);
|
||||
bkt->ksl = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
memset(map->table, 0, sizeof(*map->table) * map->tablelen);
|
||||
map->size = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,31 +33,23 @@
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
#include "nghttp2_mem.h"
|
||||
#include "nghttp2_ksl.h"
|
||||
|
||||
/* Implementation of unordered map */
|
||||
|
||||
typedef int32_t key_type;
|
||||
|
||||
typedef struct nghttp2_map_entry {
|
||||
struct nghttp2_map_entry *next;
|
||||
key_type key;
|
||||
#if SIZEOF_INT_P == 4
|
||||
/* we requires 8 bytes aligment */
|
||||
int64_t pad;
|
||||
#endif
|
||||
} nghttp2_map_entry;
|
||||
typedef int32_t nghttp2_map_key_type;
|
||||
|
||||
typedef struct nghttp2_map_bucket {
|
||||
nghttp2_map_entry *ptr;
|
||||
nghttp2_ksl *ksl;
|
||||
uint32_t hash;
|
||||
nghttp2_map_key_type key;
|
||||
void *data;
|
||||
} nghttp2_map_bucket;
|
||||
|
||||
typedef struct {
|
||||
typedef struct nghttp2_map {
|
||||
nghttp2_map_bucket *table;
|
||||
nghttp2_mem *mem;
|
||||
size_t size;
|
||||
uint32_t tablelen;
|
||||
uint32_t tablelenbits;
|
||||
} nghttp2_map;
|
||||
|
||||
/*
|
||||
@@ -81,21 +73,14 @@ void nghttp2_map_free(nghttp2_map *map);
|
||||
/*
|
||||
* Deallocates each entries using |func| function and any resources
|
||||
* allocated for |map|. The |func| function is responsible for freeing
|
||||
* given the |entry| object. The |ptr| will be passed to the |func| as
|
||||
* given the |data| object. The |ptr| will be passed to the |func| as
|
||||
* send argument. The return value of the |func| will be ignored.
|
||||
*/
|
||||
void nghttp2_map_each_free(nghttp2_map *map,
|
||||
int (*func)(nghttp2_map_entry *entry, void *ptr),
|
||||
void nghttp2_map_each_free(nghttp2_map *map, int (*func)(void *data, void *ptr),
|
||||
void *ptr);
|
||||
|
||||
/*
|
||||
* Initializes the |entry| with the |key|. All entries to be inserted
|
||||
* to the map must be initialized with this function.
|
||||
*/
|
||||
void nghttp2_map_entry_init(nghttp2_map_entry *entry, key_type key);
|
||||
|
||||
/*
|
||||
* Inserts the new |entry| with the key |entry->key| to the map |map|.
|
||||
* Inserts the new |data| with the |key| to the map |map|.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
@@ -105,25 +90,25 @@ void nghttp2_map_entry_init(nghttp2_map_entry *entry, key_type key);
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_entry *entry);
|
||||
int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_key_type key, void *data);
|
||||
|
||||
/*
|
||||
* Returns the entry associated by the key |key|. If there is no such
|
||||
* entry, this function returns NULL.
|
||||
* Returns the data associated by the key |key|. If there is no such
|
||||
* data, this function returns NULL.
|
||||
*/
|
||||
nghttp2_map_entry *nghttp2_map_find(nghttp2_map *map, key_type key);
|
||||
void *nghttp2_map_find(nghttp2_map *map, nghttp2_map_key_type key);
|
||||
|
||||
/*
|
||||
* Removes the entry associated by the key |key| from the |map|. The
|
||||
* removed entry is not freed by this function.
|
||||
* Removes the data associated by the key |key| from the |map|. The
|
||||
* removed data is not freed by this function.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_INVALID_ARGUMENT
|
||||
* The entry associated by |key| does not exist.
|
||||
* The data associated by |key| does not exist.
|
||||
*/
|
||||
int nghttp2_map_remove(nghttp2_map *map, key_type key);
|
||||
int nghttp2_map_remove(nghttp2_map *map, nghttp2_map_key_type key);
|
||||
|
||||
/*
|
||||
* Removes all entries from |map|.
|
||||
@@ -136,21 +121,22 @@ void nghttp2_map_clear(nghttp2_map *map);
|
||||
size_t nghttp2_map_size(nghttp2_map *map);
|
||||
|
||||
/*
|
||||
* Applies the function |func| to each entry in the |map| with the
|
||||
* Applies the function |func| to each data in the |map| with the
|
||||
* optional user supplied pointer |ptr|.
|
||||
*
|
||||
* If the |func| returns 0, this function calls the |func| with the
|
||||
* next entry. If the |func| returns nonzero, it will not call the
|
||||
* next data. If the |func| returns nonzero, it will not call the
|
||||
* |func| for further entries and return the return value of the
|
||||
* |func| immediately. Thus, this function returns 0 if all the
|
||||
* invocations of the |func| return 0, or nonzero value which the last
|
||||
* invocation of |func| returns.
|
||||
*
|
||||
* Don't use this function to free each entry. Use
|
||||
* Don't use this function to free each data. Use
|
||||
* nghttp2_map_each_free() instead.
|
||||
*/
|
||||
int nghttp2_map_each(nghttp2_map *map,
|
||||
int (*func)(nghttp2_map_entry *entry, void *ptr),
|
||||
int nghttp2_map_each(nghttp2_map *map, int (*func)(void *data, void *ptr),
|
||||
void *ptr);
|
||||
|
||||
void nghttp2_map_print_distance(nghttp2_map *map);
|
||||
|
||||
#endif /* NGHTTP2_MAP_H */
|
||||
|
||||
@@ -666,7 +666,7 @@ int nghttp2_session_server_new3(nghttp2_session **session_ptr,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int free_streams(nghttp2_map_entry *entry, void *ptr) {
|
||||
static int free_streams(void *entry, void *ptr) {
|
||||
nghttp2_session *session;
|
||||
nghttp2_stream *stream;
|
||||
nghttp2_outbound_item *item;
|
||||
@@ -1102,7 +1102,7 @@ nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session,
|
||||
(int32_t)session->local_settings.initial_window_size,
|
||||
stream_user_data, mem);
|
||||
|
||||
rv = nghttp2_map_insert(&session->streams, &stream->map_entry);
|
||||
rv = nghttp2_map_insert(&session->streams, stream_id, stream);
|
||||
if (rv != 0) {
|
||||
nghttp2_stream_free(stream);
|
||||
nghttp2_mem_free(mem, stream);
|
||||
@@ -2424,7 +2424,7 @@ static int session_call_on_frame_send(nghttp2_session *session,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int find_stream_on_goaway_func(nghttp2_map_entry *entry, void *ptr) {
|
||||
static int find_stream_on_goaway_func(void *entry, void *ptr) {
|
||||
nghttp2_close_stream_on_goaway_arg *arg;
|
||||
nghttp2_stream *stream;
|
||||
|
||||
@@ -4194,8 +4194,7 @@ static int session_process_rst_stream_frame(nghttp2_session *session) {
|
||||
return nghttp2_session_on_rst_stream_received(session, frame);
|
||||
}
|
||||
|
||||
static int update_remote_initial_window_size_func(nghttp2_map_entry *entry,
|
||||
void *ptr) {
|
||||
static int update_remote_initial_window_size_func(void *entry, void *ptr) {
|
||||
int rv;
|
||||
nghttp2_update_window_size_arg *arg;
|
||||
nghttp2_stream *stream;
|
||||
@@ -4248,8 +4247,7 @@ session_update_remote_initial_window_size(nghttp2_session *session,
|
||||
update_remote_initial_window_size_func, &arg);
|
||||
}
|
||||
|
||||
static int update_local_initial_window_size_func(nghttp2_map_entry *entry,
|
||||
void *ptr) {
|
||||
static int update_local_initial_window_size_func(void *entry, void *ptr) {
|
||||
int rv;
|
||||
nghttp2_update_window_size_arg *arg;
|
||||
nghttp2_stream *stream;
|
||||
@@ -6432,8 +6430,9 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
|
||||
|
||||
/* CONTINUATION won't bear NGHTTP2_PADDED flag */
|
||||
|
||||
iframe->frame.hd.flags = (uint8_t)(
|
||||
iframe->frame.hd.flags | (cont_hd.flags & NGHTTP2_FLAG_END_HEADERS));
|
||||
iframe->frame.hd.flags =
|
||||
(uint8_t)(iframe->frame.hd.flags |
|
||||
(cont_hd.flags & NGHTTP2_FLAG_END_HEADERS));
|
||||
iframe->frame.hd.length += cont_hd.length;
|
||||
|
||||
busy = 1;
|
||||
|
||||
@@ -62,7 +62,6 @@ void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
|
||||
int32_t weight, int32_t remote_initial_window_size,
|
||||
int32_t local_initial_window_size,
|
||||
void *stream_user_data, nghttp2_mem *mem) {
|
||||
nghttp2_map_entry_init(&stream->map_entry, (key_type)stream_id);
|
||||
nghttp2_pq_init(&stream->obq, stream_less, mem);
|
||||
|
||||
stream->stream_id = stream_id;
|
||||
|
||||
@@ -135,8 +135,6 @@ typedef enum {
|
||||
} nghttp2_http_flag;
|
||||
|
||||
struct nghttp2_stream {
|
||||
/* Intrusive Map */
|
||||
nghttp2_map_entry map_entry;
|
||||
/* Entry for dep_prev->obq */
|
||||
nghttp2_pq_entry pq_entry;
|
||||
/* Priority Queue storing direct descendant (nghttp2_stream). Only
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_python_devel.html
|
||||
# https://www.gnu.org/software/autoconf-archive/ax_python_devel.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
@@ -12,8 +12,8 @@
|
||||
# in your configure.ac.
|
||||
#
|
||||
# This macro checks for Python and tries to get the include path to
|
||||
# 'Python.h'. It provides the $(PYTHON_CPPFLAGS) and $(PYTHON_LDFLAGS)
|
||||
# output variables. It also exports $(PYTHON_EXTRA_LIBS) and
|
||||
# 'Python.h'. It provides the $(PYTHON_CPPFLAGS) and $(PYTHON_LIBS) output
|
||||
# variables. It also exports $(PYTHON_EXTRA_LIBS) and
|
||||
# $(PYTHON_EXTRA_LDFLAGS) for embedding Python in your code.
|
||||
#
|
||||
# You can search for some particular version of Python by passing a
|
||||
@@ -52,7 +52,7 @@
|
||||
# Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
# with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# As a special exception, the respective Autoconf Macro's copyright owner
|
||||
# gives unlimited permission to copy, distribute and modify the configure
|
||||
@@ -67,7 +67,7 @@
|
||||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 16
|
||||
#serial 21
|
||||
|
||||
AU_ALIAS([AC_PYTHON_DEVEL], [AX_PYTHON_DEVEL])
|
||||
AC_DEFUN([AX_PYTHON_DEVEL],[
|
||||
@@ -81,12 +81,10 @@ AC_DEFUN([AX_PYTHON_DEVEL],[
|
||||
|
||||
AC_PATH_PROG([PYTHON],[python[$PYTHON_VERSION]])
|
||||
if test -z "$PYTHON"; then
|
||||
AC_MSG_WARN([Cannot find python$PYTHON_VERSION in your system path])
|
||||
AC_MSG_ERROR([Cannot find python$PYTHON_VERSION in your system path])
|
||||
PYTHON_VERSION=""
|
||||
no_python_devel=yes
|
||||
fi
|
||||
|
||||
AS_IF([test -z "$no_python_devel"], [
|
||||
#
|
||||
# Check for a version of Python >= 2.1.0
|
||||
#
|
||||
@@ -97,25 +95,22 @@ AS_IF([test -z "$no_python_devel"], [
|
||||
if test "$ac_supports_python_ver" != "True"; then
|
||||
if test -z "$PYTHON_NOVERSIONCHECK"; then
|
||||
AC_MSG_RESULT([no])
|
||||
AC_MSG_WARN([
|
||||
AC_MSG_FAILURE([
|
||||
This version of the AC@&t@_PYTHON_DEVEL macro
|
||||
doesn't work properly with versions of Python before
|
||||
2.1.0. You may need to re-run configure, setting the
|
||||
variables PYTHON_CPPFLAGS, PYTHON_LDFLAGS, PYTHON_SITE_PKG,
|
||||
variables PYTHON_CPPFLAGS, PYTHON_LIBS, PYTHON_SITE_PKG,
|
||||
PYTHON_EXTRA_LIBS and PYTHON_EXTRA_LDFLAGS by hand.
|
||||
Moreover, to disable this check, set PYTHON_NOVERSIONCHECK
|
||||
to something else than an empty string.
|
||||
])
|
||||
no_python_devel=yes
|
||||
else
|
||||
AC_MSG_RESULT([skip at user request])
|
||||
fi
|
||||
else
|
||||
AC_MSG_RESULT([yes])
|
||||
fi
|
||||
]) # AS_IF
|
||||
|
||||
AS_IF([test -z "$no_python_devel"], [
|
||||
#
|
||||
# if the macro parameter ``version'' is set, honour it
|
||||
#
|
||||
@@ -128,36 +123,30 @@ AS_IF([test -z "$no_python_devel"], [
|
||||
AC_MSG_RESULT([yes])
|
||||
else
|
||||
AC_MSG_RESULT([no])
|
||||
AC_MSG_WARN([this package requires Python $1.
|
||||
AC_MSG_ERROR([this package requires Python $1.
|
||||
If you have it installed, but it isn't the default Python
|
||||
interpreter in your system path, please pass the PYTHON_VERSION
|
||||
variable to configure. See ``configure --help'' for reference.
|
||||
])
|
||||
PYTHON_VERSION=""
|
||||
no_python_devel=yes
|
||||
fi
|
||||
fi
|
||||
]) # AS_IF
|
||||
|
||||
AS_IF([test -z "$no_python_devel"], [
|
||||
#
|
||||
# Check if you have distutils, else fail
|
||||
#
|
||||
AC_MSG_CHECKING([for the distutils Python package])
|
||||
ac_distutils_result=`$PYTHON -c "import distutils" 2>&1`
|
||||
if test -z "$ac_distutils_result"; then
|
||||
if test $? -eq 0; then
|
||||
AC_MSG_RESULT([yes])
|
||||
else
|
||||
AC_MSG_RESULT([no])
|
||||
AC_MSG_WARN([cannot import Python module "distutils".
|
||||
AC_MSG_ERROR([cannot import Python module "distutils".
|
||||
Please check your Python installation. The error was:
|
||||
$ac_distutils_result])
|
||||
PYTHON_VERSION=""
|
||||
no_python_devel=yes
|
||||
fi
|
||||
]) # AS_IF
|
||||
|
||||
AS_IF([test -z "$no_python_devel"], [
|
||||
#
|
||||
# Check for Python include path
|
||||
#
|
||||
@@ -183,7 +172,7 @@ AS_IF([test -z "$no_python_devel"], [
|
||||
# Check for Python library path
|
||||
#
|
||||
AC_MSG_CHECKING([for Python library path])
|
||||
if test -z "$PYTHON_LDFLAGS"; then
|
||||
if test -z "$PYTHON_LIBS"; then
|
||||
# (makes two attempts to ensure we've got a version number
|
||||
# from the interpreter)
|
||||
ac_python_version=`cat<<EOD | $PYTHON -
|
||||
@@ -238,29 +227,26 @@ EOD`
|
||||
then
|
||||
# use the official shared library
|
||||
ac_python_library=`echo "$ac_python_library" | sed "s/^lib//"`
|
||||
PYTHON_LDFLAGS="-L$ac_python_libdir -l$ac_python_library"
|
||||
PYTHON_LIBS="-L$ac_python_libdir -l$ac_python_library"
|
||||
else
|
||||
# old way: use libpython from python_configdir
|
||||
ac_python_libdir=`$PYTHON -c \
|
||||
"from distutils.sysconfig import get_python_lib as f; \
|
||||
import os; \
|
||||
print (os.path.join(f(plat_specific=1, standard_lib=1), 'config'));"`
|
||||
PYTHON_LDFLAGS="-L$ac_python_libdir -lpython$ac_python_version"
|
||||
PYTHON_LIBS="-L$ac_python_libdir -lpython$ac_python_version"
|
||||
fi
|
||||
|
||||
if test -z "PYTHON_LDFLAGS"; then
|
||||
AC_MSG_WARN([
|
||||
if test -z "PYTHON_LIBS"; then
|
||||
AC_MSG_ERROR([
|
||||
Cannot determine location of your Python DSO. Please check it was installed with
|
||||
dynamic libraries enabled, or try setting PYTHON_LDFLAGS by hand.
|
||||
dynamic libraries enabled, or try setting PYTHON_LIBS by hand.
|
||||
])
|
||||
no_python_devel=yes
|
||||
fi
|
||||
fi
|
||||
AC_MSG_RESULT([$PYTHON_LDFLAGS])
|
||||
AC_SUBST([PYTHON_LDFLAGS])
|
||||
]) # AS_IF
|
||||
AC_MSG_RESULT([$PYTHON_LIBS])
|
||||
AC_SUBST([PYTHON_LIBS])
|
||||
|
||||
AS_IF([test -z "$no_python_devel"], [
|
||||
#
|
||||
# Check for site packages
|
||||
#
|
||||
@@ -279,7 +265,7 @@ AS_IF([test -z "$no_python_devel"], [
|
||||
if test -z "$PYTHON_EXTRA_LIBS"; then
|
||||
PYTHON_EXTRA_LIBS=`$PYTHON -c "import distutils.sysconfig; \
|
||||
conf = distutils.sysconfig.get_config_var; \
|
||||
print (conf('LIBS'))"`
|
||||
print (conf('LIBS') + ' ' + conf('SYSLIBS'))"`
|
||||
fi
|
||||
AC_MSG_RESULT([$PYTHON_EXTRA_LIBS])
|
||||
AC_SUBST(PYTHON_EXTRA_LIBS)
|
||||
@@ -302,8 +288,10 @@ AS_IF([test -z "$no_python_devel"], [
|
||||
AC_MSG_CHECKING([consistency of all components of python development environment])
|
||||
# save current global flags
|
||||
ac_save_LIBS="$LIBS"
|
||||
ac_save_LDFLAGS="$LDFLAGS"
|
||||
ac_save_CPPFLAGS="$CPPFLAGS"
|
||||
LIBS="$ac_save_LIBS $PYTHON_LDFLAGS $PYTHON_EXTRA_LDFLAGS $PYTHON_EXTRA_LIBS"
|
||||
LIBS="$ac_save_LIBS $PYTHON_LIBS $PYTHON_EXTRA_LIBS $PYTHON_EXTRA_LIBS"
|
||||
LDFLAGS="$ac_save_LDFLAGS $PYTHON_EXTRA_LDFLAGS"
|
||||
CPPFLAGS="$ac_save_CPPFLAGS $PYTHON_CPPFLAGS"
|
||||
AC_LANG_PUSH([C])
|
||||
AC_LINK_IFELSE([
|
||||
@@ -314,15 +302,16 @@ AS_IF([test -z "$no_python_devel"], [
|
||||
# turn back to default flags
|
||||
CPPFLAGS="$ac_save_CPPFLAGS"
|
||||
LIBS="$ac_save_LIBS"
|
||||
LDFLAGS="$ac_save_LDFLAGS"
|
||||
|
||||
AC_MSG_RESULT([$pythonexists])
|
||||
|
||||
if test ! "x$pythonexists" = "xyes"; then
|
||||
AC_MSG_WARN([
|
||||
AC_MSG_FAILURE([
|
||||
Could not link test program to Python. Maybe the main Python library has been
|
||||
installed in some non-standard library path. If so, pass it to configure,
|
||||
via the LDFLAGS environment variable.
|
||||
Example: ./configure LDFLAGS="-L/usr/non-standard-path/python/lib"
|
||||
via the LIBS environment variable.
|
||||
Example: ./configure LIBS="-L/usr/non-standard-path/python/lib"
|
||||
============================================================================
|
||||
ERROR!
|
||||
You probably have to install the development version of the Python package
|
||||
@@ -330,15 +319,9 @@ AS_IF([test -z "$no_python_devel"], [
|
||||
============================================================================
|
||||
])
|
||||
PYTHON_VERSION=""
|
||||
no_python_devel=yes
|
||||
fi
|
||||
|
||||
#
|
||||
# all done!
|
||||
#
|
||||
]) # AS_IF
|
||||
|
||||
AS_IF([test -z "$no_python_devel"],
|
||||
[have_python_dev=yes], [have_python_dev=no])
|
||||
|
||||
]) # AS_IF
|
||||
])
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This script read cipher suite list csv file [1] and prints out id
|
||||
@@ -8,7 +8,6 @@
|
||||
# [1] http://www.iana.org/assignments/tls-parameters/tls-parameters-4.csv
|
||||
# [2] http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import re
|
||||
import sys
|
||||
import csv
|
||||
@@ -295,7 +294,7 @@ blacklist = [
|
||||
|
||||
ciphers = []
|
||||
found = set()
|
||||
for hl, name, _, _ in csv.reader(sys.stdin):
|
||||
for hl, name, _, _, _ in csv.reader(sys.stdin):
|
||||
if name not in blacklist:
|
||||
continue
|
||||
|
||||
@@ -306,21 +305,21 @@ for hl, name, _, _ in csv.reader(sys.stdin):
|
||||
id = high + low[2:] + 'u'
|
||||
ciphers.append((id, name))
|
||||
|
||||
print '''\
|
||||
enum {'''
|
||||
print('''\
|
||||
enum {''')
|
||||
|
||||
for id, name in ciphers:
|
||||
print '{} = {},'.format(name, id)
|
||||
print('{} = {},'.format(name, id))
|
||||
|
||||
print '''\
|
||||
print('''\
|
||||
};
|
||||
'''
|
||||
''')
|
||||
|
||||
for id, name in ciphers:
|
||||
print '''\
|
||||
case {}:'''.format(name)
|
||||
print('''\
|
||||
case {}:'''.format(name))
|
||||
|
||||
if len(found) != len(blacklist):
|
||||
print '{} found out of {}; not all cipher was found: {}'.format(
|
||||
print('{} found out of {}; not all cipher was found: {}'.format(
|
||||
len(found), len(blacklist),
|
||||
found.symmetric_difference(blacklist))
|
||||
found.symmetric_difference(blacklist)))
|
||||
|
||||
85
mkhufftbl.py
85
mkhufftbl.py
@@ -1,16 +1,15 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This script reads Huffman Code table [1] and generates symbol table
|
||||
# and decoding tables in C language. The resulting code is used in
|
||||
# lib/nghttp2_hd_huffman.h and lib/nghttp2_hd_huffman_data.c
|
||||
#
|
||||
# [1] http://http2.github.io/http2-spec/compression.html
|
||||
# [1] https://httpwg.org/specs/rfc7541.html
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import re
|
||||
import sys
|
||||
import StringIO
|
||||
from io import StringIO
|
||||
|
||||
# From [1]
|
||||
HUFFMAN_CODE_TABLE = """\
|
||||
@@ -363,8 +362,8 @@ NGHTTP2_HUFF_SYM = 1 << 15
|
||||
def _print_transition_table(node):
|
||||
if node.term is not None:
|
||||
return
|
||||
print '/* {} */'.format(node.id)
|
||||
print '{'
|
||||
print('/* {} */'.format(node.id))
|
||||
print('{')
|
||||
for nd, sym in node.trans:
|
||||
flags = 0
|
||||
if sym is None:
|
||||
@@ -382,38 +381,38 @@ def _print_transition_table(node):
|
||||
flags |= NGHTTP2_HUFF_ACCEPTED
|
||||
elif nd.accept:
|
||||
flags |= NGHTTP2_HUFF_ACCEPTED
|
||||
print ' {{0x{:02x}, {}}},'.format(id | flags, out)
|
||||
print '},'
|
||||
print(' {{0x{:02x}, {}}},'.format(id | flags, out))
|
||||
print('},')
|
||||
_print_transition_table(node.left)
|
||||
_print_transition_table(node.right)
|
||||
|
||||
def huffman_tree_print_transition_table(ctx):
|
||||
_print_transition_table(ctx.root)
|
||||
print '/* 256 */'
|
||||
print '{'
|
||||
print ' {0x100, 0},'
|
||||
print ' {0x100, 0},'
|
||||
print ' {0x100, 0},'
|
||||
print ' {0x100, 0},'
|
||||
print ' {0x100, 0},'
|
||||
print ' {0x100, 0},'
|
||||
print ' {0x100, 0},'
|
||||
print ' {0x100, 0},'
|
||||
print ' {0x100, 0},'
|
||||
print ' {0x100, 0},'
|
||||
print ' {0x100, 0},'
|
||||
print ' {0x100, 0},'
|
||||
print ' {0x100, 0},'
|
||||
print ' {0x100, 0},'
|
||||
print ' {0x100, 0},'
|
||||
print ' {0x100, 0},'
|
||||
print '},'
|
||||
print('/* 256 */')
|
||||
print('{')
|
||||
print(' {0x100, 0},')
|
||||
print(' {0x100, 0},')
|
||||
print(' {0x100, 0},')
|
||||
print(' {0x100, 0},')
|
||||
print(' {0x100, 0},')
|
||||
print(' {0x100, 0},')
|
||||
print(' {0x100, 0},')
|
||||
print(' {0x100, 0},')
|
||||
print(' {0x100, 0},')
|
||||
print(' {0x100, 0},')
|
||||
print(' {0x100, 0},')
|
||||
print(' {0x100, 0},')
|
||||
print(' {0x100, 0},')
|
||||
print(' {0x100, 0},')
|
||||
print(' {0x100, 0},')
|
||||
print(' {0x100, 0},')
|
||||
print('},')
|
||||
|
||||
if __name__ == '__main__':
|
||||
ctx = Context()
|
||||
symbol_tbl = [(None, 0) for i in range(257)]
|
||||
|
||||
for line in StringIO.StringIO(HUFFMAN_CODE_TABLE):
|
||||
for line in StringIO(HUFFMAN_CODE_TABLE):
|
||||
m = re.match(
|
||||
r'.*\(\s*(\d+)\)\s+([|01]+)\s+(\S+)\s+\[\s*(\d+)\].*', line)
|
||||
if m:
|
||||
@@ -430,40 +429,40 @@ if __name__ == '__main__':
|
||||
huffman_tree_set_node_id(ctx)
|
||||
huffman_tree_build_transition_table(ctx)
|
||||
|
||||
print '''\
|
||||
print('''\
|
||||
typedef struct {
|
||||
uint32_t nbits;
|
||||
uint32_t code;
|
||||
} nghttp2_huff_sym;
|
||||
'''
|
||||
''')
|
||||
|
||||
print '''\
|
||||
const nghttp2_huff_sym huff_sym_table[] = {'''
|
||||
print('''\
|
||||
const nghttp2_huff_sym huff_sym_table[] = {''')
|
||||
for i in range(257):
|
||||
nbits = symbol_tbl[i][0]
|
||||
k = int(symbol_tbl[i][1], 16)
|
||||
k = k << (32 - nbits)
|
||||
print '''\
|
||||
print('''\
|
||||
{{ {}, 0x{}u }}{}\
|
||||
'''.format(symbol_tbl[i][0], hex(k)[2:], ',' if i < 256 else '')
|
||||
print '};'
|
||||
print ''
|
||||
'''.format(symbol_tbl[i][0], hex(k)[2:], ',' if i < 256 else ''))
|
||||
print('};')
|
||||
print()
|
||||
|
||||
print '''\
|
||||
print('''\
|
||||
enum {{
|
||||
NGHTTP2_HUFF_ACCEPTED = {},
|
||||
NGHTTP2_HUFF_SYM = {},
|
||||
}} nghttp2_huff_decode_flag;
|
||||
'''.format(NGHTTP2_HUFF_ACCEPTED, NGHTTP2_HUFF_SYM)
|
||||
'''.format(NGHTTP2_HUFF_ACCEPTED, NGHTTP2_HUFF_SYM))
|
||||
|
||||
print '''\
|
||||
print('''\
|
||||
typedef struct {
|
||||
uint16_t fstate;
|
||||
uint8_t sym;
|
||||
} nghttp2_huff_decode;
|
||||
'''
|
||||
''')
|
||||
|
||||
print '''\
|
||||
const nghttp2_huff_decode huff_decode_table[][16] = {'''
|
||||
print('''\
|
||||
const nghttp2_huff_decode huff_decode_table[][16] = {''')
|
||||
huffman_tree_print_transition_table(ctx)
|
||||
print '};'
|
||||
print('};')
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This scripts reads static table entries [1] and generates
|
||||
# nghttp2_hd_static_entry table. This table is used in
|
||||
# lib/nghttp2_hd.c.
|
||||
#
|
||||
# [1] http://http2.github.io/http2-spec/compression.html
|
||||
# [1] https://httpwg.org/specs/rfc7541.html
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import re, sys
|
||||
|
||||
def hd_map_hash(name):
|
||||
@@ -27,11 +26,11 @@ for line in sys.stdin:
|
||||
val = m.group(3).strip() if m.group(3) else ''
|
||||
entries.append((int(m.group(1)), m.group(2), val))
|
||||
|
||||
print 'static nghttp2_hd_entry static_table[] = {'
|
||||
print('static nghttp2_hd_entry static_table[] = {')
|
||||
idx = 0
|
||||
for i, ent in enumerate(entries):
|
||||
if entries[idx][1] != ent[1]:
|
||||
idx = i
|
||||
print 'MAKE_STATIC_ENT("{}", "{}", {}, {}u),'\
|
||||
.format(ent[1], ent[2], entries[idx][0] - 1, hd_map_hash(ent[1]))
|
||||
print '};'
|
||||
print('MAKE_STATIC_ENT("{}", "{}", {}, {}u),'\
|
||||
.format(ent[1], ent[2], entries[idx][0] - 1, hd_map_hash(ent[1])))
|
||||
print('};')
|
||||
|
||||
@@ -44,6 +44,6 @@ clean-local:
|
||||
-rm -f $(builddir)/nghttp2.c
|
||||
|
||||
.pyx.c:
|
||||
$(CYTHON) -o $@ $<
|
||||
$(CYTHON) -3 -o $@ $<
|
||||
|
||||
endif # ENABLE_PYTHON_BINDINGS
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# This script reads json files given in the command-line (each file
|
||||
# must be written in the format described in
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# This script reads input headers from json file given in the
|
||||
# command-line (each file must be written in the format described in
|
||||
|
||||
@@ -857,7 +857,7 @@ cdef class _HTTP2SessionCore(_HTTP2SessionCoreBase):
|
||||
|
||||
rv = cnghttp2.nghttp2_submit_settings(self.session,
|
||||
cnghttp2.NGHTTP2_FLAG_NONE,
|
||||
iv, sizeof(iv) / sizeof(iv[0]))
|
||||
iv, sizeof(iv) // sizeof(iv[0]))
|
||||
|
||||
if rv != 0:
|
||||
raise Exception('nghttp2_submit_settings failed: {}'.format\
|
||||
@@ -971,7 +971,7 @@ cdef class _HTTP2ClientSessionCore(_HTTP2SessionCoreBase):
|
||||
|
||||
rv = cnghttp2.nghttp2_submit_settings(self.session,
|
||||
cnghttp2.NGHTTP2_FLAG_NONE,
|
||||
iv, sizeof(iv) / sizeof(iv[0]))
|
||||
iv, sizeof(iv) // sizeof(iv[0]))
|
||||
|
||||
if rv != 0:
|
||||
raise Exception('nghttp2_submit_settings failed: {}'.format\
|
||||
|
||||
@@ -15,10 +15,14 @@ include_directories(
|
||||
${JEMALLOC_INCLUDE_DIRS}
|
||||
${LIBXML2_INCLUDE_DIRS}
|
||||
${LIBEV_INCLUDE_DIRS}
|
||||
${LIBNGHTTP3_INCLUDE_DIRS}
|
||||
${LIBNGTCP2_INCLUDE_DIRS}
|
||||
${LIBNGTCP2_CRYPTO_OPENSSL_INCLUDE_DIRS}
|
||||
${OPENSSL_INCLUDE_DIRS}
|
||||
${LIBCARES_INCLUDE_DIRS}
|
||||
${JANSSON_INCLUDE_DIRS}
|
||||
${ZLIB_INCLUDE_DIRS}
|
||||
${LIBBPF_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
# XXX per-target?
|
||||
@@ -27,11 +31,15 @@ link_libraries(
|
||||
${JEMALLOC_LIBRARIES}
|
||||
${LIBXML2_LIBRARIES}
|
||||
${LIBEV_LIBRARIES}
|
||||
${LIBNGHTTP3_LIBRARIES}
|
||||
${LIBNGTCP2_LIBRARIES}
|
||||
${LIBNGTCP2_CRYPTO_OPENSSL_LIBRARIES}
|
||||
${OPENSSL_LIBRARIES}
|
||||
${LIBCARES_LIBRARIES}
|
||||
${JANSSON_LIBRARIES}
|
||||
${ZLIB_LIBRARIES}
|
||||
${APP_LIBRARIES}
|
||||
${LIBBPF_LIBRARIES}
|
||||
)
|
||||
|
||||
if(ENABLE_APP)
|
||||
@@ -67,7 +75,13 @@ if(ENABLE_APP)
|
||||
h2load_http2_session.cc
|
||||
h2load_http1_session.cc
|
||||
)
|
||||
|
||||
if(ENABLE_HTTP3)
|
||||
list(APPEND H2LOAD_SOURCES
|
||||
h2load_http3_session.cc
|
||||
h2load_quic.cc
|
||||
quic.cc
|
||||
)
|
||||
endif()
|
||||
|
||||
# Common libnhttpx sources (used for nghttpx and unit tests)
|
||||
set(NGHTTPX_SRCS
|
||||
@@ -104,6 +118,7 @@ if(ENABLE_APP)
|
||||
shrpx_router.cc
|
||||
shrpx_api_downstream_connection.cc
|
||||
shrpx_health_monitor_downstream_connection.cc
|
||||
shrpx_null_downstream_connection.cc
|
||||
shrpx_exec.cc
|
||||
shrpx_dns_resolver.cc
|
||||
shrpx_dual_dns_resolver.cc
|
||||
@@ -119,6 +134,16 @@ if(ENABLE_APP)
|
||||
shrpx_mruby_module_response.cc
|
||||
)
|
||||
endif()
|
||||
if(ENABLE_HTTP3)
|
||||
list(APPEND NGHTTPX_SRCS
|
||||
shrpx_quic.cc
|
||||
shrpx_quic_listener.cc
|
||||
shrpx_quic_connection_handler.cc
|
||||
shrpx_http3_upstream.cc
|
||||
http3.cc
|
||||
quic.cc
|
||||
)
|
||||
endif()
|
||||
add_library(nghttpx_static STATIC ${NGHTTPX_SRCS})
|
||||
set_target_properties(nghttpx_static PROPERTIES ARCHIVE_OUTPUT_NAME nghttpx)
|
||||
|
||||
@@ -189,7 +214,10 @@ if(ENABLE_APP)
|
||||
add_executable(nghttpx ${NGHTTPX-bin_SOURCES} $<TARGET_OBJECTS:llhttp>
|
||||
$<TARGET_OBJECTS:url-parser>
|
||||
)
|
||||
target_compile_definitions(nghttpx PRIVATE "-DPKGDATADIR=\"${PKGDATADIR}\"")
|
||||
target_compile_definitions(nghttpx PRIVATE
|
||||
"-DPKGDATADIR=\"${PKGDATADIR}\""
|
||||
"-DPKGLIBDIR=\"${PKGLIBDIR}\""
|
||||
)
|
||||
target_link_libraries(nghttpx nghttpx_static)
|
||||
add_executable(h2load ${H2LOAD_SOURCES} $<TARGET_OBJECTS:llhttp>
|
||||
$<TARGET_OBJECTS:url-parser>
|
||||
|
||||
@@ -52,8 +52,13 @@
|
||||
#include <mutex>
|
||||
#include <deque>
|
||||
|
||||
#include "ssl_compat.h"
|
||||
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/dh.h>
|
||||
#if OPENSSL_3_0_0_API
|
||||
# include <openssl/decoder.h>
|
||||
#endif // OPENSSL_3_0_0_API
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
@@ -2138,15 +2143,22 @@ int HttpServer::run() {
|
||||
SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_SERVER);
|
||||
|
||||
#ifndef OPENSSL_NO_EC
|
||||
|
||||
// Disabled SSL_CTX_set_ecdh_auto, because computational cost of
|
||||
// chosen curve is much higher than P-256.
|
||||
|
||||
// #if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||
// SSL_CTX_set_ecdh_auto(ssl_ctx, 1);
|
||||
// #else // OPENSSL_VERSION_NUBMER < 0x10002000L
|
||||
// Use P-256, which is sufficiently secure at the time of this
|
||||
// writing.
|
||||
# if OPENSSL_3_0_0_API
|
||||
auto ecdh = EVP_EC_gen("P-256");
|
||||
if (ecdh == nullptr) {
|
||||
std::cerr << "EC_KEY_new_by_curv_name failed: "
|
||||
<< ERR_error_string(ERR_get_error(), nullptr);
|
||||
return -1;
|
||||
}
|
||||
SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh);
|
||||
EVP_PKEY_free(ecdh);
|
||||
# else // !OPENSSL_3_0_0_API
|
||||
auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
|
||||
if (ecdh == nullptr) {
|
||||
std::cerr << "EC_KEY_new_by_curv_name failed: "
|
||||
@@ -2155,19 +2167,33 @@ int HttpServer::run() {
|
||||
}
|
||||
SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh);
|
||||
EC_KEY_free(ecdh);
|
||||
// #endif // OPENSSL_VERSION_NUBMER < 0x10002000L
|
||||
|
||||
#endif // OPENSSL_NO_EC
|
||||
# endif // !OPENSSL_3_0_0_API
|
||||
#endif // OPENSSL_NO_EC
|
||||
|
||||
if (!config_->dh_param_file.empty()) {
|
||||
// Read DH parameters from file
|
||||
auto bio = BIO_new_file(config_->dh_param_file.c_str(), "r");
|
||||
auto bio = BIO_new_file(config_->dh_param_file.c_str(), "rb");
|
||||
if (bio == nullptr) {
|
||||
std::cerr << "BIO_new_file() failed: "
|
||||
<< ERR_error_string(ERR_get_error(), nullptr) << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if OPENSSL_3_0_0_API
|
||||
EVP_PKEY *dh = nullptr;
|
||||
auto dctx = OSSL_DECODER_CTX_new_for_pkey(
|
||||
&dh, "PEM", nullptr, "DH", OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS,
|
||||
nullptr, nullptr);
|
||||
|
||||
if (!OSSL_DECODER_from_bio(dctx, bio)) {
|
||||
std::cerr << "OSSL_DECODER_from_bio() failed: "
|
||||
<< ERR_error_string(ERR_get_error(), nullptr) << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
SSL_CTX_set_tmp_dh(ssl_ctx, dh);
|
||||
EVP_PKEY_free(dh);
|
||||
#else // !OPENSSL_3_0_0_API
|
||||
auto dh = PEM_read_bio_DHparams(bio, nullptr, nullptr, nullptr);
|
||||
|
||||
if (dh == nullptr) {
|
||||
@@ -2178,6 +2204,7 @@ int HttpServer::run() {
|
||||
|
||||
SSL_CTX_set_tmp_dh(ssl_ctx, dh);
|
||||
DH_free(dh);
|
||||
#endif // !OPENSSL_3_0_0_API
|
||||
BIO_free(bio);
|
||||
}
|
||||
|
||||
|
||||
@@ -242,7 +242,7 @@ private:
|
||||
};
|
||||
|
||||
ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
|
||||
uint8_t *buf, size_t length, int *eof,
|
||||
uint8_t *buf, size_t length, uint32_t *data_flags,
|
||||
nghttp2_data_source *source, void *user_data);
|
||||
|
||||
} // namespace nghttp2
|
||||
|
||||
@@ -35,19 +35,27 @@ AM_CFLAGS = $(WARNCFLAGS)
|
||||
AM_CXXFLAGS = $(WARNCXXFLAGS) $(CXX1XCXXFLAGS)
|
||||
AM_CPPFLAGS = \
|
||||
-DPKGDATADIR='"$(pkgdatadir)"' \
|
||||
-DPKGLIBDIR='"$(pkglibdir)"' \
|
||||
-I$(top_srcdir)/lib/includes \
|
||||
-I$(top_builddir)/lib/includes \
|
||||
-I$(top_srcdir)/lib \
|
||||
-I$(top_srcdir)/src/includes \
|
||||
-I$(top_srcdir)/third-party \
|
||||
-I$(top_srcdir)/third-party/llhttp/include \
|
||||
@JEMALLOC_CFLAGS@ \
|
||||
@LIBXML2_CFLAGS@ \
|
||||
@LIBEV_CFLAGS@ \
|
||||
@LIBNGHTTP3_CFLAGS@ \
|
||||
@LIBNGTCP2_CRYPTO_OPENSSL_CFLAGS@ \
|
||||
@LIBNGTCP2_CFLAGS@ \
|
||||
@OPENSSL_CFLAGS@ \
|
||||
@LIBCARES_CFLAGS@ \
|
||||
@JANSSON_CFLAGS@ \
|
||||
@LIBBPF_CFLAGS@ \
|
||||
@ZLIB_CFLAGS@ \
|
||||
@EXTRA_DEFS@ \
|
||||
@DEFS@
|
||||
AM_LDFLAGS = @LIBTOOL_LDFLAGS@
|
||||
|
||||
LDADD = $(top_builddir)/lib/libnghttp2.la \
|
||||
$(top_builddir)/third-party/liburl-parser.la \
|
||||
@@ -55,10 +63,14 @@ LDADD = $(top_builddir)/lib/libnghttp2.la \
|
||||
@JEMALLOC_LIBS@ \
|
||||
@LIBXML2_LIBS@ \
|
||||
@LIBEV_LIBS@ \
|
||||
@LIBNGHTTP3_LIBS@ \
|
||||
@LIBNGTCP2_CRYPTO_OPENSSL_LIBS@ \
|
||||
@LIBNGTCP2_LIBS@ \
|
||||
@OPENSSL_LIBS@ \
|
||||
@LIBCARES_LIBS@ \
|
||||
@SYSTEMD_LIBS@ \
|
||||
@JANSSON_LIBS@ \
|
||||
@LIBBPF_LIBS@ \
|
||||
@ZLIB_LIBS@ \
|
||||
@APPLDFLAGS@
|
||||
|
||||
@@ -97,6 +109,13 @@ h2load_SOURCES = util.cc util.h \
|
||||
h2load_http2_session.cc h2load_http2_session.h \
|
||||
h2load_http1_session.cc h2load_http1_session.h
|
||||
|
||||
if ENABLE_HTTP3
|
||||
h2load_SOURCES += \
|
||||
h2load_http3_session.cc h2load_http3_session.h \
|
||||
h2load_quic.cc h2load_quic.h \
|
||||
quic.cc quic.h
|
||||
endif # ENABLE_HTTP3
|
||||
|
||||
NGHTTPX_SRCS = \
|
||||
util.cc util.h http2.cc http2.h timegm.c timegm.h base64.h \
|
||||
app_helper.cc app_helper.h \
|
||||
@@ -137,6 +156,7 @@ NGHTTPX_SRCS = \
|
||||
shrpx_api_downstream_connection.cc shrpx_api_downstream_connection.h \
|
||||
shrpx_health_monitor_downstream_connection.cc \
|
||||
shrpx_health_monitor_downstream_connection.h \
|
||||
shrpx_null_downstream_connection.cc shrpx_null_downstream_connection.h \
|
||||
shrpx_exec.cc shrpx_exec.h \
|
||||
shrpx_dns_resolver.cc shrpx_dns_resolver.h \
|
||||
shrpx_dual_dns_resolver.cc shrpx_dual_dns_resolver.h \
|
||||
@@ -153,6 +173,16 @@ NGHTTPX_SRCS += \
|
||||
shrpx_mruby_module_response.cc shrpx_mruby_module_response.h
|
||||
endif # HAVE_MRUBY
|
||||
|
||||
if ENABLE_HTTP3
|
||||
NGHTTPX_SRCS += \
|
||||
shrpx_quic.cc shrpx_quic.h \
|
||||
shrpx_quic_listener.cc shrpx_quic_listener.h \
|
||||
shrpx_quic_connection_handler.cc shrpx_quic_connection_handler.h \
|
||||
shrpx_http3_upstream.cc shrpx_http3_upstream.h \
|
||||
http3.cc http3.h \
|
||||
quic.cc quic.h
|
||||
endif # ENABLE_HTTP3
|
||||
|
||||
noinst_LIBRARIES = libnghttpx.a
|
||||
libnghttpx_a_SOURCES = ${NGHTTPX_SRCS}
|
||||
libnghttpx_a_CPPFLAGS = ${AM_CPPFLAGS}
|
||||
@@ -262,7 +292,7 @@ libnghttp2_asio_la_SOURCES = \
|
||||
asio_client_tls_context.cc asio_client_tls_context.h
|
||||
|
||||
libnghttp2_asio_la_CPPFLAGS = ${AM_CPPFLAGS} ${BOOST_CPPFLAGS}
|
||||
libnghttp2_asio_la_LDFLAGS = -no-undefined -version-info 1:0:0
|
||||
libnghttp2_asio_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined -version-info 1:0:0
|
||||
libnghttp2_asio_la_LIBADD = \
|
||||
$(top_builddir)/lib/libnghttp2.la \
|
||||
$(top_builddir)/third-party/liburl-parser.la \
|
||||
|
||||
@@ -197,7 +197,7 @@ inline size_t concat_string_ref_count(size_t acc) { return acc; }
|
||||
// accumulated, and passed to the next function.
|
||||
template <typename... Args>
|
||||
size_t concat_string_ref_count(size_t acc, const StringRef &value,
|
||||
Args &&... args) {
|
||||
Args &&...args) {
|
||||
return concat_string_ref_count(acc + value.size(),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
@@ -212,7 +212,7 @@ inline uint8_t *concat_string_ref_copy(uint8_t *p) { return p; }
|
||||
// beyond the last byte written.
|
||||
template <typename... Args>
|
||||
uint8_t *concat_string_ref_copy(uint8_t *p, const StringRef &value,
|
||||
Args &&... args) {
|
||||
Args &&...args) {
|
||||
p = std::copy(std::begin(value), std::end(value), p);
|
||||
return concat_string_ref_copy(p, std::forward<Args>(args)...);
|
||||
}
|
||||
@@ -220,7 +220,7 @@ uint8_t *concat_string_ref_copy(uint8_t *p, const StringRef &value,
|
||||
// Returns the string which is the concatenation of |args| in the
|
||||
// given order. The resulting string will be NULL-terminated.
|
||||
template <typename BlockAllocator, typename... Args>
|
||||
StringRef concat_string_ref(BlockAllocator &alloc, Args &&... args) {
|
||||
StringRef concat_string_ref(BlockAllocator &alloc, Args &&...args) {
|
||||
size_t len = concat_string_ref_count(0, std::forward<Args>(args)...);
|
||||
auto dst = static_cast<uint8_t *>(alloc.alloc(len + 1));
|
||||
auto p = dst;
|
||||
@@ -237,7 +237,7 @@ StringRef concat_string_ref(BlockAllocator &alloc, Args &&... args) {
|
||||
// then just call concat_string_ref().
|
||||
template <typename BlockAllocator, typename... Args>
|
||||
StringRef realloc_concat_string_ref(BlockAllocator &alloc,
|
||||
const StringRef &value, Args &&... args) {
|
||||
const StringRef &value, Args &&...args) {
|
||||
if (value.empty()) {
|
||||
return concat_string_ref(alloc, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ generator_cb deferred_generator() {
|
||||
}
|
||||
|
||||
template <typename F, typename... T>
|
||||
std::shared_ptr<Defer<F, T...>> defer_shared(F &&f, T &&... t) {
|
||||
std::shared_ptr<Defer<F, T...>> defer_shared(F &&f, T &&...t) {
|
||||
return std::make_shared<Defer<F, T...>>(std::forward<F>(f),
|
||||
std::forward<T>(t)...);
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ public:
|
||||
serve_mux &mux,
|
||||
const boost::posix_time::time_duration &tls_handshake_timeout,
|
||||
const boost::posix_time::time_duration &read_timeout,
|
||||
SocketArgs &&... args)
|
||||
SocketArgs &&...args)
|
||||
: socket_(std::forward<SocketArgs>(args)...),
|
||||
mux_(mux),
|
||||
deadline_(GET_IO_SERVICE(socket_)),
|
||||
|
||||
509
src/h2load.cc
509
src/h2load.cc
@@ -34,6 +34,8 @@
|
||||
#ifdef HAVE_FCNTL_H
|
||||
# include <fcntl.h>
|
||||
#endif // HAVE_FCNTL_H
|
||||
#include <sys/mman.h>
|
||||
#include <netinet/udp.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
@@ -48,10 +50,18 @@
|
||||
|
||||
#include <openssl/err.h>
|
||||
|
||||
#ifdef ENABLE_HTTP3
|
||||
# include <ngtcp2/ngtcp2.h>
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
#include "url-parser/url_parser.h"
|
||||
|
||||
#include "h2load_http1_session.h"
|
||||
#include "h2load_http2_session.h"
|
||||
#ifdef ENABLE_HTTP3
|
||||
# include "h2load_http3_session.h"
|
||||
# include "h2load_quic.h"
|
||||
#endif // ENABLE_HTTP3
|
||||
#include "tls.h"
|
||||
#include "http2.h"
|
||||
#include "util.h"
|
||||
@@ -71,9 +81,24 @@ bool recorded(const std::chrono::steady_clock::time_point &t) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
#if OPENSSL_1_1_1_API
|
||||
namespace {
|
||||
std::ofstream keylog_file;
|
||||
void keylog_callback(const SSL *ssl, const char *line) {
|
||||
keylog_file.write(line, strlen(line));
|
||||
keylog_file.put('\n');
|
||||
keylog_file.flush();
|
||||
}
|
||||
} // namespace
|
||||
#endif // OPENSSL_1_1_1_API
|
||||
|
||||
Config::Config()
|
||||
: ciphers(tls::DEFAULT_CIPHER_LIST),
|
||||
tls13_ciphers("TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_"
|
||||
"CHACHA20_POLY1305_SHA256:TLS_AES_128_CCM_SHA256"),
|
||||
groups("X25519:P-256:P-384:P-521"),
|
||||
data_length(-1),
|
||||
data(nullptr),
|
||||
addrs(nullptr),
|
||||
nreqs(1),
|
||||
nclients(1),
|
||||
@@ -92,13 +117,17 @@ Config::Config()
|
||||
encoder_header_table_size(4_k),
|
||||
data_fd(-1),
|
||||
log_fd(-1),
|
||||
qlog_file_base(),
|
||||
port(0),
|
||||
default_port(0),
|
||||
connect_to_port(0),
|
||||
verbose(false),
|
||||
timing_script(false),
|
||||
base_uri_unix(false),
|
||||
unix_addr{} {}
|
||||
unix_addr{},
|
||||
rps(0.),
|
||||
no_udp_gso(false),
|
||||
max_udp_payload_size(0) {}
|
||||
|
||||
Config::~Config() {
|
||||
if (addrs) {
|
||||
@@ -117,6 +146,15 @@ 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()); }
|
||||
bool Config::rps_enabled() const { return this->rps > 0.0; }
|
||||
bool Config::is_quic() const {
|
||||
#ifdef ENABLE_HTTP3
|
||||
return !npn_list.empty() &&
|
||||
(npn_list[0] == NGHTTP3_ALPN_H3 || npn_list[0] == "\x5h3-29");
|
||||
#else // !ENABLE_HTTP3
|
||||
return false;
|
||||
#endif // !ENABLE_HTTP3
|
||||
}
|
||||
Config config;
|
||||
|
||||
namespace {
|
||||
@@ -136,7 +174,9 @@ Stats::Stats(size_t req_todo, size_t nclients)
|
||||
bytes_head(0),
|
||||
bytes_head_decomp(0),
|
||||
bytes_body(0),
|
||||
status() {}
|
||||
status(),
|
||||
udp_dgram_recv(0),
|
||||
udp_dgram_sent(0) {}
|
||||
|
||||
Stream::Stream() : req_stat{}, status_success(-1) {}
|
||||
|
||||
@@ -193,8 +233,7 @@ void readcb(struct ev_loop *loop, ev_io *w, int revents) {
|
||||
delete client;
|
||||
return;
|
||||
}
|
||||
writecb(loop, &client->wev, revents);
|
||||
// client->disconnect() and client->fail() may be called
|
||||
client->signal_write();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -286,6 +325,51 @@ void warmup_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void rps_cb(struct ev_loop *loop, ev_timer *w, int revents) {
|
||||
auto client = static_cast<Client *>(w->data);
|
||||
auto &session = client->session;
|
||||
|
||||
assert(!config.timing_script);
|
||||
|
||||
if (client->req_left == 0) {
|
||||
ev_timer_stop(loop, w);
|
||||
return;
|
||||
}
|
||||
|
||||
auto now = ev_now(loop);
|
||||
auto d = now - client->rps_duration_started;
|
||||
auto n = static_cast<size_t>(round(d * config.rps));
|
||||
client->rps_req_pending += n;
|
||||
client->rps_duration_started = now - d + static_cast<double>(n) / config.rps;
|
||||
|
||||
if (client->rps_req_pending == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto nreq = session->max_concurrent_streams() - client->rps_req_inflight;
|
||||
if (nreq == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
nreq = config.is_timing_based_mode() ? std::max(nreq, client->req_left)
|
||||
: std::min(nreq, client->req_left);
|
||||
nreq = std::min(nreq, client->rps_req_pending);
|
||||
|
||||
client->rps_req_inflight += nreq;
|
||||
client->rps_req_pending -= nreq;
|
||||
|
||||
for (; nreq > 0; --nreq) {
|
||||
if (client->submit_request() != 0) {
|
||||
client->process_request_failure();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
client->signal_write();
|
||||
}
|
||||
} // 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
|
||||
@@ -362,6 +446,9 @@ Client::Client(uint32_t id, Worker *worker, size_t req_todo)
|
||||
cstat{},
|
||||
worker(worker),
|
||||
ssl(nullptr),
|
||||
#ifdef ENABLE_HTTP3
|
||||
quic{},
|
||||
#endif // ENABLE_HTTP3
|
||||
next_addr(config.addrs),
|
||||
current_addr(nullptr),
|
||||
reqidx(0),
|
||||
@@ -373,8 +460,12 @@ Client::Client(uint32_t id, Worker *worker, size_t req_todo)
|
||||
req_done(0),
|
||||
id(id),
|
||||
fd(-1),
|
||||
local_addr{},
|
||||
new_connection_requested(false),
|
||||
final(false) {
|
||||
final(false),
|
||||
rps_duration_started(0),
|
||||
rps_req_pending(0),
|
||||
rps_req_inflight(0) {
|
||||
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
|
||||
@@ -396,11 +487,25 @@ Client::Client(uint32_t id, Worker *worker, size_t req_todo)
|
||||
|
||||
ev_timer_init(&request_timeout_watcher, client_request_timeout_cb, 0., 0.);
|
||||
request_timeout_watcher.data = this;
|
||||
|
||||
ev_timer_init(&rps_watcher, rps_cb, 0., 0.);
|
||||
rps_watcher.data = this;
|
||||
|
||||
#ifdef ENABLE_HTTP3
|
||||
ev_timer_init(&quic.pkt_timer, quic_pkt_timeout_cb, 0., 0.);
|
||||
quic.pkt_timer.data = this;
|
||||
#endif // ENABLE_HTTP3
|
||||
}
|
||||
|
||||
Client::~Client() {
|
||||
disconnect();
|
||||
|
||||
#ifdef ENABLE_HTTP3
|
||||
if (config.is_quic()) {
|
||||
quic_free();
|
||||
}
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
if (ssl) {
|
||||
SSL_free(ssl);
|
||||
}
|
||||
@@ -413,26 +518,59 @@ int Client::do_read() { return readfn(*this); }
|
||||
int Client::do_write() { return writefn(*this); }
|
||||
|
||||
int Client::make_socket(addrinfo *addr) {
|
||||
fd = util::create_nonblock_socket(addr->ai_family);
|
||||
if (fd == -1) {
|
||||
return -1;
|
||||
}
|
||||
if (config.scheme == "https") {
|
||||
if (!ssl) {
|
||||
ssl = SSL_new(worker->ssl_ctx);
|
||||
int rv;
|
||||
|
||||
if (config.is_quic()) {
|
||||
#ifdef ENABLE_HTTP3
|
||||
fd = util::create_nonblock_udp_socket(addr->ai_family);
|
||||
if (fd == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto config = worker->config;
|
||||
|
||||
if (!util::numeric_host(config->host.c_str())) {
|
||||
SSL_set_tlsext_host_name(ssl, config->host.c_str());
|
||||
rv = util::bind_any_addr_udp(fd, addr->ai_family);
|
||||
if (rv != 0) {
|
||||
close(fd);
|
||||
fd = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
SSL_set_fd(ssl, fd);
|
||||
SSL_set_connect_state(ssl);
|
||||
socklen_t addrlen = sizeof(local_addr.su.storage);
|
||||
rv = getsockname(fd, &local_addr.su.sa, &addrlen);
|
||||
if (rv == -1) {
|
||||
return -1;
|
||||
}
|
||||
local_addr.len = addrlen;
|
||||
|
||||
if (quic_init(&local_addr.su.sa, local_addr.len, addr->ai_addr,
|
||||
addr->ai_addrlen) != 0) {
|
||||
std::cerr << "quic_init failed" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
#endif // ENABLE_HTTP3
|
||||
} else {
|
||||
fd = util::create_nonblock_socket(addr->ai_family);
|
||||
if (fd == -1) {
|
||||
return -1;
|
||||
}
|
||||
if (config.scheme == "https") {
|
||||
if (!ssl) {
|
||||
ssl = SSL_new(worker->ssl_ctx);
|
||||
}
|
||||
|
||||
SSL_set_fd(ssl, fd);
|
||||
SSL_set_connect_state(ssl);
|
||||
}
|
||||
}
|
||||
|
||||
auto rv = ::connect(fd, addr->ai_addr, addr->ai_addrlen);
|
||||
if (ssl && !util::numeric_host(config.host.c_str())) {
|
||||
SSL_set_tlsext_host_name(ssl, config.host.c_str());
|
||||
}
|
||||
|
||||
if (config.is_quic()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rv = ::connect(fd, addr->ai_addr, addr->ai_addrlen);
|
||||
if (rv != 0 && errno != EINPROGRESS) {
|
||||
if (ssl) {
|
||||
SSL_free(ssl);
|
||||
@@ -489,13 +627,22 @@ int Client::connect() {
|
||||
current_addr = addr;
|
||||
}
|
||||
|
||||
writefn = &Client::connected;
|
||||
|
||||
ev_io_set(&rev, fd, EV_READ);
|
||||
ev_io_set(&wev, fd, EV_WRITE);
|
||||
|
||||
ev_io_start(worker->loop, &wev);
|
||||
|
||||
if (config.is_quic()) {
|
||||
#ifdef ENABLE_HTTP3
|
||||
ev_io_start(worker->loop, &rev);
|
||||
|
||||
readfn = &Client::read_quic;
|
||||
writefn = &Client::write_quic;
|
||||
#endif // ENABLE_HTTP3
|
||||
} else {
|
||||
writefn = &Client::connected;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -550,8 +697,18 @@ void Client::fail() {
|
||||
void Client::disconnect() {
|
||||
record_client_end_time();
|
||||
|
||||
#ifdef ENABLE_HTTP3
|
||||
if (config.is_quic()) {
|
||||
quic_close_connection();
|
||||
}
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
#ifdef ENABLE_HTTP3
|
||||
ev_timer_stop(worker->loop, &quic.pkt_timer);
|
||||
#endif // ENABLE_HTTP3
|
||||
ev_timer_stop(worker->loop, &conn_inactivity_watcher);
|
||||
ev_timer_stop(worker->loop, &conn_active_watcher);
|
||||
ev_timer_stop(worker->loop, &rps_watcher);
|
||||
ev_timer_stop(worker->loop, &request_timeout_watcher);
|
||||
streams.clear();
|
||||
session.reset();
|
||||
@@ -672,6 +829,16 @@ void print_server_tmp_key(SSL *ssl) {
|
||||
std::cout << "DH " << EVP_PKEY_bits(key) << " bits" << std::endl;
|
||||
break;
|
||||
case EVP_PKEY_EC: {
|
||||
# if OPENSSL_3_0_0_API
|
||||
std::array<char, 64> curve_name;
|
||||
const char *cname;
|
||||
if (!EVP_PKEY_get_utf8_string_param(key, "group", curve_name.data(),
|
||||
curve_name.size(), nullptr)) {
|
||||
cname = "<unknown>";
|
||||
} else {
|
||||
cname = curve_name.data();
|
||||
}
|
||||
# else // !OPENSSL_3_0_0_API
|
||||
auto ec = EVP_PKEY_get1_EC_KEY(key);
|
||||
auto ec_del = defer(EC_KEY_free, ec);
|
||||
auto nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec));
|
||||
@@ -679,6 +846,7 @@ void print_server_tmp_key(SSL *ssl) {
|
||||
if (!cname) {
|
||||
cname = OBJ_nid2sn(nid);
|
||||
}
|
||||
# endif // !OPENSSL_3_0_0_API
|
||||
|
||||
std::cout << "ECDH " << cname << " " << EVP_PKEY_bits(key) << " bits"
|
||||
<< std::endl;
|
||||
@@ -711,7 +879,14 @@ void Client::report_app_info() {
|
||||
}
|
||||
|
||||
void Client::terminate_session() {
|
||||
session->terminate();
|
||||
#ifdef ENABLE_HTTP3
|
||||
if (config.is_quic()) {
|
||||
quic.close_requested = true;
|
||||
}
|
||||
#endif // ENABLE_HTTP3
|
||||
if (session) {
|
||||
session->terminate();
|
||||
}
|
||||
// http1 session needs writecb to tear down session.
|
||||
signal_write();
|
||||
}
|
||||
@@ -866,8 +1041,18 @@ void Client::on_stream_close(int32_t stream_id, bool success, bool final) {
|
||||
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();
|
||||
} else if (!config.rps_enabled()) {
|
||||
if (submit_request() != 0) {
|
||||
process_request_failure();
|
||||
}
|
||||
} else if (rps_req_pending) {
|
||||
--rps_req_pending;
|
||||
if (submit_request() != 0) {
|
||||
process_request_failure();
|
||||
}
|
||||
} else {
|
||||
assert(rps_req_inflight);
|
||||
--rps_req_inflight;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -899,7 +1084,15 @@ int Client::connection_made() {
|
||||
|
||||
if (next_proto) {
|
||||
auto proto = StringRef{next_proto, next_proto_len};
|
||||
if (util::check_h2_is_selected(proto)) {
|
||||
if (config.is_quic()) {
|
||||
#ifdef ENABLE_HTTP3
|
||||
assert(session);
|
||||
if (!util::streq(StringRef{&NGHTTP3_ALPN_H3[1]}, proto) &&
|
||||
!util::streq_l("h3-29", proto)) {
|
||||
return -1;
|
||||
}
|
||||
#endif // ENABLE_HTTP3
|
||||
} else if (util::check_h2_is_selected(proto)) {
|
||||
session = std::make_unique<Http2Session>(this);
|
||||
} else if (util::streq(NGHTTP2_H1_1, proto)) {
|
||||
session = std::make_unique<Http1Session>(this);
|
||||
@@ -908,6 +1101,9 @@ int Client::connection_made() {
|
||||
// Just assign next_proto to selected_proto anyway to show the
|
||||
// negotiation result.
|
||||
selected_proto = proto.str();
|
||||
} else if (config.is_quic()) {
|
||||
std::cerr << "QUIC requires ALPN negotiation" << std::endl;
|
||||
return -1;
|
||||
} else {
|
||||
std::cout << "No protocol negotiated. Fallback behaviour may be activated"
|
||||
<< std::endl;
|
||||
@@ -962,10 +1158,25 @@ int Client::connection_made() {
|
||||
|
||||
record_connect_time();
|
||||
|
||||
if (!config.timing_script) {
|
||||
if (config.rps_enabled()) {
|
||||
rps_watcher.repeat = std::max(0.01, 1. / config.rps);
|
||||
ev_timer_again(worker->loop, &rps_watcher);
|
||||
rps_duration_started = ev_now(worker->loop);
|
||||
}
|
||||
|
||||
if (config.rps_enabled()) {
|
||||
assert(req_left);
|
||||
|
||||
++rps_req_inflight;
|
||||
|
||||
if (submit_request() != 0) {
|
||||
process_request_failure();
|
||||
}
|
||||
} else if (!config.timing_script) {
|
||||
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();
|
||||
@@ -1206,6 +1417,46 @@ int Client::write_tls() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_HTTP3
|
||||
int Client::write_udp(const sockaddr *addr, socklen_t addrlen,
|
||||
const uint8_t *data, size_t datalen, size_t gso_size) {
|
||||
iovec msg_iov;
|
||||
msg_iov.iov_base = const_cast<uint8_t *>(data);
|
||||
msg_iov.iov_len = datalen;
|
||||
|
||||
msghdr msg{};
|
||||
msg.msg_name = const_cast<sockaddr *>(addr);
|
||||
msg.msg_namelen = addrlen;
|
||||
msg.msg_iov = &msg_iov;
|
||||
msg.msg_iovlen = 1;
|
||||
|
||||
# ifdef UDP_SEGMENT
|
||||
std::array<uint8_t, CMSG_SPACE(sizeof(uint16_t))> msg_ctrl{};
|
||||
if (gso_size && datalen > gso_size) {
|
||||
msg.msg_control = msg_ctrl.data();
|
||||
msg.msg_controllen = msg_ctrl.size();
|
||||
|
||||
auto cm = CMSG_FIRSTHDR(&msg);
|
||||
cm->cmsg_level = SOL_UDP;
|
||||
cm->cmsg_type = UDP_SEGMENT;
|
||||
cm->cmsg_len = CMSG_LEN(sizeof(uint16_t));
|
||||
*(reinterpret_cast<uint16_t *>(CMSG_DATA(cm))) = gso_size;
|
||||
}
|
||||
# endif // UDP_SEGMENT
|
||||
|
||||
auto nwrite = sendmsg(fd, &msg, 0);
|
||||
if (nwrite < 0) {
|
||||
std::cerr << "sendto: errno=" << errno << std::endl;
|
||||
} else {
|
||||
++worker->stats.udp_dgram_sent;
|
||||
}
|
||||
|
||||
ev_io_stop(worker->loop, &wev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
void Client::record_request_time(RequestStat *req_stat) {
|
||||
req_stat->request_time = std::chrono::steady_clock::now();
|
||||
req_stat->request_wall_time = std::chrono::system_clock::now();
|
||||
@@ -1324,7 +1575,7 @@ Worker::~Worker() {
|
||||
|
||||
void Worker::stop_all_clients() {
|
||||
for (auto client : clients) {
|
||||
if (client && client->session) {
|
||||
if (client) {
|
||||
client->terminate_session();
|
||||
}
|
||||
}
|
||||
@@ -1859,6 +2110,7 @@ Options:
|
||||
Default: 1
|
||||
-w, --window-bits=<N>
|
||||
Sets the stream level initial window size to (2**<N>)-1.
|
||||
For QUIC, <N> is capped to 26 (roughly 64MiB).
|
||||
Default: )"
|
||||
<< config.window_bits << R"(
|
||||
-W, --connection-window-bits=<N>
|
||||
@@ -1869,10 +2121,15 @@ Options:
|
||||
-H, --header=<HEADER>
|
||||
Add/Override a header to the requests.
|
||||
--ciphers=<SUITE>
|
||||
Set allowed cipher list. The format of the string is
|
||||
described in OpenSSL ciphers(1).
|
||||
Set allowed cipher list for TLSv1.2 or ealier. The
|
||||
format of the string is described in OpenSSL ciphers(1).
|
||||
Default: )"
|
||||
<< config.ciphers << R"(
|
||||
--tls13-ciphers=<SUITE>
|
||||
Set allowed cipher list for TLSv1.3. The format of the
|
||||
string is described in OpenSSL ciphers(1).
|
||||
Default: )"
|
||||
<< config.tls13_ciphers << R"(
|
||||
-p, --no-tls-proto=<PROTOID>
|
||||
Specify ALPN identifier of the protocol to be used when
|
||||
accessing http URI without SSL/TLS.
|
||||
@@ -1903,7 +2160,7 @@ 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>
|
||||
-D, --duration=<DURATION>
|
||||
Specifies the main duration for the measurements in case
|
||||
of timing-based benchmarking. -D and -r are mutually
|
||||
exclusive.
|
||||
@@ -1943,7 +2200,8 @@ Options:
|
||||
port defined in the first URI are used solely. Values
|
||||
contained in other URIs, if present, are ignored.
|
||||
Definition of a base URI overrides all scheme, host or
|
||||
port values.
|
||||
port values. --timing-script-file and --rps are
|
||||
mutually exclusive.
|
||||
-B, --base-uri=(<URI>|unix:<PATH>)
|
||||
Specify URI from which the scheme, host and port will be
|
||||
used for all requests. The base URI overrides all
|
||||
@@ -1985,9 +2243,26 @@ Options:
|
||||
response time when using one worker thread, but may
|
||||
appear slightly out of order with multiple threads due
|
||||
to buffering. Status code is -1 for failed streams.
|
||||
--qlog-file-base=<PATH>
|
||||
Enable qlog output and specify base file name for qlogs.
|
||||
Qlog is emitted for each connection.
|
||||
For a given base name "base", each output file name
|
||||
becomes "base.M.N.qlog" where M is worker ID and N is
|
||||
client ID (e.g. "base.0.3.qlog").
|
||||
Only effective in QUIC runs.
|
||||
--connect-to=<HOST>[:<PORT>]
|
||||
Host and port to connect instead of using the authority
|
||||
in <URI>.
|
||||
--rps=<N> Specify request per second for each client. --rps and
|
||||
--timing-script-file are mutually exclusive.
|
||||
--groups=<GROUPS>
|
||||
Specify the supported groups.
|
||||
Default: )"
|
||||
<< config.groups << R"(
|
||||
--no-udp-gso
|
||||
Disable UDP GSO.
|
||||
--max-udp-payload-size=<SIZE>
|
||||
Specify the maximum outgoing UDP datagram payload size.
|
||||
-v, --verbose
|
||||
Output debug information.
|
||||
--version Display version information and exit.
|
||||
@@ -2015,6 +2290,7 @@ int main(int argc, char **argv) {
|
||||
|
||||
std::string datafile;
|
||||
std::string logfile;
|
||||
std::string qlog_base;
|
||||
bool nreqs_set_manually = false;
|
||||
while (1) {
|
||||
static int flag = 0;
|
||||
@@ -2047,6 +2323,12 @@ int main(int argc, char **argv) {
|
||||
{"warm-up-time", required_argument, &flag, 9},
|
||||
{"log-file", required_argument, &flag, 10},
|
||||
{"connect-to", required_argument, &flag, 11},
|
||||
{"rps", required_argument, &flag, 12},
|
||||
{"groups", required_argument, &flag, 13},
|
||||
{"tls13-ciphers", required_argument, &flag, 14},
|
||||
{"no-udp-gso", no_argument, &flag, 15},
|
||||
{"qlog-file-base", required_argument, &flag, 16},
|
||||
{"max-udp-payload-size", required_argument, &flag, 17},
|
||||
{nullptr, 0, nullptr, 0}};
|
||||
int option_index = 0;
|
||||
auto c = getopt_long(argc, argv,
|
||||
@@ -2199,10 +2481,9 @@ int main(int argc, char **argv) {
|
||||
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;
|
||||
config.duration = util::parse_duration_with_unit(optarg);
|
||||
if (!std::isfinite(config.duration)) {
|
||||
std::cerr << "-D: value error " << optarg << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
@@ -2287,6 +2568,49 @@ int main(int argc, char **argv) {
|
||||
config.connect_to_port = port;
|
||||
break;
|
||||
}
|
||||
case 12: {
|
||||
char *end;
|
||||
auto v = std::strtod(optarg, &end);
|
||||
if (end == optarg || *end != '\0' || !std::isfinite(v) ||
|
||||
1. / v < 1e-6) {
|
||||
std::cerr << "--rps: Invalid value " << optarg << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
config.rps = v;
|
||||
break;
|
||||
}
|
||||
case 13:
|
||||
// --groups
|
||||
config.groups = optarg;
|
||||
break;
|
||||
case 14:
|
||||
// --tls13-ciphers
|
||||
config.tls13_ciphers = optarg;
|
||||
break;
|
||||
case 15:
|
||||
// --no-udp-gso
|
||||
config.no_udp_gso = true;
|
||||
break;
|
||||
case 16:
|
||||
// --qlog-file-base
|
||||
qlog_base = optarg;
|
||||
break;
|
||||
case 17: {
|
||||
// --max-udp-payload-size
|
||||
auto n = util::parse_uint_with_unit(optarg);
|
||||
if (n == -1) {
|
||||
std::cerr << "--max-udp-payload-size: bad option value: " << optarg
|
||||
<< std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (static_cast<uint64_t>(n) > 64_k) {
|
||||
std::cerr << "--max-udp-payload-size: must not exceed 65536"
|
||||
<< std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
config.max_udp_payload_size = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -2377,6 +2701,12 @@ int main(int argc, char **argv) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (config.timing_script && config.rps_enabled()) {
|
||||
std::cerr << "--timing-script-file, --rps: they are mutually exclusive."
|
||||
<< std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
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."
|
||||
@@ -2447,6 +2777,13 @@ int main(int argc, char **argv) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
config.data_length = data_stat.st_size;
|
||||
auto addr = mmap(nullptr, config.data_length, PROT_READ, MAP_SHARED,
|
||||
config.data_fd, 0);
|
||||
if (addr == MAP_FAILED) {
|
||||
std::cerr << "-d: Could not mmap file " << datafile << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
config.data = static_cast<uint8_t *>(addr);
|
||||
}
|
||||
|
||||
if (!logfile.empty()) {
|
||||
@@ -2458,6 +2795,18 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!qlog_base.empty()) {
|
||||
if (!config.is_quic()) {
|
||||
std::cerr
|
||||
<< "Warning: --qlog-file-base: only effective in quic, ignoring."
|
||||
<< std::endl;
|
||||
} else {
|
||||
#ifdef ENABLE_HTTP3
|
||||
config.qlog_file_base = qlog_base;
|
||||
#endif // ENABLE_HTTP3
|
||||
}
|
||||
}
|
||||
|
||||
struct sigaction act {};
|
||||
act.sa_handler = SIG_IGN;
|
||||
sigaction(SIGPIPE, &act, nullptr);
|
||||
@@ -2477,9 +2826,14 @@ 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::tls::ssl_ctx_set_proto_versions(
|
||||
ssl_ctx, nghttp2::tls::NGHTTP2_TLS_MIN_VERSION,
|
||||
nghttp2::tls::NGHTTP2_TLS_MAX_VERSION) != 0) {
|
||||
if (config.is_quic()) {
|
||||
#ifdef ENABLE_HTTP3
|
||||
SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION);
|
||||
SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION);
|
||||
#endif // ENABLE_HTTP3
|
||||
} else 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);
|
||||
}
|
||||
@@ -2491,6 +2845,20 @@ int main(int argc, char **argv) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
#if OPENSSL_1_1_1_API
|
||||
if (SSL_CTX_set_ciphersuites(ssl_ctx, config.tls13_ciphers.c_str()) == 0) {
|
||||
std::cerr << "SSL_CTX_set_ciphersuites with " << config.tls13_ciphers
|
||||
<< " failed: " << ERR_error_string(ERR_get_error(), nullptr)
|
||||
<< std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
#endif // OPENSSL_1_1_1_API
|
||||
|
||||
if (SSL_CTX_set1_groups_list(ssl_ctx, config.groups.c_str()) != 1) {
|
||||
std::cerr << "SSL_CTX_set1_groups_list failed" << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
#ifndef OPENSSL_NO_NEXTPROTONEG
|
||||
SSL_CTX_set_next_proto_select_cb(ssl_ctx, client_select_next_proto_cb,
|
||||
nullptr);
|
||||
@@ -2505,6 +2873,16 @@ int main(int argc, char **argv) {
|
||||
SSL_CTX_set_alpn_protos(ssl_ctx, proto_list.data(), proto_list.size());
|
||||
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||
|
||||
#if OPENSSL_1_1_1_API
|
||||
auto keylog_filename = getenv("SSLKEYLOGFILE");
|
||||
if (keylog_filename) {
|
||||
keylog_file.open(keylog_filename, std::ios_base::app);
|
||||
if (keylog_file) {
|
||||
SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
|
||||
}
|
||||
}
|
||||
#endif // OPENSSL_1_1_1_API
|
||||
|
||||
std::string user_agent = "h2load nghttp2/" NGHTTP2_VERSION;
|
||||
Headers shared_nva;
|
||||
shared_nva.emplace_back(":scheme", config.scheme);
|
||||
@@ -2723,6 +3101,8 @@ int main(int argc, char **argv) {
|
||||
stats.bytes_head += s.bytes_head;
|
||||
stats.bytes_head_decomp += s.bytes_head_decomp;
|
||||
stats.bytes_body += s.bytes_body;
|
||||
stats.udp_dgram_recv += s.udp_dgram_recv;
|
||||
stats.udp_dgram_sent += s.udp_dgram_sent;
|
||||
|
||||
for (size_t i = 0; i < stats.status.size(); ++i) {
|
||||
stats.status[i] += s.status[i];
|
||||
@@ -2781,30 +3161,37 @@ traffic: )" << util::utos_funit(stats.bytes_total)
|
||||
<< util::utos_funit(stats.bytes_head) << "B (" << stats.bytes_head
|
||||
<< ") headers (space savings " << header_space_savings * 100
|
||||
<< "%), " << util::utos_funit(stats.bytes_body) << "B ("
|
||||
<< stats.bytes_body << R"() data
|
||||
min max mean sd +/- sd
|
||||
<< stats.bytes_body << R"() data)" << std::endl;
|
||||
#ifdef ENABLE_HTTP3
|
||||
if (config.is_quic()) {
|
||||
std::cout << "UDP datagram: " << stats.udp_dgram_sent << " sent, "
|
||||
<< stats.udp_dgram_recv << " received" << std::endl;
|
||||
}
|
||||
#endif // ENABLE_HTTP3
|
||||
std::cout
|
||||
<< R"( 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) << "%"
|
||||
<< "\ntime for connect: " << std::setw(10)
|
||||
<< util::format_duration(ts.connect.min) << " " << std::setw(10)
|
||||
<< util::format_duration(ts.connect.max) << " " << std::setw(10)
|
||||
<< util::format_duration(ts.connect.mean) << " " << std::setw(10)
|
||||
<< util::format_duration(ts.connect.sd) << std::setw(9)
|
||||
<< util::dtos(ts.connect.within_sd) << "%"
|
||||
<< "\ntime to 1st byte: " << std::setw(10)
|
||||
<< util::format_duration(ts.ttfb.min) << " " << std::setw(10)
|
||||
<< util::format_duration(ts.ttfb.max) << " " << std::setw(10)
|
||||
<< util::format_duration(ts.ttfb.mean) << " " << std::setw(10)
|
||||
<< util::format_duration(ts.ttfb.sd) << std::setw(9)
|
||||
<< util::dtos(ts.ttfb.within_sd) << "%"
|
||||
<< "\nreq/s : " << std::setw(10) << ts.rps.min << " "
|
||||
<< std::setw(10) << ts.rps.max << " " << std::setw(10)
|
||||
<< ts.rps.mean << " " << std::setw(10) << ts.rps.sd << std::setw(9)
|
||||
<< util::dtos(ts.rps.within_sd) << "%" << std::endl;
|
||||
<< 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)
|
||||
<< util::format_duration(ts.connect.mean) << " " << std::setw(10)
|
||||
<< util::format_duration(ts.connect.sd) << std::setw(9)
|
||||
<< util::dtos(ts.connect.within_sd) << "%"
|
||||
<< "\ntime to 1st byte: " << std::setw(10)
|
||||
<< util::format_duration(ts.ttfb.min) << " " << std::setw(10)
|
||||
<< util::format_duration(ts.ttfb.max) << " " << std::setw(10)
|
||||
<< util::format_duration(ts.ttfb.mean) << " " << std::setw(10)
|
||||
<< util::format_duration(ts.ttfb.sd) << std::setw(9)
|
||||
<< util::dtos(ts.ttfb.within_sd) << "%"
|
||||
<< "\nreq/s : " << std::setw(10) << ts.rps.min << " "
|
||||
<< std::setw(10) << ts.rps.max << " " << std::setw(10) << ts.rps.mean
|
||||
<< " " << std::setw(10) << ts.rps.sd << std::setw(9)
|
||||
<< util::dtos(ts.rps.within_sd) << "%" << std::endl;
|
||||
|
||||
SSL_CTX_free(ssl_ctx);
|
||||
|
||||
|
||||
84
src/h2load.h
84
src/h2load.h
@@ -45,11 +45,19 @@
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
#ifdef ENABLE_HTTP3
|
||||
# include <ngtcp2/ngtcp2.h>
|
||||
# include <ngtcp2/ngtcp2_crypto.h>
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
#include <ev.h>
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
#include "http2.h"
|
||||
#ifdef ENABLE_HTTP3
|
||||
# include "quic.h"
|
||||
#endif // ENABLE_HTTP3
|
||||
#include "memchunk.h"
|
||||
#include "template.h"
|
||||
|
||||
@@ -72,8 +80,13 @@ struct Config {
|
||||
std::string connect_to_host;
|
||||
std::string ifile;
|
||||
std::string ciphers;
|
||||
std::string tls13_ciphers;
|
||||
// supported groups (or curves).
|
||||
std::string groups;
|
||||
// length of upload data
|
||||
int64_t data_length;
|
||||
// memory mapped upload data
|
||||
uint8_t *data;
|
||||
addrinfo *addrs;
|
||||
size_t nreqs;
|
||||
size_t nclients;
|
||||
@@ -100,6 +113,8 @@ struct Config {
|
||||
int data_fd;
|
||||
// file descriptor to write per-request stats to.
|
||||
int log_fd;
|
||||
// base file name of qlog output files
|
||||
std::string qlog_file_base;
|
||||
uint16_t port;
|
||||
uint16_t default_port;
|
||||
uint16_t connect_to_port;
|
||||
@@ -114,6 +129,12 @@ struct Config {
|
||||
// list of supported NPN/ALPN protocol strings in the order of
|
||||
// preference.
|
||||
std::vector<std::string> npn_list;
|
||||
// The number of request per second for each client.
|
||||
double rps;
|
||||
// Disables GSO for UDP connections.
|
||||
bool no_udp_gso;
|
||||
// The maximum UDP datagram payload size to send.
|
||||
size_t max_udp_payload_size;
|
||||
|
||||
Config();
|
||||
~Config();
|
||||
@@ -121,6 +142,8 @@ struct Config {
|
||||
bool is_rate_mode() const;
|
||||
bool is_timing_based_mode() const;
|
||||
bool has_base_uri() const;
|
||||
bool rps_enabled() const;
|
||||
bool is_quic() const;
|
||||
};
|
||||
|
||||
struct RequestStat {
|
||||
@@ -215,8 +238,12 @@ struct Stats {
|
||||
std::array<size_t, 6> status;
|
||||
// The statistics per request
|
||||
std::vector<RequestStat> req_stats;
|
||||
// THe statistics per client
|
||||
// The statistics per client
|
||||
std::vector<ClientStat> client_stats;
|
||||
// The number of UDP datagrams received.
|
||||
size_t udp_dgram_recv;
|
||||
// The number of UDP datagrams sent.
|
||||
size_t udp_dgram_sent;
|
||||
};
|
||||
|
||||
enum ClientState { CLIENT_IDLE, CLIENT_CONNECTED };
|
||||
@@ -306,6 +333,15 @@ struct Client {
|
||||
std::function<int(Client &)> readfn, writefn;
|
||||
Worker *worker;
|
||||
SSL *ssl;
|
||||
#ifdef ENABLE_HTTP3
|
||||
struct {
|
||||
ev_timer pkt_timer;
|
||||
ngtcp2_conn *conn;
|
||||
quic::Error last_error;
|
||||
bool close_requested;
|
||||
FILE *qlog_file;
|
||||
} quic;
|
||||
#endif // ENABLE_HTTP3
|
||||
ev_timer request_timeout_watcher;
|
||||
addrinfo *next_addr;
|
||||
// Address for the current address. When try_new_connection() is
|
||||
@@ -329,6 +365,7 @@ struct Client {
|
||||
// The client id per worker
|
||||
uint32_t id;
|
||||
int fd;
|
||||
Address local_addr;
|
||||
ev_timer conn_active_watcher;
|
||||
ev_timer conn_inactivity_watcher;
|
||||
std::string selected_proto;
|
||||
@@ -336,6 +373,20 @@ struct Client {
|
||||
// true if the current connection will be closed, and no more new
|
||||
// request cannot be processed.
|
||||
bool final;
|
||||
// rps_watcher is a timer to invoke callback periodically to
|
||||
// generate a new request.
|
||||
ev_timer rps_watcher;
|
||||
// The timestamp that starts the period which contributes to the
|
||||
// next request generation.
|
||||
ev_tstamp rps_duration_started;
|
||||
// The number of requests allowed by rps, but limited by stream
|
||||
// concurrency.
|
||||
size_t rps_req_pending;
|
||||
// The number of in-flight streams. req_inflight has similar value
|
||||
// but it only measures requests made during Phase::MAIN_DURATION.
|
||||
// rps_req_inflight measures the number of requests in all phases,
|
||||
// and it is only used if --rps is given.
|
||||
size_t rps_req_inflight;
|
||||
|
||||
enum { ERR_CONNECT_FAIL = -100 };
|
||||
|
||||
@@ -402,6 +453,37 @@ struct Client {
|
||||
void record_client_end_time();
|
||||
|
||||
void signal_write();
|
||||
|
||||
#ifdef ENABLE_HTTP3
|
||||
// QUIC
|
||||
int quic_init(const sockaddr *local_addr, socklen_t local_addrlen,
|
||||
const sockaddr *remote_addr, socklen_t remote_addrlen);
|
||||
void quic_free();
|
||||
int read_quic();
|
||||
int write_quic();
|
||||
int write_udp(const sockaddr *addr, socklen_t addrlen, const uint8_t *data,
|
||||
size_t datalen, size_t gso_size);
|
||||
void quic_close_connection();
|
||||
|
||||
int quic_handshake_completed();
|
||||
int quic_recv_stream_data(uint32_t flags, int64_t stream_id,
|
||||
const uint8_t *data, size_t datalen);
|
||||
int quic_acked_stream_data_offset(int64_t stream_id, size_t datalen);
|
||||
int quic_stream_close(int64_t stream_id, uint64_t app_error_code);
|
||||
int quic_stream_reset(int64_t stream_id, uint64_t app_error_code);
|
||||
int quic_stream_stop_sending(int64_t stream_id, uint64_t app_error_code);
|
||||
int quic_extend_max_local_streams();
|
||||
|
||||
int quic_on_key(ngtcp2_crypto_level level, const uint8_t *rx_secret,
|
||||
const uint8_t *tx_secret, size_t secretlen);
|
||||
void quic_set_tls_alert(uint8_t alert);
|
||||
|
||||
void quic_write_client_handshake(ngtcp2_crypto_level level,
|
||||
const uint8_t *data, size_t datalen);
|
||||
int quic_pkt_timeout();
|
||||
void quic_restart_pkt_timer();
|
||||
void quic_write_qlog(const void *data, size_t datalen);
|
||||
#endif // ENABLE_HTTP3
|
||||
};
|
||||
|
||||
} // namespace h2load
|
||||
|
||||
@@ -105,23 +105,6 @@ int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int on_frame_not_send_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame, int lib_error_code,
|
||||
void *user_data) {
|
||||
if (frame->hd.type != NGHTTP2_HEADERS ||
|
||||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto client = static_cast<Client *>(user_data);
|
||||
// request was not sent. Mark it as error.
|
||||
client->on_stream_close(frame->hd.stream_id, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int before_frame_send_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame, void *user_data) {
|
||||
@@ -211,9 +194,6 @@ void Http2Session::on_connect() {
|
||||
nghttp2_session_callbacks_set_on_header_callback(callbacks,
|
||||
on_header_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_frame_not_send_callback(
|
||||
callbacks, on_frame_not_send_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_before_frame_send_callback(
|
||||
callbacks, before_frame_send_callback);
|
||||
|
||||
|
||||
427
src/h2load_http3_session.cc
Normal file
427
src/h2load_http3_session.cc
Normal file
@@ -0,0 +1,427 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2019 nghttp2 contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "h2load_http3_session.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <ngtcp2/ngtcp2.h>
|
||||
|
||||
#include "h2load.h"
|
||||
|
||||
namespace h2load {
|
||||
|
||||
Http3Session::Http3Session(Client *client)
|
||||
: client_(client), conn_(nullptr), npending_request_(0), reqidx_(0) {}
|
||||
|
||||
Http3Session::~Http3Session() { nghttp3_conn_del(conn_); }
|
||||
|
||||
void Http3Session::on_connect() {}
|
||||
|
||||
int Http3Session::submit_request() {
|
||||
if (npending_request_) {
|
||||
++npending_request_;
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto config = client_->worker->config;
|
||||
reqidx_ = client_->reqidx;
|
||||
|
||||
if (++client_->reqidx == config->nva.size()) {
|
||||
client_->reqidx = 0;
|
||||
}
|
||||
|
||||
auto stream_id = submit_request_internal();
|
||||
if (stream_id < 0) {
|
||||
if (stream_id == NGTCP2_ERR_STREAM_ID_BLOCKED) {
|
||||
++npending_request_;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace {
|
||||
nghttp3_ssize read_data(nghttp3_conn *conn, int64_t stream_id, nghttp3_vec *vec,
|
||||
size_t veccnt, uint32_t *pflags, void *user_data,
|
||||
void *stream_user_data) {
|
||||
auto s = static_cast<Http3Session *>(user_data);
|
||||
|
||||
s->read_data(vec, veccnt, pflags);
|
||||
|
||||
return 1;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void Http3Session::read_data(nghttp3_vec *vec, size_t veccnt,
|
||||
uint32_t *pflags) {
|
||||
assert(veccnt > 0);
|
||||
|
||||
auto config = client_->worker->config;
|
||||
|
||||
vec[0].base = config->data;
|
||||
vec[0].len = config->data_length;
|
||||
*pflags |= NGHTTP3_DATA_FLAG_EOF;
|
||||
}
|
||||
|
||||
int64_t Http3Session::submit_request_internal() {
|
||||
int rv;
|
||||
int64_t stream_id;
|
||||
|
||||
auto config = client_->worker->config;
|
||||
auto &nva = config->nva[reqidx_];
|
||||
|
||||
rv = ngtcp2_conn_open_bidi_stream(client_->quic.conn, &stream_id, nullptr);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nghttp3_data_reader dr{};
|
||||
dr.read_data = h2load::read_data;
|
||||
|
||||
rv = nghttp3_conn_submit_request(
|
||||
conn_, stream_id, reinterpret_cast<nghttp3_nv *>(nva.data()), nva.size(),
|
||||
config->data_fd == -1 ? nullptr : &dr, nullptr);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
client_->on_request(stream_id);
|
||||
auto req_stat = client_->get_req_stat(stream_id);
|
||||
assert(req_stat);
|
||||
client_->record_request_time(req_stat);
|
||||
|
||||
return stream_id;
|
||||
}
|
||||
|
||||
int Http3Session::on_read(const uint8_t *data, size_t len) { return -1; }
|
||||
|
||||
int Http3Session::on_write() { return -1; }
|
||||
|
||||
void Http3Session::terminate() {}
|
||||
|
||||
size_t Http3Session::max_concurrent_streams() {
|
||||
return (size_t)client_->worker->config->max_concurrent_streams;
|
||||
}
|
||||
|
||||
namespace {
|
||||
int stream_close(nghttp3_conn *conn, int64_t stream_id, uint64_t app_error_code,
|
||||
void *user_data, void *stream_user_data) {
|
||||
auto s = static_cast<Http3Session *>(user_data);
|
||||
if (s->stream_close(stream_id, app_error_code) != 0) {
|
||||
return NGHTTP3_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int Http3Session::stream_close(int64_t stream_id, uint64_t app_error_code) {
|
||||
if (!ngtcp2_is_bidi_stream(stream_id)) {
|
||||
assert(!ngtcp2_conn_is_local_stream(client_->quic.conn, stream_id));
|
||||
ngtcp2_conn_extend_max_streams_uni(client_->quic.conn, 1);
|
||||
}
|
||||
client_->on_stream_close(stream_id, app_error_code == NGHTTP3_H3_NO_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace {
|
||||
int recv_data(nghttp3_conn *conn, int64_t stream_id, const uint8_t *data,
|
||||
size_t datalen, void *user_data, void *stream_user_data) {
|
||||
auto s = static_cast<Http3Session *>(user_data);
|
||||
s->recv_data(stream_id, data, datalen);
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void Http3Session::recv_data(int64_t stream_id, const uint8_t *data,
|
||||
size_t datalen) {
|
||||
client_->record_ttfb();
|
||||
client_->worker->stats.bytes_body += datalen;
|
||||
consume(stream_id, datalen);
|
||||
}
|
||||
|
||||
namespace {
|
||||
int deferred_consume(nghttp3_conn *conn, int64_t stream_id, size_t nconsumed,
|
||||
void *user_data, void *stream_user_data) {
|
||||
auto s = static_cast<Http3Session *>(user_data);
|
||||
s->consume(stream_id, nconsumed);
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void Http3Session::consume(int64_t stream_id, size_t nconsumed) {
|
||||
ngtcp2_conn_extend_max_stream_offset(client_->quic.conn, stream_id,
|
||||
nconsumed);
|
||||
ngtcp2_conn_extend_max_offset(client_->quic.conn, nconsumed);
|
||||
}
|
||||
|
||||
namespace {
|
||||
int begin_headers(nghttp3_conn *conn, int64_t stream_id, void *user_data,
|
||||
void *stream_user_data) {
|
||||
auto s = static_cast<Http3Session *>(user_data);
|
||||
s->begin_headers(stream_id);
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void Http3Session::begin_headers(int64_t stream_id) {
|
||||
auto payloadlen = nghttp3_conn_get_frame_payload_left(conn_, stream_id);
|
||||
assert(payloadlen > 0);
|
||||
|
||||
client_->worker->stats.bytes_head += payloadlen;
|
||||
}
|
||||
|
||||
namespace {
|
||||
int recv_header(nghttp3_conn *conn, int64_t stream_id, int32_t token,
|
||||
nghttp3_rcbuf *name, nghttp3_rcbuf *value, uint8_t flags,
|
||||
void *user_data, void *stream_user_data) {
|
||||
auto s = static_cast<Http3Session *>(user_data);
|
||||
auto k = nghttp3_rcbuf_get_buf(name);
|
||||
auto v = nghttp3_rcbuf_get_buf(value);
|
||||
s->recv_header(stream_id, &k, &v);
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void Http3Session::recv_header(int64_t stream_id, const nghttp3_vec *name,
|
||||
const nghttp3_vec *value) {
|
||||
client_->on_header(stream_id, name->base, name->len, value->base, value->len);
|
||||
client_->worker->stats.bytes_head_decomp += name->len + value->len;
|
||||
}
|
||||
|
||||
namespace {
|
||||
int send_stop_sending(nghttp3_conn *conn, int64_t stream_id,
|
||||
uint64_t app_error_code, void *user_data,
|
||||
void *stream_user_data) {
|
||||
auto s = static_cast<Http3Session *>(user_data);
|
||||
if (s->send_stop_sending(stream_id, app_error_code) != 0) {
|
||||
return NGHTTP3_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int Http3Session::send_stop_sending(int64_t stream_id,
|
||||
uint64_t app_error_code) {
|
||||
auto rv = ngtcp2_conn_shutdown_stream_read(client_->quic.conn, stream_id,
|
||||
app_error_code);
|
||||
if (rv != 0) {
|
||||
std::cerr << "ngtcp2_conn_shutdown_stream_read: " << ngtcp2_strerror(rv)
|
||||
<< std::endl;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Http3Session::close_stream(int64_t stream_id, uint64_t app_error_code) {
|
||||
auto rv = nghttp3_conn_close_stream(conn_, stream_id, app_error_code);
|
||||
switch (rv) {
|
||||
case 0:
|
||||
return 0;
|
||||
case NGHTTP3_ERR_STREAM_NOT_FOUND:
|
||||
if (!ngtcp2_is_bidi_stream(stream_id)) {
|
||||
assert(!ngtcp2_conn_is_local_stream(client_->quic.conn, stream_id));
|
||||
ngtcp2_conn_extend_max_streams_uni(client_->quic.conn, 1);
|
||||
}
|
||||
return 0;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int Http3Session::shutdown_stream_read(int64_t stream_id) {
|
||||
auto rv = nghttp3_conn_shutdown_stream_read(conn_, stream_id);
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Http3Session::extend_max_local_streams() {
|
||||
auto config = client_->worker->config;
|
||||
|
||||
for (; npending_request_; --npending_request_) {
|
||||
auto stream_id = submit_request_internal();
|
||||
if (stream_id < 0) {
|
||||
if (stream_id == NGTCP2_ERR_STREAM_ID_BLOCKED) {
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (++reqidx_ == config->nva.size()) {
|
||||
reqidx_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Http3Session::init_conn() {
|
||||
int rv;
|
||||
|
||||
assert(conn_ == nullptr);
|
||||
|
||||
if (ngtcp2_conn_get_max_local_streams_uni(client_->quic.conn) < 3) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
nghttp3_callbacks callbacks{
|
||||
nullptr, // acked_stream_data
|
||||
h2load::stream_close,
|
||||
h2load::recv_data,
|
||||
h2load::deferred_consume,
|
||||
h2load::begin_headers,
|
||||
h2load::recv_header,
|
||||
nullptr, // end_headers
|
||||
nullptr, // begin_trailers
|
||||
h2load::recv_header,
|
||||
nullptr, // end_trailers
|
||||
h2load::send_stop_sending,
|
||||
};
|
||||
|
||||
auto config = client_->worker->config;
|
||||
|
||||
nghttp3_settings settings;
|
||||
nghttp3_settings_default(&settings);
|
||||
settings.qpack_max_table_capacity = config->header_table_size;
|
||||
settings.qpack_blocked_streams = 100;
|
||||
|
||||
auto mem = nghttp3_mem_default();
|
||||
|
||||
rv = nghttp3_conn_client_new(&conn_, &callbacks, &settings, mem, this);
|
||||
if (rv != 0) {
|
||||
std::cerr << "nghttp3_conn_client_new: " << nghttp3_strerror(rv)
|
||||
<< std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int64_t ctrl_stream_id;
|
||||
|
||||
rv = ngtcp2_conn_open_uni_stream(client_->quic.conn, &ctrl_stream_id, NULL);
|
||||
if (rv != 0) {
|
||||
std::cerr << "ngtcp2_conn_open_uni_stream: " << ngtcp2_strerror(rv)
|
||||
<< std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
rv = nghttp3_conn_bind_control_stream(conn_, ctrl_stream_id);
|
||||
if (rv != 0) {
|
||||
std::cerr << "nghttp3_conn_bind_control_stream: " << nghttp3_strerror(rv)
|
||||
<< std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int64_t qpack_enc_stream_id, qpack_dec_stream_id;
|
||||
|
||||
rv = ngtcp2_conn_open_uni_stream(client_->quic.conn, &qpack_enc_stream_id,
|
||||
NULL);
|
||||
if (rv != 0) {
|
||||
std::cerr << "ngtcp2_conn_open_uni_stream: " << ngtcp2_strerror(rv)
|
||||
<< std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
rv = ngtcp2_conn_open_uni_stream(client_->quic.conn, &qpack_dec_stream_id,
|
||||
NULL);
|
||||
if (rv != 0) {
|
||||
std::cerr << "ngtcp2_conn_open_uni_stream: " << ngtcp2_strerror(rv)
|
||||
<< std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
rv = nghttp3_conn_bind_qpack_streams(conn_, qpack_enc_stream_id,
|
||||
qpack_dec_stream_id);
|
||||
if (rv != 0) {
|
||||
std::cerr << "nghttp3_conn_bind_qpack_streams: " << nghttp3_strerror(rv)
|
||||
<< std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t Http3Session::read_stream(uint32_t flags, int64_t stream_id,
|
||||
const uint8_t *data, size_t datalen) {
|
||||
auto nconsumed = nghttp3_conn_read_stream(
|
||||
conn_, stream_id, data, datalen, flags & NGTCP2_STREAM_DATA_FLAG_FIN);
|
||||
if (nconsumed < 0) {
|
||||
std::cerr << "nghttp3_conn_read_stream: " << nghttp3_strerror(nconsumed)
|
||||
<< std::endl;
|
||||
client_->quic.last_error = quic::err_application(nconsumed);
|
||||
return -1;
|
||||
}
|
||||
return nconsumed;
|
||||
}
|
||||
|
||||
ssize_t Http3Session::write_stream(int64_t &stream_id, int &fin,
|
||||
nghttp3_vec *vec, size_t veccnt) {
|
||||
auto sveccnt =
|
||||
nghttp3_conn_writev_stream(conn_, &stream_id, &fin, vec, veccnt);
|
||||
if (sveccnt < 0) {
|
||||
client_->quic.last_error = quic::err_application(sveccnt);
|
||||
return -1;
|
||||
}
|
||||
return sveccnt;
|
||||
}
|
||||
|
||||
int Http3Session::block_stream(int64_t stream_id) {
|
||||
auto rv = nghttp3_conn_block_stream(conn_, stream_id);
|
||||
if (rv != 0) {
|
||||
client_->quic.last_error = quic::err_application(rv);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Http3Session::shutdown_stream_write(int64_t stream_id) {
|
||||
auto rv = nghttp3_conn_shutdown_stream_write(conn_, stream_id);
|
||||
if (rv != 0) {
|
||||
client_->quic.last_error = quic::err_application(rv);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Http3Session::add_write_offset(int64_t stream_id, size_t ndatalen) {
|
||||
auto rv = nghttp3_conn_add_write_offset(conn_, stream_id, ndatalen);
|
||||
if (rv != 0) {
|
||||
client_->quic.last_error = quic::err_application(rv);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Http3Session::add_ack_offset(int64_t stream_id, size_t datalen) {
|
||||
auto rv = nghttp3_conn_add_ack_offset(conn_, stream_id, datalen);
|
||||
if (rv != 0) {
|
||||
client_->quic.last_error = quic::err_application(rv);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace h2load
|
||||
81
src/h2load_http3_session.h
Normal file
81
src/h2load_http3_session.h
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2019 nghttp2 contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef H2LOAD_HTTP3_SESSION_H
|
||||
#define H2LOAD_HTTP3_SESSION_H
|
||||
|
||||
#include "h2load_session.h"
|
||||
|
||||
#include <nghttp3/nghttp3.h>
|
||||
|
||||
namespace h2load {
|
||||
|
||||
struct Client;
|
||||
|
||||
class Http3Session : public Session {
|
||||
public:
|
||||
Http3Session(Client *client);
|
||||
virtual ~Http3Session();
|
||||
virtual void on_connect();
|
||||
virtual int submit_request();
|
||||
virtual int on_read(const uint8_t *data, size_t len);
|
||||
virtual int on_write();
|
||||
virtual void terminate();
|
||||
virtual size_t max_concurrent_streams();
|
||||
|
||||
int init_conn();
|
||||
int stream_close(int64_t stream_id, uint64_t app_error_code);
|
||||
void recv_data(int64_t stream_id, const uint8_t *data, size_t datalen);
|
||||
void consume(int64_t stream_id, size_t nconsumed);
|
||||
void begin_headers(int64_t stream_id);
|
||||
void recv_header(int64_t stream_id, const nghttp3_vec *name,
|
||||
const nghttp3_vec *value);
|
||||
int send_stop_sending(int64_t stream_id, uint64_t app_error_code);
|
||||
|
||||
int close_stream(int64_t stream_id, uint64_t app_error_code);
|
||||
int shutdown_stream_read(int64_t stream_id);
|
||||
int extend_max_local_streams();
|
||||
int64_t submit_request_internal();
|
||||
|
||||
ssize_t read_stream(uint32_t flags, int64_t stream_id, const uint8_t *data,
|
||||
size_t datalen);
|
||||
ssize_t write_stream(int64_t &stream_id, int &fin, nghttp3_vec *vec,
|
||||
size_t veccnt);
|
||||
int block_stream(int64_t stream_id);
|
||||
int shutdown_stream_write(int64_t stream_id);
|
||||
int add_write_offset(int64_t stream_id, size_t ndatalen);
|
||||
int add_ack_offset(int64_t stream_id, size_t datalen);
|
||||
|
||||
void read_data(nghttp3_vec *vec, size_t veccnt, uint32_t *pflags);
|
||||
|
||||
private:
|
||||
Client *client_;
|
||||
nghttp3_conn *conn_;
|
||||
size_t npending_request_;
|
||||
size_t reqidx_;
|
||||
};
|
||||
|
||||
} // namespace h2load
|
||||
|
||||
#endif // H2LOAD_HTTP3_SESSION_H
|
||||
680
src/h2load_quic.cc
Normal file
680
src/h2load_quic.cc
Normal file
@@ -0,0 +1,680 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2019 nghttp2 contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "h2load_quic.h"
|
||||
|
||||
#include <netinet/udp.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <ngtcp2/ngtcp2_crypto_openssl.h>
|
||||
|
||||
#include <openssl/err.h>
|
||||
|
||||
#include "h2load_http3_session.h"
|
||||
|
||||
namespace h2load {
|
||||
|
||||
namespace {
|
||||
auto randgen = util::make_mt19937();
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int handshake_completed(ngtcp2_conn *conn, void *user_data) {
|
||||
auto c = static_cast<Client *>(user_data);
|
||||
|
||||
if (c->quic_handshake_completed() != 0) {
|
||||
return NGTCP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int Client::quic_handshake_completed() { return connection_made(); }
|
||||
|
||||
namespace {
|
||||
int recv_stream_data(ngtcp2_conn *conn, uint32_t flags, int64_t stream_id,
|
||||
uint64_t offset, const uint8_t *data, size_t datalen,
|
||||
void *user_data, void *stream_user_data) {
|
||||
auto c = static_cast<Client *>(user_data);
|
||||
if (c->quic_recv_stream_data(flags, stream_id, data, datalen) != 0) {
|
||||
// TODO Better to do this gracefully rather than
|
||||
// NGTCP2_ERR_CALLBACK_FAILURE. Perhaps, call
|
||||
// ngtcp2_conn_write_application_close() ?
|
||||
return NGTCP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int Client::quic_recv_stream_data(uint32_t flags, int64_t stream_id,
|
||||
const uint8_t *data, size_t datalen) {
|
||||
if (worker->current_phase == Phase::MAIN_DURATION) {
|
||||
worker->stats.bytes_total += datalen;
|
||||
}
|
||||
|
||||
auto s = static_cast<Http3Session *>(session.get());
|
||||
auto nconsumed = s->read_stream(flags, stream_id, data, datalen);
|
||||
if (nconsumed == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ngtcp2_conn_extend_max_stream_offset(quic.conn, stream_id, nconsumed);
|
||||
ngtcp2_conn_extend_max_offset(quic.conn, nconsumed);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace {
|
||||
int acked_stream_data_offset(ngtcp2_conn *conn, int64_t stream_id,
|
||||
uint64_t offset, uint64_t datalen, void *user_data,
|
||||
void *stream_user_data) {
|
||||
auto c = static_cast<Client *>(user_data);
|
||||
if (c->quic_acked_stream_data_offset(stream_id, datalen) != 0) {
|
||||
return NGTCP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int Client::quic_acked_stream_data_offset(int64_t stream_id, size_t datalen) {
|
||||
auto s = static_cast<Http3Session *>(session.get());
|
||||
if (s->add_ack_offset(stream_id, datalen) != 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace {
|
||||
int stream_close(ngtcp2_conn *conn, uint32_t flags, int64_t stream_id,
|
||||
uint64_t app_error_code, void *user_data,
|
||||
void *stream_user_data) {
|
||||
auto c = static_cast<Client *>(user_data);
|
||||
|
||||
if (!(flags & NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET)) {
|
||||
app_error_code = NGHTTP3_H3_NO_ERROR;
|
||||
}
|
||||
|
||||
if (c->quic_stream_close(stream_id, app_error_code) != 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int Client::quic_stream_close(int64_t stream_id, uint64_t app_error_code) {
|
||||
auto s = static_cast<Http3Session *>(session.get());
|
||||
if (s->close_stream(stream_id, app_error_code) != 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace {
|
||||
int stream_reset(ngtcp2_conn *conn, int64_t stream_id, uint64_t final_size,
|
||||
uint64_t app_error_code, void *user_data,
|
||||
void *stream_user_data) {
|
||||
auto c = static_cast<Client *>(user_data);
|
||||
if (c->quic_stream_reset(stream_id, app_error_code) != 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int Client::quic_stream_reset(int64_t stream_id, uint64_t app_error_code) {
|
||||
auto s = static_cast<Http3Session *>(session.get());
|
||||
if (s->shutdown_stream_read(stream_id) != 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace {
|
||||
int stream_stop_sending(ngtcp2_conn *conn, int64_t stream_id,
|
||||
uint64_t app_error_code, void *user_data,
|
||||
void *stream_user_data) {
|
||||
auto c = static_cast<Client *>(user_data);
|
||||
if (c->quic_stream_stop_sending(stream_id, app_error_code) != 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int Client::quic_stream_stop_sending(int64_t stream_id,
|
||||
uint64_t app_error_code) {
|
||||
auto s = static_cast<Http3Session *>(session.get());
|
||||
if (s->shutdown_stream_read(stream_id) != 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace {
|
||||
int extend_max_local_streams_bidi(ngtcp2_conn *conn, uint64_t max_streams,
|
||||
void *user_data) {
|
||||
auto c = static_cast<Client *>(user_data);
|
||||
|
||||
if (c->quic_extend_max_local_streams() != 0) {
|
||||
return NGTCP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int Client::quic_extend_max_local_streams() {
|
||||
auto s = static_cast<Http3Session *>(session.get());
|
||||
if (s->extend_max_local_streams() != 0) {
|
||||
return NGTCP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace {
|
||||
int get_new_connection_id(ngtcp2_conn *conn, ngtcp2_cid *cid, uint8_t *token,
|
||||
size_t cidlen, void *user_data) {
|
||||
auto dis = std::uniform_int_distribution<uint8_t>(
|
||||
0, std::numeric_limits<uint8_t>::max());
|
||||
auto f = [&dis]() { return dis(randgen); };
|
||||
|
||||
std::generate_n(cid->data, cidlen, f);
|
||||
cid->datalen = cidlen;
|
||||
std::generate_n(token, NGTCP2_STATELESS_RESET_TOKENLEN, f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void debug_log_printf(void *user_data, const char *fmt, ...) {
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void generate_cid(ngtcp2_cid &dest) {
|
||||
auto dis = std::uniform_int_distribution<uint8_t>(
|
||||
0, std::numeric_limits<uint8_t>::max());
|
||||
dest.datalen = 8;
|
||||
std::generate_n(dest.data, dest.datalen, [&dis]() { return dis(randgen); });
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
ngtcp2_tstamp timestamp(struct ev_loop *loop) {
|
||||
return ev_now(loop) * NGTCP2_SECONDS;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int set_encryption_secrets(SSL *ssl, OSSL_ENCRYPTION_LEVEL ossl_level,
|
||||
const uint8_t *rx_secret, const uint8_t *tx_secret,
|
||||
size_t secret_len) {
|
||||
auto c = static_cast<Client *>(SSL_get_app_data(ssl));
|
||||
|
||||
if (c->quic_on_key(
|
||||
ngtcp2_crypto_openssl_from_ossl_encryption_level(ossl_level),
|
||||
rx_secret, tx_secret, secret_len) != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int add_handshake_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL ossl_level,
|
||||
const uint8_t *data, size_t len) {
|
||||
auto c = static_cast<Client *>(SSL_get_app_data(ssl));
|
||||
c->quic_write_client_handshake(
|
||||
ngtcp2_crypto_openssl_from_ossl_encryption_level(ossl_level), data, len);
|
||||
return 1;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int flush_flight(SSL *ssl) { return 1; }
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int send_alert(SSL *ssl, enum ssl_encryption_level_t level, uint8_t alert) {
|
||||
auto c = static_cast<Client *>(SSL_get_app_data(ssl));
|
||||
c->quic_set_tls_alert(alert);
|
||||
return 1;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
auto quic_method = SSL_QUIC_METHOD{
|
||||
set_encryption_secrets,
|
||||
add_handshake_data,
|
||||
flush_flight,
|
||||
send_alert,
|
||||
};
|
||||
} // namespace
|
||||
|
||||
// qlog write callback -- excerpted from ngtcp2/examples/client_base.cc
|
||||
namespace {
|
||||
void qlog_write_cb(void *user_data, uint32_t flags, const void *data,
|
||||
size_t datalen) {
|
||||
auto c = static_cast<Client *>(user_data);
|
||||
c->quic_write_qlog(data, datalen);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void Client::quic_write_qlog(const void *data, size_t datalen) {
|
||||
assert(quic.qlog_file != nullptr);
|
||||
fwrite(data, 1, datalen, quic.qlog_file);
|
||||
}
|
||||
|
||||
int Client::quic_init(const sockaddr *local_addr, socklen_t local_addrlen,
|
||||
const sockaddr *remote_addr, socklen_t remote_addrlen) {
|
||||
int rv;
|
||||
|
||||
if (!ssl) {
|
||||
ssl = SSL_new(worker->ssl_ctx);
|
||||
|
||||
SSL_set_app_data(ssl, this);
|
||||
SSL_set_connect_state(ssl);
|
||||
SSL_set_quic_method(ssl, &quic_method);
|
||||
SSL_set_quic_use_legacy_codepoint(ssl, 0);
|
||||
}
|
||||
|
||||
auto callbacks = ngtcp2_callbacks{
|
||||
ngtcp2_crypto_client_initial_cb,
|
||||
nullptr, // recv_client_initial
|
||||
ngtcp2_crypto_recv_crypto_data_cb,
|
||||
h2load::handshake_completed,
|
||||
nullptr, // recv_version_negotiation
|
||||
ngtcp2_crypto_encrypt_cb,
|
||||
ngtcp2_crypto_decrypt_cb,
|
||||
ngtcp2_crypto_hp_mask_cb,
|
||||
h2load::recv_stream_data,
|
||||
h2load::acked_stream_data_offset,
|
||||
nullptr, // stream_open
|
||||
h2load::stream_close,
|
||||
nullptr, // recv_stateless_reset
|
||||
ngtcp2_crypto_recv_retry_cb,
|
||||
h2load::extend_max_local_streams_bidi,
|
||||
nullptr, // extend_max_local_streams_uni
|
||||
nullptr, // rand
|
||||
get_new_connection_id,
|
||||
nullptr, // remove_connection_id
|
||||
ngtcp2_crypto_update_key_cb,
|
||||
nullptr, // path_validation
|
||||
nullptr, // select_preferred_addr
|
||||
h2load::stream_reset,
|
||||
nullptr, // extend_max_remote_streams_bidi
|
||||
nullptr, // extend_max_remote_streams_uni
|
||||
nullptr, // extend_max_stream_data
|
||||
nullptr, // dcid_status
|
||||
nullptr, // handshake_confirmed
|
||||
nullptr, // recv_new_token
|
||||
ngtcp2_crypto_delete_crypto_aead_ctx_cb,
|
||||
ngtcp2_crypto_delete_crypto_cipher_ctx_cb,
|
||||
nullptr, // recv_datagram
|
||||
nullptr, // ack_datagram
|
||||
nullptr, // lost_datagram
|
||||
nullptr, // get_path_challenge_data
|
||||
h2load::stream_stop_sending,
|
||||
};
|
||||
|
||||
ngtcp2_cid scid, dcid;
|
||||
generate_cid(scid);
|
||||
generate_cid(dcid);
|
||||
|
||||
auto config = worker->config;
|
||||
|
||||
ngtcp2_settings settings;
|
||||
ngtcp2_settings_default(&settings);
|
||||
if (config->verbose) {
|
||||
settings.log_printf = debug_log_printf;
|
||||
}
|
||||
settings.initial_ts = timestamp(worker->loop);
|
||||
if (!config->qlog_file_base.empty()) {
|
||||
assert(quic.qlog_file == nullptr);
|
||||
auto path = config->qlog_file_base;
|
||||
path += '.';
|
||||
path += util::utos(worker->id);
|
||||
path += '.';
|
||||
path += util::utos(id);
|
||||
path += ".qlog";
|
||||
quic.qlog_file = fopen(path.c_str(), "w");
|
||||
if (quic.qlog_file == nullptr) {
|
||||
std::cerr << "Failed to open a qlog file: " << path << std::endl;
|
||||
return -1;
|
||||
}
|
||||
settings.qlog.write = qlog_write_cb;
|
||||
}
|
||||
if (config->max_udp_payload_size) {
|
||||
settings.max_udp_payload_size = config->max_udp_payload_size;
|
||||
settings.no_udp_payload_size_shaping = 1;
|
||||
}
|
||||
|
||||
ngtcp2_transport_params params;
|
||||
ngtcp2_transport_params_default(¶ms);
|
||||
auto max_stream_data =
|
||||
std::min((1 << 26) - 1, (1 << config->window_bits) - 1);
|
||||
params.initial_max_stream_data_bidi_local = max_stream_data;
|
||||
params.initial_max_stream_data_uni = max_stream_data;
|
||||
params.initial_max_data = (1 << config->connection_window_bits) - 1;
|
||||
params.initial_max_streams_bidi = 0;
|
||||
params.initial_max_streams_uni = 100;
|
||||
params.max_idle_timeout = 30 * NGTCP2_SECONDS;
|
||||
|
||||
auto path = ngtcp2_path{
|
||||
{local_addrlen, const_cast<sockaddr *>(local_addr)},
|
||||
{remote_addrlen, const_cast<sockaddr *>(remote_addr)},
|
||||
};
|
||||
|
||||
assert(config->npn_list.size());
|
||||
|
||||
uint32_t quic_version;
|
||||
|
||||
if (config->npn_list[0] == NGHTTP3_ALPN_H3) {
|
||||
quic_version = NGTCP2_PROTO_VER_V1;
|
||||
} else {
|
||||
quic_version = NGTCP2_PROTO_VER_MIN;
|
||||
}
|
||||
|
||||
rv = ngtcp2_conn_client_new(&quic.conn, &dcid, &scid, &path, quic_version,
|
||||
&callbacks, &settings, ¶ms, nullptr, this);
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ngtcp2_conn_set_tls_native_handle(quic.conn, ssl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Client::quic_free() {
|
||||
ngtcp2_conn_del(quic.conn);
|
||||
if (quic.qlog_file != nullptr) {
|
||||
fclose(quic.qlog_file);
|
||||
quic.qlog_file = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Client::quic_close_connection() {
|
||||
if (!quic.conn) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::array<uint8_t, NGTCP2_MAX_UDP_PAYLOAD_SIZE> buf;
|
||||
ngtcp2_ssize nwrite;
|
||||
ngtcp2_path_storage ps;
|
||||
ngtcp2_path_storage_zero(&ps);
|
||||
|
||||
switch (quic.last_error.type) {
|
||||
case quic::ErrorType::TransportVersionNegotiation:
|
||||
return;
|
||||
case quic::ErrorType::Transport:
|
||||
nwrite = ngtcp2_conn_write_connection_close(
|
||||
quic.conn, &ps.path, nullptr, buf.data(), buf.size(),
|
||||
quic.last_error.code, timestamp(worker->loop));
|
||||
break;
|
||||
case quic::ErrorType::Application:
|
||||
nwrite = ngtcp2_conn_write_application_close(
|
||||
quic.conn, &ps.path, nullptr, buf.data(), buf.size(),
|
||||
quic.last_error.code, timestamp(worker->loop));
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
if (nwrite < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
write_udp(reinterpret_cast<sockaddr *>(ps.path.remote.addr),
|
||||
ps.path.remote.addrlen, buf.data(), nwrite, 0);
|
||||
}
|
||||
|
||||
int Client::quic_on_key(ngtcp2_crypto_level level, const uint8_t *rx_secret,
|
||||
const uint8_t *tx_secret, size_t secretlen) {
|
||||
if (ngtcp2_crypto_derive_and_install_rx_key(quic.conn, nullptr, nullptr,
|
||||
nullptr, level, rx_secret,
|
||||
secretlen) != 0) {
|
||||
std::cerr << "ngtcp2_crypto_derive_and_install_rx_key() failed"
|
||||
<< std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ngtcp2_crypto_derive_and_install_tx_key(quic.conn, nullptr, nullptr,
|
||||
nullptr, level, tx_secret,
|
||||
secretlen) != 0) {
|
||||
std::cerr << "ngtcp2_crypto_derive_and_install_tx_key() failed"
|
||||
<< std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (level == NGTCP2_CRYPTO_LEVEL_APPLICATION) {
|
||||
auto s = std::make_unique<Http3Session>(this);
|
||||
if (s->init_conn() == -1) {
|
||||
return -1;
|
||||
}
|
||||
session = std::move(s);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Client::quic_set_tls_alert(uint8_t alert) {
|
||||
quic.last_error = quic::err_transport_tls(alert);
|
||||
}
|
||||
|
||||
void Client::quic_write_client_handshake(ngtcp2_crypto_level level,
|
||||
const uint8_t *data, size_t datalen) {
|
||||
assert(level < 2);
|
||||
|
||||
ngtcp2_conn_submit_crypto_data(quic.conn, level, data, datalen);
|
||||
}
|
||||
|
||||
void quic_pkt_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
|
||||
auto c = static_cast<Client *>(w->data);
|
||||
|
||||
if (c->quic_pkt_timeout() != 0) {
|
||||
c->fail();
|
||||
c->worker->free_client(c);
|
||||
delete c;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int Client::quic_pkt_timeout() {
|
||||
int rv;
|
||||
auto now = timestamp(worker->loop);
|
||||
|
||||
rv = ngtcp2_conn_handle_expiry(quic.conn, now);
|
||||
if (rv != 0) {
|
||||
quic.last_error = quic::err_transport(NGTCP2_ERR_INTERNAL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return write_quic();
|
||||
}
|
||||
|
||||
void Client::quic_restart_pkt_timer() {
|
||||
auto expiry = ngtcp2_conn_get_expiry(quic.conn);
|
||||
auto now = timestamp(worker->loop);
|
||||
auto t = expiry > now ? static_cast<ev_tstamp>(expiry - now) / NGTCP2_SECONDS
|
||||
: 1e-9;
|
||||
quic.pkt_timer.repeat = t;
|
||||
ev_timer_again(worker->loop, &quic.pkt_timer);
|
||||
}
|
||||
|
||||
int Client::read_quic() {
|
||||
std::array<uint8_t, 65536> buf;
|
||||
sockaddr_union su;
|
||||
socklen_t addrlen = sizeof(su);
|
||||
int rv;
|
||||
size_t pktcnt = 0;
|
||||
ngtcp2_pkt_info pi{};
|
||||
|
||||
for (;;) {
|
||||
auto nread =
|
||||
recvfrom(fd, buf.data(), buf.size(), MSG_DONTWAIT, &su.sa, &addrlen);
|
||||
if (nread == -1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
assert(quic.conn);
|
||||
|
||||
++worker->stats.udp_dgram_recv;
|
||||
|
||||
auto path = ngtcp2_path{
|
||||
{local_addr.len, &local_addr.su.sa},
|
||||
{addrlen, &su.sa},
|
||||
};
|
||||
|
||||
rv = ngtcp2_conn_read_pkt(quic.conn, &path, &pi, buf.data(), nread,
|
||||
timestamp(worker->loop));
|
||||
if (rv != 0) {
|
||||
std::cerr << "ngtcp2_conn_read_pkt: " << ngtcp2_strerror(rv) << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (++pktcnt == 100) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Client::write_quic() {
|
||||
ev_io_stop(worker->loop, &wev);
|
||||
|
||||
if (quic.close_requested) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::array<nghttp3_vec, 16> vec;
|
||||
size_t pktcnt = 0;
|
||||
auto max_udp_payload_size =
|
||||
ngtcp2_conn_get_path_max_udp_payload_size(quic.conn);
|
||||
size_t max_pktcnt =
|
||||
#ifdef UDP_SEGMENT
|
||||
worker->config->no_udp_gso
|
||||
? 1
|
||||
: std::min(static_cast<size_t>(10),
|
||||
static_cast<size_t>(64_k / max_udp_payload_size));
|
||||
#else // !UDP_SEGMENT
|
||||
1;
|
||||
#endif // !UDP_SEGMENT
|
||||
std::array<uint8_t, 64_k> buf;
|
||||
uint8_t *bufpos = buf.data();
|
||||
ngtcp2_path_storage ps;
|
||||
|
||||
ngtcp2_path_storage_zero(&ps);
|
||||
|
||||
auto s = static_cast<Http3Session *>(session.get());
|
||||
|
||||
for (;;) {
|
||||
int64_t stream_id = -1;
|
||||
int fin = 0;
|
||||
ssize_t sveccnt = 0;
|
||||
|
||||
if (session && ngtcp2_conn_get_max_data_left(quic.conn)) {
|
||||
sveccnt = s->write_stream(stream_id, fin, vec.data(), vec.size());
|
||||
if (sveccnt == -1) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
ngtcp2_ssize ndatalen;
|
||||
auto v = vec.data();
|
||||
auto vcnt = static_cast<size_t>(sveccnt);
|
||||
|
||||
uint32_t flags = NGTCP2_WRITE_STREAM_FLAG_MORE;
|
||||
if (fin) {
|
||||
flags |= NGTCP2_WRITE_STREAM_FLAG_FIN;
|
||||
}
|
||||
|
||||
auto nwrite = ngtcp2_conn_writev_stream(
|
||||
quic.conn, &ps.path, nullptr, bufpos, max_udp_payload_size, &ndatalen,
|
||||
flags, stream_id, reinterpret_cast<const ngtcp2_vec *>(v), vcnt,
|
||||
timestamp(worker->loop));
|
||||
if (nwrite < 0) {
|
||||
switch (nwrite) {
|
||||
case NGTCP2_ERR_STREAM_DATA_BLOCKED:
|
||||
assert(ndatalen == -1);
|
||||
if (s->block_stream(stream_id) != 0) {
|
||||
return -1;
|
||||
}
|
||||
continue;
|
||||
case NGTCP2_ERR_STREAM_SHUT_WR:
|
||||
assert(ndatalen == -1);
|
||||
if (s->shutdown_stream_write(stream_id) != 0) {
|
||||
return -1;
|
||||
}
|
||||
continue;
|
||||
case NGTCP2_ERR_WRITE_MORE:
|
||||
assert(ndatalen >= 0);
|
||||
if (s->add_write_offset(stream_id, ndatalen) != 0) {
|
||||
return -1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
quic.last_error = quic::err_transport(nwrite);
|
||||
return -1;
|
||||
} else if (ndatalen >= 0 && s->add_write_offset(stream_id, ndatalen) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
quic_restart_pkt_timer();
|
||||
|
||||
if (nwrite == 0) {
|
||||
if (bufpos - buf.data()) {
|
||||
write_udp(ps.path.remote.addr, ps.path.remote.addrlen, buf.data(),
|
||||
bufpos - buf.data(), max_udp_payload_size);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bufpos += nwrite;
|
||||
|
||||
// Assume that the path does not change.
|
||||
if (++pktcnt == max_pktcnt ||
|
||||
static_cast<size_t>(nwrite) < max_udp_payload_size) {
|
||||
write_udp(ps.path.remote.addr, ps.path.remote.addrlen, buf.data(),
|
||||
bufpos - buf.data(), max_udp_payload_size);
|
||||
signal_write();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace h2load
|
||||
38
src/h2load_quic.h
Normal file
38
src/h2load_quic.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2019 nghttp2 contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef H2LOAD_QUIC_H
|
||||
#define H2LOAD_QUIC_H
|
||||
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <ev.h>
|
||||
|
||||
#include "h2load.h"
|
||||
|
||||
namespace h2load {
|
||||
void quic_pkt_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents);
|
||||
} // namespace h2load
|
||||
|
||||
#endif // H2LOAD_QUIC_H
|
||||
47
src/http2.cc
47
src/http2.cc
@@ -1841,6 +1841,53 @@ StringRef normalize_path(BlockAllocator &balloc, const StringRef &path,
|
||||
query);
|
||||
}
|
||||
|
||||
StringRef normalize_path_colon(BlockAllocator &balloc, const StringRef &path,
|
||||
const StringRef &query) {
|
||||
// First, decode %XX for unreserved characters and ':', then do
|
||||
// http2::path_join
|
||||
|
||||
// We won't find %XX if length is less than 3.
|
||||
if (path.size() < 3 ||
|
||||
std::find(std::begin(path), std::end(path), '%') == std::end(path)) {
|
||||
return path_join(balloc, StringRef{}, StringRef{}, path, query);
|
||||
}
|
||||
|
||||
// includes last terminal NULL.
|
||||
auto result = make_byte_ref(balloc, path.size() + 1);
|
||||
auto p = result.base;
|
||||
|
||||
auto it = std::begin(path);
|
||||
for (; it + 2 < std::end(path);) {
|
||||
if (*it == '%') {
|
||||
if (util::is_hex_digit(*(it + 1)) && util::is_hex_digit(*(it + 2))) {
|
||||
auto c =
|
||||
(util::hex_to_uint(*(it + 1)) << 4) + util::hex_to_uint(*(it + 2));
|
||||
if (util::in_rfc3986_unreserved_chars(c) || c == ':') {
|
||||
*p++ = c;
|
||||
|
||||
it += 3;
|
||||
|
||||
continue;
|
||||
}
|
||||
*p++ = '%';
|
||||
*p++ = util::upcase(*(it + 1));
|
||||
*p++ = util::upcase(*(it + 2));
|
||||
|
||||
it += 3;
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
*p++ = *it++;
|
||||
}
|
||||
|
||||
p = std::copy(it, std::end(path), p);
|
||||
*p = '\0';
|
||||
|
||||
return path_join(balloc, StringRef{}, StringRef{}, StringRef{result.base, p},
|
||||
query);
|
||||
}
|
||||
|
||||
std::string normalize_path(const StringRef &path, const StringRef &query) {
|
||||
BlockAllocator balloc(1024, 1024);
|
||||
|
||||
|
||||
@@ -410,6 +410,12 @@ StringRef to_method_string(int method_token);
|
||||
StringRef normalize_path(BlockAllocator &balloc, const StringRef &path,
|
||||
const StringRef &query);
|
||||
|
||||
// normalize_path_colon is like normalize_path, but it additionally
|
||||
// does percent-decoding %3A in order to workaround the issue that ':'
|
||||
// cannot be included in backend pattern.
|
||||
StringRef normalize_path_colon(BlockAllocator &balloc, const StringRef &path,
|
||||
const StringRef &query);
|
||||
|
||||
std::string normalize_path(const StringRef &path, const StringRef &query);
|
||||
|
||||
StringRef rewrite_clean_path(BlockAllocator &balloc, const StringRef &src);
|
||||
|
||||
206
src/http3.cc
Normal file
206
src/http3.cc
Normal file
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2021 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "http3.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
namespace http3 {
|
||||
|
||||
namespace {
|
||||
nghttp3_nv make_nv_internal(const std::string &name, const std::string &value,
|
||||
bool never_index, uint8_t nv_flags) {
|
||||
uint8_t flags;
|
||||
|
||||
flags = nv_flags |
|
||||
(never_index ? NGHTTP3_NV_FLAG_NEVER_INDEX : NGHTTP3_NV_FLAG_NONE);
|
||||
|
||||
return {(uint8_t *)name.c_str(), (uint8_t *)value.c_str(), name.size(),
|
||||
value.size(), flags};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
nghttp3_nv make_nv_internal(const StringRef &name, const StringRef &value,
|
||||
bool never_index, uint8_t nv_flags) {
|
||||
uint8_t flags;
|
||||
|
||||
flags = nv_flags |
|
||||
(never_index ? NGHTTP3_NV_FLAG_NEVER_INDEX : NGHTTP3_NV_FLAG_NONE);
|
||||
|
||||
return {(uint8_t *)name.c_str(), (uint8_t *)value.c_str(), name.size(),
|
||||
value.size(), flags};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
nghttp3_nv make_nv(const std::string &name, const std::string &value,
|
||||
bool never_index) {
|
||||
return make_nv_internal(name, value, never_index, NGHTTP3_NV_FLAG_NONE);
|
||||
}
|
||||
|
||||
nghttp3_nv make_nv(const StringRef &name, const StringRef &value,
|
||||
bool never_index) {
|
||||
return make_nv_internal(name, value, never_index, NGHTTP3_NV_FLAG_NONE);
|
||||
}
|
||||
|
||||
nghttp3_nv make_nv_nocopy(const std::string &name, const std::string &value,
|
||||
bool never_index) {
|
||||
return make_nv_internal(name, value, never_index,
|
||||
NGHTTP3_NV_FLAG_NO_COPY_NAME |
|
||||
NGHTTP3_NV_FLAG_NO_COPY_VALUE);
|
||||
}
|
||||
|
||||
nghttp3_nv make_nv_nocopy(const StringRef &name, const StringRef &value,
|
||||
bool never_index) {
|
||||
return make_nv_internal(name, value, never_index,
|
||||
NGHTTP3_NV_FLAG_NO_COPY_NAME |
|
||||
NGHTTP3_NV_FLAG_NO_COPY_VALUE);
|
||||
}
|
||||
|
||||
namespace {
|
||||
void copy_headers_to_nva_internal(std::vector<nghttp3_nv> &nva,
|
||||
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) {
|
||||
case http2::HD_COOKIE:
|
||||
case http2::HD_CONNECTION:
|
||||
case http2::HD_HOST:
|
||||
case http2::HD_HTTP2_SETTINGS:
|
||||
case http2::HD_KEEP_ALIVE:
|
||||
case http2::HD_PROXY_CONNECTION:
|
||||
case http2::HD_SERVER:
|
||||
case http2::HD_TE:
|
||||
case http2::HD_TRANSFER_ENCODING:
|
||||
case http2::HD_UPGRADE:
|
||||
continue;
|
||||
case http2::HD_EARLY_DATA:
|
||||
if (flags & http2::HDOP_STRIP_EARLY_DATA) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case http2::HD_SEC_WEBSOCKET_ACCEPT:
|
||||
if (flags & http2::HDOP_STRIP_SEC_WEBSOCKET_ACCEPT) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case http2::HD_SEC_WEBSOCKET_KEY:
|
||||
if (flags & http2::HDOP_STRIP_SEC_WEBSOCKET_KEY) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case http2::HD_FORWARDED:
|
||||
if (flags & http2::HDOP_STRIP_FORWARDED) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (it_forwarded == std::end(headers)) {
|
||||
it_forwarded = it;
|
||||
continue;
|
||||
}
|
||||
|
||||
kv = &(*it_forwarded);
|
||||
it_forwarded = it;
|
||||
break;
|
||||
case http2::HD_X_FORWARDED_FOR:
|
||||
if (flags & http2::HDOP_STRIP_X_FORWARDED_FOR) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (it_xff == std::end(headers)) {
|
||||
it_xff = it;
|
||||
continue;
|
||||
}
|
||||
|
||||
kv = &(*it_xff);
|
||||
it_xff = it;
|
||||
break;
|
||||
case http2::HD_X_FORWARDED_PROTO:
|
||||
if (flags & http2::HDOP_STRIP_X_FORWARDED_PROTO) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (it_xfp == std::end(headers)) {
|
||||
it_xfp = it;
|
||||
continue;
|
||||
}
|
||||
|
||||
kv = &(*it_xfp);
|
||||
it_xfp = it;
|
||||
break;
|
||||
case http2::HD_VIA:
|
||||
if (flags & http2::HDOP_STRIP_VIA) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (it_via == std::end(headers)) {
|
||||
it_via = it;
|
||||
continue;
|
||||
}
|
||||
|
||||
kv = &(*it_via);
|
||||
it_via = it;
|
||||
break;
|
||||
}
|
||||
nva.push_back(
|
||||
make_nv_internal(kv->name, kv->value, kv->no_index, nv_flags));
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void copy_headers_to_nva(std::vector<nghttp3_nv> &nva,
|
||||
const HeaderRefs &headers, uint32_t flags) {
|
||||
copy_headers_to_nva_internal(nva, headers, NGHTTP3_NV_FLAG_NONE, flags);
|
||||
}
|
||||
|
||||
void copy_headers_to_nva_nocopy(std::vector<nghttp3_nv> &nva,
|
||||
const HeaderRefs &headers, uint32_t flags) {
|
||||
copy_headers_to_nva_internal(
|
||||
nva, headers,
|
||||
NGHTTP3_NV_FLAG_NO_COPY_NAME | NGHTTP3_NV_FLAG_NO_COPY_VALUE, flags);
|
||||
}
|
||||
|
||||
int check_nv(const uint8_t *name, size_t namelen, const uint8_t *value,
|
||||
size_t valuelen) {
|
||||
if (!nghttp3_check_header_name(name, namelen)) {
|
||||
return 0;
|
||||
}
|
||||
if (!nghttp3_check_header_value(value, valuelen)) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
} // namespace http3
|
||||
|
||||
} // namespace nghttp2
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user