mirror of
https://github.com/nghttp2/nghttp2.git
synced 2025-12-08 19:18:53 +08:00
Compare commits
529 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
01313c1241 | ||
|
|
99afea05b9 | ||
|
|
b99b83812b | ||
|
|
0b48448270 | ||
|
|
2a56a3d9ea | ||
|
|
1883bdaf1d | ||
|
|
82cd23e40b | ||
|
|
d56e167c54 | ||
|
|
3e5765831f | ||
|
|
b9c6162cd5 | ||
|
|
68510f1282 | ||
|
|
4b58b25c19 | ||
|
|
fbd9bcb00e | ||
|
|
682db00ba9 | ||
|
|
434e80dc7b | ||
|
|
5c93a113f3 | ||
|
|
6b4b7bef23 | ||
|
|
8d5c893929 | ||
|
|
b943fbb002 | ||
|
|
9a89db575a | ||
|
|
0fcfe16dc5 | ||
|
|
990f9ed4de | ||
|
|
0f4b0966ef | ||
|
|
ecfd593076 | ||
|
|
3c6b75fb2b | ||
|
|
d1f06b09cd | ||
|
|
3a94ad709c | ||
|
|
7ea8037ee1 | ||
|
|
064bfcc9d2 | ||
|
|
a0028ea8ef | ||
|
|
d174ffeb00 | ||
|
|
5a93bedf72 | ||
|
|
4a0dba08b9 | ||
|
|
b685747643 | ||
|
|
76a97b9718 | ||
|
|
ac1cc56fbb | ||
|
|
f604cbae70 | ||
|
|
91ae5291cc | ||
|
|
5770c6bd04 | ||
|
|
7492f628aa | ||
|
|
2f788aa214 | ||
|
|
cee22df073 | ||
|
|
e219d6a94f | ||
|
|
41e72064e0 | ||
|
|
f302f1a830 | ||
|
|
c0fc726955 | ||
|
|
041d9863c2 | ||
|
|
3c5ca63b6f | ||
|
|
53a41c953d | ||
|
|
c39f2b2c91 | ||
|
|
603b0ae501 | ||
|
|
9938a4e952 | ||
|
|
8997e4369d | ||
|
|
5a6d6ccbd4 | ||
|
|
426fbda799 | ||
|
|
16e91746d9 | ||
|
|
b9a9a23b1e | ||
|
|
8059380fb0 | ||
|
|
039d9db5a3 | ||
|
|
b9f41e34ef | ||
|
|
ca7288ae38 | ||
|
|
a789008f17 | ||
|
|
53142222fe | ||
|
|
f1bec6f05c | ||
|
|
506177e1bd | ||
|
|
91151f1f56 | ||
|
|
daec7c16d3 | ||
|
|
6e446934d4 | ||
|
|
9ab71305d1 | ||
|
|
ae1aac26a7 | ||
|
|
8eb2160890 | ||
|
|
a440bdf15e | ||
|
|
8004ea9726 | ||
|
|
5436c95788 | ||
|
|
467565589c | ||
|
|
8f45bf7b9e | ||
|
|
09939cf6bc | ||
|
|
e8053ac931 | ||
|
|
fff785178d | ||
|
|
af24f8394e | ||
|
|
62b9e4bb56 | ||
|
|
441f1cc282 | ||
|
|
f92110c54c | ||
|
|
1cb6d5cb6d | ||
|
|
3817798905 | ||
|
|
13a14ecda8 | ||
|
|
2b458666ba | ||
|
|
06a8d684a6 | ||
|
|
18d42b411b | ||
|
|
cbd878bbd5 | ||
|
|
49eeed8420 | ||
|
|
5f36d91afd | ||
|
|
a08ce38bcf | ||
|
|
5d204fc3aa | ||
|
|
84ead30e58 | ||
|
|
b11e1afc91 | ||
|
|
c3aa02f003 | ||
|
|
d142830109 | ||
|
|
22e41bab3f | ||
|
|
b0078a2379 | ||
|
|
50109bb307 | ||
|
|
aa1c8d1fa4 | ||
|
|
1de20c1232 | ||
|
|
8fe093de1d | ||
|
|
f004361ef2 | ||
|
|
d6db38a318 | ||
|
|
c88a5291b7 | ||
|
|
0d614cf103 | ||
|
|
29d6cfae80 | ||
|
|
c48a6e73e8 | ||
|
|
956c11388c | ||
|
|
5e8eb926f2 | ||
|
|
1e4f8f27fd | ||
|
|
0ea041e8cb | ||
|
|
e048deb64c | ||
|
|
8ece08e1a3 | ||
|
|
bdfb2b9a0d | ||
|
|
3fd37462bb | ||
|
|
1164e931c5 | ||
|
|
0e8afdb050 | ||
|
|
23119a6f12 | ||
|
|
8001f226b9 | ||
|
|
1c0fa46dfa | ||
|
|
f7455d48cc | ||
|
|
a86d78216c | ||
|
|
47c0b0326d | ||
|
|
0ca979b453 | ||
|
|
c87631c2e6 | ||
|
|
987aa2dd85 | ||
|
|
91b40d1e84 | ||
|
|
bc17f95c80 | ||
|
|
0069ca9ce8 | ||
|
|
fe750240c8 | ||
|
|
1795d3ea27 | ||
|
|
b933ee8e78 | ||
|
|
a3dcf1e004 | ||
|
|
b2411d949e | ||
|
|
19101f7f4a | ||
|
|
a2e4a1eb26 | ||
|
|
742d80aac4 | ||
|
|
473f1d71ff | ||
|
|
8f40bd4675 | ||
|
|
7add262721 | ||
|
|
4a218f1b79 | ||
|
|
419c03daa2 | ||
|
|
019f1e9fc7 | ||
|
|
53604782e5 | ||
|
|
689e8c0ee3 | ||
|
|
0173929538 | ||
|
|
f570757701 | ||
|
|
5473e870eb | ||
|
|
a41b7baf81 | ||
|
|
fcddb5c06c | ||
|
|
5d3544185c | ||
|
|
5dce9501a6 | ||
|
|
b313068cab | ||
|
|
08e8cc1915 | ||
|
|
52f3572d5b | ||
|
|
a804117c83 | ||
|
|
2b14e4a617 | ||
|
|
ad57435953 | ||
|
|
7702d38699 | ||
|
|
90914b38f1 | ||
|
|
8bfd900be5 | ||
|
|
40e8eaf5fd | ||
|
|
d3a606e9d9 | ||
|
|
2fb3d5fd1f | ||
|
|
ba795d86f0 | ||
|
|
e8107b68c8 | ||
|
|
94e69d5e30 | ||
|
|
d1391f1db7 | ||
|
|
b9174b828e | ||
|
|
a6cf6c00ea | ||
|
|
f6097ce6d0 | ||
|
|
1474f755cf | ||
|
|
dcdbd5ab20 | ||
|
|
cbc02bbc4c | ||
|
|
b5796c6b96 | ||
|
|
d80952a2bc | ||
|
|
2e8544f48d | ||
|
|
f26d436ee6 | ||
|
|
6052a86df1 | ||
|
|
9854e3746f | ||
|
|
7a50299cb0 | ||
|
|
7dba426db4 | ||
|
|
fcf0ceeac6 | ||
|
|
e253d8f6db | ||
|
|
8fb544523c | ||
|
|
3ae44ef2f3 | ||
|
|
730d47f7ad | ||
|
|
ea0ab938c4 | ||
|
|
9763ea768d | ||
|
|
8e3406ad20 | ||
|
|
aaf0dc825d | ||
|
|
8729d1e4c2 | ||
|
|
db6eec653b | ||
|
|
c654549d35 | ||
|
|
645897503d | ||
|
|
fe8f2a4603 | ||
|
|
0a4330ee3c | ||
|
|
d157744fb2 | ||
|
|
82320d6e55 | ||
|
|
7db1864766 | ||
|
|
a55a07940c | ||
|
|
37c01a0a4d | ||
|
|
2003be8dc5 | ||
|
|
bfac015d61 | ||
|
|
cd7258a7cd | ||
|
|
c3215af5f6 | ||
|
|
ba0f4d77a0 | ||
|
|
eb681c827d | ||
|
|
e82c53803f | ||
|
|
781d570ec1 | ||
|
|
fef9530ca5 | ||
|
|
ab1dd11705 | ||
|
|
7bf5be9c17 | ||
|
|
ac11ba32ee | ||
|
|
a20ad03f7b | ||
|
|
292f219900 | ||
|
|
55e550f08f | ||
|
|
32943a74b2 | ||
|
|
89291e4010 | ||
|
|
a8a66843db | ||
|
|
32b34c4b80 | ||
|
|
45d0d731eb | ||
|
|
125f62b71e | ||
|
|
eb6ba2a703 | ||
|
|
e559168bd7 | ||
|
|
959d05e6f8 | ||
|
|
e60183313b | ||
|
|
7c0a0c495d | ||
|
|
39eb1e4753 | ||
|
|
079682f313 | ||
|
|
e8513b3241 | ||
|
|
8bac2087cf | ||
|
|
ce1bf11d4b | ||
|
|
eb8138bfbd | ||
|
|
5180eb93e5 | ||
|
|
27fa4d310a | ||
|
|
adc22ec80b | ||
|
|
4bc9f2422b | ||
|
|
d695d2ccc0 | ||
|
|
5fa9dd7cd5 | ||
|
|
b352ae03a9 | ||
|
|
c93da867e0 | ||
|
|
556811ec64 | ||
|
|
403ece66e3 | ||
|
|
9a35dbc4ab | ||
|
|
6f70a53da6 | ||
|
|
93ee9e30d8 | ||
|
|
a84c319d62 | ||
|
|
467b419947 | ||
|
|
ee158fb0aa | ||
|
|
280c9dfcf3 | ||
|
|
e4c59dd164 | ||
|
|
f705b2aec4 | ||
|
|
dce20c3e6a | ||
|
|
e0a2353c56 | ||
|
|
c0ffed7788 | ||
|
|
21b48d24e4 | ||
|
|
e13c9102b8 | ||
|
|
14d8894b40 | ||
|
|
b607a22076 | ||
|
|
b8dafbdf5e | ||
|
|
3b03ff626a | ||
|
|
9614611969 | ||
|
|
f178b78816 | ||
|
|
381488a9bd | ||
|
|
0cbc78656e | ||
|
|
e180d8e594 | ||
|
|
cbb5da5285 | ||
|
|
03877c3752 | ||
|
|
54a3209cf5 | ||
|
|
9eb554a843 | ||
|
|
7036859823 | ||
|
|
60cb3f67f2 | ||
|
|
6b59609f9b | ||
|
|
a3c5ac4730 | ||
|
|
ed1ea91a6f | ||
|
|
209d1b0946 | ||
|
|
761cb97090 | ||
|
|
dcc7b23980 | ||
|
|
b9667fd209 | ||
|
|
d23105ccb7 | ||
|
|
30499005f8 | ||
|
|
f2cd057e89 | ||
|
|
2b465ee65f | ||
|
|
7e092a7658 | ||
|
|
d39b56adaa | ||
|
|
152a20a416 | ||
|
|
7ff0797535 | ||
|
|
ca57c2f6b6 | ||
|
|
d75ba74bbd | ||
|
|
9ff1925538 | ||
|
|
1915408096 | ||
|
|
45801883ba | ||
|
|
382a328ead | ||
|
|
babfa41424 | ||
|
|
204ff787fa | ||
|
|
2a68cc7076 | ||
|
|
b1f807abd1 | ||
|
|
2c830a4698 | ||
|
|
87ce5068bb | ||
|
|
7c794b8d93 | ||
|
|
0a406eab94 | ||
|
|
c67ccad74d | ||
|
|
5d59adc52b | ||
|
|
770cfcaae9 | ||
|
|
04dae32509 | ||
|
|
3e0813d407 | ||
|
|
c8b83d7024 | ||
|
|
d1285255eb | ||
|
|
dfbc6e6a57 | ||
|
|
2eab5d03fd | ||
|
|
fe634e5326 | ||
|
|
bac44d7ffb | ||
|
|
51b59bc8f0 | ||
|
|
2d10e31931 | ||
|
|
4cf7b3cebd | ||
|
|
e11834d1c9 | ||
|
|
8f22ff3032 | ||
|
|
8e94551881 | ||
|
|
6e1470c9d3 | ||
|
|
9416bf9079 | ||
|
|
24f83eef7c | ||
|
|
b7c0576eb5 | ||
|
|
5a48750e16 | ||
|
|
ae93f6345c | ||
|
|
9bba616426 | ||
|
|
1fe50f272b | ||
|
|
93023acc6c | ||
|
|
daf659c64e | ||
|
|
de2a855572 | ||
|
|
df401f57a2 | ||
|
|
6e178653a5 | ||
|
|
9cf1a0c77c | ||
|
|
ebdbe88bc0 | ||
|
|
74ec1d3377 | ||
|
|
1e4f288a7c | ||
|
|
6a21a6f148 | ||
|
|
661b99ec17 | ||
|
|
ecd143fcc2 | ||
|
|
bd9389b956 | ||
|
|
1a09cef0ef | ||
|
|
aa3a6d5916 | ||
|
|
bded1d1115 | ||
|
|
fa4f03525e | ||
|
|
4ee89e62fc | ||
|
|
ee65dea8af | ||
|
|
27609327ee | ||
|
|
958cd0de64 | ||
|
|
1d7601edfb | ||
|
|
8a0fdcfea9 | ||
|
|
75a1ad8bdb | ||
|
|
5bac7f0ca6 | ||
|
|
08fea5705a | ||
|
|
8e5b5d00e1 | ||
|
|
97e8482348 | ||
|
|
0cc73e279e | ||
|
|
54232c6542 | ||
|
|
ee2856f9bc | ||
|
|
737cea0b38 | ||
|
|
a8eeea0b18 | ||
|
|
a2bc88f6db | ||
|
|
5ce8ae79f0 | ||
|
|
d98e9a63d0 | ||
|
|
8e30adbca0 | ||
|
|
9adfd08848 | ||
|
|
2e3419ccbb | ||
|
|
5b6b6dc1b7 | ||
|
|
d0271a90b5 | ||
|
|
20ffe2b2a0 | ||
|
|
94c80e2507 | ||
|
|
efa344be98 | ||
|
|
2ba9a009fe | ||
|
|
c59ffa09e0 | ||
|
|
72f5e028d0 | ||
|
|
80b361dbb0 | ||
|
|
58254adb11 | ||
|
|
7f60e8a307 | ||
|
|
c31be5af4d | ||
|
|
292c01fda2 | ||
|
|
91f7d43e84 | ||
|
|
ce71e65aee | ||
|
|
1119701071 | ||
|
|
42122c270a | ||
|
|
1348621d9e | ||
|
|
8c5ea61376 | ||
|
|
c410f4055f | ||
|
|
1e86635572 | ||
|
|
62ede05c09 | ||
|
|
a067eb02a5 | ||
|
|
154876a17b | ||
|
|
f8c70993c0 | ||
|
|
03a2828fcf | ||
|
|
2fc0056ada | ||
|
|
9a33116526 | ||
|
|
29fcd7c946 | ||
|
|
189f122dd7 | ||
|
|
69c708be44 | ||
|
|
14e56fcd81 | ||
|
|
db67412511 | ||
|
|
76800dc8e7 | ||
|
|
7d282cd0bd | ||
|
|
49b8d1d88c | ||
|
|
4d93dd9d91 | ||
|
|
be1a513c59 | ||
|
|
20900b133e | ||
|
|
279fc2ad37 | ||
|
|
0ef99b90d9 | ||
|
|
1bd8f6a0e2 | ||
|
|
f70c142e10 | ||
|
|
b3fbf047b2 | ||
|
|
0cd8da2cd9 | ||
|
|
7fa0f2763e | ||
|
|
f381b13c91 | ||
|
|
6b9382d865 | ||
|
|
2c335dbc7a | ||
|
|
b3463b20a3 | ||
|
|
15bdf048cc | ||
|
|
992ca93533 | ||
|
|
b4ed3324c0 | ||
|
|
98fd6019cf | ||
|
|
6ccae48f7c | ||
|
|
6933e0ef54 | ||
|
|
a9ecdca08a | ||
|
|
af5bedd45f | ||
|
|
7097a31968 | ||
|
|
4122920dc6 | ||
|
|
9aed11e3dc | ||
|
|
9ea4905f68 | ||
|
|
c6cfcc3c30 | ||
|
|
4bc5e55113 | ||
|
|
566a252577 | ||
|
|
27c766cb04 | ||
|
|
41dd6d0205 | ||
|
|
db071ca35c | ||
|
|
e3af9d8bd3 | ||
|
|
73955f0519 | ||
|
|
5df21e3683 | ||
|
|
a6e1a40c05 | ||
|
|
e330520341 | ||
|
|
d13ed04b17 | ||
|
|
75a23c6c7e | ||
|
|
eaca5d83b0 | ||
|
|
9c1b5e8fb1 | ||
|
|
0c31dbb5d9 | ||
|
|
8a0b11e9e1 | ||
|
|
7ca2787cc8 | ||
|
|
cfbf907418 | ||
|
|
dd02c4cd9b | ||
|
|
93ee5bdba6 | ||
|
|
f2aa6f4e2b | ||
|
|
bc8a583184 | ||
|
|
502ff24568 | ||
|
|
47692d113c | ||
|
|
19c2805d0d | ||
|
|
df078dc004 | ||
|
|
e570225e97 | ||
|
|
8fffa05513 | ||
|
|
0d4120ce2c | ||
|
|
20de432725 | ||
|
|
325bb0115e | ||
|
|
cc3b41ec96 | ||
|
|
225b90eefd | ||
|
|
3931a0b04d | ||
|
|
70c0558443 | ||
|
|
f2bfe623fc | ||
|
|
80dcb565eb | ||
|
|
6d42b6697b | ||
|
|
bcbb2e8649 | ||
|
|
ba92935f64 | ||
|
|
402c262de5 | ||
|
|
03c4092862 | ||
|
|
a225bb29df | ||
|
|
f3a76d84f1 | ||
|
|
7a09feebc3 | ||
|
|
32ddca532a | ||
|
|
df875db989 | ||
|
|
1d138accb9 | ||
|
|
7e6019aef1 | ||
|
|
e20b417b84 | ||
|
|
df56b69060 | ||
|
|
b6d0a32d0e | ||
|
|
a82956d1d6 | ||
|
|
9c0760e3c1 | ||
|
|
4e71e9e2e8 | ||
|
|
521450c7ad | ||
|
|
aa57e91e85 | ||
|
|
4023c26cf1 | ||
|
|
a0f558ee3c | ||
|
|
4f0d03b4b9 | ||
|
|
88d7abcc23 | ||
|
|
409316018d | ||
|
|
d25b9da9f6 | ||
|
|
b48ceac56c | ||
|
|
34413d8d7c | ||
|
|
b7ccca4c47 | ||
|
|
e887b2516f | ||
|
|
be0f6dcaaf | ||
|
|
937bb9f768 | ||
|
|
a11fbf6e2f | ||
|
|
9aa914c756 | ||
|
|
e20d2ba9c1 | ||
|
|
c1be28684a | ||
|
|
fd07f5e142 | ||
|
|
99ca15cae0 | ||
|
|
3651467c71 | ||
|
|
c27ec6f57b | ||
|
|
485d04851c | ||
|
|
83728219db | ||
|
|
5d0bf4cc84 | ||
|
|
446f8f13aa | ||
|
|
96bb9c2018 | ||
|
|
a9b74261b6 | ||
|
|
89c3c08590 | ||
|
|
83309b6391 | ||
|
|
23dd428d65 | ||
|
|
b305495a75 | ||
|
|
727662257c | ||
|
|
b2f88f8fe3 | ||
|
|
44ac571037 | ||
|
|
5bff48a15a | ||
|
|
e4751a798a | ||
|
|
6fc12caa6d | ||
|
|
d00d4d647d | ||
|
|
5ff73de195 | ||
|
|
901de5fbce | ||
|
|
5847a56c40 |
57
.clang-format
Normal file
57
.clang-format
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
---
|
||||||
|
Language: Cpp
|
||||||
|
# BasedOnStyle: LLVM
|
||||||
|
AccessModifierOffset: -2
|
||||||
|
ConstructorInitializerIndentWidth: 4
|
||||||
|
AlignEscapedNewlinesLeft: false
|
||||||
|
AlignTrailingComments: true
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: true
|
||||||
|
AllowShortBlocksOnASingleLine: false
|
||||||
|
AllowShortIfStatementsOnASingleLine: false
|
||||||
|
AllowShortLoopsOnASingleLine: false
|
||||||
|
AllowShortFunctionsOnASingleLine: All
|
||||||
|
AlwaysBreakTemplateDeclarations: false
|
||||||
|
AlwaysBreakBeforeMultilineStrings: false
|
||||||
|
BreakBeforeBinaryOperators: false
|
||||||
|
BreakBeforeTernaryOperators: true
|
||||||
|
BreakConstructorInitializersBeforeComma: false
|
||||||
|
BinPackParameters: true
|
||||||
|
ColumnLimit: 80
|
||||||
|
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||||
|
DerivePointerAlignment: false
|
||||||
|
ExperimentalAutoDetectBinPacking: false
|
||||||
|
IndentCaseLabels: false
|
||||||
|
IndentWrappedFunctionNames: false
|
||||||
|
IndentFunctionDeclarationAfterType: false
|
||||||
|
MaxEmptyLinesToKeep: 1
|
||||||
|
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||||
|
NamespaceIndentation: None
|
||||||
|
ObjCSpaceAfterProperty: false
|
||||||
|
ObjCSpaceBeforeProtocolList: true
|
||||||
|
PenaltyBreakBeforeFirstCallParameter: 19
|
||||||
|
PenaltyBreakComment: 300
|
||||||
|
PenaltyBreakString: 1000
|
||||||
|
PenaltyBreakFirstLessLess: 120
|
||||||
|
PenaltyExcessCharacter: 1000000
|
||||||
|
PenaltyReturnTypeOnItsOwnLine: 60
|
||||||
|
PointerAlignment: Right
|
||||||
|
SpacesBeforeTrailingComments: 1
|
||||||
|
Cpp11BracedListStyle: true
|
||||||
|
Standard: Cpp11
|
||||||
|
IndentWidth: 2
|
||||||
|
TabWidth: 8
|
||||||
|
UseTab: Never
|
||||||
|
BreakBeforeBraces: Attach
|
||||||
|
SpacesInParentheses: false
|
||||||
|
SpacesInAngles: false
|
||||||
|
SpaceInEmptyParentheses: false
|
||||||
|
SpacesInCStyleCastParentheses: false
|
||||||
|
SpacesInContainerLiterals: true
|
||||||
|
SpaceBeforeAssignmentOperators: true
|
||||||
|
ContinuationIndentWidth: 4
|
||||||
|
CommentPragmas: '^ IWYU pragma:'
|
||||||
|
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
|
||||||
|
SpaceBeforeParens: ControlStatements
|
||||||
|
DisableFormat: false
|
||||||
|
...
|
||||||
|
|
||||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -21,6 +21,7 @@ install-sh
|
|||||||
.libs
|
.libs
|
||||||
lib/includes/nghttp2/nghttp2ver.h
|
lib/includes/nghttp2/nghttp2ver.h
|
||||||
lib/libnghttp2.pc
|
lib/libnghttp2.pc
|
||||||
|
src/libnghttp2_asio.pc
|
||||||
ltmain.sh
|
ltmain.sh
|
||||||
stamp-h1
|
stamp-h1
|
||||||
.deps/
|
.deps/
|
||||||
@@ -40,3 +41,9 @@ doc/h2load-howto.rst
|
|||||||
doc/tutorial-hpack.rst
|
doc/tutorial-hpack.rst
|
||||||
doc/python-apiref.rst
|
doc/python-apiref.rst
|
||||||
doc/building-android-binary.rst
|
doc/building-android-binary.rst
|
||||||
|
doc/asio_http2.h.rst
|
||||||
|
doc/libnghttp2_asio.rst
|
||||||
|
doc/contribute.rst
|
||||||
|
python/setup.py
|
||||||
|
python/dist
|
||||||
|
python/MANIFEST
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ compiler:
|
|||||||
- clang
|
- clang
|
||||||
#Disable gcc build for the moment...
|
#Disable gcc build for the moment...
|
||||||
# - gcc
|
# - gcc
|
||||||
python:
|
|
||||||
- "3.4"
|
|
||||||
before_install:
|
before_install:
|
||||||
- $CC --version
|
- $CC --version
|
||||||
- sudo add-apt-repository --yes ppa:ubuntu-toolchain-r/test
|
- sudo add-apt-repository --yes ppa:ubuntu-toolchain-r/test
|
||||||
@@ -23,6 +21,7 @@ before_install:
|
|||||||
libcunit1-dev
|
libcunit1-dev
|
||||||
libssl-dev
|
libssl-dev
|
||||||
libxml2-dev
|
libxml2-dev
|
||||||
|
libev-dev
|
||||||
libevent-dev
|
libevent-dev
|
||||||
libjansson-dev
|
libjansson-dev
|
||||||
libjemalloc-dev
|
libjemalloc-dev
|
||||||
|
|||||||
103
Dockerfile.android
Normal file
103
Dockerfile.android
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
# Dockerfile to build nghttp2 android binary
|
||||||
|
#
|
||||||
|
# $ sudo docker build -t nghttp2-android - < Dockerfile.android
|
||||||
|
#
|
||||||
|
# After successful build, android binaries are located under
|
||||||
|
# /root/build/nghttp2. You can copy the binary using docker cp. For
|
||||||
|
# example, to copy nghttpx binary to host file system location
|
||||||
|
# /path/to/dest, do this:
|
||||||
|
#
|
||||||
|
# $ sudo docker run -v /path/to/dest:/out nghttp2-android cp /root/build/nghttp2/src/nghttpx /out
|
||||||
|
|
||||||
|
FROM ubuntu
|
||||||
|
|
||||||
|
MAINTAINER Tatsuhiro Tsujikawa
|
||||||
|
|
||||||
|
ENV ANDROID_HOME /root/android
|
||||||
|
ENV PREFIX $ANDROID_HOME/usr/local
|
||||||
|
ENV TOOLCHAIN $ANDROID_HOME/toolchain
|
||||||
|
ENV PATH $TOOLCHAIN/bin:$PATH
|
||||||
|
|
||||||
|
# It would be better to use nearest ubuntu archive mirror for faster
|
||||||
|
# downloads.
|
||||||
|
# RUN sed -ie 's/archive\.ubuntu/jp.archive.ubuntu/g' /etc/apt/sources.list
|
||||||
|
|
||||||
|
RUN apt-get update
|
||||||
|
# genisoimage, libc6-i386 and lib32stdc++6 are required to decompress ndk.
|
||||||
|
RUN apt-get install -y make binutils autoconf automake autotools-dev libtool \
|
||||||
|
pkg-config git curl dpkg-dev libxml2-dev \
|
||||||
|
genisoimage libc6-i386 lib32stdc++6
|
||||||
|
|
||||||
|
WORKDIR /root/build
|
||||||
|
RUN curl -L -O http://dl.google.com/android/ndk/android-ndk-r10c-linux-x86_64.bin
|
||||||
|
RUN chmod a+x android-ndk-r10c-linux-x86_64.bin
|
||||||
|
RUN ./android-ndk-r10c-linux-x86_64.bin
|
||||||
|
|
||||||
|
WORKDIR /root/build/android-ndk-r10c
|
||||||
|
RUN /bin/bash build/tools/make-standalone-toolchain.sh \
|
||||||
|
--install-dir=$ANDROID_HOME/toolchain \
|
||||||
|
--toolchain=arm-linux-androideabi-4.9 --llvm-version=3.5 \
|
||||||
|
--system=linux-x86_64
|
||||||
|
|
||||||
|
WORKDIR /root/build
|
||||||
|
RUN git clone https://github.com/tatsuhiro-t/spdylay
|
||||||
|
WORKDIR /root/build/spdylay
|
||||||
|
RUN autoreconf -i && \
|
||||||
|
./configure \
|
||||||
|
--disable-shared \
|
||||||
|
--host=arm-linux-androideabi \
|
||||||
|
--build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \
|
||||||
|
--prefix=$PREFIX \
|
||||||
|
--without-libxml2 \
|
||||||
|
--disable-src \
|
||||||
|
--disable-examples \
|
||||||
|
CPPFLAGS="-I$PREFIX/include" \
|
||||||
|
PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \
|
||||||
|
LDFLAGS="-L$PREFIX/lib" && \
|
||||||
|
make install
|
||||||
|
|
||||||
|
WORKDIR /root/build
|
||||||
|
RUN curl -L -O https://www.openssl.org/source/openssl-1.0.1j.tar.gz
|
||||||
|
RUN tar xf openssl-1.0.1j.tar.gz
|
||||||
|
WORKDIR /root/build/openssl-1.0.1j
|
||||||
|
RUN export CROSS_COMPILE=$TOOLCHAIN/bin/arm-linux-androideabi- && \
|
||||||
|
./Configure --prefix=$PREFIX android && \
|
||||||
|
make && make install_sw
|
||||||
|
|
||||||
|
WORKDIR /root/build
|
||||||
|
RUN curl -L -O http://dist.schmorp.de/libev/libev-4.19.tar.gz
|
||||||
|
RUN curl -L -O https://gist.github.com/tatsuhiro-t/48c45f08950f587180ed/raw/80a8f003b5d1091eae497c5995bbaa68096e739b/libev-4.19-android.patch
|
||||||
|
RUN tar xf libev-4.19.tar.gz
|
||||||
|
WORKDIR /root/build/libev-4.19
|
||||||
|
RUN patch -p1 < ../libev-4.19-android.patch
|
||||||
|
RUN ./configure \
|
||||||
|
--host=arm-linux-androideabi \
|
||||||
|
--build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \
|
||||||
|
--prefix=$PREFIX \
|
||||||
|
--disable-shared \
|
||||||
|
--enable-static \
|
||||||
|
CPPFLAGS=-I$PREFIX/include \
|
||||||
|
LDFLAGS=-L$PREFIX/lib && \
|
||||||
|
make install
|
||||||
|
|
||||||
|
WORKDIR /root/build
|
||||||
|
RUN git clone https://github.com/tatsuhiro-t/nghttp2
|
||||||
|
WORKDIR /root/build/nghttp2
|
||||||
|
RUN autoreconf -i && \
|
||||||
|
./configure \
|
||||||
|
--disable-shared \
|
||||||
|
--host=arm-linux-androideabi \
|
||||||
|
--build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \
|
||||||
|
--with-xml-prefix="$PREFIX" \
|
||||||
|
--without-libxml2 \
|
||||||
|
--disable-python-bindings \
|
||||||
|
--disable-examples \
|
||||||
|
--disable-threads \
|
||||||
|
LIBSPDYLAY_CFLAGS=-I$PREFIX/usr/local/include \
|
||||||
|
LIBSPDYLAY_LIBS="-L$PREFIX/usr/local/lib -lspdylay" \
|
||||||
|
CPPFLAGS="-I$PREFIX/include" \
|
||||||
|
CXXFLAGS="-fno-strict-aliasing" \
|
||||||
|
PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \
|
||||||
|
LDFLAGS="-L$PREFIX/lib" && \
|
||||||
|
make && \
|
||||||
|
arm-linux-androideabi-strip src/nghttpx src/nghttpd src/nghttp
|
||||||
17
Makefile.am
17
Makefile.am
@@ -20,11 +20,24 @@
|
|||||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
SUBDIRS = lib third-party src examples python tests doc
|
SUBDIRS = lib third-party src examples python tests integration-tests \
|
||||||
|
doc contrib
|
||||||
|
|
||||||
ACLOCAL_AMFLAGS = -I m4
|
ACLOCAL_AMFLAGS = -I m4
|
||||||
|
|
||||||
dist_doc_DATA = README.rst
|
dist_doc_DATA = README.rst
|
||||||
|
|
||||||
EXTRA_DIST = nghttpx.conf.sample proxy.pac.sample android-config android-make \
|
EXTRA_DIST = nghttpx.conf.sample proxy.pac.sample android-config android-make \
|
||||||
Makefile.msvc
|
Dockerfile.android
|
||||||
|
|
||||||
|
.PHONY: clang-format
|
||||||
|
|
||||||
|
# Format source files using clang-format. Don't format source files
|
||||||
|
# under third-party directory since we are not responsible for thier
|
||||||
|
# coding style.
|
||||||
|
clang-format:
|
||||||
|
CLANGFORMAT=`git config --get clangformat.binary`; \
|
||||||
|
test -z $${CLANGFORMAT} && CLANGFORMAT="clang-format"; \
|
||||||
|
$${CLANGFORMAT} -i lib/*.{c,h} lib/includes/nghttp2/*.h \
|
||||||
|
src/*.{c,cc,h} src/includes/nghttp2/*.h examples/*.{c,cc} \
|
||||||
|
tests/*.{c,h}
|
||||||
|
|||||||
110
README.rst
110
README.rst
@@ -4,12 +4,14 @@ nghttp2 - HTTP/2 C Library
|
|||||||
This is an implementation of Hypertext Transfer Protocol version 2
|
This is an implementation of Hypertext Transfer Protocol version 2
|
||||||
in C.
|
in C.
|
||||||
|
|
||||||
The framing layer of HTTP/2 is implemented as form of reusable C
|
The framing layer of HTTP/2 is implemented as a form of reusable C
|
||||||
library. On top of that, we have implemented HTTP/2 client, server
|
library. On top of that, we have implemented HTTP/2 client, server
|
||||||
and proxy. Also we have developed load test/benchmarking tool for
|
and proxy. We have also developed load test and benchmarking tool for
|
||||||
HTTP/2 and SPDY.
|
HTTP/2 and SPDY.
|
||||||
|
|
||||||
HPACK encoding and decoding are available as public API.
|
HPACK encoder and decoder are available as public API.
|
||||||
|
|
||||||
|
The experimental high level C++ library is also available.
|
||||||
|
|
||||||
We have Python binding of this libary, but we have not covered
|
We have Python binding of this libary, but we have not covered
|
||||||
everything yet.
|
everything yet.
|
||||||
@@ -20,9 +22,7 @@ Development Status
|
|||||||
We started to implement h2-14
|
We started to implement h2-14
|
||||||
(http://tools.ietf.org/html/draft-ietf-httpbis-http2-14), the header
|
(http://tools.ietf.org/html/draft-ietf-httpbis-http2-14), the header
|
||||||
compression
|
compression
|
||||||
(http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-09)
|
(http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-09).
|
||||||
and HTTP Alternative Services
|
|
||||||
(http://tools.ietf.org/html/draft-ietf-httpbis-alt-svc-02).
|
|
||||||
|
|
||||||
The nghttp2 code base was forked from spdylay project.
|
The nghttp2 code base was forked from spdylay project.
|
||||||
|
|
||||||
@@ -32,7 +32,6 @@ HTTP/2 Features Support
|
|||||||
Core frames handling Yes
|
Core frames handling Yes
|
||||||
Dependency Tree Yes
|
Dependency Tree Yes
|
||||||
Large header (CONTINUATION) Yes
|
Large header (CONTINUATION) Yes
|
||||||
ALTSVC extension Yes
|
|
||||||
=========================== =======
|
=========================== =======
|
||||||
|
|
||||||
Public Test Server
|
Public Test Server
|
||||||
@@ -45,15 +44,11 @@ implementation.
|
|||||||
|
|
||||||
NPN offer ``h2-14``, ``spdy/3.1`` and ``http/1.1``.
|
NPN offer ``h2-14``, ``spdy/3.1`` and ``http/1.1``.
|
||||||
|
|
||||||
This endpoint requires TLSv1.2 and DHE or EDCHE with GCM cipher
|
This endpoint requires TLSv1.2 for HTTP/2 connection.
|
||||||
suite for HTTP/2 connection.
|
|
||||||
|
|
||||||
* http://nghttp2.org/ (Upgrade / Direct)
|
* http://nghttp2.org/ (Upgrade / Direct)
|
||||||
|
|
||||||
``h2c-14`` and ``http/1.1``. We configured this server to send
|
``h2c-14`` and ``http/1.1``.
|
||||||
ALTSVC frame or Alt-Svc header field to announce that alternative
|
|
||||||
service is available at port 443.
|
|
||||||
|
|
||||||
|
|
||||||
Requirements
|
Requirements
|
||||||
------------
|
------------
|
||||||
@@ -76,7 +71,7 @@ To build and run the application programs (``nghttp``, ``nghttpd`` and
|
|||||||
required:
|
required:
|
||||||
|
|
||||||
* OpenSSL >= 1.0.1
|
* OpenSSL >= 1.0.1
|
||||||
* libevent-openssl >= 2.0.8
|
* libev >= 4.15
|
||||||
* zlib >= 1.2.3
|
* zlib >= 1.2.3
|
||||||
|
|
||||||
ALPN support requires unreleased version OpenSSL >= 1.0.2.
|
ALPN support requires unreleased version OpenSSL >= 1.0.2.
|
||||||
@@ -95,11 +90,20 @@ The HPACK tools require the following package:
|
|||||||
|
|
||||||
* jansson >= 2.5
|
* jansson >= 2.5
|
||||||
|
|
||||||
|
To build sources under examples directory, libevent is required:
|
||||||
|
|
||||||
|
* libevent-openssl >= 2.0.8
|
||||||
|
|
||||||
To mitigate heap fragmentation in long running server programs
|
To mitigate heap fragmentation in long running server programs
|
||||||
(``nghttpd`` and ``nghttpx``), jemalloc is recommended:
|
(``nghttpd`` and ``nghttpx``), jemalloc is recommended:
|
||||||
|
|
||||||
* jemalloc
|
* jemalloc
|
||||||
|
|
||||||
|
libnghttp2_asio C++ library requires the following packages:
|
||||||
|
|
||||||
|
* libboost-dev >= 1.54.0
|
||||||
|
* libboost-thread-dev >= 1.54.0
|
||||||
|
|
||||||
The Python bindings require the following packages:
|
The Python bindings require the following packages:
|
||||||
|
|
||||||
* cython >= 0.19
|
* cython >= 0.19
|
||||||
@@ -108,6 +112,8 @@ The Python bindings require the following packages:
|
|||||||
If you are using Ubuntu 14.04 LTS, you need the following packages
|
If you are using Ubuntu 14.04 LTS, you need the following packages
|
||||||
installed:
|
installed:
|
||||||
|
|
||||||
|
* make
|
||||||
|
* binutils
|
||||||
* autoconf
|
* autoconf
|
||||||
* automake
|
* automake
|
||||||
* autotools-dev
|
* autotools-dev
|
||||||
@@ -117,6 +123,7 @@ installed:
|
|||||||
* libcunit1-dev
|
* libcunit1-dev
|
||||||
* libssl-dev
|
* libssl-dev
|
||||||
* libxml2-dev
|
* libxml2-dev
|
||||||
|
* libev-dev
|
||||||
* libevent-dev
|
* libevent-dev
|
||||||
* libjansson-dev
|
* libjansson-dev
|
||||||
* libjemalloc-dev
|
* libjemalloc-dev
|
||||||
@@ -165,6 +172,34 @@ The generated documents will not be installed with ``make install``.
|
|||||||
The online documentation is available at
|
The online documentation is available at
|
||||||
https://nghttp2.org/documentation/
|
https://nghttp2.org/documentation/
|
||||||
|
|
||||||
|
Unit tests
|
||||||
|
----------
|
||||||
|
|
||||||
|
Unit tests are done by simply running `make check`.
|
||||||
|
|
||||||
|
Integration tests
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
We have the integration tests for nghttpx proxy server. The tests are
|
||||||
|
written in `Go programming language <http://golang.org/>`_ and uses
|
||||||
|
its testing framework. We depend on the following libraries:
|
||||||
|
|
||||||
|
* https://github.com/bradfitz/http2
|
||||||
|
* https://github.com/tatsuhiro-t/go-nghttp2
|
||||||
|
* https://golang.org/x/net/spdy
|
||||||
|
|
||||||
|
To download the above packages, after settings ``GOPATH``, run the
|
||||||
|
following command under ``integration-tests`` directory::
|
||||||
|
|
||||||
|
$ make itprep
|
||||||
|
|
||||||
|
To run the tests, run the following command under
|
||||||
|
``integration-tests`` directory::
|
||||||
|
|
||||||
|
$ make it
|
||||||
|
|
||||||
|
Inside the tests, we use port 3009 to run test subject server.
|
||||||
|
|
||||||
Client, Server and Proxy programs
|
Client, Server and Proxy programs
|
||||||
---------------------------------
|
---------------------------------
|
||||||
|
|
||||||
@@ -275,8 +310,6 @@ The HTTP Upgrade is performed like this::
|
|||||||
(niv=2)
|
(niv=2)
|
||||||
[SETTINGS_MAX_CONCURRENT_STREAMS(3):100]
|
[SETTINGS_MAX_CONCURRENT_STREAMS(3):100]
|
||||||
[SETTINGS_INITIAL_WINDOW_SIZE(4):65535]
|
[SETTINGS_INITIAL_WINDOW_SIZE(4):65535]
|
||||||
[ 0.024] recv ALTSVC frame <length=43, flags=0x00, stream_id=0>
|
|
||||||
(max-age=86400, port=443, protocol_id=h2-14, host=nghttp2.org, origin=http://nghttp2.org)
|
|
||||||
[ 0.024] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
|
[ 0.024] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
|
||||||
; ACK
|
; ACK
|
||||||
(niv=0)
|
(niv=0)
|
||||||
@@ -429,7 +462,7 @@ SPDY proxy)::
|
|||||||
Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/1.1) --> Proxy
|
Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/1.1) --> Proxy
|
||||||
[secure proxy] (e.g., Squid, ATS)
|
[secure proxy] (e.g., Squid, ATS)
|
||||||
|
|
||||||
The ``Client`` in the above is needs to be configured to use
|
The ``Client`` in the above needs to be configured to use
|
||||||
``nghttpx`` as secure proxy.
|
``nghttpx`` as secure proxy.
|
||||||
|
|
||||||
At the time of this writing, Chrome is the only browser which supports
|
At the time of this writing, Chrome is the only browser which supports
|
||||||
@@ -961,6 +994,45 @@ associated value includes the state of dynamic header table after the
|
|||||||
corresponding header set was processed. The format is the same as
|
corresponding header set was processed. The format is the same as
|
||||||
``deflatehd``.
|
``deflatehd``.
|
||||||
|
|
||||||
|
libnghttp2_asio: High level HTTP/2 C++ library
|
||||||
|
----------------------------------------------
|
||||||
|
|
||||||
|
libnghttp2_asio is C++ library built on top of libnghttp2 and provides
|
||||||
|
high level abstraction API to build HTTP/2 applications. It depends
|
||||||
|
on Boost::ASIO library and OpenSSL. Currently libnghttp2_asio
|
||||||
|
provides server side API.
|
||||||
|
|
||||||
|
libnghttp2_asio is not built by default. Use ``--enable-asio-lib``
|
||||||
|
configure flag to build libnghttp2_asio. The required Boost libraries
|
||||||
|
are:
|
||||||
|
|
||||||
|
* Boost::Asio
|
||||||
|
* Boost::System
|
||||||
|
* Boost::Thread
|
||||||
|
|
||||||
|
Server API is designed to build HTTP/2 server very easily to utilize
|
||||||
|
C++11 anonymous function and closure. The bare minimum example of
|
||||||
|
HTTP/2 server looks like this:
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
#include <nghttp2/asio_http2.h>
|
||||||
|
|
||||||
|
using namespace nghttp2::asio_http2;
|
||||||
|
using namespace nghttp2::asio_http2::server;
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
http2 server;
|
||||||
|
|
||||||
|
server.listen("*", 3000, [](const std::shared_ptr<request> &req,
|
||||||
|
const std::shared_ptr<response> &res) {
|
||||||
|
res->write_head(200);
|
||||||
|
res->end("hello, world");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
For more details, see the documentation of libnghttp2_asio.
|
||||||
|
|
||||||
Python bindings
|
Python bindings
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
@@ -1098,3 +1170,7 @@ to hand over that patch/code to us. We will credit you for your
|
|||||||
changes as far as possible, to give credit but also to keep a trace
|
changes as far as possible, to give credit but also to keep a trace
|
||||||
back to who made what changes. Please always provide us with your
|
back to who made what changes. Please always provide us with your
|
||||||
full real name when contributing!
|
full real name when contributing!
|
||||||
|
|
||||||
|
See `Contribution Guidelines
|
||||||
|
<https://nghttp2.org/documentation/contribute.html>`_ for more
|
||||||
|
details.
|
||||||
|
|||||||
@@ -39,9 +39,8 @@ PATH=$TOOLCHAIN/bin:$PATH
|
|||||||
--without-libxml2 \
|
--without-libxml2 \
|
||||||
--disable-python-bindings \
|
--disable-python-bindings \
|
||||||
--disable-examples \
|
--disable-examples \
|
||||||
--disable-threads \
|
CC=clang \
|
||||||
CC=arm-linux-androideabi-clang \
|
CXX=clang++ \
|
||||||
CXX=arm-linux-androideabi-clang++ \
|
|
||||||
CPPFLAGS="-I$PREFIX/include" \
|
CPPFLAGS="-I$PREFIX/include" \
|
||||||
PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \
|
PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \
|
||||||
LDFLAGS="-L$PREFIX/lib"
|
LDFLAGS="-L$PREFIX/lib"
|
||||||
|
|||||||
219
configure.ac
219
configure.ac
@@ -1,6 +1,6 @@
|
|||||||
dnl nghttp2 - HTTP/2 C Library
|
dnl nghttp2 - HTTP/2 C Library
|
||||||
|
|
||||||
dnl Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa
|
dnl Copyright (c) 2012, 2013, 2014, 2015 Tatsuhiro Tsujikawa
|
||||||
|
|
||||||
dnl Permission is hereby granted, free of charge, to any person obtaining
|
dnl Permission is hereby granted, free of charge, to any person obtaining
|
||||||
dnl a copy of this software and associated documentation files (the
|
dnl a copy of this software and associated documentation files (the
|
||||||
@@ -20,15 +20,19 @@ dnl NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|||||||
dnl LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
dnl LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
dnl OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
dnl OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
dnl WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
dnl WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
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_PREREQ(2.61)
|
||||||
AC_INIT([nghttp2], [0.6.1], [t-tujikawa@users.sourceforge.net])
|
AC_INIT([nghttp2], [0.7.3], [t-tujikawa@users.sourceforge.net])
|
||||||
LT_PREREQ([2.2.6])
|
LT_PREREQ([2.2.6])
|
||||||
LT_INIT()
|
LT_INIT()
|
||||||
dnl See versioning rule:
|
dnl See versioning rule:
|
||||||
dnl http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
dnl http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
||||||
AC_SUBST(LT_CURRENT, 5)
|
AC_SUBST(LT_CURRENT, 9)
|
||||||
AC_SUBST(LT_REVISION, 0)
|
AC_SUBST(LT_REVISION, 0)
|
||||||
AC_SUBST(LT_AGE, 0)
|
AC_SUBST(LT_AGE, 4)
|
||||||
|
|
||||||
major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"`
|
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"`
|
minor=`echo $PACKAGE_VERSION |cut -d. -f2 | sed -e "s/[^0-9]//g"`
|
||||||
@@ -45,6 +49,9 @@ AC_CANONICAL_TARGET
|
|||||||
AC_CONFIG_MACRO_DIR([m4])
|
AC_CONFIG_MACRO_DIR([m4])
|
||||||
|
|
||||||
AM_INIT_AUTOMAKE([subdir-objects])
|
AM_INIT_AUTOMAKE([subdir-objects])
|
||||||
|
# comment out for now since this requires automake 1.13 or higher and
|
||||||
|
# travis has older one.
|
||||||
|
# AM_EXTRA_RECURSIVE_TARGETS([it])
|
||||||
|
|
||||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||||
|
|
||||||
@@ -76,6 +83,11 @@ AC_ARG_ENABLE([hpack-tools],
|
|||||||
[Build HPACK tools [default=check]])],
|
[Build HPACK tools [default=check]])],
|
||||||
[request_hpack_tools=$enableval], [request_hpack_tools=check])
|
[request_hpack_tools=$enableval], [request_hpack_tools=check])
|
||||||
|
|
||||||
|
AC_ARG_ENABLE([asio-lib],
|
||||||
|
[AS_HELP_STRING([--enable-asio-lib],
|
||||||
|
[Build C++ libnghttp2_asio library [default=no]])],
|
||||||
|
[request_asio_lib=$enableval], [request_asio_lib=no])
|
||||||
|
|
||||||
AC_ARG_ENABLE([examples],
|
AC_ARG_ENABLE([examples],
|
||||||
[AS_HELP_STRING([--enable-examples],
|
[AS_HELP_STRING([--enable-examples],
|
||||||
[Build examples [default=check]])],
|
[Build examples [default=check]])],
|
||||||
@@ -87,9 +99,9 @@ AC_ARG_ENABLE([python-bindings],
|
|||||||
[request_python_bindings=$enableval], [request_python_bindings=check])
|
[request_python_bindings=$enableval], [request_python_bindings=check])
|
||||||
|
|
||||||
AC_ARG_ENABLE([failmalloc],
|
AC_ARG_ENABLE([failmalloc],
|
||||||
[AS_HELP_STRING([--enable-failmalloc],
|
[AS_HELP_STRING([--disable-failmalloc],
|
||||||
[Build failmalloc test program [default=no]])],
|
[Do not build failmalloc test program])],
|
||||||
[request_failmalloc=$enableval], [request_failmalloc=no])
|
[request_failmalloc=$enableval], [request_failmalloc=yes])
|
||||||
|
|
||||||
AC_ARG_WITH([libxml2],
|
AC_ARG_WITH([libxml2],
|
||||||
[AS_HELP_STRING([--with-libxml2],
|
[AS_HELP_STRING([--with-libxml2],
|
||||||
@@ -135,6 +147,20 @@ else
|
|||||||
AC_SUBST([CYTHON])
|
AC_SUBST([CYTHON])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# If we're running GCC or clang define _U_ to be "__attribute__((unused))"
|
||||||
|
# so we can use _U_ to flag unused function parameters and not get warnings
|
||||||
|
# about them. Otherwise, define _U_ to be an empty string so that _U_ used
|
||||||
|
# to flag an unused function parameters will compile with other compilers.
|
||||||
|
#
|
||||||
|
# XXX - similar hints for other compilers?
|
||||||
|
#
|
||||||
|
if test "x$GCC" = "xyes" -o "x$CC" = "xclang" ; then
|
||||||
|
AC_DEFINE([_U_], [__attribute__((unused))], [Hint to the compiler that a function parameters is not used])
|
||||||
|
else
|
||||||
|
AC_DEFINE([_U_], , [Hint to the compiler that a function parameters is not used])
|
||||||
|
fi
|
||||||
|
|
||||||
AX_CXX_COMPILE_STDCXX_11([noext], [optional])
|
AX_CXX_COMPILE_STDCXX_11([noext], [optional])
|
||||||
|
|
||||||
AC_LANG_PUSH(C++)
|
AC_LANG_PUSH(C++)
|
||||||
@@ -174,46 +200,77 @@ std::vector<std::future<int>> v;
|
|||||||
[have_std_future=no
|
[have_std_future=no
|
||||||
AC_MSG_RESULT([no])])
|
AC_MSG_RESULT([no])])
|
||||||
|
|
||||||
|
# Check that thread_local is available.
|
||||||
|
AC_MSG_CHECKING([whether thread_local is available])
|
||||||
|
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
|
||||||
|
[[
|
||||||
|
]],
|
||||||
|
[[
|
||||||
|
thread_local int n;
|
||||||
|
]])],
|
||||||
|
[AC_DEFINE([HAVE_THREAD_LOCAL], [1],
|
||||||
|
[Define to 1 if you have the `thread_local`.])
|
||||||
|
have_thread_local=yes
|
||||||
|
AC_MSG_RESULT([yes])],
|
||||||
|
[have_thread_local=no
|
||||||
|
AC_MSG_RESULT([no])])
|
||||||
|
|
||||||
|
# Check that std::map::emplace is available for g++-4.7.
|
||||||
|
AC_MSG_CHECKING([whether std::map::emplace is available])
|
||||||
|
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
|
||||||
|
[[
|
||||||
|
#include <map>
|
||||||
|
]],
|
||||||
|
[[
|
||||||
|
std::map<int, int>().emplace(1, 2);
|
||||||
|
]])],
|
||||||
|
[AC_DEFINE([HAVE_STD_MAP_EMPLACE], [1],
|
||||||
|
[Define to 1 if you have the `std::map::emplace`.])
|
||||||
|
have_std_map_emplace=yes
|
||||||
|
AC_MSG_RESULT([yes])],
|
||||||
|
[have_std_map_emplace=no
|
||||||
|
AC_MSG_RESULT([no])])
|
||||||
|
|
||||||
AC_LANG_POP()
|
AC_LANG_POP()
|
||||||
|
|
||||||
# Checks for libraries.
|
# Checks for libraries.
|
||||||
|
|
||||||
# Additional libraries required for tests.
|
# Additional libraries required for tests.
|
||||||
TESTS_LIBS=
|
TESTLDADD=
|
||||||
|
|
||||||
# Additional libraries required for programs under src directory.
|
# Additional libraries required for programs under src directory.
|
||||||
SRC_LIBS=
|
APPLDFLAGS=
|
||||||
|
|
||||||
LIBS_OLD=$LIBS
|
LIBS_OLD=$LIBS
|
||||||
# Search for dlsym function, which is used in tests. Linux needs -ldl,
|
# Search for dlsym function, which is used in tests. Linux needs -ldl,
|
||||||
# but netbsd does not need it.
|
# but netbsd does not need it.
|
||||||
AC_SEARCH_LIBS([dlsym], [dl])
|
AC_SEARCH_LIBS([dlsym], [dl])
|
||||||
TESTS_LIBS="$LIBS $TESTS_LIBS"
|
TESTLDADD="$LIBS $TESTLDADD"
|
||||||
LIBS=$LIBS_OLD
|
LIBS=$LIBS_OLD
|
||||||
|
|
||||||
LIBS_OLD=$LIBS
|
LIBS_OLD=$LIBS
|
||||||
AC_SEARCH_LIBS([clock_gettime], [rt],
|
AC_SEARCH_LIBS([clock_gettime], [rt],
|
||||||
[AC_DEFINE([HAVE_CLOCK_GETTIME], [1],
|
[AC_DEFINE([HAVE_CLOCK_GETTIME], [1],
|
||||||
[Define to 1 if you have the `clock_gettime`.])])
|
[Define to 1 if you have the `clock_gettime`.])])
|
||||||
SRC_LIBS="$LIBS $SRC_LIBS"
|
APPLDFLAGS="$LIBS $APPLDFLAGS"
|
||||||
LIBS=$LIBS_OLD
|
LIBS=$LIBS_OLD
|
||||||
|
|
||||||
case "$host" in
|
case "$host" in
|
||||||
*android*)
|
*android*)
|
||||||
android_build=yes
|
android_build=yes
|
||||||
# android does not need -pthread, but needs followng 2 libs for C++
|
# android does not need -pthread, but needs followng 3 libs for C++
|
||||||
SRC_LIBS="$SRC_LIBS -lstdc++ -lsupc++"
|
APPLDFLAGS="$APPLDFLAGS -lstdc++ -latomic -lsupc++"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
PTHREAD_LDFLAGS="-pthread"
|
PTHREAD_LDFLAGS="-pthread"
|
||||||
SRC_LIBS="$SRC_LIBS $PTHREAD_LDFLAGS"
|
APPLDFLAGS="$APPLDFLAGS $PTHREAD_LDFLAGS"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
# zlib
|
# zlib
|
||||||
if test "x$android_build" = "xyes"; then
|
if test "x$android_build" = "xyes"; then
|
||||||
# Use zlib provided by NDK
|
# Use zlib provided by NDK
|
||||||
SRC_LIBS="-lz $SRC_LIBS"
|
APPLDFLAGS="-lz $APPLDFLAGS"
|
||||||
have_zlib=yes
|
have_zlib=yes
|
||||||
else
|
else
|
||||||
PKG_CHECK_MODULES([ZLIB], [zlib >= 1.2.3], [have_zlib=yes], [have_zlib=no])
|
PKG_CHECK_MODULES([ZLIB], [zlib >= 1.2.3], [have_zlib=yes], [have_zlib=no])
|
||||||
@@ -252,6 +309,22 @@ fi
|
|||||||
|
|
||||||
AM_CONDITIONAL([HAVE_CUNIT], [ test "x${have_cunit}" = "xyes" ])
|
AM_CONDITIONAL([HAVE_CUNIT], [ test "x${have_cunit}" = "xyes" ])
|
||||||
|
|
||||||
|
# libev (for src)
|
||||||
|
# libev does not have pkg-config file. Check it in an old way.
|
||||||
|
LIBS_OLD=$LIBS
|
||||||
|
# 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])
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
LIBS=$LIBS_OLD
|
||||||
|
|
||||||
# openssl (for src)
|
# openssl (for src)
|
||||||
PKG_CHECK_MODULES([OPENSSL], [openssl >= 1.0.1],
|
PKG_CHECK_MODULES([OPENSSL], [openssl >= 1.0.1],
|
||||||
[have_openssl=yes], [have_openssl=no])
|
[have_openssl=yes], [have_openssl=no])
|
||||||
@@ -259,7 +332,7 @@ if test "x${have_openssl}" = "xno"; then
|
|||||||
AC_MSG_NOTICE($OPENSSL_PKG_ERRORS)
|
AC_MSG_NOTICE($OPENSSL_PKG_ERRORS)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# libevent_openssl (for src)
|
# libevent_openssl (for examples)
|
||||||
# 2.0.8 is required because we use evconnlistener_set_error_cb()
|
# 2.0.8 is required because we use evconnlistener_set_error_cb()
|
||||||
PKG_CHECK_MODULES([LIBEVENT_OPENSSL], [libevent_openssl >= 2.0.8],
|
PKG_CHECK_MODULES([LIBEVENT_OPENSSL], [libevent_openssl >= 2.0.8],
|
||||||
[have_libevent_openssl=yes], [have_libevent_openssl=no])
|
[have_libevent_openssl=yes], [have_libevent_openssl=no])
|
||||||
@@ -267,10 +340,13 @@ if test "x${have_libevent_openssl}" = "xno"; then
|
|||||||
AC_MSG_NOTICE($LIBEVENT_OPENSSL_PKG_ERRORS)
|
AC_MSG_NOTICE($LIBEVENT_OPENSSL_PKG_ERRORS)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# jansson (for hdtest/deflatehd and hdtest/inflatehd)
|
# jansson (for src/nghttp, src/deflatehd and src/inflatehd)
|
||||||
PKG_CHECK_MODULES([JANSSON], [jansson >= 2.5],
|
PKG_CHECK_MODULES([JANSSON], [jansson >= 2.5],
|
||||||
[have_jansson=yes], [have_jansson=no])
|
[have_jansson=yes], [have_jansson=no])
|
||||||
if test "x${have_jansson}" == "xno"; then
|
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)
|
AC_MSG_NOTICE($JANSSON_PKG_ERRORS)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -329,13 +405,32 @@ fi
|
|||||||
|
|
||||||
AM_CONDITIONAL([HAVE_SPDYLAY], [ test "x${have_spdylay}" = "xyes" ])
|
AM_CONDITIONAL([HAVE_SPDYLAY], [ test "x${have_spdylay}" = "xyes" ])
|
||||||
|
|
||||||
|
# Check Boost Asio library
|
||||||
|
have_asio_lib=no
|
||||||
|
|
||||||
|
if test "x${request_asio_lib}" = "xyes"; then
|
||||||
|
AX_BOOST_BASE([1.54.0], [have_boost_base=yes], [have_boost_base=no])
|
||||||
|
|
||||||
|
if test "x${have_boost_base}" = "xyes"; then
|
||||||
|
AX_BOOST_ASIO()
|
||||||
|
AX_BOOST_SYSTEM()
|
||||||
|
AX_BOOST_THREAD()
|
||||||
|
|
||||||
|
if test "x${ax_cv_boost_asio}" = "xyes" &&
|
||||||
|
test "x${ax_cv_boost_system}" = "xyes" &&
|
||||||
|
test "x${ax_cv_boost_thread}" = "xyes"; then
|
||||||
|
have_asio_lib=yes
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# The nghttp, nghttpd and nghttpx under src depend on zlib, OpenSSL
|
# The nghttp, nghttpd and nghttpx under src depend on zlib, OpenSSL
|
||||||
# and libevent_openssl
|
# and libev
|
||||||
enable_app=no
|
enable_app=no
|
||||||
if test "x${request_app}" != "xno" &&
|
if test "x${request_app}" != "xno" &&
|
||||||
test "x${have_zlib}" = "xyes" &&
|
test "x${have_zlib}" = "xyes" &&
|
||||||
test "x${have_openssl}" = "xyes" &&
|
test "x${have_openssl}" = "xyes" &&
|
||||||
test "x${have_libevent_openssl}" = "xyes"; then
|
test "x${have_libev}" = "xyes"; then
|
||||||
enable_app=yes
|
enable_app=yes
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -345,7 +440,6 @@ if test "x${request_app}" = "xyes" &&
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
AM_CONDITIONAL([ENABLE_APP], [ test "x${enable_app}" = "xyes" ])
|
AM_CONDITIONAL([ENABLE_APP], [ test "x${enable_app}" = "xyes" ])
|
||||||
AM_CONDITIONAL([ENABLE_H2LOAD], [ test "x${have_std_future}" = "xyes" ])
|
|
||||||
|
|
||||||
enable_hpack_tools=no
|
enable_hpack_tools=no
|
||||||
# HPACK tools requires jansson
|
# HPACK tools requires jansson
|
||||||
@@ -361,6 +455,16 @@ fi
|
|||||||
|
|
||||||
AM_CONDITIONAL([ENABLE_HPACK_TOOLS], [ test "x${enable_hpack_tools}" = "xyes" ])
|
AM_CONDITIONAL([ENABLE_HPACK_TOOLS], [ test "x${enable_hpack_tools}" = "xyes" ])
|
||||||
|
|
||||||
|
# C++ library libnghttp2_asio
|
||||||
|
|
||||||
|
enable_asio_lib=no
|
||||||
|
if test "x${request_asio_lib}" != "xno" &&
|
||||||
|
test "x${have_asio_lib}" = "xyes"; then
|
||||||
|
enable_asio_lib=yes
|
||||||
|
fi
|
||||||
|
|
||||||
|
AM_CONDITIONAL([ENABLE_ASIO_LIB], [ test "x${enable_asio_lib}" = "xyes" ])
|
||||||
|
|
||||||
# The example programs depend on OpenSSL and libevent_openssl
|
# The example programs depend on OpenSSL and libevent_openssl
|
||||||
enable_examples=no
|
enable_examples=no
|
||||||
if test "x${request_examples}" != "xno" &&
|
if test "x${request_examples}" != "xno" &&
|
||||||
@@ -398,6 +502,11 @@ AM_CONDITIONAL([ENABLE_PYTHON_BINDINGS],
|
|||||||
AM_CONDITIONAL([HAVE_CYTHON], [test "x${CYTHON}" != "x"])
|
AM_CONDITIONAL([HAVE_CYTHON], [test "x${CYTHON}" != "x"])
|
||||||
|
|
||||||
# failmalloc tests
|
# failmalloc tests
|
||||||
|
enable_failmalloc=no
|
||||||
|
if test "x${request_failmalloc}" = "xyes"; then
|
||||||
|
enable_failmalloc=yes
|
||||||
|
fi
|
||||||
|
|
||||||
AM_CONDITIONAL([ENABLE_FAILMALLOC], [ test "x${enable_failmalloc}" = "xyes" ])
|
AM_CONDITIONAL([ENABLE_FAILMALLOC], [ test "x${enable_failmalloc}" = "xyes" ])
|
||||||
|
|
||||||
# Checks for header files.
|
# Checks for header files.
|
||||||
@@ -432,17 +541,40 @@ AC_CHECK_TYPES([ptrdiff_t])
|
|||||||
AC_C_BIGENDIAN
|
AC_C_BIGENDIAN
|
||||||
AC_SYS_LARGEFILE
|
AC_SYS_LARGEFILE
|
||||||
|
|
||||||
|
AC_CHECK_MEMBER([struct tm.tm_gmtoff], [have_struct_tm_tm_gmtoff=yes],
|
||||||
|
[have_struct_tm_tm_gmtoff=no], [[#include <time.h>]])
|
||||||
|
|
||||||
|
if test "x$have_struct_tm_tm_gmtoff" = "xyes"; then
|
||||||
|
AC_DEFINE([HAVE_STRUCT_TM_TM_GMTOFF], [1],
|
||||||
|
[Define to 1 if you have `struct tm.tm_gmtoff` member.])
|
||||||
|
fi
|
||||||
|
|
||||||
# Checks for library functions.
|
# Checks for library functions.
|
||||||
if test "x$cross_compiling" != "xyes"; then
|
if test "x$cross_compiling" != "xyes"; then
|
||||||
AC_FUNC_MALLOC
|
AC_FUNC_MALLOC
|
||||||
fi
|
fi
|
||||||
AC_CHECK_FUNCS([ \
|
AC_CHECK_FUNCS([ \
|
||||||
|
_Exit \
|
||||||
|
accept4 \
|
||||||
getpwnam \
|
getpwnam \
|
||||||
memmove \
|
memmove \
|
||||||
memset \
|
memset \
|
||||||
timegm \
|
timegm \
|
||||||
])
|
])
|
||||||
|
|
||||||
|
# timerfd_create was added in linux kernel 2.6.25
|
||||||
|
|
||||||
|
AC_CHECK_FUNC([timerfd_create],
|
||||||
|
[have_timerfd_create=yes], [have_timerfd_create=no])
|
||||||
|
|
||||||
|
|
||||||
|
# Checks for epoll availability, primarily for examples/tiny-nghttpd
|
||||||
|
AX_HAVE_EPOLL([have_epoll=yes], [have_epoll=no])
|
||||||
|
|
||||||
|
AM_CONDITIONAL([ENABLE_TINY_NGHTTPD],
|
||||||
|
[ test "x${have_epoll}" = "xyes" &&
|
||||||
|
test "x${have_timerfd_create}" = "xyes"])
|
||||||
|
|
||||||
dnl Windows library for winsock2
|
dnl Windows library for winsock2
|
||||||
case "${host}" in
|
case "${host}" in
|
||||||
*mingw*)
|
*mingw*)
|
||||||
@@ -450,6 +582,9 @@ case "${host}" in
|
|||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
ac_save_CFLAGS=$CFLAGS
|
||||||
|
CFLAGS=
|
||||||
|
|
||||||
if test "x$werror" != "xno"; then
|
if test "x$werror" != "xno"; then
|
||||||
AX_CHECK_COMPILE_FLAG([-Wall], [CFLAGS="$CFLAGS -Wall"])
|
AX_CHECK_COMPILE_FLAG([-Wall], [CFLAGS="$CFLAGS -Wall"])
|
||||||
AX_CHECK_COMPILE_FLAG([-Wextra], [CFLAGS="$CFLAGS -Wextra"])
|
AX_CHECK_COMPILE_FLAG([-Wextra], [CFLAGS="$CFLAGS -Wextra"])
|
||||||
@@ -471,7 +606,6 @@ if test "x$werror" != "xno"; then
|
|||||||
AX_CHECK_COMPILE_FLAG([-Wcast-align], [CFLAGS="$CFLAGS -Wcast-align"])
|
AX_CHECK_COMPILE_FLAG([-Wcast-align], [CFLAGS="$CFLAGS -Wcast-align"])
|
||||||
AX_CHECK_COMPILE_FLAG([-Wclobbered], [CFLAGS="$CFLAGS -Wclobbered"])
|
AX_CHECK_COMPILE_FLAG([-Wclobbered], [CFLAGS="$CFLAGS -Wclobbered"])
|
||||||
AX_CHECK_COMPILE_FLAG([-Wvla], [CFLAGS="$CFLAGS -Wvla"])
|
AX_CHECK_COMPILE_FLAG([-Wvla], [CFLAGS="$CFLAGS -Wvla"])
|
||||||
AX_CHECK_COMPILE_FLAG([-Wno-unused-parameter], [CFLAGS="$CFLAGS -Wno-unused-parameter"])
|
|
||||||
AX_CHECK_COMPILE_FLAG([-Wpragmas], [CFLAGS="$CFLAGS -Wpragmas"])
|
AX_CHECK_COMPILE_FLAG([-Wpragmas], [CFLAGS="$CFLAGS -Wpragmas"])
|
||||||
AX_CHECK_COMPILE_FLAG([-Wunreachable-code], [CFLAGS="$CFLAGS -Wunreachable-code"])
|
AX_CHECK_COMPILE_FLAG([-Wunreachable-code], [CFLAGS="$CFLAGS -Wunreachable-code"])
|
||||||
AX_CHECK_COMPILE_FLAG([-Waddress], [CFLAGS="$CFLAGS -Waddress"])
|
AX_CHECK_COMPILE_FLAG([-Waddress], [CFLAGS="$CFLAGS -Waddress"])
|
||||||
@@ -483,16 +617,27 @@ if test "x$werror" != "xno"; then
|
|||||||
AX_CHECK_COMPILE_FLAG([-Wheader-guard], [CFLAGS="$CFLAGS -Wheader-guard"])
|
AX_CHECK_COMPILE_FLAG([-Wheader-guard], [CFLAGS="$CFLAGS -Wheader-guard"])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
WARNCFLAGS=$CFLAGS
|
||||||
|
CFLAGS=$ac_save_CFLAGS
|
||||||
|
|
||||||
|
AC_SUBST([WARNCFLAGS])
|
||||||
|
|
||||||
if test "x$debug" != "xno"; then
|
if test "x$debug" != "xno"; then
|
||||||
AC_DEFINE([DEBUGBUILD], [1], [Define to 1 to enable debug output.])
|
AC_DEFINE([DEBUGBUILD], [1], [Define to 1 to enable debug output.])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if test "x$threads" != "xyes"; then
|
enable_threads=yes
|
||||||
|
# Some platform does not have working std::future or thread_local. We
|
||||||
|
# disable threading for those platforms.
|
||||||
|
if test "x$threads" != "xyes" ||
|
||||||
|
test "x$have_std_future" != "xyes" ||
|
||||||
|
test "x$have_thread_local" != "xyes"; then
|
||||||
|
enable_threads=no
|
||||||
AC_DEFINE([NOTHREADS], [1], [Define to 1 if you want to disable threads.])
|
AC_DEFINE([NOTHREADS], [1], [Define to 1 if you want to disable threads.])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
AC_SUBST([TESTS_LIBS])
|
AC_SUBST([TESTLDADD])
|
||||||
AC_SUBST([SRC_LIBS])
|
AC_SUBST([APPLDFLAGS])
|
||||||
|
|
||||||
AC_CONFIG_FILES([
|
AC_CONFIG_FILES([
|
||||||
Makefile
|
Makefile
|
||||||
@@ -504,9 +649,14 @@ AC_CONFIG_FILES([
|
|||||||
tests/testdata/Makefile
|
tests/testdata/Makefile
|
||||||
third-party/Makefile
|
third-party/Makefile
|
||||||
src/Makefile
|
src/Makefile
|
||||||
|
src/includes/Makefile
|
||||||
|
src/libnghttp2_asio.pc
|
||||||
examples/Makefile
|
examples/Makefile
|
||||||
python/Makefile
|
python/Makefile
|
||||||
python/setup.py
|
python/setup.py
|
||||||
|
integration-tests/Makefile
|
||||||
|
integration-tests/config.go
|
||||||
|
integration-tests/setenv
|
||||||
doc/Makefile
|
doc/Makefile
|
||||||
doc/conf.py
|
doc/conf.py
|
||||||
doc/index.rst
|
doc/index.rst
|
||||||
@@ -516,10 +666,14 @@ AC_CONFIG_FILES([
|
|||||||
doc/tutorial-hpack.rst
|
doc/tutorial-hpack.rst
|
||||||
doc/nghttpx-howto.rst
|
doc/nghttpx-howto.rst
|
||||||
doc/h2load-howto.rst
|
doc/h2load-howto.rst
|
||||||
|
doc/libnghttp2_asio.rst
|
||||||
doc/python-apiref.rst
|
doc/python-apiref.rst
|
||||||
doc/building-android-binary.rst
|
doc/building-android-binary.rst
|
||||||
doc/nghttp2.h.rst
|
doc/nghttp2.h.rst
|
||||||
doc/nghttp2ver.h.rst
|
doc/nghttp2ver.h.rst
|
||||||
|
doc/asio_http2.h.rst
|
||||||
|
doc/contribute.rst
|
||||||
|
contrib/Makefile
|
||||||
])
|
])
|
||||||
AC_OUTPUT
|
AC_OUTPUT
|
||||||
|
|
||||||
@@ -530,6 +684,7 @@ AC_MSG_NOTICE([summary of build options:
|
|||||||
Install prefix: ${prefix}
|
Install prefix: ${prefix}
|
||||||
C compiler: ${CC}
|
C compiler: ${CC}
|
||||||
CFLAGS: ${CFLAGS}
|
CFLAGS: ${CFLAGS}
|
||||||
|
WARNCFLAGS: ${WARNCFLAGS}
|
||||||
LDFLAGS: ${LDFLAGS}
|
LDFLAGS: ${LDFLAGS}
|
||||||
LIBS: ${LIBS}
|
LIBS: ${LIBS}
|
||||||
CPPFLAGS: ${CPPFLAGS}
|
CPPFLAGS: ${CPPFLAGS}
|
||||||
@@ -538,6 +693,7 @@ AC_MSG_NOTICE([summary of build options:
|
|||||||
CXXFLAGS: ${CXXFLAGS}
|
CXXFLAGS: ${CXXFLAGS}
|
||||||
CXXCPP: ${CXXCPP}
|
CXXCPP: ${CXXCPP}
|
||||||
Library types: Shared=${enable_shared}, Static=${enable_static}
|
Library types: Shared=${enable_shared}, Static=${enable_static}
|
||||||
|
Python:
|
||||||
Python: ${PYTHON}
|
Python: ${PYTHON}
|
||||||
PYTHON_VERSION: ${PYTHON_VERSION}
|
PYTHON_VERSION: ${PYTHON_VERSION}
|
||||||
pyexecdir: ${pyexecdir}
|
pyexecdir: ${pyexecdir}
|
||||||
@@ -545,16 +701,27 @@ AC_MSG_NOTICE([summary of build options:
|
|||||||
PYTHON_CPPFLAGS:${PYTHON_CPPFLAGS}
|
PYTHON_CPPFLAGS:${PYTHON_CPPFLAGS}
|
||||||
PYTHON_LDFLAGS: ${PYTHON_LDFLAGS}
|
PYTHON_LDFLAGS: ${PYTHON_LDFLAGS}
|
||||||
Cython: ${CYTHON}
|
Cython: ${CYTHON}
|
||||||
|
Test:
|
||||||
CUnit: ${have_cunit}
|
CUnit: ${have_cunit}
|
||||||
|
Failmalloc: ${enable_failmalloc}
|
||||||
|
Libs:
|
||||||
OpenSSL: ${have_openssl}
|
OpenSSL: ${have_openssl}
|
||||||
Libxml2: ${have_libxml2}
|
Libxml2: ${have_libxml2}
|
||||||
|
Libev: ${have_libev}
|
||||||
Libevent(SSL): ${have_libevent_openssl}
|
Libevent(SSL): ${have_libevent_openssl}
|
||||||
Spdylay: ${have_spdylay}
|
Spdylay: ${have_spdylay}
|
||||||
Jansson: ${have_jansson}
|
Jansson: ${have_jansson}
|
||||||
Jemalloc: ${have_jemalloc}
|
Jemalloc: ${have_jemalloc}
|
||||||
|
Boost CPPFLAGS: ${BOOST_CPPFLAGS}
|
||||||
|
Boost LDFLAGS: ${BOOST_LDFLAGS}
|
||||||
|
Boost::ASIO: ${BOOST_ASIO_LIB}
|
||||||
|
Boost::System: ${BOOST_SYSTEM_LIB}
|
||||||
|
Boost::Thread: ${BOOST_THREAD_LIB}
|
||||||
|
Features:
|
||||||
Applications: ${enable_app}
|
Applications: ${enable_app}
|
||||||
HPACK tools: ${enable_hpack_tools}
|
HPACK tools: ${enable_hpack_tools}
|
||||||
|
Libnghttp2_asio:${enable_asio_lib}
|
||||||
Examples: ${enable_examples}
|
Examples: ${enable_examples}
|
||||||
Python bindings:${enable_python_bindings}
|
Python bindings:${enable_python_bindings}
|
||||||
Failmalloc: ${request_failmalloc}
|
Threading: ${enable_threads}
|
||||||
])
|
])
|
||||||
|
|||||||
1
contrib/.gitignore
vendored
Normal file
1
contrib/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
nghttpx-init
|
||||||
39
contrib/Makefile.am
Normal file
39
contrib/Makefile.am
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# nghttp2 - HTTP/2 C Library
|
||||||
|
|
||||||
|
# Copyright (c) 2014 Tatsuhiro Tsujikawa
|
||||||
|
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
# a copy of this software and associated documentation files (the
|
||||||
|
# "Software"), to deal in the Software without restriction, including
|
||||||
|
# without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
# permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
# the following conditions:
|
||||||
|
|
||||||
|
# The above copyright notice and this permission notice shall be
|
||||||
|
# included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
EXTRA_DIST = nghttpx-init.in nghttpx-logrotate
|
||||||
|
|
||||||
|
edit = sed -e 's|@bindir[@]|$(bindir)|g'
|
||||||
|
|
||||||
|
nghttpx-init: Makefile
|
||||||
|
rm -f $@ $@.tmp
|
||||||
|
$(edit) $(srcdir)/$@.in > $@.tmp
|
||||||
|
chmod +x $@.tmp
|
||||||
|
mv $@.tmp $@
|
||||||
|
|
||||||
|
nghttpx-init: $(srcdir)/nghttpx-init.in
|
||||||
|
|
||||||
|
all-local: nghttpx-init
|
||||||
|
|
||||||
|
clean-local:
|
||||||
|
-rm -f nghttpx-init nghttpx-init.tmp
|
||||||
173
contrib/nghttpx-init.in
Normal file
173
contrib/nghttpx-init.in
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
#! /bin/sh
|
||||||
|
### BEGIN INIT INFO
|
||||||
|
# Provides: nghttpx
|
||||||
|
# Required-Start: $remote_fs $syslog
|
||||||
|
# Required-Stop: $remote_fs $syslog
|
||||||
|
# Default-Start: 2 3 4 5
|
||||||
|
# Default-Stop: 0 1 6
|
||||||
|
# Short-Description: nghttpx initscript
|
||||||
|
# Description: nghttpx initscript
|
||||||
|
### END INIT INFO
|
||||||
|
|
||||||
|
# Author: Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
|
||||||
|
#
|
||||||
|
# Do NOT "set -e"
|
||||||
|
|
||||||
|
# PATH should only include /usr/* if it runs after the mountnfs.sh script
|
||||||
|
PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin
|
||||||
|
DESC="HTTP/2 reverse proxy"
|
||||||
|
NAME=nghttpx
|
||||||
|
# Depending on the configuration, binary may be located under @sbindir@
|
||||||
|
DAEMON=@bindir@/$NAME
|
||||||
|
PIDFILE=/var/run/$NAME.pid
|
||||||
|
DAEMON_ARGS="--conf /etc/nghttpx/nghttpx.conf --pid-file=$PIDFILE"
|
||||||
|
SCRIPTNAME=/etc/init.d/$NAME
|
||||||
|
|
||||||
|
# Exit if the package is not installed
|
||||||
|
[ -x "$DAEMON" ] || exit 0
|
||||||
|
|
||||||
|
# Read configuration variable file if it is present
|
||||||
|
[ -r /etc/default/$NAME ] && . /etc/default/$NAME
|
||||||
|
|
||||||
|
# Load the VERBOSE setting and other rcS variables
|
||||||
|
. /lib/init/vars.sh
|
||||||
|
|
||||||
|
# Define LSB log_* functions.
|
||||||
|
# Depend on lsb-base (>= 3.2-14) to ensure that this file is present
|
||||||
|
# and status_of_proc is working.
|
||||||
|
. /lib/lsb/init-functions
|
||||||
|
|
||||||
|
#
|
||||||
|
# Function that starts the daemon/service
|
||||||
|
#
|
||||||
|
do_start()
|
||||||
|
{
|
||||||
|
# Return
|
||||||
|
# 0 if daemon has been started
|
||||||
|
# 1 if daemon was already running
|
||||||
|
# 2 if daemon could not be started
|
||||||
|
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
|
||||||
|
|| return 1
|
||||||
|
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \
|
||||||
|
$DAEMON_ARGS \
|
||||||
|
|| return 2
|
||||||
|
# Add code here, if necessary, that waits for the process to be ready
|
||||||
|
# to handle requests from services started subsequently which depend
|
||||||
|
# on this one. As a last resort, sleep for some time.
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Function that stops the daemon/service
|
||||||
|
#
|
||||||
|
do_stop()
|
||||||
|
{
|
||||||
|
# Return
|
||||||
|
# 0 if daemon has been stopped
|
||||||
|
# 1 if daemon was already stopped
|
||||||
|
# 2 if daemon could not be stopped
|
||||||
|
# other if a failure occurred
|
||||||
|
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE
|
||||||
|
RETVAL="$?"
|
||||||
|
[ "$RETVAL" = 2 ] && return 2
|
||||||
|
|
||||||
|
# Wait for children to finish too if this is a daemon that forks
|
||||||
|
# and if the daemon is only ever run from this initscript.
|
||||||
|
# If the above conditions are not satisfied then add some other code
|
||||||
|
# that waits for the process to drop all resources that could be
|
||||||
|
# needed by services started subsequently. A last resort is to
|
||||||
|
# sleep for some time.
|
||||||
|
#start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
|
||||||
|
#[ "$?" = 2 ] && return 2
|
||||||
|
# Many daemons don't delete their pidfiles when they exit.
|
||||||
|
rm -f $PIDFILE
|
||||||
|
return "$RETVAL"
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Function that sends a SIGHUP to the daemon/service
|
||||||
|
#
|
||||||
|
do_reload() {
|
||||||
|
#
|
||||||
|
# If the daemon can reload its configuration without
|
||||||
|
# restarting (for example, when it is sent a SIGHUP),
|
||||||
|
# then implement that here.
|
||||||
|
#
|
||||||
|
start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
start)
|
||||||
|
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
|
||||||
|
do_start
|
||||||
|
case "$?" in
|
||||||
|
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
|
||||||
|
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
stop)
|
||||||
|
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
|
||||||
|
do_stop
|
||||||
|
case "$?" in
|
||||||
|
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
|
||||||
|
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
status)
|
||||||
|
status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
|
||||||
|
;;
|
||||||
|
upgrade)
|
||||||
|
log_daemon_msg "Upgrade $DESC" "$NAME"
|
||||||
|
pid=`pidofproc -p $PIDFILE $NAME`
|
||||||
|
case "$?" in
|
||||||
|
0) echo "Sending USR2 signal to $pid"
|
||||||
|
kill -USR2 $pid
|
||||||
|
echo "Waiting for new binary..."
|
||||||
|
sleep 5
|
||||||
|
echo "Sending QUIT signal to $pid"
|
||||||
|
kill -QUIT $pid
|
||||||
|
log_end_msg 0
|
||||||
|
;;
|
||||||
|
*) echo "pidofproc() failed"
|
||||||
|
log_end_msg 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
#reload|force-reload)
|
||||||
|
#
|
||||||
|
# If do_reload() is not implemented then leave this commented out
|
||||||
|
# and leave 'force-reload' as an alias for 'restart'.
|
||||||
|
#
|
||||||
|
#log_daemon_msg "Reloading $DESC" "$NAME"
|
||||||
|
#do_reload
|
||||||
|
#log_end_msg $?
|
||||||
|
#;;
|
||||||
|
restart|force-reload)
|
||||||
|
#
|
||||||
|
# If the "reload" option is implemented then remove the
|
||||||
|
# 'force-reload' alias
|
||||||
|
#
|
||||||
|
log_daemon_msg "Restarting $DESC" "$NAME"
|
||||||
|
do_stop
|
||||||
|
case "$?" in
|
||||||
|
0|1)
|
||||||
|
do_start
|
||||||
|
case "$?" in
|
||||||
|
0) log_end_msg 0 ;;
|
||||||
|
1) log_end_msg 1 ;; # Old process is still running
|
||||||
|
*) log_end_msg 1 ;; # Failed to start
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
# Failed to stop
|
||||||
|
log_end_msg 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload|upgrade}" >&2
|
||||||
|
exit 3
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
:
|
||||||
18
contrib/nghttpx-logrotate
Normal file
18
contrib/nghttpx-logrotate
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
/var/log/nghttpx/*.log {
|
||||||
|
weekly
|
||||||
|
missingok
|
||||||
|
rotate 52
|
||||||
|
compress
|
||||||
|
delaycompress
|
||||||
|
notifempty
|
||||||
|
create 0640 www-data adm
|
||||||
|
sharedscripts
|
||||||
|
prerotate
|
||||||
|
if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
|
||||||
|
run-parts /etc/logrotate.d/httpd-prerotate; \
|
||||||
|
fi \
|
||||||
|
endscript
|
||||||
|
postrotate
|
||||||
|
[ -s /run/nghttpx.pid ] && kill -USR1 `cat /run/nghttpx.pid`
|
||||||
|
endscript
|
||||||
|
}
|
||||||
@@ -37,8 +37,10 @@ EXTRA_DIST = \
|
|||||||
sources/tutorial-hpack.rst \
|
sources/tutorial-hpack.rst \
|
||||||
sources/nghttpx-howto.rst \
|
sources/nghttpx-howto.rst \
|
||||||
sources/h2load-howto.rst \
|
sources/h2load-howto.rst \
|
||||||
|
sources/libnghttp2_asio.rst \
|
||||||
sources/python-apiref.rst \
|
sources/python-apiref.rst \
|
||||||
sources/building-android-binary.rst \
|
sources/building-android-binary.rst \
|
||||||
|
sources/contribute.rst \
|
||||||
_themes/sphinx_rtd_theme/footer.html \
|
_themes/sphinx_rtd_theme/footer.html \
|
||||||
_themes/sphinx_rtd_theme/theme.conf \
|
_themes/sphinx_rtd_theme/theme.conf \
|
||||||
_themes/sphinx_rtd_theme/layout_old.html \
|
_themes/sphinx_rtd_theme/layout_old.html \
|
||||||
@@ -174,7 +176,7 @@ text:
|
|||||||
@echo
|
@echo
|
||||||
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
||||||
|
|
||||||
man:
|
man: apiref.rst
|
||||||
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
||||||
@echo
|
@echo
|
||||||
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
||||||
|
|||||||
3
doc/_themes/sphinx_rtd_theme/footer.html
vendored
3
doc/_themes/sphinx_rtd_theme/footer.html
vendored
@@ -28,5 +28,6 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% trans %}<a href="https://github.com/snide/sphinx_rtd_theme">Sphinx theme</a> provided by <a href="https://readthedocs.org">Read the Docs</a>{% endtrans %}
|
{% trans %}Built with <a href="http://sphinx-doc.org/">Sphinx</a> using a <a href="https://github.com/snide/sphinx_rtd_theme">theme</a> provided by <a href="https://readthedocs.org">Read the Docs</a>{% endtrans %}.
|
||||||
|
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
4
doc/_themes/sphinx_rtd_theme/layout.html
vendored
4
doc/_themes/sphinx_rtd_theme/layout.html
vendored
@@ -23,7 +23,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{# CSS #}
|
{# CSS #}
|
||||||
<link href='https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic|Roboto+Slab:400,700|Inconsolata:400,700' rel='stylesheet' type='text/css'>
|
<link href='https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic|Roboto+Slab:400,700|Inconsolata:400,700&subset=latin,cyrillic' rel='stylesheet' type='text/css'>
|
||||||
|
|
||||||
{# OPENSEARCH #}
|
{# OPENSEARCH #}
|
||||||
{% if not embedded %}
|
{% if not embedded %}
|
||||||
@@ -89,6 +89,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
|
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
|
||||||
|
{% block menu %}
|
||||||
{% set toctree = toctree(maxdepth=2, collapse=False, includehidden=True) %}
|
{% set toctree = toctree(maxdepth=2, collapse=False, includehidden=True) %}
|
||||||
{% if toctree %}
|
{% if toctree %}
|
||||||
{{ toctree }}
|
{{ toctree }}
|
||||||
@@ -96,6 +97,7 @@
|
|||||||
<!-- Local TOC -->
|
<!-- Local TOC -->
|
||||||
<div class="local-toc">{{ toc }}</div>
|
<div class="local-toc">{{ toc }}</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
2
doc/_themes/sphinx_rtd_theme/searchbox.html
vendored
2
doc/_themes/sphinx_rtd_theme/searchbox.html
vendored
@@ -1,3 +1,4 @@
|
|||||||
|
{%- if builder != 'singlehtml' %}
|
||||||
<div role="search">
|
<div role="search">
|
||||||
<form id="rtd-search-form" class="wy-form" action="{{ pathto('search') }}" method="get">
|
<form id="rtd-search-form" class="wy-form" action="{{ pathto('search') }}" method="get">
|
||||||
<input type="text" name="q" placeholder="Search docs" />
|
<input type="text" name="q" placeholder="Search docs" />
|
||||||
@@ -5,3 +6,4 @@
|
|||||||
<input type="hidden" name="area" value="default" />
|
<input type="hidden" name="area" value="default" />
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
{%- endif %}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -26,6 +26,8 @@ do not send client connection preface
|
|||||||
responsible to send it before sending any HTTP/2 frames using these
|
responsible to send it before sending any HTTP/2 frames using these
|
||||||
functions if :type:`nghttp2_session` is configured as client.
|
functions if :type:`nghttp2_session` is configured as client.
|
||||||
Similarly, `nghttp2_session_recv()` and `nghttp2_session_mem_recv()`
|
Similarly, `nghttp2_session_recv()` and `nghttp2_session_mem_recv()`
|
||||||
do not consume client connection preface. The applications are
|
do not consume client connection preface unless
|
||||||
responsible to receive it before calling these functions if
|
`nghttp2_option_set_recv_client_preface()` is used with nonzero option
|
||||||
:type:`nghttp2_session` is configured as server.
|
value. The applications are responsible to receive it before calling
|
||||||
|
these functions if :type:`nghttp2_session` is configured as server and
|
||||||
|
`nghttp2_option_set_recv_client_preface()` is not used.
|
||||||
|
|||||||
5
doc/asio_http2.h.rst.in
Normal file
5
doc/asio_http2.h.rst.in
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
asio_http2.h
|
||||||
|
============
|
||||||
|
|
||||||
|
.. literalinclude:: @top_srcdir@/src/includes/nghttp2/asio_http2.h
|
||||||
|
:language: cpp
|
||||||
@@ -64,7 +64,7 @@ master_doc = 'index'
|
|||||||
|
|
||||||
# General information about the project.
|
# General information about the project.
|
||||||
project = u'nghttp2'
|
project = u'nghttp2'
|
||||||
copyright = u'2012, 2014, Tatsuhiro Tsujikawa'
|
copyright = u'2012, 2015, Tatsuhiro Tsujikawa'
|
||||||
|
|
||||||
# The version info for the project you're documenting, acts as replacement for
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
# |version| and |release|, also used in various other places throughout the
|
# |version| and |release|, also used in various other places throughout the
|
||||||
@@ -155,7 +155,7 @@ html_theme_path = ['@top_srcdir@/doc/_themes']
|
|||||||
|
|
||||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||||
# typographically correct entities.
|
# typographically correct entities.
|
||||||
#html_use_smartypants = True
|
html_use_smartypants = False
|
||||||
|
|
||||||
# Custom sidebar templates, maps document names to template names.
|
# Custom sidebar templates, maps document names to template names.
|
||||||
html_sidebars = {
|
html_sidebars = {
|
||||||
@@ -242,6 +242,12 @@ latex_documents = [
|
|||||||
# One entry per manual page. List of tuples
|
# One entry per manual page. List of tuples
|
||||||
# (source start file, name, description, authors, manual section).
|
# (source start file, name, description, authors, manual section).
|
||||||
man_pages = [
|
man_pages = [
|
||||||
('index', 'nghttp2', u'nghttp2 Documentation',
|
('nghttp.1', 'nghttp', u'HTTP/2 experimental client',
|
||||||
|
[u'Tatsuhiro Tsujikawa'], 1),
|
||||||
|
('nghttpd.1', 'nghttpd', u'HTTP/2 experimental server',
|
||||||
|
[u'Tatsuhiro Tsujikawa'], 1),
|
||||||
|
('nghttpx.1', 'nghttpx', u'HTTP/2 experimental proxy',
|
||||||
|
[u'Tatsuhiro Tsujikawa'], 1),
|
||||||
|
('h2load.1', 'h2load', u'HTTP/2 benchmarking tool',
|
||||||
[u'Tatsuhiro Tsujikawa'], 1)
|
[u'Tatsuhiro Tsujikawa'], 1)
|
||||||
]
|
]
|
||||||
|
|||||||
1
doc/contribute.rst.in
Normal file
1
doc/contribute.rst.in
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.. include:: @top_srcdir@/doc/sources/contribute.rst
|
||||||
165
doc/h2load.1
165
doc/h2load.1
@@ -1,61 +1,142 @@
|
|||||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.1.
|
.\" Man page generated from reStructuredText.
|
||||||
.TH H2LOAD "1" "September 2014" "h2load nghttp2/0.6.1" "User Commands"
|
.
|
||||||
|
.TH "H2LOAD" "1" "January 25, 2015" "0.7.3" "nghttp2"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
h2load \- HTTP/2 benchmarking tool
|
h2load \- HTTP/2 benchmarking tool
|
||||||
|
.
|
||||||
|
.nr rst2man-indent-level 0
|
||||||
|
.
|
||||||
|
.de1 rstReportMargin
|
||||||
|
\\$1 \\n[an-margin]
|
||||||
|
level \\n[rst2man-indent-level]
|
||||||
|
level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||||
|
-
|
||||||
|
\\n[rst2man-indent0]
|
||||||
|
\\n[rst2man-indent1]
|
||||||
|
\\n[rst2man-indent2]
|
||||||
|
..
|
||||||
|
.de1 INDENT
|
||||||
|
.\" .rstReportMargin pre:
|
||||||
|
. RS \\$1
|
||||||
|
. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
|
||||||
|
. nr rst2man-indent-level +1
|
||||||
|
.\" .rstReportMargin post:
|
||||||
|
..
|
||||||
|
.de UNINDENT
|
||||||
|
. RE
|
||||||
|
.\" indent \\n[an-margin]
|
||||||
|
.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||||
|
.nr rst2man-indent-level -1
|
||||||
|
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||||
|
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||||
|
..
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
.B h2load
|
.sp
|
||||||
[\fI\,OPTIONS\/\fR]... \fI\,<URI>\/\fR...
|
\fBh2load\fP [OPTIONS]... [URI]...
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
|
.sp
|
||||||
benchmarking tool for HTTP/2 and SPDY server
|
benchmarking tool for HTTP/2 and SPDY server
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
<URI>
|
.B <URI>
|
||||||
Specify URI to access. Multiple URIs can be
|
Specify URI to access. Multiple URIs can be specified.
|
||||||
specified. URIs are used in this order for each
|
URIs are used in this order for each client. All URIs
|
||||||
client. All URIs are used, then first URI is
|
are used, then first URI is used and then 2nd URI, and
|
||||||
used and then 2nd URI, and so on. The scheme,
|
so on. The scheme, host and port in the subsequent
|
||||||
host and port in the subsequent URIs, if present,
|
URIs, if present, are ignored. Those in the first URI
|
||||||
are ignored. Those in the first URI are used
|
are used solely.
|
||||||
solely.
|
.UNINDENT
|
||||||
.SH OPTIONS
|
.SH OPTIONS:
|
||||||
.HP
|
.INDENT 0.0
|
||||||
\fB\-n\fR, \fB\-\-requests=\fR<N> Number of requests. Default: 1
|
|
||||||
.TP
|
.TP
|
||||||
\fB\-c\fR, \fB\-\-clients=\fR<N>
|
.B \-n, \-\-requests=<N>
|
||||||
Number of concurrent clients. Default: 1
|
Number of requests.
|
||||||
|
.sp
|
||||||
|
Default: \fB1\fP
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-t\fR, \fB\-\-threads=\fR<N>
|
.B \-c, \-\-clients=<N>
|
||||||
Number of native threads. Default: 1
|
Number of concurrent clients.
|
||||||
|
.sp
|
||||||
|
Default: \fB1\fP
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-m\fR, \fB\-\-max\-concurrent\-streams=\fR(auto|<N>)
|
.B \-t, \-\-threads=<N>
|
||||||
Max concurrent streams to issue per session. If
|
Number of native threads.
|
||||||
"auto" is given, the number of given URIs is
|
.sp
|
||||||
used. Default: auto
|
Default: \fB1\fP
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-w\fR, \fB\-\-window\-bits=\fR<N>
|
.B \-i, \-\-input\-file=<FILE>
|
||||||
Sets the stream level initial window size to
|
Path of a file with multiple URIs are seperated by EOLs.
|
||||||
(2**<N>)\-1. For SPDY, 2**<N> is used instead.
|
This option will disable URIs getting from command\-line.
|
||||||
|
If \(aq\-\(aq is given as <FILE>, URIs will be read from stdin.
|
||||||
|
URIs are used in this order for each client. All URIs
|
||||||
|
are used, then first URI is used and then 2nd URI, and
|
||||||
|
so on. The scheme, host and port in the subsequent
|
||||||
|
URIs, if present, are ignored. Those in the first URI
|
||||||
|
are used solely.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-W\fR, \fB\-\-connection\-window\-bits=\fR<N>
|
.B \-m, \-\-max\-concurrent\-streams=(auto|<N>)
|
||||||
|
Max concurrent streams to issue per session. If "auto"
|
||||||
|
is given, the number of given URIs is used.
|
||||||
|
.sp
|
||||||
|
Default: \fBauto\fP
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
|
.TP
|
||||||
|
.B \-w, \-\-window\-bits=<N>
|
||||||
|
Sets the stream level initial window size to (2**<N>)\-1.
|
||||||
|
For SPDY, 2**<N> is used instead.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
|
.TP
|
||||||
|
.B \-W, \-\-connection\-window\-bits=<N>
|
||||||
Sets the connection level initial window size to
|
Sets the connection level initial window size to
|
||||||
(2**<N>)\-1. For SPDY, if <N> is strictly less
|
(2**<N>)\-1. For SPDY, if <N> is strictly less than 16,
|
||||||
than 16, this option is ignored. Otherwise
|
this option is ignored. Otherwise 2**<N> is used for
|
||||||
2**<N> is used for SPDY.
|
SPDY.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-p\fR, \fB\-\-no\-tls\-proto=\fR<PROTOID>
|
.B \-H, \-\-header=<HEADER>
|
||||||
Specify ALPN identifier of the protocol to be
|
Add/Override a header to the requests.
|
||||||
used when accessing http URI without SSL/TLS.
|
.UNINDENT
|
||||||
Available protocols: spdy/2, spdy/3, spdy/3.1 and
|
.INDENT 0.0
|
||||||
h2c\-14
|
|
||||||
Default: h2c\-14
|
|
||||||
.TP
|
.TP
|
||||||
\fB\-v\fR, \fB\-\-verbose\fR
|
.B \-p, \-\-no\-tls\-proto=<PROTOID>
|
||||||
|
Specify ALPN identifier of the protocol to be used when
|
||||||
|
accessing http URI without SSL/TLS.
|
||||||
|
Available protocols: spdy/2, spdy/3, spdy/3.1 and h2c\-14
|
||||||
|
.sp
|
||||||
|
Default: \fBh2c\-14\fP
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
|
.TP
|
||||||
|
.B \-v, \-\-verbose
|
||||||
Output debug information.
|
Output debug information.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-version\fR
|
.B \-\-version
|
||||||
Display version information and exit.
|
Display version information and exit.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-h\fR, \fB\-\-help\fR
|
.B \-h, \-\-help
|
||||||
Display this help and exit.
|
Display this help and exit.
|
||||||
.SH "SEE ALSO"
|
.UNINDENT
|
||||||
|
.SH SEE ALSO
|
||||||
nghttp(1), nghttpd(1), nghttpx(1)
|
.sp
|
||||||
|
\fInghttp(1)\fP, \fInghttpd(1)\fP, \fInghttpx(1)\fP
|
||||||
|
.SH AUTHOR
|
||||||
|
Tatsuhiro Tsujikawa
|
||||||
|
.SH COPYRIGHT
|
||||||
|
2012, 2015, Tatsuhiro Tsujikawa
|
||||||
|
.\" Generated by docutils manpage writer.
|
||||||
|
.
|
||||||
|
|||||||
@@ -1,93 +1,102 @@
|
|||||||
.. program:: h2load
|
|
||||||
|
|
||||||
h2load(1)
|
h2load(1)
|
||||||
=========
|
=========
|
||||||
|
|
||||||
NAME
|
|
||||||
----
|
|
||||||
h2load - HTTP/2 benchmarking tool
|
|
||||||
|
|
||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
--------
|
--------
|
||||||
**h2load** [OPTIONS]... <URI>...
|
|
||||||
|
**h2load** [OPTIONS]... [URI]...
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
benchmarking tool for HTTP/2 and SPDY server
|
benchmarking tool for HTTP/2 and SPDY server
|
||||||
|
|
||||||
.. option:: URI
|
.. describe:: <URI>
|
||||||
|
|
||||||
Specify URI to access. Multiple URIs can be
|
Specify URI to access. Multiple URIs can be specified.
|
||||||
specified. URIs are used in this order for each
|
URIs are used in this order for each client. All URIs
|
||||||
client. All URIs are used, then first URI is
|
are used, then first URI is used and then 2nd URI, and
|
||||||
used and then 2nd URI, and so on. The scheme,
|
so on. The scheme, host and port in the subsequent
|
||||||
host and port in the subsequent URIs, if present,
|
URIs, if present, are ignored. Those in the first URI
|
||||||
are ignored. Those in the first URI are used
|
are used solely.
|
||||||
solely.
|
|
||||||
|
|
||||||
OPTIONS
|
OPTIONS:
|
||||||
-------
|
--------
|
||||||
|
|
||||||
.. option:: -n, --requests=<N>
|
.. option:: -n, --requests=<N>
|
||||||
|
|
||||||
Number of requests. Default: 1
|
Number of requests.
|
||||||
|
|
||||||
|
Default: ``1``
|
||||||
|
|
||||||
.. option:: -c, --clients=<N>
|
.. option:: -c, --clients=<N>
|
||||||
|
|
||||||
|
Number of concurrent clients.
|
||||||
|
|
||||||
Number of concurrent clients. Default: 1
|
Default: ``1``
|
||||||
|
|
||||||
.. option:: -t, --threads=<N>
|
.. option:: -t, --threads=<N>
|
||||||
|
|
||||||
|
Number of native threads.
|
||||||
|
|
||||||
Number of native threads. Default: 1
|
Default: ``1``
|
||||||
|
|
||||||
|
.. option:: -i, --input-file=<FILE>
|
||||||
|
|
||||||
|
Path of a file with multiple URIs are seperated by EOLs.
|
||||||
|
This option will disable URIs getting from command-line.
|
||||||
|
If '-' is given as <FILE>, URIs will be read from stdin.
|
||||||
|
URIs are used in this order for each client. All URIs
|
||||||
|
are used, then first URI is used and then 2nd URI, and
|
||||||
|
so on. The scheme, host and port in the subsequent
|
||||||
|
URIs, if present, are ignored. Those in the first URI
|
||||||
|
are used solely.
|
||||||
|
|
||||||
.. option:: -m, --max-concurrent-streams=(auto|<N>)
|
.. option:: -m, --max-concurrent-streams=(auto|<N>)
|
||||||
|
|
||||||
|
Max concurrent streams to issue per session. If "auto"
|
||||||
|
is given, the number of given URIs is used.
|
||||||
|
|
||||||
Max concurrent streams to issue per session. If
|
Default: ``auto``
|
||||||
"auto" is given, the number of given URIs is
|
|
||||||
used. Default: auto
|
|
||||||
|
|
||||||
.. option:: -w, --window-bits=<N>
|
.. option:: -w, --window-bits=<N>
|
||||||
|
|
||||||
|
Sets the stream level initial window size to (2\*\*<N>)-1.
|
||||||
Sets the stream level initial window size to
|
For SPDY, 2**<N> is used instead.
|
||||||
(2\*\*<N>)-1. For SPDY, 2\*\*<N> is used instead.
|
|
||||||
|
|
||||||
.. option:: -W, --connection-window-bits=<N>
|
.. option:: -W, --connection-window-bits=<N>
|
||||||
|
|
||||||
|
|
||||||
Sets the connection level initial window size to
|
Sets the connection level initial window size to
|
||||||
(2\*\*<N>)-1. For SPDY, if <N> is strictly less
|
(2**<N>)-1. For SPDY, if <N> is strictly less than 16,
|
||||||
than 16, this option is ignored. Otherwise
|
this option is ignored. Otherwise 2\*\*<N> is used for
|
||||||
2\*\*<N> is used for SPDY.
|
SPDY.
|
||||||
|
|
||||||
|
.. option:: -H, --header=<HEADER>
|
||||||
|
|
||||||
|
Add/Override a header to the requests.
|
||||||
|
|
||||||
.. option:: -p, --no-tls-proto=<PROTOID>
|
.. option:: -p, --no-tls-proto=<PROTOID>
|
||||||
|
|
||||||
|
Specify ALPN identifier of the protocol to be used when
|
||||||
|
accessing http URI without SSL/TLS.
|
||||||
|
Available protocols: spdy/2, spdy/3, spdy/3.1 and h2c-14
|
||||||
|
|
||||||
Specify ALPN identifier of the protocol to be
|
Default: ``h2c-14``
|
||||||
used when accessing http URI without SSL/TLS.
|
|
||||||
Available protocols: spdy/2, spdy/3, spdy/3.1 and
|
|
||||||
h2c-14
|
|
||||||
Default: h2c-14
|
|
||||||
|
|
||||||
.. option:: -v, --verbose
|
.. option:: -v, --verbose
|
||||||
|
|
||||||
|
|
||||||
Output debug information.
|
Output debug information.
|
||||||
|
|
||||||
.. option:: --version
|
.. option:: --version
|
||||||
|
|
||||||
|
|
||||||
Display version information and exit.
|
Display version information and exit.
|
||||||
|
|
||||||
.. option:: -h, --help
|
.. option:: -h, --help
|
||||||
|
|
||||||
|
|
||||||
Display this help and exit.
|
Display this help and exit.
|
||||||
|
|
||||||
SEE ALSO
|
SEE ALSO
|
||||||
--------
|
--------
|
||||||
|
|
||||||
nghttp(1), nghttpd(1), nghttpx(1)
|
:manpage:`nghttp(1)`, :manpage:`nghttpd(1)`, :manpage:`nghttpx(1)`
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
[SEE ALSO]
|
|
||||||
|
|
||||||
nghttp(1), nghttpd(1), nghttpx(1)
|
|
||||||
4
doc/h2load.h2r
Normal file
4
doc/h2load.h2r
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
SEE ALSO
|
||||||
|
--------
|
||||||
|
|
||||||
|
:manpage:`nghttp(1)`, :manpage:`nghttpd(1)`, :manpage:`nghttpx(1)`
|
||||||
1
doc/libnghttp2_asio.rst.in
Normal file
1
doc/libnghttp2_asio.rst.in
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.. include:: @top_srcdir@/doc/sources/libnghttp2_asio.rst
|
||||||
237
doc/nghttp.1
237
doc/nghttp.1
@@ -1,108 +1,211 @@
|
|||||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.1.
|
.\" Man page generated from reStructuredText.
|
||||||
.TH NGHTTP "1" "September 2014" "nghttp nghttp2/0.6.1" "User Commands"
|
.
|
||||||
|
.TH "NGHTTP" "1" "January 25, 2015" "0.7.3" "nghttp2"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
nghttp \- HTTP/2 experimental client
|
nghttp \- HTTP/2 experimental client
|
||||||
|
.
|
||||||
|
.nr rst2man-indent-level 0
|
||||||
|
.
|
||||||
|
.de1 rstReportMargin
|
||||||
|
\\$1 \\n[an-margin]
|
||||||
|
level \\n[rst2man-indent-level]
|
||||||
|
level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||||
|
-
|
||||||
|
\\n[rst2man-indent0]
|
||||||
|
\\n[rst2man-indent1]
|
||||||
|
\\n[rst2man-indent2]
|
||||||
|
..
|
||||||
|
.de1 INDENT
|
||||||
|
.\" .rstReportMargin pre:
|
||||||
|
. RS \\$1
|
||||||
|
. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
|
||||||
|
. nr rst2man-indent-level +1
|
||||||
|
.\" .rstReportMargin post:
|
||||||
|
..
|
||||||
|
.de UNINDENT
|
||||||
|
. RE
|
||||||
|
.\" indent \\n[an-margin]
|
||||||
|
.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||||
|
.nr rst2man-indent-level -1
|
||||||
|
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||||
|
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||||
|
..
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
.B nghttp
|
.sp
|
||||||
[\fI\,OPTIONS\/\fR]... \fI\,<URI>\/\fR...
|
\fBnghttp\fP [OPTIONS]... <URI>...
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
|
.sp
|
||||||
HTTP/2 experimental client
|
HTTP/2 experimental client
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
<URI>
|
.B <URI>
|
||||||
Specify URI to access.
|
Specify URI to access.
|
||||||
.SH OPTIONS
|
.UNINDENT
|
||||||
|
.SH OPTIONS:
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-v\fR, \fB\-\-verbose\fR
|
.B \-v, \-\-verbose
|
||||||
Print debug information such as reception and
|
Print debug information such as reception and
|
||||||
transmission of frames and name/value pairs.
|
transmission of frames and name/value pairs. Specifying
|
||||||
|
this option multiple times increases verbosity.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-n\fR, \fB\-\-null\-out\fR
|
.B \-n, \-\-null\-out
|
||||||
Discard downloaded data.
|
Discard downloaded data.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-O\fR, \fB\-\-remote\-name\fR
|
.B \-O, \-\-remote\-name
|
||||||
Save download data in the current directory. The
|
Save download data in the current directory. The
|
||||||
filename is dereived from URI. If URI ends with
|
filename is dereived from URI. If URI ends with \(aq\fI/\fP\(aq,
|
||||||
\&'/', 'index.html' is used as a filename. Not
|
\(aqindex.html\(aq is used as a filename. Not implemented
|
||||||
implemented yet.
|
yet.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-t\fR, \fB\-\-timeout=\fR<N>
|
.B \-t, \-\-timeout=<SEC>
|
||||||
Timeout each request after <N> seconds.
|
Timeout each request after <SEC> seconds.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-w\fR, \fB\-\-window\-bits=\fR<N>
|
.B \-w, \-\-window\-bits=<N>
|
||||||
Sets the stream level initial window size to
|
Sets the stream level initial window size to 2**<N>\-1.
|
||||||
2**<N>\-1.
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-W\fR, \fB\-\-connection\-window\-bits=\fR<N>
|
.B \-W, \-\-connection\-window\-bits=<N>
|
||||||
Sets the connection level initial window size to
|
Sets the connection level initial window size to
|
||||||
2**<N>\-1.
|
2**<N>\-1.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-a\fR, \fB\-\-get\-assets\fR
|
.B \-a, \-\-get\-assets
|
||||||
Download assets such as stylesheets, images and
|
Download assets such as stylesheets, images and script
|
||||||
script files linked from the downloaded resource.
|
files linked from the downloaded resource. Only links
|
||||||
Only links whose origins are the same with the
|
whose origins are the same with the linking resource
|
||||||
linking resource will be downloaded.
|
will be downloaded. nghttp prioritizes resources using
|
||||||
|
HTTP/2 dependency based priority. The priority order,
|
||||||
|
from highest to lowest, is html itself, css, javascript
|
||||||
|
and images.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-s\fR, \fB\-\-stat\fR
|
.B \-s, \-\-stat
|
||||||
Print statistics.
|
Print statistics.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-H\fR, \fB\-\-header\fR
|
.B \-H, \-\-header=<HEADER>
|
||||||
Add a header to the requests.
|
Add a header to the requests. Example: \fI\%\-H\fP\(aq:method: PUT\(aq
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-cert=\fR<CERT>
|
.B \-\-cert=<CERT>
|
||||||
Use the specified client certificate file. The
|
Use the specified client certificate file. The file
|
||||||
file must be in PEM format.
|
must be in PEM format.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-key=\fR<KEY>
|
.B \-\-key=<KEY>
|
||||||
Use the client private key file. The file must
|
Use the client private key file. The file must be in
|
||||||
be in PEM format.
|
PEM format.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-d\fR, \fB\-\-data=\fR<FILE>
|
.B \-d, \-\-data=<FILE>
|
||||||
Post FILE to server. If '\-' is given, data will
|
Post FILE to server. If \(aq\-\(aq is given, data will be read
|
||||||
be read from stdin.
|
from stdin.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-m\fR, \fB\-\-multiply=\fR<N> Request each URI <N> times.
|
.B \-m, \-\-multiply=<N>
|
||||||
By default, same URI
|
Request each URI <N> times. By default, same URI is not
|
||||||
is not requested twice. This option disables it
|
requested twice. This option disables it too.
|
||||||
too.
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-u\fR, \fB\-\-upgrade\fR
|
.B \-u, \-\-upgrade
|
||||||
Perform HTTP Upgrade for HTTP/2. This option is
|
Perform HTTP Upgrade for HTTP/2. This option is ignored
|
||||||
ignored if the request URI has https scheme. If
|
if the request URI has https scheme. If \fI\-d\fP is used, the
|
||||||
\fB\-d\fR is used, the HTTP upgrade request is performed
|
HTTP upgrade request is performed with OPTIONS method.
|
||||||
with OPTIONS method.
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-p\fR, \fB\-\-weight=\fR<WEIGHT>
|
.B \-p, \-\-weight=<WEIGHT>
|
||||||
Sets priority group weight. The valid value
|
Sets priority group weight. The valid value range is
|
||||||
range is [1, 256], inclusive.
|
[1, 256], inclusive.
|
||||||
Default: 16
|
.sp
|
||||||
|
Default: \fB16\fP
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-M\fR, \fB\-\-peer\-max\-concurrent\-streams=\fR<N>
|
.B \-M, \-\-peer\-max\-concurrent\-streams=<N>
|
||||||
Use <N> as SETTINGS_MAX_CONCURRENT_STREAMS value
|
Use <N> as SETTINGS_MAX_CONCURRENT_STREAMS value of
|
||||||
of remote endpoint as if it is received in
|
remote endpoint as if it is received in SETTINGS frame.
|
||||||
SETTINGS frame. The default is large enough as
|
The default is large enough as it is seen as unlimited.
|
||||||
it is seen as unlimited.
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-c\fR, \fB\-\-header\-table\-size=\fR<N>
|
.B \-c, \-\-header\-table\-size=<SIZE>
|
||||||
Specify decoder header table size.
|
Specify decoder header table size.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-b\fR, \fB\-\-padding=\fR<N>
|
.B \-b, \-\-padding=<N>
|
||||||
Add at most <N> bytes to a frame payload as
|
Add at most <N> bytes to a frame payload as padding.
|
||||||
padding. Specify 0 to disable padding.
|
Specify 0 to disable padding.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-color\fR
|
.B \-r, \-\-har=<FILE>
|
||||||
|
Output HTTP transactions <FILE> in HAR format. If \(aq\-\(aq
|
||||||
|
is given, data is written to stdout.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
|
.TP
|
||||||
|
.B \-\-color
|
||||||
Force colored log output.
|
Force colored log output.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-continuation\fR
|
.B \-\-continuation
|
||||||
Send large header to test CONTINUATION.
|
Send large header to test CONTINUATION.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-no\-content\-length\fR
|
.B \-\-no\-content\-length
|
||||||
Don't send content\-length header field.
|
Don\(aqt send content\-length header field.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-version\fR
|
.B \-\-no\-dep
|
||||||
|
Don\(aqt send dependency based priority hint to server.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
|
.TP
|
||||||
|
.B \-\-dep\-idle
|
||||||
|
Use idle streams as anchor nodes to express priority.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
|
.TP
|
||||||
|
.B \-\-version
|
||||||
Display version information and exit.
|
Display version information and exit.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-h\fR, \fB\-\-help\fR
|
.B \-h, \-\-help
|
||||||
Display this help and exit.
|
Display this help and exit.
|
||||||
.SH "SEE ALSO"
|
.UNINDENT
|
||||||
|
.sp
|
||||||
nghttpd(1), nghttpx(1), h2load(1)
|
The <SIZE> argument is an integer and an optional unit (e.g., 10K is
|
||||||
|
10 * 1024). Units are K, M and G (powers of 1024).
|
||||||
|
.SH SEE ALSO
|
||||||
|
.sp
|
||||||
|
\fInghttpd(1)\fP, \fInghttpx(1)\fP, \fIh2load(1)\fP
|
||||||
|
.SH AUTHOR
|
||||||
|
Tatsuhiro Tsujikawa
|
||||||
|
.SH COPYRIGHT
|
||||||
|
2012, 2015, Tatsuhiro Tsujikawa
|
||||||
|
.\" Generated by docutils manpage writer.
|
||||||
|
.
|
||||||
|
|||||||
126
doc/nghttp.1.rst
126
doc/nghttp.1.rst
@@ -1,166 +1,158 @@
|
|||||||
.. program:: nghttp
|
|
||||||
|
|
||||||
nghttp(1)
|
nghttp(1)
|
||||||
=========
|
=========
|
||||||
|
|
||||||
NAME
|
|
||||||
----
|
|
||||||
nghttp - HTTP/2 experimental client
|
|
||||||
|
|
||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
--------
|
--------
|
||||||
|
|
||||||
**nghttp** [OPTIONS]... <URI>...
|
**nghttp** [OPTIONS]... <URI>...
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
HTTP/2 experimental client
|
HTTP/2 experimental client
|
||||||
|
|
||||||
.. option:: URI
|
.. describe:: <URI>
|
||||||
|
|
||||||
Specify URI to access.
|
Specify URI to access.
|
||||||
|
|
||||||
OPTIONS
|
OPTIONS:
|
||||||
-------
|
--------
|
||||||
|
|
||||||
.. option:: -v, --verbose
|
.. option:: -v, --verbose
|
||||||
|
|
||||||
|
|
||||||
Print debug information such as reception and
|
Print debug information such as reception and
|
||||||
transmission of frames and name/value pairs.
|
transmission of frames and name/value pairs. Specifying
|
||||||
|
this option multiple times increases verbosity.
|
||||||
|
|
||||||
.. option:: -n, --null-out
|
.. option:: -n, --null-out
|
||||||
|
|
||||||
|
|
||||||
Discard downloaded data.
|
Discard downloaded data.
|
||||||
|
|
||||||
.. option:: -O, --remote-name
|
.. option:: -O, --remote-name
|
||||||
|
|
||||||
|
|
||||||
Save download data in the current directory. The
|
Save download data in the current directory. The
|
||||||
filename is dereived from URI. If URI ends with
|
filename is dereived from URI. If URI ends with '*/*',
|
||||||
\&'/', 'index.html' is used as a filename. Not
|
'index.html' is used as a filename. Not implemented
|
||||||
implemented yet.
|
yet.
|
||||||
|
|
||||||
.. option:: -t, --timeout=<N>
|
.. option:: -t, --timeout=<SEC>
|
||||||
|
|
||||||
|
Timeout each request after <SEC> seconds.
|
||||||
Timeout each request after <N> seconds.
|
|
||||||
|
|
||||||
.. option:: -w, --window-bits=<N>
|
.. option:: -w, --window-bits=<N>
|
||||||
|
|
||||||
|
Sets the stream level initial window size to 2\*\*<N>-1.
|
||||||
Sets the stream level initial window size to
|
|
||||||
2\*\*<N>-1.
|
|
||||||
|
|
||||||
.. option:: -W, --connection-window-bits=<N>
|
.. option:: -W, --connection-window-bits=<N>
|
||||||
|
|
||||||
|
|
||||||
Sets the connection level initial window size to
|
Sets the connection level initial window size to
|
||||||
2\*\*<N>-1.
|
2\*\*<N>-1.
|
||||||
|
|
||||||
.. option:: -a, --get-assets
|
.. option:: -a, --get-assets
|
||||||
|
|
||||||
|
Download assets such as stylesheets, images and script
|
||||||
Download assets such as stylesheets, images and
|
files linked from the downloaded resource. Only links
|
||||||
script files linked from the downloaded resource.
|
whose origins are the same with the linking resource
|
||||||
Only links whose origins are the same with the
|
will be downloaded. nghttp prioritizes resources using
|
||||||
linking resource will be downloaded.
|
HTTP/2 dependency based priority. The priority order,
|
||||||
|
from highest to lowest, is html itself, css, javascript
|
||||||
|
and images.
|
||||||
|
|
||||||
.. option:: -s, --stat
|
.. option:: -s, --stat
|
||||||
|
|
||||||
|
|
||||||
Print statistics.
|
Print statistics.
|
||||||
|
|
||||||
.. option:: -H, --header
|
.. option:: -H, --header=<HEADER>
|
||||||
|
|
||||||
|
Add a header to the requests. Example: :option:`-H`\':method: PUT'
|
||||||
Add a header to the requests.
|
|
||||||
|
|
||||||
.. option:: --cert=<CERT>
|
.. option:: --cert=<CERT>
|
||||||
|
|
||||||
|
Use the specified client certificate file. The file
|
||||||
Use the specified client certificate file. The
|
must be in PEM format.
|
||||||
file must be in PEM format.
|
|
||||||
|
|
||||||
.. option:: --key=<KEY>
|
.. option:: --key=<KEY>
|
||||||
|
|
||||||
|
Use the client private key file. The file must be in
|
||||||
Use the client private key file. The file must
|
PEM format.
|
||||||
be in PEM format.
|
|
||||||
|
|
||||||
.. option:: -d, --data=<FILE>
|
.. option:: -d, --data=<FILE>
|
||||||
|
|
||||||
|
Post FILE to server. If '-' is given, data will be read
|
||||||
Post FILE to server. If '-' is given, data will
|
from stdin.
|
||||||
be read from stdin.
|
|
||||||
|
|
||||||
.. option:: -m, --multiply=<N>
|
.. option:: -m, --multiply=<N>
|
||||||
|
|
||||||
Request each URI <N> times.
|
Request each URI <N> times. By default, same URI is not
|
||||||
By default, same URI
|
requested twice. This option disables it too.
|
||||||
is not requested twice. This option disables it
|
|
||||||
too.
|
|
||||||
|
|
||||||
.. option:: -u, --upgrade
|
.. option:: -u, --upgrade
|
||||||
|
|
||||||
|
Perform HTTP Upgrade for HTTP/2. This option is ignored
|
||||||
Perform HTTP Upgrade for HTTP/2. This option is
|
if the request URI has https scheme. If :option:`-d` is used, the
|
||||||
ignored if the request URI has https scheme. If
|
HTTP upgrade request is performed with OPTIONS method.
|
||||||
:option:`-d` is used, the HTTP upgrade request is performed
|
|
||||||
with OPTIONS method.
|
|
||||||
|
|
||||||
.. option:: -p, --weight=<WEIGHT>
|
.. option:: -p, --weight=<WEIGHT>
|
||||||
|
|
||||||
|
Sets priority group weight. The valid value range is
|
||||||
|
[1, 256], inclusive.
|
||||||
|
|
||||||
Sets priority group weight. The valid value
|
Default: ``16``
|
||||||
range is [1, 256], inclusive.
|
|
||||||
Default: 16
|
|
||||||
|
|
||||||
.. option:: -M, --peer-max-concurrent-streams=<N>
|
.. option:: -M, --peer-max-concurrent-streams=<N>
|
||||||
|
|
||||||
|
Use <N> as SETTINGS_MAX_CONCURRENT_STREAMS value of
|
||||||
|
remote endpoint as if it is received in SETTINGS frame.
|
||||||
|
The default is large enough as it is seen as unlimited.
|
||||||
|
|
||||||
Use <N> as SETTINGS_MAX_CONCURRENT_STREAMS value
|
.. option:: -c, --header-table-size=<SIZE>
|
||||||
of remote endpoint as if it is received in
|
|
||||||
SETTINGS frame. The default is large enough as
|
|
||||||
it is seen as unlimited.
|
|
||||||
|
|
||||||
.. option:: -c, --header-table-size=<N>
|
|
||||||
|
|
||||||
|
|
||||||
Specify decoder header table size.
|
Specify decoder header table size.
|
||||||
|
|
||||||
.. option:: -b, --padding=<N>
|
.. option:: -b, --padding=<N>
|
||||||
|
|
||||||
|
Add at most <N> bytes to a frame payload as padding.
|
||||||
|
Specify 0 to disable padding.
|
||||||
|
|
||||||
Add at most <N> bytes to a frame payload as
|
.. option:: -r, --har=<FILE>
|
||||||
padding. Specify 0 to disable padding.
|
|
||||||
|
Output HTTP transactions <FILE> in HAR format. If '-'
|
||||||
|
is given, data is written to stdout.
|
||||||
|
|
||||||
.. option:: --color
|
.. option:: --color
|
||||||
|
|
||||||
|
|
||||||
Force colored log output.
|
Force colored log output.
|
||||||
|
|
||||||
.. option:: --continuation
|
.. option:: --continuation
|
||||||
|
|
||||||
|
|
||||||
Send large header to test CONTINUATION.
|
Send large header to test CONTINUATION.
|
||||||
|
|
||||||
.. option:: --no-content-length
|
.. option:: --no-content-length
|
||||||
|
|
||||||
|
|
||||||
Don't send content-length header field.
|
Don't send content-length header field.
|
||||||
|
|
||||||
.. option:: --version
|
.. option:: --no-dep
|
||||||
|
|
||||||
|
Don't send dependency based priority hint to server.
|
||||||
|
|
||||||
|
.. option:: --dep-idle
|
||||||
|
|
||||||
|
Use idle streams as anchor nodes to express priority.
|
||||||
|
|
||||||
|
.. option:: --version
|
||||||
|
|
||||||
Display version information and exit.
|
Display version information and exit.
|
||||||
|
|
||||||
.. option:: -h, --help
|
.. option:: -h, --help
|
||||||
|
|
||||||
|
|
||||||
Display this help and exit.
|
Display this help and exit.
|
||||||
|
|
||||||
|
|
||||||
|
The <SIZE> argument is an integer and an optional unit (e.g., 10K is
|
||||||
|
10 * 1024). Units are K, M and G (powers of 1024).
|
||||||
|
|
||||||
SEE ALSO
|
SEE ALSO
|
||||||
--------
|
--------
|
||||||
|
|
||||||
nghttpd(1), nghttpx(1), h2load(1)
|
:manpage:`nghttpd(1)`, :manpage:`nghttpx(1)`, :manpage:`h2load(1)`
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
[SEE ALSO]
|
|
||||||
|
|
||||||
nghttpd(1), nghttpx(1), h2load(1)
|
|
||||||
4
doc/nghttp.h2r
Normal file
4
doc/nghttp.h2r
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
SEE ALSO
|
||||||
|
--------
|
||||||
|
|
||||||
|
:manpage:`nghttpd(1)`, :manpage:`nghttpx(1)`, :manpage:`h2load(1)`
|
||||||
191
doc/nghttpd.1
191
doc/nghttpd.1
@@ -1,93 +1,160 @@
|
|||||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.1.
|
.\" Man page generated from reStructuredText.
|
||||||
.TH NGHTTPD "1" "September 2014" "nghttpd nghttp2/0.6.1" "User Commands"
|
.
|
||||||
|
.TH "NGHTTPD" "1" "January 25, 2015" "0.7.3" "nghttp2"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
nghttpd \- HTTP/2 experimental server
|
nghttpd \- HTTP/2 experimental server
|
||||||
|
.
|
||||||
|
.nr rst2man-indent-level 0
|
||||||
|
.
|
||||||
|
.de1 rstReportMargin
|
||||||
|
\\$1 \\n[an-margin]
|
||||||
|
level \\n[rst2man-indent-level]
|
||||||
|
level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||||
|
-
|
||||||
|
\\n[rst2man-indent0]
|
||||||
|
\\n[rst2man-indent1]
|
||||||
|
\\n[rst2man-indent2]
|
||||||
|
..
|
||||||
|
.de1 INDENT
|
||||||
|
.\" .rstReportMargin pre:
|
||||||
|
. RS \\$1
|
||||||
|
. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
|
||||||
|
. nr rst2man-indent-level +1
|
||||||
|
.\" .rstReportMargin post:
|
||||||
|
..
|
||||||
|
.de UNINDENT
|
||||||
|
. RE
|
||||||
|
.\" indent \\n[an-margin]
|
||||||
|
.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||||
|
.nr rst2man-indent-level -1
|
||||||
|
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||||
|
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||||
|
..
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
.B nghttpd
|
.sp
|
||||||
[\fI\,OPTION\/\fR]... \fI\,<PORT> <PRIVATE_KEY> <CERT>\/\fR
|
\fBnghttpd\fP [OPTION]... <PORT> [<PRIVATE_KEY> <CERT>]
|
||||||
.br
|
|
||||||
.B nghttpd
|
|
||||||
\fI\,--no-tls \/\fR[\fI\,OPTION\/\fR]... \fI\,<PORT>\/\fR
|
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
|
.sp
|
||||||
HTTP/2 experimental server
|
HTTP/2 experimental server
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
<PORT>
|
.B <PORT>
|
||||||
Specify listening port number.
|
Specify listening port number.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
<PRIVATE_KEY>
|
.B <PRIVATE_KEY>
|
||||||
Set path to server's private key. Required
|
Set path to server\(aqs private key. Required unless
|
||||||
unless \fB\-\-no\-tls\fR is specified.
|
\fI\%\-\-no\-tls\fP is specified.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
<CERT>
|
.B <CERT>
|
||||||
Set path to server's certificate. Required
|
Set path to server\(aqs certificate. Required unless
|
||||||
unless \fB\-\-no\-tls\fR is specified.
|
\fI\%\-\-no\-tls\fP is specified.
|
||||||
.SH OPTIONS
|
.UNINDENT
|
||||||
|
.SH OPTIONS:
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-D\fR, \fB\-\-daemon\fR
|
.B \-D, \-\-daemon
|
||||||
Run in a background. If \fB\-D\fR is used, the current
|
Run in a background. If \fI\-D\fP is used, the current working
|
||||||
working directory is changed to '/'. Therefore
|
directory is changed to \(aq\fI/\fP\(aq. Therefore if this option
|
||||||
if this option is used, \fB\-d\fR option must be
|
is used, \fI\%\-d\fP option must be specified.
|
||||||
specified.
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-V\fR, \fB\-\-verify\-client\fR
|
.B \-V, \-\-verify\-client
|
||||||
The server sends a client certificate request.
|
The server sends a client certificate request. If the
|
||||||
If the client did not return a certificate, the
|
client did not return a certificate, the handshake is
|
||||||
handshake is terminated. Currently, this option
|
terminated. Currently, this option just requests a
|
||||||
just requests a client certificate and does not
|
client certificate and does not verify it.
|
||||||
verify it.
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-d\fR, \fB\-\-htdocs=\fR<PATH>
|
.B \-d, \-\-htdocs=<PATH>
|
||||||
Specify document root. If this option is not
|
Specify document root. If this option is not specified,
|
||||||
specified, the document root is the current
|
the document root is the current working directory.
|
||||||
working directory.
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-v\fR, \fB\-\-verbose\fR
|
.B \-v, \-\-verbose
|
||||||
Print debug information such as reception/
|
Print debug information such as reception/ transmission
|
||||||
transmission of frames and name/value pairs.
|
of frames and name/value pairs.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-no\-tls\fR
|
.B \-\-no\-tls
|
||||||
Disable SSL/TLS.
|
Disable SSL/TLS.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-c\fR, \fB\-\-header\-table\-size=\fR<N>
|
.B \-c, \-\-header\-table\-size=<SIZE>
|
||||||
Specify decoder header table size.
|
Specify decoder header table size.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-color\fR
|
.B \-\-color
|
||||||
Force colored log output.
|
Force colored log output.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-p\fR, \fB\-\-push=\fR<PATH>=<PUSH_PATH,...>
|
.B \-p, \-\-push=<PATH>=<PUSH_PATH,...>
|
||||||
Push resources <PUSH_PATH>s when <PATH> is
|
Push resources <PUSH_PATH>s when <PATH> is requested.
|
||||||
requested. This option can be used repeatedly to
|
This option can be used repeatedly to specify multiple
|
||||||
specify multiple push configurations. <PATH> and
|
push configurations. <PATH> and <PUSH_PATH>s are
|
||||||
<PUSH_PATH>s are relative to document root. See
|
relative to document root. See \fI\%\-\-htdocs\fP option.
|
||||||
\fB\-\-htdocs\fR option. Example: \fB\-p\fR/=/foo.png
|
Example: \fI\-p\fP/=/foo.png \fI\-p\fP/doc=/bar.css
|
||||||
\fB\-p\fR/doc=/bar.css
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-b\fR, \fB\-\-padding=\fR<N>
|
.B \-b, \-\-padding=<N>
|
||||||
Add at most <N> bytes to a frame payload as
|
Add at most <N> bytes to a frame payload as padding.
|
||||||
padding. Specify 0 to disable padding.
|
Specify 0 to disable padding.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-n\fR, \fB\-\-workers=\fR<CORE>
|
.B \-n, \-\-workers=<N>
|
||||||
Set the number of worker threads.
|
Set the number of worker threads.
|
||||||
Default: 1
|
.sp
|
||||||
|
Default: \fB1\fP
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-e\fR, \fB\-\-error\-gzip\fR
|
.B \-e, \-\-error\-gzip
|
||||||
Make error response gzipped.
|
Make error response gzipped.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-dh\-param\-file=\fR<PATH>
|
.B \-\-dh\-param\-file=<PATH>
|
||||||
Path to file that contains DH parameters in PEM
|
Path to file that contains DH parameters in PEM format.
|
||||||
format. Without this option, DHE cipher suites
|
Without this option, DHE cipher suites are not
|
||||||
are not available.
|
available.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-early\-response\fR
|
.B \-\-early\-response
|
||||||
Start sending response when request HEADERS is
|
Start sending response when request HEADERS is received,
|
||||||
received, rather than complete request is
|
rather than complete request is received.
|
||||||
received.
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-version\fR
|
.B \-\-version
|
||||||
Display version information and exit.
|
Display version information and exit.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-h\fR, \fB\-\-help\fR
|
.B \-h, \-\-help
|
||||||
Display this help and exit.
|
Display this help and exit.
|
||||||
.SH "SEE ALSO"
|
.UNINDENT
|
||||||
|
.sp
|
||||||
nghttp(1), nghttpx(1), h2load(1)
|
The <SIZE> argument is an integer and an optional unit (e.g., 10K is
|
||||||
|
10 * 1024). Units are K, M and G (powers of 1024).
|
||||||
|
.SH SEE ALSO
|
||||||
|
.sp
|
||||||
|
\fInghttp(1)\fP, \fInghttpx(1)\fP, \fIh2load(1)\fP
|
||||||
|
.SH AUTHOR
|
||||||
|
Tatsuhiro Tsujikawa
|
||||||
|
.SH COPYRIGHT
|
||||||
|
2012, 2015, Tatsuhiro Tsujikawa
|
||||||
|
.\" Generated by docutils manpage writer.
|
||||||
|
.
|
||||||
|
|||||||
@@ -1,136 +1,117 @@
|
|||||||
.. program:: nghttpd
|
|
||||||
|
|
||||||
nghttpd(1)
|
nghttpd(1)
|
||||||
==========
|
==========
|
||||||
|
|
||||||
NAME
|
|
||||||
----
|
|
||||||
nghttpd - HTTP/2 experimental server
|
|
||||||
|
|
||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
--------
|
--------
|
||||||
**nghttpd** [OPTION]... <PORT> <PRIVATE_KEY> <CERT>
|
|
||||||
|
|
||||||
**nghttpd** --no-tls [OPTION]... <PORT>
|
**nghttpd** [OPTION]... <PORT> [<PRIVATE_KEY> <CERT>]
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
HTTP/2 experimental server
|
HTTP/2 experimental server
|
||||||
|
|
||||||
.. option:: PORT
|
.. describe:: <PORT>
|
||||||
|
|
||||||
Specify listening port number.
|
Specify listening port number.
|
||||||
|
|
||||||
.. option:: PRIVATE_KEY
|
.. describe:: <PRIVATE_KEY>
|
||||||
|
|
||||||
Set path to server's private key. Required
|
|
||||||
unless :option:`--no-tls` is specified.
|
|
||||||
|
|
||||||
.. option:: CERT
|
Set path to server's private key. Required unless
|
||||||
|
:option:`--no-tls` is specified.
|
||||||
|
|
||||||
Set path to server's certificate. Required
|
.. describe:: <CERT>
|
||||||
unless :option:`--no-tls` is specified.
|
|
||||||
|
|
||||||
OPTIONS
|
Set path to server's certificate. Required unless
|
||||||
-------
|
:option:`--no-tls` is specified.
|
||||||
|
|
||||||
|
OPTIONS:
|
||||||
|
--------
|
||||||
|
|
||||||
.. option:: -D, --daemon
|
.. option:: -D, --daemon
|
||||||
|
|
||||||
|
Run in a background. If :option:`-D` is used, the current working
|
||||||
Run in a background. If :option:`-D` is used, the current
|
directory is changed to '*/*'. Therefore if this option
|
||||||
working directory is changed to '/'. Therefore
|
is used, :option:`-d` option must be specified.
|
||||||
if this option is used, :option:`-d` option must be
|
|
||||||
specified.
|
|
||||||
|
|
||||||
.. option:: -V, --verify-client
|
.. option:: -V, --verify-client
|
||||||
|
|
||||||
|
The server sends a client certificate request. If the
|
||||||
The server sends a client certificate request.
|
client did not return a certificate, the handshake is
|
||||||
If the client did not return a certificate, the
|
terminated. Currently, this option just requests a
|
||||||
handshake is terminated. Currently, this option
|
client certificate and does not verify it.
|
||||||
just requests a client certificate and does not
|
|
||||||
verify it.
|
|
||||||
|
|
||||||
.. option:: -d, --htdocs=<PATH>
|
.. option:: -d, --htdocs=<PATH>
|
||||||
|
|
||||||
|
Specify document root. If this option is not specified,
|
||||||
Specify document root. If this option is not
|
the document root is the current working directory.
|
||||||
specified, the document root is the current
|
|
||||||
working directory.
|
|
||||||
|
|
||||||
.. option:: -v, --verbose
|
.. option:: -v, --verbose
|
||||||
|
|
||||||
|
Print debug information such as reception/ transmission
|
||||||
Print debug information such as reception/
|
of frames and name/value pairs.
|
||||||
transmission of frames and name/value pairs.
|
|
||||||
|
|
||||||
.. option:: --no-tls
|
.. option:: --no-tls
|
||||||
|
|
||||||
|
|
||||||
Disable SSL/TLS.
|
Disable SSL/TLS.
|
||||||
|
|
||||||
.. option:: -c, --header-table-size=<N>
|
.. option:: -c, --header-table-size=<SIZE>
|
||||||
|
|
||||||
|
|
||||||
Specify decoder header table size.
|
Specify decoder header table size.
|
||||||
|
|
||||||
.. option:: --color
|
.. option:: --color
|
||||||
|
|
||||||
|
|
||||||
Force colored log output.
|
Force colored log output.
|
||||||
|
|
||||||
.. option:: -p, --push=<PATH>=<PUSH_PATH,...>
|
.. option:: -p, --push=<PATH>=<PUSH_PATH,...>
|
||||||
|
|
||||||
|
Push resources <PUSH_PATH>s when <PATH> is requested.
|
||||||
Push resources <PUSH_PATH>s when <PATH> is
|
This option can be used repeatedly to specify multiple
|
||||||
requested. This option can be used repeatedly to
|
push configurations. <PATH> and <PUSH_PATH>s are
|
||||||
specify multiple push configurations. <PATH> and
|
relative to document root. See :option:`--htdocs` option.
|
||||||
<PUSH_PATH>s are relative to document root. See
|
Example: :option:`-p`\/=/foo.png :option:`-p`\/doc=/bar.css
|
||||||
:option:`--htdocs` option. Example: -p/=/foo.png
|
|
||||||
-p/doc=/bar.css
|
|
||||||
|
|
||||||
.. option:: -b, --padding=<N>
|
.. option:: -b, --padding=<N>
|
||||||
|
|
||||||
|
Add at most <N> bytes to a frame payload as padding.
|
||||||
|
Specify 0 to disable padding.
|
||||||
|
|
||||||
Add at most <N> bytes to a frame payload as
|
.. option:: -n, --workers=<N>
|
||||||
padding. Specify 0 to disable padding.
|
|
||||||
|
|
||||||
.. option:: -n, --workers=<CORE>
|
|
||||||
|
|
||||||
|
|
||||||
Set the number of worker threads.
|
Set the number of worker threads.
|
||||||
Default: 1
|
|
||||||
|
Default: ``1``
|
||||||
|
|
||||||
.. option:: -e, --error-gzip
|
.. option:: -e, --error-gzip
|
||||||
|
|
||||||
|
|
||||||
Make error response gzipped.
|
Make error response gzipped.
|
||||||
|
|
||||||
.. option:: --dh-param-file=<PATH>
|
.. option:: --dh-param-file=<PATH>
|
||||||
|
|
||||||
|
Path to file that contains DH parameters in PEM format.
|
||||||
Path to file that contains DH parameters in PEM
|
Without this option, DHE cipher suites are not
|
||||||
format. Without this option, DHE cipher suites
|
available.
|
||||||
are not available.
|
|
||||||
|
|
||||||
.. option:: --early-response
|
.. option:: --early-response
|
||||||
|
|
||||||
|
Start sending response when request HEADERS is received,
|
||||||
Start sending response when request HEADERS is
|
rather than complete request is received.
|
||||||
received, rather than complete request is
|
|
||||||
received.
|
|
||||||
|
|
||||||
.. option:: --version
|
.. option:: --version
|
||||||
|
|
||||||
|
|
||||||
Display version information and exit.
|
Display version information and exit.
|
||||||
|
|
||||||
.. option:: -h, --help
|
.. option:: -h, --help
|
||||||
|
|
||||||
|
|
||||||
Display this help and exit.
|
Display this help and exit.
|
||||||
|
|
||||||
|
|
||||||
|
The <SIZE> argument is an integer and an optional unit (e.g., 10K is
|
||||||
|
10 * 1024). Units are K, M and G (powers of 1024).
|
||||||
|
|
||||||
SEE ALSO
|
SEE ALSO
|
||||||
--------
|
--------
|
||||||
|
|
||||||
nghttp(1), nghttpx(1), h2load(1)
|
:manpage:`nghttp(1)`, :manpage:`nghttpx(1)`, :manpage:`h2load(1)`
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
[SEE ALSO]
|
|
||||||
|
|
||||||
nghttp(1), nghttpx(1), h2load(1)
|
|
||||||
4
doc/nghttpd.h2r
Normal file
4
doc/nghttpd.h2r
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
SEE ALSO
|
||||||
|
--------
|
||||||
|
|
||||||
|
:manpage:`nghttp(1)`, :manpage:`nghttpx(1)`, :manpage:`h2load(1)`
|
||||||
1013
doc/nghttpx.1
1013
doc/nghttpx.1
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,3 +0,0 @@
|
|||||||
[SEE ALSO]
|
|
||||||
|
|
||||||
nghttp(1), nghttpd(1), h2load(1)
|
|
||||||
48
doc/nghttpx.h2r
Normal file
48
doc/nghttpx.h2r
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
FILES
|
||||||
|
-----
|
||||||
|
|
||||||
|
*/etc/nghttpx/nghttpx.conf*
|
||||||
|
The default configuration file path nghttpx searches at startup.
|
||||||
|
The configuration file path can be changed using :option:`--conf`
|
||||||
|
option.
|
||||||
|
|
||||||
|
Those lines which are staring ``#`` are treated as comment.
|
||||||
|
|
||||||
|
The option name in the configuration file is the long command-line
|
||||||
|
option name with leading ``--`` stripped (e.g., ``frontend``). Put
|
||||||
|
``=`` between option name and value. Don't put extra leading or
|
||||||
|
trailing spaces.
|
||||||
|
|
||||||
|
The options which do not take argument in the command-line *take*
|
||||||
|
argument in the configuration file. Specify ``yes`` as an argument
|
||||||
|
(e.g., ``http2-proxy=yes``). If other string is given, it is
|
||||||
|
ignored.
|
||||||
|
|
||||||
|
To specify private key and certificate file which are given as
|
||||||
|
positional arguments in commnad-line, use ``private-key-file`` and
|
||||||
|
``certificate-file``.
|
||||||
|
|
||||||
|
:option:`--conf` option cannot be used in the configuration file and
|
||||||
|
will be ignored if specified.
|
||||||
|
|
||||||
|
SIGNALS
|
||||||
|
-------
|
||||||
|
|
||||||
|
SIGQUIT
|
||||||
|
Shutdown gracefully. First accept pending connections and stop
|
||||||
|
accepting connection. After all connections are handled, nghttpx
|
||||||
|
exits.
|
||||||
|
|
||||||
|
SIGUSR1
|
||||||
|
Reopen log files.
|
||||||
|
|
||||||
|
SIGUSR2
|
||||||
|
Fork and execute nghttpx. It will execute the binary in the same
|
||||||
|
path with same command-line arguments and environment variables.
|
||||||
|
After new process comes up, sending SIGQUIT to the original process
|
||||||
|
to perform hot swapping.
|
||||||
|
|
||||||
|
SEE ALSO
|
||||||
|
--------
|
||||||
|
|
||||||
|
:manpage:`nghttp(1)`, :manpage:`nghttpd(1)`, :manpage:`h2load(1)`
|
||||||
@@ -5,6 +5,11 @@ In this article, we briefly describe how to build Android binary using
|
|||||||
`Android NDK <http://developer.android.com/tools/sdk/ndk/index.html>`_
|
`Android NDK <http://developer.android.com/tools/sdk/ndk/index.html>`_
|
||||||
cross-compiler on Debian Linux.
|
cross-compiler on Debian Linux.
|
||||||
|
|
||||||
|
The easiest way to build android binary is use Dockerfile.android.
|
||||||
|
See Dockerfile.android for more details. If you cannot use
|
||||||
|
Dockerfile.android for whatever reason, continue to read the rest of
|
||||||
|
this article.
|
||||||
|
|
||||||
We offer ``android-config`` and ``android-make`` scripts to make the
|
We offer ``android-config`` and ``android-make`` scripts to make the
|
||||||
build easier. To make these script work, NDK toolchain must be
|
build easier. To make these script work, NDK toolchain must be
|
||||||
installed in the following way. First, let us introduce
|
installed in the following way. First, let us introduce
|
||||||
@@ -16,8 +21,9 @@ unpacked::
|
|||||||
|
|
||||||
$ build/tools/make-standalone-toolchain.sh \
|
$ build/tools/make-standalone-toolchain.sh \
|
||||||
--install-dir=$ANDROID_HOME/toolchain \
|
--install-dir=$ANDROID_HOME/toolchain \
|
||||||
--toolchain=arm-linux-androideabi-4.8 \
|
--toolchain=arm-linux-androideabi-4.9 \
|
||||||
--llvm-version=3.4
|
--llvm-version=3.5 \
|
||||||
|
--platform=android-16
|
||||||
|
|
||||||
The additional flag ``--system=linux-x86_64`` may be required if you
|
The additional flag ``--system=linux-x86_64`` may be required if you
|
||||||
are using x86_64 system.
|
are using x86_64 system.
|
||||||
@@ -25,7 +31,7 @@ are using x86_64 system.
|
|||||||
The platform level is not important here because we don't use Android
|
The platform level is not important here because we don't use Android
|
||||||
specific C/C++ API.
|
specific C/C++ API.
|
||||||
|
|
||||||
The dependent libraries, such as OpenSSL and libevent should be built
|
The dependent libraries, such as OpenSSL and libev should be built
|
||||||
with the toolchain and installed under ``$ANDROID_HOME/usr/local``.
|
with the toolchain and installed under ``$ANDROID_HOME/usr/local``.
|
||||||
We recommend to build these libraries as static library to make the
|
We recommend to build these libraries as static library to make the
|
||||||
deployment easier. libxml2 support is currently disabled.
|
deployment easier. libxml2 support is currently disabled.
|
||||||
@@ -42,7 +48,9 @@ correct path. Also add ``$ANDROID_HOME/toolchain/bin`` to ``PATH``::
|
|||||||
|
|
||||||
$ export PATH=$PATH:$ANDROID_HOME/toolchain/bin
|
$ export PATH=$PATH:$ANDROID_HOME/toolchain/bin
|
||||||
|
|
||||||
To configure OpenSSL, use the following script::
|
To configure OpenSSL, use the following script:
|
||||||
|
|
||||||
|
.. code-block:: sh
|
||||||
|
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
@@ -59,7 +67,12 @@ To configure OpenSSL, use the following script::
|
|||||||
|
|
||||||
And run ``make install`` to build and install.
|
And run ``make install`` to build and install.
|
||||||
|
|
||||||
To configure libevent, use the following script::
|
We cannot compile libev without modification. Apply `this patch
|
||||||
|
<https://gist.github.com/tatsuhiro-t/48c45f08950f587180ed>`_ before
|
||||||
|
configuring libev. This patch is for libev-4.19. After applying the
|
||||||
|
patch, to configure libev, use the following script:
|
||||||
|
|
||||||
|
.. code-block:: sh
|
||||||
|
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
@@ -82,7 +95,9 @@ To configure libevent, use the following script::
|
|||||||
|
|
||||||
And run ``make install`` to build and install.
|
And run ``make install`` to build and install.
|
||||||
|
|
||||||
To configure spdylay, use the following script::
|
To configure spdylay, use the following script:
|
||||||
|
|
||||||
|
.. code-block:: sh
|
||||||
|
|
||||||
if [ -z "$ANDROID_HOME" ]; then
|
if [ -z "$ANDROID_HOME" ]; then
|
||||||
echo 'No $ANDROID_HOME specified.'
|
echo 'No $ANDROID_HOME specified.'
|
||||||
|
|||||||
57
doc/sources/contribute.rst
Normal file
57
doc/sources/contribute.rst
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
Contribution Guidelines
|
||||||
|
=======================
|
||||||
|
|
||||||
|
[This text was composed based on 1.2. License section of curl/libcurl
|
||||||
|
project.]
|
||||||
|
|
||||||
|
When contributing with code, you agree to put your changes and new
|
||||||
|
code under the same license nghttp2 is already using unless stated and
|
||||||
|
agreed otherwise.
|
||||||
|
|
||||||
|
When changing existing source code, you do not alter the copyright of
|
||||||
|
the original file(s). The copyright will still be owned by the
|
||||||
|
original creator(s) or those who have been assigned copyright by the
|
||||||
|
original author(s).
|
||||||
|
|
||||||
|
By submitting a patch to the nghttp2 project, you are assumed to have
|
||||||
|
the right to the code and to be allowed by your employer or whatever
|
||||||
|
to hand over that patch/code to us. We will credit you for your
|
||||||
|
changes as far as possible, to give credit but also to keep a trace
|
||||||
|
back to who made what changes. Please always provide us with your
|
||||||
|
full real name when contributing!
|
||||||
|
|
||||||
|
Coding style
|
||||||
|
------------
|
||||||
|
|
||||||
|
We use clang-format to format source code consistently. The
|
||||||
|
clang-format configuration file .clang-format is located at the root
|
||||||
|
directory. Since clang-format produces slightly different results
|
||||||
|
between versions, we currently use clang-format which comes with
|
||||||
|
clang-3.5.
|
||||||
|
|
||||||
|
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-3.5 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
|
||||||
|
found, download it from `here
|
||||||
|
<https://llvm.org/svn/llvm-project/cfe/trunk/tools/clang-format/clang-format.el>`_.
|
||||||
|
And add these lines to your .emacs file:
|
||||||
|
|
||||||
|
.. code-block:: lisp
|
||||||
|
|
||||||
|
;; From
|
||||||
|
;; https://code.google.com/p/chromium/wiki/Emacs#Use_Google's_C++_style!
|
||||||
|
(load "/<path/to>/clang-format.el")
|
||||||
|
(add-hook 'c-mode-common-hook
|
||||||
|
(function (lambda () (local-set-key (kbd "TAB")
|
||||||
|
'clang-format-region))))
|
||||||
|
|
||||||
|
You can find other editor integration in
|
||||||
|
http://clang.llvm.org/docs/ClangFormat.html.
|
||||||
@@ -17,6 +17,7 @@ Contents:
|
|||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
|
||||||
package_README
|
package_README
|
||||||
|
contribute
|
||||||
building-android-binary
|
building-android-binary
|
||||||
tutorial-client
|
tutorial-client
|
||||||
tutorial-server
|
tutorial-server
|
||||||
@@ -28,25 +29,22 @@ Contents:
|
|||||||
nghttpx-howto
|
nghttpx-howto
|
||||||
h2load-howto
|
h2load-howto
|
||||||
apiref
|
apiref
|
||||||
|
libnghttp2_asio
|
||||||
python-apiref
|
python-apiref
|
||||||
nghttp2.h
|
nghttp2.h
|
||||||
nghttp2ver.h
|
nghttp2ver.h
|
||||||
|
asio_http2.h
|
||||||
Source <https://github.com/tatsuhiro-t/nghttp2>
|
Source <https://github.com/tatsuhiro-t/nghttp2>
|
||||||
Issues <https://github.com/tatsuhiro-t/nghttp2/issues>
|
Issues <https://github.com/tatsuhiro-t/nghttp2/issues>
|
||||||
|
nghttp2.org <https://nghttp2.org/>
|
||||||
|
|
||||||
Released Versions
|
Released Versions
|
||||||
=================
|
=================
|
||||||
|
|
||||||
* `v0.3.2 <released-versions/v0.3.2/>`_ `(Download v0.3.2) <https://github.com/tatsuhiro-t/nghttp2/releases/tag/v0.3.2>`_
|
https://github.com/tatsuhiro-t/nghttp2/releases
|
||||||
* `v0.3.1 <released-versions/v0.3.1/>`_ `(Download v0.3.1) <https://github.com/tatsuhiro-t/nghttp2/releases/tag/v0.3.1>`_
|
|
||||||
* `v0.3.0 <released-versions/v0.3.0/>`_ `(Download v0.3.0) <https://github.com/tatsuhiro-t/nghttp2/releases/tag/v0.3.0>`_
|
|
||||||
* `v0.2.0 <released-versions/v0.2.0/>`_ `(Download v0.2.0) <https://github.com/tatsuhiro-t/nghttp2/releases/tag/v0.2.0>`_
|
|
||||||
* `v0.1.0 <released-versions/v0.1.0/>`_ `(Download v0.1.0) <https://github.com/tatsuhiro-t/nghttp2/releases/tag/v0.1.0>`_
|
|
||||||
|
|
||||||
|
|
||||||
Resources
|
Resources
|
||||||
---------
|
---------
|
||||||
|
|
||||||
* http://tools.ietf.org/html/draft-ietf-httpbis-http2-14
|
* http://tools.ietf.org/html/draft-ietf-httpbis-http2-14
|
||||||
* http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-09
|
* http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-09
|
||||||
* http://tools.ietf.org/html/draft-ietf-httpbis-alt-svc-02
|
|
||||||
|
|||||||
228
doc/sources/libnghttp2_asio.rst
Normal file
228
doc/sources/libnghttp2_asio.rst
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
libnghttp2_asio: High level HTTP/2 C++ library
|
||||||
|
==============================================
|
||||||
|
|
||||||
|
libnghttp2_asio is C++ library built on top of libnghttp2 and provides
|
||||||
|
high level abstraction API to build HTTP/2 applications. It depends
|
||||||
|
on Boost::ASIO library and OpenSSL. Currently libnghttp2_asio
|
||||||
|
provides server side API.
|
||||||
|
|
||||||
|
libnghttp2_asio is not built by default. Use ``--enable-asio-lib``
|
||||||
|
configure flag to build libnghttp2_asio. The required Boost libraries
|
||||||
|
are:
|
||||||
|
|
||||||
|
* Boost::Asio
|
||||||
|
* Boost::System
|
||||||
|
* Boost::Thread
|
||||||
|
|
||||||
|
To use libnghttp2_asio, first include following header file:
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
#include <nghttp2/asio_http2.h>
|
||||||
|
|
||||||
|
Also take a look at that header file :doc:`asio_http2.h`.
|
||||||
|
|
||||||
|
Server API
|
||||||
|
----------
|
||||||
|
|
||||||
|
Server API is designed to build HTTP/2 server very easily to utilize
|
||||||
|
C++11 anonymous function and closure. The bare minimum example of
|
||||||
|
HTTP/2 server looks like this:
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
#include <nghttp2/asio_http2.h>
|
||||||
|
|
||||||
|
using namespace nghttp2::asio_http2;
|
||||||
|
using namespace nghttp2::asio_http2::server;
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
http2 server;
|
||||||
|
|
||||||
|
server.listen("*", 3000, [](const std::shared_ptr<request> &req,
|
||||||
|
const std::shared_ptr<response> &res) {
|
||||||
|
res->write_head(200);
|
||||||
|
res->end("hello, world");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
First we instantiate ``nghttp2::asio_http2::server::http2`` object.
|
||||||
|
Then call ``nghttp2::asio_http2::server::http2::listen`` function with
|
||||||
|
address and port to listen to and callback function, namely "request
|
||||||
|
callback", invoked when request arrives.
|
||||||
|
|
||||||
|
The ``req`` and ``res`` represent HTTP request and response
|
||||||
|
respectively. ``nghttp2::asio_http2_::server::response::write_head``
|
||||||
|
constructs HTTP response header fields. The first argument is HTTP
|
||||||
|
status code, in the above example, which is 200. The second argument,
|
||||||
|
which is omitted in the above example, is additional header fields to
|
||||||
|
send.
|
||||||
|
|
||||||
|
``nghttp2::asio_http2::server::response::end`` sends responde body.
|
||||||
|
In the above example, we send string "hello, world".
|
||||||
|
|
||||||
|
Serving static files and enabling SSL/TLS
|
||||||
|
+++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
In this example, we serve a couple of static files and also enable
|
||||||
|
SSL/TLS.
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
#include <nghttp2/asio_http2.h>
|
||||||
|
|
||||||
|
using namespace nghttp2::asio_http2;
|
||||||
|
using namespace nghttp2::asio_http2::server;
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
http2 server;
|
||||||
|
|
||||||
|
server.tls("server.key", "server.crt");
|
||||||
|
|
||||||
|
server.listen("*", 3000, [](const std::shared_ptr<request> &req,
|
||||||
|
const std::shared_ptr<response> &res) {
|
||||||
|
if (req->path() == "/" || req->path() == "/index.html") {
|
||||||
|
res->write_head(200);
|
||||||
|
res->end(file_reader("index.html"));
|
||||||
|
} else {
|
||||||
|
res->write_head(404);
|
||||||
|
res->end("<html><head><title>404</title></head>"
|
||||||
|
"<body>404 Not Found</body></html>");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Specifying path to private key file and certificate file in
|
||||||
|
``nghttp2::asio_http2::server::http2::tls`` will enable SSL/TLS. Both
|
||||||
|
files must be in PEM format.
|
||||||
|
|
||||||
|
In the above example, if request path is either "/" or "/index.html",
|
||||||
|
we serve index.html file in the current working directory.
|
||||||
|
``nghttp2::asio_http2::server::response::end`` has overload to take
|
||||||
|
function of type ``nghttp2::asio_http2::read_cb`` and application pass
|
||||||
|
its implementation to generate response body. For the convenience,
|
||||||
|
libnghttp2_asio library provides ``nghttp2::asio_http2::file_reader``
|
||||||
|
function to generate function to server static file.
|
||||||
|
|
||||||
|
Server push
|
||||||
|
+++++++++++
|
||||||
|
|
||||||
|
Server push is also supported.
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
#include <nghttp2/asio_http2.h>
|
||||||
|
|
||||||
|
using namespace nghttp2::asio_http2;
|
||||||
|
using namespace nghttp2::asio_http2::server;
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
http2 server;
|
||||||
|
|
||||||
|
server.tls("server.key", "server.crt");
|
||||||
|
|
||||||
|
server.listen("*", 3000, [](const std::shared_ptr<request> &req,
|
||||||
|
const std::shared_ptr<response> &res) {
|
||||||
|
if (req->path() == "/") {
|
||||||
|
req->push("GET", "/my.css");
|
||||||
|
|
||||||
|
res->write_head(200);
|
||||||
|
res->end(file_reader("index.html"));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req->path() == "/my.css") {
|
||||||
|
res->write_head(200);
|
||||||
|
res->end(file_reader("my.css"));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
res->write_head(404);
|
||||||
|
res->end("<html><head><title>404</title></head>"
|
||||||
|
"<body>404 Not Found</body></html>");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
When client requested "/", we push "/my.css". To push resource, call
|
||||||
|
``nghttp2::asio_http2::server::request::push`` function with desired
|
||||||
|
method and path. Later, the callback will be called with the pushed
|
||||||
|
resource "/my.css".
|
||||||
|
|
||||||
|
Enable multi-threading
|
||||||
|
++++++++++++++++++++++
|
||||||
|
|
||||||
|
Enabling multi-threading is very easy. Just call
|
||||||
|
``nghttp2::asio_http2::server::http2::num_threads`` function with the
|
||||||
|
desired number of threads:
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
http2 server;
|
||||||
|
|
||||||
|
// Use 4 native threads
|
||||||
|
server.num_threads(4);
|
||||||
|
|
||||||
|
Run blocking tasks in background thread
|
||||||
|
+++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
The request callback is called in the same thread where HTTP request
|
||||||
|
is handled. And many connections shares the same thread, we cannot
|
||||||
|
directly run blocking tasks in request callback.
|
||||||
|
|
||||||
|
To run blocking tasks, use
|
||||||
|
``nghttp2::asio_http2::server::request::run_task``. The passed
|
||||||
|
callback will be executed in the different thread from the thread
|
||||||
|
where request callback was executed. So application can perform
|
||||||
|
blocking task there. The example follows:
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <nghttp2/asio_http2.h>
|
||||||
|
|
||||||
|
using namespace nghttp2::asio_http2;
|
||||||
|
using namespace nghttp2::asio_http2::server;
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
http2 server;
|
||||||
|
|
||||||
|
server.num_concurrent_tasks(16);
|
||||||
|
|
||||||
|
server.listen("*", 3000, [](const std::shared_ptr<request> &req,
|
||||||
|
const std::shared_ptr<response> &res) {
|
||||||
|
req->run_task([res](channel &channel) {
|
||||||
|
// executed in different thread than the thread where
|
||||||
|
// request callback was executed.
|
||||||
|
|
||||||
|
// using res directly here is not safe. Capturing it by
|
||||||
|
// value is safe because it is std::shared_ptr.
|
||||||
|
|
||||||
|
sleep(1);
|
||||||
|
|
||||||
|
channel.post([res]() {
|
||||||
|
// executed in the same thread where request callback
|
||||||
|
// was executed.
|
||||||
|
res->write_head(200);
|
||||||
|
res->end("hello, world");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
First we set the number of background threads which run tasks. By
|
||||||
|
default it is set to 1. In this example, we set it to 16, so at most
|
||||||
|
16 tasks can be executed concurrently without blocking handling new
|
||||||
|
requests.
|
||||||
|
|
||||||
|
We call ``req->run_task()`` to execute task in background thread. In
|
||||||
|
the passed callback, we just simply sleeps 1 second. After sleep is
|
||||||
|
over, we schedule another callback to send response to the client.
|
||||||
|
Since the callback passed to ``req->run_task()`` is executed in the
|
||||||
|
different thread from the thread where request callback is called,
|
||||||
|
using ``req`` or ``res`` object directly there may cause undefined
|
||||||
|
behaviour. To avoid this issue, we can use
|
||||||
|
``nghttp2::asio_http2::channel::post`` by supplying a callback which
|
||||||
|
in turn get called in the same thread where request callback was
|
||||||
|
called.
|
||||||
@@ -52,18 +52,18 @@ like forward proxy and assumes the backend is HTTP/1 proxy server
|
|||||||
(e.g., squid, traffic server). So HTTP/1 request must include
|
(e.g., squid, traffic server). So HTTP/1 request must include
|
||||||
absolute URI in request line.
|
absolute URI in request line.
|
||||||
|
|
||||||
By default, frontend connection is encrypted, this mode is also called
|
By default, frontend connection is encrypted. So this mode is also
|
||||||
secure proxy. If nghttpx is linked with spdylay, it supports SPDY
|
called secure proxy. If nghttpx is linked with spdylay, it supports
|
||||||
protocols and it works as so called SPDY proxy.
|
SPDY protocols and it works as so called SPDY proxy.
|
||||||
|
|
||||||
With ``--frontend-no-tls`` option, SSL/TLS is turned off in frontend
|
With ``--frontend-no-tls`` option, SSL/TLS is turned off in frontend
|
||||||
connection, so the connection gets insecure.
|
connection, so the connection gets insecure.
|
||||||
|
|
||||||
The backend must be HTTP/1 proxy server. nghttpx only supports 1
|
The backend must be HTTP/1 proxy server. nghttpx only supports
|
||||||
backend server address. It translates incoming requests to HTTP/1
|
multiple backend server addresses. It translates incoming requests to
|
||||||
request to backend server. The backend server performs real proxy
|
HTTP/1 request to backend server. The backend server performs real
|
||||||
work for each request, for example, dispatching requests to the origin
|
proxy work for each request, for example, dispatching requests to the
|
||||||
server and caching contents.
|
origin server and caching contents.
|
||||||
|
|
||||||
For example, to make nghttpx listen to encrypted HTTP/2 requests at
|
For example, to make nghttpx listen to encrypted HTTP/2 requests at
|
||||||
port 8443, and a backend HTTP/1 proxy server is configured to listen
|
port 8443, and a backend HTTP/1 proxy server is configured to listen
|
||||||
@@ -87,7 +87,7 @@ proxy, user has to create proxy.pac script file like this:
|
|||||||
|
|
||||||
``SERVERADDR`` and ``PORT`` is the hostname/address and port of the
|
``SERVERADDR`` and ``PORT`` is the hostname/address and port of the
|
||||||
machine nghttpx is running. Please note that both Firefox nightly and
|
machine nghttpx is running. Please note that both Firefox nightly and
|
||||||
Chromium requires valid certificate for secure proxy.
|
Chromium require valid certificate for secure proxy.
|
||||||
|
|
||||||
For Firefox nightly, open Preference window and select Advanced then
|
For Firefox nightly, open Preference window and select Advanced then
|
||||||
click Network tab. Clicking Connection Settings button will show the
|
click Network tab. Clicking Connection Settings button will show the
|
||||||
@@ -100,9 +100,9 @@ For Chromium, use following command-line::
|
|||||||
|
|
||||||
$ google-chrome --proxy-pac-url=file:///path/to/proxy.pac --use-npn
|
$ google-chrome --proxy-pac-url=file:///path/to/proxy.pac --use-npn
|
||||||
|
|
||||||
Squid may work as out-of-box. Traffic server requires to be
|
As HTTP/1 proxy server, Squid may work as out-of-box. Traffic server
|
||||||
configured as forward proxy. Here is the minimum configuration items
|
requires to be configured as forward proxy. Here is the minimum
|
||||||
to edit::
|
configuration items to edit::
|
||||||
|
|
||||||
CONFIG proxy.config.reverse_proxy.enabled INT 0
|
CONFIG proxy.config.reverse_proxy.enabled INT 0
|
||||||
CONFIG proxy.config.url_remap.remap_required INT 0
|
CONFIG proxy.config.url_remap.remap_required INT 0
|
||||||
@@ -235,12 +235,12 @@ Read/write rate limit
|
|||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
nghttpx supports transfer rate limiting on frontend connections. You
|
nghttpx supports transfer rate limiting on frontend connections. You
|
||||||
can do rate limit per worker (thread) for reading and writeing
|
can do rate limit per frontend connection for reading and writeing
|
||||||
individually.
|
individually.
|
||||||
|
|
||||||
To rate limit per worker (thread), use ``--worker-read-rate`` and
|
To perform rate limit for reading, use ``--read-rate`` and
|
||||||
``--worker-read-burst`` options. For writing, use
|
``--read-burst`` options. For writing, use ``--write-rate`` and
|
||||||
``--worker-write-rate`` and ``--worker-write-burst``.
|
``--write-burst``.
|
||||||
|
|
||||||
Please note that rate limit is performed on top of TCP and nothing to
|
Please note that rate limit is performed on top of TCP and nothing to
|
||||||
do with HTTP/2 flow control.
|
do with HTTP/2 flow control.
|
||||||
@@ -263,10 +263,10 @@ precedence. If the above conditions are not met with the host value
|
|||||||
in :authority header field, rewrite is retried with the value in host
|
in :authority header field, rewrite is retried with the value in host
|
||||||
header field.
|
header field.
|
||||||
|
|
||||||
Hot deploy
|
Hot swapping
|
||||||
----------
|
------------
|
||||||
|
|
||||||
nghttpx supports hot deploy feature using signals. The hot deploy in
|
nghttpx supports hot swapping using signals. The hot swapping in
|
||||||
nghttpx is multi step process. First send USR2 signal to nghttpx
|
nghttpx is multi step process. First send USR2 signal to nghttpx
|
||||||
process. It will do fork and execute new executable, using same
|
process. It will do fork and execute new executable, using same
|
||||||
command-line arguments and environment variables. At this point, both
|
command-line arguments and environment variables. At this point, both
|
||||||
@@ -284,3 +284,12 @@ log rotation daemon renamed existing log files. To tell nghttpx to
|
|||||||
re-open log files, send USR1 signal to nghttpx process. It will
|
re-open log files, send USR1 signal to nghttpx process. It will
|
||||||
re-open files specified by ``--accesslog-file`` and
|
re-open files specified by ``--accesslog-file`` and
|
||||||
``--errorlog-file`` options.
|
``--errorlog-file`` options.
|
||||||
|
|
||||||
|
Multiple HTTP/1 backend addresses
|
||||||
|
---------------------------------
|
||||||
|
|
||||||
|
nghttpx supports multiple HTTP/1 backend addresses. To specify them,
|
||||||
|
just use ``-b`` option repeatedly. For example, to use backend1:8080
|
||||||
|
and backend2:8080, use command-line like this: ``-bbackend1,8080
|
||||||
|
-bbackend2,8080``. Please note that HTTP/2 backend only supports 1
|
||||||
|
backend address.
|
||||||
|
|||||||
@@ -22,11 +22,9 @@ protocol over the SSL/TLS transport. In this tutorial, we use
|
|||||||
`nghttp2_select_next_protocol()` function to select the HTTP/2
|
`nghttp2_select_next_protocol()` function to select the HTTP/2
|
||||||
protocol the library supports::
|
protocol the library supports::
|
||||||
|
|
||||||
static int select_next_proto_cb(SSL* ssl,
|
static int select_next_proto_cb(SSL *ssl _U_, unsigned char **out,
|
||||||
unsigned char **out, unsigned char *outlen,
|
unsigned char *outlen, const unsigned char *in,
|
||||||
const unsigned char *in, unsigned int inlen,
|
unsigned int inlen, void *arg _U_) {
|
||||||
void *arg)
|
|
||||||
{
|
|
||||||
if (nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) {
|
if (nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) {
|
||||||
errx(1, "Server did not advertise " NGHTTP2_PROTO_VERSION_ID);
|
errx(1, "Server did not advertise " NGHTTP2_PROTO_VERSION_ID);
|
||||||
}
|
}
|
||||||
@@ -36,8 +34,7 @@ protocol the library supports::
|
|||||||
The callback is set to the SSL_CTX object using
|
The callback is set to the SSL_CTX object using
|
||||||
``SSL_CTX_set_next_proto_select_cb()`` function::
|
``SSL_CTX_set_next_proto_select_cb()`` function::
|
||||||
|
|
||||||
static SSL_CTX* create_ssl_ctx(void)
|
static SSL_CTX *create_ssl_ctx(void) {
|
||||||
{
|
|
||||||
SSL_CTX *ssl_ctx;
|
SSL_CTX *ssl_ctx;
|
||||||
ssl_ctx = SSL_CTX_new(SSLv23_client_method());
|
ssl_ctx = SSL_CTX_new(SSLv23_client_method());
|
||||||
if (!ssl_ctx) {
|
if (!ssl_ctx) {
|
||||||
@@ -45,7 +42,8 @@ The callback is set to the SSL_CTX object using
|
|||||||
ERR_error_string(ERR_get_error(), NULL));
|
ERR_error_string(ERR_get_error(), NULL));
|
||||||
}
|
}
|
||||||
SSL_CTX_set_options(ssl_ctx,
|
SSL_CTX_set_options(ssl_ctx,
|
||||||
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_COMPRESSION |
|
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
|
||||||
|
SSL_OP_NO_COMPRESSION |
|
||||||
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
|
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
|
||||||
SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL);
|
SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL);
|
||||||
return ssl_ctx;
|
return ssl_ctx;
|
||||||
@@ -91,20 +89,17 @@ respectively.
|
|||||||
Then we call function ``initiate_connection()`` to start connecting to
|
Then we call function ``initiate_connection()`` to start connecting to
|
||||||
the remote server::
|
the remote server::
|
||||||
|
|
||||||
static void initiate_connection(struct event_base *evbase,
|
static void initiate_connection(struct event_base *evbase, SSL_CTX *ssl_ctx,
|
||||||
SSL_CTX *ssl_ctx,
|
|
||||||
const char *host, uint16_t port,
|
const char *host, uint16_t port,
|
||||||
http2_session_data *session_data)
|
http2_session_data *session_data) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
struct bufferevent *bev;
|
struct bufferevent *bev;
|
||||||
SSL *ssl;
|
SSL *ssl;
|
||||||
|
|
||||||
ssl = create_ssl(ssl_ctx);
|
ssl = create_ssl(ssl_ctx);
|
||||||
bev = bufferevent_openssl_socket_new(evbase, -1, ssl,
|
bev = bufferevent_openssl_socket_new(
|
||||||
BUFFEREVENT_SSL_CONNECTING,
|
evbase, -1, ssl, BUFFEREVENT_SSL_CONNECTING,
|
||||||
BEV_OPT_DEFER_CALLBACKS |
|
BEV_OPT_DEFER_CALLBACKS | BEV_OPT_CLOSE_ON_FREE);
|
||||||
BEV_OPT_CLOSE_ON_FREE);
|
|
||||||
bufferevent_setcb(bev, readcb, writecb, eventcb, session_data);
|
bufferevent_setcb(bev, readcb, writecb, eventcb, session_data);
|
||||||
rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase,
|
rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase,
|
||||||
AF_UNSPEC, host, port);
|
AF_UNSPEC, host, port);
|
||||||
@@ -122,8 +117,7 @@ The ``eventcb()`` is invoked by libevent event loop when an event
|
|||||||
(e.g., connection has been established, timeout, etc) happens on the
|
(e.g., connection has been established, timeout, etc) happens on the
|
||||||
underlying network socket::
|
underlying network socket::
|
||||||
|
|
||||||
static void eventcb(struct bufferevent *bev, short events, void *ptr)
|
static void eventcb(struct bufferevent *bev, short events, void *ptr) {
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||||
if (events & BEV_EVENT_CONNECTED) {
|
if (events & BEV_EVENT_CONNECTED) {
|
||||||
int fd = bufferevent_getfd(bev);
|
int fd = bufferevent_getfd(bev);
|
||||||
@@ -154,28 +148,27 @@ event, we just simply tear down the connection. The
|
|||||||
finished successfully. We first initialize nghttp2 session object in
|
finished successfully. We first initialize nghttp2 session object in
|
||||||
``initialize_nghttp2_session()`` function::
|
``initialize_nghttp2_session()`` function::
|
||||||
|
|
||||||
static void initialize_nghttp2_session(http2_session_data *session_data)
|
static void initialize_nghttp2_session(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
nghttp2_session_callbacks *callbacks;
|
nghttp2_session_callbacks *callbacks;
|
||||||
|
|
||||||
nghttp2_session_callbacks_new(&callbacks);
|
nghttp2_session_callbacks_new(&callbacks);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_frame_recv_callback
|
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
|
||||||
(callbacks, on_frame_recv_callback);
|
on_frame_recv_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_data_chunk_recv_callback
|
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
|
||||||
(callbacks, on_data_chunk_recv_callback);
|
callbacks, on_data_chunk_recv_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_stream_close_callback
|
nghttp2_session_callbacks_set_on_stream_close_callback(
|
||||||
(callbacks, on_stream_close_callback);
|
callbacks, on_stream_close_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_header_callback
|
nghttp2_session_callbacks_set_on_header_callback(callbacks,
|
||||||
(callbacks, on_header_callback);
|
on_header_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_begin_headers_callback
|
nghttp2_session_callbacks_set_on_begin_headers_callback(
|
||||||
(callbacks, on_begin_headers_callback);
|
callbacks, on_begin_headers_callback);
|
||||||
|
|
||||||
nghttp2_session_client_new(&session_data->session, callbacks, session_data);
|
nghttp2_session_client_new(&session_data->session, callbacks, session_data);
|
||||||
|
|
||||||
@@ -196,18 +189,15 @@ which is 24 bytes magic byte sequence
|
|||||||
transmission of client connection header is done in
|
transmission of client connection header is done in
|
||||||
``send_client_connection_header()``::
|
``send_client_connection_header()``::
|
||||||
|
|
||||||
static void send_client_connection_header(http2_session_data *session_data)
|
static void send_client_connection_header(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
nghttp2_settings_entry iv[1] = {
|
nghttp2_settings_entry iv[1] = {
|
||||||
{ NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 }
|
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
|
||||||
};
|
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
bufferevent_write(session_data->bev,
|
bufferevent_write(session_data->bev, NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
||||||
NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
|
||||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
|
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
|
||||||
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE,
|
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
|
||||||
iv, ARRLEN(iv));
|
ARRLEN(iv));
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
errx(1, "Could not submit SETTINGS: %s", nghttp2_strerror(rv));
|
errx(1, "Could not submit SETTINGS: %s", nghttp2_strerror(rv));
|
||||||
}
|
}
|
||||||
@@ -225,23 +215,21 @@ used, which is described about later.
|
|||||||
After the transmission of client connection header, we enqueue HTTP
|
After the transmission of client connection header, we enqueue HTTP
|
||||||
request in ``submit_request()`` function::
|
request in ``submit_request()`` function::
|
||||||
|
|
||||||
static void submit_request(http2_session_data *session_data)
|
static void submit_request(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
int32_t stream_id;
|
int32_t stream_id;
|
||||||
http2_stream_data *stream_data = session_data->stream_data;
|
http2_stream_data *stream_data = session_data->stream_data;
|
||||||
const char *uri = stream_data->uri;
|
const char *uri = stream_data->uri;
|
||||||
const struct http_parser_url *u = stream_data->u;
|
const struct http_parser_url *u = stream_data->u;
|
||||||
nghttp2_nv hdrs[] = {
|
nghttp2_nv hdrs[] = {
|
||||||
MAKE_NV2(":method", "GET"),
|
MAKE_NV2(":method", "GET"),
|
||||||
MAKE_NV(":scheme",
|
MAKE_NV(":scheme", &uri[u->field_data[UF_SCHEMA].off],
|
||||||
&uri[u->field_data[UF_SCHEMA].off], u->field_data[UF_SCHEMA].len),
|
u->field_data[UF_SCHEMA].len),
|
||||||
MAKE_NV(":authority", stream_data->authority, stream_data->authoritylen),
|
MAKE_NV(":authority", stream_data->authority, stream_data->authoritylen),
|
||||||
MAKE_NV(":path", stream_data->path, stream_data->pathlen)
|
MAKE_NV(":path", stream_data->path, stream_data->pathlen)};
|
||||||
};
|
|
||||||
fprintf(stderr, "Request headers:\n");
|
fprintf(stderr, "Request headers:\n");
|
||||||
print_headers(stderr, hdrs, ARRLEN(hdrs));
|
print_headers(stderr, hdrs, ARRLEN(hdrs));
|
||||||
stream_id = nghttp2_submit_request(session_data->session, NULL,
|
stream_id = nghttp2_submit_request(session_data->session, NULL, hdrs,
|
||||||
hdrs, ARRLEN(hdrs), NULL, stream_data);
|
ARRLEN(hdrs), NULL, stream_data);
|
||||||
if (stream_id < 0) {
|
if (stream_id < 0) {
|
||||||
errx(1, "Could not submit HTTP request: %s", nghttp2_strerror(stream_id));
|
errx(1, "Could not submit HTTP request: %s", nghttp2_strerror(stream_id));
|
||||||
}
|
}
|
||||||
@@ -261,8 +249,7 @@ this request.
|
|||||||
The next bufferevent callback is ``readcb()``, which is invoked when
|
The next bufferevent callback is ``readcb()``, which is invoked when
|
||||||
data is available to read in the bufferevent input buffer::
|
data is available to read in the bufferevent input buffer::
|
||||||
|
|
||||||
static void readcb(struct bufferevent *bev, void *ptr)
|
static void readcb(struct bufferevent *bev, void *ptr) {
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||||
ssize_t readlen;
|
ssize_t readlen;
|
||||||
struct evbuffer *input = bufferevent_get_input(bev);
|
struct evbuffer *input = bufferevent_get_input(bev);
|
||||||
@@ -293,8 +280,7 @@ invoke nghttp2 callbacks and also queue frames. Since there may be
|
|||||||
pending frames, we call ``session_send()`` function to send those
|
pending frames, we call ``session_send()`` function to send those
|
||||||
frames. The ``session_send()`` function is defined as follows::
|
frames. The ``session_send()`` function is defined as follows::
|
||||||
|
|
||||||
static int session_send(http2_session_data *session_data)
|
static int session_send(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
rv = nghttp2_session_send(session_data->session);
|
rv = nghttp2_session_send(session_data->session);
|
||||||
@@ -310,10 +296,8 @@ format and call ``send_callback()`` function of type
|
|||||||
:type:`nghttp2_send_callback`. The ``send_callback()`` is defined as
|
:type:`nghttp2_send_callback`. The ``send_callback()`` is defined as
|
||||||
follows::
|
follows::
|
||||||
|
|
||||||
static ssize_t send_callback(nghttp2_session *session,
|
static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
|
||||||
const uint8_t *data, size_t length,
|
size_t length, int flags _U_, void *user_data) {
|
||||||
int flags, void *user_data)
|
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
struct bufferevent *bev = session_data->bev;
|
struct bufferevent *bev = session_data->bev;
|
||||||
bufferevent_write(bev, data, length);
|
bufferevent_write(bev, data, length);
|
||||||
@@ -336,8 +320,7 @@ buffered data, see the ``send_callback()`` in the server tutorial.
|
|||||||
The third bufferevent callback is ``writecb()``, which is invoked when
|
The third bufferevent callback is ``writecb()``, which is invoked when
|
||||||
all data written in the bufferevent output buffer have been sent::
|
all data written in the bufferevent output buffer have been sent::
|
||||||
|
|
||||||
static void writecb(struct bufferevent *bev, void *ptr)
|
static void writecb(struct bufferevent *bev _U_, void *ptr) {
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||||
if (nghttp2_session_want_read(session_data->session) == 0 &&
|
if (nghttp2_session_want_read(session_data->session) == 0 &&
|
||||||
nghttp2_session_want_write(session_data->session) == 0 &&
|
nghttp2_session_want_write(session_data->session) == 0 &&
|
||||||
@@ -367,13 +350,11 @@ Let's describe remaining nghttp2 callbacks we setup in
|
|||||||
Each request header name/value pair is emitted via
|
Each request header name/value pair is emitted via
|
||||||
``on_header_callback`` function::
|
``on_header_callback`` function::
|
||||||
|
|
||||||
static int on_header_callback(nghttp2_session *session,
|
static int on_header_callback(nghttp2_session *session _U_,
|
||||||
const nghttp2_frame *frame,
|
const nghttp2_frame *frame, const uint8_t *name,
|
||||||
const uint8_t *name, size_t namelen,
|
size_t namelen, const uint8_t *value,
|
||||||
const uint8_t *value, size_t valuelen,
|
size_t valuelen, uint8_t flags _U_,
|
||||||
uint8_t flags,
|
void *user_data) {
|
||||||
void *user_data)
|
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
switch (frame->hd.type) {
|
switch (frame->hd.type) {
|
||||||
case NGHTTP2_HEADERS:
|
case NGHTTP2_HEADERS:
|
||||||
@@ -387,14 +368,13 @@ Each request header name/value pair is emitted via
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
In this turotial, we just print the name/value pair.
|
In this tutorial, we just print the name/value pair.
|
||||||
|
|
||||||
After all name/value pairs are emitted for a frame,
|
After all name/value pairs are emitted for a frame,
|
||||||
``on_frame_recv_callback`` function is called::
|
``on_frame_recv_callback`` function is called::
|
||||||
|
|
||||||
static int on_frame_recv_callback(nghttp2_session *session,
|
static int on_frame_recv_callback(nghttp2_session *session _U_,
|
||||||
const nghttp2_frame *frame, void *user_data)
|
const nghttp2_frame *frame, void *user_data) {
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
switch (frame->hd.type) {
|
switch (frame->hd.type) {
|
||||||
case NGHTTP2_HEADERS:
|
case NGHTTP2_HEADERS:
|
||||||
@@ -415,11 +395,10 @@ its stream ID.
|
|||||||
The ``on_data_chunk_recv_callback()`` function is invoked when a chunk
|
The ``on_data_chunk_recv_callback()`` function is invoked when a chunk
|
||||||
of data is received from the remote peer::
|
of data is received from the remote peer::
|
||||||
|
|
||||||
static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
static int on_data_chunk_recv_callback(nghttp2_session *session _U_,
|
||||||
int32_t stream_id,
|
uint8_t flags _U_, int32_t stream_id,
|
||||||
const uint8_t *data, size_t len,
|
const uint8_t *data, size_t len,
|
||||||
void *user_data)
|
void *user_data) {
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
if (session_data->stream_data->stream_id == stream_id) {
|
if (session_data->stream_data->stream_id == stream_id) {
|
||||||
fwrite(data, len, 1, stdout);
|
fwrite(data, len, 1, stdout);
|
||||||
@@ -435,17 +414,15 @@ some binary data.
|
|||||||
The ``on_stream_close_callback()`` function is invoked when the stream
|
The ``on_stream_close_callback()`` function is invoked when the stream
|
||||||
is about to close::
|
is about to close::
|
||||||
|
|
||||||
static int on_stream_close_callback(nghttp2_session *session,
|
static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||||
int32_t stream_id,
|
|
||||||
nghttp2_error_code error_code,
|
nghttp2_error_code error_code,
|
||||||
void *user_data)
|
void *user_data) {
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
if (session_data->stream_data->stream_id == stream_id) {
|
if (session_data->stream_data->stream_id == stream_id) {
|
||||||
fprintf(stderr, "Stream %d closed with error_code=%d\n",
|
fprintf(stderr, "Stream %d closed with error_code=%d\n", stream_id,
|
||||||
stream_id, error_code);
|
error_code);
|
||||||
rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
|
rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
|
|||||||
@@ -66,18 +66,6 @@ size of buffer.
|
|||||||
To delete :type:`nghttp2_hd_deflater` object, use `nghttp2_hd_deflate_del()`
|
To delete :type:`nghttp2_hd_deflater` object, use `nghttp2_hd_deflate_del()`
|
||||||
function.
|
function.
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
Generally, the order of header fields passed to
|
|
||||||
`nghttp2_hd_deflate_hd()` function is not preserved. It is known
|
|
||||||
that the relative ordering of header fields which do not share the
|
|
||||||
same name is insignificant. But some header fields sharing the
|
|
||||||
same name require the explicit ordering. To preserve this
|
|
||||||
ordering, those header values are concatenated into single header
|
|
||||||
field value using NULL (0x00) as delimiter. This is transparent to
|
|
||||||
HPACK API. Therefore, the application should examine the inflated
|
|
||||||
header values and split into multiple header field values by NULL.
|
|
||||||
|
|
||||||
Inflating (decoding) headers
|
Inflating (decoding) headers
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -29,17 +29,17 @@ time::
|
|||||||
static unsigned char next_proto_list[256];
|
static unsigned char next_proto_list[256];
|
||||||
static size_t next_proto_list_len;
|
static size_t next_proto_list_len;
|
||||||
|
|
||||||
static int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len,
|
static int next_proto_cb(SSL *s _U_, const unsigned char **data,
|
||||||
void *arg)
|
unsigned int *len, void *arg _U_) {
|
||||||
{
|
|
||||||
*data = next_proto_list;
|
*data = next_proto_list;
|
||||||
*len = (unsigned int)next_proto_list_len;
|
*len = (unsigned int)next_proto_list_len;
|
||||||
return SSL_TLSEXT_ERR_OK;
|
return SSL_TLSEXT_ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static SSL_CTX* create_ssl_ctx(const char *key_file, const char *cert_file)
|
static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) {
|
||||||
{
|
|
||||||
SSL_CTX *ssl_ctx;
|
SSL_CTX *ssl_ctx;
|
||||||
|
EC_KEY *ecdh;
|
||||||
|
|
||||||
ssl_ctx = SSL_CTX_new(SSLv23_server_method());
|
ssl_ctx = SSL_CTX_new(SSLv23_server_method());
|
||||||
|
|
||||||
...
|
...
|
||||||
@@ -79,7 +79,6 @@ We use the ``http2_session_data`` structure to store session-level
|
|||||||
app_context *app_ctx;
|
app_context *app_ctx;
|
||||||
nghttp2_session *session;
|
nghttp2_session *session;
|
||||||
char *client_addr;
|
char *client_addr;
|
||||||
size_t handshake_leftlen;
|
|
||||||
} http2_session_data;
|
} http2_session_data;
|
||||||
|
|
||||||
We use the ``http2_stream_data`` structure to store stream-level data::
|
We use the ``http2_stream_data`` structure to store stream-level data::
|
||||||
@@ -91,24 +90,21 @@ We use the ``http2_stream_data`` structure to store stream-level data::
|
|||||||
int fd;
|
int fd;
|
||||||
} http2_stream_data;
|
} http2_stream_data;
|
||||||
|
|
||||||
A single HTTP/2 session can have multiple streams. We manage these multiple
|
A single HTTP/2 session can have multiple streams. We manage these
|
||||||
streams with a doubly linked list. The first element of this list is pointed
|
multiple streams with a doubly linked list. The first element of this
|
||||||
to by the ``root->next`` in ``http2_session_data``. Initially, ``root->next``
|
list is pointed to by the ``root->next`` in ``http2_session_data``.
|
||||||
is ``NULL``. The ``handshake_leftlen`` member of ``http2_session_data`` is
|
Initially, ``root->next`` is ``NULL``. We use libevent's bufferevent
|
||||||
used to track the number of bytes remaining when receiving the first client
|
structure to perform network I/O. Note that the bufferevent object is
|
||||||
connection preface (:macro:`NGHTTP2_CLIENT_CONNECTION_PREFACE`), which is a 24
|
kept in ``http2_session_data`` and not in ``http2_stream_data``. This
|
||||||
bytes long magic string from the client. We use libevent's bufferevent
|
is because ``http2_stream_data`` is just a logical stream multiplexed
|
||||||
structure to perform network I/O. Note that the bufferevent object is kept in
|
over the single connection managed by bufferevent in
|
||||||
``http2_session_data`` and not in ``http2_stream_data``. This is because
|
``http2_session_data``.
|
||||||
``http2_stream_data`` is just a logical stream multiplexed over the single
|
|
||||||
connection managed by bufferevent in ``http2_session_data``.
|
|
||||||
|
|
||||||
We first create a listener object to accept incoming connections. We use
|
We first create a listener object to accept incoming connections. We use
|
||||||
libevent's ``struct evconnlistener`` for this purpose::
|
libevent's ``struct evconnlistener`` for this purpose::
|
||||||
|
|
||||||
static void start_listen(struct event_base *evbase, const char *service,
|
static void start_listen(struct event_base *evbase, const char *service,
|
||||||
app_context *app_ctx)
|
app_context *app_ctx) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
struct addrinfo hints;
|
struct addrinfo hints;
|
||||||
struct addrinfo *res, *rp;
|
struct addrinfo *res, *rp;
|
||||||
@@ -119,7 +115,7 @@ libevent's ``struct evconnlistener`` for this purpose::
|
|||||||
hints.ai_flags = AI_PASSIVE;
|
hints.ai_flags = AI_PASSIVE;
|
||||||
#ifdef AI_ADDRCONFIG
|
#ifdef AI_ADDRCONFIG
|
||||||
hints.ai_flags |= AI_ADDRCONFIG;
|
hints.ai_flags |= AI_ADDRCONFIG;
|
||||||
#endif // AI_ADDRCONFIG
|
#endif /* AI_ADDRCONFIG */
|
||||||
|
|
||||||
rv = getaddrinfo(NULL, service, &hints, &res);
|
rv = getaddrinfo(NULL, service, &hints, &res);
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
@@ -127,11 +123,12 @@ libevent's ``struct evconnlistener`` for this purpose::
|
|||||||
}
|
}
|
||||||
for (rp = res; rp; rp = rp->ai_next) {
|
for (rp = res; rp; rp = rp->ai_next) {
|
||||||
struct evconnlistener *listener;
|
struct evconnlistener *listener;
|
||||||
listener = evconnlistener_new_bind(evbase, acceptcb, app_ctx,
|
listener = evconnlistener_new_bind(
|
||||||
LEV_OPT_CLOSE_ON_FREE |
|
evbase, acceptcb, app_ctx, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
|
||||||
LEV_OPT_REUSEABLE, -1,
|
16, rp->ai_addr, rp->ai_addrlen);
|
||||||
rp->ai_addr, rp->ai_addrlen);
|
|
||||||
if (listener) {
|
if (listener) {
|
||||||
|
freeaddrinfo(res);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -141,30 +138,37 @@ libevent's ``struct evconnlistener`` for this purpose::
|
|||||||
We specify the ``acceptcb`` callback which is called when a new connection is
|
We specify the ``acceptcb`` callback which is called when a new connection is
|
||||||
accepted::
|
accepted::
|
||||||
|
|
||||||
static void acceptcb(struct evconnlistener *listener, int fd,
|
static void acceptcb(struct evconnlistener *listener _U_, int fd,
|
||||||
struct sockaddr *addr, int addrlen, void *arg)
|
struct sockaddr *addr, int addrlen, void *arg) {
|
||||||
{
|
|
||||||
app_context *app_ctx = (app_context *)arg;
|
app_context *app_ctx = (app_context *)arg;
|
||||||
http2_session_data *session_data;
|
http2_session_data *session_data;
|
||||||
|
|
||||||
session_data = create_http2_session_data(app_ctx, fd, addr, addrlen);
|
session_data = create_http2_session_data(app_ctx, fd, addr, addrlen);
|
||||||
bufferevent_setcb(session_data->bev, handshake_readcb, NULL, eventcb,
|
|
||||||
session_data);
|
bufferevent_setcb(session_data->bev, readcb, writecb, eventcb, session_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
Here we create the ``http2_session_data`` object. The bufferevent for this
|
Here we create the ``http2_session_data`` object. The bufferevent for
|
||||||
connection is also initialized at this time. We specify two callbacks for the
|
this connection is also initialized at this time. We specify three
|
||||||
bufferevent: ``handshake_readcb`` and ``eventcb``.
|
callbacks for the bufferevent: ``readcb``, ``writecb`` and
|
||||||
|
``eventcb``.
|
||||||
|
|
||||||
The ``eventcb()`` callback is invoked by the libevent event loop when an event
|
The ``eventcb()`` callback is invoked by the libevent event loop when an event
|
||||||
(e.g., connection has been established, timeout, etc) happens on the
|
(e.g., connection has been established, timeout, etc) happens on the
|
||||||
underlying network socket::
|
underlying network socket::
|
||||||
|
|
||||||
static void eventcb(struct bufferevent *bev, short events, void *ptr)
|
static void eventcb(struct bufferevent *bev _U_, short events, void *ptr) {
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||||
if (events & BEV_EVENT_CONNECTED) {
|
if (events & BEV_EVENT_CONNECTED) {
|
||||||
fprintf(stderr, "%s connected\n", session_data->client_addr);
|
fprintf(stderr, "%s connected\n", session_data->client_addr);
|
||||||
|
|
||||||
|
initialize_nghttp2_session(session_data);
|
||||||
|
|
||||||
|
if (send_server_connection_header(session_data) != 0) {
|
||||||
|
delete_http2_session_data(session_data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (events & BEV_EVENT_EOF) {
|
if (events & BEV_EVENT_EOF) {
|
||||||
@@ -177,97 +181,69 @@ underlying network socket::
|
|||||||
delete_http2_session_data(session_data);
|
delete_http2_session_data(session_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
For the ``BEV_EVENT_EOF``, ``BEV_EVENT_ERROR`` and ``BEV_EVENT_TIMEOUT``
|
For the ``BEV_EVENT_EOF``, ``BEV_EVENT_ERROR`` and
|
||||||
events, we just simply tear down the connection. The
|
``BEV_EVENT_TIMEOUT`` events, we just simply tear down the connection.
|
||||||
``delete_http2_session_data()`` function destroys the ``http2_session_data``
|
The ``delete_http2_session_data()`` function destroys the
|
||||||
object and thus also its bufferevent member. As a result, the underlying
|
``http2_session_data`` object and thus also its bufferevent member.
|
||||||
connection is closed. The ``BEV_EVENT_CONNECTED`` event is invoked when
|
As a result, the underlying connection is closed. The
|
||||||
SSL/TLS handshake is finished successfully.
|
``BEV_EVENT_CONNECTED`` event is invoked when SSL/TLS handshake is
|
||||||
|
finished successfully. Now we are ready to start the HTTP/2
|
||||||
``handshake_readcb()`` is a callback function to handle a 24 bytes magic byte
|
communication.
|
||||||
string coming from a client, since the nghttp2 library does not handle it::
|
|
||||||
|
|
||||||
static void handshake_readcb(struct bufferevent *bev, void *ptr)
|
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
|
||||||
uint8_t data[24];
|
|
||||||
struct evbuffer *input = bufferevent_get_input(session_data->bev);
|
|
||||||
int readlen = evbuffer_remove(input, data, session_data->handshake_leftlen);
|
|
||||||
const char *conhead = NGHTTP2_CLIENT_CONNECTION_PREFACE;
|
|
||||||
|
|
||||||
if(memcmp(conhead + NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN
|
|
||||||
- session_data->handshake_leftlen, data, readlen) != 0) {
|
|
||||||
delete_http2_session_data(session_data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
session_data->handshake_leftlen -= readlen;
|
|
||||||
if(session_data->handshake_leftlen == 0) {
|
|
||||||
bufferevent_setcb(session_data->bev, readcb, writecb, eventcb, ptr);
|
|
||||||
/* Process pending data in buffer since they are not notified
|
|
||||||
further */
|
|
||||||
initialize_nghttp2_session(session_data);
|
|
||||||
if(send_server_connection_header(session_data) != 0) {
|
|
||||||
delete_http2_session_data(session_data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(session_recv(session_data) != 0) {
|
|
||||||
delete_http2_session_data(session_data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
We check that the received byte string matches
|
|
||||||
:macro:`NGHTTP2_CLIENT_CONNECTION_PREFACE`. When they match, the connection
|
|
||||||
state is ready to start the HTTP/2 communication. First we change the callback
|
|
||||||
functions for the bufferevent object. We use the same ``eventcb`` callback as
|
|
||||||
before, but we specify new ``readcb`` and ``writecb`` functions to handle the
|
|
||||||
HTTP/2 communication. These two functions are described later.
|
|
||||||
|
|
||||||
We initialize a nghttp2 session object which is done in
|
We initialize a nghttp2 session object which is done in
|
||||||
``initialize_nghttp2_session()``::
|
``initialize_nghttp2_session()``::
|
||||||
|
|
||||||
static void initialize_nghttp2_session(http2_session_data *session_data)
|
static void initialize_nghttp2_session(http2_session_data *session_data) {
|
||||||
{
|
nghttp2_option *option;
|
||||||
nghttp2_session_callbacks *callbacks;
|
nghttp2_session_callbacks *callbacks;
|
||||||
|
|
||||||
|
nghttp2_option_new(&option);
|
||||||
|
|
||||||
|
/* Tells nghttp2_session object that it handles client connection
|
||||||
|
preface */
|
||||||
|
nghttp2_option_set_recv_client_preface(option, 1);
|
||||||
|
|
||||||
nghttp2_session_callbacks_new(&callbacks);
|
nghttp2_session_callbacks_new(&callbacks);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_frame_recv_callback
|
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
|
||||||
(callbacks, on_frame_recv_callback);
|
on_frame_recv_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_stream_close_callback
|
nghttp2_session_callbacks_set_on_stream_close_callback(
|
||||||
(callbacks, on_stream_close_callback);
|
callbacks, on_stream_close_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_header_callback
|
nghttp2_session_callbacks_set_on_header_callback(callbacks,
|
||||||
(callbacks, on_header_callback);
|
on_header_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_begin_headers_callback
|
nghttp2_session_callbacks_set_on_begin_headers_callback(
|
||||||
(callbacks, on_begin_headers_callback);
|
callbacks, on_begin_headers_callback);
|
||||||
|
|
||||||
nghttp2_session_server_new(&session_data->session, callbacks, session_data);
|
nghttp2_session_server_new2(&session_data->session, callbacks, session_data,
|
||||||
|
option);
|
||||||
|
|
||||||
nghttp2_session_callbacks_del(callbacks);
|
nghttp2_session_callbacks_del(callbacks);
|
||||||
|
nghttp2_option_del(option);
|
||||||
}
|
}
|
||||||
|
|
||||||
Since we are creating a server, the nghttp2 session object is created using
|
Since we are creating a server and uses options, the nghttp2 session
|
||||||
`nghttp2_session_server_new()` function. We registers five callbacks for
|
object is created using `nghttp2_session_server_new2()` function. We
|
||||||
nghttp2 session object. We'll talk about these callbacks later.
|
registers five callbacks for nghttp2 session object. We'll talk about
|
||||||
|
these callbacks later. Our server only speaks HTTP/2. In this case,
|
||||||
|
we use `nghttp2_option_set_recv_client_preface()` to make
|
||||||
|
:type:`nghttp2_session` object handle client connection preface, which
|
||||||
|
saves some lines of application code.
|
||||||
|
|
||||||
After initialization of the nghttp2 session object, we are going to send
|
After initialization of the nghttp2 session object, we are going to send
|
||||||
a server connection header in ``send_server_connection_header()``::
|
a server connection header in ``send_server_connection_header()``::
|
||||||
|
|
||||||
static int send_server_connection_header(http2_session_data *session_data)
|
static int send_server_connection_header(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
nghttp2_settings_entry iv[1] = {
|
nghttp2_settings_entry iv[1] = {
|
||||||
{ NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 }
|
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
|
||||||
};
|
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE,
|
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
|
||||||
iv, ARRLEN(iv));
|
ARRLEN(iv));
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||||
return -1;
|
return -1;
|
||||||
@@ -289,8 +265,7 @@ have to process them here since libevent won't invoke callback functions for
|
|||||||
this pending data. To process the received data, we call the
|
this pending data. To process the received data, we call the
|
||||||
``session_recv()`` function::
|
``session_recv()`` function::
|
||||||
|
|
||||||
static int session_recv(http2_session_data *session_data)
|
static int session_recv(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
ssize_t readlen;
|
ssize_t readlen;
|
||||||
struct evbuffer *input = bufferevent_get_input(session_data->bev);
|
struct evbuffer *input = bufferevent_get_input(session_data->bev);
|
||||||
size_t datalen = evbuffer_get_length(input);
|
size_t datalen = evbuffer_get_length(input);
|
||||||
@@ -318,8 +293,7 @@ nghttp2 callbacks and also queue outgoing frames. Since there may be pending
|
|||||||
outgoing frames, we call ``session_send()`` function to send off those
|
outgoing frames, we call ``session_send()`` function to send off those
|
||||||
frames. The ``session_send()`` function is defined as follows::
|
frames. The ``session_send()`` function is defined as follows::
|
||||||
|
|
||||||
static int session_send(http2_session_data *session_data)
|
static int session_send(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
rv = nghttp2_session_send(session_data->session);
|
rv = nghttp2_session_send(session_data->session);
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
@@ -334,10 +308,8 @@ format and calls ``send_callback()`` of type
|
|||||||
:type:`nghttp2_send_callback`. The ``send_callback()`` is defined as
|
:type:`nghttp2_send_callback`. The ``send_callback()`` is defined as
|
||||||
follows::
|
follows::
|
||||||
|
|
||||||
static ssize_t send_callback(nghttp2_session *session,
|
static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
|
||||||
const uint8_t *data, size_t length,
|
size_t length, int flags _U_, void *user_data) {
|
||||||
int flags, void *user_data)
|
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
struct bufferevent *bev = session_data->bev;
|
struct bufferevent *bev = session_data->bev;
|
||||||
/* Avoid excessive buffering in server side. */
|
/* Avoid excessive buffering in server side. */
|
||||||
@@ -367,8 +339,7 @@ calling send_callback.
|
|||||||
The next bufferevent callback is ``readcb()``, which is invoked when
|
The next bufferevent callback is ``readcb()``, which is invoked when
|
||||||
data is available to read in the bufferevent input buffer::
|
data is available to read in the bufferevent input buffer::
|
||||||
|
|
||||||
static void readcb(struct bufferevent *bev, void *ptr)
|
static void readcb(struct bufferevent *bev _U_, void *ptr) {
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||||
if (session_recv(session_data) != 0) {
|
if (session_recv(session_data) != 0) {
|
||||||
delete_http2_session_data(session_data);
|
delete_http2_session_data(session_data);
|
||||||
@@ -382,8 +353,7 @@ data.
|
|||||||
The third bufferevent callback is ``writecb()``, which is invoked when all
|
The third bufferevent callback is ``writecb()``, which is invoked when all
|
||||||
data in the bufferevent output buffer has been sent::
|
data in the bufferevent output buffer has been sent::
|
||||||
|
|
||||||
static void writecb(struct bufferevent *bev, void *ptr)
|
static void writecb(struct bufferevent *bev, void *ptr) {
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||||
if (evbuffer_get_length(bufferevent_get_output(bev)) > 0) {
|
if (evbuffer_get_length(bufferevent_get_output(bev)) > 0) {
|
||||||
return;
|
return;
|
||||||
@@ -424,8 +394,7 @@ a header block in HEADERS or PUSH_PROMISE frame is started::
|
|||||||
|
|
||||||
static int on_begin_headers_callback(nghttp2_session *session,
|
static int on_begin_headers_callback(nghttp2_session *session,
|
||||||
const nghttp2_frame *frame,
|
const nghttp2_frame *frame,
|
||||||
void *user_data)
|
void *user_data) {
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
http2_stream_data *stream_data;
|
http2_stream_data *stream_data;
|
||||||
|
|
||||||
@@ -453,11 +422,10 @@ emitted via ``on_header_callback`` function, which is called after
|
|||||||
``on_begin_headers_callback()``::
|
``on_begin_headers_callback()``::
|
||||||
|
|
||||||
static int on_header_callback(nghttp2_session *session,
|
static int on_header_callback(nghttp2_session *session,
|
||||||
const nghttp2_frame *frame,
|
const nghttp2_frame *frame, const uint8_t *name,
|
||||||
const uint8_t *name, size_t namelen,
|
size_t namelen, const uint8_t *value,
|
||||||
const uint8_t *value, size_t valuelen,
|
size_t valuelen, uint8_t flags _U_,
|
||||||
void *user_data)
|
void *user_data _U_) {
|
||||||
{
|
|
||||||
http2_stream_data *stream_data;
|
http2_stream_data *stream_data;
|
||||||
const char PATH[] = ":path";
|
const char PATH[] = ":path";
|
||||||
switch (frame->hd.type) {
|
switch (frame->hd.type) {
|
||||||
@@ -465,14 +433,15 @@ emitted via ``on_header_callback`` function, which is called after
|
|||||||
if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
stream_data = nghttp2_session_get_stream_user_data(session,
|
stream_data =
|
||||||
frame->hd.stream_id);
|
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
|
||||||
if (!stream_data || stream_data->request_path) {
|
if (!stream_data || stream_data->request_path) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) {
|
if (namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) {
|
||||||
size_t j;
|
size_t j;
|
||||||
for(j = 0; j < valuelen && value[j] != '?'; ++j);
|
for (j = 0; j < valuelen && value[j] != '?'; ++j)
|
||||||
|
;
|
||||||
stream_data->request_path = percent_decode(value, j);
|
stream_data->request_path = percent_decode(value, j);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -489,8 +458,7 @@ The ``on_frame_recv_callback()`` function is invoked when a frame is
|
|||||||
fully received::
|
fully received::
|
||||||
|
|
||||||
static int on_frame_recv_callback(nghttp2_session *session,
|
static int on_frame_recv_callback(nghttp2_session *session,
|
||||||
const nghttp2_frame *frame, void *user_data)
|
const nghttp2_frame *frame, void *user_data) {
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
http2_stream_data *stream_data;
|
http2_stream_data *stream_data;
|
||||||
switch (frame->hd.type) {
|
switch (frame->hd.type) {
|
||||||
@@ -498,8 +466,8 @@ fully received::
|
|||||||
case NGHTTP2_HEADERS:
|
case NGHTTP2_HEADERS:
|
||||||
/* Check that the client request has finished */
|
/* Check that the client request has finished */
|
||||||
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||||
stream_data = nghttp2_session_get_stream_user_data(session,
|
stream_data =
|
||||||
frame->hd.stream_id);
|
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
|
||||||
/* For DATA and HEADERS frame, this callback may be called after
|
/* For DATA and HEADERS frame, this callback may be called after
|
||||||
on_stream_close_callback. Check that stream still alive. */
|
on_stream_close_callback. Check that stream still alive. */
|
||||||
if (!stream_data) {
|
if (!stream_data) {
|
||||||
@@ -525,8 +493,7 @@ header.
|
|||||||
Sending the content of the file is done in ``send_response()`` function::
|
Sending the content of the file is done in ``send_response()`` function::
|
||||||
|
|
||||||
static int send_response(nghttp2_session *session, int32_t stream_id,
|
static int send_response(nghttp2_session *session, int32_t stream_id,
|
||||||
nghttp2_nv *nva, size_t nvlen, int fd)
|
nghttp2_nv *nva, size_t nvlen, int fd) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
nghttp2_data_provider data_prd;
|
nghttp2_data_provider data_prd;
|
||||||
data_prd.source.fd = fd;
|
data_prd.source.fd = fd;
|
||||||
@@ -547,14 +514,15 @@ intended to be used as file descriptor. In this example server, we use
|
|||||||
the file descriptor. We also set the ``file_read_callback()`` callback
|
the file descriptor. We also set the ``file_read_callback()`` callback
|
||||||
function to read the contents of the file::
|
function to read the contents of the file::
|
||||||
|
|
||||||
static ssize_t file_read_callback
|
static ssize_t file_read_callback(nghttp2_session *session _U_,
|
||||||
(nghttp2_session *session, int32_t stream_id,
|
int32_t stream_id _U_, uint8_t *buf,
|
||||||
uint8_t *buf, size_t length, uint32_t *data_flags,
|
size_t length, uint32_t *data_flags,
|
||||||
nghttp2_data_source *source, void *user_data)
|
nghttp2_data_source *source,
|
||||||
{
|
void *user_data _U_) {
|
||||||
int fd = source->fd;
|
int fd = source->fd;
|
||||||
ssize_t r;
|
ssize_t r;
|
||||||
while((r = read(fd, buf, length)) == -1 && errno == EINTR);
|
while ((r = read(fd, buf, length)) == -1 && errno == EINTR)
|
||||||
|
;
|
||||||
if (r == -1) {
|
if (r == -1) {
|
||||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||||
}
|
}
|
||||||
@@ -576,11 +544,8 @@ remote peer.
|
|||||||
The ``on_stream_close_callback()`` function is invoked when the stream
|
The ``on_stream_close_callback()`` function is invoked when the stream
|
||||||
is about to close::
|
is about to close::
|
||||||
|
|
||||||
static int on_stream_close_callback(nghttp2_session *session,
|
static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||||
int32_t stream_id,
|
uint32_t error_code _U_, void *user_data) {
|
||||||
nghttp2_error_code error_code,
|
|
||||||
void *user_data)
|
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
http2_stream_data *stream_data;
|
http2_stream_data *stream_data;
|
||||||
|
|
||||||
|
|||||||
4
examples/.gitignore
vendored
4
examples/.gitignore
vendored
@@ -2,3 +2,7 @@ client
|
|||||||
libevent-client
|
libevent-client
|
||||||
libevent-server
|
libevent-server
|
||||||
deflate
|
deflate
|
||||||
|
asio-sv
|
||||||
|
tiny-nghttpd
|
||||||
|
asio-sv2
|
||||||
|
asio-sv3
|
||||||
|
|||||||
@@ -23,20 +23,20 @@
|
|||||||
|
|
||||||
if ENABLE_EXAMPLES
|
if ENABLE_EXAMPLES
|
||||||
|
|
||||||
|
AM_CFLAGS = $(WARNCFLAGS)
|
||||||
AM_CPPFLAGS = \
|
AM_CPPFLAGS = \
|
||||||
-Wall \
|
-Wall \
|
||||||
-I$(top_srcdir)/lib/includes \
|
-I$(top_srcdir)/lib/includes \
|
||||||
-I$(top_builddir)/lib/includes \
|
-I$(top_builddir)/lib/includes \
|
||||||
|
-I$(top_srcdir)/src/includes \
|
||||||
-I$(top_srcdir)/third-party \
|
-I$(top_srcdir)/third-party \
|
||||||
@LIBEVENT_OPENSSL_CFLAGS@ \
|
@LIBEVENT_OPENSSL_CFLAGS@ \
|
||||||
@OPENSSL_CFLAGS@ \
|
@OPENSSL_CFLAGS@ \
|
||||||
@DEFS@
|
@DEFS@
|
||||||
AM_LDFLAGS = \
|
LDADD = $(top_builddir)/lib/libnghttp2.la \
|
||||||
|
$(top_builddir)/third-party/libhttp-parser.la \
|
||||||
@LIBEVENT_OPENSSL_LIBS@ \
|
@LIBEVENT_OPENSSL_LIBS@ \
|
||||||
@OPENSSL_LIBS@
|
@OPENSSL_LIBS@
|
||||||
LDADD = \
|
|
||||||
$(top_builddir)/lib/libnghttp2.la \
|
|
||||||
$(top_builddir)/third-party/libhttp-parser.la
|
|
||||||
|
|
||||||
noinst_PROGRAMS = client libevent-client libevent-server deflate
|
noinst_PROGRAMS = client libevent-client libevent-server deflate
|
||||||
|
|
||||||
@@ -48,4 +48,33 @@ libevent_server_SOURCES = libevent-server.c
|
|||||||
|
|
||||||
deflate_SOURCES = deflate.c
|
deflate_SOURCES = deflate.c
|
||||||
|
|
||||||
|
if ENABLE_TINY_NGHTTPD
|
||||||
|
|
||||||
|
noinst_PROGRAMS += tiny-nghttpd
|
||||||
|
|
||||||
|
tiny_nghttpd_SOURCES = tiny-nghttpd.c
|
||||||
|
|
||||||
|
endif # ENABLE_TINY_NGHTTPD
|
||||||
|
|
||||||
|
if ENABLE_ASIO_LIB
|
||||||
|
|
||||||
|
noinst_PROGRAMS += asio-sv asio-sv2 asio-sv3
|
||||||
|
|
||||||
|
ASIOCPPFLAGS = ${BOOST_CPPFLAGS} ${AM_CPPFLAGS}
|
||||||
|
ASIOLDADD = $(top_builddir)/src/libnghttp2_asio.la @JEMALLOC_LIBS@
|
||||||
|
|
||||||
|
asio_sv_SOURCES = asio-sv.cc
|
||||||
|
asio_sv_CPPFLAGS = ${ASIOCPPFLAGS}
|
||||||
|
asio_sv_LDADD = ${ASIOLDADD}
|
||||||
|
|
||||||
|
asio_sv2_SOURCES = asio-sv2.cc
|
||||||
|
asio_sv2_CPPFLAGS = ${ASIOCPPFLAGS}
|
||||||
|
asio_sv2_LDADD = ${ASIOLDADD}
|
||||||
|
|
||||||
|
asio_sv3_SOURCES = asio-sv3.cc
|
||||||
|
asio_sv3_CPPFLAGS = ${ASIOCPPFLAGS}
|
||||||
|
asio_sv3_LDADD = ${ASIOLDADD}
|
||||||
|
|
||||||
|
endif # ENABLE_ASIO_LIB
|
||||||
|
|
||||||
endif # ENABLE_EXAMPLES
|
endif # ENABLE_EXAMPLES
|
||||||
|
|||||||
75
examples/asio-sv.cc
Normal file
75
examples/asio-sv.cc
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* nghttp2 - HTTP/2 C Library
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014 Tatsuhiro Tsujikawa
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
// We wrote this code based on the original code which has the
|
||||||
|
// following license:
|
||||||
|
//
|
||||||
|
// main.cpp
|
||||||
|
// ~~~~~~~~
|
||||||
|
//
|
||||||
|
// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <nghttp2/asio_http2.h>
|
||||||
|
|
||||||
|
using namespace nghttp2::asio_http2;
|
||||||
|
using namespace nghttp2::asio_http2::server;
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
try {
|
||||||
|
// Check command line arguments.
|
||||||
|
if (argc < 3) {
|
||||||
|
std::cerr << "Usage: asio-sv <port> <threads> <private-key-file> "
|
||||||
|
<< "<cert-file>\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t port = std::stoi(argv[1]);
|
||||||
|
std::size_t num_threads = std::stoi(argv[2]);
|
||||||
|
|
||||||
|
http2 server;
|
||||||
|
|
||||||
|
server.num_threads(num_threads);
|
||||||
|
|
||||||
|
if (argc >= 5) {
|
||||||
|
server.tls(argv[3], argv[4]);
|
||||||
|
}
|
||||||
|
|
||||||
|
server.listen("*", port, [](const std::shared_ptr<request> &req,
|
||||||
|
const std::shared_ptr<response> &res) {
|
||||||
|
res->write_head(200, {header{"foo", "bar"}});
|
||||||
|
res->end("hello, world");
|
||||||
|
});
|
||||||
|
} catch (std::exception &e) {
|
||||||
|
std::cerr << "exception: " << e.what() << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
107
examples/asio-sv2.cc
Normal file
107
examples/asio-sv2.cc
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* nghttp2 - HTTP/2 C Library
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014 Tatsuhiro Tsujikawa
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
// We wrote this code based on the original code which has the
|
||||||
|
// following license:
|
||||||
|
//
|
||||||
|
// main.cpp
|
||||||
|
// ~~~~~~~~
|
||||||
|
//
|
||||||
|
// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <nghttp2/asio_http2.h>
|
||||||
|
|
||||||
|
using namespace nghttp2::asio_http2;
|
||||||
|
using namespace nghttp2::asio_http2::server;
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
try {
|
||||||
|
// Check command line arguments.
|
||||||
|
if (argc < 4) {
|
||||||
|
std::cerr << "Usage: asio-sv2 <port> <threads> <doc-root> "
|
||||||
|
<< "<private-key-file> <cert-file>\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t port = std::stoi(argv[1]);
|
||||||
|
std::size_t num_threads = std::stoi(argv[2]);
|
||||||
|
std::string docroot = argv[3];
|
||||||
|
|
||||||
|
http2 server;
|
||||||
|
|
||||||
|
server.num_threads(num_threads);
|
||||||
|
|
||||||
|
if (argc >= 6) {
|
||||||
|
server.tls(argv[4], argv[5]);
|
||||||
|
}
|
||||||
|
|
||||||
|
server.listen("*", port, [&docroot](const std::shared_ptr<request> &req,
|
||||||
|
const std::shared_ptr<response> &res) {
|
||||||
|
auto path = percent_decode(req->path());
|
||||||
|
if (!check_path(path)) {
|
||||||
|
res->write_head(404);
|
||||||
|
res->end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path == "/") {
|
||||||
|
path = "/index.html";
|
||||||
|
}
|
||||||
|
|
||||||
|
path = docroot + path;
|
||||||
|
auto fd = open(path.c_str(), O_RDONLY);
|
||||||
|
if (fd == -1) {
|
||||||
|
res->write_head(404);
|
||||||
|
res->end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto headers = std::vector<header>();
|
||||||
|
|
||||||
|
struct stat stbuf;
|
||||||
|
if (stat(path.c_str(), &stbuf) == 0) {
|
||||||
|
headers.push_back(
|
||||||
|
header{"content-length", std::to_string(stbuf.st_size)});
|
||||||
|
headers.push_back(
|
||||||
|
header{"last-modified", http_date(stbuf.st_mtim.tv_sec)});
|
||||||
|
}
|
||||||
|
res->write_head(200, std::move(headers));
|
||||||
|
res->end(file_reader_from_fd(fd));
|
||||||
|
});
|
||||||
|
} catch (std::exception &e) {
|
||||||
|
std::cerr << "exception: " << e.what() << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
142
examples/asio-sv3.cc
Normal file
142
examples/asio-sv3.cc
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
/*
|
||||||
|
* nghttp2 - HTTP/2 C Library
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014 Tatsuhiro Tsujikawa
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
// We wrote this code based on the original code which has the
|
||||||
|
// following license:
|
||||||
|
//
|
||||||
|
// main.cpp
|
||||||
|
// ~~~~~~~~
|
||||||
|
//
|
||||||
|
// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <deque>
|
||||||
|
|
||||||
|
#include <nghttp2/asio_http2.h>
|
||||||
|
|
||||||
|
using namespace nghttp2::asio_http2;
|
||||||
|
using namespace nghttp2::asio_http2::server;
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
try {
|
||||||
|
// Check command line arguments.
|
||||||
|
if (argc < 4) {
|
||||||
|
std::cerr << "Usage: asio-sv3 <port> <threads> <tasks> "
|
||||||
|
<< " <private-key-file> <cert-file>\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t port = std::stoi(argv[1]);
|
||||||
|
std::size_t num_threads = std::stoi(argv[2]);
|
||||||
|
std::size_t num_concurrent_tasks = std::stoi(argv[3]);
|
||||||
|
|
||||||
|
http2 server;
|
||||||
|
|
||||||
|
server.num_threads(num_threads);
|
||||||
|
|
||||||
|
if (argc >= 5) {
|
||||||
|
server.tls(argv[4], argv[5]);
|
||||||
|
}
|
||||||
|
|
||||||
|
server.num_concurrent_tasks(num_concurrent_tasks);
|
||||||
|
|
||||||
|
server.listen("*", port, [](const std::shared_ptr<request> &req,
|
||||||
|
const std::shared_ptr<response> &res) {
|
||||||
|
res->write_head(200);
|
||||||
|
|
||||||
|
auto msgq = std::make_shared<std::deque<std::string>>();
|
||||||
|
|
||||||
|
res->end([msgq](uint8_t * buf, std::size_t len)
|
||||||
|
-> std::pair<ssize_t, bool> {
|
||||||
|
if (msgq->empty()) {
|
||||||
|
// if msgq is empty, tells the library that don't call
|
||||||
|
// this callback until we call res->resume(). This is
|
||||||
|
// done by returing std::make_pair(0, false).
|
||||||
|
return std::make_pair(0, false);
|
||||||
|
}
|
||||||
|
auto msg = std::move(msgq->front());
|
||||||
|
msgq->pop_front();
|
||||||
|
|
||||||
|
if (msg.empty()) {
|
||||||
|
// The empty message signals the end of response in
|
||||||
|
// this simple protocol.
|
||||||
|
return std::make_pair(0, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto nwrite = std::min(len, msg.size());
|
||||||
|
std::copy(std::begin(msg), std::begin(msg) + nwrite, buf);
|
||||||
|
if (msg.size() > nwrite) {
|
||||||
|
msgq->push_front(msg.substr(nwrite));
|
||||||
|
}
|
||||||
|
return std::make_pair(nwrite, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
req->run_task([res, msgq](channel &channel) {
|
||||||
|
// executed in different thread from request callback
|
||||||
|
// was called.
|
||||||
|
|
||||||
|
// Using res and msgq is not safe inside this callback.
|
||||||
|
// But using them in callback passed to channel::post is
|
||||||
|
// safe.
|
||||||
|
|
||||||
|
// We just emit simple message "message N\n" in every 1
|
||||||
|
// second and 3 times in total.
|
||||||
|
for (std::size_t i = 0; i < 3; ++i) {
|
||||||
|
msgq->push_back("message " + std::to_string(i + 1) + "\n");
|
||||||
|
|
||||||
|
channel.post([res]() {
|
||||||
|
// executed in same thread where
|
||||||
|
// request callback was called.
|
||||||
|
|
||||||
|
// Tells library we have new message.
|
||||||
|
res->resume();
|
||||||
|
});
|
||||||
|
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send empty message to signal the end of response
|
||||||
|
// body.
|
||||||
|
msgq->push_back("");
|
||||||
|
|
||||||
|
channel.post([res]() {
|
||||||
|
// executed in same thread where request
|
||||||
|
// callback was called.
|
||||||
|
res->resume();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
} catch (std::exception &e) {
|
||||||
|
std::cerr << "exception: " << e.what() << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -26,6 +26,10 @@
|
|||||||
* This program is written to show how to use nghttp2 API in C and
|
* This program is written to show how to use nghttp2 API in C and
|
||||||
* intentionally made simple.
|
* intentionally made simple.
|
||||||
*/
|
*/
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif /* !HAVE_CONFIG_H */
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@@ -46,19 +50,19 @@
|
|||||||
#include <openssl/err.h>
|
#include <openssl/err.h>
|
||||||
#include <openssl/conf.h>
|
#include <openssl/conf.h>
|
||||||
|
|
||||||
enum {
|
enum { IO_NONE, WANT_READ, WANT_WRITE };
|
||||||
IO_NONE,
|
|
||||||
WANT_READ,
|
|
||||||
WANT_WRITE
|
|
||||||
};
|
|
||||||
|
|
||||||
#define MAKE_NV(NAME, VALUE) \
|
#define MAKE_NV(NAME, VALUE) \
|
||||||
{(uint8_t*)NAME, (uint8_t*)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \
|
{ \
|
||||||
NGHTTP2_NV_FLAG_NONE}
|
(uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \
|
||||||
|
NGHTTP2_NV_FLAG_NONE \
|
||||||
|
}
|
||||||
|
|
||||||
#define MAKE_NV_CS(NAME, VALUE) \
|
#define MAKE_NV_CS(NAME, VALUE) \
|
||||||
{(uint8_t*)NAME, (uint8_t*)VALUE, sizeof(NAME) - 1, strlen(VALUE), \
|
{ \
|
||||||
NGHTTP2_NV_FLAG_NONE}
|
(uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, strlen(VALUE), \
|
||||||
|
NGHTTP2_NV_FLAG_NONE \
|
||||||
|
}
|
||||||
|
|
||||||
struct Connection {
|
struct Connection {
|
||||||
SSL *ssl;
|
SSL *ssl;
|
||||||
@@ -99,8 +103,7 @@ struct URI {
|
|||||||
* Returns copy of string |s| with the length |len|. The returned
|
* Returns copy of string |s| with the length |len|. The returned
|
||||||
* string is NULL-terminated.
|
* string is NULL-terminated.
|
||||||
*/
|
*/
|
||||||
static char* strcopy(const char *s, size_t len)
|
static char *strcopy(const char *s, size_t len) {
|
||||||
{
|
|
||||||
char *dst;
|
char *dst;
|
||||||
dst = malloc(len + 1);
|
dst = malloc(len + 1);
|
||||||
memcpy(dst, s, len);
|
memcpy(dst, s, len);
|
||||||
@@ -111,8 +114,7 @@ static char* strcopy(const char *s, size_t len)
|
|||||||
/*
|
/*
|
||||||
* Prints error message |msg| and exit.
|
* Prints error message |msg| and exit.
|
||||||
*/
|
*/
|
||||||
static void die(const char *msg)
|
static void die(const char *msg) {
|
||||||
{
|
|
||||||
fprintf(stderr, "FATAL: %s\n", msg);
|
fprintf(stderr, "FATAL: %s\n", msg);
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
@@ -121,8 +123,7 @@ static void die(const char *msg)
|
|||||||
* Prints error containing the function name |func| and message |msg|
|
* Prints error containing the function name |func| and message |msg|
|
||||||
* and exit.
|
* and exit.
|
||||||
*/
|
*/
|
||||||
static void dief(const char *func, const char *msg)
|
static void dief(const char *func, const char *msg) {
|
||||||
{
|
|
||||||
fprintf(stderr, "FATAL: %s: %s\n", func, msg);
|
fprintf(stderr, "FATAL: %s: %s\n", func, msg);
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
@@ -131,8 +132,7 @@ static void dief(const char *func, const char *msg)
|
|||||||
* Prints error containing the function name |func| and error code
|
* Prints error containing the function name |func| and error code
|
||||||
* |error_code| and exit.
|
* |error_code| and exit.
|
||||||
*/
|
*/
|
||||||
static void diec(const char *func, int error_code)
|
static void diec(const char *func, int error_code) {
|
||||||
{
|
|
||||||
fprintf(stderr, "FATAL: %s: error_code=%d, msg=%s\n", func, error_code,
|
fprintf(stderr, "FATAL: %s: error_code=%d, msg=%s\n", func, error_code,
|
||||||
nghttp2_strerror(error_code));
|
nghttp2_strerror(error_code));
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
@@ -144,21 +144,19 @@ static void diec(const char *func, int error_code)
|
|||||||
* bytes actually written. See the documentation of
|
* bytes actually written. See the documentation of
|
||||||
* nghttp2_send_callback for the details.
|
* nghttp2_send_callback for the details.
|
||||||
*/
|
*/
|
||||||
static ssize_t send_callback(nghttp2_session *session,
|
static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
|
||||||
const uint8_t *data, size_t length, int flags,
|
size_t length, int flags _U_, void *user_data) {
|
||||||
void *user_data)
|
|
||||||
{
|
|
||||||
struct Connection *connection;
|
struct Connection *connection;
|
||||||
int rv;
|
int rv;
|
||||||
connection = (struct Connection *)user_data;
|
connection = (struct Connection *)user_data;
|
||||||
connection->want_io = IO_NONE;
|
connection->want_io = IO_NONE;
|
||||||
ERR_clear_error();
|
ERR_clear_error();
|
||||||
rv = SSL_write(connection->ssl, data, (int)length);
|
rv = SSL_write(connection->ssl, data, (int)length);
|
||||||
if(rv < 0) {
|
if (rv <= 0) {
|
||||||
int err = SSL_get_error(connection->ssl, rv);
|
int err = SSL_get_error(connection->ssl, rv);
|
||||||
if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
|
if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
|
||||||
connection->want_io = (err == SSL_ERROR_WANT_READ ?
|
connection->want_io =
|
||||||
WANT_READ : WANT_WRITE);
|
(err == SSL_ERROR_WANT_READ ? WANT_READ : WANT_WRITE);
|
||||||
rv = NGHTTP2_ERR_WOULDBLOCK;
|
rv = NGHTTP2_ERR_WOULDBLOCK;
|
||||||
} else {
|
} else {
|
||||||
rv = NGHTTP2_ERR_CALLBACK_FAILURE;
|
rv = NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
@@ -173,10 +171,8 @@ static ssize_t send_callback(nghttp2_session *session,
|
|||||||
* |length| bytes. Returns the number of bytes stored in |buf|. See
|
* |length| bytes. Returns the number of bytes stored in |buf|. See
|
||||||
* the documentation of nghttp2_recv_callback for the details.
|
* the documentation of nghttp2_recv_callback for the details.
|
||||||
*/
|
*/
|
||||||
static ssize_t recv_callback(nghttp2_session *session,
|
static ssize_t recv_callback(nghttp2_session *session _U_, uint8_t *buf,
|
||||||
uint8_t *buf, size_t length, int flags,
|
size_t length, int flags _U_, void *user_data) {
|
||||||
void *user_data)
|
|
||||||
{
|
|
||||||
struct Connection *connection;
|
struct Connection *connection;
|
||||||
int rv;
|
int rv;
|
||||||
connection = (struct Connection *)user_data;
|
connection = (struct Connection *)user_data;
|
||||||
@@ -186,8 +182,8 @@ static ssize_t recv_callback(nghttp2_session *session,
|
|||||||
if (rv < 0) {
|
if (rv < 0) {
|
||||||
int err = SSL_get_error(connection->ssl, rv);
|
int err = SSL_get_error(connection->ssl, rv);
|
||||||
if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
|
if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
|
||||||
connection->want_io = (err == SSL_ERROR_WANT_READ ?
|
connection->want_io =
|
||||||
WANT_READ : WANT_WRITE);
|
(err == SSL_ERROR_WANT_READ ? WANT_READ : WANT_WRITE);
|
||||||
rv = NGHTTP2_ERR_WOULDBLOCK;
|
rv = NGHTTP2_ERR_WOULDBLOCK;
|
||||||
} else {
|
} else {
|
||||||
rv = NGHTTP2_ERR_CALLBACK_FAILURE;
|
rv = NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
@@ -199,8 +195,8 @@ static ssize_t recv_callback(nghttp2_session *session,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int on_frame_send_callback(nghttp2_session *session,
|
static int on_frame_send_callback(nghttp2_session *session,
|
||||||
const nghttp2_frame *frame, void *user_data)
|
const nghttp2_frame *frame,
|
||||||
{
|
void *user_data _U_) {
|
||||||
size_t i;
|
size_t i;
|
||||||
switch (frame->hd.type) {
|
switch (frame->hd.type) {
|
||||||
case NGHTTP2_HEADERS:
|
case NGHTTP2_HEADERS:
|
||||||
@@ -226,8 +222,8 @@ static int on_frame_send_callback(nghttp2_session *session,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int on_frame_recv_callback(nghttp2_session *session,
|
static int on_frame_recv_callback(nghttp2_session *session,
|
||||||
const nghttp2_frame *frame, void *user_data)
|
const nghttp2_frame *frame,
|
||||||
{
|
void *user_data _U_) {
|
||||||
size_t i;
|
size_t i;
|
||||||
switch (frame->hd.type) {
|
switch (frame->hd.type) {
|
||||||
case NGHTTP2_HEADERS:
|
case NGHTTP2_HEADERS:
|
||||||
@@ -262,11 +258,9 @@ static int on_frame_recv_callback(nghttp2_session *session,
|
|||||||
* fetch 1 resource in this program, after reception of the response,
|
* fetch 1 resource in this program, after reception of the response,
|
||||||
* we submit GOAWAY and close the session.
|
* we submit GOAWAY and close the session.
|
||||||
*/
|
*/
|
||||||
static int on_stream_close_callback(nghttp2_session *session,
|
static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||||
int32_t stream_id,
|
uint32_t error_code _U_,
|
||||||
nghttp2_error_code error_code,
|
void *user_data _U_) {
|
||||||
void *user_data)
|
|
||||||
{
|
|
||||||
struct Request *req;
|
struct Request *req;
|
||||||
req = nghttp2_session_get_stream_user_data(session, stream_id);
|
req = nghttp2_session_get_stream_user_data(session, stream_id);
|
||||||
if (req) {
|
if (req) {
|
||||||
@@ -286,16 +280,16 @@ static int on_stream_close_callback(nghttp2_session *session,
|
|||||||
* The implementation of nghttp2_on_data_chunk_recv_callback type. We
|
* The implementation of nghttp2_on_data_chunk_recv_callback type. We
|
||||||
* use this function to print the received response body.
|
* use this function to print the received response body.
|
||||||
*/
|
*/
|
||||||
static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
static int on_data_chunk_recv_callback(nghttp2_session *session,
|
||||||
int32_t stream_id,
|
uint8_t flags _U_, int32_t stream_id,
|
||||||
const uint8_t *data, size_t len,
|
const uint8_t *data, size_t len,
|
||||||
void *user_data)
|
void *user_data _U_) {
|
||||||
{
|
|
||||||
struct Request *req;
|
struct Request *req;
|
||||||
req = nghttp2_session_get_stream_user_data(session, stream_id);
|
req = nghttp2_session_get_stream_user_data(session, stream_id);
|
||||||
if (req) {
|
if (req) {
|
||||||
printf("[INFO] C <---------------------------- S (DATA chunk)\n"
|
printf("[INFO] C <---------------------------- S (DATA chunk)\n"
|
||||||
"%lu bytes\n", (unsigned long int)len);
|
"%lu bytes\n",
|
||||||
|
(unsigned long int)len);
|
||||||
fwrite(data, 1, len, stdout);
|
fwrite(data, 1, len, stdout);
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
@@ -308,23 +302,22 @@ static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
|||||||
* always required. Since we use nghttp2_session_recv(), the
|
* always required. Since we use nghttp2_session_recv(), the
|
||||||
* recv_callback is also required.
|
* recv_callback is also required.
|
||||||
*/
|
*/
|
||||||
static void setup_nghttp2_callbacks(nghttp2_session_callbacks *callbacks)
|
static void setup_nghttp2_callbacks(nghttp2_session_callbacks *callbacks) {
|
||||||
{
|
|
||||||
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_recv_callback(callbacks, recv_callback);
|
nghttp2_session_callbacks_set_recv_callback(callbacks, recv_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_frame_send_callback
|
nghttp2_session_callbacks_set_on_frame_send_callback(callbacks,
|
||||||
(callbacks, on_frame_send_callback);
|
on_frame_send_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_frame_recv_callback
|
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
|
||||||
(callbacks, on_frame_recv_callback);
|
on_frame_recv_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_stream_close_callback
|
nghttp2_session_callbacks_set_on_stream_close_callback(
|
||||||
(callbacks, on_stream_close_callback);
|
callbacks, on_stream_close_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_data_chunk_recv_callback
|
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
|
||||||
(callbacks, on_data_chunk_recv_callback);
|
callbacks, on_data_chunk_recv_callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -332,11 +325,9 @@ static void setup_nghttp2_callbacks(nghttp2_session_callbacks *callbacks)
|
|||||||
* HTTP/2 protocol, if server does not offer HTTP/2 the nghttp2
|
* HTTP/2 protocol, if server does not offer HTTP/2 the nghttp2
|
||||||
* library supports, we terminate program.
|
* library supports, we terminate program.
|
||||||
*/
|
*/
|
||||||
static int select_next_proto_cb(SSL* ssl,
|
static int select_next_proto_cb(SSL *ssl _U_, unsigned char **out,
|
||||||
unsigned char **out, unsigned char *outlen,
|
unsigned char *outlen, const unsigned char *in,
|
||||||
const unsigned char *in, unsigned int inlen,
|
unsigned int inlen, void *arg _U_) {
|
||||||
void *arg)
|
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
/* nghttp2_select_next_protocol() selects HTTP/2 protocol the
|
/* nghttp2_select_next_protocol() selects HTTP/2 protocol the
|
||||||
nghttp2 library supports. */
|
nghttp2 library supports. */
|
||||||
@@ -350,8 +341,7 @@ static int select_next_proto_cb(SSL* ssl,
|
|||||||
/*
|
/*
|
||||||
* Setup SSL/TLS context.
|
* Setup SSL/TLS context.
|
||||||
*/
|
*/
|
||||||
static void init_ssl_ctx(SSL_CTX *ssl_ctx)
|
static void init_ssl_ctx(SSL_CTX *ssl_ctx) {
|
||||||
{
|
|
||||||
/* Disable SSLv2 and enable all workarounds for buggy servers */
|
/* Disable SSLv2 and enable all workarounds for buggy servers */
|
||||||
SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2);
|
SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2);
|
||||||
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
|
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
|
||||||
@@ -360,8 +350,7 @@ static void init_ssl_ctx(SSL_CTX *ssl_ctx)
|
|||||||
SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL);
|
SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ssl_handshake(SSL *ssl, int fd)
|
static void ssl_handshake(SSL *ssl, int fd) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
if (SSL_set_fd(ssl, fd) == 0) {
|
if (SSL_set_fd(ssl, fd) == 0) {
|
||||||
dief("SSL_set_fd", ERR_error_string(ERR_get_error(), NULL));
|
dief("SSL_set_fd", ERR_error_string(ERR_get_error(), NULL));
|
||||||
@@ -377,8 +366,7 @@ static void ssl_handshake(SSL *ssl, int fd)
|
|||||||
* Connects to the host |host| and port |port|. This function returns
|
* Connects to the host |host| and port |port|. This function returns
|
||||||
* the file descriptor of the client socket.
|
* the file descriptor of the client socket.
|
||||||
*/
|
*/
|
||||||
static int connect_to(const char *host, uint16_t port)
|
static int connect_to(const char *host, uint16_t port) {
|
||||||
{
|
|
||||||
struct addrinfo hints;
|
struct addrinfo hints;
|
||||||
int fd = -1;
|
int fd = -1;
|
||||||
int rv;
|
int rv;
|
||||||
@@ -398,7 +386,8 @@ static int connect_to(const char *host, uint16_t port)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
while ((rv = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 &&
|
while ((rv = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 &&
|
||||||
errno == EINTR);
|
errno == EINTR)
|
||||||
|
;
|
||||||
if (rv == 0) {
|
if (rv == 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -409,21 +398,21 @@ static int connect_to(const char *host, uint16_t port)
|
|||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void make_non_block(int fd)
|
static void make_non_block(int fd) {
|
||||||
{
|
|
||||||
int flags, rv;
|
int flags, rv;
|
||||||
while((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR);
|
while ((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR)
|
||||||
|
;
|
||||||
if (flags == -1) {
|
if (flags == -1) {
|
||||||
dief("fcntl", strerror(errno));
|
dief("fcntl", strerror(errno));
|
||||||
}
|
}
|
||||||
while((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR);
|
while ((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR)
|
||||||
|
;
|
||||||
if (rv == -1) {
|
if (rv == -1) {
|
||||||
dief("fcntl", strerror(errno));
|
dief("fcntl", strerror(errno));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void set_tcp_nodelay(int fd)
|
static void set_tcp_nodelay(int fd) {
|
||||||
{
|
|
||||||
int val = 1;
|
int val = 1;
|
||||||
int rv;
|
int rv;
|
||||||
rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val));
|
rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val));
|
||||||
@@ -435,8 +424,7 @@ static void set_tcp_nodelay(int fd)
|
|||||||
/*
|
/*
|
||||||
* Update |pollfd| based on the state of |connection|.
|
* Update |pollfd| based on the state of |connection|.
|
||||||
*/
|
*/
|
||||||
static void ctl_poll(struct pollfd *pollfd, struct Connection *connection)
|
static void ctl_poll(struct pollfd *pollfd, struct Connection *connection) {
|
||||||
{
|
|
||||||
pollfd->events = 0;
|
pollfd->events = 0;
|
||||||
if (nghttp2_session_want_read(connection->session) ||
|
if (nghttp2_session_want_read(connection->session) ||
|
||||||
connection->want_io == WANT_READ) {
|
connection->want_io == WANT_READ) {
|
||||||
@@ -453,22 +441,18 @@ static void ctl_poll(struct pollfd *pollfd, struct Connection *connection)
|
|||||||
* function does not send packets; just append the request to the
|
* function does not send packets; just append the request to the
|
||||||
* internal queue in |connection->session|.
|
* internal queue in |connection->session|.
|
||||||
*/
|
*/
|
||||||
static void submit_request(struct Connection *connection, struct Request *req)
|
static void submit_request(struct Connection *connection, struct Request *req) {
|
||||||
{
|
|
||||||
int32_t stream_id;
|
int32_t stream_id;
|
||||||
const nghttp2_nv nva[] = {
|
|
||||||
/* Make sure that the last item is NULL */
|
/* Make sure that the last item is NULL */
|
||||||
MAKE_NV(":method", "GET"),
|
const nghttp2_nv nva[] = {MAKE_NV(":method", "GET"),
|
||||||
MAKE_NV_CS(":path", req->path),
|
MAKE_NV_CS(":path", req->path),
|
||||||
MAKE_NV(":scheme", "https"),
|
MAKE_NV(":scheme", "https"),
|
||||||
MAKE_NV_CS(":authority", req->hostport),
|
MAKE_NV_CS(":authority", req->hostport),
|
||||||
MAKE_NV("accept", "*/*"),
|
MAKE_NV("accept", "*/*"),
|
||||||
MAKE_NV("user-agent", "nghttp2/"NGHTTP2_VERSION)
|
MAKE_NV("user-agent", "nghttp2/" NGHTTP2_VERSION)};
|
||||||
};
|
|
||||||
|
|
||||||
stream_id = nghttp2_submit_request(connection->session, NULL,
|
stream_id = nghttp2_submit_request(connection->session, NULL, nva,
|
||||||
nva, sizeof(nva)/sizeof(nva[0]),
|
sizeof(nva) / sizeof(nva[0]), NULL, req);
|
||||||
NULL, req);
|
|
||||||
|
|
||||||
if (stream_id < 0) {
|
if (stream_id < 0) {
|
||||||
diec("nghttp2_submit_request", stream_id);
|
diec("nghttp2_submit_request", stream_id);
|
||||||
@@ -481,8 +465,7 @@ static void submit_request(struct Connection *connection, struct Request *req)
|
|||||||
/*
|
/*
|
||||||
* Performs the network I/O.
|
* Performs the network I/O.
|
||||||
*/
|
*/
|
||||||
static void exec_io(struct Connection *connection)
|
static void exec_io(struct Connection *connection) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
rv = nghttp2_session_recv(connection->session);
|
rv = nghttp2_session_recv(connection->session);
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
@@ -494,8 +477,7 @@ static void exec_io(struct Connection *connection)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void request_init(struct Request *req, const struct URI *uri)
|
static void request_init(struct Request *req, const struct URI *uri) {
|
||||||
{
|
|
||||||
req->host = strcopy(uri->host, uri->hostlen);
|
req->host = strcopy(uri->host, uri->hostlen);
|
||||||
req->port = uri->port;
|
req->port = uri->port;
|
||||||
req->path = strcopy(uri->path, uri->pathlen);
|
req->path = strcopy(uri->path, uri->pathlen);
|
||||||
@@ -503,8 +485,7 @@ static void request_init(struct Request *req, const struct URI *uri)
|
|||||||
req->stream_id = -1;
|
req->stream_id = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void request_free(struct Request *req)
|
static void request_free(struct Request *req) {
|
||||||
{
|
|
||||||
free(req->host);
|
free(req->host);
|
||||||
free(req->path);
|
free(req->path);
|
||||||
free(req->hostport);
|
free(req->hostport);
|
||||||
@@ -513,8 +494,7 @@ static void request_free(struct Request *req)
|
|||||||
/*
|
/*
|
||||||
* Fetches the resource denoted by |uri|.
|
* Fetches the resource denoted by |uri|.
|
||||||
*/
|
*/
|
||||||
static void fetch_uri(const struct URI *uri)
|
static void fetch_uri(const struct URI *uri) {
|
||||||
{
|
|
||||||
nghttp2_session_callbacks *callbacks;
|
nghttp2_session_callbacks *callbacks;
|
||||||
int fd;
|
int fd;
|
||||||
SSL_CTX *ssl_ctx;
|
SSL_CTX *ssl_ctx;
|
||||||
@@ -549,8 +529,12 @@ static void fetch_uri(const struct URI *uri)
|
|||||||
connection.want_io = IO_NONE;
|
connection.want_io = IO_NONE;
|
||||||
|
|
||||||
/* Send connection header in blocking I/O mode */
|
/* Send connection header in blocking I/O mode */
|
||||||
SSL_write(ssl, NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
rv = SSL_write(ssl, NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
||||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
|
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
|
||||||
|
if (rv <= 0) {
|
||||||
|
dief("SSL_write failed: could not send connection preface",
|
||||||
|
ERR_error_string(ERR_get_error(), NULL));
|
||||||
|
}
|
||||||
|
|
||||||
/* Here make file descriptor non-block */
|
/* Here make file descriptor non-block */
|
||||||
make_non_block(fd);
|
make_non_block(fd);
|
||||||
@@ -566,8 +550,7 @@ static void fetch_uri(const struct URI *uri)
|
|||||||
|
|
||||||
setup_nghttp2_callbacks(callbacks);
|
setup_nghttp2_callbacks(callbacks);
|
||||||
|
|
||||||
rv = nghttp2_session_client_new(&connection.session, callbacks,
|
rv = nghttp2_session_client_new(&connection.session, callbacks, &connection);
|
||||||
&connection);
|
|
||||||
|
|
||||||
nghttp2_session_callbacks_del(callbacks);
|
nghttp2_session_callbacks_del(callbacks);
|
||||||
|
|
||||||
@@ -607,8 +590,7 @@ static void fetch_uri(const struct URI *uri)
|
|||||||
request_free(&req);
|
request_free(&req);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_uri(struct URI *res, const char *uri)
|
static int parse_uri(struct URI *res, const char *uri) {
|
||||||
{
|
|
||||||
/* We only interested in https */
|
/* We only interested in https */
|
||||||
size_t len, i, offset;
|
size_t len, i, offset;
|
||||||
int ipv6addr = 0;
|
int ipv6addr = 0;
|
||||||
@@ -690,8 +672,7 @@ static int parse_uri(struct URI *res, const char *uri)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv) {
|
||||||
{
|
|
||||||
struct URI uri;
|
struct URI uri;
|
||||||
struct sigaction act;
|
struct sigaction act;
|
||||||
int rv;
|
int rv;
|
||||||
|
|||||||
@@ -22,44 +22,44 @@
|
|||||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif /* !HAVE_CONFIG_H */
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <nghttp2/nghttp2.h>
|
#include <nghttp2/nghttp2.h>
|
||||||
|
|
||||||
#define MAKE_NV(K, V) \
|
#define MAKE_NV(K, V) \
|
||||||
{ (uint8_t*)K, (uint8_t*)V, sizeof(K) - 1, sizeof(V) - 1, \
|
{ \
|
||||||
NGHTTP2_NV_FLAG_NONE }
|
(uint8_t *) K, (uint8_t *)V, sizeof(K) - 1, sizeof(V) - 1, \
|
||||||
|
NGHTTP2_NV_FLAG_NONE \
|
||||||
|
}
|
||||||
|
|
||||||
static void deflate(nghttp2_hd_deflater *deflater,
|
static void deflate(nghttp2_hd_deflater *deflater,
|
||||||
nghttp2_hd_inflater *inflater,
|
nghttp2_hd_inflater *inflater, const nghttp2_nv *const nva,
|
||||||
const nghttp2_nv * const nva, size_t nvlen);
|
size_t nvlen);
|
||||||
|
|
||||||
static int inflate_header_block(nghttp2_hd_inflater *inflater,
|
static int inflate_header_block(nghttp2_hd_inflater *inflater, uint8_t *in,
|
||||||
uint8_t *in, size_t inlen, int final);
|
size_t inlen, int final);
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc _U_, char **argv _U_) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
nghttp2_hd_deflater *deflater;
|
nghttp2_hd_deflater *deflater;
|
||||||
nghttp2_hd_inflater *inflater;
|
nghttp2_hd_inflater *inflater;
|
||||||
/* Define 1st header set. This is looks like a HTTP request. */
|
/* Define 1st header set. This is looks like a HTTP request. */
|
||||||
nghttp2_nv nva1[] = {
|
nghttp2_nv nva1[] = {
|
||||||
MAKE_NV(":scheme", "https"),
|
MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "example.org"),
|
||||||
MAKE_NV(":authority", "example.org"),
|
MAKE_NV(":path", "/"), MAKE_NV("user-agent", "libnghttp2"),
|
||||||
MAKE_NV(":path", "/"),
|
MAKE_NV("accept-encoding", "gzip, deflate")};
|
||||||
MAKE_NV("user-agent", "libnghttp2"),
|
|
||||||
MAKE_NV("accept-encoding", "gzip, deflate")
|
|
||||||
};
|
|
||||||
/* Define 2nd header set */
|
/* Define 2nd header set */
|
||||||
nghttp2_nv nva2[] = {
|
nghttp2_nv nva2[] = {MAKE_NV(":scheme", "https"),
|
||||||
MAKE_NV(":scheme", "https"),
|
|
||||||
MAKE_NV(":authority", "example.org"),
|
MAKE_NV(":authority", "example.org"),
|
||||||
MAKE_NV(":path", "/stylesheet/style.css"),
|
MAKE_NV(":path", "/stylesheet/style.css"),
|
||||||
MAKE_NV("user-agent", "libnghttp2"),
|
MAKE_NV("user-agent", "libnghttp2"),
|
||||||
MAKE_NV("accept-encoding", "gzip, deflate"),
|
MAKE_NV("accept-encoding", "gzip, deflate"),
|
||||||
MAKE_NV("referer", "https://example.org")
|
MAKE_NV("referer", "https://example.org")};
|
||||||
};
|
|
||||||
|
|
||||||
rv = nghttp2_hd_deflate_new(&deflater, 4096);
|
rv = nghttp2_hd_deflate_new(&deflater, 4096);
|
||||||
|
|
||||||
@@ -91,9 +91,8 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void deflate(nghttp2_hd_deflater *deflater,
|
static void deflate(nghttp2_hd_deflater *deflater,
|
||||||
nghttp2_hd_inflater *inflater,
|
nghttp2_hd_inflater *inflater, const nghttp2_nv *const nva,
|
||||||
const nghttp2_nv * const nva, size_t nvlen)
|
size_t nvlen) {
|
||||||
{
|
|
||||||
ssize_t rv;
|
ssize_t rv;
|
||||||
uint8_t *buf;
|
uint8_t *buf;
|
||||||
size_t buflen;
|
size_t buflen;
|
||||||
@@ -132,8 +131,8 @@ static void deflate(nghttp2_hd_deflater *deflater,
|
|||||||
|
|
||||||
outlen = rv;
|
outlen = rv;
|
||||||
|
|
||||||
printf("\nDeflate (%zu byte(s), ratio %.02f):\n\n",
|
printf("\nDeflate (%zu byte(s), ratio %.02f):\n\n", outlen,
|
||||||
outlen, sum == 0 ? 0 : (double)outlen / sum);
|
sum == 0 ? 0 : (double)outlen / sum);
|
||||||
|
|
||||||
for (i = 0; i < outlen; ++i) {
|
for (i = 0; i < outlen; ++i) {
|
||||||
if ((i & 0x0fu) == 0) {
|
if ((i & 0x0fu) == 0) {
|
||||||
@@ -165,9 +164,8 @@ static void deflate(nghttp2_hd_deflater *deflater,
|
|||||||
free(buf);
|
free(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
int inflate_header_block(nghttp2_hd_inflater *inflater,
|
int inflate_header_block(nghttp2_hd_inflater *inflater, uint8_t *in,
|
||||||
uint8_t *in, size_t inlen, int final)
|
size_t inlen, int final) {
|
||||||
{
|
|
||||||
ssize_t rv;
|
ssize_t rv;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
@@ -175,8 +173,7 @@ int inflate_header_block(nghttp2_hd_inflater *inflater,
|
|||||||
int inflate_flags = 0;
|
int inflate_flags = 0;
|
||||||
size_t proclen;
|
size_t proclen;
|
||||||
|
|
||||||
rv = nghttp2_hd_inflate_hd(inflater, &nv, &inflate_flags,
|
rv = nghttp2_hd_inflate_hd(inflater, &nv, &inflate_flags, in, inlen, final);
|
||||||
in, inlen, final);
|
|
||||||
|
|
||||||
if (rv < 0) {
|
if (rv < 0) {
|
||||||
fprintf(stderr, "inflate failed with error code %zd", rv);
|
fprintf(stderr, "inflate failed with error code %zd", rv);
|
||||||
@@ -200,8 +197,7 @@ int inflate_header_block(nghttp2_hd_inflater *inflater,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 &&
|
if ((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && inlen == 0) {
|
||||||
inlen == 0) {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,10 @@
|
|||||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif /* !HAVE_CONFIG_H */
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
@@ -71,8 +75,7 @@ typedef struct {
|
|||||||
} http2_session_data;
|
} http2_session_data;
|
||||||
|
|
||||||
static http2_stream_data *create_http2_stream_data(const char *uri,
|
static http2_stream_data *create_http2_stream_data(const char *uri,
|
||||||
struct http_parser_url *u)
|
struct http_parser_url *u) {
|
||||||
{
|
|
||||||
/* MAX 5 digits (max 65535) + 1 ':' + 1 NULL (because of snprintf) */
|
/* MAX 5 digits (max 65535) + 1 ':' + 1 NULL (because of snprintf) */
|
||||||
size_t extra = 7;
|
size_t extra = 7;
|
||||||
http2_stream_data *stream_data = malloc(sizeof(http2_stream_data));
|
http2_stream_data *stream_data = malloc(sizeof(http2_stream_data));
|
||||||
@@ -83,8 +86,8 @@ static http2_stream_data* create_http2_stream_data(const char *uri,
|
|||||||
|
|
||||||
stream_data->authoritylen = u->field_data[UF_HOST].len;
|
stream_data->authoritylen = u->field_data[UF_HOST].len;
|
||||||
stream_data->authority = malloc(stream_data->authoritylen + extra);
|
stream_data->authority = malloc(stream_data->authoritylen + extra);
|
||||||
memcpy(stream_data->authority,
|
memcpy(stream_data->authority, &uri[u->field_data[UF_HOST].off],
|
||||||
&uri[u->field_data[UF_HOST].off], u->field_data[UF_HOST].len);
|
u->field_data[UF_HOST].len);
|
||||||
if (u->field_set & (1 << UF_PORT)) {
|
if (u->field_set & (1 << UF_PORT)) {
|
||||||
stream_data->authoritylen +=
|
stream_data->authoritylen +=
|
||||||
snprintf(stream_data->authority + u->field_data[UF_HOST].len, extra,
|
snprintf(stream_data->authority + u->field_data[UF_HOST].len, extra,
|
||||||
@@ -102,8 +105,8 @@ static http2_stream_data* create_http2_stream_data(const char *uri,
|
|||||||
if (stream_data->pathlen > 0) {
|
if (stream_data->pathlen > 0) {
|
||||||
stream_data->path = malloc(stream_data->pathlen);
|
stream_data->path = malloc(stream_data->pathlen);
|
||||||
if (u->field_set & (1 << UF_PATH)) {
|
if (u->field_set & (1 << UF_PATH)) {
|
||||||
memcpy(stream_data->path,
|
memcpy(stream_data->path, &uri[u->field_data[UF_PATH].off],
|
||||||
&uri[u->field_data[UF_PATH].off], u->field_data[UF_PATH].len);
|
u->field_data[UF_PATH].len);
|
||||||
}
|
}
|
||||||
if (u->field_set & (1 << UF_QUERY)) {
|
if (u->field_set & (1 << UF_QUERY)) {
|
||||||
memcpy(stream_data->path + u->field_data[UF_PATH].len + 1,
|
memcpy(stream_data->path + u->field_data[UF_PATH].len + 1,
|
||||||
@@ -115,16 +118,15 @@ static http2_stream_data* create_http2_stream_data(const char *uri,
|
|||||||
return stream_data;
|
return stream_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void delete_http2_stream_data(http2_stream_data *stream_data)
|
static void delete_http2_stream_data(http2_stream_data *stream_data) {
|
||||||
{
|
|
||||||
free(stream_data->path);
|
free(stream_data->path);
|
||||||
free(stream_data->authority);
|
free(stream_data->authority);
|
||||||
free(stream_data);
|
free(stream_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initializes |session_data| */
|
/* Initializes |session_data| */
|
||||||
static http2_session_data *create_http2_session_data(struct event_base *evbase)
|
static http2_session_data *
|
||||||
{
|
create_http2_session_data(struct event_base *evbase) {
|
||||||
http2_session_data *session_data = malloc(sizeof(http2_session_data));
|
http2_session_data *session_data = malloc(sizeof(http2_session_data));
|
||||||
|
|
||||||
memset(session_data, 0, sizeof(http2_session_data));
|
memset(session_data, 0, sizeof(http2_session_data));
|
||||||
@@ -132,8 +134,7 @@ static http2_session_data *create_http2_session_data(struct event_base *evbase)
|
|||||||
return session_data;
|
return session_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void delete_http2_session_data(http2_session_data *session_data)
|
static void delete_http2_session_data(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
SSL *ssl = bufferevent_openssl_get_ssl(session_data->bev);
|
SSL *ssl = bufferevent_openssl_get_ssl(session_data->bev);
|
||||||
|
|
||||||
if (ssl) {
|
if (ssl) {
|
||||||
@@ -152,10 +153,8 @@ static void delete_http2_session_data(http2_session_data *session_data)
|
|||||||
free(session_data);
|
free(session_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_header(FILE *f,
|
static void print_header(FILE *f, const uint8_t *name, size_t namelen,
|
||||||
const uint8_t *name, size_t namelen,
|
const uint8_t *value, size_t valuelen) {
|
||||||
const uint8_t *value, size_t valuelen)
|
|
||||||
{
|
|
||||||
fwrite(name, namelen, 1, f);
|
fwrite(name, namelen, 1, f);
|
||||||
fprintf(f, ": ");
|
fprintf(f, ": ");
|
||||||
fwrite(value, valuelen, 1, f);
|
fwrite(value, valuelen, 1, f);
|
||||||
@@ -165,13 +164,10 @@ static void print_header(FILE *f,
|
|||||||
/* Print HTTP headers to |f|. Please note that this function does not
|
/* Print HTTP headers to |f|. Please note that this function does not
|
||||||
take into account that header name and value are sequence of
|
take into account that header name and value are sequence of
|
||||||
octets, therefore they may contain non-printable characters. */
|
octets, therefore they may contain non-printable characters. */
|
||||||
static void print_headers(FILE *f, nghttp2_nv *nva, size_t nvlen)
|
static void print_headers(FILE *f, nghttp2_nv *nva, size_t nvlen) {
|
||||||
{
|
|
||||||
size_t i;
|
size_t i;
|
||||||
for (i = 0; i < nvlen; ++i) {
|
for (i = 0; i < nvlen; ++i) {
|
||||||
print_header(f,
|
print_header(f, nva[i].name, nva[i].namelen, nva[i].value, nva[i].valuelen);
|
||||||
nva[i].name, nva[i].namelen,
|
|
||||||
nva[i].value, nva[i].valuelen);
|
|
||||||
}
|
}
|
||||||
fprintf(f, "\n");
|
fprintf(f, "\n");
|
||||||
}
|
}
|
||||||
@@ -179,10 +175,8 @@ static void print_headers(FILE *f, nghttp2_nv *nva, size_t nvlen)
|
|||||||
/* nghttp2_send_callback. Here we transmit the |data|, |length| bytes,
|
/* nghttp2_send_callback. Here we transmit the |data|, |length| bytes,
|
||||||
to the network. Because we are using libevent bufferevent, we just
|
to the network. Because we are using libevent bufferevent, we just
|
||||||
write those bytes into bufferevent buffer. */
|
write those bytes into bufferevent buffer. */
|
||||||
static ssize_t send_callback(nghttp2_session *session,
|
static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
|
||||||
const uint8_t *data, size_t length,
|
size_t length, int flags _U_, void *user_data) {
|
||||||
int flags, void *user_data)
|
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
struct bufferevent *bev = session_data->bev;
|
struct bufferevent *bev = session_data->bev;
|
||||||
bufferevent_write(bev, data, length);
|
bufferevent_write(bev, data, length);
|
||||||
@@ -191,13 +185,11 @@ static ssize_t send_callback(nghttp2_session *session,
|
|||||||
|
|
||||||
/* nghttp2_on_header_callback: Called when nghttp2 library emits
|
/* nghttp2_on_header_callback: Called when nghttp2 library emits
|
||||||
single header name/value pair. */
|
single header name/value pair. */
|
||||||
static int on_header_callback(nghttp2_session *session,
|
static int on_header_callback(nghttp2_session *session _U_,
|
||||||
const nghttp2_frame *frame,
|
const nghttp2_frame *frame, const uint8_t *name,
|
||||||
const uint8_t *name, size_t namelen,
|
size_t namelen, const uint8_t *value,
|
||||||
const uint8_t *value, size_t valuelen,
|
size_t valuelen, uint8_t flags _U_,
|
||||||
uint8_t flags,
|
void *user_data) {
|
||||||
void *user_data)
|
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
switch (frame->hd.type) {
|
switch (frame->hd.type) {
|
||||||
case NGHTTP2_HEADERS:
|
case NGHTTP2_HEADERS:
|
||||||
@@ -213,10 +205,9 @@ static int on_header_callback(nghttp2_session *session,
|
|||||||
|
|
||||||
/* nghttp2_on_begin_headers_callback: Called when nghttp2 library gets
|
/* nghttp2_on_begin_headers_callback: Called when nghttp2 library gets
|
||||||
started to receive header block. */
|
started to receive header block. */
|
||||||
static int on_begin_headers_callback(nghttp2_session *session,
|
static int on_begin_headers_callback(nghttp2_session *session _U_,
|
||||||
const nghttp2_frame *frame,
|
const nghttp2_frame *frame,
|
||||||
void *user_data)
|
void *user_data) {
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
switch (frame->hd.type) {
|
switch (frame->hd.type) {
|
||||||
case NGHTTP2_HEADERS:
|
case NGHTTP2_HEADERS:
|
||||||
@@ -232,9 +223,8 @@ static int on_begin_headers_callback(nghttp2_session *session,
|
|||||||
|
|
||||||
/* nghttp2_on_frame_recv_callback: Called when nghttp2 library
|
/* nghttp2_on_frame_recv_callback: Called when nghttp2 library
|
||||||
received a complete frame from the remote peer. */
|
received a complete frame from the remote peer. */
|
||||||
static int on_frame_recv_callback(nghttp2_session *session,
|
static int on_frame_recv_callback(nghttp2_session *session _U_,
|
||||||
const nghttp2_frame *frame, void *user_data)
|
const nghttp2_frame *frame, void *user_data) {
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
switch (frame->hd.type) {
|
switch (frame->hd.type) {
|
||||||
case NGHTTP2_HEADERS:
|
case NGHTTP2_HEADERS:
|
||||||
@@ -252,11 +242,10 @@ static int on_frame_recv_callback(nghttp2_session *session,
|
|||||||
is meant to the stream we initiated, print the received data in
|
is meant to the stream we initiated, print the received data in
|
||||||
stdout, so that the user can redirect its output to the file
|
stdout, so that the user can redirect its output to the file
|
||||||
easily. */
|
easily. */
|
||||||
static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
static int on_data_chunk_recv_callback(nghttp2_session *session _U_,
|
||||||
int32_t stream_id,
|
uint8_t flags _U_, int32_t stream_id,
|
||||||
const uint8_t *data, size_t len,
|
const uint8_t *data, size_t len,
|
||||||
void *user_data)
|
void *user_data) {
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
if (session_data->stream_data->stream_id == stream_id) {
|
if (session_data->stream_data->stream_id == stream_id) {
|
||||||
fwrite(data, len, 1, stdout);
|
fwrite(data, len, 1, stdout);
|
||||||
@@ -268,17 +257,15 @@ static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
|||||||
closed. This example program only deals with 1 HTTP request (1
|
closed. This example program only deals with 1 HTTP request (1
|
||||||
stream), if it is closed, we send GOAWAY and tear down the
|
stream), if it is closed, we send GOAWAY and tear down the
|
||||||
session */
|
session */
|
||||||
static int on_stream_close_callback(nghttp2_session *session,
|
static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||||
int32_t stream_id,
|
uint32_t error_code,
|
||||||
nghttp2_error_code error_code,
|
void *user_data) {
|
||||||
void *user_data)
|
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
if (session_data->stream_data->stream_id == stream_id) {
|
if (session_data->stream_data->stream_id == stream_id) {
|
||||||
fprintf(stderr, "Stream %d closed with error_code=%d\n",
|
fprintf(stderr, "Stream %d closed with error_code=%d\n", stream_id,
|
||||||
stream_id, error_code);
|
error_code);
|
||||||
rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
|
rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
@@ -290,11 +277,9 @@ static int on_stream_close_callback(nghttp2_session *session,
|
|||||||
/* NPN TLS extension client callback. We check that server advertised
|
/* NPN TLS extension client callback. We check that server advertised
|
||||||
the HTTP/2 protocol the nghttp2 library supports. If not, exit
|
the HTTP/2 protocol the nghttp2 library supports. If not, exit
|
||||||
the program. */
|
the program. */
|
||||||
static int select_next_proto_cb(SSL* ssl,
|
static int select_next_proto_cb(SSL *ssl _U_, unsigned char **out,
|
||||||
unsigned char **out, unsigned char *outlen,
|
unsigned char *outlen, const unsigned char *in,
|
||||||
const unsigned char *in, unsigned int inlen,
|
unsigned int inlen, void *arg _U_) {
|
||||||
void *arg)
|
|
||||||
{
|
|
||||||
if (nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) {
|
if (nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) {
|
||||||
errx(1, "Server did not advertise " NGHTTP2_PROTO_VERSION_ID);
|
errx(1, "Server did not advertise " NGHTTP2_PROTO_VERSION_ID);
|
||||||
}
|
}
|
||||||
@@ -302,8 +287,7 @@ static int select_next_proto_cb(SSL* ssl,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Create SSL_CTX. */
|
/* Create SSL_CTX. */
|
||||||
static SSL_CTX* create_ssl_ctx(void)
|
static SSL_CTX *create_ssl_ctx(void) {
|
||||||
{
|
|
||||||
SSL_CTX *ssl_ctx;
|
SSL_CTX *ssl_ctx;
|
||||||
ssl_ctx = SSL_CTX_new(SSLv23_client_method());
|
ssl_ctx = SSL_CTX_new(SSLv23_client_method());
|
||||||
if (!ssl_ctx) {
|
if (!ssl_ctx) {
|
||||||
@@ -311,15 +295,15 @@ static SSL_CTX* create_ssl_ctx(void)
|
|||||||
ERR_error_string(ERR_get_error(), NULL));
|
ERR_error_string(ERR_get_error(), NULL));
|
||||||
}
|
}
|
||||||
SSL_CTX_set_options(ssl_ctx,
|
SSL_CTX_set_options(ssl_ctx,
|
||||||
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_COMPRESSION |
|
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
|
||||||
|
SSL_OP_NO_COMPRESSION |
|
||||||
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
|
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
|
||||||
SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL);
|
SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL);
|
||||||
return ssl_ctx;
|
return ssl_ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create SSL object */
|
/* Create SSL object */
|
||||||
static SSL* create_ssl(SSL_CTX *ssl_ctx)
|
static SSL *create_ssl(SSL_CTX *ssl_ctx) {
|
||||||
{
|
|
||||||
SSL *ssl;
|
SSL *ssl;
|
||||||
ssl = SSL_new(ssl_ctx);
|
ssl = SSL_new(ssl_ctx);
|
||||||
if (!ssl) {
|
if (!ssl) {
|
||||||
@@ -329,77 +313,75 @@ static SSL* create_ssl(SSL_CTX *ssl_ctx)
|
|||||||
return ssl;
|
return ssl;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void initialize_nghttp2_session(http2_session_data *session_data)
|
static void initialize_nghttp2_session(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
nghttp2_session_callbacks *callbacks;
|
nghttp2_session_callbacks *callbacks;
|
||||||
|
|
||||||
nghttp2_session_callbacks_new(&callbacks);
|
nghttp2_session_callbacks_new(&callbacks);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_frame_recv_callback
|
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
|
||||||
(callbacks, on_frame_recv_callback);
|
on_frame_recv_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_data_chunk_recv_callback
|
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
|
||||||
(callbacks, on_data_chunk_recv_callback);
|
callbacks, on_data_chunk_recv_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_stream_close_callback
|
nghttp2_session_callbacks_set_on_stream_close_callback(
|
||||||
(callbacks, on_stream_close_callback);
|
callbacks, on_stream_close_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_header_callback
|
nghttp2_session_callbacks_set_on_header_callback(callbacks,
|
||||||
(callbacks, on_header_callback);
|
on_header_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_begin_headers_callback
|
nghttp2_session_callbacks_set_on_begin_headers_callback(
|
||||||
(callbacks, on_begin_headers_callback);
|
callbacks, on_begin_headers_callback);
|
||||||
|
|
||||||
nghttp2_session_client_new(&session_data->session, callbacks, session_data);
|
nghttp2_session_client_new(&session_data->session, callbacks, session_data);
|
||||||
|
|
||||||
nghttp2_session_callbacks_del(callbacks);
|
nghttp2_session_callbacks_del(callbacks);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void send_client_connection_header(http2_session_data *session_data)
|
static void send_client_connection_header(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
nghttp2_settings_entry iv[1] = {
|
nghttp2_settings_entry iv[1] = {
|
||||||
{ NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 }
|
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
|
||||||
};
|
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
bufferevent_write(session_data->bev,
|
bufferevent_write(session_data->bev, NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
||||||
NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
|
||||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
|
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
|
||||||
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE,
|
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
|
||||||
iv, ARRLEN(iv));
|
ARRLEN(iv));
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
errx(1, "Could not submit SETTINGS: %s", nghttp2_strerror(rv));
|
errx(1, "Could not submit SETTINGS: %s", nghttp2_strerror(rv));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#define MAKE_NV(NAME, VALUE, VALUELEN) \
|
#define MAKE_NV(NAME, VALUE, VALUELEN) \
|
||||||
{ (uint8_t*)NAME, (uint8_t*)VALUE, sizeof(NAME) - 1, VALUELEN, \
|
{ \
|
||||||
NGHTTP2_NV_FLAG_NONE }
|
(uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, VALUELEN, \
|
||||||
|
NGHTTP2_NV_FLAG_NONE \
|
||||||
|
}
|
||||||
|
|
||||||
#define MAKE_NV2(NAME, VALUE) \
|
#define MAKE_NV2(NAME, VALUE) \
|
||||||
{ (uint8_t*)NAME, (uint8_t*)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \
|
{ \
|
||||||
NGHTTP2_NV_FLAG_NONE }
|
(uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \
|
||||||
|
NGHTTP2_NV_FLAG_NONE \
|
||||||
|
}
|
||||||
|
|
||||||
/* Send HTTP request to the remote peer */
|
/* Send HTTP request to the remote peer */
|
||||||
static void submit_request(http2_session_data *session_data)
|
static void submit_request(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
int32_t stream_id;
|
int32_t stream_id;
|
||||||
http2_stream_data *stream_data = session_data->stream_data;
|
http2_stream_data *stream_data = session_data->stream_data;
|
||||||
const char *uri = stream_data->uri;
|
const char *uri = stream_data->uri;
|
||||||
const struct http_parser_url *u = stream_data->u;
|
const struct http_parser_url *u = stream_data->u;
|
||||||
nghttp2_nv hdrs[] = {
|
nghttp2_nv hdrs[] = {
|
||||||
MAKE_NV2(":method", "GET"),
|
MAKE_NV2(":method", "GET"),
|
||||||
MAKE_NV(":scheme",
|
MAKE_NV(":scheme", &uri[u->field_data[UF_SCHEMA].off],
|
||||||
&uri[u->field_data[UF_SCHEMA].off], u->field_data[UF_SCHEMA].len),
|
u->field_data[UF_SCHEMA].len),
|
||||||
MAKE_NV(":authority", stream_data->authority, stream_data->authoritylen),
|
MAKE_NV(":authority", stream_data->authority, stream_data->authoritylen),
|
||||||
MAKE_NV(":path", stream_data->path, stream_data->pathlen)
|
MAKE_NV(":path", stream_data->path, stream_data->pathlen)};
|
||||||
};
|
|
||||||
fprintf(stderr, "Request headers:\n");
|
fprintf(stderr, "Request headers:\n");
|
||||||
print_headers(stderr, hdrs, ARRLEN(hdrs));
|
print_headers(stderr, hdrs, ARRLEN(hdrs));
|
||||||
stream_id = nghttp2_submit_request(session_data->session, NULL,
|
stream_id = nghttp2_submit_request(session_data->session, NULL, hdrs,
|
||||||
hdrs, ARRLEN(hdrs), NULL, stream_data);
|
ARRLEN(hdrs), NULL, stream_data);
|
||||||
if (stream_id < 0) {
|
if (stream_id < 0) {
|
||||||
errx(1, "Could not submit HTTP request: %s", nghttp2_strerror(stream_id));
|
errx(1, "Could not submit HTTP request: %s", nghttp2_strerror(stream_id));
|
||||||
}
|
}
|
||||||
@@ -409,8 +391,7 @@ static void submit_request(http2_session_data *session_data)
|
|||||||
|
|
||||||
/* Serialize the frame and send (or buffer) the data to
|
/* Serialize the frame and send (or buffer) the data to
|
||||||
bufferevent. */
|
bufferevent. */
|
||||||
static int session_send(http2_session_data *session_data)
|
static int session_send(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
rv = nghttp2_session_send(session_data->session);
|
rv = nghttp2_session_send(session_data->session);
|
||||||
@@ -425,8 +406,7 @@ static int session_send(http2_session_data *session_data)
|
|||||||
of bufferevent and feed them to nghttp2 library. This may invoke
|
of bufferevent and feed them to nghttp2 library. This may invoke
|
||||||
nghttp2 callbacks. It may also queues the frame in nghttp2 session
|
nghttp2 callbacks. It may also queues the frame in nghttp2 session
|
||||||
context. To send them, we call session_send() in the end. */
|
context. To send them, we call session_send() in the end. */
|
||||||
static void readcb(struct bufferevent *bev, void *ptr)
|
static void readcb(struct bufferevent *bev, void *ptr) {
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||||
ssize_t readlen;
|
ssize_t readlen;
|
||||||
struct evbuffer *input = bufferevent_get_input(bev);
|
struct evbuffer *input = bufferevent_get_input(bev);
|
||||||
@@ -454,8 +434,7 @@ static void readcb(struct bufferevent *bev, void *ptr)
|
|||||||
receiving GOAWAY, we check the some conditions on the nghttp2
|
receiving GOAWAY, we check the some conditions on the nghttp2
|
||||||
library and output buffer of bufferevent. If it indicates we have
|
library and output buffer of bufferevent. If it indicates we have
|
||||||
no business to this session, tear down the connection. */
|
no business to this session, tear down the connection. */
|
||||||
static void writecb(struct bufferevent *bev, void *ptr)
|
static void writecb(struct bufferevent *bev _U_, void *ptr) {
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||||
if (nghttp2_session_want_read(session_data->session) == 0 &&
|
if (nghttp2_session_want_read(session_data->session) == 0 &&
|
||||||
nghttp2_session_want_write(session_data->session) == 0 &&
|
nghttp2_session_want_write(session_data->session) == 0 &&
|
||||||
@@ -469,8 +448,7 @@ static void writecb(struct bufferevent *bev, void *ptr)
|
|||||||
peer verification. After SSL/TLS handshake is over, initialize
|
peer verification. After SSL/TLS handshake is over, initialize
|
||||||
nghttp2 library session, and send client connection header. Then
|
nghttp2 library session, and send client connection header. Then
|
||||||
send HTTP request. */
|
send HTTP request. */
|
||||||
static void eventcb(struct bufferevent *bev, short events, void *ptr)
|
static void eventcb(struct bufferevent *bev, short events, void *ptr) {
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||||
if (events & BEV_EVENT_CONNECTED) {
|
if (events & BEV_EVENT_CONNECTED) {
|
||||||
int fd = bufferevent_getfd(bev);
|
int fd = bufferevent_getfd(bev);
|
||||||
@@ -496,20 +474,17 @@ static void eventcb(struct bufferevent *bev, short events, void *ptr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Start connecting to the remote peer |host:port| */
|
/* Start connecting to the remote peer |host:port| */
|
||||||
static void initiate_connection(struct event_base *evbase,
|
static void initiate_connection(struct event_base *evbase, SSL_CTX *ssl_ctx,
|
||||||
SSL_CTX *ssl_ctx,
|
|
||||||
const char *host, uint16_t port,
|
const char *host, uint16_t port,
|
||||||
http2_session_data *session_data)
|
http2_session_data *session_data) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
struct bufferevent *bev;
|
struct bufferevent *bev;
|
||||||
SSL *ssl;
|
SSL *ssl;
|
||||||
|
|
||||||
ssl = create_ssl(ssl_ctx);
|
ssl = create_ssl(ssl_ctx);
|
||||||
bev = bufferevent_openssl_socket_new(evbase, -1, ssl,
|
bev = bufferevent_openssl_socket_new(
|
||||||
BUFFEREVENT_SSL_CONNECTING,
|
evbase, -1, ssl, BUFFEREVENT_SSL_CONNECTING,
|
||||||
BEV_OPT_DEFER_CALLBACKS |
|
BEV_OPT_DEFER_CALLBACKS | BEV_OPT_CLOSE_ON_FREE);
|
||||||
BEV_OPT_CLOSE_ON_FREE);
|
|
||||||
bufferevent_setcb(bev, readcb, writecb, eventcb, session_data);
|
bufferevent_setcb(bev, readcb, writecb, eventcb, session_data);
|
||||||
rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase,
|
rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase,
|
||||||
AF_UNSPEC, host, port);
|
AF_UNSPEC, host, port);
|
||||||
@@ -522,8 +497,7 @@ static void initiate_connection(struct event_base *evbase,
|
|||||||
|
|
||||||
/* Get resource denoted by the |uri|. The debug and error messages are
|
/* Get resource denoted by the |uri|. The debug and error messages are
|
||||||
printed in stderr, while the response body is printed in stdout. */
|
printed in stderr, while the response body is printed in stdout. */
|
||||||
static void run(const char *uri)
|
static void run(const char *uri) {
|
||||||
{
|
|
||||||
struct http_parser_url u;
|
struct http_parser_url u;
|
||||||
char *host;
|
char *host;
|
||||||
uint16_t port;
|
uint16_t port;
|
||||||
@@ -561,8 +535,7 @@ static void run(const char *uri)
|
|||||||
SSL_CTX_free(ssl_ctx);
|
SSL_CTX_free(ssl_ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv) {
|
||||||
{
|
|
||||||
struct sigaction act;
|
struct sigaction act;
|
||||||
|
|
||||||
if (argc < 2) {
|
if (argc < 2) {
|
||||||
|
|||||||
@@ -22,6 +22,10 @@
|
|||||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif /* !HAVE_CONFIG_H */
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
@@ -50,8 +54,10 @@
|
|||||||
#define ARRLEN(x) (sizeof(x) / sizeof(x[0]))
|
#define ARRLEN(x) (sizeof(x) / sizeof(x[0]))
|
||||||
|
|
||||||
#define MAKE_NV(NAME, VALUE) \
|
#define MAKE_NV(NAME, VALUE) \
|
||||||
{ (uint8_t*)NAME, (uint8_t*)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \
|
{ \
|
||||||
NGHTTP2_NV_FLAG_NONE }
|
(uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \
|
||||||
|
NGHTTP2_NV_FLAG_NONE \
|
||||||
|
}
|
||||||
|
|
||||||
struct app_context;
|
struct app_context;
|
||||||
typedef struct app_context app_context;
|
typedef struct app_context app_context;
|
||||||
@@ -69,7 +75,6 @@ typedef struct http2_session_data {
|
|||||||
app_context *app_ctx;
|
app_context *app_ctx;
|
||||||
nghttp2_session *session;
|
nghttp2_session *session;
|
||||||
char *client_addr;
|
char *client_addr;
|
||||||
size_t handshake_leftlen;
|
|
||||||
} http2_session_data;
|
} http2_session_data;
|
||||||
|
|
||||||
struct app_context {
|
struct app_context {
|
||||||
@@ -80,29 +85,37 @@ struct app_context {
|
|||||||
static unsigned char next_proto_list[256];
|
static unsigned char next_proto_list[256];
|
||||||
static size_t next_proto_list_len;
|
static size_t next_proto_list_len;
|
||||||
|
|
||||||
static int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len,
|
static int next_proto_cb(SSL *s _U_, const unsigned char **data,
|
||||||
void *arg)
|
unsigned int *len, void *arg _U_) {
|
||||||
{
|
|
||||||
*data = next_proto_list;
|
*data = next_proto_list;
|
||||||
*len = (unsigned int)next_proto_list_len;
|
*len = (unsigned int)next_proto_list_len;
|
||||||
return SSL_TLSEXT_ERR_OK;
|
return SSL_TLSEXT_ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create SSL_CTX. */
|
/* Create SSL_CTX. */
|
||||||
static SSL_CTX* create_ssl_ctx(const char *key_file, const char *cert_file)
|
static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) {
|
||||||
{
|
|
||||||
SSL_CTX *ssl_ctx;
|
SSL_CTX *ssl_ctx;
|
||||||
|
EC_KEY *ecdh;
|
||||||
|
|
||||||
ssl_ctx = SSL_CTX_new(SSLv23_server_method());
|
ssl_ctx = SSL_CTX_new(SSLv23_server_method());
|
||||||
if (!ssl_ctx) {
|
if (!ssl_ctx) {
|
||||||
errx(1, "Could not create SSL/TLS context: %s",
|
errx(1, "Could not create SSL/TLS context: %s",
|
||||||
ERR_error_string(ERR_get_error(), NULL));
|
ERR_error_string(ERR_get_error(), NULL));
|
||||||
}
|
}
|
||||||
SSL_CTX_set_options(ssl_ctx,
|
SSL_CTX_set_options(ssl_ctx,
|
||||||
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_COMPRESSION |
|
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
|
||||||
|
SSL_OP_NO_COMPRESSION |
|
||||||
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
|
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
|
||||||
|
|
||||||
if(SSL_CTX_use_PrivateKey_file(ssl_ctx, key_file,
|
ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
|
||||||
SSL_FILETYPE_PEM) != 1) {
|
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);
|
||||||
|
|
||||||
|
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);
|
errx(1, "Could not read private key file %s", key_file);
|
||||||
}
|
}
|
||||||
if (SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file) != 1) {
|
if (SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file) != 1) {
|
||||||
@@ -119,8 +132,7 @@ static SSL_CTX* create_ssl_ctx(const char *key_file, const char *cert_file)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Create SSL object */
|
/* Create SSL object */
|
||||||
static SSL* create_ssl(SSL_CTX *ssl_ctx)
|
static SSL *create_ssl(SSL_CTX *ssl_ctx) {
|
||||||
{
|
|
||||||
SSL *ssl;
|
SSL *ssl;
|
||||||
ssl = SSL_new(ssl_ctx);
|
ssl = SSL_new(ssl_ctx);
|
||||||
if (!ssl) {
|
if (!ssl) {
|
||||||
@@ -131,8 +143,7 @@ static SSL* create_ssl(SSL_CTX *ssl_ctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void add_stream(http2_session_data *session_data,
|
static void add_stream(http2_session_data *session_data,
|
||||||
http2_stream_data *stream_data)
|
http2_stream_data *stream_data) {
|
||||||
{
|
|
||||||
stream_data->next = session_data->root.next;
|
stream_data->next = session_data->root.next;
|
||||||
session_data->root.next = stream_data;
|
session_data->root.next = stream_data;
|
||||||
stream_data->prev = &session_data->root;
|
stream_data->prev = &session_data->root;
|
||||||
@@ -141,18 +152,16 @@ static void add_stream(http2_session_data *session_data,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void remove_stream(http2_session_data *session_data,
|
static void remove_stream(http2_session_data *session_data _U_,
|
||||||
http2_stream_data *stream_data)
|
http2_stream_data *stream_data) {
|
||||||
{
|
|
||||||
stream_data->prev->next = stream_data->next;
|
stream_data->prev->next = stream_data->next;
|
||||||
if (stream_data->next) {
|
if (stream_data->next) {
|
||||||
stream_data->next->prev = stream_data->prev;
|
stream_data->next->prev = stream_data->prev;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static http2_stream_data* create_http2_stream_data
|
static http2_stream_data *
|
||||||
(http2_session_data *session_data, int32_t stream_id)
|
create_http2_stream_data(http2_session_data *session_data, int32_t stream_id) {
|
||||||
{
|
|
||||||
http2_stream_data *stream_data;
|
http2_stream_data *stream_data;
|
||||||
stream_data = malloc(sizeof(http2_stream_data));
|
stream_data = malloc(sizeof(http2_stream_data));
|
||||||
memset(stream_data, 0, sizeof(http2_stream_data));
|
memset(stream_data, 0, sizeof(http2_stream_data));
|
||||||
@@ -163,8 +172,7 @@ static http2_stream_data* create_http2_stream_data
|
|||||||
return stream_data;
|
return stream_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void delete_http2_stream_data(http2_stream_data *stream_data)
|
static void delete_http2_stream_data(http2_stream_data *stream_data) {
|
||||||
{
|
|
||||||
if (stream_data->fd != -1) {
|
if (stream_data->fd != -1) {
|
||||||
close(stream_data->fd);
|
close(stream_data->fd);
|
||||||
}
|
}
|
||||||
@@ -175,8 +183,7 @@ static void delete_http2_stream_data(http2_stream_data *stream_data)
|
|||||||
static http2_session_data *create_http2_session_data(app_context *app_ctx,
|
static http2_session_data *create_http2_session_data(app_context *app_ctx,
|
||||||
int fd,
|
int fd,
|
||||||
struct sockaddr *addr,
|
struct sockaddr *addr,
|
||||||
int addrlen)
|
int addrlen) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
http2_session_data *session_data;
|
http2_session_data *session_data;
|
||||||
SSL *ssl;
|
SSL *ssl;
|
||||||
@@ -188,11 +195,9 @@ static http2_session_data* create_http2_session_data(app_context *app_ctx,
|
|||||||
memset(session_data, 0, sizeof(http2_session_data));
|
memset(session_data, 0, sizeof(http2_session_data));
|
||||||
session_data->app_ctx = app_ctx;
|
session_data->app_ctx = app_ctx;
|
||||||
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val));
|
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val));
|
||||||
session_data->bev = bufferevent_openssl_socket_new
|
session_data->bev = bufferevent_openssl_socket_new(
|
||||||
(app_ctx->evbase, fd, ssl,
|
app_ctx->evbase, fd, ssl, BUFFEREVENT_SSL_ACCEPTING,
|
||||||
BUFFEREVENT_SSL_ACCEPTING,
|
|
||||||
BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
|
BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
|
||||||
session_data->handshake_leftlen = NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN;
|
|
||||||
rv = getnameinfo(addr, addrlen, host, sizeof(host), NULL, 0, NI_NUMERICHOST);
|
rv = getnameinfo(addr, addrlen, host, sizeof(host), NULL, 0, NI_NUMERICHOST);
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
session_data->client_addr = strdup("(unknown)");
|
session_data->client_addr = strdup("(unknown)");
|
||||||
@@ -203,8 +208,7 @@ static http2_session_data* create_http2_session_data(app_context *app_ctx,
|
|||||||
return session_data;
|
return session_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void delete_http2_session_data(http2_session_data *session_data)
|
static void delete_http2_session_data(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
http2_stream_data *stream_data;
|
http2_stream_data *stream_data;
|
||||||
SSL *ssl = bufferevent_openssl_get_ssl(session_data->bev);
|
SSL *ssl = bufferevent_openssl_get_ssl(session_data->bev);
|
||||||
fprintf(stderr, "%s disconnected\n", session_data->client_addr);
|
fprintf(stderr, "%s disconnected\n", session_data->client_addr);
|
||||||
@@ -224,8 +228,7 @@ static void delete_http2_session_data(http2_session_data *session_data)
|
|||||||
|
|
||||||
/* Serialize the frame and send (or buffer) the data to
|
/* Serialize the frame and send (or buffer) the data to
|
||||||
bufferevent. */
|
bufferevent. */
|
||||||
static int session_send(http2_session_data *session_data)
|
static int session_send(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
rv = nghttp2_session_send(session_data->session);
|
rv = nghttp2_session_send(session_data->session);
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
@@ -239,8 +242,7 @@ static int session_send(http2_session_data *session_data)
|
|||||||
function. Invocation of nghttp2_session_mem_recv() may make
|
function. Invocation of nghttp2_session_mem_recv() may make
|
||||||
additional pending frames, so call session_send() at the end of the
|
additional pending frames, so call session_send() at the end of the
|
||||||
function. */
|
function. */
|
||||||
static int session_recv(http2_session_data *session_data)
|
static int session_recv(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
ssize_t readlen;
|
ssize_t readlen;
|
||||||
struct evbuffer *input = bufferevent_get_input(session_data->bev);
|
struct evbuffer *input = bufferevent_get_input(session_data->bev);
|
||||||
size_t datalen = evbuffer_get_length(input);
|
size_t datalen = evbuffer_get_length(input);
|
||||||
@@ -261,10 +263,8 @@ static int session_recv(http2_session_data *session_data)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t send_callback(nghttp2_session *session,
|
static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
|
||||||
const uint8_t *data, size_t length,
|
size_t length, int flags _U_, void *user_data) {
|
||||||
int flags, void *user_data)
|
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
struct bufferevent *bev = session_data->bev;
|
struct bufferevent *bev = session_data->bev;
|
||||||
/* Avoid excessive buffering in server side. */
|
/* Avoid excessive buffering in server side. */
|
||||||
@@ -277,8 +277,7 @@ static ssize_t send_callback(nghttp2_session *session,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Returns nonzero if the string |s| ends with the substring |sub| */
|
/* Returns nonzero if the string |s| ends with the substring |sub| */
|
||||||
static int ends_with(const char *s, const char *sub)
|
static int ends_with(const char *s, const char *sub) {
|
||||||
{
|
|
||||||
size_t slen = strlen(s);
|
size_t slen = strlen(s);
|
||||||
size_t sublen = strlen(sub);
|
size_t sublen = strlen(sub);
|
||||||
if (slen < sublen) {
|
if (slen < sublen) {
|
||||||
@@ -288,8 +287,7 @@ static int ends_with(const char *s, const char *sub)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Returns int value of hex string character |c| */
|
/* Returns int value of hex string character |c| */
|
||||||
static uint8_t hex_to_uint(uint8_t c)
|
static uint8_t hex_to_uint(uint8_t c) {
|
||||||
{
|
|
||||||
if ('0' <= c && c <= '9') {
|
if ('0' <= c && c <= '9') {
|
||||||
return c - '0';
|
return c - '0';
|
||||||
}
|
}
|
||||||
@@ -306,16 +304,15 @@ static uint8_t hex_to_uint(uint8_t c)
|
|||||||
and returns the decoded byte string in allocated buffer. The return
|
and returns the decoded byte string in allocated buffer. The return
|
||||||
value is NULL terminated. The caller must free the returned
|
value is NULL terminated. The caller must free the returned
|
||||||
string. */
|
string. */
|
||||||
static char* percent_decode(const uint8_t *value, size_t valuelen)
|
static char *percent_decode(const uint8_t *value, size_t valuelen) {
|
||||||
{
|
|
||||||
char *res;
|
char *res;
|
||||||
|
|
||||||
res = malloc(valuelen + 1);
|
res = malloc(valuelen + 1);
|
||||||
if (valuelen > 3) {
|
if (valuelen > 3) {
|
||||||
size_t i, j;
|
size_t i, j;
|
||||||
for (i = 0, j = 0; i < valuelen - 2;) {
|
for (i = 0, j = 0; i < valuelen - 2;) {
|
||||||
if(value[i] != '%' ||
|
if (value[i] != '%' || !isxdigit(value[i + 1]) ||
|
||||||
!isxdigit(value[i + 1]) || !isxdigit(value[i + 2])) {
|
!isxdigit(value[i + 2])) {
|
||||||
res[j++] = value[i++];
|
res[j++] = value[i++];
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -331,14 +328,15 @@ static char* percent_decode(const uint8_t *value, size_t valuelen)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t file_read_callback
|
static ssize_t file_read_callback(nghttp2_session *session _U_,
|
||||||
(nghttp2_session *session, int32_t stream_id,
|
int32_t stream_id _U_, uint8_t *buf,
|
||||||
uint8_t *buf, size_t length, uint32_t *data_flags,
|
size_t length, uint32_t *data_flags,
|
||||||
nghttp2_data_source *source, void *user_data)
|
nghttp2_data_source *source,
|
||||||
{
|
void *user_data _U_) {
|
||||||
int fd = source->fd;
|
int fd = source->fd;
|
||||||
ssize_t r;
|
ssize_t r;
|
||||||
while((r = read(fd, buf, length)) == -1 && errno == EINTR);
|
while ((r = read(fd, buf, length)) == -1 && errno == EINTR)
|
||||||
|
;
|
||||||
if (r == -1) {
|
if (r == -1) {
|
||||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||||
}
|
}
|
||||||
@@ -349,8 +347,7 @@ static ssize_t file_read_callback
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int send_response(nghttp2_session *session, int32_t stream_id,
|
static int send_response(nghttp2_session *session, int32_t stream_id,
|
||||||
nghttp2_nv *nva, size_t nvlen, int fd)
|
nghttp2_nv *nva, size_t nvlen, int fd) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
nghttp2_data_provider data_prd;
|
nghttp2_data_provider data_prd;
|
||||||
data_prd.source.fd = fd;
|
data_prd.source.fd = fd;
|
||||||
@@ -368,14 +365,11 @@ const char ERROR_HTML[] = "<html><head><title>404</title></head>"
|
|||||||
"<body><h1>404 Not Found</h1></body></html>";
|
"<body><h1>404 Not Found</h1></body></html>";
|
||||||
|
|
||||||
static int error_reply(nghttp2_session *session,
|
static int error_reply(nghttp2_session *session,
|
||||||
http2_stream_data *stream_data)
|
http2_stream_data *stream_data) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
ssize_t writelen;
|
ssize_t writelen;
|
||||||
int pipefd[2];
|
int pipefd[2];
|
||||||
nghttp2_nv hdrs[] = {
|
nghttp2_nv hdrs[] = {MAKE_NV(":status", "404")};
|
||||||
MAKE_NV(":status", "404")
|
|
||||||
};
|
|
||||||
|
|
||||||
rv = pipe(pipefd);
|
rv = pipe(pipefd);
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
@@ -411,12 +405,10 @@ static int error_reply(nghttp2_session *session,
|
|||||||
/* nghttp2_on_header_callback: Called when nghttp2 library emits
|
/* nghttp2_on_header_callback: Called when nghttp2 library emits
|
||||||
single header name/value pair. */
|
single header name/value pair. */
|
||||||
static int on_header_callback(nghttp2_session *session,
|
static int on_header_callback(nghttp2_session *session,
|
||||||
const nghttp2_frame *frame,
|
const nghttp2_frame *frame, const uint8_t *name,
|
||||||
const uint8_t *name, size_t namelen,
|
size_t namelen, const uint8_t *value,
|
||||||
const uint8_t *value, size_t valuelen,
|
size_t valuelen, uint8_t flags _U_,
|
||||||
uint8_t flags,
|
void *user_data _U_) {
|
||||||
void *user_data)
|
|
||||||
{
|
|
||||||
http2_stream_data *stream_data;
|
http2_stream_data *stream_data;
|
||||||
const char PATH[] = ":path";
|
const char PATH[] = ":path";
|
||||||
switch (frame->hd.type) {
|
switch (frame->hd.type) {
|
||||||
@@ -424,14 +416,15 @@ static int on_header_callback(nghttp2_session *session,
|
|||||||
if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
stream_data = nghttp2_session_get_stream_user_data(session,
|
stream_data =
|
||||||
frame->hd.stream_id);
|
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
|
||||||
if (!stream_data || stream_data->request_path) {
|
if (!stream_data || stream_data->request_path) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) {
|
if (namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) {
|
||||||
size_t j;
|
size_t j;
|
||||||
for(j = 0; j < valuelen && value[j] != '?'; ++j);
|
for (j = 0; j < valuelen && value[j] != '?'; ++j)
|
||||||
|
;
|
||||||
stream_data->request_path = percent_decode(value, j);
|
stream_data->request_path = percent_decode(value, j);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -441,8 +434,7 @@ static int on_header_callback(nghttp2_session *session,
|
|||||||
|
|
||||||
static int on_begin_headers_callback(nghttp2_session *session,
|
static int on_begin_headers_callback(nghttp2_session *session,
|
||||||
const nghttp2_frame *frame,
|
const nghttp2_frame *frame,
|
||||||
void *user_data)
|
void *user_data) {
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
http2_stream_data *stream_data;
|
http2_stream_data *stream_data;
|
||||||
|
|
||||||
@@ -458,24 +450,18 @@ static int on_begin_headers_callback(nghttp2_session *session,
|
|||||||
|
|
||||||
/* Minimum check for directory traversal. Returns nonzero if it is
|
/* Minimum check for directory traversal. Returns nonzero if it is
|
||||||
safe. */
|
safe. */
|
||||||
static int check_path(const char *path)
|
static int check_path(const char *path) {
|
||||||
{
|
|
||||||
/* We don't like '\' in url. */
|
/* We don't like '\' in url. */
|
||||||
return path[0] && path[0] == '/' &&
|
return path[0] && path[0] == '/' && strchr(path, '\\') == NULL &&
|
||||||
strchr(path, '\\') == NULL &&
|
strstr(path, "/../") == NULL && strstr(path, "/./") == NULL &&
|
||||||
strstr(path, "/../") == NULL &&
|
|
||||||
strstr(path, "/./") == NULL &&
|
|
||||||
!ends_with(path, "/..") && !ends_with(path, "/.");
|
!ends_with(path, "/..") && !ends_with(path, "/.");
|
||||||
}
|
}
|
||||||
|
|
||||||
static int on_request_recv(nghttp2_session *session,
|
static int on_request_recv(nghttp2_session *session,
|
||||||
http2_session_data *session_data,
|
http2_session_data *session_data,
|
||||||
http2_stream_data *stream_data)
|
http2_stream_data *stream_data) {
|
||||||
{
|
|
||||||
int fd;
|
int fd;
|
||||||
nghttp2_nv hdrs[] = {
|
nghttp2_nv hdrs[] = {MAKE_NV(":status", "200")};
|
||||||
MAKE_NV(":status", "200")
|
|
||||||
};
|
|
||||||
char *rel_path;
|
char *rel_path;
|
||||||
|
|
||||||
if (!stream_data->request_path) {
|
if (!stream_data->request_path) {
|
||||||
@@ -492,7 +478,8 @@ static int on_request_recv(nghttp2_session *session,
|
|||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
for(rel_path = stream_data->request_path; *rel_path == '/'; ++rel_path);
|
for (rel_path = stream_data->request_path; *rel_path == '/'; ++rel_path)
|
||||||
|
;
|
||||||
fd = open(rel_path, O_RDONLY);
|
fd = open(rel_path, O_RDONLY);
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
if (error_reply(session, stream_data) != 0) {
|
if (error_reply(session, stream_data) != 0) {
|
||||||
@@ -502,8 +489,8 @@ static int on_request_recv(nghttp2_session *session,
|
|||||||
}
|
}
|
||||||
stream_data->fd = fd;
|
stream_data->fd = fd;
|
||||||
|
|
||||||
if(send_response(session, stream_data->stream_id, hdrs,
|
if (send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs), fd) !=
|
||||||
ARRLEN(hdrs), fd) != 0) {
|
0) {
|
||||||
close(fd);
|
close(fd);
|
||||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
}
|
}
|
||||||
@@ -511,8 +498,7 @@ static int on_request_recv(nghttp2_session *session,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int on_frame_recv_callback(nghttp2_session *session,
|
static int on_frame_recv_callback(nghttp2_session *session,
|
||||||
const nghttp2_frame *frame, void *user_data)
|
const nghttp2_frame *frame, void *user_data) {
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
http2_stream_data *stream_data;
|
http2_stream_data *stream_data;
|
||||||
switch (frame->hd.type) {
|
switch (frame->hd.type) {
|
||||||
@@ -520,8 +506,8 @@ static int on_frame_recv_callback(nghttp2_session *session,
|
|||||||
case NGHTTP2_HEADERS:
|
case NGHTTP2_HEADERS:
|
||||||
/* Check that the client request has finished */
|
/* Check that the client request has finished */
|
||||||
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||||
stream_data = nghttp2_session_get_stream_user_data(session,
|
stream_data =
|
||||||
frame->hd.stream_id);
|
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
|
||||||
/* For DATA and HEADERS frame, this callback may be called after
|
/* For DATA and HEADERS frame, this callback may be called after
|
||||||
on_stream_close_callback. Check that stream still alive. */
|
on_stream_close_callback. Check that stream still alive. */
|
||||||
if (!stream_data) {
|
if (!stream_data) {
|
||||||
@@ -536,11 +522,8 @@ static int on_frame_recv_callback(nghttp2_session *session,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int on_stream_close_callback(nghttp2_session *session,
|
static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||||
int32_t stream_id,
|
uint32_t error_code _U_, void *user_data) {
|
||||||
nghttp2_error_code error_code,
|
|
||||||
void *user_data)
|
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
http2_stream_data *stream_data;
|
http2_stream_data *stream_data;
|
||||||
|
|
||||||
@@ -553,42 +536,48 @@ static int on_stream_close_callback(nghttp2_session *session,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void initialize_nghttp2_session(http2_session_data *session_data)
|
static void initialize_nghttp2_session(http2_session_data *session_data) {
|
||||||
{
|
nghttp2_option *option;
|
||||||
nghttp2_session_callbacks *callbacks;
|
nghttp2_session_callbacks *callbacks;
|
||||||
|
|
||||||
|
nghttp2_option_new(&option);
|
||||||
|
|
||||||
|
/* Tells nghttp2_session object that it handles client connection
|
||||||
|
preface */
|
||||||
|
nghttp2_option_set_recv_client_preface(option, 1);
|
||||||
|
|
||||||
nghttp2_session_callbacks_new(&callbacks);
|
nghttp2_session_callbacks_new(&callbacks);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_frame_recv_callback
|
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
|
||||||
(callbacks, on_frame_recv_callback);
|
on_frame_recv_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_stream_close_callback
|
nghttp2_session_callbacks_set_on_stream_close_callback(
|
||||||
(callbacks, on_stream_close_callback);
|
callbacks, on_stream_close_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_header_callback
|
nghttp2_session_callbacks_set_on_header_callback(callbacks,
|
||||||
(callbacks, on_header_callback);
|
on_header_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_begin_headers_callback
|
nghttp2_session_callbacks_set_on_begin_headers_callback(
|
||||||
(callbacks, on_begin_headers_callback);
|
callbacks, on_begin_headers_callback);
|
||||||
|
|
||||||
nghttp2_session_server_new(&session_data->session, callbacks, session_data);
|
nghttp2_session_server_new2(&session_data->session, callbacks, session_data,
|
||||||
|
option);
|
||||||
|
|
||||||
nghttp2_session_callbacks_del(callbacks);
|
nghttp2_session_callbacks_del(callbacks);
|
||||||
|
nghttp2_option_del(option);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Send HTTP/2 client connection header, which includes 24 bytes
|
/* Send HTTP/2 client connection header, which includes 24 bytes
|
||||||
magic octets and SETTINGS frame */
|
magic octets and SETTINGS frame */
|
||||||
static int send_server_connection_header(http2_session_data *session_data)
|
static int send_server_connection_header(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
nghttp2_settings_entry iv[1] = {
|
nghttp2_settings_entry iv[1] = {
|
||||||
{ NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 }
|
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
|
||||||
};
|
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE,
|
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
|
||||||
iv, ARRLEN(iv));
|
ARRLEN(iv));
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||||
return -1;
|
return -1;
|
||||||
@@ -598,8 +587,7 @@ static int send_server_connection_header(http2_session_data *session_data)
|
|||||||
|
|
||||||
/* readcb for bufferevent after client connection header was
|
/* readcb for bufferevent after client connection header was
|
||||||
checked. */
|
checked. */
|
||||||
static void readcb(struct bufferevent *bev, void *ptr)
|
static void readcb(struct bufferevent *bev _U_, void *ptr) {
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||||
if (session_recv(session_data) != 0) {
|
if (session_recv(session_data) != 0) {
|
||||||
delete_http2_session_data(session_data);
|
delete_http2_session_data(session_data);
|
||||||
@@ -615,8 +603,7 @@ static void readcb(struct bufferevent *bev, void *ptr)
|
|||||||
process pending data in the output buffer. This is necessary
|
process pending data in the output buffer. This is necessary
|
||||||
because we have a threshold on the buffer size to avoid too much
|
because we have a threshold on the buffer size to avoid too much
|
||||||
buffering. See send_callback(). */
|
buffering. See send_callback(). */
|
||||||
static void writecb(struct bufferevent *bev, void *ptr)
|
static void writecb(struct bufferevent *bev, void *ptr) {
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||||
if (evbuffer_get_length(bufferevent_get_output(bev)) > 0) {
|
if (evbuffer_get_length(bufferevent_get_output(bev)) > 0) {
|
||||||
return;
|
return;
|
||||||
@@ -633,11 +620,18 @@ static void writecb(struct bufferevent *bev, void *ptr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* eventcb for bufferevent */
|
/* eventcb for bufferevent */
|
||||||
static void eventcb(struct bufferevent *bev, short events, void *ptr)
|
static void eventcb(struct bufferevent *bev _U_, short events, void *ptr) {
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||||
if (events & BEV_EVENT_CONNECTED) {
|
if (events & BEV_EVENT_CONNECTED) {
|
||||||
fprintf(stderr, "%s connected\n", session_data->client_addr);
|
fprintf(stderr, "%s connected\n", session_data->client_addr);
|
||||||
|
|
||||||
|
initialize_nghttp2_session(session_data);
|
||||||
|
|
||||||
|
if (send_server_connection_header(session_data) != 0) {
|
||||||
|
delete_http2_session_data(session_data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (events & BEV_EVENT_EOF) {
|
if (events & BEV_EVENT_EOF) {
|
||||||
@@ -650,53 +644,19 @@ static void eventcb(struct bufferevent *bev, short events, void *ptr)
|
|||||||
delete_http2_session_data(session_data);
|
delete_http2_session_data(session_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* readcb for bufferevent to check first 24 bytes client connection
|
|
||||||
header. */
|
|
||||||
static void handshake_readcb(struct bufferevent *bev, void *ptr)
|
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
|
||||||
uint8_t data[24];
|
|
||||||
struct evbuffer *input = bufferevent_get_input(session_data->bev);
|
|
||||||
int readlen = evbuffer_remove(input, data, session_data->handshake_leftlen);
|
|
||||||
const char *conhead = NGHTTP2_CLIENT_CONNECTION_PREFACE;
|
|
||||||
|
|
||||||
if(memcmp(conhead + NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN
|
|
||||||
- session_data->handshake_leftlen, data, readlen) != 0) {
|
|
||||||
delete_http2_session_data(session_data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
session_data->handshake_leftlen -= readlen;
|
|
||||||
if(session_data->handshake_leftlen == 0) {
|
|
||||||
bufferevent_setcb(session_data->bev, readcb, writecb, eventcb, ptr);
|
|
||||||
/* Process pending data in buffer since they are not notified
|
|
||||||
further */
|
|
||||||
initialize_nghttp2_session(session_data);
|
|
||||||
if(send_server_connection_header(session_data) != 0) {
|
|
||||||
delete_http2_session_data(session_data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(session_recv(session_data) != 0) {
|
|
||||||
delete_http2_session_data(session_data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* callback for evconnlistener */
|
/* callback for evconnlistener */
|
||||||
static void acceptcb(struct evconnlistener *listener, int fd,
|
static void acceptcb(struct evconnlistener *listener _U_, int fd,
|
||||||
struct sockaddr *addr, int addrlen, void *arg)
|
struct sockaddr *addr, int addrlen, void *arg) {
|
||||||
{
|
|
||||||
app_context *app_ctx = (app_context *)arg;
|
app_context *app_ctx = (app_context *)arg;
|
||||||
http2_session_data *session_data;
|
http2_session_data *session_data;
|
||||||
|
|
||||||
session_data = create_http2_session_data(app_ctx, fd, addr, addrlen);
|
session_data = create_http2_session_data(app_ctx, fd, addr, addrlen);
|
||||||
bufferevent_setcb(session_data->bev, handshake_readcb, NULL, eventcb,
|
|
||||||
session_data);
|
bufferevent_setcb(session_data->bev, readcb, writecb, eventcb, session_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void start_listen(struct event_base *evbase, const char *service,
|
static void start_listen(struct event_base *evbase, const char *service,
|
||||||
app_context *app_ctx)
|
app_context *app_ctx) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
struct addrinfo hints;
|
struct addrinfo hints;
|
||||||
struct addrinfo *res, *rp;
|
struct addrinfo *res, *rp;
|
||||||
@@ -715,10 +675,9 @@ static void start_listen(struct event_base *evbase, const char *service,
|
|||||||
}
|
}
|
||||||
for (rp = res; rp; rp = rp->ai_next) {
|
for (rp = res; rp; rp = rp->ai_next) {
|
||||||
struct evconnlistener *listener;
|
struct evconnlistener *listener;
|
||||||
listener = evconnlistener_new_bind(evbase, acceptcb, app_ctx,
|
listener = evconnlistener_new_bind(
|
||||||
LEV_OPT_CLOSE_ON_FREE |
|
evbase, acceptcb, app_ctx, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
|
||||||
LEV_OPT_REUSEABLE, 16,
|
16, rp->ai_addr, rp->ai_addrlen);
|
||||||
rp->ai_addr, rp->ai_addrlen);
|
|
||||||
if (listener) {
|
if (listener) {
|
||||||
freeaddrinfo(res);
|
freeaddrinfo(res);
|
||||||
|
|
||||||
@@ -729,16 +688,14 @@ static void start_listen(struct event_base *evbase, const char *service,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void initialize_app_context(app_context *app_ctx, SSL_CTX *ssl_ctx,
|
static void initialize_app_context(app_context *app_ctx, SSL_CTX *ssl_ctx,
|
||||||
struct event_base *evbase)
|
struct event_base *evbase) {
|
||||||
{
|
|
||||||
memset(app_ctx, 0, sizeof(app_context));
|
memset(app_ctx, 0, sizeof(app_context));
|
||||||
app_ctx->ssl_ctx = ssl_ctx;
|
app_ctx->ssl_ctx = ssl_ctx;
|
||||||
app_ctx->evbase = evbase;
|
app_ctx->evbase = evbase;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void run(const char *service,
|
static void run(const char *service, const char *key_file,
|
||||||
const char *key_file, const char *cert_file)
|
const char *cert_file) {
|
||||||
{
|
|
||||||
SSL_CTX *ssl_ctx;
|
SSL_CTX *ssl_ctx;
|
||||||
app_context app_ctx;
|
app_context app_ctx;
|
||||||
struct event_base *evbase;
|
struct event_base *evbase;
|
||||||
@@ -754,8 +711,7 @@ static void run(const char *service,
|
|||||||
SSL_CTX_free(ssl_ctx);
|
SSL_CTX_free(ssl_ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv) {
|
||||||
{
|
|
||||||
struct sigaction act;
|
struct sigaction act;
|
||||||
|
|
||||||
if (argc < 4) {
|
if (argc < 4) {
|
||||||
|
|||||||
1301
examples/tiny-nghttpd.c
Normal file
1301
examples/tiny-nghttpd.c
Normal file
File diff suppressed because it is too large
Load Diff
30
gendowncasetbl.py
Executable file
30
gendowncasetbl.py
Executable file
@@ -0,0 +1,30 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def name(i):
|
||||||
|
if i < 0x20:
|
||||||
|
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 '][i]
|
||||||
|
elif i == 0x7f:
|
||||||
|
return 'DEL '
|
||||||
|
|
||||||
|
for i in range(256):
|
||||||
|
if chr(i) == ' ':
|
||||||
|
sys.stdout.write('{} /* SPC */, '.format(i))
|
||||||
|
elif chr(i) == '\t':
|
||||||
|
sys.stdout.write('{} /* HT */, '.format(i))
|
||||||
|
elif 'A' <= chr(i) and chr(i) <= 'Z':
|
||||||
|
sys.stdout.write('{} /* {} */, '.format(i - ord('A') + ord('a'), chr(i)))
|
||||||
|
elif (0x21 <= i and i < 0x7f):
|
||||||
|
sys.stdout.write('{} /* {} */, '.format(i, chr(i)))
|
||||||
|
elif 0x80 <= i:
|
||||||
|
sys.stdout.write('{} /* {} */, '.format(i, hex(i)))
|
||||||
|
elif 0 == i:
|
||||||
|
sys.stdout.write('{} /* NUL */, '.format(i))
|
||||||
|
else:
|
||||||
|
sys.stdout.write('{} /* {} */, '.format(i, name(i)))
|
||||||
|
if (i + 1)%4 == 0:
|
||||||
|
sys.stdout.write('\n')
|
||||||
98
genheaderfunc.py
Executable file
98
genheaderfunc.py
Executable file
@@ -0,0 +1,98 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
HEADERS = [
|
||||||
|
':authority',
|
||||||
|
':method',
|
||||||
|
':path',
|
||||||
|
':scheme',
|
||||||
|
':status',
|
||||||
|
':host', # for spdy
|
||||||
|
'expect',
|
||||||
|
'host',
|
||||||
|
'if-modified-since',
|
||||||
|
"te",
|
||||||
|
"cookie",
|
||||||
|
"http2-settings",
|
||||||
|
"server",
|
||||||
|
"via",
|
||||||
|
"x-forwarded-for",
|
||||||
|
"x-forwarded-proto",
|
||||||
|
"alt-svc",
|
||||||
|
"content-length",
|
||||||
|
"location",
|
||||||
|
"trailer",
|
||||||
|
# disallowed h1 headers
|
||||||
|
'connection',
|
||||||
|
'keep-alive',
|
||||||
|
'proxy-connection',
|
||||||
|
'transfer-encoding',
|
||||||
|
'upgrade'
|
||||||
|
]
|
||||||
|
|
||||||
|
def to_enum_hd(k):
|
||||||
|
res = 'HD_'
|
||||||
|
for c in k.upper():
|
||||||
|
if c == ':' or c == '-':
|
||||||
|
res += '_'
|
||||||
|
continue
|
||||||
|
res += c
|
||||||
|
return res
|
||||||
|
|
||||||
|
def build_header(headers):
|
||||||
|
res = {}
|
||||||
|
for k in headers:
|
||||||
|
size = len(k)
|
||||||
|
if size not in res:
|
||||||
|
res[size] = {}
|
||||||
|
ent = res[size]
|
||||||
|
c = k[-1]
|
||||||
|
if c not in ent:
|
||||||
|
ent[c] = []
|
||||||
|
ent[c].append(k)
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
def gen_enum():
|
||||||
|
print '''\
|
||||||
|
enum {'''
|
||||||
|
for k in sorted(HEADERS):
|
||||||
|
print '''\
|
||||||
|
{},'''.format(to_enum_hd(k))
|
||||||
|
print '''\
|
||||||
|
HD_MAXIDX,
|
||||||
|
};'''
|
||||||
|
|
||||||
|
def gen_index_header():
|
||||||
|
print '''\
|
||||||
|
int lookup_token(const uint8_t *name, size_t namelen) {
|
||||||
|
switch (namelen) {'''
|
||||||
|
b = build_header(HEADERS)
|
||||||
|
for size in sorted(b.keys()):
|
||||||
|
ents = b[size]
|
||||||
|
print '''\
|
||||||
|
case {}:'''.format(size)
|
||||||
|
print '''\
|
||||||
|
switch (name[namelen - 1]) {'''
|
||||||
|
for c in sorted(ents.keys()):
|
||||||
|
headers = sorted(ents[c])
|
||||||
|
print '''\
|
||||||
|
case '{}':'''.format(c)
|
||||||
|
for k in headers:
|
||||||
|
print '''\
|
||||||
|
if (util::streq("{}", name, {})) {{
|
||||||
|
return {};
|
||||||
|
}}'''.format(k[:-1], size - 1, to_enum_hd(k))
|
||||||
|
print '''\
|
||||||
|
break;'''
|
||||||
|
print '''\
|
||||||
|
}
|
||||||
|
break;'''
|
||||||
|
print '''\
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}'''
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
gen_enum()
|
||||||
|
print ''
|
||||||
|
gen_index_header()
|
||||||
0
gennmchartbl.py
Normal file → Executable file
0
gennmchartbl.py
Normal file → Executable file
0
genvchartbl.py
Normal file → Executable file
0
genvchartbl.py
Normal file → Executable file
484
git-clang-format
Executable file
484
git-clang-format
Executable file
@@ -0,0 +1,484 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
#
|
||||||
|
#===- git-clang-format - ClangFormat Git Integration ---------*- python -*--===#
|
||||||
|
#
|
||||||
|
# The LLVM Compiler Infrastructure
|
||||||
|
#
|
||||||
|
# This file is distributed under the University of Illinois Open Source
|
||||||
|
# License. See LICENSE.TXT for details.
|
||||||
|
#
|
||||||
|
#===------------------------------------------------------------------------===#
|
||||||
|
|
||||||
|
r"""
|
||||||
|
clang-format git integration
|
||||||
|
============================
|
||||||
|
|
||||||
|
This file provides a clang-format integration for git. Put it somewhere in your
|
||||||
|
path and ensure that it is executable. Then, "git clang-format" will invoke
|
||||||
|
clang-format on the changes in current files or a specific commit.
|
||||||
|
|
||||||
|
For further details, run:
|
||||||
|
git clang-format -h
|
||||||
|
|
||||||
|
Requires Python 2.7
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import collections
|
||||||
|
import contextlib
|
||||||
|
import errno
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
usage = 'git clang-format [OPTIONS] [<commit>] [--] [<file>...]'
|
||||||
|
|
||||||
|
desc = '''
|
||||||
|
Run clang-format on all lines that differ between the working directory
|
||||||
|
and <commit>, which defaults to HEAD. Changes are only applied to the working
|
||||||
|
directory.
|
||||||
|
|
||||||
|
The following git-config settings set the default of the corresponding option:
|
||||||
|
clangFormat.binary
|
||||||
|
clangFormat.commit
|
||||||
|
clangFormat.extension
|
||||||
|
clangFormat.style
|
||||||
|
'''
|
||||||
|
|
||||||
|
# Name of the temporary index file in which save the output of clang-format.
|
||||||
|
# This file is created within the .git directory.
|
||||||
|
temp_index_basename = 'clang-format-index'
|
||||||
|
|
||||||
|
|
||||||
|
Range = collections.namedtuple('Range', 'start, count')
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
config = load_git_config()
|
||||||
|
|
||||||
|
# In order to keep '--' yet allow options after positionals, we need to
|
||||||
|
# check for '--' ourselves. (Setting nargs='*' throws away the '--', while
|
||||||
|
# nargs=argparse.REMAINDER disallows options after positionals.)
|
||||||
|
argv = sys.argv[1:]
|
||||||
|
try:
|
||||||
|
idx = argv.index('--')
|
||||||
|
except ValueError:
|
||||||
|
dash_dash = []
|
||||||
|
else:
|
||||||
|
dash_dash = argv[idx:]
|
||||||
|
argv = argv[:idx]
|
||||||
|
|
||||||
|
default_extensions = ','.join([
|
||||||
|
# From clang/lib/Frontend/FrontendOptions.cpp, all lower case
|
||||||
|
'c', 'h', # C
|
||||||
|
'm', # ObjC
|
||||||
|
'mm', # ObjC++
|
||||||
|
'cc', 'cp', 'cpp', 'c++', 'cxx', 'hpp', # C++
|
||||||
|
# Other languages that clang-format supports
|
||||||
|
'proto', 'protodevel', # Protocol Buffers
|
||||||
|
'js', # JavaScript
|
||||||
|
])
|
||||||
|
|
||||||
|
p = argparse.ArgumentParser(
|
||||||
|
usage=usage, formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
|
description=desc)
|
||||||
|
p.add_argument('--binary',
|
||||||
|
default=config.get('clangformat.binary', 'clang-format'),
|
||||||
|
help='path to clang-format'),
|
||||||
|
p.add_argument('--commit',
|
||||||
|
default=config.get('clangformat.commit', 'HEAD'),
|
||||||
|
help='default commit to use if none is specified'),
|
||||||
|
p.add_argument('--diff', action='store_true',
|
||||||
|
help='print a diff instead of applying the changes')
|
||||||
|
p.add_argument('--extensions',
|
||||||
|
default=config.get('clangformat.extensions',
|
||||||
|
default_extensions),
|
||||||
|
help=('comma-separated list of file extensions to format, '
|
||||||
|
'excluding the period and case-insensitive')),
|
||||||
|
p.add_argument('-f', '--force', action='store_true',
|
||||||
|
help='allow changes to unstaged files')
|
||||||
|
p.add_argument('-p', '--patch', action='store_true',
|
||||||
|
help='select hunks interactively')
|
||||||
|
p.add_argument('-q', '--quiet', action='count', default=0,
|
||||||
|
help='print less information')
|
||||||
|
p.add_argument('--style',
|
||||||
|
default=config.get('clangformat.style', None),
|
||||||
|
help='passed to clang-format'),
|
||||||
|
p.add_argument('-v', '--verbose', action='count', default=0,
|
||||||
|
help='print extra information')
|
||||||
|
# We gather all the remaining positional arguments into 'args' since we need
|
||||||
|
# to use some heuristics to determine whether or not <commit> was present.
|
||||||
|
# However, to print pretty messages, we make use of metavar and help.
|
||||||
|
p.add_argument('args', nargs='*', metavar='<commit>',
|
||||||
|
help='revision from which to compute the diff')
|
||||||
|
p.add_argument('ignored', nargs='*', metavar='<file>...',
|
||||||
|
help='if specified, only consider differences in these files')
|
||||||
|
opts = p.parse_args(argv)
|
||||||
|
|
||||||
|
opts.verbose -= opts.quiet
|
||||||
|
del opts.quiet
|
||||||
|
|
||||||
|
commit, files = interpret_args(opts.args, dash_dash, opts.commit)
|
||||||
|
changed_lines = compute_diff_and_extract_lines(commit, files)
|
||||||
|
if opts.verbose >= 1:
|
||||||
|
ignored_files = set(changed_lines)
|
||||||
|
filter_by_extension(changed_lines, opts.extensions.lower().split(','))
|
||||||
|
if opts.verbose >= 1:
|
||||||
|
ignored_files.difference_update(changed_lines)
|
||||||
|
if ignored_files:
|
||||||
|
print 'Ignoring changes in the following files (wrong extension):'
|
||||||
|
for filename in ignored_files:
|
||||||
|
print ' ', filename
|
||||||
|
if changed_lines:
|
||||||
|
print 'Running clang-format on the following files:'
|
||||||
|
for filename in changed_lines:
|
||||||
|
print ' ', filename
|
||||||
|
if not changed_lines:
|
||||||
|
print 'no modified files to format'
|
||||||
|
return
|
||||||
|
# The computed diff outputs absolute paths, so we must cd before accessing
|
||||||
|
# those files.
|
||||||
|
cd_to_toplevel()
|
||||||
|
old_tree = create_tree_from_workdir(changed_lines)
|
||||||
|
new_tree = run_clang_format_and_save_to_tree(changed_lines,
|
||||||
|
binary=opts.binary,
|
||||||
|
style=opts.style)
|
||||||
|
if opts.verbose >= 1:
|
||||||
|
print 'old tree:', old_tree
|
||||||
|
print 'new tree:', new_tree
|
||||||
|
if old_tree == new_tree:
|
||||||
|
if opts.verbose >= 0:
|
||||||
|
print 'clang-format did not modify any files'
|
||||||
|
elif opts.diff:
|
||||||
|
print_diff(old_tree, new_tree)
|
||||||
|
else:
|
||||||
|
changed_files = apply_changes(old_tree, new_tree, force=opts.force,
|
||||||
|
patch_mode=opts.patch)
|
||||||
|
if (opts.verbose >= 0 and not opts.patch) or opts.verbose >= 1:
|
||||||
|
print 'changed files:'
|
||||||
|
for filename in changed_files:
|
||||||
|
print ' ', filename
|
||||||
|
|
||||||
|
|
||||||
|
def load_git_config(non_string_options=None):
|
||||||
|
"""Return the git configuration as a dictionary.
|
||||||
|
|
||||||
|
All options are assumed to be strings unless in `non_string_options`, in which
|
||||||
|
is a dictionary mapping option name (in lower case) to either "--bool" or
|
||||||
|
"--int"."""
|
||||||
|
if non_string_options is None:
|
||||||
|
non_string_options = {}
|
||||||
|
out = {}
|
||||||
|
for entry in run('git', 'config', '--list', '--null').split('\0'):
|
||||||
|
if entry:
|
||||||
|
name, value = entry.split('\n', 1)
|
||||||
|
if name in non_string_options:
|
||||||
|
value = run('git', 'config', non_string_options[name], name)
|
||||||
|
out[name] = value
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def interpret_args(args, dash_dash, default_commit):
|
||||||
|
"""Interpret `args` as "[commit] [--] [files...]" and return (commit, files).
|
||||||
|
|
||||||
|
It is assumed that "--" and everything that follows has been removed from
|
||||||
|
args and placed in `dash_dash`.
|
||||||
|
|
||||||
|
If "--" is present (i.e., `dash_dash` is non-empty), the argument to its
|
||||||
|
left (if present) is taken as commit. Otherwise, the first argument is
|
||||||
|
checked if it is a commit or a file. If commit is not given,
|
||||||
|
`default_commit` is used."""
|
||||||
|
if dash_dash:
|
||||||
|
if len(args) == 0:
|
||||||
|
commit = default_commit
|
||||||
|
elif len(args) > 1:
|
||||||
|
die('at most one commit allowed; %d given' % len(args))
|
||||||
|
else:
|
||||||
|
commit = args[0]
|
||||||
|
object_type = get_object_type(commit)
|
||||||
|
if object_type not in ('commit', 'tag'):
|
||||||
|
if object_type is None:
|
||||||
|
die("'%s' is not a commit" % commit)
|
||||||
|
else:
|
||||||
|
die("'%s' is a %s, but a commit was expected" % (commit, object_type))
|
||||||
|
files = dash_dash[1:]
|
||||||
|
elif args:
|
||||||
|
if disambiguate_revision(args[0]):
|
||||||
|
commit = args[0]
|
||||||
|
files = args[1:]
|
||||||
|
else:
|
||||||
|
commit = default_commit
|
||||||
|
files = args
|
||||||
|
else:
|
||||||
|
commit = default_commit
|
||||||
|
files = []
|
||||||
|
return commit, files
|
||||||
|
|
||||||
|
|
||||||
|
def disambiguate_revision(value):
|
||||||
|
"""Returns True if `value` is a revision, False if it is a file, or dies."""
|
||||||
|
# If `value` is ambiguous (neither a commit nor a file), the following
|
||||||
|
# command will die with an appropriate error message.
|
||||||
|
run('git', 'rev-parse', value, verbose=False)
|
||||||
|
object_type = get_object_type(value)
|
||||||
|
if object_type is None:
|
||||||
|
return False
|
||||||
|
if object_type in ('commit', 'tag'):
|
||||||
|
return True
|
||||||
|
die('`%s` is a %s, but a commit or filename was expected' %
|
||||||
|
(value, object_type))
|
||||||
|
|
||||||
|
|
||||||
|
def get_object_type(value):
|
||||||
|
"""Returns a string description of an object's type, or None if it is not
|
||||||
|
a valid git object."""
|
||||||
|
cmd = ['git', 'cat-file', '-t', value]
|
||||||
|
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
stdout, stderr = p.communicate()
|
||||||
|
if p.returncode != 0:
|
||||||
|
return None
|
||||||
|
return stdout.strip()
|
||||||
|
|
||||||
|
|
||||||
|
def compute_diff_and_extract_lines(commit, files):
|
||||||
|
"""Calls compute_diff() followed by extract_lines()."""
|
||||||
|
diff_process = compute_diff(commit, files)
|
||||||
|
changed_lines = extract_lines(diff_process.stdout)
|
||||||
|
diff_process.stdout.close()
|
||||||
|
diff_process.wait()
|
||||||
|
if diff_process.returncode != 0:
|
||||||
|
# Assume error was already printed to stderr.
|
||||||
|
sys.exit(2)
|
||||||
|
return changed_lines
|
||||||
|
|
||||||
|
|
||||||
|
def compute_diff(commit, files):
|
||||||
|
"""Return a subprocess object producing the diff from `commit`.
|
||||||
|
|
||||||
|
The return value's `stdin` file object will produce a patch with the
|
||||||
|
differences between the working directory and `commit`, filtered on `files`
|
||||||
|
(if non-empty). Zero context lines are used in the patch."""
|
||||||
|
cmd = ['git', 'diff-index', '-p', '-U0', commit, '--']
|
||||||
|
cmd.extend(files)
|
||||||
|
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||||
|
p.stdin.close()
|
||||||
|
return p
|
||||||
|
|
||||||
|
|
||||||
|
def extract_lines(patch_file):
|
||||||
|
"""Extract the changed lines in `patch_file`.
|
||||||
|
|
||||||
|
The return value is a dictionary mapping filename to a list of (start_line,
|
||||||
|
line_count) pairs.
|
||||||
|
|
||||||
|
The input must have been produced with ``-U0``, meaning unidiff format with
|
||||||
|
zero lines of context. The return value is a dict mapping filename to a
|
||||||
|
list of line `Range`s."""
|
||||||
|
matches = {}
|
||||||
|
for line in patch_file:
|
||||||
|
match = re.search(r'^\+\+\+\ [^/]+/(.*)', line)
|
||||||
|
if match:
|
||||||
|
filename = match.group(1).rstrip('\r\n')
|
||||||
|
match = re.search(r'^@@ -[0-9,]+ \+(\d+)(,(\d+))?', line)
|
||||||
|
if match:
|
||||||
|
start_line = int(match.group(1))
|
||||||
|
line_count = 1
|
||||||
|
if match.group(3):
|
||||||
|
line_count = int(match.group(3))
|
||||||
|
if line_count > 0:
|
||||||
|
matches.setdefault(filename, []).append(Range(start_line, line_count))
|
||||||
|
return matches
|
||||||
|
|
||||||
|
|
||||||
|
def filter_by_extension(dictionary, allowed_extensions):
|
||||||
|
"""Delete every key in `dictionary` that doesn't have an allowed extension.
|
||||||
|
|
||||||
|
`allowed_extensions` must be a collection of lowercase file extensions,
|
||||||
|
excluding the period."""
|
||||||
|
allowed_extensions = frozenset(allowed_extensions)
|
||||||
|
for filename in dictionary.keys():
|
||||||
|
base_ext = filename.rsplit('.', 1)
|
||||||
|
if len(base_ext) == 1 or base_ext[1].lower() not in allowed_extensions:
|
||||||
|
del dictionary[filename]
|
||||||
|
|
||||||
|
|
||||||
|
def cd_to_toplevel():
|
||||||
|
"""Change to the top level of the git repository."""
|
||||||
|
toplevel = run('git', 'rev-parse', '--show-toplevel')
|
||||||
|
os.chdir(toplevel)
|
||||||
|
|
||||||
|
|
||||||
|
def create_tree_from_workdir(filenames):
|
||||||
|
"""Create a new git tree with the given files from the working directory.
|
||||||
|
|
||||||
|
Returns the object ID (SHA-1) of the created tree."""
|
||||||
|
return create_tree(filenames, '--stdin')
|
||||||
|
|
||||||
|
|
||||||
|
def run_clang_format_and_save_to_tree(changed_lines, binary='clang-format',
|
||||||
|
style=None):
|
||||||
|
"""Run clang-format on each file and save the result to a git tree.
|
||||||
|
|
||||||
|
Returns the object ID (SHA-1) of the created tree."""
|
||||||
|
def index_info_generator():
|
||||||
|
for filename, line_ranges in changed_lines.iteritems():
|
||||||
|
mode = oct(os.stat(filename).st_mode)
|
||||||
|
blob_id = clang_format_to_blob(filename, line_ranges, binary=binary,
|
||||||
|
style=style)
|
||||||
|
yield '%s %s\t%s' % (mode, blob_id, filename)
|
||||||
|
return create_tree(index_info_generator(), '--index-info')
|
||||||
|
|
||||||
|
|
||||||
|
def create_tree(input_lines, mode):
|
||||||
|
"""Create a tree object from the given input.
|
||||||
|
|
||||||
|
If mode is '--stdin', it must be a list of filenames. If mode is
|
||||||
|
'--index-info' is must be a list of values suitable for "git update-index
|
||||||
|
--index-info", such as "<mode> <SP> <sha1> <TAB> <filename>". Any other mode
|
||||||
|
is invalid."""
|
||||||
|
assert mode in ('--stdin', '--index-info')
|
||||||
|
cmd = ['git', 'update-index', '--add', '-z', mode]
|
||||||
|
with temporary_index_file():
|
||||||
|
p = subprocess.Popen(cmd, stdin=subprocess.PIPE)
|
||||||
|
for line in input_lines:
|
||||||
|
p.stdin.write('%s\0' % line)
|
||||||
|
p.stdin.close()
|
||||||
|
if p.wait() != 0:
|
||||||
|
die('`%s` failed' % ' '.join(cmd))
|
||||||
|
tree_id = run('git', 'write-tree')
|
||||||
|
return tree_id
|
||||||
|
|
||||||
|
|
||||||
|
def clang_format_to_blob(filename, line_ranges, binary='clang-format',
|
||||||
|
style=None):
|
||||||
|
"""Run clang-format on the given file and save the result to a git blob.
|
||||||
|
|
||||||
|
Returns the object ID (SHA-1) of the created blob."""
|
||||||
|
clang_format_cmd = [binary, filename]
|
||||||
|
if style:
|
||||||
|
clang_format_cmd.extend(['-style='+style])
|
||||||
|
clang_format_cmd.extend([
|
||||||
|
'-lines=%s:%s' % (start_line, start_line+line_count-1)
|
||||||
|
for start_line, line_count in line_ranges])
|
||||||
|
try:
|
||||||
|
clang_format = subprocess.Popen(clang_format_cmd, stdin=subprocess.PIPE,
|
||||||
|
stdout=subprocess.PIPE)
|
||||||
|
except OSError as e:
|
||||||
|
if e.errno == errno.ENOENT:
|
||||||
|
die('cannot find executable "%s"' % binary)
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
clang_format.stdin.close()
|
||||||
|
hash_object_cmd = ['git', 'hash-object', '-w', '--path='+filename, '--stdin']
|
||||||
|
hash_object = subprocess.Popen(hash_object_cmd, stdin=clang_format.stdout,
|
||||||
|
stdout=subprocess.PIPE)
|
||||||
|
clang_format.stdout.close()
|
||||||
|
stdout = hash_object.communicate()[0]
|
||||||
|
if hash_object.returncode != 0:
|
||||||
|
die('`%s` failed' % ' '.join(hash_object_cmd))
|
||||||
|
if clang_format.wait() != 0:
|
||||||
|
die('`%s` failed' % ' '.join(clang_format_cmd))
|
||||||
|
return stdout.rstrip('\r\n')
|
||||||
|
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def temporary_index_file(tree=None):
|
||||||
|
"""Context manager for setting GIT_INDEX_FILE to a temporary file and deleting
|
||||||
|
the file afterward."""
|
||||||
|
index_path = create_temporary_index(tree)
|
||||||
|
old_index_path = os.environ.get('GIT_INDEX_FILE')
|
||||||
|
os.environ['GIT_INDEX_FILE'] = index_path
|
||||||
|
try:
|
||||||
|
yield
|
||||||
|
finally:
|
||||||
|
if old_index_path is None:
|
||||||
|
del os.environ['GIT_INDEX_FILE']
|
||||||
|
else:
|
||||||
|
os.environ['GIT_INDEX_FILE'] = old_index_path
|
||||||
|
os.remove(index_path)
|
||||||
|
|
||||||
|
|
||||||
|
def create_temporary_index(tree=None):
|
||||||
|
"""Create a temporary index file and return the created file's path.
|
||||||
|
|
||||||
|
If `tree` is not None, use that as the tree to read in. Otherwise, an
|
||||||
|
empty index is created."""
|
||||||
|
gitdir = run('git', 'rev-parse', '--git-dir')
|
||||||
|
path = os.path.join(gitdir, temp_index_basename)
|
||||||
|
if tree is None:
|
||||||
|
tree = '--empty'
|
||||||
|
run('git', 'read-tree', '--index-output='+path, tree)
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
def print_diff(old_tree, new_tree):
|
||||||
|
"""Print the diff between the two trees to stdout."""
|
||||||
|
# We use the porcelain 'diff' and not plumbing 'diff-tree' because the output
|
||||||
|
# is expected to be viewed by the user, and only the former does nice things
|
||||||
|
# like color and pagination.
|
||||||
|
subprocess.check_call(['git', 'diff', old_tree, new_tree, '--'])
|
||||||
|
|
||||||
|
|
||||||
|
def apply_changes(old_tree, new_tree, force=False, patch_mode=False):
|
||||||
|
"""Apply the changes in `new_tree` to the working directory.
|
||||||
|
|
||||||
|
Bails if there are local changes in those files and not `force`. If
|
||||||
|
`patch_mode`, runs `git checkout --patch` to select hunks interactively."""
|
||||||
|
changed_files = run('git', 'diff-tree', '-r', '-z', '--name-only', old_tree,
|
||||||
|
new_tree).rstrip('\0').split('\0')
|
||||||
|
if not force:
|
||||||
|
unstaged_files = run('git', 'diff-files', '--name-status', *changed_files)
|
||||||
|
if unstaged_files:
|
||||||
|
print >>sys.stderr, ('The following files would be modified but '
|
||||||
|
'have unstaged changes:')
|
||||||
|
print >>sys.stderr, unstaged_files
|
||||||
|
print >>sys.stderr, 'Please commit, stage, or stash them first.'
|
||||||
|
sys.exit(2)
|
||||||
|
if patch_mode:
|
||||||
|
# In patch mode, we could just as well create an index from the new tree
|
||||||
|
# and checkout from that, but then the user will be presented with a
|
||||||
|
# message saying "Discard ... from worktree". Instead, we use the old
|
||||||
|
# tree as the index and checkout from new_tree, which gives the slightly
|
||||||
|
# better message, "Apply ... to index and worktree". This is not quite
|
||||||
|
# right, since it won't be applied to the user's index, but oh well.
|
||||||
|
with temporary_index_file(old_tree):
|
||||||
|
subprocess.check_call(['git', 'checkout', '--patch', new_tree])
|
||||||
|
index_tree = old_tree
|
||||||
|
else:
|
||||||
|
with temporary_index_file(new_tree):
|
||||||
|
run('git', 'checkout-index', '-a', '-f')
|
||||||
|
return changed_files
|
||||||
|
|
||||||
|
|
||||||
|
def run(*args, **kwargs):
|
||||||
|
stdin = kwargs.pop('stdin', '')
|
||||||
|
verbose = kwargs.pop('verbose', True)
|
||||||
|
strip = kwargs.pop('strip', True)
|
||||||
|
for name in kwargs:
|
||||||
|
raise TypeError("run() got an unexpected keyword argument '%s'" % name)
|
||||||
|
p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
||||||
|
stdin=subprocess.PIPE)
|
||||||
|
stdout, stderr = p.communicate(input=stdin)
|
||||||
|
if p.returncode == 0:
|
||||||
|
if stderr:
|
||||||
|
if verbose:
|
||||||
|
print >>sys.stderr, '`%s` printed to stderr:' % ' '.join(args)
|
||||||
|
print >>sys.stderr, stderr.rstrip()
|
||||||
|
if strip:
|
||||||
|
stdout = stdout.rstrip('\r\n')
|
||||||
|
return stdout
|
||||||
|
if verbose:
|
||||||
|
print >>sys.stderr, '`%s` returned %s' % (' '.join(args), p.returncode)
|
||||||
|
if stderr:
|
||||||
|
print >>sys.stderr, stderr.rstrip()
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
|
|
||||||
|
def die(message):
|
||||||
|
print >>sys.stderr, 'error:', message
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
178
help2rst.py
Executable file
178
help2rst.py
Executable file
@@ -0,0 +1,178 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# script to produce rst file from program's help output.
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import sys
|
||||||
|
import re
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
arg_indent = ' ' * 14
|
||||||
|
|
||||||
|
def help2man(infile):
|
||||||
|
# We assume that first line is usage line like this:
|
||||||
|
#
|
||||||
|
# Usage: nghttp [OPTIONS]... URI...
|
||||||
|
#
|
||||||
|
# The second line is description of the command. Multiple lines
|
||||||
|
# are permitted. The blank line signals the end of this section.
|
||||||
|
# After that, we parses positional and optional arguments.
|
||||||
|
#
|
||||||
|
# The positional argument is enclosed with < and >:
|
||||||
|
#
|
||||||
|
# <PRIVATE_KEY>
|
||||||
|
#
|
||||||
|
# We may describe default behavior without any options by encoding
|
||||||
|
# ( and ):
|
||||||
|
#
|
||||||
|
# (default mode)
|
||||||
|
#
|
||||||
|
# "Options:" is treated specially and produces "OPTIONS" section.
|
||||||
|
# We allow subsection under OPTIONS. Lines not starting with (, <
|
||||||
|
# and Options: are treated as subsection name and produces section
|
||||||
|
# one level down:
|
||||||
|
#
|
||||||
|
# TLS/SSL:
|
||||||
|
#
|
||||||
|
# The above is an example of subsection.
|
||||||
|
#
|
||||||
|
# The description of arguments must be indented by len(arg_indent)
|
||||||
|
# characters. The default value should be placed in separate line
|
||||||
|
# and should be start with "Default: " after indentation.
|
||||||
|
|
||||||
|
line = infile.readline().strip()
|
||||||
|
m = re.match(r'^Usage: (.*)', line)
|
||||||
|
if not m:
|
||||||
|
print 'usage line is invalid. Expected following lines:'
|
||||||
|
print 'Usage: cmdname ...'
|
||||||
|
sys.exit(1)
|
||||||
|
synopsis = m.group(1).split(' ', 1)
|
||||||
|
if len(synopsis) == 2:
|
||||||
|
cmdname, args = synopsis
|
||||||
|
else:
|
||||||
|
cmdname, args = synopsis[0], ''
|
||||||
|
|
||||||
|
description = []
|
||||||
|
for line in infile:
|
||||||
|
line = line.strip()
|
||||||
|
if not line:
|
||||||
|
break
|
||||||
|
description.append(line)
|
||||||
|
|
||||||
|
print '''
|
||||||
|
{cmdname}(1)
|
||||||
|
{cmdnameunderline}
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
--------
|
||||||
|
|
||||||
|
**{cmdname}** {args}
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
-----------
|
||||||
|
|
||||||
|
{description}
|
||||||
|
'''.format(cmdname=cmdname, args=args,
|
||||||
|
cmdnameunderline='=' * (len(cmdname) + 3),
|
||||||
|
synopsis=synopsis, description=format_text('\n'.join(description)))
|
||||||
|
|
||||||
|
in_arg = False
|
||||||
|
|
||||||
|
for line in infile:
|
||||||
|
line = line.rstrip()
|
||||||
|
|
||||||
|
if not line.strip() and in_arg:
|
||||||
|
print ''
|
||||||
|
continue
|
||||||
|
if line.startswith(' ') and in_arg:
|
||||||
|
if not line.startswith(arg_indent):
|
||||||
|
sys.stderr.write('warning: argument description is not indented correctly. We need {} spaces as indentation.\n'.format(len(arg_indent)))
|
||||||
|
print '{}'.format(format_arg_text(line[len(arg_indent):]))
|
||||||
|
continue
|
||||||
|
|
||||||
|
if in_arg:
|
||||||
|
print ''
|
||||||
|
in_arg = False
|
||||||
|
|
||||||
|
if line == 'Options:':
|
||||||
|
print 'OPTIONS:'
|
||||||
|
print '--------'
|
||||||
|
print ''
|
||||||
|
continue
|
||||||
|
|
||||||
|
if line.startswith(' <'):
|
||||||
|
# positional argument
|
||||||
|
m = re.match(r'^(?:\s+)([a-zA-Z0-9-_<>]+)(.*)', line)
|
||||||
|
argname, rest = m.group(1), m.group(2)
|
||||||
|
print '.. describe:: {}'.format(argname)
|
||||||
|
print ''
|
||||||
|
print '{}'.format(format_arg_text(rest.strip()))
|
||||||
|
in_arg = True
|
||||||
|
continue
|
||||||
|
|
||||||
|
if line.startswith(' ('):
|
||||||
|
# positional argument
|
||||||
|
m = re.match(r'^(?:\s+)(\([a-zA-Z0-9-_<> ]+\))(.*)', line)
|
||||||
|
argname, rest = m.group(1), m.group(2)
|
||||||
|
print '.. describe:: {}'.format(argname)
|
||||||
|
print ''
|
||||||
|
print '{}'.format(format_arg_text(rest.strip()))
|
||||||
|
in_arg = True
|
||||||
|
continue
|
||||||
|
|
||||||
|
if line.startswith(' -'):
|
||||||
|
# optional argument
|
||||||
|
m = re.match(
|
||||||
|
r'^(?:\s+)(-\S+?(?:, -\S+?)*)($| .*)',
|
||||||
|
line)
|
||||||
|
argname, rest = m.group(1), m.group(2)
|
||||||
|
print '.. option:: {}'.format(argname)
|
||||||
|
print ''
|
||||||
|
rest = rest.strip()
|
||||||
|
if len(rest):
|
||||||
|
print '{}'.format(format_arg_text(rest))
|
||||||
|
in_arg = True
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not line.startswith(' ') and line.endswith(':'):
|
||||||
|
# subsection
|
||||||
|
subsec = line.strip()
|
||||||
|
print '{}'.format(subsec)
|
||||||
|
print '{}'.format('~' * len(subsec))
|
||||||
|
print ''
|
||||||
|
continue
|
||||||
|
|
||||||
|
print line.strip()
|
||||||
|
|
||||||
|
def format_text(text):
|
||||||
|
# escape *
|
||||||
|
if len(text) > len(arg_indent):
|
||||||
|
text = text[:len(arg_indent) + 1] + re.sub(r'\*', r'\*', text[len(arg_indent) + 1:])
|
||||||
|
else:
|
||||||
|
text = re.sub(r'\*', r'\*', text)
|
||||||
|
# markup option reference
|
||||||
|
text = re.sub(r'(^|\s)(-[a-zA-Z0-9-]+)', r'\1:option:`\2`', text)
|
||||||
|
# sphinx does not like markup like ':option:`-f`='. We need
|
||||||
|
# backslash between ` and =.
|
||||||
|
text = re.sub(r'(:option:`.*?`)(\S)', r'\1\\\2', text)
|
||||||
|
# file path should be italic
|
||||||
|
text = re.sub(r'(^|\s|\'|")(/[^\s\'"]*)', r'\1*\2*', text)
|
||||||
|
return text
|
||||||
|
|
||||||
|
def format_arg_text(text):
|
||||||
|
if text.strip().startswith('Default: '):
|
||||||
|
return '\n ' + re.sub(r'^(\s*Default: )(.*)$', r'\1``\2``', text)
|
||||||
|
return ' {}'.format(format_text(text))
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description='Produces rst document from help output.')
|
||||||
|
parser.add_argument('-i', '--include', metavar='FILE',
|
||||||
|
help='include content of <FILE> as verbatim. It should be ReST formatted text.')
|
||||||
|
args = parser.parse_args()
|
||||||
|
help2man(sys.stdin)
|
||||||
|
if args.include:
|
||||||
|
print ''
|
||||||
|
with open(args.include) as f:
|
||||||
|
sys.stdout.write(f.read())
|
||||||
43
integration-tests/Makefile.am
Normal file
43
integration-tests/Makefile.am
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
# nghttp2 - HTTP/2 C Library
|
||||||
|
|
||||||
|
# Copyright (c) 2015 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 = \
|
||||||
|
nghttpx_http1_test.go \
|
||||||
|
nghttpx_http2_test.go \
|
||||||
|
nghttpx_spdy_test.go \
|
||||||
|
server_tester.go \
|
||||||
|
server.key \
|
||||||
|
server.crt \
|
||||||
|
alt-server.key \
|
||||||
|
alt-server.crt \
|
||||||
|
setenv
|
||||||
|
|
||||||
|
.PHONY: itprep it
|
||||||
|
|
||||||
|
itprep:
|
||||||
|
go get -d -v github.com/bradfitz/http2
|
||||||
|
go get -d -v github.com/tatsuhiro-t/go-nghttp2
|
||||||
|
go get -d -v golang.org/x/net/spdy
|
||||||
|
|
||||||
|
it:
|
||||||
|
sh setenv go test -v
|
||||||
21
integration-tests/alt-server.crt
Normal file
21
integration-tests/alt-server.crt
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDhzCCAm+gAwIBAgIJANfuEldiquMNMA0GCSqGSIb3DQEBCwUAMFoxCzAJBgNV
|
||||||
|
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
|
||||||
|
aWRnaXRzIFB0eSBMdGQxEzARBgNVBAMMCmFsdC1kb21haW4wHhcNMTUwMTI1MDYy
|
||||||
|
NTQxWhcNMjUwMTIyMDYyNTQxWjBaMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29t
|
||||||
|
ZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRMwEQYD
|
||||||
|
VQQDDAphbHQtZG9tYWluMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
|
||||||
|
0IwhDOGDipGrJQ9IoRSzPdkU/Ii4aJgGKHlXminym42X0VI3IW61RLvOHRlHVmVH
|
||||||
|
JQjFuDo2x+y81t9NlDg3HGUbSpzOzpm6StiutB7c4hreT5G4r0YKya1ugiemN0+p
|
||||||
|
qjIPJWm2jVnf448eZvUKRKEQ9W0MLZjiNjVGKrKlwo7fIlXg4N3+YixLYffAT1NV
|
||||||
|
d1T6V5jzlbruj15gK2nGjMQ9D1h1t9vTbTxY+mtk72aX0Y64IE6pPBWLFSSH8ozU
|
||||||
|
idDoL3AZwz2Jker+ALKK8CM4uho/RPpyW1C06HH+HLdH2MqEjDOROde/Nzxm668O
|
||||||
|
gK/JWGIEyUqYiUXx0yhFxwIDAQABo1AwTjAdBgNVHQ4EFgQU/Y0GDN2uPjbyePcu
|
||||||
|
95ZvYEK/gHIwHwYDVR0jBBgwFoAU/Y0GDN2uPjbyePcu95ZvYEK/gHIwDAYDVR0T
|
||||||
|
BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAodD6LVCzL3wfsZ6TxTzf9TfgIdbj
|
||||||
|
ilL3SEMT/xnfTXT3SLYScTRqQIAI29Y7dOLMq89p4hY2wmeUEhBUAz+y9G2JVr8o
|
||||||
|
6EbxXrQpWgNJogELqoNnMdrDxB5RsmDDKEJ/rLjDfSkjWbK7B2PZsqVTDgjekCFw
|
||||||
|
u6FqTIjn/O1O/L5tjwxwxjHmQod/maFCvXoDOVBuwdHnkp298tqlvsHfHO8m++Wj
|
||||||
|
+XYB8plMIjpeTh9v4w9Jc4QZ59lK/3Tt4qaENeQrMEubKSY/Zen7L2bzhk+cChWT
|
||||||
|
GSGz9uNXieoZaH79D0wnyZaSZ5Ds4ActMevnGg3iYXuzuFqx8Pungn74Vg==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
28
integration-tests/alt-server.key
Normal file
28
integration-tests/alt-server.key
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDQjCEM4YOKkasl
|
||||||
|
D0ihFLM92RT8iLhomAYoeVeaKfKbjZfRUjchbrVEu84dGUdWZUclCMW4OjbH7LzW
|
||||||
|
302UODccZRtKnM7OmbpK2K60HtziGt5PkbivRgrJrW6CJ6Y3T6mqMg8labaNWd/j
|
||||||
|
jx5m9QpEoRD1bQwtmOI2NUYqsqXCjt8iVeDg3f5iLEth98BPU1V3VPpXmPOVuu6P
|
||||||
|
XmAracaMxD0PWHW329NtPFj6a2TvZpfRjrggTqk8FYsVJIfyjNSJ0OgvcBnDPYmR
|
||||||
|
6v4AsorwIzi6Gj9E+nJbULTocf4ct0fYyoSMM5E51783PGbrrw6Ar8lYYgTJSpiJ
|
||||||
|
RfHTKEXHAgMBAAECggEBALTrjFSXY72YB+h7rN+JjMIwDIPUvF6I3HbKZhQpJf6K
|
||||||
|
xNVkRM2tNHavku0tm/S4ohLf3F+pqRKiL2Udjjjy1+S7VgTRqpwTQ0lhV5aNW8SP
|
||||||
|
2KMg4R61XfB+k+s4KHu9kYxEJ12mqydPe+r3o0FgfYryTDsOYk1AX6b1aqzqFOGF
|
||||||
|
7GaqLALSbKU59tcJJ1SZNBbpIKFUrAT9nZt9dW02/foqP5bzUk43Yjw48xmLwegc
|
||||||
|
bMXXcpZhNZSktltvwRw7Q4Foc9kuRlMdTAnAD9PnMCcZwicS/YeVVF6Rz4fGviKv
|
||||||
|
7/kPHQ7g4YpFktVDzuZ5xw6GDVFeJ6uGMVUX8+EePvkCgYEA+/nrcn82nFHCxm8Q
|
||||||
|
0iiUhi/AoXjZg+O5Ytaje9O/YNoX+c4ywe13h0+TXKH79O0KfTwXeJyDgPZbAIFV
|
||||||
|
9oURellRYUzKDafnBHis2f+Ywn6GqHL5e2X30ZxIp1GK46pcvne1YuvJhgGmiVay
|
||||||
|
vd7sRx09OKU124dG22rIFCis6asCgYEA0+CsA6LrEwQ/aPJYASY3VHNO/WoAOnPg
|
||||||
|
Cwsg+02XWsPEwP//lNmpanz8TUm2URS063ZK8bx7t3ejvDgBdsRwwjiMlDp7XTUU
|
||||||
|
3Zk+mhCV2qkMi02aKemvz29bDhmh5JoH7W3IwsXtJYO0yZDYrDR3ioiKRccioPoE
|
||||||
|
b/Nq781sEFUCgYEA4xqx9xRpaCLY5nicNI6WrwrDF8YQZisNn+PMnYKP7v8itOgA
|
||||||
|
H4GkRbSXINpueKZc2dsbXH3UmJtyEdaAYBw3UIrIKmZHhl9afFE3mZQhXssjGxfl
|
||||||
|
fC6/WZD+eq+n+uJFjPXf6jSSAdHjA828dB1D4CSeVTuyexZF6uUnR+QRVNkCgYEA
|
||||||
|
i+pb7XLSpZYygY03zFp+Q0h6KyKqz+7hTqmkuA8/GfMZpRHop1UtaWLsAeXhfZ2c
|
||||||
|
87kEOKptUHSzLYIWhWWnyLorK1+LQ7vf8Y5XJso5C1KDNCKk4XSuYt94U9FddWa6
|
||||||
|
QXI0F1s5BYL6Cfma++0R2+va08Vy+rbf40XtojoXWJkCgYEA0hMQSCvok7is27nQ
|
||||||
|
G80KXfmghU2eEB7zif3T00/fwJycxEbmnNeof+SKmhdY4ZgqTscfOxlQPflV/eqB
|
||||||
|
xs4GnFDDeM0F8KH0BimOXxr7sJPFCg22PCCQQcRtM/KoU+ip/kNmTfwrsC0xMFPU
|
||||||
|
HD8M1JCZF2eLMekXXP3cB0U4sUs=
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
5
integration-tests/config.go.in
Normal file
5
integration-tests/config.go.in
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package nghttp2
|
||||||
|
|
||||||
|
const (
|
||||||
|
buildDir = "@top_builddir@"
|
||||||
|
)
|
||||||
211
integration-tests/nghttpx_http1_test.go
Normal file
211
integration-tests/nghttpx_http1_test.go
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
package nghttp2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"github.com/bradfitz/http2/hpack"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"syscall"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestH1H1PlainGET tests whether simple HTTP/1 GET request works.
|
||||||
|
func TestH1H1PlainGET(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http1(requestParam{
|
||||||
|
name: "TestH1H1PlainGET",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http1() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
want := 200
|
||||||
|
if got := res.status; got != want {
|
||||||
|
t.Errorf("status = %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH1H1PlainGETClose tests whether simple HTTP/1 GET request with
|
||||||
|
// Connetion: close request header field works.
|
||||||
|
func TestH1H1PlainGETClose(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http1(requestParam{
|
||||||
|
name: "TestH1H1PlainGETClose",
|
||||||
|
header: []hpack.HeaderField{
|
||||||
|
pair("Connection", "close"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http1() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
want := 200
|
||||||
|
if got := res.status; got != want {
|
||||||
|
t.Errorf("status = %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH1H1MultipleRequestCL tests that server rejects request which
|
||||||
|
// contains multiple Content-Length header fields.
|
||||||
|
func TestH1H1MultipleRequestCL(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
t.Errorf("server should not forward bad request")
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
if _, err := io.WriteString(st.conn, fmt.Sprintf(`GET / HTTP/1.1
|
||||||
|
Host: %v
|
||||||
|
Test-Case: TestH1H1MultipleRequestCL
|
||||||
|
Content-Length: 0
|
||||||
|
Content-Length: 0
|
||||||
|
|
||||||
|
`, st.authority)); err != nil {
|
||||||
|
t.Fatalf("Error io.WriteString() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error http.ReadResponse() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
want := 400
|
||||||
|
if got := resp.StatusCode; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH1H1ConnectFailure tests that server handles the situation that
|
||||||
|
// connection attempt to HTTP/1 backend failed.
|
||||||
|
func TestH1H1ConnectFailure(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
// shutdown backend server to simulate backend connect failure
|
||||||
|
st.ts.Close()
|
||||||
|
|
||||||
|
res, err := st.http1(requestParam{
|
||||||
|
name: "TestH1H1ConnectFailure",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http1() = %v", err)
|
||||||
|
}
|
||||||
|
want := 503
|
||||||
|
if got := res.status; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH1H1GracefulShutdown tests graceful shutdown.
|
||||||
|
func TestH1H1GracefulShutdown(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http1(requestParam{
|
||||||
|
name: "TestH1H1GracefulShutdown-1",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http1() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if got, want := res.status, 200; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
st.cmd.Process.Signal(syscall.SIGQUIT)
|
||||||
|
|
||||||
|
res, err = st.http1(requestParam{
|
||||||
|
name: "TestH1H1GracefulShutdown-2",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http1() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if got, want := res.status, 200; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
if got, want := res.connClose, true; got != want {
|
||||||
|
t.Errorf("res.connClose: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
want := io.EOF
|
||||||
|
if _, err := st.conn.Read(nil); err == nil || err != want {
|
||||||
|
t.Errorf("st.conn.Read(): %v; want %v", err, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH1H2ConnectFailure tests that server handles the situation that
|
||||||
|
// connection attempt to HTTP/2 backend failed.
|
||||||
|
func TestH1H2ConnectFailure(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--http2-bridge"}, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
// simulate backend connect attempt failure
|
||||||
|
st.ts.Close()
|
||||||
|
|
||||||
|
res, err := st.http1(requestParam{
|
||||||
|
name: "TestH1H2ConnectFailure",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http1() = %v", err)
|
||||||
|
}
|
||||||
|
want := 503
|
||||||
|
if got := res.status; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH1H2NoHost tests that server rejects request without Host
|
||||||
|
// header field for HTTP/2 backend.
|
||||||
|
func TestH1H2NoHost(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
t.Errorf("server should not forward bad request")
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
// without Host header field, we expect 400 response
|
||||||
|
if _, err := io.WriteString(st.conn, "GET / HTTP/1.1\r\nTest-Case: TestH1H2NoHost\r\n\r\n"); err != nil {
|
||||||
|
t.Fatalf("Error io.WriteString() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error http.ReadResponse() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
want := 400
|
||||||
|
if got := resp.StatusCode; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH1H2CrumbleCookie tests that Cookies are crumbled and assembled
|
||||||
|
// when forwarding to HTTP/2 backend link. go-nghttp2 server
|
||||||
|
// concatenates crumbled Cookies automatically, so this test is not
|
||||||
|
// much effective now.
|
||||||
|
func TestH1H2CrumbleCookie(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if got, want := r.Header.Get("Cookie"), "alpha; bravo; charlie"; got != want {
|
||||||
|
t.Errorf("Cookie: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http1(requestParam{
|
||||||
|
name: "TestH1H2CrumbleCookie",
|
||||||
|
header: []hpack.HeaderField{
|
||||||
|
pair("Cookie", "alpha; bravo; charlie"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http1() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.status, 200; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
515
integration-tests/nghttpx_http2_test.go
Normal file
515
integration-tests/nghttpx_http2_test.go
Normal file
@@ -0,0 +1,515 @@
|
|||||||
|
package nghttp2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
|
"github.com/bradfitz/http2"
|
||||||
|
"github.com/bradfitz/http2/hpack"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"syscall"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestH1H2PlainGET tests whether simple HTTP/2 GET request works.
|
||||||
|
func TestH2H1PlainGET(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1PlainGET",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
want := 200
|
||||||
|
if res.status != want {
|
||||||
|
t.Errorf("status = %v; want %v", res.status, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1AddXff tests that server generates X-Forwarded-For header
|
||||||
|
// field when forwarding request to backend.
|
||||||
|
func TestH2H1AddXff(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--add-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
xff := r.Header.Get("X-Forwarded-For")
|
||||||
|
want := "127.0.0.1"
|
||||||
|
if xff != want {
|
||||||
|
t.Errorf("X-Forwarded-For = %v; want %v", xff, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
_, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1AddXff",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1AddXff2 tests that server appends X-Forwarded-For header
|
||||||
|
// field to existing one when forwarding request to backend.
|
||||||
|
func TestH2H1AddXff2(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--add-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
xff := r.Header.Get("X-Forwarded-For")
|
||||||
|
want := "host, 127.0.0.1"
|
||||||
|
if xff != want {
|
||||||
|
t.Errorf("X-Forwarded-For = %v; want %v", xff, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
_, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1AddXff2",
|
||||||
|
header: []hpack.HeaderField{
|
||||||
|
pair("x-forwarded-for", "host"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1StripXff tests that --strip-incoming-x-forwarded-for
|
||||||
|
// option.
|
||||||
|
func TestH2H1StripXff(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--strip-incoming-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if xff, found := r.Header["X-Forwarded-For"]; found {
|
||||||
|
t.Errorf("X-Forwarded-For = %v; want nothing", xff)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
_, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1StripXff1",
|
||||||
|
header: []hpack.HeaderField{
|
||||||
|
pair("x-forwarded-for", "host"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1StripAddXff tests that --strip-incoming-x-forwarded-for and
|
||||||
|
// --add-x-forwarded-for options.
|
||||||
|
func TestH2H1StripAddXff(t *testing.T) {
|
||||||
|
args := []string{
|
||||||
|
"--strip-incoming-x-forwarded-for",
|
||||||
|
"--add-x-forwarded-for",
|
||||||
|
}
|
||||||
|
st := newServerTester(args, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
xff := r.Header.Get("X-Forwarded-For")
|
||||||
|
want := "127.0.0.1"
|
||||||
|
if xff != want {
|
||||||
|
t.Errorf("X-Forwarded-For = %v; want %v", xff, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
_, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1StripAddXff",
|
||||||
|
header: []hpack.HeaderField{
|
||||||
|
pair("x-forwarded-for", "host"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1BadRequestCL tests that server rejects request whose
|
||||||
|
// content-length header field value does not match its request body
|
||||||
|
// size.
|
||||||
|
func TestH2H1BadRequestCL(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
// we set content-length: 1024, but the actual request body is
|
||||||
|
// 3 bytes.
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1BadRequestCL",
|
||||||
|
method: "POST",
|
||||||
|
header: []hpack.HeaderField{
|
||||||
|
pair("content-length", "1024"),
|
||||||
|
},
|
||||||
|
body: []byte("foo"),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
want := http2.ErrCodeProtocol
|
||||||
|
if res.errCode != want {
|
||||||
|
t.Errorf("res.errCode = %v; want %v", res.errCode, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1BadResponseCL tests that server returns error when
|
||||||
|
// content-length response header field value does not match its
|
||||||
|
// response body size.
|
||||||
|
func TestH2H1BadResponseCL(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// we set content-length: 1024, but only send 3 bytes.
|
||||||
|
w.Header().Add("Content-Length", "1024")
|
||||||
|
w.Write([]byte("foo"))
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1BadResponseCL",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
want := http2.ErrCodeProtocol
|
||||||
|
if res.errCode != want {
|
||||||
|
t.Errorf("res.errCode = %v; want %v", res.errCode, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1LocationRewrite tests location header field rewriting
|
||||||
|
// works.
|
||||||
|
func TestH2H1LocationRewrite(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// TODO we cannot get st.ts's port number here.. 8443
|
||||||
|
// is just a place holder. We ignore it on rewrite.
|
||||||
|
w.Header().Add("Location", "http://127.0.0.1:8443/p/q?a=b#fragment")
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1LocationRewrite",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
want := fmt.Sprintf("http://127.0.0.1:%v/p/q?a=b#fragment", serverPort)
|
||||||
|
if got := res.header.Get("Location"); got != want {
|
||||||
|
t.Errorf("Location: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1ChunkedRequestBody tests that chunked request body works.
|
||||||
|
func TestH2H1ChunkedRequestBody(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
want := "[chunked]"
|
||||||
|
if got := fmt.Sprint(r.TransferEncoding); got != want {
|
||||||
|
t.Errorf("Transfer-Encoding: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error reading r.body: %v", err)
|
||||||
|
}
|
||||||
|
want = "foo"
|
||||||
|
if got := string(body); got != want {
|
||||||
|
t.Errorf("body: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
_, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1ChunkedRequestBody",
|
||||||
|
method: "POST",
|
||||||
|
body: []byte("foo"),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1MultipleRequestCL tests that server rejects request with
|
||||||
|
// multiple Content-Length request header fields.
|
||||||
|
func TestH2H1MultipleRequestCL(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
t.Errorf("server should not forward bad request")
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1MultipleRequestCL",
|
||||||
|
header: []hpack.HeaderField{
|
||||||
|
pair("content-length", "1"),
|
||||||
|
pair("content-length", "1"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
want := 400
|
||||||
|
if got := res.status; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1InvalidRequestCL tests that server rejects request with
|
||||||
|
// Content-Length which cannot be parsed as a number.
|
||||||
|
func TestH2H1InvalidRequestCL(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
t.Errorf("server should not forward bad request")
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1InvalidRequestCL",
|
||||||
|
header: []hpack.HeaderField{
|
||||||
|
pair("content-length", ""),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
want := 400
|
||||||
|
if got := res.status; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1ConnectFailure tests that server handles the situation that
|
||||||
|
// connection attempt to HTTP/1 backend failed.
|
||||||
|
func TestH2H1ConnectFailure(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
// shutdown backend server to simulate backend connect failure
|
||||||
|
st.ts.Close()
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1ConnectFailure",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
want := 503
|
||||||
|
if got := res.status; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1AssembleCookies tests that crumbled cookies in HTTP/2
|
||||||
|
// request is assembled into 1 when forwarding to HTTP/1 backend link.
|
||||||
|
func TestH2H1AssembleCookies(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if got, want := r.Header.Get("Cookie"), "alpha; bravo; charlie"; got != want {
|
||||||
|
t.Errorf("Cookie: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1AssembleCookies",
|
||||||
|
header: []hpack.HeaderField{
|
||||||
|
pair("cookie", "alpha"),
|
||||||
|
pair("cookie", "bravo"),
|
||||||
|
pair("cookie", "charlie"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.status, 200; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1TETrailers tests that server accepts TE request header
|
||||||
|
// field if it has trailers only.
|
||||||
|
func TestH2H1TETrailers(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1TETrailers",
|
||||||
|
header: []hpack.HeaderField{
|
||||||
|
pair("te", "trailers"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.status, 200; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1TEGzip tests that server resets stream if TE request header
|
||||||
|
// field contains gzip.
|
||||||
|
func TestH2H1TEGzip(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
t.Error("server should not forward bad request")
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1TEGzip",
|
||||||
|
header: []hpack.HeaderField{
|
||||||
|
pair("te", "gzip"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.errCode, http2.ErrCodeProtocol; got != want {
|
||||||
|
t.Errorf("res.errCode = %v; want %v", res.errCode, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestH2H1SNI(t *testing.T) {
|
||||||
|
st := newServerTesterTLSConfig([]string{"--subcert=" + testDir + "/alt-server.key:" + testDir + "/alt-server.crt"}, t, noopHandler, &tls.Config{
|
||||||
|
ServerName: "alt-domain",
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
tlsConn := st.conn.(*tls.Conn)
|
||||||
|
connState := tlsConn.ConnectionState()
|
||||||
|
cert := connState.PeerCertificates[0]
|
||||||
|
|
||||||
|
if got, want := cert.Subject.CommonName, "alt-domain"; got != want {
|
||||||
|
t.Errorf("CommonName: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1GracefulShutdown tests graceful shutdown.
|
||||||
|
func TestH2H1GracefulShutdown(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
fmt.Fprint(st.conn, "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")
|
||||||
|
if err := st.fr.WriteSettings(); err != nil {
|
||||||
|
t.Fatalf("st.fr.WriteSettings(): %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
header := []hpack.HeaderField{
|
||||||
|
pair(":method", "GET"),
|
||||||
|
pair(":scheme", "http"),
|
||||||
|
pair(":authority", st.authority),
|
||||||
|
pair(":path", "/"),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, h := range header {
|
||||||
|
_ = st.enc.WriteField(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := st.fr.WriteHeaders(http2.HeadersFrameParam{
|
||||||
|
StreamID: 1,
|
||||||
|
EndStream: false,
|
||||||
|
EndHeaders: true,
|
||||||
|
BlockFragment: st.headerBlkBuf.Bytes(),
|
||||||
|
}); err != nil {
|
||||||
|
t.Fatalf("st.fr.WriteHeaders(): %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// send SIGQUIT signal to nghttpx to perform graceful shutdown
|
||||||
|
st.cmd.Process.Signal(syscall.SIGQUIT)
|
||||||
|
|
||||||
|
// after signal, finish request body
|
||||||
|
if err := st.fr.WriteData(1, true, nil); err != nil {
|
||||||
|
t.Fatalf("st.fr.WriteData(): %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
numGoAway := 0
|
||||||
|
|
||||||
|
for {
|
||||||
|
fr, err := st.readFrame()
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
want := 2
|
||||||
|
if got := numGoAway; got != want {
|
||||||
|
t.Fatalf("numGoAway: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Fatalf("st.readFrame(): %v", err)
|
||||||
|
}
|
||||||
|
switch f := fr.(type) {
|
||||||
|
case *http2.GoAwayFrame:
|
||||||
|
numGoAway += 1
|
||||||
|
want := http2.ErrCodeNo
|
||||||
|
if got := f.ErrCode; got != want {
|
||||||
|
t.Fatalf("f.ErrCode(%v): %v; want %v", numGoAway, got, want)
|
||||||
|
}
|
||||||
|
switch numGoAway {
|
||||||
|
case 1:
|
||||||
|
want := (uint32(1) << 31) - 1
|
||||||
|
if got := f.LastStreamID; got != want {
|
||||||
|
t.Fatalf("f.LastStreamID(%v): %v; want %v", numGoAway, got, want)
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
want := uint32(1)
|
||||||
|
if got := f.LastStreamID; got != want {
|
||||||
|
t.Fatalf("f.LastStreamID(%v): %v; want %v", numGoAway, got, want)
|
||||||
|
}
|
||||||
|
case 3:
|
||||||
|
t.Fatalf("too many GOAWAYs received")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H2MultipleResponseCL tests that server returns error if
|
||||||
|
// multiple Content-Length response header fields are received.
|
||||||
|
func TestH2H2MultipleResponseCL(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Add("content-length", "1")
|
||||||
|
w.Header().Add("content-length", "1")
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H2MultipleResponseCL",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
want := 502
|
||||||
|
if got := res.status; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H2InvalidResponseCL tests that server returns error if
|
||||||
|
// Content-Length response header field value cannot be parsed as a
|
||||||
|
// number.
|
||||||
|
func TestH2H2InvalidResponseCL(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Add("content-length", "")
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H2InvalidResponseCL",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
want := 502
|
||||||
|
if got := res.status; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H2ConnectFailure tests that server handles the situation that
|
||||||
|
// connection attempt to HTTP/2 backend failed.
|
||||||
|
func TestH2H2ConnectFailure(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--http2-bridge"}, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
// simulate backend connect attempt failure
|
||||||
|
st.ts.Close()
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H2ConnectFailure",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
want := 503
|
||||||
|
if got := res.status; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
121
integration-tests/nghttpx_spdy_test.go
Normal file
121
integration-tests/nghttpx_spdy_test.go
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
package nghttp2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/bradfitz/http2/hpack"
|
||||||
|
"golang.org/x/net/spdy"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestS3H1PlainGET tests whether simple SPDY GET request works.
|
||||||
|
func TestS3H1PlainGET(t *testing.T) {
|
||||||
|
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.spdy(requestParam{
|
||||||
|
name: "TestS3H1PlainGET",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.spdy() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
want := 200
|
||||||
|
if got := res.status; got != want {
|
||||||
|
t.Errorf("status = %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestS3H1BadRequestCL tests that server rejects request whose
|
||||||
|
// content-length header field value does not match its request body
|
||||||
|
// size.
|
||||||
|
func TestS3H1BadRequestCL(t *testing.T) {
|
||||||
|
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
// we set content-length: 1024, but the actual request body is
|
||||||
|
// 3 bytes.
|
||||||
|
res, err := st.spdy(requestParam{
|
||||||
|
name: "TestS3H1BadRequestCL",
|
||||||
|
method: "POST",
|
||||||
|
header: []hpack.HeaderField{
|
||||||
|
pair("content-length", "1024"),
|
||||||
|
},
|
||||||
|
body: []byte("foo"),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.spdy() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
want := spdy.ProtocolError
|
||||||
|
if got := res.spdyRstErrCode; got != want {
|
||||||
|
t.Errorf("res.spdyRstErrCode = %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestS3H1MultipleRequestCL tests that server rejects request with
|
||||||
|
// multiple Content-Length request header fields.
|
||||||
|
func TestS3H1MultipleRequestCL(t *testing.T) {
|
||||||
|
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
t.Errorf("server should not forward bad request")
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.spdy(requestParam{
|
||||||
|
name: "TestS3H1MultipleRequestCL",
|
||||||
|
header: []hpack.HeaderField{
|
||||||
|
pair("content-length", "1"),
|
||||||
|
pair("content-length", "1"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.spdy() = %v", err)
|
||||||
|
}
|
||||||
|
want := 400
|
||||||
|
if got := res.status; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestS3H1InvalidRequestCL tests that server rejects request with
|
||||||
|
// Content-Length which cannot be parsed as a number.
|
||||||
|
func TestS3H1InvalidRequestCL(t *testing.T) {
|
||||||
|
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
t.Errorf("server should not forward bad request")
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.spdy(requestParam{
|
||||||
|
name: "TestS3H1InvalidRequestCL",
|
||||||
|
header: []hpack.HeaderField{
|
||||||
|
pair("content-length", ""),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.spdy() = %v", err)
|
||||||
|
}
|
||||||
|
want := 400
|
||||||
|
if got := res.status; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestS3H2ConnectFailure tests that server handles the situation that
|
||||||
|
// connection attempt to HTTP/2 backend failed.
|
||||||
|
func TestS3H2ConnectFailure(t *testing.T) {
|
||||||
|
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--http2-bridge"}, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
// simulate backend connect attempt failure
|
||||||
|
st.ts.Close()
|
||||||
|
|
||||||
|
res, err := st.spdy(requestParam{
|
||||||
|
name: "TestS3H2ConnectFailure",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.spdy() = %v", err)
|
||||||
|
}
|
||||||
|
want := 503
|
||||||
|
if got := res.status; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
21
integration-tests/server.crt
Normal file
21
integration-tests/server.crt
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDhTCCAm2gAwIBAgIJAOvIx8xIxgyOMA0GCSqGSIb3DQEBCwUAMFkxCzAJBgNV
|
||||||
|
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
|
||||||
|
aWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMMCTEyNy4wLjAuMTAeFw0xNTAxMjMxMjI0
|
||||||
|
MjdaFw0yNTAxMjAxMjI0MjdaMFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21l
|
||||||
|
LVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNV
|
||||||
|
BAMMCTEyNy4wLjAuMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMuI
|
||||||
|
QZRI/iBaxPTjTWGemt8tCEfzZWxuIW3hY/gIhwJDfH2SbourBh1s9vqcqhBq5vmo
|
||||||
|
kdfVQXAnNLjIG1uhWmcHuNnKrE5hU82N6i9RsmuM5TQRvhsamHri4G+EXJMu9GqF
|
||||||
|
Mso8g7MWpRSGKf+8gfjAVNwfCHFiu8oBcMmy3l54MFHgRLSveAMhiPB0e3Xlnpr5
|
||||||
|
2bS/oGTx5ynwPgBpEn2FrpT4Z/aLCLzJ/ysgNH8BXEh7n/v7xM3vd5grqB039rd5
|
||||||
|
JoxlWvp+4XpzKp5upaqmOcVUq4pDSFUQ3w6C+v33Z3OK6Qaon7GMxLv3Us3b7PZ3
|
||||||
|
1CLoWJR2o3OSnUfO/gUCAwEAAaNQME4wHQYDVR0OBBYEFLc5JWPUUVx4GJesogMV
|
||||||
|
w2Rz0L3yMB8GA1UdIwQYMBaAFLc5JWPUUVx4GJesogMVw2Rz0L3yMAwGA1UdEwQF
|
||||||
|
MAMBAf8wDQYJKoZIhvcNAQELBQADggEBAAP/cJWpM+GEjmVYHFacKTdbXBMox2Xn
|
||||||
|
QY2NLm00WPOGvKnO7czMFfX/pEmiq71kD45rLLfbaJP205QpxqiAIvhFhuq50Co7
|
||||||
|
sTDtwcDTPLX9H7Ugjt4sTMPiwC14uVXFfoT/J46zMjXwP00qKyfszc2tkIgHfrTl
|
||||||
|
h4M1hkdfmMximir/Ii7TdYYJ3oGS8tdcYb6D4DZwAljKmxF6iUOwFCUgpTmqDBT5
|
||||||
|
irXY8D27DzuNN5Pg07rwAlwXLCzrJE10UtO4MmRVXwpzmoaRQD4/tna6bZzdetvs
|
||||||
|
gPdGP6W1o0q85gullieMJWeKyQA/wasoE7fypn4pHAdTZm/vH+v7GHg=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
28
integration-tests/server.key
Normal file
28
integration-tests/server.key
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDLiEGUSP4gWsT0
|
||||||
|
401hnprfLQhH82VsbiFt4WP4CIcCQ3x9km6LqwYdbPb6nKoQaub5qJHX1UFwJzS4
|
||||||
|
yBtboVpnB7jZyqxOYVPNjeovUbJrjOU0Eb4bGph64uBvhFyTLvRqhTLKPIOzFqUU
|
||||||
|
hin/vIH4wFTcHwhxYrvKAXDJst5eeDBR4ES0r3gDIYjwdHt15Z6a+dm0v6Bk8ecp
|
||||||
|
8D4AaRJ9ha6U+Gf2iwi8yf8rIDR/AVxIe5/7+8TN73eYK6gdN/a3eSaMZVr6fuF6
|
||||||
|
cyqebqWqpjnFVKuKQ0hVEN8Ogvr992dziukGqJ+xjMS791LN2+z2d9Qi6FiUdqNz
|
||||||
|
kp1Hzv4FAgMBAAECggEACG26GYP0Ui6wHVwUZkiFLVzWDPS9bIIbDEvbMfhYbvWQ
|
||||||
|
gDrCLTKF7E4I5FP8jvV+XzRl5cRFE3nsKwLObzr9XWrqcsp73DsXl1mbKx58/ws0
|
||||||
|
qrVZZBHz4pLmrHeUxduZ75dYhRuAcLgtWe48awTJdR2x5fO7C8cE89afbxrjLpJE
|
||||||
|
tVyiw6vVB0GfWTZodxtAFMTX1KVm4bTngXfg0NF1FBNHAX3Cm6t4YCE41hKSc0IQ
|
||||||
|
Jr3C4e9uj8poze1B17k79bGB8HNMbbc8Ws0sdbxi5xnY+HUA/mYQrmGXo8sdqiYC
|
||||||
|
EYCMqPm3iJrCmmpHukGf2Vt9k1aLlJ+lxOclSwFO+QKBgQDoRmoprfdmU20LyxYH
|
||||||
|
eVeVqggqmhNohwnuhIvOAyrWGUkbDsssqx2Vv82z0WHAAkwEvQ984UzaYWCCL3m3
|
||||||
|
+JzpF2dz6aKhXIaYnXBlk3STMGUCDT5ysPvsin9z/unzkffh3vrbDBARGFYWG18x
|
||||||
|
eUyTDOVVeTZNHUJXGjRyiftCkwKBgQDgUkR6dHU4ciSt7Y0UkyAgtZ7POR41T05L
|
||||||
|
bcxbjJeqm6qlj+oP9WUk7JxeSEFUbrMiROABLPPqTwmGo4xrDRx/e7WrqN6QBKC+
|
||||||
|
Y8CfalrKRb0np60x7Mxx0kbmHp5cwv9QDKznKViOYSgKxFrOFZyMAEXQdZ3FvjXF
|
||||||
|
OQWrw86kBwKBgQDXuxa9MWO3uUJtkqkaNfw/+FVvY/0kt09lJdxHci+l/IQmyl2w
|
||||||
|
Vhm7TRK7sXvtfvSl7gblgMgFiC2/nGKbmR/7ag5e3R98aVhlhMywuvyp/GfEORLI
|
||||||
|
KVNChfwMezVFUUx+j8BEFHcTuZuzGqcWZ0fUyER0V4k0pDlKdv9BZqBkWwKBgCdP
|
||||||
|
o3qGQCilMDJex/OMGPxCd9M+4kFbZZAobMC6cbXPU+dxwgYL7i67XGfVZ8WBJNlj
|
||||||
|
kpICK7irIzM6JBh6krzwlBTCIkbA2N6kopQNUl3SPOTfKKXwJp/nxs77HKuK7K09
|
||||||
|
m2tjPoatFhRU9sjY1rdeMN3oTr7hp5CpfonsZaEvAoGAEPsZcDd4N9ap5bgaeDy9
|
||||||
|
NOfLsIyaxT5k6moRIiy83QPihvCuECP16+r6M5tiSfgt/PtCimdjhRiqXzIHNRhh
|
||||||
|
Nfsv13vUtZgt8cYXuTdI4a8feKI7Q4876ME8Qp3WM5/UNZWq6/sWCuZFqbXUhqM0
|
||||||
|
mwNEi5Zddzf8VsSL2gCraQg=
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
561
integration-tests/server_tester.go
Normal file
561
integration-tests/server_tester.go
Normal file
@@ -0,0 +1,561 @@
|
|||||||
|
package nghttp2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/bradfitz/http2"
|
||||||
|
"github.com/bradfitz/http2/hpack"
|
||||||
|
"github.com/tatsuhiro-t/go-nghttp2"
|
||||||
|
"golang.org/x/net/spdy"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"net/url"
|
||||||
|
"os/exec"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
serverBin = buildDir + "/src/nghttpx"
|
||||||
|
serverPort = 3009
|
||||||
|
testDir = buildDir + "/integration-tests"
|
||||||
|
)
|
||||||
|
|
||||||
|
func pair(name, value string) hpack.HeaderField {
|
||||||
|
return hpack.HeaderField{
|
||||||
|
Name: name,
|
||||||
|
Value: value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type serverTester struct {
|
||||||
|
args []string // command-line arguments
|
||||||
|
cmd *exec.Cmd // test frontend server process, which is test subject
|
||||||
|
url string // test frontend server URL
|
||||||
|
t *testing.T
|
||||||
|
ts *httptest.Server // backend server
|
||||||
|
conn net.Conn // connection to frontend server
|
||||||
|
h2PrefaceSent bool // HTTP/2 preface was sent in conn
|
||||||
|
nextStreamID uint32 // next stream ID
|
||||||
|
fr *http2.Framer // HTTP/2 framer
|
||||||
|
spdyFr *spdy.Framer // SPDY/3.1 framer
|
||||||
|
headerBlkBuf bytes.Buffer // buffer to store encoded header block
|
||||||
|
enc *hpack.Encoder // HTTP/2 HPACK encoder
|
||||||
|
header http.Header // received header fields
|
||||||
|
dec *hpack.Decoder // HTTP/2 HPACK decoder
|
||||||
|
authority string // server's host:port
|
||||||
|
frCh chan http2.Frame // used for incoming HTTP/2 frame
|
||||||
|
spdyFrCh chan spdy.Frame // used for incoming SPDY frame
|
||||||
|
errCh chan error
|
||||||
|
}
|
||||||
|
|
||||||
|
// newServerTester creates test context for plain TCP frontend
|
||||||
|
// connection.
|
||||||
|
func newServerTester(args []string, t *testing.T, handler http.HandlerFunc) *serverTester {
|
||||||
|
return newServerTesterInternal(args, t, handler, false, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// newServerTester creates test context for TLS frontend connection.
|
||||||
|
func newServerTesterTLS(args []string, t *testing.T, handler http.HandlerFunc) *serverTester {
|
||||||
|
return newServerTesterInternal(args, t, handler, true, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// newServerTester creates test context for TLS frontend connection
|
||||||
|
// with given clientConfig
|
||||||
|
func newServerTesterTLSConfig(args []string, t *testing.T, handler http.HandlerFunc, clientConfig *tls.Config) *serverTester {
|
||||||
|
return newServerTesterInternal(args, t, handler, true, clientConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// newServerTesterInternal creates test context. If frontendTLS is
|
||||||
|
// true, set up TLS frontend connection.
|
||||||
|
func newServerTesterInternal(args []string, t *testing.T, handler http.HandlerFunc, frontendTLS bool, clientConfig *tls.Config) *serverTester {
|
||||||
|
ts := httptest.NewUnstartedServer(handler)
|
||||||
|
|
||||||
|
backendTLS := false
|
||||||
|
for _, k := range args {
|
||||||
|
switch k {
|
||||||
|
case "--http2-bridge":
|
||||||
|
backendTLS = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if backendTLS {
|
||||||
|
nghttp2.ConfigureServer(ts.Config, &nghttp2.Server{})
|
||||||
|
// According to httptest/server.go, we have to set
|
||||||
|
// NextProtos separately for ts.TLS. NextProtos set
|
||||||
|
// in nghttp2.ConfigureServer is effectively ignored.
|
||||||
|
ts.TLS = new(tls.Config)
|
||||||
|
ts.TLS.NextProtos = append(ts.TLS.NextProtos, "h2-14")
|
||||||
|
ts.StartTLS()
|
||||||
|
args = append(args, "-k")
|
||||||
|
} else {
|
||||||
|
ts.Start()
|
||||||
|
}
|
||||||
|
scheme := "http"
|
||||||
|
if frontendTLS {
|
||||||
|
scheme = "https"
|
||||||
|
args = append(args, testDir+"/server.key", testDir+"/server.crt")
|
||||||
|
} else {
|
||||||
|
args = append(args, "--frontend-no-tls")
|
||||||
|
}
|
||||||
|
|
||||||
|
backendURL, err := url.Parse(ts.URL)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error parsing URL from httptest.Server: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// URL.Host looks like "127.0.0.1:8080", but we want
|
||||||
|
// "127.0.0.1,8080"
|
||||||
|
b := "-b" + strings.Replace(backendURL.Host, ":", ",", -1)
|
||||||
|
args = append(args, fmt.Sprintf("-f127.0.0.1,%v", serverPort), b,
|
||||||
|
"--errorlog-file="+testDir+"/log.txt", "-LINFO")
|
||||||
|
|
||||||
|
authority := fmt.Sprintf("127.0.0.1:%v", serverPort)
|
||||||
|
|
||||||
|
st := &serverTester{
|
||||||
|
cmd: exec.Command(serverBin, args...),
|
||||||
|
t: t,
|
||||||
|
ts: ts,
|
||||||
|
url: fmt.Sprintf("%v://%v", scheme, authority),
|
||||||
|
nextStreamID: 1,
|
||||||
|
authority: authority,
|
||||||
|
frCh: make(chan http2.Frame),
|
||||||
|
spdyFrCh: make(chan spdy.Frame),
|
||||||
|
errCh: make(chan error),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := st.cmd.Start(); err != nil {
|
||||||
|
st.t.Fatalf("Error starting %v: %v", serverBin, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
retry := 0
|
||||||
|
for {
|
||||||
|
var conn net.Conn
|
||||||
|
var err error
|
||||||
|
if frontendTLS {
|
||||||
|
var tlsConfig *tls.Config
|
||||||
|
if clientConfig == nil {
|
||||||
|
tlsConfig = new(tls.Config)
|
||||||
|
} else {
|
||||||
|
tlsConfig = clientConfig
|
||||||
|
}
|
||||||
|
tlsConfig.InsecureSkipVerify = true
|
||||||
|
tlsConfig.NextProtos = []string{"h2-14", "spdy/3.1"}
|
||||||
|
conn, err = tls.Dial("tcp", authority, tlsConfig)
|
||||||
|
} else {
|
||||||
|
conn, err = net.Dial("tcp", authority)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
retry += 1
|
||||||
|
if retry >= 100 {
|
||||||
|
st.Close()
|
||||||
|
st.t.Fatalf("Error server is not responding too long; server command-line arguments may be invalid")
|
||||||
|
}
|
||||||
|
time.Sleep(150 * time.Millisecond)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if frontendTLS {
|
||||||
|
tlsConn := conn.(*tls.Conn)
|
||||||
|
cs := tlsConn.ConnectionState()
|
||||||
|
if !cs.NegotiatedProtocolIsMutual {
|
||||||
|
st.Close()
|
||||||
|
st.t.Fatalf("Error negotiated next protocol is not mutual")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
st.conn = conn
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
st.fr = http2.NewFramer(st.conn, st.conn)
|
||||||
|
spdyFr, err := spdy.NewFramer(st.conn, st.conn)
|
||||||
|
if err != nil {
|
||||||
|
st.Close()
|
||||||
|
st.t.Fatalf("Error spdy.NewFramer: %v", err)
|
||||||
|
}
|
||||||
|
st.spdyFr = spdyFr
|
||||||
|
st.enc = hpack.NewEncoder(&st.headerBlkBuf)
|
||||||
|
st.dec = hpack.NewDecoder(4096, func(f hpack.HeaderField) {
|
||||||
|
st.header.Add(f.Name, f.Value)
|
||||||
|
})
|
||||||
|
|
||||||
|
return st
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *serverTester) Close() {
|
||||||
|
if st.conn != nil {
|
||||||
|
st.conn.Close()
|
||||||
|
}
|
||||||
|
if st.cmd != nil {
|
||||||
|
st.cmd.Process.Kill()
|
||||||
|
st.cmd.Wait()
|
||||||
|
}
|
||||||
|
if st.ts != nil {
|
||||||
|
st.ts.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *serverTester) readFrame() (http2.Frame, error) {
|
||||||
|
go func() {
|
||||||
|
f, err := st.fr.ReadFrame()
|
||||||
|
if err != nil {
|
||||||
|
st.errCh <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
st.frCh <- f
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case f := <-st.frCh:
|
||||||
|
return f, nil
|
||||||
|
case err := <-st.errCh:
|
||||||
|
return nil, err
|
||||||
|
case <-time.After(5 * time.Second):
|
||||||
|
return nil, errors.New("timeout waiting for frame")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *serverTester) readSpdyFrame() (spdy.Frame, error) {
|
||||||
|
go func() {
|
||||||
|
f, err := st.spdyFr.ReadFrame()
|
||||||
|
if err != nil {
|
||||||
|
st.errCh <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
st.spdyFrCh <- f
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case f := <-st.spdyFrCh:
|
||||||
|
return f, nil
|
||||||
|
case err := <-st.errCh:
|
||||||
|
return nil, err
|
||||||
|
case <-time.After(2 * time.Second):
|
||||||
|
return nil, errors.New("timeout waiting for frame")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type requestParam struct {
|
||||||
|
name string // name for this request to identify the request in log easily
|
||||||
|
streamID uint32 // stream ID, automatically assigned if 0
|
||||||
|
method string // method, defaults to GET
|
||||||
|
scheme string // scheme, defaults to http
|
||||||
|
authority string // authority, defaults to backend server address
|
||||||
|
path string // path, defaults to /
|
||||||
|
header []hpack.HeaderField // additional request header fields
|
||||||
|
body []byte // request body
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *serverTester) http1(rp requestParam) (*serverResponse, error) {
|
||||||
|
method := "GET"
|
||||||
|
if rp.method != "" {
|
||||||
|
method = rp.method
|
||||||
|
}
|
||||||
|
|
||||||
|
var body io.Reader
|
||||||
|
if rp.body != nil {
|
||||||
|
body = bytes.NewBuffer(rp.body)
|
||||||
|
}
|
||||||
|
req, err := http.NewRequest(method, st.url, body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, h := range rp.header {
|
||||||
|
req.Header.Add(h.Name, h.Value)
|
||||||
|
}
|
||||||
|
req.Header.Add("Test-Case", rp.name)
|
||||||
|
|
||||||
|
if err := req.Write(st.conn); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resp, err := http.ReadResponse(bufio.NewReader(st.conn), req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
respBody, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resp.Body.Close()
|
||||||
|
|
||||||
|
res := &serverResponse{
|
||||||
|
status: resp.StatusCode,
|
||||||
|
header: resp.Header,
|
||||||
|
body: respBody,
|
||||||
|
connClose: resp.Close,
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *serverTester) spdy(rp requestParam) (*serverResponse, error) {
|
||||||
|
res := &serverResponse{}
|
||||||
|
|
||||||
|
var id spdy.StreamId
|
||||||
|
if rp.streamID != 0 {
|
||||||
|
id = spdy.StreamId(rp.streamID)
|
||||||
|
if id >= spdy.StreamId(st.nextStreamID) && id%2 == 1 {
|
||||||
|
st.nextStreamID = uint32(id) + 2
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
id = spdy.StreamId(st.nextStreamID)
|
||||||
|
st.nextStreamID += 2
|
||||||
|
}
|
||||||
|
|
||||||
|
method := "GET"
|
||||||
|
if rp.method != "" {
|
||||||
|
method = rp.method
|
||||||
|
}
|
||||||
|
|
||||||
|
scheme := "http"
|
||||||
|
if rp.scheme != "" {
|
||||||
|
scheme = rp.scheme
|
||||||
|
}
|
||||||
|
|
||||||
|
host := st.authority
|
||||||
|
if rp.authority != "" {
|
||||||
|
host = rp.authority
|
||||||
|
}
|
||||||
|
|
||||||
|
path := "/"
|
||||||
|
if rp.path != "" {
|
||||||
|
path = rp.path
|
||||||
|
}
|
||||||
|
|
||||||
|
header := make(http.Header)
|
||||||
|
header.Add(":method", method)
|
||||||
|
header.Add(":scheme", scheme)
|
||||||
|
header.Add(":host", host)
|
||||||
|
header.Add(":path", path)
|
||||||
|
header.Add(":version", "HTTP/1.1")
|
||||||
|
header.Add("test-case", rp.name)
|
||||||
|
for _, h := range rp.header {
|
||||||
|
header.Add(h.Name, h.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
var synStreamFlags spdy.ControlFlags
|
||||||
|
if len(rp.body) == 0 {
|
||||||
|
synStreamFlags = spdy.ControlFlagFin
|
||||||
|
}
|
||||||
|
if err := st.spdyFr.WriteFrame(&spdy.SynStreamFrame{
|
||||||
|
CFHeader: spdy.ControlFrameHeader{
|
||||||
|
Flags: synStreamFlags,
|
||||||
|
},
|
||||||
|
StreamId: id,
|
||||||
|
Headers: header,
|
||||||
|
}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(rp.body) != 0 {
|
||||||
|
if err := st.spdyFr.WriteFrame(&spdy.DataFrame{
|
||||||
|
StreamId: id,
|
||||||
|
Flags: spdy.DataFlagFin,
|
||||||
|
Data: rp.body,
|
||||||
|
}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loop:
|
||||||
|
for {
|
||||||
|
fr, err := st.readSpdyFrame()
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
switch f := fr.(type) {
|
||||||
|
case *spdy.SynReplyFrame:
|
||||||
|
if f.StreamId != id {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
res.header = cloneHeader(f.Headers)
|
||||||
|
if _, err := fmt.Sscan(res.header.Get(":status"), &res.status); err != nil {
|
||||||
|
return res, fmt.Errorf("Error parsing status code: %v", err)
|
||||||
|
}
|
||||||
|
if f.CFHeader.Flags&spdy.ControlFlagFin != 0 {
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
case *spdy.DataFrame:
|
||||||
|
if f.StreamId != id {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
res.body = append(res.body, f.Data...)
|
||||||
|
if f.Flags&spdy.DataFlagFin != 0 {
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
case *spdy.RstStreamFrame:
|
||||||
|
if f.StreamId != id {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
res.spdyRstErrCode = f.Status
|
||||||
|
break loop
|
||||||
|
case *spdy.GoAwayFrame:
|
||||||
|
if f.Status == spdy.GoAwayOK {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
res.spdyGoAwayErrCode = f.Status
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *serverTester) http2(rp requestParam) (*serverResponse, error) {
|
||||||
|
res := &serverResponse{}
|
||||||
|
st.headerBlkBuf.Reset()
|
||||||
|
st.header = make(http.Header)
|
||||||
|
|
||||||
|
var id uint32
|
||||||
|
if rp.streamID != 0 {
|
||||||
|
id = rp.streamID
|
||||||
|
if id >= st.nextStreamID && id%2 == 1 {
|
||||||
|
st.nextStreamID = id + 2
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
id = st.nextStreamID
|
||||||
|
st.nextStreamID += 2
|
||||||
|
}
|
||||||
|
|
||||||
|
if !st.h2PrefaceSent {
|
||||||
|
st.h2PrefaceSent = true
|
||||||
|
fmt.Fprint(st.conn, "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")
|
||||||
|
if err := st.fr.WriteSettings(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
method := "GET"
|
||||||
|
if rp.method != "" {
|
||||||
|
method = rp.method
|
||||||
|
}
|
||||||
|
_ = st.enc.WriteField(pair(":method", method))
|
||||||
|
|
||||||
|
scheme := "http"
|
||||||
|
if rp.scheme != "" {
|
||||||
|
scheme = rp.scheme
|
||||||
|
}
|
||||||
|
_ = st.enc.WriteField(pair(":scheme", scheme))
|
||||||
|
|
||||||
|
authority := st.authority
|
||||||
|
if rp.authority != "" {
|
||||||
|
authority = rp.authority
|
||||||
|
}
|
||||||
|
_ = st.enc.WriteField(pair(":authority", authority))
|
||||||
|
|
||||||
|
path := "/"
|
||||||
|
if rp.path != "" {
|
||||||
|
path = rp.path
|
||||||
|
}
|
||||||
|
_ = st.enc.WriteField(pair(":path", path))
|
||||||
|
|
||||||
|
_ = st.enc.WriteField(pair("test-case", rp.name))
|
||||||
|
|
||||||
|
for _, h := range rp.header {
|
||||||
|
_ = st.enc.WriteField(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := st.fr.WriteHeaders(http2.HeadersFrameParam{
|
||||||
|
StreamID: id,
|
||||||
|
EndStream: len(rp.body) == 0,
|
||||||
|
EndHeaders: true,
|
||||||
|
BlockFragment: st.headerBlkBuf.Bytes(),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(rp.body) != 0 {
|
||||||
|
// TODO we assume rp.body fits in 1 frame
|
||||||
|
if err := st.fr.WriteData(id, true, rp.body); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loop:
|
||||||
|
for {
|
||||||
|
fr, err := st.readFrame()
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
switch f := fr.(type) {
|
||||||
|
case *http2.HeadersFrame:
|
||||||
|
_, err := st.dec.Write(f.HeaderBlockFragment())
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
if f.FrameHeader.StreamID != id {
|
||||||
|
st.header = make(http.Header)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
res.header = cloneHeader(st.header)
|
||||||
|
var status int
|
||||||
|
status, err = strconv.Atoi(res.header.Get(":status"))
|
||||||
|
if err != nil {
|
||||||
|
return res, fmt.Errorf("Error parsing status code: %v", err)
|
||||||
|
}
|
||||||
|
res.status = status
|
||||||
|
if f.StreamEnded() {
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
case *http2.DataFrame:
|
||||||
|
if f.FrameHeader.StreamID != id {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
res.body = append(res.body, f.Data()...)
|
||||||
|
if f.StreamEnded() {
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
case *http2.RSTStreamFrame:
|
||||||
|
if f.FrameHeader.StreamID != id {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
res.errCode = f.ErrCode
|
||||||
|
break loop
|
||||||
|
case *http2.GoAwayFrame:
|
||||||
|
if f.ErrCode == http2.ErrCodeNo {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
res.errCode = f.ErrCode
|
||||||
|
res.connErr = true
|
||||||
|
break loop
|
||||||
|
case *http2.SettingsFrame:
|
||||||
|
if f.IsAck() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err := st.fr.WriteSettingsAck(); err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
// TODO handle PUSH_PROMISE as well, since it alters HPACK context
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type serverResponse struct {
|
||||||
|
status int // HTTP status code
|
||||||
|
header http.Header // response header fields
|
||||||
|
body []byte // response body
|
||||||
|
errCode http2.ErrCode // error code received in HTTP/2 RST_STREAM or GOAWAY
|
||||||
|
connErr bool // true if HTTP/2 connection error
|
||||||
|
spdyGoAwayErrCode spdy.GoAwayStatus // status code received in SPDY RST_STREAM
|
||||||
|
spdyRstErrCode spdy.RstStreamStatus // status code received in SPDY GOAWAY
|
||||||
|
connClose bool // Conection: close is included in response header in HTTP/1 test
|
||||||
|
}
|
||||||
|
|
||||||
|
func cloneHeader(h http.Header) http.Header {
|
||||||
|
h2 := make(http.Header, len(h))
|
||||||
|
for k, vv := range h {
|
||||||
|
vv2 := make([]string, len(vv))
|
||||||
|
copy(vv2, vv)
|
||||||
|
h2[k] = vv2
|
||||||
|
}
|
||||||
|
return h2
|
||||||
|
}
|
||||||
|
|
||||||
|
func noopHandler(w http.ResponseWriter, r *http.Request) {}
|
||||||
6
integration-tests/setenv.in
Normal file
6
integration-tests/setenv.in
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/sh -e
|
||||||
|
|
||||||
|
export CGO_CFLAGS="-I@abs_top_srcdir@/lib/includes -I@abs_top_builddir@/lib/includes"
|
||||||
|
export CGO_LDFLAGS="-L@abs_top_builddir@/lib/.libs"
|
||||||
|
export LD_LIBRARY_PATH="@abs_top_builddir@/lib/.libs"
|
||||||
|
"$@"
|
||||||
@@ -22,7 +22,9 @@
|
|||||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
SUBDIRS = includes
|
SUBDIRS = includes
|
||||||
|
|
||||||
AM_CFLAGS = -Wall
|
EXTRA_DIST = Makefile.msvc
|
||||||
|
|
||||||
|
AM_CFLAGS = $(WARNCFLAGS)
|
||||||
AM_CPPFLAGS = -I$(srcdir)/includes -I$(builddir)/includes @DEFS@
|
AM_CPPFLAGS = -I$(srcdir)/includes -I$(builddir)/includes @DEFS@
|
||||||
|
|
||||||
pkgconfigdir = $(libdir)/pkgconfig
|
pkgconfigdir = $(libdir)/pkgconfig
|
||||||
@@ -42,7 +44,8 @@ OBJECTS = nghttp2_pq.c nghttp2_map.c nghttp2_queue.c \
|
|||||||
nghttp2_version.c \
|
nghttp2_version.c \
|
||||||
nghttp2_priority_spec.c \
|
nghttp2_priority_spec.c \
|
||||||
nghttp2_option.c \
|
nghttp2_option.c \
|
||||||
nghttp2_callbacks.c
|
nghttp2_callbacks.c \
|
||||||
|
nghttp2_mem.c
|
||||||
|
|
||||||
HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \
|
HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \
|
||||||
nghttp2_frame.h \
|
nghttp2_frame.h \
|
||||||
@@ -54,7 +57,8 @@ HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \
|
|||||||
nghttp2_hd.h nghttp2_hd_huffman.h \
|
nghttp2_hd.h nghttp2_hd_huffman.h \
|
||||||
nghttp2_priority_spec.h \
|
nghttp2_priority_spec.h \
|
||||||
nghttp2_option.h \
|
nghttp2_option.h \
|
||||||
nghttp2_callbacks.h
|
nghttp2_callbacks.h \
|
||||||
|
nghttp2_mem.h
|
||||||
|
|
||||||
libnghttp2_la_SOURCES = $(HFILES) $(OBJECTS)
|
libnghttp2_la_SOURCES = $(HFILES) $(OBJECTS)
|
||||||
libnghttp2_la_LDFLAGS = -no-undefined \
|
libnghttp2_la_LDFLAGS = -no-undefined \
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -28,8 +28,7 @@
|
|||||||
|
|
||||||
#include "nghttp2_helper.h"
|
#include "nghttp2_helper.h"
|
||||||
|
|
||||||
void nghttp2_buf_init(nghttp2_buf *buf)
|
void nghttp2_buf_init(nghttp2_buf *buf) {
|
||||||
{
|
|
||||||
buf->begin = NULL;
|
buf->begin = NULL;
|
||||||
buf->end = NULL;
|
buf->end = NULL;
|
||||||
buf->pos = NULL;
|
buf->pos = NULL;
|
||||||
@@ -37,24 +36,21 @@ void nghttp2_buf_init(nghttp2_buf *buf)
|
|||||||
buf->mark = NULL;
|
buf->mark = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_buf_init2(nghttp2_buf *buf, size_t initial)
|
int nghttp2_buf_init2(nghttp2_buf *buf, size_t initial, nghttp2_mem *mem) {
|
||||||
{
|
|
||||||
nghttp2_buf_init(buf);
|
nghttp2_buf_init(buf);
|
||||||
return nghttp2_buf_reserve(buf, initial);
|
return nghttp2_buf_reserve(buf, initial, mem);
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_buf_free(nghttp2_buf *buf)
|
void nghttp2_buf_free(nghttp2_buf *buf, nghttp2_mem *mem) {
|
||||||
{
|
|
||||||
if (buf == NULL) {
|
if (buf == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
free(buf->begin);
|
nghttp2_mem_free(mem, buf->begin);
|
||||||
buf->begin = NULL;
|
buf->begin = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_buf_reserve(nghttp2_buf *buf, size_t new_cap)
|
int nghttp2_buf_reserve(nghttp2_buf *buf, size_t new_cap, nghttp2_mem *mem) {
|
||||||
{
|
|
||||||
uint8_t *ptr;
|
uint8_t *ptr;
|
||||||
size_t cap;
|
size_t cap;
|
||||||
|
|
||||||
@@ -66,7 +62,7 @@ int nghttp2_buf_reserve(nghttp2_buf *buf, size_t new_cap)
|
|||||||
|
|
||||||
new_cap = nghttp2_max(new_cap, cap * 2);
|
new_cap = nghttp2_max(new_cap, cap * 2);
|
||||||
|
|
||||||
ptr = realloc(buf->begin, new_cap);
|
ptr = nghttp2_mem_realloc(mem, buf->begin, new_cap);
|
||||||
if (ptr == NULL) {
|
if (ptr == NULL) {
|
||||||
return NGHTTP2_ERR_NOMEM;
|
return NGHTTP2_ERR_NOMEM;
|
||||||
}
|
}
|
||||||
@@ -80,68 +76,54 @@ int nghttp2_buf_reserve(nghttp2_buf *buf, size_t new_cap)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_buf_pos_reserve(nghttp2_buf *buf, size_t new_rel_cap)
|
void nghttp2_buf_reset(nghttp2_buf *buf) {
|
||||||
{
|
|
||||||
return nghttp2_buf_reserve(buf, nghttp2_buf_pos_offset(buf) + new_rel_cap);
|
|
||||||
}
|
|
||||||
|
|
||||||
int nghttp2_buf_last_reserve(nghttp2_buf *buf, size_t new_rel_cap)
|
|
||||||
{
|
|
||||||
return nghttp2_buf_reserve(buf, nghttp2_buf_last_offset(buf) + new_rel_cap);
|
|
||||||
}
|
|
||||||
|
|
||||||
void nghttp2_buf_reset(nghttp2_buf *buf)
|
|
||||||
{
|
|
||||||
buf->pos = buf->last = buf->mark = buf->begin;
|
buf->pos = buf->last = buf->mark = buf->begin;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_buf_wrap_init(nghttp2_buf *buf, uint8_t *begin, size_t len)
|
void nghttp2_buf_wrap_init(nghttp2_buf *buf, uint8_t *begin, size_t len) {
|
||||||
{
|
|
||||||
buf->begin = buf->pos = buf->last = buf->mark = begin;
|
buf->begin = buf->pos = buf->last = buf->mark = begin;
|
||||||
buf->end = begin + len;
|
buf->end = begin + len;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int buf_chain_new(nghttp2_buf_chain **chain, size_t chunk_length)
|
static int buf_chain_new(nghttp2_buf_chain **chain, size_t chunk_length,
|
||||||
{
|
nghttp2_mem *mem) {
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
*chain = malloc(sizeof(nghttp2_buf_chain));
|
*chain = nghttp2_mem_malloc(mem, sizeof(nghttp2_buf_chain));
|
||||||
if (*chain == NULL) {
|
if (*chain == NULL) {
|
||||||
return NGHTTP2_ERR_NOMEM;
|
return NGHTTP2_ERR_NOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
(*chain)->next = NULL;
|
(*chain)->next = NULL;
|
||||||
|
|
||||||
rv = nghttp2_buf_init2(&(*chain)->buf, chunk_length);
|
rv = nghttp2_buf_init2(&(*chain)->buf, chunk_length, mem);
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
free(*chain);
|
nghttp2_mem_free(mem, *chain);
|
||||||
return NGHTTP2_ERR_NOMEM;
|
return NGHTTP2_ERR_NOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void buf_chain_del(nghttp2_buf_chain *chain)
|
static void buf_chain_del(nghttp2_buf_chain *chain, nghttp2_mem *mem) {
|
||||||
{
|
nghttp2_buf_free(&chain->buf, mem);
|
||||||
nghttp2_buf_free(&chain->buf);
|
nghttp2_mem_free(mem, chain);
|
||||||
free(chain);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_bufs_init(nghttp2_bufs *bufs, size_t chunk_length,
|
int nghttp2_bufs_init(nghttp2_bufs *bufs, size_t chunk_length, size_t max_chunk,
|
||||||
size_t max_chunk)
|
nghttp2_mem *mem) {
|
||||||
{
|
return nghttp2_bufs_init2(bufs, chunk_length, max_chunk, 0, mem);
|
||||||
return nghttp2_bufs_init2(bufs, chunk_length, max_chunk, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_bufs_init2(nghttp2_bufs *bufs, size_t chunk_length,
|
int nghttp2_bufs_init2(nghttp2_bufs *bufs, size_t chunk_length,
|
||||||
size_t max_chunk, size_t offset)
|
size_t max_chunk, size_t offset, nghttp2_mem *mem) {
|
||||||
{
|
return nghttp2_bufs_init3(bufs, chunk_length, max_chunk, max_chunk, offset,
|
||||||
return nghttp2_bufs_init3(bufs, chunk_length, max_chunk, max_chunk, offset);
|
mem);
|
||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_bufs_init3(nghttp2_bufs *bufs, size_t chunk_length,
|
int nghttp2_bufs_init3(nghttp2_bufs *bufs, size_t chunk_length,
|
||||||
size_t max_chunk, size_t chunk_keep, size_t offset)
|
size_t max_chunk, size_t chunk_keep, size_t offset,
|
||||||
{
|
nghttp2_mem *mem) {
|
||||||
int rv;
|
int rv;
|
||||||
nghttp2_buf_chain *chain;
|
nghttp2_buf_chain *chain;
|
||||||
|
|
||||||
@@ -149,11 +131,12 @@ int nghttp2_bufs_init3(nghttp2_bufs *bufs, size_t chunk_length,
|
|||||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
rv = buf_chain_new(&chain, chunk_length);
|
rv = buf_chain_new(&chain, chunk_length, mem);
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bufs->mem = mem;
|
||||||
bufs->offset = offset;
|
bufs->offset = offset;
|
||||||
|
|
||||||
bufs->head = chain;
|
bufs->head = chain;
|
||||||
@@ -169,8 +152,7 @@ int nghttp2_bufs_init3(nghttp2_bufs *bufs, size_t chunk_length,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_bufs_realloc(nghttp2_bufs *bufs, size_t chunk_length)
|
int nghttp2_bufs_realloc(nghttp2_bufs *bufs, size_t chunk_length) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
nghttp2_buf_chain *chain;
|
nghttp2_buf_chain *chain;
|
||||||
|
|
||||||
@@ -178,7 +160,7 @@ int nghttp2_bufs_realloc(nghttp2_bufs *bufs, size_t chunk_length)
|
|||||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
rv = buf_chain_new(&chain, chunk_length);
|
rv = buf_chain_new(&chain, chunk_length, bufs->mem);
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
@@ -196,8 +178,7 @@ int nghttp2_bufs_realloc(nghttp2_bufs *bufs, size_t chunk_length)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_bufs_free(nghttp2_bufs *bufs)
|
void nghttp2_bufs_free(nghttp2_bufs *bufs) {
|
||||||
{
|
|
||||||
nghttp2_buf_chain *chain, *next_chain;
|
nghttp2_buf_chain *chain, *next_chain;
|
||||||
|
|
||||||
if (bufs == NULL) {
|
if (bufs == NULL) {
|
||||||
@@ -207,7 +188,7 @@ void nghttp2_bufs_free(nghttp2_bufs *bufs)
|
|||||||
for (chain = bufs->head; chain;) {
|
for (chain = bufs->head; chain;) {
|
||||||
next_chain = chain->next;
|
next_chain = chain->next;
|
||||||
|
|
||||||
buf_chain_del(chain);
|
buf_chain_del(chain, bufs->mem);
|
||||||
|
|
||||||
chain = next_chain;
|
chain = next_chain;
|
||||||
}
|
}
|
||||||
@@ -215,11 +196,11 @@ void nghttp2_bufs_free(nghttp2_bufs *bufs)
|
|||||||
bufs->head = NULL;
|
bufs->head = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_bufs_wrap_init(nghttp2_bufs *bufs, uint8_t *begin, size_t len)
|
int nghttp2_bufs_wrap_init(nghttp2_bufs *bufs, uint8_t *begin, size_t len,
|
||||||
{
|
nghttp2_mem *mem) {
|
||||||
nghttp2_buf_chain *chain;
|
nghttp2_buf_chain *chain;
|
||||||
|
|
||||||
chain = malloc(sizeof(nghttp2_buf_chain));
|
chain = nghttp2_mem_malloc(mem, sizeof(nghttp2_buf_chain));
|
||||||
if (chain == NULL) {
|
if (chain == NULL) {
|
||||||
return NGHTTP2_ERR_NOMEM;
|
return NGHTTP2_ERR_NOMEM;
|
||||||
}
|
}
|
||||||
@@ -228,6 +209,7 @@ int nghttp2_bufs_wrap_init(nghttp2_bufs *bufs, uint8_t *begin, size_t len)
|
|||||||
|
|
||||||
nghttp2_buf_wrap_init(&chain->buf, begin, len);
|
nghttp2_buf_wrap_init(&chain->buf, begin, len);
|
||||||
|
|
||||||
|
bufs->mem = mem;
|
||||||
bufs->offset = 0;
|
bufs->offset = 0;
|
||||||
|
|
||||||
bufs->head = chain;
|
bufs->head = chain;
|
||||||
@@ -241,18 +223,16 @@ int nghttp2_bufs_wrap_init(nghttp2_bufs *bufs, uint8_t *begin, size_t len)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_bufs_wrap_free(nghttp2_bufs *bufs)
|
void nghttp2_bufs_wrap_free(nghttp2_bufs *bufs) {
|
||||||
{
|
|
||||||
if (bufs == NULL) {
|
if (bufs == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
free(bufs->head);
|
nghttp2_mem_free(bufs->mem, bufs->head);
|
||||||
bufs->head = NULL;
|
bufs->head = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_bufs_seek_last_present(nghttp2_bufs *bufs)
|
void nghttp2_bufs_seek_last_present(nghttp2_bufs *bufs) {
|
||||||
{
|
|
||||||
nghttp2_buf_chain *ci;
|
nghttp2_buf_chain *ci;
|
||||||
|
|
||||||
for (ci = bufs->cur; ci; ci = ci->next) {
|
for (ci = bufs->cur; ci; ci = ci->next) {
|
||||||
@@ -264,8 +244,7 @@ void nghttp2_bufs_seek_last_present(nghttp2_bufs *bufs)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t nghttp2_bufs_len(nghttp2_bufs *bufs)
|
ssize_t nghttp2_bufs_len(nghttp2_bufs *bufs) {
|
||||||
{
|
|
||||||
nghttp2_buf_chain *ci;
|
nghttp2_buf_chain *ci;
|
||||||
ssize_t len;
|
ssize_t len;
|
||||||
|
|
||||||
@@ -277,14 +256,13 @@ ssize_t nghttp2_bufs_len(nghttp2_bufs *bufs)
|
|||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t bufs_avail(nghttp2_bufs *bufs)
|
static ssize_t bufs_avail(nghttp2_bufs *bufs) {
|
||||||
{
|
|
||||||
return (ssize_t)(nghttp2_buf_avail(&bufs->cur->buf) +
|
return (ssize_t)(nghttp2_buf_avail(&bufs->cur->buf) +
|
||||||
(bufs->chunk_length - bufs->offset) * (bufs->max_chunk - bufs->chunk_used));
|
(bufs->chunk_length - bufs->offset) *
|
||||||
|
(bufs->max_chunk - bufs->chunk_used));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bufs_alloc_chain(nghttp2_bufs *bufs)
|
static int bufs_alloc_chain(nghttp2_bufs *bufs) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
nghttp2_buf_chain *chain;
|
nghttp2_buf_chain *chain;
|
||||||
|
|
||||||
@@ -298,7 +276,7 @@ static int bufs_alloc_chain(nghttp2_bufs *bufs)
|
|||||||
return NGHTTP2_ERR_BUFFER_ERROR;
|
return NGHTTP2_ERR_BUFFER_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
rv = buf_chain_new(&chain, bufs->chunk_length);
|
rv = buf_chain_new(&chain, bufs->chunk_length, bufs->mem);
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
@@ -317,8 +295,7 @@ static int bufs_alloc_chain(nghttp2_bufs *bufs)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_bufs_add(nghttp2_bufs *bufs, const void *data, size_t len)
|
int nghttp2_bufs_add(nghttp2_bufs *bufs, const void *data, size_t len) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
size_t nwrite;
|
size_t nwrite;
|
||||||
nghttp2_buf *buf;
|
nghttp2_buf *buf;
|
||||||
@@ -350,8 +327,7 @@ int nghttp2_bufs_add(nghttp2_bufs *bufs, const void *data, size_t len)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bufs_ensure_addb(nghttp2_bufs *bufs)
|
static int bufs_ensure_addb(nghttp2_bufs *bufs) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
nghttp2_buf *buf;
|
nghttp2_buf *buf;
|
||||||
|
|
||||||
@@ -369,8 +345,7 @@ static int bufs_ensure_addb(nghttp2_bufs *bufs)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_bufs_addb(nghttp2_bufs *bufs, uint8_t b)
|
int nghttp2_bufs_addb(nghttp2_bufs *bufs, uint8_t b) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
rv = bufs_ensure_addb(bufs);
|
rv = bufs_ensure_addb(bufs);
|
||||||
@@ -383,8 +358,7 @@ int nghttp2_bufs_addb(nghttp2_bufs *bufs, uint8_t b)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_bufs_addb_hold(nghttp2_bufs *bufs, uint8_t b)
|
int nghttp2_bufs_addb_hold(nghttp2_bufs *bufs, uint8_t b) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
rv = bufs_ensure_addb(bufs);
|
rv = bufs_ensure_addb(bufs);
|
||||||
@@ -397,8 +371,7 @@ int nghttp2_bufs_addb_hold(nghttp2_bufs *bufs, uint8_t b)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_bufs_orb(nghttp2_bufs *bufs, uint8_t b)
|
int nghttp2_bufs_orb(nghttp2_bufs *bufs, uint8_t b) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
rv = bufs_ensure_addb(bufs);
|
rv = bufs_ensure_addb(bufs);
|
||||||
@@ -411,8 +384,7 @@ int nghttp2_bufs_orb(nghttp2_bufs *bufs, uint8_t b)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b)
|
int nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
rv = bufs_ensure_addb(bufs);
|
rv = bufs_ensure_addb(bufs);
|
||||||
@@ -425,8 +397,7 @@ int nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out)
|
ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out) {
|
||||||
{
|
|
||||||
size_t len;
|
size_t len;
|
||||||
nghttp2_buf_chain *chain;
|
nghttp2_buf_chain *chain;
|
||||||
nghttp2_buf *buf;
|
nghttp2_buf *buf;
|
||||||
@@ -442,7 +413,7 @@ ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out)
|
|||||||
if (!len) {
|
if (!len) {
|
||||||
res = NULL;
|
res = NULL;
|
||||||
} else {
|
} else {
|
||||||
res = malloc(len);
|
res = nghttp2_mem_malloc(bufs->mem, len);
|
||||||
|
|
||||||
if (res == NULL) {
|
if (res == NULL) {
|
||||||
return NGHTTP2_ERR_NOMEM;
|
return NGHTTP2_ERR_NOMEM;
|
||||||
@@ -455,8 +426,7 @@ ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out)
|
|||||||
buf = &chain->buf;
|
buf = &chain->buf;
|
||||||
|
|
||||||
if (resbuf.last) {
|
if (resbuf.last) {
|
||||||
resbuf.last = nghttp2_cpymem(resbuf.last,
|
resbuf.last = nghttp2_cpymem(resbuf.last, buf->pos, nghttp2_buf_len(buf));
|
||||||
buf->pos, nghttp2_buf_len(buf));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nghttp2_buf_reset(buf);
|
nghttp2_buf_reset(buf);
|
||||||
@@ -470,8 +440,7 @@ ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out)
|
|||||||
return (ssize_t)len;
|
return (ssize_t)len;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_bufs_reset(nghttp2_bufs *bufs)
|
void nghttp2_bufs_reset(nghttp2_bufs *bufs) {
|
||||||
{
|
|
||||||
nghttp2_buf_chain *chain, *ci;
|
nghttp2_buf_chain *chain, *ci;
|
||||||
size_t k;
|
size_t k;
|
||||||
|
|
||||||
@@ -493,7 +462,7 @@ void nghttp2_bufs_reset(nghttp2_bufs *bufs)
|
|||||||
for (ci = chain; ci;) {
|
for (ci = chain; ci;) {
|
||||||
chain = ci->next;
|
chain = ci->next;
|
||||||
|
|
||||||
buf_chain_del(ci);
|
buf_chain_del(ci, bufs->mem);
|
||||||
|
|
||||||
ci = chain;
|
ci = chain;
|
||||||
}
|
}
|
||||||
@@ -504,17 +473,12 @@ void nghttp2_bufs_reset(nghttp2_bufs *bufs)
|
|||||||
bufs->cur = bufs->head;
|
bufs->cur = bufs->head;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_bufs_advance(nghttp2_bufs *bufs)
|
int nghttp2_bufs_advance(nghttp2_bufs *bufs) { return bufs_alloc_chain(bufs); }
|
||||||
{
|
|
||||||
return bufs_alloc_chain(bufs);
|
|
||||||
}
|
|
||||||
|
|
||||||
int nghttp2_bufs_next_present(nghttp2_bufs *bufs)
|
int nghttp2_bufs_next_present(nghttp2_bufs *bufs) {
|
||||||
{
|
|
||||||
nghttp2_buf_chain *chain;
|
nghttp2_buf_chain *chain;
|
||||||
|
|
||||||
chain = bufs->cur->next;
|
chain = bufs->cur->next;
|
||||||
|
|
||||||
return chain && nghttp2_buf_len(&chain->buf);
|
return chain && nghttp2_buf_len(&chain->buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
#include <nghttp2/nghttp2.h>
|
#include <nghttp2/nghttp2.h>
|
||||||
|
|
||||||
#include "nghttp2_int.h"
|
#include "nghttp2_int.h"
|
||||||
|
#include "nghttp2_mem.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
/* This points to the beginning of the buffer. The effective range
|
/* This points to the beginning of the buffer. The effective range
|
||||||
@@ -76,7 +77,6 @@ typedef struct {
|
|||||||
*/
|
*/
|
||||||
void nghttp2_buf_init(nghttp2_buf *buf);
|
void nghttp2_buf_init(nghttp2_buf *buf);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initializes the |buf| and allocates at least |initial| bytes of
|
* Initializes the |buf| and allocates at least |initial| bytes of
|
||||||
* memory.
|
* memory.
|
||||||
@@ -87,12 +87,12 @@ void nghttp2_buf_init(nghttp2_buf *buf);
|
|||||||
* NGHTTP2_ERR_NOMEM
|
* NGHTTP2_ERR_NOMEM
|
||||||
* Out of memory
|
* Out of memory
|
||||||
*/
|
*/
|
||||||
int nghttp2_buf_init2(nghttp2_buf *buf, size_t initial);
|
int nghttp2_buf_init2(nghttp2_buf *buf, size_t initial, nghttp2_mem *mem);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Frees buffer in |buf|.
|
* Frees buffer in |buf|.
|
||||||
*/
|
*/
|
||||||
void nghttp2_buf_free(nghttp2_buf *buf);
|
void nghttp2_buf_free(nghttp2_buf *buf, nghttp2_mem *mem);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Extends buffer so that nghttp2_buf_cap() returns at least
|
* Extends buffer so that nghttp2_buf_cap() returns at least
|
||||||
@@ -105,23 +105,7 @@ void nghttp2_buf_free(nghttp2_buf *buf);
|
|||||||
* NGHTTP2_ERR_NOMEM
|
* NGHTTP2_ERR_NOMEM
|
||||||
* Out of memory
|
* Out of memory
|
||||||
*/
|
*/
|
||||||
int nghttp2_buf_reserve(nghttp2_buf *buf, size_t new_cap);
|
int nghttp2_buf_reserve(nghttp2_buf *buf, size_t new_cap, nghttp2_mem *mem);
|
||||||
|
|
||||||
/*
|
|
||||||
* This function behaves like nghttp2_buf_reserve(), but new capacity
|
|
||||||
* is calculated as nghttp2_buf_pos_offset(buf) + new_rel_cap. In
|
|
||||||
* other words, this function reserves memory at least |new_rel_cap|
|
|
||||||
* bytes from buf->pos.
|
|
||||||
*/
|
|
||||||
int nghttp2_buf_pos_reserve(nghttp2_buf *buf, size_t new_rel_cap);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This function behaves like nghttp2_buf_reserve(), but new capacity
|
|
||||||
* is calculated as nghttp2_buf_last_offset(buf) + new_rel_cap. In
|
|
||||||
* other words, this function reserves memory at least |new_rel_cap|
|
|
||||||
* bytes from buf->last.
|
|
||||||
*/
|
|
||||||
int nghttp2_buf_last_reserve(nghttp2_buf *buf, size_t new_rel_cap);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Resets pos, last, mark member of |buf| to buf->begin.
|
* Resets pos, last, mark member of |buf| to buf->begin.
|
||||||
@@ -152,6 +136,8 @@ typedef struct {
|
|||||||
nghttp2_buf_chain *head;
|
nghttp2_buf_chain *head;
|
||||||
/* Buffer pointer where write occurs. */
|
/* Buffer pointer where write occurs. */
|
||||||
nghttp2_buf_chain *cur;
|
nghttp2_buf_chain *cur;
|
||||||
|
/* Memory allocator */
|
||||||
|
nghttp2_mem *mem;
|
||||||
/* The buffer capacity of each buf */
|
/* The buffer capacity of each buf */
|
||||||
size_t chunk_length;
|
size_t chunk_length;
|
||||||
/* The maximum number of nghttp2_buf_chain */
|
/* The maximum number of nghttp2_buf_chain */
|
||||||
@@ -170,15 +156,15 @@ typedef struct {
|
|||||||
* This is the same as calling nghttp2_bufs_init2 with the given
|
* This is the same as calling nghttp2_bufs_init2 with the given
|
||||||
* arguments and offset = 0.
|
* arguments and offset = 0.
|
||||||
*/
|
*/
|
||||||
int nghttp2_bufs_init(nghttp2_bufs *bufs, size_t chunk_length,
|
int nghttp2_bufs_init(nghttp2_bufs *bufs, size_t chunk_length, size_t max_chunk,
|
||||||
size_t max_chunk);
|
nghttp2_mem *mem);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is the same as calling nghttp2_bufs_init3 with the given
|
* This is the same as calling nghttp2_bufs_init3 with the given
|
||||||
* arguments and chunk_keep = max_chunk.
|
* arguments and chunk_keep = max_chunk.
|
||||||
*/
|
*/
|
||||||
int nghttp2_bufs_init2(nghttp2_bufs *bufs, size_t chunk_length,
|
int nghttp2_bufs_init2(nghttp2_bufs *bufs, size_t chunk_length,
|
||||||
size_t max_chunk, size_t offset);
|
size_t max_chunk, size_t offset, nghttp2_mem *mem);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initializes |bufs|. Each buffer size is given in the
|
* Initializes |bufs|. Each buffer size is given in the
|
||||||
@@ -200,7 +186,8 @@ int nghttp2_bufs_init2(nghttp2_bufs *bufs, size_t chunk_length,
|
|||||||
* long.
|
* long.
|
||||||
*/
|
*/
|
||||||
int nghttp2_bufs_init3(nghttp2_bufs *bufs, size_t chunk_length,
|
int nghttp2_bufs_init3(nghttp2_bufs *bufs, size_t chunk_length,
|
||||||
size_t max_chunk, size_t chunk_keep, size_t offset);
|
size_t max_chunk, size_t chunk_keep, size_t offset,
|
||||||
|
nghttp2_mem *mem);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Frees any related resources to the |bufs|.
|
* Frees any related resources to the |bufs|.
|
||||||
@@ -220,7 +207,8 @@ void nghttp2_bufs_free(nghttp2_bufs *bufs);
|
|||||||
* NGHTTP2_ERR_NOMEM
|
* NGHTTP2_ERR_NOMEM
|
||||||
* Out of memory.
|
* Out of memory.
|
||||||
*/
|
*/
|
||||||
int nghttp2_bufs_wrap_init(nghttp2_bufs *bufs, uint8_t *begin, size_t len);
|
int nghttp2_bufs_wrap_init(nghttp2_bufs *bufs, uint8_t *begin, size_t len,
|
||||||
|
nghttp2_mem *mem);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Frees any related resource to the |bufs|. This function does not
|
* Frees any related resource to the |bufs|. This function does not
|
||||||
|
|||||||
@@ -26,8 +26,7 @@
|
|||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
int nghttp2_session_callbacks_new(nghttp2_session_callbacks **callbacks_ptr)
|
int nghttp2_session_callbacks_new(nghttp2_session_callbacks **callbacks_ptr) {
|
||||||
{
|
|
||||||
*callbacks_ptr = calloc(1, sizeof(nghttp2_session_callbacks));
|
*callbacks_ptr = calloc(1, sizeof(nghttp2_session_callbacks));
|
||||||
|
|
||||||
if (*callbacks_ptr == NULL) {
|
if (*callbacks_ptr == NULL) {
|
||||||
@@ -37,103 +36,88 @@ int nghttp2_session_callbacks_new(nghttp2_session_callbacks **callbacks_ptr)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_session_callbacks_del(nghttp2_session_callbacks *callbacks)
|
void nghttp2_session_callbacks_del(nghttp2_session_callbacks *callbacks) {
|
||||||
{
|
|
||||||
free(callbacks);
|
free(callbacks);
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_session_callbacks_set_send_callback
|
void nghttp2_session_callbacks_set_send_callback(
|
||||||
(nghttp2_session_callbacks *cbs, nghttp2_send_callback send_callback)
|
nghttp2_session_callbacks *cbs, nghttp2_send_callback send_callback) {
|
||||||
{
|
|
||||||
cbs->send_callback = send_callback;
|
cbs->send_callback = send_callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_session_callbacks_set_recv_callback
|
void nghttp2_session_callbacks_set_recv_callback(
|
||||||
(nghttp2_session_callbacks *cbs, nghttp2_recv_callback recv_callback)
|
nghttp2_session_callbacks *cbs, nghttp2_recv_callback recv_callback) {
|
||||||
{
|
|
||||||
cbs->recv_callback = recv_callback;
|
cbs->recv_callback = recv_callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_session_callbacks_set_on_frame_recv_callback
|
void nghttp2_session_callbacks_set_on_frame_recv_callback(
|
||||||
(nghttp2_session_callbacks *cbs,
|
nghttp2_session_callbacks *cbs,
|
||||||
nghttp2_on_frame_recv_callback on_frame_recv_callback)
|
nghttp2_on_frame_recv_callback on_frame_recv_callback) {
|
||||||
{
|
|
||||||
cbs->on_frame_recv_callback = on_frame_recv_callback;
|
cbs->on_frame_recv_callback = on_frame_recv_callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_session_callbacks_set_on_invalid_frame_recv_callback
|
void nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(
|
||||||
(nghttp2_session_callbacks *cbs,
|
nghttp2_session_callbacks *cbs,
|
||||||
nghttp2_on_invalid_frame_recv_callback on_invalid_frame_recv_callback)
|
nghttp2_on_invalid_frame_recv_callback on_invalid_frame_recv_callback) {
|
||||||
{
|
|
||||||
cbs->on_invalid_frame_recv_callback = on_invalid_frame_recv_callback;
|
cbs->on_invalid_frame_recv_callback = on_invalid_frame_recv_callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_session_callbacks_set_on_data_chunk_recv_callback
|
void nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
|
||||||
(nghttp2_session_callbacks *cbs,
|
nghttp2_session_callbacks *cbs,
|
||||||
nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback)
|
nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback) {
|
||||||
{
|
|
||||||
cbs->on_data_chunk_recv_callback = on_data_chunk_recv_callback;
|
cbs->on_data_chunk_recv_callback = on_data_chunk_recv_callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_session_callbacks_set_before_frame_send_callback
|
void nghttp2_session_callbacks_set_before_frame_send_callback(
|
||||||
(nghttp2_session_callbacks *cbs,
|
nghttp2_session_callbacks *cbs,
|
||||||
nghttp2_before_frame_send_callback before_frame_send_callback)
|
nghttp2_before_frame_send_callback before_frame_send_callback) {
|
||||||
{
|
|
||||||
cbs->before_frame_send_callback = before_frame_send_callback;
|
cbs->before_frame_send_callback = before_frame_send_callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_session_callbacks_set_on_frame_send_callback
|
void nghttp2_session_callbacks_set_on_frame_send_callback(
|
||||||
(nghttp2_session_callbacks *cbs,
|
nghttp2_session_callbacks *cbs,
|
||||||
nghttp2_on_frame_send_callback on_frame_send_callback)
|
nghttp2_on_frame_send_callback on_frame_send_callback) {
|
||||||
{
|
|
||||||
cbs->on_frame_send_callback = on_frame_send_callback;
|
cbs->on_frame_send_callback = on_frame_send_callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_session_callbacks_set_on_frame_not_send_callback
|
void nghttp2_session_callbacks_set_on_frame_not_send_callback(
|
||||||
(nghttp2_session_callbacks *cbs,
|
nghttp2_session_callbacks *cbs,
|
||||||
nghttp2_on_frame_not_send_callback on_frame_not_send_callback)
|
nghttp2_on_frame_not_send_callback on_frame_not_send_callback) {
|
||||||
{
|
|
||||||
cbs->on_frame_not_send_callback = on_frame_not_send_callback;
|
cbs->on_frame_not_send_callback = on_frame_not_send_callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_session_callbacks_set_on_stream_close_callback
|
void nghttp2_session_callbacks_set_on_stream_close_callback(
|
||||||
(nghttp2_session_callbacks *cbs,
|
nghttp2_session_callbacks *cbs,
|
||||||
nghttp2_on_stream_close_callback on_stream_close_callback)
|
nghttp2_on_stream_close_callback on_stream_close_callback) {
|
||||||
{
|
|
||||||
cbs->on_stream_close_callback = on_stream_close_callback;
|
cbs->on_stream_close_callback = on_stream_close_callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_session_callbacks_set_on_begin_headers_callback
|
void nghttp2_session_callbacks_set_on_begin_headers_callback(
|
||||||
(nghttp2_session_callbacks *cbs,
|
nghttp2_session_callbacks *cbs,
|
||||||
nghttp2_on_begin_headers_callback on_begin_headers_callback)
|
nghttp2_on_begin_headers_callback on_begin_headers_callback) {
|
||||||
{
|
|
||||||
cbs->on_begin_headers_callback = on_begin_headers_callback;
|
cbs->on_begin_headers_callback = on_begin_headers_callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_session_callbacks_set_on_header_callback
|
void nghttp2_session_callbacks_set_on_header_callback(
|
||||||
(nghttp2_session_callbacks *cbs,
|
nghttp2_session_callbacks *cbs,
|
||||||
nghttp2_on_header_callback on_header_callback)
|
nghttp2_on_header_callback on_header_callback) {
|
||||||
{
|
|
||||||
cbs->on_header_callback = on_header_callback;
|
cbs->on_header_callback = on_header_callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_session_callbacks_set_select_padding_callback
|
void nghttp2_session_callbacks_set_select_padding_callback(
|
||||||
(nghttp2_session_callbacks *cbs,
|
nghttp2_session_callbacks *cbs,
|
||||||
nghttp2_select_padding_callback select_padding_callback)
|
nghttp2_select_padding_callback select_padding_callback) {
|
||||||
{
|
|
||||||
cbs->select_padding_callback = select_padding_callback;
|
cbs->select_padding_callback = select_padding_callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_session_callbacks_set_data_source_read_length_callback
|
void nghttp2_session_callbacks_set_data_source_read_length_callback(
|
||||||
(nghttp2_session_callbacks *cbs,
|
nghttp2_session_callbacks *cbs,
|
||||||
nghttp2_data_source_read_length_callback data_source_read_length_callback)
|
nghttp2_data_source_read_length_callback data_source_read_length_callback) {
|
||||||
{
|
|
||||||
cbs->read_length_callback = data_source_read_length_callback;
|
cbs->read_length_callback = data_source_read_length_callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_session_callbacks_set_on_begin_frame_callback
|
void nghttp2_session_callbacks_set_on_begin_frame_callback(
|
||||||
(nghttp2_session_callbacks *cbs,
|
nghttp2_session_callbacks *cbs,
|
||||||
nghttp2_on_begin_frame_callback on_begin_frame_callback)
|
nghttp2_on_begin_frame_callback on_begin_frame_callback) {
|
||||||
{
|
|
||||||
cbs->on_begin_frame_callback = on_begin_frame_callback;
|
cbs->on_begin_frame_callback = on_begin_frame_callback;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,13 +33,7 @@
|
|||||||
#include "nghttp2_net.h"
|
#include "nghttp2_net.h"
|
||||||
#include "nghttp2_priority_spec.h"
|
#include "nghttp2_priority_spec.h"
|
||||||
|
|
||||||
int nghttp2_frame_is_data_frame(uint8_t *head)
|
void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd) {
|
||||||
{
|
|
||||||
return head[2] == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nghttp2_frame_pack_frame_hd(uint8_t* buf, const nghttp2_frame_hd *hd)
|
|
||||||
{
|
|
||||||
nghttp2_put_uint32be(&buf[0], (uint32_t)(hd->length << 8));
|
nghttp2_put_uint32be(&buf[0], (uint32_t)(hd->length << 8));
|
||||||
buf[3] = hd->type;
|
buf[3] = hd->type;
|
||||||
buf[4] = hd->flags;
|
buf[4] = hd->flags;
|
||||||
@@ -47,8 +41,7 @@ void nghttp2_frame_pack_frame_hd(uint8_t* buf, const nghttp2_frame_hd *hd)
|
|||||||
/* ignore hd->reserved for now */
|
/* ignore hd->reserved for now */
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_frame_unpack_frame_hd(nghttp2_frame_hd *hd, const uint8_t* buf)
|
void nghttp2_frame_unpack_frame_hd(nghttp2_frame_hd *hd, const uint8_t *buf) {
|
||||||
{
|
|
||||||
hd->length = nghttp2_get_uint32(&buf[0]) >> 8;
|
hd->length = nghttp2_get_uint32(&buf[0]) >> 8;
|
||||||
hd->type = buf[3];
|
hd->type = buf[3];
|
||||||
hd->flags = buf[4];
|
hd->flags = buf[4];
|
||||||
@@ -56,10 +49,8 @@ void nghttp2_frame_unpack_frame_hd(nghttp2_frame_hd *hd, const uint8_t* buf)
|
|||||||
hd->reserved = 0;
|
hd->reserved = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_frame_hd_init(nghttp2_frame_hd *hd, size_t length,
|
void nghttp2_frame_hd_init(nghttp2_frame_hd *hd, size_t length, uint8_t type,
|
||||||
uint8_t type, uint8_t flags,
|
uint8_t flags, int32_t stream_id) {
|
||||||
int32_t stream_id)
|
|
||||||
{
|
|
||||||
hd->length = length;
|
hd->length = length;
|
||||||
hd->type = type;
|
hd->type = type;
|
||||||
hd->flags = flags;
|
hd->flags = flags;
|
||||||
@@ -67,12 +58,10 @@ void nghttp2_frame_hd_init(nghttp2_frame_hd *hd, size_t length,
|
|||||||
hd->reserved = 0;
|
hd->reserved = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_frame_headers_init(nghttp2_headers *frame,
|
void nghttp2_frame_headers_init(nghttp2_headers *frame, uint8_t flags,
|
||||||
uint8_t flags, int32_t stream_id,
|
int32_t stream_id, nghttp2_headers_category cat,
|
||||||
nghttp2_headers_category cat,
|
|
||||||
const nghttp2_priority_spec *pri_spec,
|
const nghttp2_priority_spec *pri_spec,
|
||||||
nghttp2_nv *nva, size_t nvlen)
|
nghttp2_nv *nva, size_t nvlen) {
|
||||||
{
|
|
||||||
nghttp2_frame_hd_init(&frame->hd, 0, NGHTTP2_HEADERS, flags, stream_id);
|
nghttp2_frame_hd_init(&frame->hd, 0, NGHTTP2_HEADERS, flags, stream_id);
|
||||||
frame->padlen = 0;
|
frame->padlen = 0;
|
||||||
frame->nva = nva;
|
frame->nva = nva;
|
||||||
@@ -86,54 +75,44 @@ void nghttp2_frame_headers_init(nghttp2_headers *frame,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_frame_headers_free(nghttp2_headers *frame)
|
void nghttp2_frame_headers_free(nghttp2_headers *frame, nghttp2_mem *mem) {
|
||||||
{
|
nghttp2_nv_array_del(frame->nva, mem);
|
||||||
nghttp2_nv_array_del(frame->nva);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_frame_priority_init(nghttp2_priority *frame, int32_t stream_id,
|
void nghttp2_frame_priority_init(nghttp2_priority *frame, int32_t stream_id,
|
||||||
const nghttp2_priority_spec *pri_spec)
|
const nghttp2_priority_spec *pri_spec) {
|
||||||
{
|
|
||||||
nghttp2_frame_hd_init(&frame->hd, NGHTTP2_PRIORITY_SPECLEN, NGHTTP2_PRIORITY,
|
nghttp2_frame_hd_init(&frame->hd, NGHTTP2_PRIORITY_SPECLEN, NGHTTP2_PRIORITY,
|
||||||
NGHTTP2_FLAG_NONE, stream_id);
|
NGHTTP2_FLAG_NONE, stream_id);
|
||||||
frame->pri_spec = *pri_spec;
|
frame->pri_spec = *pri_spec;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_frame_priority_free(nghttp2_priority *frame)
|
void nghttp2_frame_priority_free(nghttp2_priority *frame _U_) {}
|
||||||
{}
|
|
||||||
|
|
||||||
void nghttp2_frame_rst_stream_init(nghttp2_rst_stream *frame,
|
void nghttp2_frame_rst_stream_init(nghttp2_rst_stream *frame, int32_t stream_id,
|
||||||
int32_t stream_id,
|
uint32_t error_code) {
|
||||||
uint32_t error_code)
|
|
||||||
{
|
|
||||||
nghttp2_frame_hd_init(&frame->hd, 4, NGHTTP2_RST_STREAM, NGHTTP2_FLAG_NONE,
|
nghttp2_frame_hd_init(&frame->hd, 4, NGHTTP2_RST_STREAM, NGHTTP2_FLAG_NONE,
|
||||||
stream_id);
|
stream_id);
|
||||||
frame->error_code = error_code;
|
frame->error_code = error_code;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_frame_rst_stream_free(nghttp2_rst_stream *frame)
|
void nghttp2_frame_rst_stream_free(nghttp2_rst_stream *frame _U_) {}
|
||||||
{}
|
|
||||||
|
|
||||||
|
|
||||||
void nghttp2_frame_settings_init(nghttp2_settings *frame, uint8_t flags,
|
void nghttp2_frame_settings_init(nghttp2_settings *frame, uint8_t flags,
|
||||||
nghttp2_settings_entry *iv, size_t niv)
|
nghttp2_settings_entry *iv, size_t niv) {
|
||||||
{
|
|
||||||
nghttp2_frame_hd_init(&frame->hd, niv * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH,
|
nghttp2_frame_hd_init(&frame->hd, niv * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH,
|
||||||
NGHTTP2_SETTINGS, flags, 0);
|
NGHTTP2_SETTINGS, flags, 0);
|
||||||
frame->niv = niv;
|
frame->niv = niv;
|
||||||
frame->iv = iv;
|
frame->iv = iv;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_frame_settings_free(nghttp2_settings *frame)
|
void nghttp2_frame_settings_free(nghttp2_settings *frame, nghttp2_mem *mem) {
|
||||||
{
|
nghttp2_mem_free(mem, frame->iv);
|
||||||
free(frame->iv);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_frame_push_promise_init(nghttp2_push_promise *frame,
|
void nghttp2_frame_push_promise_init(nghttp2_push_promise *frame, uint8_t flags,
|
||||||
uint8_t flags, int32_t stream_id,
|
int32_t stream_id,
|
||||||
int32_t promised_stream_id,
|
int32_t promised_stream_id,
|
||||||
nghttp2_nv *nva, size_t nvlen)
|
nghttp2_nv *nva, size_t nvlen) {
|
||||||
{
|
|
||||||
nghttp2_frame_hd_init(&frame->hd, 0, NGHTTP2_PUSH_PROMISE, flags, stream_id);
|
nghttp2_frame_hd_init(&frame->hd, 0, NGHTTP2_PUSH_PROMISE, flags, stream_id);
|
||||||
frame->padlen = 0;
|
frame->padlen = 0;
|
||||||
frame->nva = nva;
|
frame->nva = nva;
|
||||||
@@ -142,14 +121,13 @@ void nghttp2_frame_push_promise_init(nghttp2_push_promise *frame,
|
|||||||
frame->reserved = 0;
|
frame->reserved = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_frame_push_promise_free(nghttp2_push_promise *frame)
|
void nghttp2_frame_push_promise_free(nghttp2_push_promise *frame,
|
||||||
{
|
nghttp2_mem *mem) {
|
||||||
nghttp2_nv_array_del(frame->nva);
|
nghttp2_nv_array_del(frame->nva, mem);
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_frame_ping_init(nghttp2_ping *frame, uint8_t flags,
|
void nghttp2_frame_ping_init(nghttp2_ping *frame, uint8_t flags,
|
||||||
const uint8_t *opaque_data)
|
const uint8_t *opaque_data) {
|
||||||
{
|
|
||||||
nghttp2_frame_hd_init(&frame->hd, 8, NGHTTP2_PING, flags, 0);
|
nghttp2_frame_hd_init(&frame->hd, 8, NGHTTP2_PING, flags, 0);
|
||||||
if (opaque_data) {
|
if (opaque_data) {
|
||||||
memcpy(frame->opaque_data, opaque_data, sizeof(frame->opaque_data));
|
memcpy(frame->opaque_data, opaque_data, sizeof(frame->opaque_data));
|
||||||
@@ -158,13 +136,11 @@ void nghttp2_frame_ping_init(nghttp2_ping *frame, uint8_t flags,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_frame_ping_free(nghttp2_ping *frame)
|
void nghttp2_frame_ping_free(nghttp2_ping *frame _U_) {}
|
||||||
{}
|
|
||||||
|
|
||||||
void nghttp2_frame_goaway_init(nghttp2_goaway *frame, int32_t last_stream_id,
|
void nghttp2_frame_goaway_init(nghttp2_goaway *frame, int32_t last_stream_id,
|
||||||
uint32_t error_code,
|
uint32_t error_code, uint8_t *opaque_data,
|
||||||
uint8_t *opaque_data, size_t opaque_data_len)
|
size_t opaque_data_len) {
|
||||||
{
|
|
||||||
nghttp2_frame_hd_init(&frame->hd, 8 + opaque_data_len, NGHTTP2_GOAWAY,
|
nghttp2_frame_hd_init(&frame->hd, 8 + opaque_data_len, NGHTTP2_GOAWAY,
|
||||||
NGHTTP2_FLAG_NONE, 0);
|
NGHTTP2_FLAG_NONE, 0);
|
||||||
frame->last_stream_id = last_stream_id;
|
frame->last_stream_id = last_stream_id;
|
||||||
@@ -174,99 +150,34 @@ void nghttp2_frame_goaway_init(nghttp2_goaway *frame, int32_t last_stream_id,
|
|||||||
frame->reserved = 0;
|
frame->reserved = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_frame_goaway_free(nghttp2_goaway *frame)
|
void nghttp2_frame_goaway_free(nghttp2_goaway *frame, nghttp2_mem *mem) {
|
||||||
{
|
nghttp2_mem_free(mem, frame->opaque_data);
|
||||||
free(frame->opaque_data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_frame_window_update_init(nghttp2_window_update *frame,
|
void nghttp2_frame_window_update_init(nghttp2_window_update *frame,
|
||||||
uint8_t flags,
|
uint8_t flags, int32_t stream_id,
|
||||||
int32_t stream_id,
|
int32_t window_size_increment) {
|
||||||
int32_t window_size_increment)
|
nghttp2_frame_hd_init(&frame->hd, 4, NGHTTP2_WINDOW_UPDATE, flags, stream_id);
|
||||||
{
|
|
||||||
nghttp2_frame_hd_init(&frame->hd, 4, NGHTTP2_WINDOW_UPDATE, flags,
|
|
||||||
stream_id);
|
|
||||||
frame->window_size_increment = window_size_increment;
|
frame->window_size_increment = window_size_increment;
|
||||||
frame->reserved = 0;
|
frame->reserved = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_frame_window_update_free(nghttp2_window_update *frame)
|
void nghttp2_frame_window_update_free(nghttp2_window_update *frame _U_) {}
|
||||||
{}
|
|
||||||
|
|
||||||
void nghttp2_frame_altsvc_init(nghttp2_extension *frame, int32_t stream_id,
|
size_t nghttp2_frame_trail_padlen(nghttp2_frame *frame, size_t padlen) {
|
||||||
uint32_t max_age,
|
|
||||||
uint16_t port,
|
|
||||||
uint8_t *protocol_id,
|
|
||||||
size_t protocol_id_len,
|
|
||||||
uint8_t *host, size_t host_len,
|
|
||||||
uint8_t *origin, size_t origin_len)
|
|
||||||
{
|
|
||||||
size_t payloadlen;
|
|
||||||
nghttp2_ext_altsvc *altsvc;
|
|
||||||
|
|
||||||
altsvc = frame->payload;
|
|
||||||
|
|
||||||
payloadlen = NGHTTP2_ALTSVC_MINLEN + protocol_id_len + host_len + origin_len;
|
|
||||||
|
|
||||||
nghttp2_frame_hd_init(&frame->hd, payloadlen, NGHTTP2_EXT_ALTSVC,
|
|
||||||
NGHTTP2_FLAG_NONE, stream_id);
|
|
||||||
|
|
||||||
altsvc->max_age = max_age;
|
|
||||||
altsvc->port = port;
|
|
||||||
altsvc->protocol_id = protocol_id;
|
|
||||||
altsvc->protocol_id_len = protocol_id_len;
|
|
||||||
altsvc->host = host;
|
|
||||||
altsvc->host_len = host_len;
|
|
||||||
altsvc->origin = origin;
|
|
||||||
altsvc->origin_len = origin_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nghttp2_frame_altsvc_free(nghttp2_extension *frame)
|
|
||||||
{
|
|
||||||
nghttp2_ext_altsvc *altsvc;
|
|
||||||
|
|
||||||
altsvc = frame->payload;
|
|
||||||
|
|
||||||
if(altsvc == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(altsvc->protocol_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void nghttp2_frame_data_init(nghttp2_data *frame, nghttp2_private_data *pdata)
|
|
||||||
{
|
|
||||||
frame->hd = pdata->hd;
|
|
||||||
frame->padlen = pdata->padlen;
|
|
||||||
/* flags may have NGHTTP2_FLAG_END_STREAM even if the sent chunk is
|
|
||||||
not the end of the stream */
|
|
||||||
if(!pdata->eof) {
|
|
||||||
frame->hd.flags &= ~NGHTTP2_FLAG_END_STREAM;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t nghttp2_frame_trail_padlen(nghttp2_frame *frame, size_t padlen)
|
|
||||||
{
|
|
||||||
return padlen - ((frame->hd.flags & NGHTTP2_FLAG_PADDED) > 0);
|
return padlen - ((frame->hd.flags & NGHTTP2_FLAG_PADDED) > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_frame_private_data_init(nghttp2_private_data *frame,
|
void nghttp2_frame_data_init(nghttp2_data *frame, uint8_t flags,
|
||||||
uint8_t flags,
|
int32_t stream_id) {
|
||||||
int32_t stream_id,
|
|
||||||
const nghttp2_data_provider *data_prd)
|
|
||||||
{
|
|
||||||
/* At this moment, the length of DATA frame is unknown */
|
/* At this moment, the length of DATA frame is unknown */
|
||||||
nghttp2_frame_hd_init(&frame->hd, 0, NGHTTP2_DATA, flags, stream_id);
|
nghttp2_frame_hd_init(&frame->hd, 0, NGHTTP2_DATA, flags, stream_id);
|
||||||
frame->data_prd = *data_prd;
|
|
||||||
frame->padlen = 0;
|
frame->padlen = 0;
|
||||||
frame->eof = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_frame_private_data_free(nghttp2_private_data *frame)
|
void nghttp2_frame_data_free(nghttp2_data *frame _U_) {}
|
||||||
{}
|
|
||||||
|
|
||||||
size_t nghttp2_frame_priority_len(uint8_t flags)
|
size_t nghttp2_frame_priority_len(uint8_t flags) {
|
||||||
{
|
|
||||||
if (flags & NGHTTP2_FLAG_PRIORITY) {
|
if (flags & NGHTTP2_FLAG_PRIORITY) {
|
||||||
return NGHTTP2_PRIORITY_SPECLEN;
|
return NGHTTP2_PRIORITY_SPECLEN;
|
||||||
}
|
}
|
||||||
@@ -274,8 +185,7 @@ size_t nghttp2_frame_priority_len(uint8_t flags)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t nghttp2_frame_headers_payload_nv_offset(nghttp2_headers *frame)
|
size_t nghttp2_frame_headers_payload_nv_offset(nghttp2_headers *frame) {
|
||||||
{
|
|
||||||
return nghttp2_frame_priority_len(frame->hd.flags);
|
return nghttp2_frame_priority_len(frame->hd.flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,8 +202,7 @@ size_t nghttp2_frame_headers_payload_nv_offset(nghttp2_headers *frame)
|
|||||||
* We don't process any padding here.
|
* We don't process any padding here.
|
||||||
*/
|
*/
|
||||||
static int frame_pack_headers_shared(nghttp2_bufs *bufs,
|
static int frame_pack_headers_shared(nghttp2_bufs *bufs,
|
||||||
nghttp2_frame_hd *frame_hd)
|
nghttp2_frame_hd *frame_hd) {
|
||||||
{
|
|
||||||
nghttp2_buf *buf;
|
nghttp2_buf *buf;
|
||||||
nghttp2_buf_chain *ci, *ce;
|
nghttp2_buf_chain *ci, *ce;
|
||||||
nghttp2_frame_hd hd;
|
nghttp2_frame_hd hd;
|
||||||
@@ -303,8 +212,8 @@ static int frame_pack_headers_shared(nghttp2_bufs *bufs,
|
|||||||
hd = *frame_hd;
|
hd = *frame_hd;
|
||||||
hd.length = nghttp2_buf_len(buf);
|
hd.length = nghttp2_buf_len(buf);
|
||||||
|
|
||||||
DEBUGF(fprintf(stderr,
|
DEBUGF(fprintf(stderr, "send: HEADERS/PUSH_PROMISE, payloadlen=%zu\n",
|
||||||
"send: HEADERS/PUSH_PROMISE, payloadlen=%zu\n", hd.length));
|
hd.length));
|
||||||
|
|
||||||
/* We have multiple frame buffers, which means one or more
|
/* We have multiple frame buffers, which means one or more
|
||||||
CONTINUATION frame is involved. Remove END_HEADERS flag from the
|
CONTINUATION frame is involved. Remove END_HEADERS flag from the
|
||||||
@@ -329,8 +238,8 @@ static int frame_pack_headers_shared(nghttp2_bufs *bufs,
|
|||||||
|
|
||||||
hd.length = nghttp2_buf_len(buf);
|
hd.length = nghttp2_buf_len(buf);
|
||||||
|
|
||||||
DEBUGF(fprintf(stderr,
|
DEBUGF(fprintf(stderr, "send: int CONTINUATION, payloadlen=%zu\n",
|
||||||
"send: int CONTINUATION, payloadlen=%zu\n", hd.length));
|
hd.length));
|
||||||
|
|
||||||
buf->pos -= NGHTTP2_FRAME_HDLEN;
|
buf->pos -= NGHTTP2_FRAME_HDLEN;
|
||||||
nghttp2_frame_pack_frame_hd(buf->pos, &hd);
|
nghttp2_frame_pack_frame_hd(buf->pos, &hd);
|
||||||
@@ -341,8 +250,8 @@ static int frame_pack_headers_shared(nghttp2_bufs *bufs,
|
|||||||
/* Set END_HEADERS flag for last CONTINUATION */
|
/* Set END_HEADERS flag for last CONTINUATION */
|
||||||
hd.flags = NGHTTP2_FLAG_END_HEADERS;
|
hd.flags = NGHTTP2_FLAG_END_HEADERS;
|
||||||
|
|
||||||
DEBUGF(fprintf(stderr,
|
DEBUGF(fprintf(stderr, "send: last CONTINUATION, payloadlen=%zu\n",
|
||||||
"send: last CONTINUATION, payloadlen=%zu\n", hd.length));
|
hd.length));
|
||||||
|
|
||||||
buf->pos -= NGHTTP2_FRAME_HDLEN;
|
buf->pos -= NGHTTP2_FRAME_HDLEN;
|
||||||
nghttp2_frame_pack_frame_hd(buf->pos, &hd);
|
nghttp2_frame_pack_frame_hd(buf->pos, &hd);
|
||||||
@@ -351,10 +260,8 @@ static int frame_pack_headers_shared(nghttp2_bufs *bufs,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_frame_pack_headers(nghttp2_bufs *bufs,
|
int nghttp2_frame_pack_headers(nghttp2_bufs *bufs, nghttp2_headers *frame,
|
||||||
nghttp2_headers *frame,
|
nghttp2_hd_deflater *deflater) {
|
||||||
nghttp2_hd_deflater *deflater)
|
|
||||||
{
|
|
||||||
size_t nv_offset;
|
size_t nv_offset;
|
||||||
int rv;
|
int rv;
|
||||||
nghttp2_buf *buf;
|
nghttp2_buf *buf;
|
||||||
@@ -392,8 +299,7 @@ int nghttp2_frame_pack_headers(nghttp2_bufs *bufs,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_frame_pack_priority_spec(uint8_t *buf,
|
void nghttp2_frame_pack_priority_spec(uint8_t *buf,
|
||||||
const nghttp2_priority_spec *pri_spec)
|
const nghttp2_priority_spec *pri_spec) {
|
||||||
{
|
|
||||||
nghttp2_put_uint32be(buf, pri_spec->stream_id);
|
nghttp2_put_uint32be(buf, pri_spec->stream_id);
|
||||||
if (pri_spec->exclusive) {
|
if (pri_spec->exclusive) {
|
||||||
buf[0] |= 0x80;
|
buf[0] |= 0x80;
|
||||||
@@ -402,10 +308,9 @@ void nghttp2_frame_pack_priority_spec(uint8_t *buf,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_frame_unpack_priority_spec(nghttp2_priority_spec *pri_spec,
|
void nghttp2_frame_unpack_priority_spec(nghttp2_priority_spec *pri_spec,
|
||||||
uint8_t flags,
|
uint8_t flags _U_,
|
||||||
const uint8_t *payload,
|
const uint8_t *payload,
|
||||||
size_t payloadlen)
|
size_t payloadlen _U_) {
|
||||||
{
|
|
||||||
int32_t dep_stream_id;
|
int32_t dep_stream_id;
|
||||||
uint8_t exclusive;
|
uint8_t exclusive;
|
||||||
int32_t weight;
|
int32_t weight;
|
||||||
@@ -419,8 +324,7 @@ void nghttp2_frame_unpack_priority_spec(nghttp2_priority_spec *pri_spec,
|
|||||||
|
|
||||||
int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame,
|
int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame,
|
||||||
const uint8_t *payload,
|
const uint8_t *payload,
|
||||||
size_t payloadlen)
|
size_t payloadlen) {
|
||||||
{
|
|
||||||
if (frame->hd.flags & NGHTTP2_FLAG_PRIORITY) {
|
if (frame->hd.flags & NGHTTP2_FLAG_PRIORITY) {
|
||||||
nghttp2_frame_unpack_priority_spec(&frame->pri_spec, frame->hd.flags,
|
nghttp2_frame_unpack_priority_spec(&frame->pri_spec, frame->hd.flags,
|
||||||
payload, payloadlen);
|
payload, payloadlen);
|
||||||
@@ -434,8 +338,7 @@ int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_frame_pack_priority(nghttp2_bufs *bufs, nghttp2_priority *frame)
|
int nghttp2_frame_pack_priority(nghttp2_bufs *bufs, nghttp2_priority *frame) {
|
||||||
{
|
|
||||||
nghttp2_buf *buf;
|
nghttp2_buf *buf;
|
||||||
|
|
||||||
assert(bufs->head == bufs->cur);
|
assert(bufs->head == bufs->cur);
|
||||||
@@ -457,15 +360,13 @@ int nghttp2_frame_pack_priority(nghttp2_bufs *bufs, nghttp2_priority *frame)
|
|||||||
|
|
||||||
void nghttp2_frame_unpack_priority_payload(nghttp2_priority *frame,
|
void nghttp2_frame_unpack_priority_payload(nghttp2_priority *frame,
|
||||||
const uint8_t *payload,
|
const uint8_t *payload,
|
||||||
size_t payloadlen)
|
size_t payloadlen) {
|
||||||
{
|
nghttp2_frame_unpack_priority_spec(&frame->pri_spec, frame->hd.flags, payload,
|
||||||
nghttp2_frame_unpack_priority_spec(&frame->pri_spec, frame->hd.flags,
|
payloadlen);
|
||||||
payload, payloadlen);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_frame_pack_rst_stream(nghttp2_bufs *bufs,
|
int nghttp2_frame_pack_rst_stream(nghttp2_bufs *bufs,
|
||||||
nghttp2_rst_stream *frame)
|
nghttp2_rst_stream *frame) {
|
||||||
{
|
|
||||||
nghttp2_buf *buf;
|
nghttp2_buf *buf;
|
||||||
|
|
||||||
assert(bufs->head == bufs->cur);
|
assert(bufs->head == bufs->cur);
|
||||||
@@ -486,13 +387,11 @@ int nghttp2_frame_pack_rst_stream(nghttp2_bufs *bufs,
|
|||||||
|
|
||||||
void nghttp2_frame_unpack_rst_stream_payload(nghttp2_rst_stream *frame,
|
void nghttp2_frame_unpack_rst_stream_payload(nghttp2_rst_stream *frame,
|
||||||
const uint8_t *payload,
|
const uint8_t *payload,
|
||||||
size_t payloadlen)
|
size_t payloadlen _U_) {
|
||||||
{
|
|
||||||
frame->error_code = nghttp2_get_uint32(payload);
|
frame->error_code = nghttp2_get_uint32(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_frame_pack_settings(nghttp2_bufs *bufs, nghttp2_settings *frame)
|
int nghttp2_frame_pack_settings(nghttp2_bufs *bufs, nghttp2_settings *frame) {
|
||||||
{
|
|
||||||
nghttp2_buf *buf;
|
nghttp2_buf *buf;
|
||||||
|
|
||||||
assert(bufs->head == bufs->cur);
|
assert(bufs->head == bufs->cur);
|
||||||
@@ -507,16 +406,15 @@ int nghttp2_frame_pack_settings(nghttp2_bufs *bufs, nghttp2_settings *frame)
|
|||||||
|
|
||||||
nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
|
nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
|
||||||
|
|
||||||
buf->last += nghttp2_frame_pack_settings_payload(buf->last,
|
buf->last +=
|
||||||
frame->iv, frame->niv);
|
nghttp2_frame_pack_settings_payload(buf->last, frame->iv, frame->niv);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t nghttp2_frame_pack_settings_payload(uint8_t *buf,
|
size_t nghttp2_frame_pack_settings_payload(uint8_t *buf,
|
||||||
const nghttp2_settings_entry *iv,
|
const nghttp2_settings_entry *iv,
|
||||||
size_t niv)
|
size_t niv) {
|
||||||
{
|
|
||||||
size_t i;
|
size_t i;
|
||||||
for (i = 0; i < niv; ++i, buf += NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) {
|
for (i = 0; i < niv; ++i, buf += NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) {
|
||||||
nghttp2_put_uint16be(buf, iv[i].settings_id);
|
nghttp2_put_uint16be(buf, iv[i].settings_id);
|
||||||
@@ -527,14 +425,13 @@ size_t nghttp2_frame_pack_settings_payload(uint8_t *buf,
|
|||||||
|
|
||||||
int nghttp2_frame_unpack_settings_payload(nghttp2_settings *frame,
|
int nghttp2_frame_unpack_settings_payload(nghttp2_settings *frame,
|
||||||
nghttp2_settings_entry *iv,
|
nghttp2_settings_entry *iv,
|
||||||
size_t niv)
|
size_t niv, nghttp2_mem *mem) {
|
||||||
{
|
|
||||||
size_t payloadlen = niv * sizeof(nghttp2_settings_entry);
|
size_t payloadlen = niv * sizeof(nghttp2_settings_entry);
|
||||||
|
|
||||||
if (niv == 0) {
|
if (niv == 0) {
|
||||||
frame->iv = NULL;
|
frame->iv = NULL;
|
||||||
} else {
|
} else {
|
||||||
frame->iv = malloc(payloadlen);
|
frame->iv = nghttp2_mem_malloc(mem, payloadlen);
|
||||||
|
|
||||||
if (frame->iv == NULL) {
|
if (frame->iv == NULL) {
|
||||||
return NGHTTP2_ERR_NOMEM;
|
return NGHTTP2_ERR_NOMEM;
|
||||||
@@ -548,8 +445,7 @@ int nghttp2_frame_unpack_settings_payload(nghttp2_settings *frame,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_frame_unpack_settings_entry(nghttp2_settings_entry *iv,
|
void nghttp2_frame_unpack_settings_entry(nghttp2_settings_entry *iv,
|
||||||
const uint8_t *payload)
|
const uint8_t *payload) {
|
||||||
{
|
|
||||||
iv->settings_id = nghttp2_get_uint16(&payload[0]);
|
iv->settings_id = nghttp2_get_uint16(&payload[0]);
|
||||||
iv->value = nghttp2_get_uint32(&payload[2]);
|
iv->value = nghttp2_get_uint32(&payload[2]);
|
||||||
}
|
}
|
||||||
@@ -557,8 +453,8 @@ void nghttp2_frame_unpack_settings_entry(nghttp2_settings_entry *iv,
|
|||||||
int nghttp2_frame_unpack_settings_payload2(nghttp2_settings_entry **iv_ptr,
|
int nghttp2_frame_unpack_settings_payload2(nghttp2_settings_entry **iv_ptr,
|
||||||
size_t *niv_ptr,
|
size_t *niv_ptr,
|
||||||
const uint8_t *payload,
|
const uint8_t *payload,
|
||||||
size_t payloadlen)
|
size_t payloadlen,
|
||||||
{
|
nghttp2_mem *mem) {
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
*niv_ptr = payloadlen / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH;
|
*niv_ptr = payloadlen / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH;
|
||||||
@@ -569,8 +465,8 @@ int nghttp2_frame_unpack_settings_payload2(nghttp2_settings_entry **iv_ptr,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*iv_ptr =
|
||||||
*iv_ptr = malloc((*niv_ptr)*sizeof(nghttp2_settings_entry));
|
nghttp2_mem_malloc(mem, (*niv_ptr) * sizeof(nghttp2_settings_entry));
|
||||||
|
|
||||||
if (*iv_ptr == NULL) {
|
if (*iv_ptr == NULL) {
|
||||||
return NGHTTP2_ERR_NOMEM;
|
return NGHTTP2_ERR_NOMEM;
|
||||||
@@ -586,8 +482,7 @@ int nghttp2_frame_unpack_settings_payload2(nghttp2_settings_entry **iv_ptr,
|
|||||||
|
|
||||||
int nghttp2_frame_pack_push_promise(nghttp2_bufs *bufs,
|
int nghttp2_frame_pack_push_promise(nghttp2_bufs *bufs,
|
||||||
nghttp2_push_promise *frame,
|
nghttp2_push_promise *frame,
|
||||||
nghttp2_hd_deflater *deflater)
|
nghttp2_hd_deflater *deflater) {
|
||||||
{
|
|
||||||
size_t nv_offset = 4;
|
size_t nv_offset = 4;
|
||||||
int rv;
|
int rv;
|
||||||
nghttp2_buf *buf;
|
nghttp2_buf *buf;
|
||||||
@@ -622,17 +517,15 @@ int nghttp2_frame_pack_push_promise(nghttp2_bufs *bufs,
|
|||||||
|
|
||||||
int nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame,
|
int nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame,
|
||||||
const uint8_t *payload,
|
const uint8_t *payload,
|
||||||
size_t payloadlen)
|
size_t payloadlen _U_) {
|
||||||
{
|
frame->promised_stream_id =
|
||||||
frame->promised_stream_id = nghttp2_get_uint32(payload) &
|
nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK;
|
||||||
NGHTTP2_STREAM_ID_MASK;
|
|
||||||
frame->nva = NULL;
|
frame->nva = NULL;
|
||||||
frame->nvlen = 0;
|
frame->nvlen = 0;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_frame_pack_ping(nghttp2_bufs *bufs, nghttp2_ping *frame)
|
int nghttp2_frame_pack_ping(nghttp2_bufs *bufs, nghttp2_ping *frame) {
|
||||||
{
|
|
||||||
nghttp2_buf *buf;
|
nghttp2_buf *buf;
|
||||||
|
|
||||||
assert(bufs->head == bufs->cur);
|
assert(bufs->head == bufs->cur);
|
||||||
@@ -645,21 +538,19 @@ int nghttp2_frame_pack_ping(nghttp2_bufs *bufs, nghttp2_ping *frame)
|
|||||||
|
|
||||||
nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
|
nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
|
||||||
|
|
||||||
buf->last = nghttp2_cpymem(buf->last, frame->opaque_data,
|
buf->last =
|
||||||
sizeof(frame->opaque_data));
|
nghttp2_cpymem(buf->last, frame->opaque_data, sizeof(frame->opaque_data));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_frame_unpack_ping_payload(nghttp2_ping *frame,
|
void nghttp2_frame_unpack_ping_payload(nghttp2_ping *frame,
|
||||||
const uint8_t *payload,
|
const uint8_t *payload,
|
||||||
size_t payloadlen)
|
size_t payloadlen _U_) {
|
||||||
{
|
|
||||||
memcpy(frame->opaque_data, payload, sizeof(frame->opaque_data));
|
memcpy(frame->opaque_data, payload, sizeof(frame->opaque_data));
|
||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_frame_pack_goaway(nghttp2_bufs *bufs, nghttp2_goaway *frame)
|
int nghttp2_frame_pack_goaway(nghttp2_bufs *bufs, nghttp2_goaway *frame) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
nghttp2_buf *buf;
|
nghttp2_buf *buf;
|
||||||
|
|
||||||
@@ -692,10 +583,9 @@ int nghttp2_frame_pack_goaway(nghttp2_bufs *bufs, nghttp2_goaway *frame)
|
|||||||
|
|
||||||
void nghttp2_frame_unpack_goaway_payload(nghttp2_goaway *frame,
|
void nghttp2_frame_unpack_goaway_payload(nghttp2_goaway *frame,
|
||||||
const uint8_t *payload,
|
const uint8_t *payload,
|
||||||
size_t payloadlen,
|
size_t payloadlen _U_,
|
||||||
uint8_t *var_gift_payload,
|
uint8_t *var_gift_payload,
|
||||||
size_t var_gift_payloadlen)
|
size_t var_gift_payloadlen) {
|
||||||
{
|
|
||||||
frame->last_stream_id = nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK;
|
frame->last_stream_id = nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK;
|
||||||
frame->error_code = nghttp2_get_uint32(payload + 4);
|
frame->error_code = nghttp2_get_uint32(payload + 4);
|
||||||
|
|
||||||
@@ -705,8 +595,7 @@ void nghttp2_frame_unpack_goaway_payload(nghttp2_goaway *frame,
|
|||||||
|
|
||||||
int nghttp2_frame_unpack_goaway_payload2(nghttp2_goaway *frame,
|
int nghttp2_frame_unpack_goaway_payload2(nghttp2_goaway *frame,
|
||||||
const uint8_t *payload,
|
const uint8_t *payload,
|
||||||
size_t payloadlen)
|
size_t payloadlen, nghttp2_mem *mem) {
|
||||||
{
|
|
||||||
uint8_t *var_gift_payload;
|
uint8_t *var_gift_payload;
|
||||||
size_t var_gift_payloadlen;
|
size_t var_gift_payloadlen;
|
||||||
|
|
||||||
@@ -721,7 +610,7 @@ int nghttp2_frame_unpack_goaway_payload2(nghttp2_goaway *frame,
|
|||||||
if (!var_gift_payloadlen) {
|
if (!var_gift_payloadlen) {
|
||||||
var_gift_payload = NULL;
|
var_gift_payload = NULL;
|
||||||
} else {
|
} else {
|
||||||
var_gift_payload = malloc(var_gift_payloadlen);
|
var_gift_payload = nghttp2_mem_malloc(mem, var_gift_payloadlen);
|
||||||
|
|
||||||
if (var_gift_payload == NULL) {
|
if (var_gift_payload == NULL) {
|
||||||
return NGHTTP2_ERR_NOMEM;
|
return NGHTTP2_ERR_NOMEM;
|
||||||
@@ -737,8 +626,7 @@ int nghttp2_frame_unpack_goaway_payload2(nghttp2_goaway *frame,
|
|||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_frame_pack_window_update(nghttp2_bufs *bufs,
|
int nghttp2_frame_pack_window_update(nghttp2_bufs *bufs,
|
||||||
nghttp2_window_update *frame)
|
nghttp2_window_update *frame) {
|
||||||
{
|
|
||||||
nghttp2_buf *buf;
|
nghttp2_buf *buf;
|
||||||
|
|
||||||
assert(bufs->head == bufs->cur);
|
assert(bufs->head == bufs->cur);
|
||||||
@@ -759,117 +647,13 @@ int nghttp2_frame_pack_window_update(nghttp2_bufs *bufs,
|
|||||||
|
|
||||||
void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame,
|
void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame,
|
||||||
const uint8_t *payload,
|
const uint8_t *payload,
|
||||||
size_t payloadlen)
|
size_t payloadlen _U_) {
|
||||||
{
|
frame->window_size_increment =
|
||||||
frame->window_size_increment = nghttp2_get_uint32(payload) &
|
nghttp2_get_uint32(payload) & NGHTTP2_WINDOW_SIZE_INCREMENT_MASK;
|
||||||
NGHTTP2_WINDOW_SIZE_INCREMENT_MASK;
|
|
||||||
}
|
|
||||||
|
|
||||||
int nghttp2_frame_pack_altsvc(nghttp2_bufs *bufs, nghttp2_extension *frame)
|
|
||||||
{
|
|
||||||
int rv;
|
|
||||||
nghttp2_buf *buf;
|
|
||||||
nghttp2_ext_altsvc *altsvc;
|
|
||||||
|
|
||||||
assert(bufs->head == bufs->cur);
|
|
||||||
|
|
||||||
altsvc = frame->payload;
|
|
||||||
|
|
||||||
buf = &bufs->head->buf;
|
|
||||||
|
|
||||||
buf->pos -= NGHTTP2_FRAME_HDLEN;
|
|
||||||
|
|
||||||
nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
|
|
||||||
|
|
||||||
nghttp2_put_uint32be(buf->last, altsvc->max_age);
|
|
||||||
buf->last += 4;
|
|
||||||
|
|
||||||
nghttp2_put_uint16be(buf->last, altsvc->port);
|
|
||||||
buf->last += 2;
|
|
||||||
|
|
||||||
buf->last[0] = altsvc->protocol_id_len;
|
|
||||||
++buf->last;
|
|
||||||
|
|
||||||
rv = nghttp2_bufs_add(bufs, altsvc->protocol_id, altsvc->protocol_id_len);
|
|
||||||
if(rv != 0) {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = nghttp2_bufs_addb(bufs, altsvc->host_len);
|
|
||||||
if(rv != 0) {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = nghttp2_bufs_add(bufs, altsvc->host, altsvc->host_len);
|
|
||||||
if(rv != 0) {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = nghttp2_bufs_add(bufs, altsvc->origin, altsvc->origin_len);
|
|
||||||
if(rv != 0) {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
fail:
|
|
||||||
|
|
||||||
if(rv == NGHTTP2_ERR_BUFFER_ERROR) {
|
|
||||||
return NGHTTP2_ERR_FRAME_SIZE_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
int nghttp2_frame_unpack_altsvc_payload(nghttp2_extension *frame,
|
|
||||||
const uint8_t *payload,
|
|
||||||
size_t payloadlen,
|
|
||||||
uint8_t *var_gift_payload,
|
|
||||||
size_t var_gift_payloadlen)
|
|
||||||
{
|
|
||||||
nghttp2_buf buf;
|
|
||||||
nghttp2_ext_altsvc *altsvc;
|
|
||||||
|
|
||||||
altsvc = frame->payload;
|
|
||||||
|
|
||||||
altsvc->max_age = nghttp2_get_uint32(payload);
|
|
||||||
payload += 4;
|
|
||||||
|
|
||||||
altsvc->port = nghttp2_get_uint16(payload);
|
|
||||||
payload += 2;
|
|
||||||
|
|
||||||
altsvc->protocol_id_len = *payload;
|
|
||||||
|
|
||||||
nghttp2_buf_wrap_init(&buf, var_gift_payload, var_gift_payloadlen);
|
|
||||||
buf.last += var_gift_payloadlen;
|
|
||||||
|
|
||||||
/* 1 for Host-Len */
|
|
||||||
if(nghttp2_buf_len(&buf) < 1 + (ssize_t)altsvc->protocol_id_len) {
|
|
||||||
return NGHTTP2_ERR_FRAME_SIZE_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
altsvc->protocol_id = buf.pos;
|
|
||||||
buf.pos += altsvc->protocol_id_len;
|
|
||||||
|
|
||||||
altsvc->host_len = *buf.pos;
|
|
||||||
++buf.pos;
|
|
||||||
|
|
||||||
if(nghttp2_buf_len(&buf) < (ssize_t)altsvc->host_len) {
|
|
||||||
return NGHTTP2_ERR_FRAME_SIZE_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
altsvc->host = buf.pos;
|
|
||||||
buf.pos += altsvc->host_len;
|
|
||||||
|
|
||||||
altsvc->origin = buf.pos;
|
|
||||||
altsvc->origin_len = nghttp2_buf_len(&buf);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nghttp2_settings_entry *nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv,
|
nghttp2_settings_entry *nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv,
|
||||||
size_t niv)
|
size_t niv, nghttp2_mem *mem) {
|
||||||
{
|
|
||||||
nghttp2_settings_entry *iv_copy;
|
nghttp2_settings_entry *iv_copy;
|
||||||
size_t len = niv * sizeof(nghttp2_settings_entry);
|
size_t len = niv * sizeof(nghttp2_settings_entry);
|
||||||
|
|
||||||
@@ -877,7 +661,7 @@ nghttp2_settings_entry* nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
iv_copy = malloc(len);
|
iv_copy = nghttp2_mem_malloc(mem, len);
|
||||||
|
|
||||||
if (iv_copy == NULL) {
|
if (iv_copy == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -888,21 +672,18 @@ nghttp2_settings_entry* nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv,
|
|||||||
return iv_copy;
|
return iv_copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_nv_equal(const nghttp2_nv *a, const nghttp2_nv *b)
|
int nghttp2_nv_equal(const nghttp2_nv *a, const nghttp2_nv *b) {
|
||||||
{
|
|
||||||
return a->namelen == b->namelen && a->valuelen == b->valuelen &&
|
return a->namelen == b->namelen && a->valuelen == b->valuelen &&
|
||||||
memcmp(a->name, b->name, a->namelen) == 0 &&
|
memcmp(a->name, b->name, a->namelen) == 0 &&
|
||||||
memcmp(a->value, b->value, a->valuelen) == 0;
|
memcmp(a->value, b->value, a->valuelen) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_nv_array_del(nghttp2_nv *nva)
|
void nghttp2_nv_array_del(nghttp2_nv *nva, nghttp2_mem *mem) {
|
||||||
{
|
nghttp2_mem_free(mem, nva);
|
||||||
free(nva);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bytes_compar(const uint8_t *a, size_t alen,
|
static int bytes_compar(const uint8_t *a, size_t alen, const uint8_t *b,
|
||||||
const uint8_t *b, size_t blen)
|
size_t blen) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
if (alen == blen) {
|
if (alen == blen) {
|
||||||
@@ -928,13 +709,11 @@ static int bytes_compar(const uint8_t *a, size_t alen,
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_nv_compare_name(const nghttp2_nv *lhs, const nghttp2_nv *rhs)
|
int nghttp2_nv_compare_name(const nghttp2_nv *lhs, const nghttp2_nv *rhs) {
|
||||||
{
|
|
||||||
return bytes_compar(lhs->name, lhs->namelen, rhs->name, rhs->namelen);
|
return bytes_compar(lhs->name, lhs->namelen, rhs->name, rhs->namelen);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nv_compar(const void *lhs, const void *rhs)
|
static int nv_compar(const void *lhs, const void *rhs) {
|
||||||
{
|
|
||||||
const nghttp2_nv *a = (const nghttp2_nv *)lhs;
|
const nghttp2_nv *a = (const nghttp2_nv *)lhs;
|
||||||
const nghttp2_nv *b = (const nghttp2_nv *)rhs;
|
const nghttp2_nv *b = (const nghttp2_nv *)rhs;
|
||||||
int rv;
|
int rv;
|
||||||
@@ -948,14 +727,12 @@ static int nv_compar(const void *lhs, const void *rhs)
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_nv_array_sort(nghttp2_nv *nva, size_t nvlen)
|
void nghttp2_nv_array_sort(nghttp2_nv *nva, size_t nvlen) {
|
||||||
{
|
|
||||||
qsort(nva, nvlen, sizeof(nghttp2_nv), nv_compar);
|
qsort(nva, nvlen, sizeof(nghttp2_nv), nv_compar);
|
||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr,
|
int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, const nghttp2_nv *nva,
|
||||||
const nghttp2_nv *nva, size_t nvlen)
|
size_t nvlen, nghttp2_mem *mem) {
|
||||||
{
|
|
||||||
size_t i;
|
size_t i;
|
||||||
uint8_t *data;
|
uint8_t *data;
|
||||||
size_t buflen = 0;
|
size_t buflen = 0;
|
||||||
@@ -973,7 +750,7 @@ int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr,
|
|||||||
|
|
||||||
buflen += sizeof(nghttp2_nv) * nvlen;
|
buflen += sizeof(nghttp2_nv) * nvlen;
|
||||||
|
|
||||||
*nva_ptr = malloc(buflen);
|
*nva_ptr = nghttp2_mem_malloc(mem, buflen);
|
||||||
|
|
||||||
if (*nva_ptr == NULL) {
|
if (*nva_ptr == NULL) {
|
||||||
return NGHTTP2_ERR_NOMEM;
|
return NGHTTP2_ERR_NOMEM;
|
||||||
@@ -999,8 +776,7 @@ int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv)
|
int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv) {
|
||||||
{
|
|
||||||
size_t i;
|
size_t i;
|
||||||
for (i = 0; i < niv; ++i) {
|
for (i = 0; i < niv; ++i) {
|
||||||
switch (iv[i].settings_id) {
|
switch (iv[i].settings_id) {
|
||||||
@@ -1034,8 +810,7 @@ int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void frame_set_pad(nghttp2_buf *buf, size_t padlen)
|
static void frame_set_pad(nghttp2_buf *buf, size_t padlen) {
|
||||||
{
|
|
||||||
size_t trail_padlen;
|
size_t trail_padlen;
|
||||||
size_t newlen;
|
size_t newlen;
|
||||||
|
|
||||||
@@ -1063,8 +838,7 @@ static void frame_set_pad(nghttp2_buf *buf, size_t padlen)
|
|||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd,
|
int nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd,
|
||||||
size_t padlen)
|
size_t padlen) {
|
||||||
{
|
|
||||||
nghttp2_buf *buf;
|
nghttp2_buf *buf;
|
||||||
|
|
||||||
if (padlen == 0) {
|
if (padlen == 0) {
|
||||||
@@ -1102,8 +876,8 @@ int nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd,
|
|||||||
hd->length += padlen;
|
hd->length += padlen;
|
||||||
hd->flags |= NGHTTP2_FLAG_PADDED;
|
hd->flags |= NGHTTP2_FLAG_PADDED;
|
||||||
|
|
||||||
DEBUGF(fprintf(stderr, "send: final payloadlen=%zu, padlen=%zu\n",
|
DEBUGF(fprintf(stderr, "send: final payloadlen=%zu, padlen=%zu\n", hd->length,
|
||||||
hd->length, padlen));
|
padlen));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,8 @@
|
|||||||
|
|
||||||
/* The default length of DATA frame payload. This should be small enough
|
/* The default length of DATA frame payload. This should be small enough
|
||||||
* for the data payload and the header to fit into 1 TLS record */
|
* for the data payload and the header to fit into 1 TLS record */
|
||||||
#define NGHTTP2_DATA_PAYLOADLEN ((NGHTTP2_MAX_FRAME_SIZE_MIN) - (NGHTTP2_FRAME_HDLEN))
|
#define NGHTTP2_DATA_PAYLOADLEN \
|
||||||
|
((NGHTTP2_MAX_FRAME_SIZE_MIN) - (NGHTTP2_FRAME_HDLEN))
|
||||||
|
|
||||||
/* Maximum headers payload length, calculated in compressed form.
|
/* Maximum headers payload length, calculated in compressed form.
|
||||||
This applies to transmission only. */
|
This applies to transmission only. */
|
||||||
@@ -72,56 +73,11 @@
|
|||||||
/* Length of priority related fields in HEADERS/PRIORITY frames */
|
/* Length of priority related fields in HEADERS/PRIORITY frames */
|
||||||
#define NGHTTP2_PRIORITY_SPECLEN 5
|
#define NGHTTP2_PRIORITY_SPECLEN 5
|
||||||
|
|
||||||
/* Length of fixed part in ALTSVC frame, that is the sum of fields of
|
|
||||||
Max-Age, Port and Proto-Len. */
|
|
||||||
#define NGHTTP2_ALTSVC_FIXED_PARTLEN 7
|
|
||||||
|
|
||||||
/* Minimum length of ALTSVC extension frame payload.
|
|
||||||
NGHTTP2_ALTSVC_FIXED_PARTLEN + Host-Len. */
|
|
||||||
#define NGHTTP2_ALTSVC_MINLEN 8
|
|
||||||
|
|
||||||
/* Maximum length of padding in bytes. */
|
/* Maximum length of padding in bytes. */
|
||||||
#define NGHTTP2_MAX_PADLEN 256
|
#define NGHTTP2_MAX_PADLEN 256
|
||||||
|
|
||||||
/* Category of frames. */
|
|
||||||
typedef enum {
|
|
||||||
/* non-DATA frame */
|
|
||||||
NGHTTP2_CAT_CTRL,
|
|
||||||
/* DATA frame */
|
|
||||||
NGHTTP2_CAT_DATA
|
|
||||||
} nghttp2_frame_category;
|
|
||||||
|
|
||||||
/* Union of extension frame payload */
|
/* Union of extension frame payload */
|
||||||
typedef union {
|
typedef union { nghttp2_ext_altsvc altsvc; } nghttp2_ext_frame_payload;
|
||||||
nghttp2_ext_altsvc altsvc;
|
|
||||||
} nghttp2_ext_frame_payload;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @struct
|
|
||||||
*
|
|
||||||
* The DATA frame used in the library privately. It has the following
|
|
||||||
* members:
|
|
||||||
*/
|
|
||||||
typedef struct {
|
|
||||||
nghttp2_frame_hd hd;
|
|
||||||
/**
|
|
||||||
* The data to be sent for this DATA frame.
|
|
||||||
*/
|
|
||||||
nghttp2_data_provider data_prd;
|
|
||||||
/**
|
|
||||||
* The number of bytes added as padding. This includes Pad Length
|
|
||||||
* field (1 byte).
|
|
||||||
*/
|
|
||||||
size_t padlen;
|
|
||||||
/**
|
|
||||||
* The flag to indicate whether EOF was reached or not. Initially
|
|
||||||
* |eof| is 0. It becomes 1 after all data were read. This is used
|
|
||||||
* exclusively by nghttp2 library and not in the spec.
|
|
||||||
*/
|
|
||||||
uint8_t eof;
|
|
||||||
} nghttp2_private_data;
|
|
||||||
|
|
||||||
int nghttp2_frame_is_data_frame(uint8_t *head);
|
|
||||||
|
|
||||||
void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd);
|
void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd);
|
||||||
|
|
||||||
@@ -131,9 +87,8 @@ void nghttp2_frame_unpack_frame_hd(nghttp2_frame_hd *hd, const uint8_t* buf);
|
|||||||
* Initializes frame header |hd| with given parameters. Reserved bit
|
* Initializes frame header |hd| with given parameters. Reserved bit
|
||||||
* is set to 0.
|
* is set to 0.
|
||||||
*/
|
*/
|
||||||
void nghttp2_frame_hd_init(nghttp2_frame_hd *hd, size_t length,
|
void nghttp2_frame_hd_init(nghttp2_frame_hd *hd, size_t length, uint8_t type,
|
||||||
uint8_t type, uint8_t flags,
|
uint8_t flags, int32_t stream_id);
|
||||||
int32_t stream_id);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the number of priority field depending on the |flags|. If
|
* Returns the number of priority field depending on the |flags|. If
|
||||||
@@ -156,8 +111,7 @@ void nghttp2_frame_pack_priority_spec(uint8_t *buf,
|
|||||||
* assumes the |payload| contains whole priority specification.
|
* assumes the |payload| contains whole priority specification.
|
||||||
*/
|
*/
|
||||||
void nghttp2_frame_unpack_priority_spec(nghttp2_priority_spec *pri_spec,
|
void nghttp2_frame_unpack_priority_spec(nghttp2_priority_spec *pri_spec,
|
||||||
uint8_t flags,
|
uint8_t flags, const uint8_t *payload,
|
||||||
const uint8_t *payload,
|
|
||||||
size_t payloadlen);
|
size_t payloadlen);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -186,8 +140,7 @@ size_t nghttp2_frame_headers_payload_nv_offset(nghttp2_headers *frame);
|
|||||||
* NGHTTP2_ERR_NOMEM
|
* NGHTTP2_ERR_NOMEM
|
||||||
* Out of memory.
|
* Out of memory.
|
||||||
*/
|
*/
|
||||||
int nghttp2_frame_pack_headers(nghttp2_bufs *bufs,
|
int nghttp2_frame_pack_headers(nghttp2_bufs *bufs, nghttp2_headers *frame,
|
||||||
nghttp2_headers *frame,
|
|
||||||
nghttp2_hd_deflater *deflater);
|
nghttp2_hd_deflater *deflater);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -210,8 +163,7 @@ int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame,
|
|||||||
*
|
*
|
||||||
* This function always succeeds and returns 0.
|
* This function always succeeds and returns 0.
|
||||||
*/
|
*/
|
||||||
int nghttp2_frame_pack_priority(nghttp2_bufs *bufs,
|
int nghttp2_frame_pack_priority(nghttp2_bufs *bufs, nghttp2_priority *frame);
|
||||||
nghttp2_priority *frame);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Unpacks PRIORITY wire format into |frame|.
|
* Unpacks PRIORITY wire format into |frame|.
|
||||||
@@ -279,7 +231,7 @@ void nghttp2_frame_unpack_settings_entry(nghttp2_settings_entry *iv,
|
|||||||
*/
|
*/
|
||||||
int nghttp2_frame_unpack_settings_payload(nghttp2_settings *frame,
|
int nghttp2_frame_unpack_settings_payload(nghttp2_settings *frame,
|
||||||
nghttp2_settings_entry *iv,
|
nghttp2_settings_entry *iv,
|
||||||
size_t niv);
|
size_t niv, nghttp2_mem *mem);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Unpacks SETTINGS payload into |*iv_ptr|. The number of entries are
|
* Unpacks SETTINGS payload into |*iv_ptr|. The number of entries are
|
||||||
@@ -296,7 +248,7 @@ int nghttp2_frame_unpack_settings_payload(nghttp2_settings *frame,
|
|||||||
int nghttp2_frame_unpack_settings_payload2(nghttp2_settings_entry **iv_ptr,
|
int nghttp2_frame_unpack_settings_payload2(nghttp2_settings_entry **iv_ptr,
|
||||||
size_t *niv_ptr,
|
size_t *niv_ptr,
|
||||||
const uint8_t *payload,
|
const uint8_t *payload,
|
||||||
size_t payloadlen);
|
size_t payloadlen, nghttp2_mem *mem);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Packs PUSH_PROMISE frame |frame| in wire format and store it in
|
* Packs PUSH_PROMISE frame |frame| in wire format and store it in
|
||||||
@@ -399,7 +351,7 @@ void nghttp2_frame_unpack_goaway_payload(nghttp2_goaway *frame,
|
|||||||
*/
|
*/
|
||||||
int nghttp2_frame_unpack_goaway_payload2(nghttp2_goaway *frame,
|
int nghttp2_frame_unpack_goaway_payload2(nghttp2_goaway *frame,
|
||||||
const uint8_t *payload,
|
const uint8_t *payload,
|
||||||
size_t payloadlen);
|
size_t payloadlen, nghttp2_mem *mem);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Packs WINDOW_UPDATE frame |frame| in wire frame format and store it
|
* Packs WINDOW_UPDATE frame |frame| in wire frame format and store it
|
||||||
@@ -420,71 +372,24 @@ void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame,
|
|||||||
const uint8_t *payload,
|
const uint8_t *payload,
|
||||||
size_t payloadlen);
|
size_t payloadlen);
|
||||||
|
|
||||||
/*
|
|
||||||
* Packs ALTSVC frame |frame| in wire format and store it in |bufs|.
|
|
||||||
* This function expands |bufs| as necessary to store frame.
|
|
||||||
*
|
|
||||||
* The caller must make sure that nghttp2_bufs_reset(bufs) is called
|
|
||||||
* before calling this function.
|
|
||||||
*
|
|
||||||
* The caller must make sure that frame->payload points to
|
|
||||||
* nghttp2_ext_altsvc object.
|
|
||||||
*
|
|
||||||
* This function returns 0 if it succeeds or one of the following
|
|
||||||
* negative error codes:
|
|
||||||
*
|
|
||||||
* NGHTTP2_ERR_NOMEM
|
|
||||||
* Out of memory.
|
|
||||||
* NGHTTP2_ERR_FRAME_SIZE_ERROR
|
|
||||||
* The length of the frame is too large.
|
|
||||||
*/
|
|
||||||
int nghttp2_frame_pack_altsvc(nghttp2_bufs *bufs, nghttp2_extension *frame);
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Unpacks ALTSVC frame byte sequence into |frame|.
|
|
||||||
* The |payload| of length |payloadlen| contains first 8 bytes of
|
|
||||||
* payload. The |var_gift_payload| of length |var_gift_payloadlen|
|
|
||||||
* contains remaining payload and its buffer is gifted to the function
|
|
||||||
* and then |frame|. The |var_gift_payloadlen| must be freed by
|
|
||||||
* nghttp2_frame_altsvc_free().
|
|
||||||
*
|
|
||||||
* The caller must make sure that frame->payload points to
|
|
||||||
* nghttp2_ext_altsvc object.
|
|
||||||
*
|
|
||||||
* This function returns 0 if it succeeds or one of the following
|
|
||||||
* negative error codes:
|
|
||||||
*
|
|
||||||
* NGHTTP2_ERR_FRAME_SIZE_ERROR
|
|
||||||
* The |var_gift_payload| does not contain required data.
|
|
||||||
*/
|
|
||||||
int nghttp2_frame_unpack_altsvc_payload(nghttp2_extension *frame,
|
|
||||||
const uint8_t *payload,
|
|
||||||
size_t payloadlen,
|
|
||||||
uint8_t *var_gift_payload,
|
|
||||||
size_t var_gift_payloadlen);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initializes HEADERS frame |frame| with given values. |frame| takes
|
* Initializes HEADERS frame |frame| with given values. |frame| takes
|
||||||
* ownership of |nva|, so caller must not free it. If |stream_id| is
|
* ownership of |nva|, so caller must not free it. If |stream_id| is
|
||||||
* not assigned yet, it must be -1.
|
* not assigned yet, it must be -1.
|
||||||
*/
|
*/
|
||||||
void nghttp2_frame_headers_init(nghttp2_headers *frame,
|
void nghttp2_frame_headers_init(nghttp2_headers *frame, uint8_t flags,
|
||||||
uint8_t flags, int32_t stream_id,
|
int32_t stream_id, nghttp2_headers_category cat,
|
||||||
nghttp2_headers_category cat,
|
|
||||||
const nghttp2_priority_spec *pri_spec,
|
const nghttp2_priority_spec *pri_spec,
|
||||||
nghttp2_nv *nva, size_t nvlen);
|
nghttp2_nv *nva, size_t nvlen);
|
||||||
|
|
||||||
void nghttp2_frame_headers_free(nghttp2_headers *frame);
|
void nghttp2_frame_headers_free(nghttp2_headers *frame, nghttp2_mem *mem);
|
||||||
|
|
||||||
|
|
||||||
void nghttp2_frame_priority_init(nghttp2_priority *frame, int32_t stream_id,
|
void nghttp2_frame_priority_init(nghttp2_priority *frame, int32_t stream_id,
|
||||||
const nghttp2_priority_spec *pri_spec);
|
const nghttp2_priority_spec *pri_spec);
|
||||||
|
|
||||||
void nghttp2_frame_priority_free(nghttp2_priority *frame);
|
void nghttp2_frame_priority_free(nghttp2_priority *frame);
|
||||||
|
|
||||||
void nghttp2_frame_rst_stream_init(nghttp2_rst_stream *frame,
|
void nghttp2_frame_rst_stream_init(nghttp2_rst_stream *frame, int32_t stream_id,
|
||||||
int32_t stream_id,
|
|
||||||
uint32_t error_code);
|
uint32_t error_code);
|
||||||
|
|
||||||
void nghttp2_frame_rst_stream_free(nghttp2_rst_stream *frame);
|
void nghttp2_frame_rst_stream_free(nghttp2_rst_stream *frame);
|
||||||
@@ -493,12 +398,13 @@ void nghttp2_frame_rst_stream_free(nghttp2_rst_stream *frame);
|
|||||||
* Initializes PUSH_PROMISE frame |frame| with given values. |frame|
|
* Initializes PUSH_PROMISE frame |frame| with given values. |frame|
|
||||||
* takes ownership of |nva|, so caller must not free it.
|
* takes ownership of |nva|, so caller must not free it.
|
||||||
*/
|
*/
|
||||||
void nghttp2_frame_push_promise_init(nghttp2_push_promise *frame,
|
void nghttp2_frame_push_promise_init(nghttp2_push_promise *frame, uint8_t flags,
|
||||||
uint8_t flags, int32_t stream_id,
|
int32_t stream_id,
|
||||||
int32_t promised_stream_id,
|
int32_t promised_stream_id,
|
||||||
nghttp2_nv *nva, size_t nvlen);
|
nghttp2_nv *nva, size_t nvlen);
|
||||||
|
|
||||||
void nghttp2_frame_push_promise_free(nghttp2_push_promise *frame);
|
void nghttp2_frame_push_promise_free(nghttp2_push_promise *frame,
|
||||||
|
nghttp2_mem *mem);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initializes SETTINGS frame |frame| with given values. |frame| takes
|
* Initializes SETTINGS frame |frame| with given values. |frame| takes
|
||||||
@@ -508,7 +414,7 @@ void nghttp2_frame_push_promise_free(nghttp2_push_promise *frame);
|
|||||||
void nghttp2_frame_settings_init(nghttp2_settings *frame, uint8_t flags,
|
void nghttp2_frame_settings_init(nghttp2_settings *frame, uint8_t flags,
|
||||||
nghttp2_settings_entry *iv, size_t niv);
|
nghttp2_settings_entry *iv, size_t niv);
|
||||||
|
|
||||||
void nghttp2_frame_settings_free(nghttp2_settings *frame);
|
void nghttp2_frame_settings_free(nghttp2_settings *frame, nghttp2_mem *mem);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initializes PING frame |frame| with given values. If the
|
* Initializes PING frame |frame| with given values. If the
|
||||||
@@ -527,40 +433,17 @@ void nghttp2_frame_ping_free(nghttp2_ping *frame);
|
|||||||
* free it. If the |opaque_data_len| is 0, opaque_data could be NULL.
|
* free it. If the |opaque_data_len| is 0, opaque_data could be NULL.
|
||||||
*/
|
*/
|
||||||
void nghttp2_frame_goaway_init(nghttp2_goaway *frame, int32_t last_stream_id,
|
void nghttp2_frame_goaway_init(nghttp2_goaway *frame, int32_t last_stream_id,
|
||||||
uint32_t error_code,
|
uint32_t error_code, uint8_t *opaque_data,
|
||||||
uint8_t *opaque_data, size_t opaque_data_len);
|
size_t opaque_data_len);
|
||||||
|
|
||||||
void nghttp2_frame_goaway_free(nghttp2_goaway *frame);
|
void nghttp2_frame_goaway_free(nghttp2_goaway *frame, nghttp2_mem *mem);
|
||||||
|
|
||||||
void nghttp2_frame_window_update_init(nghttp2_window_update *frame,
|
void nghttp2_frame_window_update_init(nghttp2_window_update *frame,
|
||||||
uint8_t flags,
|
uint8_t flags, int32_t stream_id,
|
||||||
int32_t stream_id,
|
|
||||||
int32_t window_size_increment);
|
int32_t window_size_increment);
|
||||||
|
|
||||||
void nghttp2_frame_window_update_free(nghttp2_window_update *frame);
|
void nghttp2_frame_window_update_free(nghttp2_window_update *frame);
|
||||||
|
|
||||||
/* protocol_id, host and origin must be allocated to the one chunk of
|
|
||||||
memory region and protocol_id must point to it. We only free
|
|
||||||
protocol_id. This means that |protocol_id| is not NULL even if
|
|
||||||
|protocol_id_len| == 0 and |host_len| + |origin_len| > 0. If
|
|
||||||
|protocol_id_len|, |host_len| and |origin_len| are all zero,
|
|
||||||
|protocol_id| can be NULL. */
|
|
||||||
void nghttp2_frame_altsvc_init(nghttp2_extension *frame, int32_t stream_id,
|
|
||||||
uint32_t max_age,
|
|
||||||
uint16_t port,
|
|
||||||
uint8_t *protocol_id,
|
|
||||||
size_t protocol_id_len,
|
|
||||||
uint8_t *host, size_t host_len,
|
|
||||||
uint8_t *origin, size_t origin_len);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Frees resources used by |frame|. This function does not free
|
|
||||||
* frame->payload itself.
|
|
||||||
*/
|
|
||||||
void nghttp2_frame_altsvc_free(nghttp2_extension *frame);
|
|
||||||
|
|
||||||
void nghttp2_frame_data_init(nghttp2_data *frame, nghttp2_private_data *pdata);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns the number of padding bytes after payload. The total
|
* Returns the number of padding bytes after payload. The total
|
||||||
* padding length is given in the |padlen|. The returned value does
|
* padding length is given in the |padlen|. The returned value does
|
||||||
@@ -568,12 +451,10 @@ void nghttp2_frame_data_init(nghttp2_data *frame, nghttp2_private_data *pdata);
|
|||||||
*/
|
*/
|
||||||
size_t nghttp2_frame_trail_padlen(nghttp2_frame *frame, size_t padlen);
|
size_t nghttp2_frame_trail_padlen(nghttp2_frame *frame, size_t padlen);
|
||||||
|
|
||||||
void nghttp2_frame_private_data_init(nghttp2_private_data *frame,
|
void nghttp2_frame_data_init(nghttp2_data *frame, uint8_t flags,
|
||||||
uint8_t flags,
|
int32_t stream_id);
|
||||||
int32_t stream_id,
|
|
||||||
const nghttp2_data_provider *data_prd);
|
|
||||||
|
|
||||||
void nghttp2_frame_private_data_free(nghttp2_private_data *frame);
|
void nghttp2_frame_data_free(nghttp2_data *frame);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Makes copy of |iv| and return the copy. The |niv| is the number of
|
* Makes copy of |iv| and return the copy. The |niv| is the number of
|
||||||
@@ -581,7 +462,7 @@ void nghttp2_frame_private_data_free(nghttp2_private_data *frame);
|
|||||||
* it succeeds, or NULL.
|
* it succeeds, or NULL.
|
||||||
*/
|
*/
|
||||||
nghttp2_settings_entry *nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv,
|
nghttp2_settings_entry *nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv,
|
||||||
size_t niv);
|
size_t niv, nghttp2_mem *mem);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Sorts the |nva| in ascending order of name and value. If names are
|
* Sorts the |nva| in ascending order of name and value. If names are
|
||||||
@@ -602,8 +483,8 @@ void nghttp2_nv_array_sort(nghttp2_nv *nva, size_t nvlen);
|
|||||||
* NGHTTP2_ERR_NOMEM
|
* NGHTTP2_ERR_NOMEM
|
||||||
* Out of memory.
|
* Out of memory.
|
||||||
*/
|
*/
|
||||||
int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr,
|
int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, const nghttp2_nv *nva,
|
||||||
const nghttp2_nv *nva, size_t nvlen);
|
size_t nvlen, nghttp2_mem *mem);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns nonzero if the name/value pair |a| equals to |b|. The name
|
* Returns nonzero if the name/value pair |a| equals to |b|. The name
|
||||||
@@ -615,7 +496,7 @@ int nghttp2_nv_equal(const nghttp2_nv *a, const nghttp2_nv *b);
|
|||||||
/*
|
/*
|
||||||
* Frees |nva|.
|
* Frees |nva|.
|
||||||
*/
|
*/
|
||||||
void nghttp2_nv_array_del(nghttp2_nv *nva);
|
void nghttp2_nv_array_del(nghttp2_nv *nva, nghttp2_mem *mem);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Checks that the |iv|, which includes |niv| entries, does not have
|
* Checks that the |iv|, which includes |niv| entries, does not have
|
||||||
|
|||||||
791
lib/nghttp2_hd.c
791
lib/nghttp2_hd.c
File diff suppressed because it is too large
Load Diff
@@ -33,6 +33,7 @@
|
|||||||
|
|
||||||
#include "nghttp2_hd_huffman.h"
|
#include "nghttp2_hd_huffman.h"
|
||||||
#include "nghttp2_buf.h"
|
#include "nghttp2_buf.h"
|
||||||
|
#include "nghttp2_mem.h"
|
||||||
|
|
||||||
#define NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE NGHTTP2_DEFAULT_HEADER_TABLE_SIZE
|
#define NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE NGHTTP2_DEFAULT_HEADER_TABLE_SIZE
|
||||||
#define NGHTTP2_HD_ENTRY_OVERHEAD 32
|
#define NGHTTP2_HD_ENTRY_OVERHEAD 32
|
||||||
@@ -73,6 +74,11 @@ typedef struct {
|
|||||||
uint8_t flags;
|
uint8_t flags;
|
||||||
} nghttp2_hd_entry;
|
} nghttp2_hd_entry;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
nghttp2_hd_entry ent;
|
||||||
|
size_t index;
|
||||||
|
} nghttp2_hd_static_entry;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
nghttp2_hd_entry **buffer;
|
nghttp2_hd_entry **buffer;
|
||||||
size_t mask;
|
size_t mask;
|
||||||
@@ -104,6 +110,8 @@ typedef enum {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
/* dynamic header table */
|
/* dynamic header table */
|
||||||
nghttp2_hd_ringbuf hd_table;
|
nghttp2_hd_ringbuf hd_table;
|
||||||
|
/* Memory allocator */
|
||||||
|
nghttp2_mem *mem;
|
||||||
/* Abstract buffer size of hd_table as described in the spec. This
|
/* Abstract buffer size of hd_table as described in the spec. This
|
||||||
is the sum of length of name/value in hd_table +
|
is the sum of length of name/value in hd_table +
|
||||||
NGHTTP2_HD_ENTRY_OVERHEAD bytes overhead per each entry. */
|
NGHTTP2_HD_ENTRY_OVERHEAD bytes overhead per each entry. */
|
||||||
@@ -178,12 +186,12 @@ struct nghttp2_hd_inflater {
|
|||||||
* NGHTTP2_ERR_NOMEM
|
* NGHTTP2_ERR_NOMEM
|
||||||
* Out of memory.
|
* Out of memory.
|
||||||
*/
|
*/
|
||||||
int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t flags,
|
int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t flags, uint8_t *name,
|
||||||
uint8_t *name, size_t namelen,
|
size_t namelen, uint8_t *value, size_t valuelen,
|
||||||
uint8_t *value, size_t valuelen,
|
uint32_t name_hash, uint32_t value_hash,
|
||||||
uint32_t name_hash, uint32_t value_hash);
|
nghttp2_mem *mem);
|
||||||
|
|
||||||
void nghttp2_hd_entry_free(nghttp2_hd_entry *ent);
|
void nghttp2_hd_entry_free(nghttp2_hd_entry *ent, nghttp2_mem *mem);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initializes |deflater| for deflating name/values pairs.
|
* Initializes |deflater| for deflating name/values pairs.
|
||||||
@@ -199,7 +207,7 @@ void nghttp2_hd_entry_free(nghttp2_hd_entry *ent);
|
|||||||
* NGHTTP2_ERR_NOMEM
|
* NGHTTP2_ERR_NOMEM
|
||||||
* Out of memory.
|
* Out of memory.
|
||||||
*/
|
*/
|
||||||
int nghttp2_hd_deflate_init(nghttp2_hd_deflater *deflater);
|
int nghttp2_hd_deflate_init(nghttp2_hd_deflater *deflater, nghttp2_mem *mem);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initializes |deflater| for deflating name/values pairs.
|
* Initializes |deflater| for deflating name/values pairs.
|
||||||
@@ -215,7 +223,8 @@ int nghttp2_hd_deflate_init(nghttp2_hd_deflater *deflater);
|
|||||||
* Out of memory.
|
* Out of memory.
|
||||||
*/
|
*/
|
||||||
int nghttp2_hd_deflate_init2(nghttp2_hd_deflater *deflater,
|
int nghttp2_hd_deflate_init2(nghttp2_hd_deflater *deflater,
|
||||||
size_t deflate_hd_table_bufsize_max);
|
size_t deflate_hd_table_bufsize_max,
|
||||||
|
nghttp2_mem *mem);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Deallocates any resources allocated for |deflater|.
|
* Deallocates any resources allocated for |deflater|.
|
||||||
@@ -243,8 +252,8 @@ void nghttp2_hd_deflate_free(nghttp2_hd_deflater *deflater);
|
|||||||
* Out of buffer space.
|
* Out of buffer space.
|
||||||
*/
|
*/
|
||||||
int nghttp2_hd_deflate_hd_bufs(nghttp2_hd_deflater *deflater,
|
int nghttp2_hd_deflate_hd_bufs(nghttp2_hd_deflater *deflater,
|
||||||
nghttp2_bufs *bufs,
|
nghttp2_bufs *bufs, const nghttp2_nv *nva,
|
||||||
const nghttp2_nv *nva, size_t nvlen);
|
size_t nvlen);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initializes |inflater| for inflating name/values pairs.
|
* Initializes |inflater| for inflating name/values pairs.
|
||||||
@@ -255,7 +264,7 @@ int nghttp2_hd_deflate_hd_bufs(nghttp2_hd_deflater *deflater,
|
|||||||
* :enum:`NGHTTP2_ERR_NOMEM`
|
* :enum:`NGHTTP2_ERR_NOMEM`
|
||||||
* Out of memory.
|
* Out of memory.
|
||||||
*/
|
*/
|
||||||
int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater);
|
int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater, nghttp2_mem *mem);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Deallocates any resources allocated for |inflater|.
|
* Deallocates any resources allocated for |inflater|.
|
||||||
@@ -279,8 +288,8 @@ nghttp2_hd_entry* nghttp2_hd_table_get(nghttp2_hd_context *context,
|
|||||||
|
|
||||||
/* For unittesting purpose */
|
/* For unittesting purpose */
|
||||||
ssize_t nghttp2_hd_decode_length(uint32_t *res, size_t *shift_ptr, int *final,
|
ssize_t nghttp2_hd_decode_length(uint32_t *res, size_t *shift_ptr, int *final,
|
||||||
uint32_t initial, size_t shift,
|
uint32_t initial, size_t shift, uint8_t *in,
|
||||||
uint8_t *in, uint8_t *last, size_t prefix);
|
uint8_t *last, size_t prefix);
|
||||||
|
|
||||||
/* Huffman encoding/decoding functions */
|
/* Huffman encoding/decoding functions */
|
||||||
|
|
||||||
@@ -305,8 +314,8 @@ size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len);
|
|||||||
* NGHTTP2_ERR_BUFFER_ERROR
|
* NGHTTP2_ERR_BUFFER_ERROR
|
||||||
* Out of buffer space.
|
* Out of buffer space.
|
||||||
*/
|
*/
|
||||||
int nghttp2_hd_huff_encode(nghttp2_bufs *bufs,
|
int nghttp2_hd_huff_encode(nghttp2_bufs *bufs, const uint8_t *src,
|
||||||
const uint8_t *src, size_t srclen);
|
size_t srclen);
|
||||||
|
|
||||||
void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx);
|
void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx);
|
||||||
|
|
||||||
@@ -334,7 +343,7 @@ void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx);
|
|||||||
* Decoding process has failed.
|
* Decoding process has failed.
|
||||||
*/
|
*/
|
||||||
ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx,
|
ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx,
|
||||||
nghttp2_bufs *bufs,
|
nghttp2_bufs *bufs, const uint8_t *src,
|
||||||
const uint8_t *src, size_t srclen, int final);
|
size_t srclen, int final);
|
||||||
|
|
||||||
#endif /* NGHTTP2_HD_H */
|
#endif /* NGHTTP2_HD_H */
|
||||||
|
|||||||
@@ -41,65 +41,78 @@ extern const nghttp2_huff_decode huff_decode_table[][16];
|
|||||||
* unfilled bits in the pointed location is returned.
|
* unfilled bits in the pointed location is returned.
|
||||||
*/
|
*/
|
||||||
static ssize_t huff_encode_sym(nghttp2_bufs *bufs, size_t *avail_ptr,
|
static ssize_t huff_encode_sym(nghttp2_bufs *bufs, size_t *avail_ptr,
|
||||||
size_t rembits,
|
size_t rembits, const nghttp2_huff_sym *sym) {
|
||||||
const nghttp2_huff_sym *sym)
|
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
size_t nbits = sym->nbits;
|
size_t nbits = sym->nbits;
|
||||||
|
uint32_t code = sym->code;
|
||||||
|
|
||||||
for(;;) {
|
/* We assume that sym->nbits <= 32 */
|
||||||
if (rembits > nbits) {
|
if (rembits > nbits) {
|
||||||
if(*avail_ptr) {
|
nghttp2_bufs_fast_orb_hold(bufs, code << (rembits - nbits));
|
||||||
nghttp2_bufs_fast_orb_hold(bufs, sym->code << (rembits - nbits));
|
return (ssize_t)(rembits - nbits);
|
||||||
} else {
|
|
||||||
rv = nghttp2_bufs_orb_hold(bufs, sym->code << (rembits - nbits));
|
|
||||||
if(rv != 0) {
|
|
||||||
return rv;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*avail_ptr = nghttp2_bufs_cur_avail(bufs);
|
if (rembits == nbits) {
|
||||||
}
|
nghttp2_bufs_fast_orb(bufs, code);
|
||||||
|
|
||||||
rembits -= nbits;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(*avail_ptr) {
|
|
||||||
nghttp2_bufs_fast_orb(bufs, sym->code >> (nbits - rembits));
|
|
||||||
--*avail_ptr;
|
--*avail_ptr;
|
||||||
} else {
|
return 8;
|
||||||
rv = nghttp2_bufs_orb(bufs, sym->code >> (nbits - rembits));
|
|
||||||
if(rv != 0) {
|
|
||||||
return rv;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*avail_ptr = nghttp2_bufs_cur_avail(bufs);
|
nghttp2_bufs_fast_orb(bufs, code >> (nbits - rembits));
|
||||||
}
|
--*avail_ptr;
|
||||||
|
|
||||||
nbits -= rembits;
|
nbits -= rembits;
|
||||||
rembits = 8;
|
if (nbits & 0x7) {
|
||||||
|
/* align code to MSB byte boundary */
|
||||||
if(nbits == 0) {
|
code <<= 8 - (nbits & 0x7);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(*avail_ptr) {
|
/* we lose at most 3 bytes, but it is not critical in practice */
|
||||||
nghttp2_bufs_fast_addb_hold(bufs, 0);
|
if (*avail_ptr < (nbits + 7) / 8) {
|
||||||
} else {
|
rv = nghttp2_bufs_advance(bufs);
|
||||||
rv = nghttp2_bufs_addb_hold(bufs, 0);
|
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
*avail_ptr = nghttp2_bufs_cur_avail(bufs);
|
*avail_ptr = nghttp2_bufs_cur_avail(bufs);
|
||||||
}
|
/* we assume that we at least 3 buffer space available */
|
||||||
}
|
assert(*avail_ptr >= 3);
|
||||||
return (ssize_t)rembits;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len)
|
/* fast path, since most code is less than 8 */
|
||||||
{
|
if (nbits < 8) {
|
||||||
|
nghttp2_bufs_fast_addb_hold(bufs, code);
|
||||||
|
*avail_ptr = nghttp2_bufs_cur_avail(bufs);
|
||||||
|
return (ssize_t)(8 - nbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* handle longer code path */
|
||||||
|
if (nbits > 24) {
|
||||||
|
nghttp2_bufs_fast_addb(bufs, code >> 24);
|
||||||
|
nbits -= 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nbits > 16) {
|
||||||
|
nghttp2_bufs_fast_addb(bufs, code >> 16);
|
||||||
|
nbits -= 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nbits > 8) {
|
||||||
|
nghttp2_bufs_fast_addb(bufs, code >> 8);
|
||||||
|
nbits -= 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nbits == 8) {
|
||||||
|
nghttp2_bufs_fast_addb(bufs, code);
|
||||||
|
*avail_ptr = nghttp2_bufs_cur_avail(bufs);
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
nghttp2_bufs_fast_addb_hold(bufs, code);
|
||||||
|
*avail_ptr = nghttp2_bufs_cur_avail(bufs);
|
||||||
|
return (ssize_t)(8 - nbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len) {
|
||||||
size_t i;
|
size_t i;
|
||||||
size_t nbits = 0;
|
size_t nbits = 0;
|
||||||
|
|
||||||
@@ -110,9 +123,8 @@ size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len)
|
|||||||
return (nbits + 7) / 8;
|
return (nbits + 7) / 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_hd_huff_encode(nghttp2_bufs *bufs,
|
int nghttp2_hd_huff_encode(nghttp2_bufs *bufs, const uint8_t *src,
|
||||||
const uint8_t *src, size_t srclen)
|
size_t srclen) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
ssize_t rembits = 8;
|
ssize_t rembits = 8;
|
||||||
size_t i;
|
size_t i;
|
||||||
@@ -140,32 +152,25 @@ int nghttp2_hd_huff_encode(nghttp2_bufs *bufs,
|
|||||||
}
|
}
|
||||||
/* 256 is special terminal symbol, pad with its prefix */
|
/* 256 is special terminal symbol, pad with its prefix */
|
||||||
if (rembits < 8) {
|
if (rembits < 8) {
|
||||||
|
/* if rembits < 8, we should have at least 1 buffer space
|
||||||
|
available */
|
||||||
const nghttp2_huff_sym *sym = &huff_sym_table[256];
|
const nghttp2_huff_sym *sym = &huff_sym_table[256];
|
||||||
|
assert(avail);
|
||||||
/* Caution we no longer adjust avail here */
|
/* Caution we no longer adjust avail here */
|
||||||
if(avail) {
|
|
||||||
nghttp2_bufs_fast_orb(bufs, sym->code >> (sym->nbits - rembits));
|
nghttp2_bufs_fast_orb(bufs, sym->code >> (sym->nbits - rembits));
|
||||||
} else {
|
|
||||||
rv = nghttp2_bufs_orb(bufs, sym->code >> (sym->nbits - rembits));
|
|
||||||
if(rv != 0) {
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx)
|
void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx) {
|
||||||
{
|
|
||||||
ctx->state = 0;
|
ctx->state = 0;
|
||||||
ctx->accept = 1;
|
ctx->accept = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx,
|
ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx,
|
||||||
nghttp2_bufs *bufs,
|
nghttp2_bufs *bufs, const uint8_t *src,
|
||||||
const uint8_t *src, size_t srclen, int final)
|
size_t srclen, int final) {
|
||||||
{
|
|
||||||
size_t i, j;
|
size_t i, j;
|
||||||
int rv;
|
int rv;
|
||||||
size_t avail;
|
size_t avail;
|
||||||
|
|||||||
@@ -24,8 +24,9 @@
|
|||||||
*/
|
*/
|
||||||
#include "nghttp2_hd_huffman.h"
|
#include "nghttp2_hd_huffman.h"
|
||||||
|
|
||||||
const nghttp2_huff_sym huff_sym_table[] = {
|
/* Generated by mkhufftbl.py */
|
||||||
{ 13, 0x1ff8u },
|
|
||||||
|
const nghttp2_huff_sym huff_sym_table[] = {{13, 0x1ff8u},
|
||||||
{23, 0x7fffd8u},
|
{23, 0x7fffd8u},
|
||||||
{28, 0xfffffe2u},
|
{28, 0xfffffe2u},
|
||||||
{28, 0xfffffe3u},
|
{28, 0xfffffe3u},
|
||||||
@@ -281,8 +282,7 @@ const nghttp2_huff_sym huff_sym_table[] = {
|
|||||||
{27, 0x7ffffefu},
|
{27, 0x7ffffefu},
|
||||||
{27, 0x7fffff0u},
|
{27, 0x7fffff0u},
|
||||||
{26, 0x3ffffeeu},
|
{26, 0x3ffffeeu},
|
||||||
{ 30, 0x3fffffffu }
|
{30, 0x3fffffffu}};
|
||||||
};
|
|
||||||
|
|
||||||
const nghttp2_huff_decode huff_decode_table[][16] = {
|
const nghttp2_huff_decode huff_decode_table[][16] = {
|
||||||
/* 0 */
|
/* 0 */
|
||||||
|
|||||||
@@ -29,58 +29,36 @@
|
|||||||
|
|
||||||
#include "nghttp2_net.h"
|
#include "nghttp2_net.h"
|
||||||
|
|
||||||
void nghttp2_put_uint16be(uint8_t *buf, uint16_t n)
|
void nghttp2_put_uint16be(uint8_t *buf, uint16_t n) {
|
||||||
{
|
|
||||||
uint16_t x = htons(n);
|
uint16_t x = htons(n);
|
||||||
memcpy(buf, &x, sizeof(uint16_t));
|
memcpy(buf, &x, sizeof(uint16_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_put_uint32be(uint8_t *buf, uint32_t n)
|
void nghttp2_put_uint32be(uint8_t *buf, uint32_t n) {
|
||||||
{
|
|
||||||
uint32_t x = htonl(n);
|
uint32_t x = htonl(n);
|
||||||
memcpy(buf, &x, sizeof(uint32_t));
|
memcpy(buf, &x, sizeof(uint32_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t nghttp2_get_uint16(const uint8_t *data)
|
uint16_t nghttp2_get_uint16(const uint8_t *data) {
|
||||||
{
|
|
||||||
uint16_t n;
|
uint16_t n;
|
||||||
memcpy(&n, data, sizeof(uint16_t));
|
memcpy(&n, data, sizeof(uint16_t));
|
||||||
return ntohs(n);
|
return ntohs(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t nghttp2_get_uint32(const uint8_t *data)
|
uint32_t nghttp2_get_uint32(const uint8_t *data) {
|
||||||
{
|
|
||||||
uint32_t n;
|
uint32_t n;
|
||||||
memcpy(&n, data, sizeof(uint32_t));
|
memcpy(&n, data, sizeof(uint32_t));
|
||||||
return ntohl(n);
|
return ntohl(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_reserve_buffer(uint8_t **buf_ptr, size_t *buflen_ptr,
|
void *nghttp2_memdup(const void *src, size_t n, nghttp2_mem *mem) {
|
||||||
size_t min_length)
|
|
||||||
{
|
|
||||||
if(min_length > *buflen_ptr) {
|
|
||||||
uint8_t *temp;
|
|
||||||
min_length = (min_length+4095)/4096*4096;
|
|
||||||
temp = realloc(*buf_ptr, min_length);
|
|
||||||
if(temp == NULL) {
|
|
||||||
return NGHTTP2_ERR_NOMEM;
|
|
||||||
} else {
|
|
||||||
*buf_ptr = temp;
|
|
||||||
*buflen_ptr = min_length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* nghttp2_memdup(const void* src, size_t n)
|
|
||||||
{
|
|
||||||
void *dest;
|
void *dest;
|
||||||
|
|
||||||
if (n == 0) {
|
if (n == 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
dest = malloc(n);
|
dest = nghttp2_mem_malloc(mem, n);
|
||||||
if (dest == NULL) {
|
if (dest == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -88,55 +66,151 @@ void* nghttp2_memdup(const void* src, size_t n)
|
|||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_downcase(uint8_t *s, size_t len)
|
/* Generated by gendowncasetbl.py */
|
||||||
{
|
static const int DOWNCASE_TBL[] = {
|
||||||
|
0 /* NUL */, 1 /* SOH */, 2 /* STX */, 3 /* ETX */,
|
||||||
|
4 /* EOT */, 5 /* ENQ */, 6 /* ACK */, 7 /* BEL */,
|
||||||
|
8 /* BS */, 9 /* HT */, 10 /* LF */, 11 /* VT */,
|
||||||
|
12 /* FF */, 13 /* CR */, 14 /* SO */, 15 /* SI */,
|
||||||
|
16 /* DLE */, 17 /* DC1 */, 18 /* DC2 */, 19 /* DC3 */,
|
||||||
|
20 /* DC4 */, 21 /* NAK */, 22 /* SYN */, 23 /* ETB */,
|
||||||
|
24 /* CAN */, 25 /* EM */, 26 /* SUB */, 27 /* ESC */,
|
||||||
|
28 /* FS */, 29 /* GS */, 30 /* RS */, 31 /* US */,
|
||||||
|
32 /* SPC */, 33 /* ! */, 34 /* " */, 35 /* # */,
|
||||||
|
36 /* $ */, 37 /* % */, 38 /* & */, 39 /* ' */,
|
||||||
|
40 /* ( */, 41 /* ) */, 42 /* * */, 43 /* + */,
|
||||||
|
44 /* , */, 45 /* - */, 46 /* . */, 47 /* / */,
|
||||||
|
48 /* 0 */, 49 /* 1 */, 50 /* 2 */, 51 /* 3 */,
|
||||||
|
52 /* 4 */, 53 /* 5 */, 54 /* 6 */, 55 /* 7 */,
|
||||||
|
56 /* 8 */, 57 /* 9 */, 58 /* : */, 59 /* ; */,
|
||||||
|
60 /* < */, 61 /* = */, 62 /* > */, 63 /* ? */,
|
||||||
|
64 /* @ */, 97 /* A */, 98 /* B */, 99 /* C */,
|
||||||
|
100 /* D */, 101 /* E */, 102 /* F */, 103 /* G */,
|
||||||
|
104 /* H */, 105 /* I */, 106 /* J */, 107 /* K */,
|
||||||
|
108 /* L */, 109 /* M */, 110 /* N */, 111 /* O */,
|
||||||
|
112 /* P */, 113 /* Q */, 114 /* R */, 115 /* S */,
|
||||||
|
116 /* T */, 117 /* U */, 118 /* V */, 119 /* W */,
|
||||||
|
120 /* X */, 121 /* Y */, 122 /* Z */, 91 /* [ */,
|
||||||
|
92 /* \ */, 93 /* ] */, 94 /* ^ */, 95 /* _ */,
|
||||||
|
96 /* ` */, 97 /* a */, 98 /* b */, 99 /* c */,
|
||||||
|
100 /* d */, 101 /* e */, 102 /* f */, 103 /* g */,
|
||||||
|
104 /* h */, 105 /* i */, 106 /* j */, 107 /* k */,
|
||||||
|
108 /* l */, 109 /* m */, 110 /* n */, 111 /* o */,
|
||||||
|
112 /* p */, 113 /* q */, 114 /* r */, 115 /* s */,
|
||||||
|
116 /* t */, 117 /* u */, 118 /* v */, 119 /* w */,
|
||||||
|
120 /* x */, 121 /* y */, 122 /* z */, 123 /* { */,
|
||||||
|
124 /* | */, 125 /* } */, 126 /* ~ */, 127 /* DEL */,
|
||||||
|
128 /* 0x80 */, 129 /* 0x81 */, 130 /* 0x82 */, 131 /* 0x83 */,
|
||||||
|
132 /* 0x84 */, 133 /* 0x85 */, 134 /* 0x86 */, 135 /* 0x87 */,
|
||||||
|
136 /* 0x88 */, 137 /* 0x89 */, 138 /* 0x8a */, 139 /* 0x8b */,
|
||||||
|
140 /* 0x8c */, 141 /* 0x8d */, 142 /* 0x8e */, 143 /* 0x8f */,
|
||||||
|
144 /* 0x90 */, 145 /* 0x91 */, 146 /* 0x92 */, 147 /* 0x93 */,
|
||||||
|
148 /* 0x94 */, 149 /* 0x95 */, 150 /* 0x96 */, 151 /* 0x97 */,
|
||||||
|
152 /* 0x98 */, 153 /* 0x99 */, 154 /* 0x9a */, 155 /* 0x9b */,
|
||||||
|
156 /* 0x9c */, 157 /* 0x9d */, 158 /* 0x9e */, 159 /* 0x9f */,
|
||||||
|
160 /* 0xa0 */, 161 /* 0xa1 */, 162 /* 0xa2 */, 163 /* 0xa3 */,
|
||||||
|
164 /* 0xa4 */, 165 /* 0xa5 */, 166 /* 0xa6 */, 167 /* 0xa7 */,
|
||||||
|
168 /* 0xa8 */, 169 /* 0xa9 */, 170 /* 0xaa */, 171 /* 0xab */,
|
||||||
|
172 /* 0xac */, 173 /* 0xad */, 174 /* 0xae */, 175 /* 0xaf */,
|
||||||
|
176 /* 0xb0 */, 177 /* 0xb1 */, 178 /* 0xb2 */, 179 /* 0xb3 */,
|
||||||
|
180 /* 0xb4 */, 181 /* 0xb5 */, 182 /* 0xb6 */, 183 /* 0xb7 */,
|
||||||
|
184 /* 0xb8 */, 185 /* 0xb9 */, 186 /* 0xba */, 187 /* 0xbb */,
|
||||||
|
188 /* 0xbc */, 189 /* 0xbd */, 190 /* 0xbe */, 191 /* 0xbf */,
|
||||||
|
192 /* 0xc0 */, 193 /* 0xc1 */, 194 /* 0xc2 */, 195 /* 0xc3 */,
|
||||||
|
196 /* 0xc4 */, 197 /* 0xc5 */, 198 /* 0xc6 */, 199 /* 0xc7 */,
|
||||||
|
200 /* 0xc8 */, 201 /* 0xc9 */, 202 /* 0xca */, 203 /* 0xcb */,
|
||||||
|
204 /* 0xcc */, 205 /* 0xcd */, 206 /* 0xce */, 207 /* 0xcf */,
|
||||||
|
208 /* 0xd0 */, 209 /* 0xd1 */, 210 /* 0xd2 */, 211 /* 0xd3 */,
|
||||||
|
212 /* 0xd4 */, 213 /* 0xd5 */, 214 /* 0xd6 */, 215 /* 0xd7 */,
|
||||||
|
216 /* 0xd8 */, 217 /* 0xd9 */, 218 /* 0xda */, 219 /* 0xdb */,
|
||||||
|
220 /* 0xdc */, 221 /* 0xdd */, 222 /* 0xde */, 223 /* 0xdf */,
|
||||||
|
224 /* 0xe0 */, 225 /* 0xe1 */, 226 /* 0xe2 */, 227 /* 0xe3 */,
|
||||||
|
228 /* 0xe4 */, 229 /* 0xe5 */, 230 /* 0xe6 */, 231 /* 0xe7 */,
|
||||||
|
232 /* 0xe8 */, 233 /* 0xe9 */, 234 /* 0xea */, 235 /* 0xeb */,
|
||||||
|
236 /* 0xec */, 237 /* 0xed */, 238 /* 0xee */, 239 /* 0xef */,
|
||||||
|
240 /* 0xf0 */, 241 /* 0xf1 */, 242 /* 0xf2 */, 243 /* 0xf3 */,
|
||||||
|
244 /* 0xf4 */, 245 /* 0xf5 */, 246 /* 0xf6 */, 247 /* 0xf7 */,
|
||||||
|
248 /* 0xf8 */, 249 /* 0xf9 */, 250 /* 0xfa */, 251 /* 0xfb */,
|
||||||
|
252 /* 0xfc */, 253 /* 0xfd */, 254 /* 0xfe */, 255 /* 0xff */,
|
||||||
|
};
|
||||||
|
|
||||||
|
void nghttp2_downcase(uint8_t *s, size_t len) {
|
||||||
size_t i;
|
size_t i;
|
||||||
for (i = 0; i < len; ++i) {
|
for (i = 0; i < len; ++i) {
|
||||||
if('A' <= s[i] && s[i] <= 'Z') {
|
s[i] = DOWNCASE_TBL[s[i]];
|
||||||
s[i] += 'a'-'A';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* local_window_size
|
||||||
|
* ^ *
|
||||||
|
* | * recv_window_size
|
||||||
|
* | * * ^
|
||||||
|
* | * * |
|
||||||
|
* 0+++++++++
|
||||||
|
* | * * \
|
||||||
|
* | * * | This rage is hidden in flow control. But it must be
|
||||||
|
* v * * / kept in order to restore it when window size is enlarged.
|
||||||
|
* recv_reduction
|
||||||
|
* (+ for negative direction)
|
||||||
|
*
|
||||||
|
* recv_window_size could be negative if we decrease
|
||||||
|
* local_window_size more than recv_window_size:
|
||||||
|
*
|
||||||
|
* local_window_size
|
||||||
|
* ^ *
|
||||||
|
* | *
|
||||||
|
* | *
|
||||||
|
* 0++++++++
|
||||||
|
* | * ^ recv_window_size (negative)
|
||||||
|
* | * |
|
||||||
|
* v * *
|
||||||
|
* recv_reduction
|
||||||
|
*/
|
||||||
int nghttp2_adjust_local_window_size(int32_t *local_window_size_ptr,
|
int nghttp2_adjust_local_window_size(int32_t *local_window_size_ptr,
|
||||||
int32_t *recv_window_size_ptr,
|
int32_t *recv_window_size_ptr,
|
||||||
int32_t *recv_reduction_ptr,
|
int32_t *recv_reduction_ptr,
|
||||||
int32_t *delta_ptr)
|
int32_t *delta_ptr) {
|
||||||
{
|
|
||||||
if (*delta_ptr > 0) {
|
if (*delta_ptr > 0) {
|
||||||
|
int32_t recv_reduction_delta;
|
||||||
|
int32_t delta;
|
||||||
int32_t new_recv_window_size =
|
int32_t new_recv_window_size =
|
||||||
nghttp2_max(0, *recv_window_size_ptr) - *delta_ptr;
|
nghttp2_max(0, *recv_window_size_ptr) - *delta_ptr;
|
||||||
if(new_recv_window_size < 0) {
|
|
||||||
|
if (new_recv_window_size >= 0) {
|
||||||
|
*recv_window_size_ptr = new_recv_window_size;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
delta = -new_recv_window_size;
|
||||||
|
|
||||||
/* The delta size is strictly more than received bytes. Increase
|
/* The delta size is strictly more than received bytes. Increase
|
||||||
local_window_size by that difference. */
|
local_window_size by that difference |delta|. */
|
||||||
int32_t recv_reduction_diff;
|
if (*local_window_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - delta) {
|
||||||
if(*local_window_size_ptr >
|
|
||||||
NGHTTP2_MAX_WINDOW_SIZE + new_recv_window_size) {
|
|
||||||
return NGHTTP2_ERR_FLOW_CONTROL;
|
return NGHTTP2_ERR_FLOW_CONTROL;
|
||||||
}
|
}
|
||||||
*local_window_size_ptr -= new_recv_window_size;
|
*local_window_size_ptr += delta;
|
||||||
/* If there is recv_reduction due to earlier window_size
|
/* If there is recv_reduction due to earlier window_size
|
||||||
reduction, we have to adjust it too. */
|
reduction, we have to adjust it too. */
|
||||||
recv_reduction_diff = nghttp2_min(*recv_reduction_ptr,
|
recv_reduction_delta = nghttp2_min(*recv_reduction_ptr, delta);
|
||||||
-new_recv_window_size);
|
*recv_reduction_ptr -= recv_reduction_delta;
|
||||||
*recv_reduction_ptr -= recv_reduction_diff;
|
|
||||||
if (*recv_window_size_ptr < 0) {
|
if (*recv_window_size_ptr < 0) {
|
||||||
*recv_window_size_ptr += recv_reduction_diff;
|
*recv_window_size_ptr += recv_reduction_delta;
|
||||||
} else {
|
} else {
|
||||||
/* If *recv_window_size_ptr > 0, then those bytes are
|
/* If *recv_window_size_ptr > 0, then those bytes are going to
|
||||||
considered to be backed to the remote peer (by
|
be returned to the remote peer (by WINDOW_UPDATE with the
|
||||||
WINDOW_UPDATE with the adjusted *delta_ptr), so it is
|
adjusted *delta_ptr), so it is effectively 0 now. We set to
|
||||||
effectively 0 now. */
|
*recv_reduction_delta, because caller does not take into
|
||||||
*recv_window_size_ptr = recv_reduction_diff;
|
account it in *delta_ptr. */
|
||||||
|
*recv_window_size_ptr = recv_reduction_delta;
|
||||||
}
|
}
|
||||||
/* recv_reduction_diff must be paied from *delta_ptr, since it
|
/* recv_reduction_delta must be paied from *delta_ptr, since it
|
||||||
was added in window size reduction (see below). */
|
was added in window size reduction (see below). */
|
||||||
*delta_ptr -= recv_reduction_diff;
|
*delta_ptr -= recv_reduction_delta;
|
||||||
} else {
|
|
||||||
*recv_window_size_ptr = new_recv_window_size;
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
}
|
||||||
|
|
||||||
if (*local_window_size_ptr + *delta_ptr < 0 ||
|
if (*local_window_size_ptr + *delta_ptr < 0 ||
|
||||||
*recv_window_size_ptr < INT32_MIN - *delta_ptr ||
|
*recv_window_size_ptr < INT32_MIN - *delta_ptr ||
|
||||||
*recv_reduction_ptr > INT32_MAX + *delta_ptr) {
|
*recv_reduction_ptr > INT32_MAX + *delta_ptr) {
|
||||||
@@ -150,18 +224,16 @@ int nghttp2_adjust_local_window_size(int32_t *local_window_size_ptr,
|
|||||||
*recv_window_size_ptr += *delta_ptr;
|
*recv_window_size_ptr += *delta_ptr;
|
||||||
*recv_reduction_ptr -= *delta_ptr;
|
*recv_reduction_ptr -= *delta_ptr;
|
||||||
*delta_ptr = 0;
|
*delta_ptr = 0;
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_should_send_window_update(int32_t local_window_size,
|
int nghttp2_should_send_window_update(int32_t local_window_size,
|
||||||
int32_t recv_window_size)
|
int32_t recv_window_size) {
|
||||||
{
|
|
||||||
return recv_window_size >= local_window_size / 2;
|
return recv_window_size >= local_window_size / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* nghttp2_strerror(int error_code)
|
const char *nghttp2_strerror(int error_code) {
|
||||||
{
|
|
||||||
switch (error_code) {
|
switch (error_code) {
|
||||||
case 0:
|
case 0:
|
||||||
return "Success";
|
return "Success";
|
||||||
@@ -195,6 +267,8 @@ const char* nghttp2_strerror(int error_code)
|
|||||||
return "Invalid stream state";
|
return "Invalid stream state";
|
||||||
case NGHTTP2_ERR_DEFERRED_DATA_EXIST:
|
case NGHTTP2_ERR_DEFERRED_DATA_EXIST:
|
||||||
return "Another DATA frame has already been deferred";
|
return "Another DATA frame has already been deferred";
|
||||||
|
case NGHTTP2_ERR_SESSION_CLOSING:
|
||||||
|
return "The current session is closing";
|
||||||
case NGHTTP2_ERR_START_STREAM_NOT_ALLOWED:
|
case NGHTTP2_ERR_START_STREAM_NOT_ALLOWED:
|
||||||
return "request HEADERS is not allowed";
|
return "request HEADERS is not allowed";
|
||||||
case NGHTTP2_ERR_GOAWAY_ALREADY_SENT:
|
case NGHTTP2_ERR_GOAWAY_ALREADY_SENT:
|
||||||
@@ -225,85 +299,70 @@ const char* nghttp2_strerror(int error_code)
|
|||||||
return "Out of memory";
|
return "Out of memory";
|
||||||
case NGHTTP2_ERR_CALLBACK_FAILURE:
|
case NGHTTP2_ERR_CALLBACK_FAILURE:
|
||||||
return "The user callback function failed";
|
return "The user callback function failed";
|
||||||
|
case NGHTTP2_ERR_BAD_PREFACE:
|
||||||
|
return "Received bad connection preface";
|
||||||
default:
|
default:
|
||||||
return "Unknown error code";
|
return "Unknown error code";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_free(void *ptr)
|
/* Generated by gennmchartbl.py */
|
||||||
{
|
|
||||||
free(ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int VALID_HD_NAME_CHARS[] = {
|
static int VALID_HD_NAME_CHARS[] = {
|
||||||
0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */,
|
0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */,
|
||||||
0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */,
|
0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */,
|
||||||
0 /* BS */, 0 /* HT */, 0 /* LF */, 0 /* VT */,
|
0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */,
|
||||||
0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */,
|
0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
|
||||||
0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
|
0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */,
|
||||||
0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */,
|
0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */,
|
||||||
0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */,
|
0 /* RS */, 0 /* US */, 0 /* SPC */, 1 /* ! */, 0 /* " */,
|
||||||
0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */,
|
1 /* # */, 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
|
||||||
0 /* SPC */, 1 /* ! */, 0 /* " */, 1 /* # */,
|
0 /* ( */, 0 /* ) */, 1 /* * */, 1 /* + */, 0 /* , */,
|
||||||
1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
|
1 /* - */, 1 /* . */, 0 /* / */, 1 /* 0 */, 1 /* 1 */,
|
||||||
0 /* ( */, 0 /* ) */, 1 /* * */, 1 /* + */,
|
1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */,
|
||||||
0 /* , */, 1 /* - */, 1 /* . */, 0 /* / */,
|
1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */,
|
||||||
1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */,
|
0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */, 0 /* @ */,
|
||||||
1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */,
|
0 /* A */, 0 /* B */, 0 /* C */, 0 /* D */, 0 /* E */,
|
||||||
1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */,
|
0 /* F */, 0 /* G */, 0 /* H */, 0 /* I */, 0 /* J */,
|
||||||
0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */,
|
0 /* K */, 0 /* L */, 0 /* M */, 0 /* N */, 0 /* O */,
|
||||||
0 /* @ */, 0 /* A */, 0 /* B */, 0 /* C */,
|
0 /* P */, 0 /* Q */, 0 /* R */, 0 /* S */, 0 /* T */,
|
||||||
0 /* D */, 0 /* E */, 0 /* F */, 0 /* G */,
|
0 /* U */, 0 /* V */, 0 /* W */, 0 /* X */, 0 /* Y */,
|
||||||
0 /* H */, 0 /* I */, 0 /* J */, 0 /* K */,
|
0 /* Z */, 0 /* [ */, 0 /* \ */, 0 /* ] */, 1 /* ^ */,
|
||||||
0 /* L */, 0 /* M */, 0 /* N */, 0 /* O */,
|
1 /* _ */, 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
|
||||||
0 /* P */, 0 /* Q */, 0 /* R */, 0 /* S */,
|
1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */,
|
||||||
0 /* T */, 0 /* U */, 0 /* V */, 0 /* W */,
|
1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */,
|
||||||
0 /* X */, 0 /* Y */, 0 /* Z */, 0 /* [ */,
|
1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */,
|
||||||
0 /* \ */, 0 /* ] */, 1 /* ^ */, 1 /* _ */,
|
1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
|
||||||
1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
|
1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, 1 /* | */,
|
||||||
1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */,
|
0 /* } */, 1 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */,
|
||||||
1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */,
|
0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */,
|
||||||
1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */,
|
0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
|
||||||
1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */,
|
0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */,
|
||||||
1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
|
0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */,
|
||||||
1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */,
|
0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */,
|
||||||
1 /* | */, 0 /* } */, 1 /* ~ */, 0 /* DEL */,
|
0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
|
||||||
0 /* 0x80 */, 0 /* 0x81 */, 0 /* 0x82 */, 0 /* 0x83 */,
|
0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */,
|
||||||
0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, 0 /* 0x87 */,
|
0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */,
|
||||||
0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
|
0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */,
|
||||||
0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */,
|
0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
|
||||||
0 /* 0x90 */, 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */,
|
0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */,
|
||||||
0 /* 0x94 */, 0 /* 0x95 */, 0 /* 0x96 */, 0 /* 0x97 */,
|
0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */,
|
||||||
0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, 0 /* 0x9b */,
|
0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */,
|
||||||
0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
|
0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
|
||||||
0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */,
|
0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */,
|
||||||
0 /* 0xa4 */, 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */,
|
0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */,
|
||||||
0 /* 0xa8 */, 0 /* 0xa9 */, 0 /* 0xaa */, 0 /* 0xab */,
|
0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */,
|
||||||
0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, 0 /* 0xaf */,
|
0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
|
||||||
0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
|
0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */,
|
||||||
0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */,
|
0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */,
|
||||||
0 /* 0xb8 */, 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */,
|
0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */,
|
||||||
0 /* 0xbc */, 0 /* 0xbd */, 0 /* 0xbe */, 0 /* 0xbf */,
|
0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
|
||||||
0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, 0 /* 0xc3 */,
|
0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */,
|
||||||
0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
|
0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */,
|
||||||
0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */,
|
0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */,
|
||||||
0 /* 0xcc */, 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */,
|
0 /* 0xff */
|
||||||
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_header_name(const uint8_t *name, size_t len)
|
int nghttp2_check_header_name(const uint8_t *name, size_t len) {
|
||||||
{
|
|
||||||
const uint8_t *last;
|
const uint8_t *last;
|
||||||
if (len == 0) {
|
if (len == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -323,75 +382,63 @@ int nghttp2_check_header_name(const uint8_t *name, size_t len)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Generated by genvchartbl.py */
|
||||||
static int VALID_HD_VALUE_CHARS[] = {
|
static int VALID_HD_VALUE_CHARS[] = {
|
||||||
0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */,
|
0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */,
|
||||||
0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */,
|
0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 1 /* HT */,
|
||||||
0 /* BS */, 1 /* HT */, 0 /* LF */, 0 /* VT */,
|
0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */,
|
||||||
0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */,
|
0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
|
||||||
0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
|
0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */,
|
||||||
0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */,
|
0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */,
|
||||||
0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */,
|
0 /* RS */, 0 /* US */, 1 /* SPC */, 1 /* ! */, 1 /* " */,
|
||||||
0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */,
|
1 /* # */, 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
|
||||||
1 /* SPC */, 1 /* ! */, 1 /* " */, 1 /* # */,
|
1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */, 1 /* , */,
|
||||||
1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
|
1 /* - */, 1 /* . */, 1 /* / */, 1 /* 0 */, 1 /* 1 */,
|
||||||
1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */,
|
1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */,
|
||||||
1 /* , */, 1 /* - */, 1 /* . */, 1 /* / */,
|
1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */,
|
||||||
1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */,
|
1 /* < */, 1 /* = */, 1 /* > */, 1 /* ? */, 1 /* @ */,
|
||||||
1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */,
|
1 /* A */, 1 /* B */, 1 /* C */, 1 /* D */, 1 /* E */,
|
||||||
1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */,
|
1 /* F */, 1 /* G */, 1 /* H */, 1 /* I */, 1 /* J */,
|
||||||
1 /* < */, 1 /* = */, 1 /* > */, 1 /* ? */,
|
1 /* K */, 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */,
|
||||||
1 /* @ */, 1 /* A */, 1 /* B */, 1 /* C */,
|
1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, 1 /* T */,
|
||||||
1 /* D */, 1 /* E */, 1 /* F */, 1 /* G */,
|
1 /* U */, 1 /* V */, 1 /* W */, 1 /* X */, 1 /* Y */,
|
||||||
1 /* H */, 1 /* I */, 1 /* J */, 1 /* K */,
|
1 /* Z */, 1 /* [ */, 1 /* \ */, 1 /* ] */, 1 /* ^ */,
|
||||||
1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */,
|
1 /* _ */, 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
|
||||||
1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */,
|
1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */,
|
||||||
1 /* T */, 1 /* U */, 1 /* V */, 1 /* W */,
|
1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */,
|
||||||
1 /* X */, 1 /* Y */, 1 /* Z */, 1 /* [ */,
|
1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */,
|
||||||
1 /* \ */, 1 /* ] */, 1 /* ^ */, 1 /* _ */,
|
1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
|
||||||
1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
|
1 /* x */, 1 /* y */, 1 /* z */, 1 /* { */, 1 /* | */,
|
||||||
1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */,
|
1 /* } */, 1 /* ~ */, 0 /* DEL */, 1 /* 0x80 */, 1 /* 0x81 */,
|
||||||
1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */,
|
1 /* 0x82 */, 1 /* 0x83 */, 1 /* 0x84 */, 1 /* 0x85 */, 1 /* 0x86 */,
|
||||||
1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */,
|
1 /* 0x87 */, 1 /* 0x88 */, 1 /* 0x89 */, 1 /* 0x8a */, 1 /* 0x8b */,
|
||||||
1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */,
|
1 /* 0x8c */, 1 /* 0x8d */, 1 /* 0x8e */, 1 /* 0x8f */, 1 /* 0x90 */,
|
||||||
1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
|
1 /* 0x91 */, 1 /* 0x92 */, 1 /* 0x93 */, 1 /* 0x94 */, 1 /* 0x95 */,
|
||||||
1 /* x */, 1 /* y */, 1 /* z */, 1 /* { */,
|
1 /* 0x96 */, 1 /* 0x97 */, 1 /* 0x98 */, 1 /* 0x99 */, 1 /* 0x9a */,
|
||||||
1 /* | */, 1 /* } */, 1 /* ~ */, 0 /* DEL */,
|
1 /* 0x9b */, 1 /* 0x9c */, 1 /* 0x9d */, 1 /* 0x9e */, 1 /* 0x9f */,
|
||||||
1 /* 0x80 */, 1 /* 0x81 */, 1 /* 0x82 */, 1 /* 0x83 */,
|
1 /* 0xa0 */, 1 /* 0xa1 */, 1 /* 0xa2 */, 1 /* 0xa3 */, 1 /* 0xa4 */,
|
||||||
1 /* 0x84 */, 1 /* 0x85 */, 1 /* 0x86 */, 1 /* 0x87 */,
|
1 /* 0xa5 */, 1 /* 0xa6 */, 1 /* 0xa7 */, 1 /* 0xa8 */, 1 /* 0xa9 */,
|
||||||
1 /* 0x88 */, 1 /* 0x89 */, 1 /* 0x8a */, 1 /* 0x8b */,
|
1 /* 0xaa */, 1 /* 0xab */, 1 /* 0xac */, 1 /* 0xad */, 1 /* 0xae */,
|
||||||
1 /* 0x8c */, 1 /* 0x8d */, 1 /* 0x8e */, 1 /* 0x8f */,
|
1 /* 0xaf */, 1 /* 0xb0 */, 1 /* 0xb1 */, 1 /* 0xb2 */, 1 /* 0xb3 */,
|
||||||
1 /* 0x90 */, 1 /* 0x91 */, 1 /* 0x92 */, 1 /* 0x93 */,
|
1 /* 0xb4 */, 1 /* 0xb5 */, 1 /* 0xb6 */, 1 /* 0xb7 */, 1 /* 0xb8 */,
|
||||||
1 /* 0x94 */, 1 /* 0x95 */, 1 /* 0x96 */, 1 /* 0x97 */,
|
1 /* 0xb9 */, 1 /* 0xba */, 1 /* 0xbb */, 1 /* 0xbc */, 1 /* 0xbd */,
|
||||||
1 /* 0x98 */, 1 /* 0x99 */, 1 /* 0x9a */, 1 /* 0x9b */,
|
1 /* 0xbe */, 1 /* 0xbf */, 1 /* 0xc0 */, 1 /* 0xc1 */, 1 /* 0xc2 */,
|
||||||
1 /* 0x9c */, 1 /* 0x9d */, 1 /* 0x9e */, 1 /* 0x9f */,
|
1 /* 0xc3 */, 1 /* 0xc4 */, 1 /* 0xc5 */, 1 /* 0xc6 */, 1 /* 0xc7 */,
|
||||||
1 /* 0xa0 */, 1 /* 0xa1 */, 1 /* 0xa2 */, 1 /* 0xa3 */,
|
1 /* 0xc8 */, 1 /* 0xc9 */, 1 /* 0xca */, 1 /* 0xcb */, 1 /* 0xcc */,
|
||||||
1 /* 0xa4 */, 1 /* 0xa5 */, 1 /* 0xa6 */, 1 /* 0xa7 */,
|
1 /* 0xcd */, 1 /* 0xce */, 1 /* 0xcf */, 1 /* 0xd0 */, 1 /* 0xd1 */,
|
||||||
1 /* 0xa8 */, 1 /* 0xa9 */, 1 /* 0xaa */, 1 /* 0xab */,
|
1 /* 0xd2 */, 1 /* 0xd3 */, 1 /* 0xd4 */, 1 /* 0xd5 */, 1 /* 0xd6 */,
|
||||||
1 /* 0xac */, 1 /* 0xad */, 1 /* 0xae */, 1 /* 0xaf */,
|
1 /* 0xd7 */, 1 /* 0xd8 */, 1 /* 0xd9 */, 1 /* 0xda */, 1 /* 0xdb */,
|
||||||
1 /* 0xb0 */, 1 /* 0xb1 */, 1 /* 0xb2 */, 1 /* 0xb3 */,
|
1 /* 0xdc */, 1 /* 0xdd */, 1 /* 0xde */, 1 /* 0xdf */, 1 /* 0xe0 */,
|
||||||
1 /* 0xb4 */, 1 /* 0xb5 */, 1 /* 0xb6 */, 1 /* 0xb7 */,
|
1 /* 0xe1 */, 1 /* 0xe2 */, 1 /* 0xe3 */, 1 /* 0xe4 */, 1 /* 0xe5 */,
|
||||||
1 /* 0xb8 */, 1 /* 0xb9 */, 1 /* 0xba */, 1 /* 0xbb */,
|
1 /* 0xe6 */, 1 /* 0xe7 */, 1 /* 0xe8 */, 1 /* 0xe9 */, 1 /* 0xea */,
|
||||||
1 /* 0xbc */, 1 /* 0xbd */, 1 /* 0xbe */, 1 /* 0xbf */,
|
1 /* 0xeb */, 1 /* 0xec */, 1 /* 0xed */, 1 /* 0xee */, 1 /* 0xef */,
|
||||||
1 /* 0xc0 */, 1 /* 0xc1 */, 1 /* 0xc2 */, 1 /* 0xc3 */,
|
1 /* 0xf0 */, 1 /* 0xf1 */, 1 /* 0xf2 */, 1 /* 0xf3 */, 1 /* 0xf4 */,
|
||||||
1 /* 0xc4 */, 1 /* 0xc5 */, 1 /* 0xc6 */, 1 /* 0xc7 */,
|
1 /* 0xf5 */, 1 /* 0xf6 */, 1 /* 0xf7 */, 1 /* 0xf8 */, 1 /* 0xf9 */,
|
||||||
1 /* 0xc8 */, 1 /* 0xc9 */, 1 /* 0xca */, 1 /* 0xcb */,
|
1 /* 0xfa */, 1 /* 0xfb */, 1 /* 0xfc */, 1 /* 0xfd */, 1 /* 0xfe */,
|
||||||
1 /* 0xcc */, 1 /* 0xcd */, 1 /* 0xce */, 1 /* 0xcf */,
|
1 /* 0xff */
|
||||||
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_header_value(const uint8_t *value, size_t len)
|
int nghttp2_check_header_value(const uint8_t *value, size_t len) {
|
||||||
{
|
|
||||||
const uint8_t *last;
|
const uint8_t *last;
|
||||||
for (last = value + len; value != last; ++value) {
|
for (last = value + len; value != last; ++value) {
|
||||||
if (!VALID_HD_VALUE_CHARS[*value]) {
|
if (!VALID_HD_VALUE_CHARS[*value]) {
|
||||||
@@ -401,8 +448,7 @@ int nghttp2_check_header_value(const uint8_t *value, size_t len)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t* nghttp2_cpymem(uint8_t *dest, const void *src, size_t len)
|
uint8_t *nghttp2_cpymem(uint8_t *dest, const void *src, size_t len) {
|
||||||
{
|
|
||||||
memcpy(dest, src, len);
|
memcpy(dest, src, len);
|
||||||
|
|
||||||
return dest + len;
|
return dest + len;
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
#endif /* HAVE_CONFIG_H */
|
#endif /* HAVE_CONFIG_H */
|
||||||
|
|
||||||
#include <nghttp2/nghttp2.h>
|
#include <nghttp2/nghttp2.h>
|
||||||
|
#include "nghttp2_mem.h"
|
||||||
|
|
||||||
#define nghttp2_min(A, B) ((A) < (B) ? (A) : (B))
|
#define nghttp2_min(A, B) ((A) < (B) ? (A) : (B))
|
||||||
#define nghttp2_max(A, B) ((A) > (B) ? (A) : (B))
|
#define nghttp2_max(A, B) ((A) > (B) ? (A) : (B))
|
||||||
@@ -58,25 +59,6 @@ uint16_t nghttp2_get_uint16(const uint8_t *data);
|
|||||||
*/
|
*/
|
||||||
uint32_t nghttp2_get_uint32(const uint8_t *data);
|
uint32_t nghttp2_get_uint32(const uint8_t *data);
|
||||||
|
|
||||||
/*
|
|
||||||
* Ensures that buffer |*buf_ptr| with |*buflen_ptr| length has at
|
|
||||||
* least |min_length| bytes. If |min_length| > |*buflen_ptr|,
|
|
||||||
* allocates new buffer having at least |min_length| bytes and assigns
|
|
||||||
* its pointer to |*buf_ptr| and allocated number of bytes to
|
|
||||||
* |*buflen_ptr|. The memory pointed by |*buf_ptr| previously may
|
|
||||||
* change. No memory copy is done between old and new buffer.
|
|
||||||
* |*buf_ptr| and |*buflen_ptr| are only updated iff this function
|
|
||||||
* succeeds.
|
|
||||||
*
|
|
||||||
* This function returns 0 if it succeeds, or one of the following
|
|
||||||
* negative error codes:
|
|
||||||
*
|
|
||||||
* NGHTTP2_ERR_NOMEM
|
|
||||||
* Out of memory.
|
|
||||||
*/
|
|
||||||
int nghttp2_reserve_buffer(uint8_t **buf_ptr, size_t *buflen_ptr,
|
|
||||||
size_t min_length);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allocates |n| bytes of memory and copy the memory region pointed by
|
* Allocates |n| bytes of memory and copy the memory region pointed by
|
||||||
* |src| with the length |n| bytes into it. Returns the allocated memory.
|
* |src| with the length |n| bytes into it. Returns the allocated memory.
|
||||||
@@ -87,7 +69,7 @@ int nghttp2_reserve_buffer(uint8_t **buf_ptr, size_t *buflen_ptr,
|
|||||||
* NGHTTP2_ERR_NOMEM
|
* NGHTTP2_ERR_NOMEM
|
||||||
* Out of memory.
|
* Out of memory.
|
||||||
*/
|
*/
|
||||||
void* nghttp2_memdup(const void* src, size_t n);
|
void *nghttp2_memdup(const void *src, size_t n, nghttp2_mem *mem);
|
||||||
|
|
||||||
void nghttp2_downcase(uint8_t *s, size_t len);
|
void nghttp2_downcase(uint8_t *s, size_t len);
|
||||||
|
|
||||||
@@ -118,14 +100,6 @@ int nghttp2_adjust_local_window_size(int32_t *local_window_size_ptr,
|
|||||||
int nghttp2_should_send_window_update(int32_t local_window_size,
|
int nghttp2_should_send_window_update(int32_t local_window_size,
|
||||||
int32_t recv_window_size);
|
int32_t recv_window_size);
|
||||||
|
|
||||||
/*
|
|
||||||
* Deallocates memory space pointed by |ptr|. This function exists for
|
|
||||||
* the application to free the memory space allocated by the library
|
|
||||||
* functions. Currently this function is hidden from the public API,
|
|
||||||
* but may be exposed as public API.
|
|
||||||
*/
|
|
||||||
void nghttp2_free(void *ptr);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copies the buffer |src| of length |len| to the destination pointed
|
* Copies the buffer |src| of length |len| to the destination pointed
|
||||||
* by the |dest|, assuming that the |dest| is at lest |len| bytes long
|
* by the |dest|, assuming that the |dest| is at lest |len| bytes long
|
||||||
|
|||||||
@@ -34,7 +34,9 @@
|
|||||||
#ifdef DEBUGBUILD
|
#ifdef DEBUGBUILD
|
||||||
#define DEBUGF(x) x
|
#define DEBUGF(x) x
|
||||||
#else
|
#else
|
||||||
#define DEBUGF(x) do { } while(0)
|
#define DEBUGF(x) \
|
||||||
|
do { \
|
||||||
|
} while (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef int (*nghttp2_compar)(const void *lhs, const void *rhs);
|
typedef int (*nghttp2_compar)(const void *lhs, const void *rhs);
|
||||||
|
|||||||
@@ -26,12 +26,13 @@
|
|||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#define INITIAL_TABLE_LENGTH 16
|
#define INITIAL_TABLE_LENGTH 256
|
||||||
|
|
||||||
int nghttp2_map_init(nghttp2_map *map)
|
int nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem) {
|
||||||
{
|
map->mem = mem;
|
||||||
map->tablelen = INITIAL_TABLE_LENGTH;
|
map->tablelen = INITIAL_TABLE_LENGTH;
|
||||||
map->table = calloc(map->tablelen, sizeof(nghttp2_map_entry*));
|
map->table =
|
||||||
|
nghttp2_mem_calloc(mem, map->tablelen, sizeof(nghttp2_map_entry *));
|
||||||
if (map->table == NULL) {
|
if (map->table == NULL) {
|
||||||
return NGHTTP2_ERR_NOMEM;
|
return NGHTTP2_ERR_NOMEM;
|
||||||
}
|
}
|
||||||
@@ -41,15 +42,13 @@ int nghttp2_map_init(nghttp2_map *map)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_map_free(nghttp2_map *map)
|
void nghttp2_map_free(nghttp2_map *map) {
|
||||||
{
|
nghttp2_mem_free(map->mem, map->table);
|
||||||
free(map->table);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_map_each_free(nghttp2_map *map,
|
void nghttp2_map_each_free(nghttp2_map *map,
|
||||||
int (*func)(nghttp2_map_entry *entry, void *ptr),
|
int (*func)(nghttp2_map_entry *entry, void *ptr),
|
||||||
void *ptr)
|
void *ptr) {
|
||||||
{
|
|
||||||
size_t i;
|
size_t i;
|
||||||
for (i = 0; i < map->tablelen; ++i) {
|
for (i = 0; i < map->tablelen; ++i) {
|
||||||
nghttp2_map_entry *entry;
|
nghttp2_map_entry *entry;
|
||||||
@@ -64,8 +63,7 @@ void nghttp2_map_each_free(nghttp2_map *map,
|
|||||||
|
|
||||||
int nghttp2_map_each(nghttp2_map *map,
|
int nghttp2_map_each(nghttp2_map *map,
|
||||||
int (*func)(nghttp2_map_entry *entry, void *ptr),
|
int (*func)(nghttp2_map_entry *entry, void *ptr),
|
||||||
void *ptr)
|
void *ptr) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
size_t i;
|
size_t i;
|
||||||
for (i = 0; i < map->tablelen; ++i) {
|
for (i = 0; i < map->tablelen; ++i) {
|
||||||
@@ -80,24 +78,21 @@ int nghttp2_map_each(nghttp2_map *map,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_map_entry_init(nghttp2_map_entry *entry, key_type key)
|
void nghttp2_map_entry_init(nghttp2_map_entry *entry, key_type key) {
|
||||||
{
|
|
||||||
entry->key = key;
|
entry->key = key;
|
||||||
entry->next = NULL;
|
entry->next = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Same hash function in android HashMap source code. */
|
/* Same hash function in android HashMap source code. */
|
||||||
/* The |mod| must be power of 2 */
|
/* The |mod| must be power of 2 */
|
||||||
static int32_t hash(int32_t h, size_t mod)
|
static int32_t hash(int32_t h, size_t mod) {
|
||||||
{
|
|
||||||
h ^= (h >> 20) ^ (h >> 12);
|
h ^= (h >> 20) ^ (h >> 12);
|
||||||
h ^= (h >> 7) ^ (h >> 4);
|
h ^= (h >> 7) ^ (h >> 4);
|
||||||
return h & (mod - 1);
|
return h & (mod - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int insert(nghttp2_map_entry **table, size_t tablelen,
|
static int insert(nghttp2_map_entry **table, size_t tablelen,
|
||||||
nghttp2_map_entry *entry)
|
nghttp2_map_entry *entry) {
|
||||||
{
|
|
||||||
int32_t h = hash(entry->key, tablelen);
|
int32_t h = hash(entry->key, tablelen);
|
||||||
if (table[h] == NULL) {
|
if (table[h] == NULL) {
|
||||||
table[h] = entry;
|
table[h] = entry;
|
||||||
@@ -116,11 +111,12 @@ static int insert(nghttp2_map_entry **table, size_t tablelen,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* new_tablelen must be power of 2 */
|
/* new_tablelen must be power of 2 */
|
||||||
static int resize(nghttp2_map *map, size_t new_tablelen)
|
static int resize(nghttp2_map *map, size_t new_tablelen) {
|
||||||
{
|
|
||||||
size_t i;
|
size_t i;
|
||||||
nghttp2_map_entry **new_table;
|
nghttp2_map_entry **new_table;
|
||||||
new_table = calloc(new_tablelen, sizeof(nghttp2_map_entry*));
|
|
||||||
|
new_table =
|
||||||
|
nghttp2_mem_calloc(map->mem, new_tablelen, sizeof(nghttp2_map_entry *));
|
||||||
if (new_table == NULL) {
|
if (new_table == NULL) {
|
||||||
return NGHTTP2_ERR_NOMEM;
|
return NGHTTP2_ERR_NOMEM;
|
||||||
}
|
}
|
||||||
@@ -135,14 +131,14 @@ static int resize(nghttp2_map *map, size_t new_tablelen)
|
|||||||
entry = next;
|
entry = next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
free(map->table);
|
nghttp2_mem_free(map->mem, map->table);
|
||||||
map->tablelen = new_tablelen;
|
map->tablelen = new_tablelen;
|
||||||
map->table = new_table;
|
map->table = new_table;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_entry *new_entry)
|
int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_entry *new_entry) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
/* Load factor is 0.75 */
|
/* Load factor is 0.75 */
|
||||||
if ((map->size + 1) * 4 > map->tablelen * 3) {
|
if ((map->size + 1) * 4 > map->tablelen * 3) {
|
||||||
@@ -159,8 +155,7 @@ int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_entry *new_entry)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
nghttp2_map_entry* nghttp2_map_find(nghttp2_map *map, key_type key)
|
nghttp2_map_entry *nghttp2_map_find(nghttp2_map *map, key_type key) {
|
||||||
{
|
|
||||||
int32_t h;
|
int32_t h;
|
||||||
nghttp2_map_entry *entry;
|
nghttp2_map_entry *entry;
|
||||||
h = hash(key, map->tablelen);
|
h = hash(key, map->tablelen);
|
||||||
@@ -172,8 +167,7 @@ nghttp2_map_entry* nghttp2_map_find(nghttp2_map *map, key_type key)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_map_remove(nghttp2_map *map, key_type key)
|
int nghttp2_map_remove(nghttp2_map *map, key_type key) {
|
||||||
{
|
|
||||||
int32_t h;
|
int32_t h;
|
||||||
nghttp2_map_entry *entry, *prev;
|
nghttp2_map_entry *entry, *prev;
|
||||||
h = hash(key, map->tablelen);
|
h = hash(key, map->tablelen);
|
||||||
@@ -193,7 +187,4 @@ int nghttp2_map_remove(nghttp2_map *map, key_type key)
|
|||||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t nghttp2_map_size(nghttp2_map *map)
|
size_t nghttp2_map_size(nghttp2_map *map) { return map->size; }
|
||||||
{
|
|
||||||
return map->size;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
#include <nghttp2/nghttp2.h>
|
#include <nghttp2/nghttp2.h>
|
||||||
#include "nghttp2_int.h"
|
#include "nghttp2_int.h"
|
||||||
|
#include "nghttp2_mem.h"
|
||||||
|
|
||||||
/* Implementation of unordered map */
|
/* Implementation of unordered map */
|
||||||
|
|
||||||
@@ -43,6 +44,7 @@ typedef struct nghttp2_map_entry {
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
nghttp2_map_entry **table;
|
nghttp2_map_entry **table;
|
||||||
|
nghttp2_mem *mem;
|
||||||
size_t tablelen;
|
size_t tablelen;
|
||||||
size_t size;
|
size_t size;
|
||||||
} nghttp2_map;
|
} nghttp2_map;
|
||||||
@@ -56,7 +58,7 @@ typedef struct {
|
|||||||
* NGHTTP2_ERR_NOMEM
|
* NGHTTP2_ERR_NOMEM
|
||||||
* Out of memory
|
* Out of memory
|
||||||
*/
|
*/
|
||||||
int nghttp2_map_init(nghttp2_map *map);
|
int nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Deallocates any resources allocated for |map|. The stored entries
|
* Deallocates any resources allocated for |map|. The stored entries
|
||||||
|
|||||||
61
lib/nghttp2_mem.c
Normal file
61
lib/nghttp2_mem.c
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* nghttp2 - HTTP/2 C Library
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014 Tatsuhiro Tsujikawa
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include "nghttp2_mem.h"
|
||||||
|
|
||||||
|
static void *default_malloc(size_t size, void *mem_user_data _U_) {
|
||||||
|
return malloc(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void default_free(void *ptr, void *mem_user_data _U_) { free(ptr); }
|
||||||
|
|
||||||
|
static void *default_calloc(size_t nmemb, size_t size,
|
||||||
|
void *mem_user_data _U_) {
|
||||||
|
return calloc(nmemb, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *default_realloc(void *ptr, size_t size, void *mem_user_data _U_) {
|
||||||
|
return realloc(ptr, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static nghttp2_mem mem_default = {NULL, default_malloc, default_free,
|
||||||
|
default_calloc, default_realloc};
|
||||||
|
|
||||||
|
nghttp2_mem *nghttp2_mem_default(void) { return &mem_default; }
|
||||||
|
|
||||||
|
void *nghttp2_mem_malloc(nghttp2_mem *mem, size_t size) {
|
||||||
|
return mem->malloc(size, mem->mem_user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nghttp2_mem_free(nghttp2_mem *mem, void *ptr) {
|
||||||
|
mem->free(ptr, mem->mem_user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *nghttp2_mem_calloc(nghttp2_mem *mem, size_t nmemb, size_t size) {
|
||||||
|
return mem->calloc(nmemb, size, mem->mem_user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *nghttp2_mem_realloc(nghttp2_mem *mem, void *ptr, size_t size) {
|
||||||
|
return mem->realloc(ptr, size, mem->mem_user_data);
|
||||||
|
}
|
||||||
44
lib/nghttp2_mem.h
Normal file
44
lib/nghttp2_mem.h
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* nghttp2 - HTTP/2 C Library
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014 Tatsuhiro Tsujikawa
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#ifndef NGHTTP2_MEM_H
|
||||||
|
#define NGHTTP2_MEM_H
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif /* HAVE_CONFIG_H */
|
||||||
|
|
||||||
|
#include <nghttp2/nghttp2.h>
|
||||||
|
|
||||||
|
/* The default, system standard memory allocator */
|
||||||
|
nghttp2_mem *nghttp2_mem_default(void);
|
||||||
|
|
||||||
|
/* Convenient wrapper functions to call allocator function in
|
||||||
|
|mem|. */
|
||||||
|
void *nghttp2_mem_malloc(nghttp2_mem *mem, size_t size);
|
||||||
|
void nghttp2_mem_free(nghttp2_mem *mem, void *ptr);
|
||||||
|
void *nghttp2_mem_calloc(nghttp2_mem *mem, size_t nmemb, size_t size);
|
||||||
|
void *nghttp2_mem_realloc(nghttp2_mem *mem, void *ptr, size_t size);
|
||||||
|
|
||||||
|
#endif /* NGHTTP2_MEM_H */
|
||||||
@@ -27,13 +27,11 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
int nghttp2_select_next_protocol(unsigned char **out, unsigned char *outlen,
|
int nghttp2_select_next_protocol(unsigned char **out, unsigned char *outlen,
|
||||||
const unsigned char *in, unsigned int inlen)
|
const unsigned char *in, unsigned int inlen) {
|
||||||
{
|
|
||||||
int http_selected = 0;
|
int http_selected = 0;
|
||||||
unsigned int i = 0;
|
unsigned int i = 0;
|
||||||
for (; i < inlen; i += in [i] + 1) {
|
for (; i < inlen; i += in [i] + 1) {
|
||||||
if(in[i] == NGHTTP2_PROTO_VERSION_ID_LEN &&
|
if (in[i] == NGHTTP2_PROTO_VERSION_ID_LEN && i + 1 + in[i] <= inlen &&
|
||||||
i + 1 + in[i] <= inlen &&
|
|
||||||
memcmp(&in[i + 1], NGHTTP2_PROTO_VERSION_ID, in[i]) == 0) {
|
memcmp(&in[i + 1], NGHTTP2_PROTO_VERSION_ID, in[i]) == 0) {
|
||||||
*out = (unsigned char *)&in[i + 1];
|
*out = (unsigned char *)&in[i + 1];
|
||||||
*outlen = in[i];
|
*outlen = in[i];
|
||||||
|
|||||||
@@ -24,8 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
#include "nghttp2_option.h"
|
#include "nghttp2_option.h"
|
||||||
|
|
||||||
int nghttp2_option_new(nghttp2_option **option_ptr)
|
int nghttp2_option_new(nghttp2_option **option_ptr) {
|
||||||
{
|
|
||||||
*option_ptr = calloc(1, sizeof(nghttp2_option));
|
*option_ptr = calloc(1, sizeof(nghttp2_option));
|
||||||
|
|
||||||
if (*option_ptr == NULL) {
|
if (*option_ptr == NULL) {
|
||||||
@@ -35,20 +34,20 @@ int nghttp2_option_new(nghttp2_option **option_ptr)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_option_del(nghttp2_option *option)
|
void nghttp2_option_del(nghttp2_option *option) { free(option); }
|
||||||
{
|
|
||||||
free(option);
|
|
||||||
}
|
|
||||||
|
|
||||||
void nghttp2_option_set_no_auto_window_update(nghttp2_option *option, int val)
|
void nghttp2_option_set_no_auto_window_update(nghttp2_option *option, int val) {
|
||||||
{
|
|
||||||
option->opt_set_mask |= NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE;
|
option->opt_set_mask |= NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE;
|
||||||
option->no_auto_window_update = val;
|
option->no_auto_window_update = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_option_set_peer_max_concurrent_streams(nghttp2_option *option,
|
void nghttp2_option_set_peer_max_concurrent_streams(nghttp2_option *option,
|
||||||
uint32_t val)
|
uint32_t val) {
|
||||||
{
|
|
||||||
option->opt_set_mask |= NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS;
|
option->opt_set_mask |= NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS;
|
||||||
option->peer_max_concurrent_streams = val;
|
option->peer_max_concurrent_streams = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void nghttp2_option_set_recv_client_preface(nghttp2_option *option, int val) {
|
||||||
|
option->opt_set_mask |= NGHTTP2_OPT_RECV_CLIENT_PREFACE;
|
||||||
|
option->recv_client_preface = val;
|
||||||
|
}
|
||||||
|
|||||||
@@ -56,7 +56,8 @@ typedef enum {
|
|||||||
* will be overwritten if the local endpoint receives
|
* will be overwritten if the local endpoint receives
|
||||||
* SETTINGS_MAX_CONCURRENT_STREAMS from the remote endpoint.
|
* SETTINGS_MAX_CONCURRENT_STREAMS from the remote endpoint.
|
||||||
*/
|
*/
|
||||||
NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS = 1 << 1
|
NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS = 1 << 1,
|
||||||
|
NGHTTP2_OPT_RECV_CLIENT_PREFACE = 1 << 2,
|
||||||
} nghttp2_option_flag;
|
} nghttp2_option_flag;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -76,6 +77,10 @@ struct nghttp2_option {
|
|||||||
* NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE
|
* NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE
|
||||||
*/
|
*/
|
||||||
uint8_t no_auto_window_update;
|
uint8_t no_auto_window_update;
|
||||||
|
/**
|
||||||
|
* NGHTTP2_OPT_RECV_CLIENT_PREFACE
|
||||||
|
*/
|
||||||
|
uint8_t recv_client_preface;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* NGHTTP2_OPTION_H */
|
#endif /* NGHTTP2_OPTION_H */
|
||||||
|
|||||||
@@ -26,20 +26,21 @@
|
|||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
void nghttp2_outbound_item_free(nghttp2_outbound_item *item)
|
void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem) {
|
||||||
{
|
nghttp2_frame *frame;
|
||||||
|
|
||||||
if (item == NULL) {
|
if (item == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(item->frame_cat == NGHTTP2_CAT_CTRL) {
|
|
||||||
nghttp2_frame *frame;
|
frame = &item->frame;
|
||||||
frame = nghttp2_outbound_item_get_ctrl_frame(item);
|
|
||||||
switch (frame->hd.type) {
|
switch (frame->hd.type) {
|
||||||
|
case NGHTTP2_DATA:
|
||||||
|
nghttp2_frame_data_free(&frame->data);
|
||||||
|
break;
|
||||||
case NGHTTP2_HEADERS:
|
case NGHTTP2_HEADERS:
|
||||||
nghttp2_frame_headers_free(&frame->headers);
|
nghttp2_frame_headers_free(&frame->headers, mem);
|
||||||
if(item->aux_data) {
|
|
||||||
free(((nghttp2_headers_aux_data*)item->aux_data)->data_prd);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case NGHTTP2_PRIORITY:
|
case NGHTTP2_PRIORITY:
|
||||||
nghttp2_frame_priority_free(&frame->priority);
|
nghttp2_frame_priority_free(&frame->priority);
|
||||||
@@ -48,33 +49,19 @@ void nghttp2_outbound_item_free(nghttp2_outbound_item *item)
|
|||||||
nghttp2_frame_rst_stream_free(&frame->rst_stream);
|
nghttp2_frame_rst_stream_free(&frame->rst_stream);
|
||||||
break;
|
break;
|
||||||
case NGHTTP2_SETTINGS:
|
case NGHTTP2_SETTINGS:
|
||||||
nghttp2_frame_settings_free(&frame->settings);
|
nghttp2_frame_settings_free(&frame->settings, mem);
|
||||||
break;
|
break;
|
||||||
case NGHTTP2_PUSH_PROMISE:
|
case NGHTTP2_PUSH_PROMISE:
|
||||||
nghttp2_frame_push_promise_free(&frame->push_promise);
|
nghttp2_frame_push_promise_free(&frame->push_promise, mem);
|
||||||
break;
|
break;
|
||||||
case NGHTTP2_PING:
|
case NGHTTP2_PING:
|
||||||
nghttp2_frame_ping_free(&frame->ping);
|
nghttp2_frame_ping_free(&frame->ping);
|
||||||
break;
|
break;
|
||||||
case NGHTTP2_GOAWAY:
|
case NGHTTP2_GOAWAY:
|
||||||
nghttp2_frame_goaway_free(&frame->goaway);
|
nghttp2_frame_goaway_free(&frame->goaway, mem);
|
||||||
break;
|
break;
|
||||||
case NGHTTP2_WINDOW_UPDATE:
|
case NGHTTP2_WINDOW_UPDATE:
|
||||||
nghttp2_frame_window_update_free(&frame->window_update);
|
nghttp2_frame_window_update_free(&frame->window_update);
|
||||||
break;
|
break;
|
||||||
case NGHTTP2_EXT_ALTSVC:
|
|
||||||
nghttp2_frame_altsvc_free(&frame->ext);
|
|
||||||
free(frame->ext.payload);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
} else if(item->frame_cat == NGHTTP2_CAT_DATA) {
|
|
||||||
nghttp2_private_data *data_frame;
|
|
||||||
data_frame = nghttp2_outbound_item_get_data_frame(item);
|
|
||||||
nghttp2_frame_private_data_free(data_frame);
|
|
||||||
} else {
|
|
||||||
/* Unreachable */
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
free(item->frame);
|
|
||||||
free(item->aux_data);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
#include <nghttp2/nghttp2.h>
|
#include <nghttp2/nghttp2.h>
|
||||||
#include "nghttp2_frame.h"
|
#include "nghttp2_frame.h"
|
||||||
|
#include "nghttp2_mem.h"
|
||||||
|
|
||||||
/* A bit higher weight for non-DATA frames */
|
/* A bit higher weight for non-DATA frames */
|
||||||
#define NGHTTP2_OB_EX_WEIGHT 300
|
#define NGHTTP2_OB_EX_WEIGHT 300
|
||||||
@@ -39,21 +40,67 @@
|
|||||||
/* Highest weight for PING */
|
/* Highest weight for PING */
|
||||||
#define NGHTTP2_OB_PING_WEIGHT 302
|
#define NGHTTP2_OB_PING_WEIGHT 302
|
||||||
|
|
||||||
|
/* struct used for HEADERS and PUSH_PROMISE frame */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
nghttp2_data_provider *data_prd;
|
nghttp2_data_provider data_prd;
|
||||||
void *stream_user_data;
|
void *stream_user_data;
|
||||||
|
/* nonzero if this item should be attached to stream object to make
|
||||||
|
it under priority control */
|
||||||
|
uint8_t attach_stream;
|
||||||
} nghttp2_headers_aux_data;
|
} nghttp2_headers_aux_data;
|
||||||
|
|
||||||
|
/* struct used for DATA frame */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
/**
|
||||||
|
* The data to be sent for this DATA frame.
|
||||||
|
*/
|
||||||
|
nghttp2_data_provider data_prd;
|
||||||
|
/**
|
||||||
|
* The flags of DATA frame. We use separate flags here and
|
||||||
|
* nghttp2_data frame. The latter contains flags actually sent to
|
||||||
|
* peer. This |flags| may contain NGHTTP2_FLAG_END_STREAM and only
|
||||||
|
* when |eof| becomes nonzero, flags in nghttp2_data has
|
||||||
|
* NGHTTP2_FLAG_END_STREAM set.
|
||||||
|
*/
|
||||||
|
uint8_t flags;
|
||||||
|
/**
|
||||||
|
* The flag to indicate whether EOF was reached or not. Initially
|
||||||
|
* |eof| is 0. It becomes 1 after all data were read.
|
||||||
|
*/
|
||||||
|
uint8_t eof;
|
||||||
|
} nghttp2_data_aux_data;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
NGHTTP2_GOAWAY_AUX_NONE = 0x0,
|
||||||
|
/* indicates that session should be terminated after the
|
||||||
|
transmission of this frame. */
|
||||||
|
NGHTTP2_GOAWAY_AUX_TERM_ON_SEND = 0x1,
|
||||||
|
/* indicates that this GOAWAY is just a notification for graceful
|
||||||
|
shutdown. No nghttp2_session.goaway_flags should be updated on
|
||||||
|
the reaction to this frame. */
|
||||||
|
NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE = 0x2,
|
||||||
|
} nghttp2_goaway_aux_flag;
|
||||||
|
|
||||||
|
/* struct used for GOAWAY frame */
|
||||||
|
typedef struct {
|
||||||
|
/* bitwise-OR of one or more of nghttp2_goaway_aux_flag. */
|
||||||
|
uint8_t flags;
|
||||||
|
} nghttp2_goaway_aux_data;
|
||||||
|
|
||||||
|
/* Additional data which cannot be stored in nghttp2_frame struct */
|
||||||
|
typedef union {
|
||||||
|
nghttp2_data_aux_data data;
|
||||||
|
nghttp2_headers_aux_data headers;
|
||||||
|
nghttp2_goaway_aux_data goaway;
|
||||||
|
} nghttp2_aux_data;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
nghttp2_frame frame;
|
||||||
|
nghttp2_aux_data aux_data;
|
||||||
int64_t seq;
|
int64_t seq;
|
||||||
/* Reset count of weight. See comment for last_cycle in
|
/* Reset count of weight. See comment for last_cycle in
|
||||||
nghttp2_session.h */
|
nghttp2_session.h */
|
||||||
uint64_t cycle;
|
uint64_t cycle;
|
||||||
void *frame;
|
|
||||||
void *aux_data;
|
|
||||||
/* Type of |frame|. NGHTTP2_CTRL: nghttp2_frame*, NGHTTP2_DATA:
|
|
||||||
nghttp2_private_data* */
|
|
||||||
nghttp2_frame_category frame_cat;
|
|
||||||
/* The priority used in priority comparion. Larger is served
|
/* The priority used in priority comparion. Larger is served
|
||||||
ealier. */
|
ealier. */
|
||||||
int32_t weight;
|
int32_t weight;
|
||||||
@@ -65,13 +112,6 @@ typedef struct {
|
|||||||
* Deallocates resource for |item|. If |item| is NULL, this function
|
* Deallocates resource for |item|. If |item| is NULL, this function
|
||||||
* does nothing.
|
* does nothing.
|
||||||
*/
|
*/
|
||||||
void nghttp2_outbound_item_free(nghttp2_outbound_item *item);
|
void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem);
|
||||||
|
|
||||||
/* Macros to cast nghttp2_outbound_item.frame to the proper type. */
|
|
||||||
#define nghttp2_outbound_item_get_ctrl_frame(ITEM) ((nghttp2_frame*)ITEM->frame)
|
|
||||||
#define nghttp2_outbound_item_get_ctrl_frame_type(ITEM) \
|
|
||||||
(((nghttp2_frame*)ITEM->frame)->hd.type)
|
|
||||||
#define nghttp2_outbound_item_get_data_frame(ITEM) \
|
|
||||||
((nghttp2_private_data*)ITEM->frame)
|
|
||||||
|
|
||||||
#endif /* NGHTTP2_OUTBOUND_ITEM_H */
|
#endif /* NGHTTP2_OUTBOUND_ITEM_H */
|
||||||
|
|||||||
@@ -24,10 +24,10 @@
|
|||||||
*/
|
*/
|
||||||
#include "nghttp2_pq.h"
|
#include "nghttp2_pq.h"
|
||||||
|
|
||||||
int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_compar compar)
|
int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_compar compar, nghttp2_mem *mem) {
|
||||||
{
|
pq->mem = mem;
|
||||||
pq->capacity = 128;
|
pq->capacity = 128;
|
||||||
pq->q = malloc(pq->capacity * sizeof(void*));
|
pq->q = nghttp2_mem_malloc(mem, pq->capacity * sizeof(void *));
|
||||||
if (pq->q == NULL) {
|
if (pq->q == NULL) {
|
||||||
return NGHTTP2_ERR_NOMEM;
|
return NGHTTP2_ERR_NOMEM;
|
||||||
}
|
}
|
||||||
@@ -36,21 +36,18 @@ int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_compar compar)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_pq_free(nghttp2_pq *pq)
|
void nghttp2_pq_free(nghttp2_pq *pq) {
|
||||||
{
|
nghttp2_mem_free(pq->mem, pq->q);
|
||||||
free(pq->q);
|
|
||||||
pq->q = NULL;
|
pq->q = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void swap(nghttp2_pq *pq, size_t i, size_t j)
|
static void swap(nghttp2_pq *pq, size_t i, size_t j) {
|
||||||
{
|
|
||||||
void *t = pq->q[i];
|
void *t = pq->q[i];
|
||||||
pq->q[i] = pq->q[j];
|
pq->q[i] = pq->q[j];
|
||||||
pq->q[j] = t;
|
pq->q[j] = t;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bubble_up(nghttp2_pq *pq, size_t index)
|
static void bubble_up(nghttp2_pq *pq, size_t index) {
|
||||||
{
|
|
||||||
if (index == 0) {
|
if (index == 0) {
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
@@ -62,11 +59,11 @@ static void bubble_up(nghttp2_pq *pq, size_t index)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_pq_push(nghttp2_pq *pq, void *item)
|
int nghttp2_pq_push(nghttp2_pq *pq, void *item) {
|
||||||
{
|
|
||||||
if (pq->capacity <= pq->length) {
|
if (pq->capacity <= pq->length) {
|
||||||
void *nq;
|
void *nq;
|
||||||
nq = realloc(pq->q, (pq->capacity*2) * sizeof(void*));
|
nq = nghttp2_mem_realloc(pq->mem, pq->q,
|
||||||
|
(pq->capacity * 2) * sizeof(void *));
|
||||||
if (nq == NULL) {
|
if (nq == NULL) {
|
||||||
return NGHTTP2_ERR_NOMEM;
|
return NGHTTP2_ERR_NOMEM;
|
||||||
}
|
}
|
||||||
@@ -79,8 +76,7 @@ int nghttp2_pq_push(nghttp2_pq *pq, void *item)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void* nghttp2_pq_top(nghttp2_pq *pq)
|
void *nghttp2_pq_top(nghttp2_pq *pq) {
|
||||||
{
|
|
||||||
if (pq->length == 0) {
|
if (pq->length == 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
} else {
|
} else {
|
||||||
@@ -88,8 +84,7 @@ void* nghttp2_pq_top(nghttp2_pq *pq)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bubble_down(nghttp2_pq *pq, size_t index)
|
static void bubble_down(nghttp2_pq *pq, size_t index) {
|
||||||
{
|
|
||||||
size_t lchild = index * 2 + 1;
|
size_t lchild = index * 2 + 1;
|
||||||
size_t minindex = index;
|
size_t minindex = index;
|
||||||
size_t i, j;
|
size_t i, j;
|
||||||
@@ -108,8 +103,7 @@ static void bubble_down(nghttp2_pq *pq, size_t index)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_pq_pop(nghttp2_pq *pq)
|
void nghttp2_pq_pop(nghttp2_pq *pq) {
|
||||||
{
|
|
||||||
if (pq->length > 0) {
|
if (pq->length > 0) {
|
||||||
pq->q[0] = pq->q[pq->length - 1];
|
pq->q[0] = pq->q[pq->length - 1];
|
||||||
--pq->length;
|
--pq->length;
|
||||||
@@ -117,18 +111,11 @@ void nghttp2_pq_pop(nghttp2_pq *pq)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_pq_empty(nghttp2_pq *pq)
|
int nghttp2_pq_empty(nghttp2_pq *pq) { return pq->length == 0; }
|
||||||
{
|
|
||||||
return pq->length == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t nghttp2_pq_size(nghttp2_pq *pq)
|
size_t nghttp2_pq_size(nghttp2_pq *pq) { return pq->length; }
|
||||||
{
|
|
||||||
return pq->length;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nghttp2_pq_update(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg)
|
void nghttp2_pq_update(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg) {
|
||||||
{
|
|
||||||
size_t i;
|
size_t i;
|
||||||
int rv = 0;
|
int rv = 0;
|
||||||
if (pq->length == 0) {
|
if (pq->length == 0) {
|
||||||
|
|||||||
@@ -31,12 +31,15 @@
|
|||||||
|
|
||||||
#include <nghttp2/nghttp2.h>
|
#include <nghttp2/nghttp2.h>
|
||||||
#include "nghttp2_int.h"
|
#include "nghttp2_int.h"
|
||||||
|
#include "nghttp2_mem.h"
|
||||||
|
|
||||||
/* Implementation of priority queue */
|
/* Implementation of priority queue */
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
/* The pointer to the pointer to the item stored */
|
/* The pointer to the pointer to the item stored */
|
||||||
void **q;
|
void **q;
|
||||||
|
/* Memory allocator */
|
||||||
|
nghttp2_mem *mem;
|
||||||
/* The number of items sotred */
|
/* The number of items sotred */
|
||||||
size_t length;
|
size_t length;
|
||||||
/* The maximum number of items this pq can store. This is
|
/* The maximum number of items this pq can store. This is
|
||||||
@@ -55,7 +58,7 @@ typedef struct {
|
|||||||
* NGHTTP2_ERR_NOMEM
|
* NGHTTP2_ERR_NOMEM
|
||||||
* Out of memory.
|
* Out of memory.
|
||||||
*/
|
*/
|
||||||
int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_compar cmp);
|
int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_compar cmp, nghttp2_mem *mem);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Deallocates any resources allocated for |pq|. The stored items are
|
* Deallocates any resources allocated for |pq|. The stored items are
|
||||||
|
|||||||
@@ -26,23 +26,19 @@
|
|||||||
|
|
||||||
void nghttp2_priority_spec_init(nghttp2_priority_spec *pri_spec,
|
void nghttp2_priority_spec_init(nghttp2_priority_spec *pri_spec,
|
||||||
int32_t stream_id, int32_t weight,
|
int32_t stream_id, int32_t weight,
|
||||||
int exclusive)
|
int exclusive) {
|
||||||
{
|
|
||||||
pri_spec->stream_id = stream_id;
|
pri_spec->stream_id = stream_id;
|
||||||
pri_spec->weight = weight;
|
pri_spec->weight = weight;
|
||||||
pri_spec->exclusive = exclusive != 0;
|
pri_spec->exclusive = exclusive != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_priority_spec_default_init(nghttp2_priority_spec *pri_spec)
|
void nghttp2_priority_spec_default_init(nghttp2_priority_spec *pri_spec) {
|
||||||
{
|
|
||||||
pri_spec->stream_id = 0;
|
pri_spec->stream_id = 0;
|
||||||
pri_spec->weight = NGHTTP2_DEFAULT_WEIGHT;
|
pri_spec->weight = NGHTTP2_DEFAULT_WEIGHT;
|
||||||
pri_spec->exclusive = 0;
|
pri_spec->exclusive = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec)
|
int nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec) {
|
||||||
{
|
|
||||||
return pri_spec->stream_id == 0 &&
|
return pri_spec->stream_id == 0 &&
|
||||||
pri_spec->weight == NGHTTP2_DEFAULT_WEIGHT &&
|
pri_spec->weight == NGHTTP2_DEFAULT_WEIGHT && pri_spec->exclusive == 0;
|
||||||
pri_spec->exclusive == 0;
|
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user