mirror of
https://github.com/nghttp2/nghttp2.git
synced 2025-12-07 02:28:53 +08:00
Compare commits
548 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
0da0140026 | ||
|
|
207b1db6af | ||
|
|
85f605f20d | ||
|
|
5cf07f5c21 | ||
|
|
340b52da84 | ||
|
|
3de678e164 | ||
|
|
0af71ee6be | ||
|
|
d8c0d87c90 | ||
|
|
9b2c24ad68 | ||
|
|
b4bb6a6101 | ||
|
|
8890e593e6 | ||
|
|
ec0a2e7cca | ||
|
|
f8471a5f45 | ||
|
|
f34cbf9b45 | ||
|
|
a23a705121 | ||
|
|
822ec75814 | ||
|
|
0209b7c083 | ||
|
|
70a8fd59b1 | ||
|
|
223242b512 | ||
|
|
42ac80d3da | ||
|
|
4c11cd0671 | ||
|
|
4c5c6749a0 | ||
|
|
dd038bf753 | ||
|
|
577512f2ca | ||
|
|
565c635e9b | ||
|
|
0b1ab90fb8 | ||
|
|
93b4d9efc3 | ||
|
|
82bc7198e6 | ||
|
|
03ed29953e | ||
|
|
a36c4c6f5f | ||
|
|
3daa6f2c30 | ||
|
|
53ee21caa9 | ||
|
|
31528b6267 | ||
|
|
ab5b81bee1 | ||
|
|
9f6bb989e3 | ||
|
|
3655090997 | ||
|
|
3cd08251ca | ||
|
|
1673ae2c99 | ||
|
|
bf48ef9bab | ||
|
|
1093b3eeab | ||
|
|
d5da7611fa | ||
|
|
9893ae81af | ||
|
|
7bfa276e96 | ||
|
|
02c5621c61 | ||
|
|
9ad2c0887e | ||
|
|
0c7e2fbec6 | ||
|
|
cfee9cab36 | ||
|
|
679a0a0fa1 | ||
|
|
d5dcbf6f3b | ||
|
|
69b9ce6b68 | ||
|
|
c81e87bf37 | ||
|
|
97533c966d | ||
|
|
d6b495f2a7 | ||
|
|
c7e9fe8154 | ||
|
|
fb62a5ed4f | ||
|
|
345121975a | ||
|
|
a5dfe24e49 | ||
|
|
273d9f4f7d | ||
|
|
500c5eea56 | ||
|
|
83a39f5b49 | ||
|
|
2fb675f13c | ||
|
|
eebd1f5492 | ||
|
|
00ead22395 | ||
|
|
2b4bd57c7f | ||
|
|
86dd1519b4 | ||
|
|
a507fc80b6 | ||
|
|
69c3920a1a | ||
|
|
bbe4f5a3d1 | ||
|
|
49a9ec2cb3 | ||
|
|
5d2390deba | ||
|
|
02c347fe6b | ||
|
|
3c056973a1 | ||
|
|
d17f35a488 | ||
|
|
1b8ad61779 | ||
|
|
ede801d099 | ||
|
|
9649b2d346 | ||
|
|
53e52194b5 | ||
|
|
0e8419ac37 | ||
|
|
da08ba5d50 | ||
|
|
30fa6d24d0 | ||
|
|
f776c50d43 | ||
|
|
7b85f6c50d | ||
|
|
21cbf417c8 | ||
|
|
ca680c16e3 | ||
|
|
bf13d91264 | ||
|
|
8aa6580d89 | ||
|
|
11fa71ba6c | ||
|
|
58afce2382 | ||
|
|
24cfb52b5a | ||
|
|
d499803221 | ||
|
|
894783f572 | ||
|
|
93ed89df5f | ||
|
|
1a2e50ca08 | ||
|
|
76703f79fa | ||
|
|
4679188069 | ||
|
|
4c3aa081a0 | ||
|
|
ab9b0538bc | ||
|
|
24edd2972d | ||
|
|
a8b7fa524f | ||
|
|
b827b99b2f | ||
|
|
88add854ff | ||
|
|
704bbbfcaa | ||
|
|
e217e789de | ||
|
|
d4d56e1846 | ||
|
|
8883df2fd6 | ||
|
|
d496c42dc9 | ||
|
|
8433a75a6b | ||
|
|
76b3ba2832 | ||
|
|
05f982dcfb | ||
|
|
cc24b9aaf0 | ||
|
|
e6695d9ba7 | ||
|
|
9fb2bc8468 | ||
|
|
6ccf06c6da | ||
|
|
49e3fd6862 | ||
|
|
9c1a956e47 | ||
|
|
86b089f957 | ||
|
|
3f212a60a5 | ||
|
|
5c61917007 | ||
|
|
8736f61fbd | ||
|
|
04e94824a0 | ||
|
|
ec93c9f55f | ||
|
|
6c71889552 | ||
|
|
4bbb4172aa | ||
|
|
9ccf4c037d | ||
|
|
1f356391f1 | ||
|
|
3c603ec4ae | ||
|
|
d36bea8554 | ||
|
|
16101b8b3f | ||
|
|
455d911f61 | ||
|
|
2fb750f2e3 | ||
|
|
7c781bcd1a | ||
|
|
a234166fc4 | ||
|
|
ecb7e7db74 | ||
|
|
99ceb7df33 | ||
|
|
2e0775d506 | ||
|
|
ac28cd7efa | ||
|
|
a5ec5c1a1d | ||
|
|
7952029752 | ||
|
|
2e083352d5 | ||
|
|
d848b9815a | ||
|
|
48734b6d05 | ||
|
|
8838f666cb | ||
|
|
d8d14a3fc9 | ||
|
|
6e027ad830 | ||
|
|
9d78167297 | ||
|
|
c13329b328 | ||
|
|
b8a2bf2675 | ||
|
|
98be65a1eb | ||
|
|
742b28833a | ||
|
|
77374ac6e2 | ||
|
|
c4be7d48a0 | ||
|
|
0752ce6701 | ||
|
|
8d5422c9bb | ||
|
|
04b5d1679f | ||
|
|
15055c11f9 | ||
|
|
c859fb8f7c | ||
|
|
dd1850aed0 | ||
|
|
079db14d45 | ||
|
|
9f17bee51d | ||
|
|
e904842504 | ||
|
|
4f815521ae | ||
|
|
78df530b90 | ||
|
|
e147c14186 | ||
|
|
06453fb15e | ||
|
|
af5fd2019d | ||
|
|
744ec4dba1 | ||
|
|
38bfbffb1b | ||
|
|
63398f30dd | ||
|
|
961dcf614a | ||
|
|
5b572d8d59 | ||
|
|
8d29710393 | ||
|
|
e839ea8596 | ||
|
|
57e9b94aaa | ||
|
|
9de9b6ebd6 | ||
|
|
44310c6de5 | ||
|
|
139c3b508a | ||
|
|
8207dd33b7 | ||
|
|
d99e1135c8 | ||
|
|
db3b07735b |
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
|
||||
...
|
||||
|
||||
8
.gitignore
vendored
8
.gitignore
vendored
@@ -21,6 +21,7 @@ install-sh
|
||||
.libs
|
||||
lib/includes/nghttp2/nghttp2ver.h
|
||||
lib/libnghttp2.pc
|
||||
src/libnghttp2_asio.pc
|
||||
ltmain.sh
|
||||
stamp-h1
|
||||
.deps/
|
||||
@@ -39,3 +40,10 @@ doc/nghttpx-howto.rst
|
||||
doc/h2load-howto.rst
|
||||
doc/tutorial-hpack.rst
|
||||
doc/python-apiref.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
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
language: cpp
|
||||
compiler:
|
||||
- clang
|
||||
- gcc
|
||||
python:
|
||||
- "3.4"
|
||||
#Disable gcc build for the moment...
|
||||
# - gcc
|
||||
before_install:
|
||||
- $CC --version
|
||||
- sudo add-apt-repository --yes ppa:ubuntu-toolchain-r/test
|
||||
@@ -22,6 +21,7 @@ before_install:
|
||||
libcunit1-dev
|
||||
libssl-dev
|
||||
libxml2-dev
|
||||
libev-dev
|
||||
libevent-dev
|
||||
libjansson-dev
|
||||
libjemalloc-dev
|
||||
@@ -34,3 +34,4 @@ before_script:
|
||||
- ./configure --enable-werror
|
||||
script:
|
||||
- make
|
||||
- make check
|
||||
|
||||
19
COPYING
19
COPYING
@@ -20,3 +20,22 @@ 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.
|
||||
|
||||
[The text below 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!
|
||||
|
||||
100
Dockerfile.android
Normal file
100
Dockerfile.android
Normal file
@@ -0,0 +1,100 @@
|
||||
# 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 https://github.com/downloads/libevent/libevent/libevent-2.0.21-stable.tar.gz
|
||||
RUN tar xf libevent-2.0.21-stable.tar.gz
|
||||
WORKDIR /root/build/libevent-2.0.21-stable
|
||||
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" \
|
||||
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,10 +20,23 @@
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
SUBDIRS = lib third-party src examples python tests doc
|
||||
SUBDIRS = lib third-party src examples python tests doc contrib
|
||||
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
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 \
|
||||
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}
|
||||
|
||||
131
README.rst
131
README.rst
@@ -4,12 +4,14 @@ nghttp2 - HTTP/2 C Library
|
||||
This is an implementation of Hypertext Transfer Protocol version 2
|
||||
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
|
||||
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.
|
||||
|
||||
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
|
||||
everything yet.
|
||||
@@ -17,10 +19,10 @@ everything yet.
|
||||
Development Status
|
||||
------------------
|
||||
|
||||
We started to implement h2-13
|
||||
(http://tools.ietf.org/html/draft-ietf-httpbis-http2-13) and the
|
||||
header compression
|
||||
(http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-08).
|
||||
We started to implement h2-14
|
||||
(http://tools.ietf.org/html/draft-ietf-httpbis-http2-14), the header
|
||||
compression
|
||||
(http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-09).
|
||||
|
||||
The nghttp2 code base was forked from spdylay project.
|
||||
|
||||
@@ -30,35 +32,23 @@ HTTP/2 Features Support
|
||||
Core frames handling Yes
|
||||
Dependency Tree Yes
|
||||
Large header (CONTINUATION) Yes
|
||||
ALTSVC extension Yes \*1
|
||||
=========================== =======
|
||||
|
||||
* \*1 As described in draft-12, but reserved byte was removed. ALTSVC
|
||||
may be removed from nghttp2 public API since it is not stabilized
|
||||
yet.
|
||||
|
||||
BLOCKED frame, which once existed in h2-12, was removed in h2-13.
|
||||
|
||||
Public Test Server
|
||||
------------------
|
||||
|
||||
The following endpoints are available to try out nghttp2
|
||||
implementation.
|
||||
|
||||
* https://nghttp2.org/ (TLS + NPN)
|
||||
* https://nghttp2.org/ (TLS + ALPN/NPN)
|
||||
|
||||
NPN offer ``h2-13``, ``spdy/3.1`` and ``http/1.1``.
|
||||
ALPN is currently disabled.
|
||||
NPN offer ``h2-14``, ``spdy/3.1`` and ``http/1.1``.
|
||||
|
||||
This endpoint requires TLSv1.2 and DHE or EDCHE with GCM cipher
|
||||
suite for HTTP/2 connection.
|
||||
This endpoint requires TLSv1.2 for HTTP/2 connection.
|
||||
|
||||
* http://nghttp2.org/ (Upgrade / Direct)
|
||||
|
||||
``h2c-13`` and ``http/1.1``. We configured this server to send
|
||||
ALTSVC frame or Alt-Svc header field to announce that alternative
|
||||
service is available at port 443.
|
||||
|
||||
``h2c-14`` and ``http/1.1``.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
@@ -81,7 +71,7 @@ To build and run the application programs (``nghttp``, ``nghttpd`` and
|
||||
required:
|
||||
|
||||
* OpenSSL >= 1.0.1
|
||||
* libevent-openssl >= 2.0.8
|
||||
* libev >= 4.15
|
||||
* zlib >= 1.2.3
|
||||
|
||||
ALPN support requires unreleased version OpenSSL >= 1.0.2.
|
||||
@@ -89,7 +79,7 @@ ALPN support requires unreleased version OpenSSL >= 1.0.2.
|
||||
To enable SPDY protocol in the application program ``nghttpx`` and
|
||||
``h2load``, the following package is required:
|
||||
|
||||
* spdylay >= 1.2.3
|
||||
* spdylay >= 1.3.0
|
||||
|
||||
To enable ``-a`` option (getting linked assets from the downloaded
|
||||
resource) in ``nghttp``, the following package is required:
|
||||
@@ -100,11 +90,20 @@ The HPACK tools require the following package:
|
||||
|
||||
* 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
|
||||
(``nghttpd`` and ``nghttpx``), jemalloc is recommended:
|
||||
|
||||
* 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:
|
||||
|
||||
* cython >= 0.19
|
||||
@@ -113,6 +112,8 @@ The Python bindings require the following packages:
|
||||
If you are using Ubuntu 14.04 LTS, you need the following packages
|
||||
installed:
|
||||
|
||||
* make
|
||||
* binutils
|
||||
* autoconf
|
||||
* automake
|
||||
* autotools-dev
|
||||
@@ -122,6 +123,7 @@ installed:
|
||||
* libcunit1-dev
|
||||
* libssl-dev
|
||||
* libxml2-dev
|
||||
* libev-dev
|
||||
* libevent-dev
|
||||
* libjansson-dev
|
||||
* libjemalloc-dev
|
||||
@@ -186,10 +188,10 @@ output from ``nghttp`` client::
|
||||
|
||||
$ src/nghttp -nv https://nghttp2.org
|
||||
[ 0.033][NPN] server offers:
|
||||
* h2-13
|
||||
* h2-14
|
||||
* spdy/3.1
|
||||
* http/1.1
|
||||
The negotiated protocol: h2-13
|
||||
The negotiated protocol: h2-14
|
||||
[ 0.068] send SETTINGS frame <length=15, flags=0x00, stream_id=0>
|
||||
(niv=3)
|
||||
[SETTINGS_MAX_CONCURRENT_STREAMS(3):100]
|
||||
@@ -258,7 +260,7 @@ The HTTP Upgrade is performed like this::
|
||||
GET / HTTP/1.1
|
||||
Host: nghttp2.org
|
||||
Connection: Upgrade, HTTP2-Settings
|
||||
Upgrade: h2c-13
|
||||
Upgrade: h2c-14
|
||||
HTTP2-Settings: AwAAAGQEAAD__wUAAAAB
|
||||
Accept: */*
|
||||
User-Agent: nghttp2/0.4.0-DEV
|
||||
@@ -267,7 +269,7 @@ The HTTP Upgrade is performed like this::
|
||||
[ 0.024] HTTP Upgrade response
|
||||
HTTP/1.1 101 Switching Protocols
|
||||
Connection: Upgrade
|
||||
Upgrade: h2c-13
|
||||
Upgrade: h2c-14
|
||||
|
||||
|
||||
[ 0.024] HTTP Upgrade success
|
||||
@@ -280,8 +282,6 @@ The HTTP Upgrade is performed like this::
|
||||
(niv=2)
|
||||
[SETTINGS_MAX_CONCURRENT_STREAMS(3):100]
|
||||
[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-13, host=nghttp2.org, origin=http://nghttp2.org)
|
||||
[ 0.024] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
|
||||
; ACK
|
||||
(niv=0)
|
||||
@@ -389,7 +389,7 @@ information. Here is sample output from ``nghttpd`` server::
|
||||
nghttpx - proxy
|
||||
+++++++++++++++
|
||||
|
||||
``nghttpx`` is a multi-threaded reverse proxy for ``h2-13``, SPDY and
|
||||
``nghttpx`` is a multi-threaded reverse proxy for ``h2-14``, SPDY and
|
||||
HTTP/1.1 and powers nghttp2.org site. It has several operation modes:
|
||||
|
||||
================== ============================ ============== =============
|
||||
@@ -403,7 +403,7 @@ default mode HTTP/2, SPDY, HTTP/1.1 (TLS) HTTP/1.1 Reverse proxy
|
||||
================== ============================ ============== =============
|
||||
|
||||
The interesting mode at the moment is the default mode. It works like
|
||||
a reverse proxy and listens for ``h2-13``, SPDY and HTTP/1.1 and can
|
||||
a reverse proxy and listens for ``h2-14``, SPDY and HTTP/1.1 and can
|
||||
be deployed SSL/TLS terminator for existing web server.
|
||||
|
||||
The default mode, ``--http2-proxy`` and ``--http2-bridge`` modes use
|
||||
@@ -966,6 +966,45 @@ associated value includes the state of dynamic header table after the
|
||||
corresponding header set was processed. The format is the same as
|
||||
``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
|
||||
---------------
|
||||
|
||||
@@ -1081,3 +1120,29 @@ BaseRequestHandler usage:
|
||||
# give None to ssl to make the server non-SSL/TLS
|
||||
server = nghttp2.HTTP2Server(('127.0.0.1', 8443), Handler, ssl=ctx)
|
||||
server.serve_forever()
|
||||
|
||||
Contribution
|
||||
------------
|
||||
|
||||
[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!
|
||||
|
||||
See `Contribution Guidelines
|
||||
<https://nghttp2.org/documentation/contribute.html>`_ for more
|
||||
details.
|
||||
|
||||
@@ -36,12 +36,10 @@ PATH=$TOOLCHAIN/bin:$PATH
|
||||
--host=arm-linux-androideabi \
|
||||
--build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \
|
||||
--with-xml-prefix="$PREFIX" \
|
||||
--without-libxml2 \
|
||||
--disable-python-bindings \
|
||||
--disable-examples \
|
||||
--enable-werror \
|
||||
--disable-threads \
|
||||
CC=clang \
|
||||
CXX=clang++ \
|
||||
CPPFLAGS="-I$PREFIX/include" \
|
||||
PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \
|
||||
LDFLAGS="-L$PREFIX/lib"
|
||||
|
||||
215
configure.ac
215
configure.ac
@@ -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 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 Do not change user variables!
|
||||
dnl http://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html
|
||||
|
||||
AC_PREREQ(2.61)
|
||||
AC_INIT([nghttp2], [0.5.0], [t-tujikawa@users.sourceforge.net])
|
||||
AC_INIT([nghttp2], [0.7.0], [t-tujikawa@users.sourceforge.net])
|
||||
LT_PREREQ([2.2.6])
|
||||
LT_INIT()
|
||||
dnl See versioning rule:
|
||||
dnl http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
||||
AC_SUBST(LT_CURRENT, 4)
|
||||
AC_SUBST(LT_CURRENT, 8)
|
||||
AC_SUBST(LT_REVISION, 0)
|
||||
AC_SUBST(LT_AGE, 0)
|
||||
AC_SUBST(LT_AGE, 3)
|
||||
|
||||
major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"`
|
||||
minor=`echo $PACKAGE_VERSION |cut -d. -f2 | sed -e "s/[^0-9]//g"`
|
||||
@@ -76,6 +80,11 @@ AC_ARG_ENABLE([hpack-tools],
|
||||
[Build HPACK tools [default=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],
|
||||
[AS_HELP_STRING([--enable-examples],
|
||||
[Build examples [default=check]])],
|
||||
@@ -87,9 +96,9 @@ AC_ARG_ENABLE([python-bindings],
|
||||
[request_python_bindings=$enableval], [request_python_bindings=check])
|
||||
|
||||
AC_ARG_ENABLE([failmalloc],
|
||||
[AS_HELP_STRING([--enable-failmalloc],
|
||||
[Build failmalloc test program [default=no]])],
|
||||
[request_failmalloc=$enableval], [request_failmalloc=no])
|
||||
[AS_HELP_STRING([--disable-failmalloc],
|
||||
[Do not build failmalloc test program])],
|
||||
[request_failmalloc=$enableval], [request_failmalloc=yes])
|
||||
|
||||
AC_ARG_WITH([libxml2],
|
||||
[AS_HELP_STRING([--with-libxml2],
|
||||
@@ -135,6 +144,20 @@ else
|
||||
AC_SUBST([CYTHON])
|
||||
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])
|
||||
|
||||
AC_LANG_PUSH(C++)
|
||||
@@ -179,40 +202,41 @@ AC_LANG_POP()
|
||||
# Checks for libraries.
|
||||
|
||||
# Additional libraries required for tests.
|
||||
TESTS_LIBS=
|
||||
TESTLDFLAGS=
|
||||
|
||||
# Additional libraries required for programs under src directory.
|
||||
SRC_LIBS=
|
||||
APPLDFLAGS=
|
||||
|
||||
LIBS_OLD=$LIBS
|
||||
# Search for dlsym function, which is used in tests. Linux needs -ldl,
|
||||
# but netbsd does not need it.
|
||||
AC_SEARCH_LIBS([dlsym], [dl])
|
||||
TESTS_LIBS="$LIBS $TESTS_LIBS"
|
||||
TESTLDFLAGS="$LIBS $TESTLDFLAGS"
|
||||
LIBS=$LIBS_OLD
|
||||
|
||||
LIBS_OLD=$LIBS
|
||||
AC_SEARCH_LIBS([clock_gettime], [rt],
|
||||
[AC_DEFINE([HAVE_CLOCK_GETTIME], [1],
|
||||
[Define to 1 if you have the `clock_gettime`.])])
|
||||
SRC_LIBS="$LIBS $SRC_LIBS"
|
||||
APPLDFLAGS="$LIBS $APPLDFLAGS"
|
||||
LIBS=$LIBS_OLD
|
||||
|
||||
case "$host" in
|
||||
*android*)
|
||||
android_build=yes
|
||||
# android does not need -pthread, but needs followng 2 libs for C++
|
||||
SRC_LIBS="$SRC_LIBS -lstdc++ -lsupc++"
|
||||
APPLDFLAGS="$APPLDFLAGS -lstdc++ -lsupc++"
|
||||
;;
|
||||
*)
|
||||
SRC_LIBS="$SRC_LIBS -pthread"
|
||||
PTHREAD_LDFLAGS="-pthread"
|
||||
APPLDFLAGS="$APPLDFLAGS $PTHREAD_LDFLAGS"
|
||||
;;
|
||||
esac
|
||||
|
||||
# zlib
|
||||
if test "x$android_build" = "xyes"; then
|
||||
# Use zlib provided by NDK
|
||||
SRC_LIBS="-lz $SRC_LIBS"
|
||||
APPLDFLAGS="-lz $APPLDFLAGS"
|
||||
have_zlib=yes
|
||||
else
|
||||
PKG_CHECK_MODULES([ZLIB], [zlib >= 1.2.3], [have_zlib=yes], [have_zlib=no])
|
||||
@@ -251,6 +275,21 @@ fi
|
||||
|
||||
AM_CONDITIONAL([HAVE_CUNIT], [ test "x${have_cunit}" = "xyes" ])
|
||||
|
||||
# libev (for src)
|
||||
# libev does not have pkg-config file. Check it in an old way.
|
||||
LIBS_OLD=$LIBS
|
||||
AC_CHECK_LIB([ev], [ev_time], [have_libev=yes], [have_libev=no])
|
||||
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)
|
||||
PKG_CHECK_MODULES([OPENSSL], [openssl >= 1.0.1],
|
||||
[have_openssl=yes], [have_openssl=no])
|
||||
@@ -258,7 +297,7 @@ if test "x${have_openssl}" = "xno"; then
|
||||
AC_MSG_NOTICE($OPENSSL_PKG_ERRORS)
|
||||
fi
|
||||
|
||||
# libevent_openssl (for src)
|
||||
# libevent_openssl (for examples)
|
||||
# 2.0.8 is required because we use evconnlistener_set_error_cb()
|
||||
PKG_CHECK_MODULES([LIBEVENT_OPENSSL], [libevent_openssl >= 2.0.8],
|
||||
[have_libevent_openssl=yes], [have_libevent_openssl=no])
|
||||
@@ -266,10 +305,13 @@ if test "x${have_libevent_openssl}" = "xno"; then
|
||||
AC_MSG_NOTICE($LIBEVENT_OPENSSL_PKG_ERRORS)
|
||||
fi
|
||||
|
||||
# jansson (for hdtest/deflatehd and hdtest/inflatehd)
|
||||
# jansson (for src/nghttp, src/deflatehd and src/inflatehd)
|
||||
PKG_CHECK_MODULES([JANSSON], [jansson >= 2.5],
|
||||
[have_jansson=yes], [have_jansson=no])
|
||||
if test "x${have_jansson}" == "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)
|
||||
fi
|
||||
|
||||
@@ -293,7 +335,8 @@ AM_CONDITIONAL([HAVE_LIBXML2], [ test "x${have_libxml2}" = "xyes" ])
|
||||
have_jemalloc=no
|
||||
if test "x${request_jemalloc}" != "xno"; then
|
||||
LIBS_OLD=$LIBS
|
||||
AC_SEARCH_LIBS([malloc_stats_print], [jemalloc], [have_jemalloc=yes])
|
||||
AC_SEARCH_LIBS([malloc_stats_print], [jemalloc], [have_jemalloc=yes], [],
|
||||
[$PTHREAD_LDFLAGS])
|
||||
LIBS=$LIBS_OLD
|
||||
if test "x${have_jemalloc}" = "xyes" &&
|
||||
test "x${ac_cv_search_malloc_stats_print}" != "xnone required"; then
|
||||
@@ -310,7 +353,7 @@ fi
|
||||
# spdylay (for src/nghttpx and src/h2load)
|
||||
have_spdylay=no
|
||||
if test "x${request_spdylay}" != "xno"; then
|
||||
PKG_CHECK_MODULES([LIBSPDYLAY], [libspdylay >= 1.2.3],
|
||||
PKG_CHECK_MODULES([LIBSPDYLAY], [libspdylay >= 1.3.0],
|
||||
[have_spdylay=yes], [have_spdylay=no])
|
||||
if test "x${have_spdylay}" = "xyes"; then
|
||||
AC_DEFINE([HAVE_SPDYLAY], [1], [Define to 1 if you have `spdylay` library.])
|
||||
@@ -327,13 +370,32 @@ fi
|
||||
|
||||
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
|
||||
# and libevent_openssl
|
||||
# and libev
|
||||
enable_app=no
|
||||
if test "x${request_app}" != "xno" &&
|
||||
test "x${have_zlib}" = "xyes" &&
|
||||
test "x${have_openssl}" = "xyes" &&
|
||||
test "x${have_libevent_openssl}" = "xyes"; then
|
||||
test "x${have_libev}" = "xyes"; then
|
||||
enable_app=yes
|
||||
fi
|
||||
|
||||
@@ -343,7 +405,6 @@ if test "x${request_app}" = "xyes" &&
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL([ENABLE_APP], [ test "x${enable_app}" = "xyes" ])
|
||||
AM_CONDITIONAL([ENABLE_H2LOAD], [ test "x${have_std_future}" = "xyes" ])
|
||||
|
||||
enable_hpack_tools=no
|
||||
# HPACK tools requires jansson
|
||||
@@ -359,6 +420,16 @@ fi
|
||||
|
||||
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
|
||||
enable_examples=no
|
||||
if test "x${request_examples}" != "xno" &&
|
||||
@@ -396,6 +467,11 @@ AM_CONDITIONAL([ENABLE_PYTHON_BINDINGS],
|
||||
AM_CONDITIONAL([HAVE_CYTHON], [test "x${CYTHON}" != "x"])
|
||||
|
||||
# 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" ])
|
||||
|
||||
# Checks for header files.
|
||||
@@ -430,17 +506,40 @@ AC_CHECK_TYPES([ptrdiff_t])
|
||||
AC_C_BIGENDIAN
|
||||
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.
|
||||
if test "x$cross_compiling" != "xyes"; then
|
||||
AC_FUNC_MALLOC
|
||||
fi
|
||||
AC_CHECK_FUNCS([ \
|
||||
_Exit \
|
||||
accept4 \
|
||||
getpwnam \
|
||||
memmove \
|
||||
memset \
|
||||
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
|
||||
case "${host}" in
|
||||
*mingw*)
|
||||
@@ -448,6 +547,9 @@ case "${host}" in
|
||||
;;
|
||||
esac
|
||||
|
||||
ac_save_CFLAGS=$CFLAGS
|
||||
CFLAGS=
|
||||
|
||||
if test "x$werror" != "xno"; then
|
||||
AX_CHECK_COMPILE_FLAG([-Wall], [CFLAGS="$CFLAGS -Wall"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wextra], [CFLAGS="$CFLAGS -Wextra"])
|
||||
@@ -469,7 +571,6 @@ if test "x$werror" != "xno"; then
|
||||
AX_CHECK_COMPILE_FLAG([-Wcast-align], [CFLAGS="$CFLAGS -Wcast-align"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wclobbered], [CFLAGS="$CFLAGS -Wclobbered"])
|
||||
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([-Wunreachable-code], [CFLAGS="$CFLAGS -Wunreachable-code"])
|
||||
AX_CHECK_COMPILE_FLAG([-Waddress], [CFLAGS="$CFLAGS -Waddress"])
|
||||
@@ -481,16 +582,23 @@ if test "x$werror" != "xno"; then
|
||||
AX_CHECK_COMPILE_FLAG([-Wheader-guard], [CFLAGS="$CFLAGS -Wheader-guard"])
|
||||
fi
|
||||
|
||||
WARNCFLAGS=$CFLAGS
|
||||
CFLAGS=$ac_save_CFLAGS
|
||||
|
||||
AC_SUBST([WARNCFLAGS])
|
||||
|
||||
if test "x$debug" != "xno"; then
|
||||
AC_DEFINE([DEBUGBUILD], [1], [Define to 1 to enable debug output.])
|
||||
fi
|
||||
|
||||
if test "x$threads" != "xyes"; then
|
||||
# Some platform does not have working std::future. We disable
|
||||
# threading for those platforms to exclude std::future use.
|
||||
if test "x$threads" != "xyes" || test "x$have_std_future" != "xyes"; then
|
||||
AC_DEFINE([NOTHREADS], [1], [Define to 1 if you want to disable threads.])
|
||||
fi
|
||||
|
||||
AC_SUBST([TESTS_LIBS])
|
||||
AC_SUBST([SRC_LIBS])
|
||||
AC_SUBST([TESTLDFLAGS])
|
||||
AC_SUBST([APPLDFLAGS])
|
||||
|
||||
AC_CONFIG_FILES([
|
||||
Makefile
|
||||
@@ -502,6 +610,8 @@ AC_CONFIG_FILES([
|
||||
tests/testdata/Makefile
|
||||
third-party/Makefile
|
||||
src/Makefile
|
||||
src/includes/Makefile
|
||||
src/libnghttp2_asio.pc
|
||||
examples/Makefile
|
||||
python/Makefile
|
||||
python/setup.py
|
||||
@@ -514,9 +624,14 @@ AC_CONFIG_FILES([
|
||||
doc/tutorial-hpack.rst
|
||||
doc/nghttpx-howto.rst
|
||||
doc/h2load-howto.rst
|
||||
doc/libnghttp2_asio.rst
|
||||
doc/python-apiref.rst
|
||||
doc/building-android-binary.rst
|
||||
doc/nghttp2.h.rst
|
||||
doc/nghttp2ver.h.rst
|
||||
doc/asio_http2.h.rst
|
||||
doc/contribute.rst
|
||||
contrib/Makefile
|
||||
])
|
||||
AC_OUTPUT
|
||||
|
||||
@@ -527,6 +642,7 @@ AC_MSG_NOTICE([summary of build options:
|
||||
Install prefix: ${prefix}
|
||||
C compiler: ${CC}
|
||||
CFLAGS: ${CFLAGS}
|
||||
WARNCFLAGS: ${WARNCFLAGS}
|
||||
LDFLAGS: ${LDFLAGS}
|
||||
LIBS: ${LIBS}
|
||||
CPPFLAGS: ${CPPFLAGS}
|
||||
@@ -535,23 +651,34 @@ AC_MSG_NOTICE([summary of build options:
|
||||
CXXFLAGS: ${CXXFLAGS}
|
||||
CXXCPP: ${CXXCPP}
|
||||
Library types: Shared=${enable_shared}, Static=${enable_static}
|
||||
Python: ${PYTHON}
|
||||
PYTHON_VERSION: ${PYTHON_VERSION}
|
||||
pyexecdir: ${pyexecdir}
|
||||
Python-dev: ${have_python_dev}
|
||||
PYTHON_CPPFLAGS:${PYTHON_CPPFLAGS}
|
||||
PYTHON_LDFLAGS: ${PYTHON_LDFLAGS}
|
||||
Cython: ${CYTHON}
|
||||
CUnit: ${have_cunit}
|
||||
OpenSSL: ${have_openssl}
|
||||
Libxml2: ${have_libxml2}
|
||||
Libevent(SSL): ${have_libevent_openssl}
|
||||
Spdylay: ${have_spdylay}
|
||||
Jansson: ${have_jansson}
|
||||
Jemalloc: ${have_jemalloc}
|
||||
Applications: ${enable_app}
|
||||
HPACK tools: ${enable_hpack_tools}
|
||||
Examples: ${enable_examples}
|
||||
Python bindings:${enable_python_bindings}
|
||||
Failmalloc: ${request_failmalloc}
|
||||
Python:
|
||||
Python: ${PYTHON}
|
||||
PYTHON_VERSION: ${PYTHON_VERSION}
|
||||
pyexecdir: ${pyexecdir}
|
||||
Python-dev: ${have_python_dev}
|
||||
PYTHON_CPPFLAGS:${PYTHON_CPPFLAGS}
|
||||
PYTHON_LDFLAGS: ${PYTHON_LDFLAGS}
|
||||
Cython: ${CYTHON}
|
||||
Test:
|
||||
CUnit: ${have_cunit}
|
||||
Failmalloc: ${enable_failmalloc}
|
||||
Libs:
|
||||
OpenSSL: ${have_openssl}
|
||||
Libxml2: ${have_libxml2}
|
||||
Libev: ${have_libev}
|
||||
Libevent(SSL): ${have_libevent_openssl}
|
||||
Spdylay: ${have_spdylay}
|
||||
Jansson: ${have_jansson}
|
||||
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}
|
||||
HPACK tools: ${enable_hpack_tools}
|
||||
Libnghttp2_asio:${enable_asio_lib}
|
||||
Examples: ${enable_examples}
|
||||
Python bindings:${enable_python_bindings}
|
||||
])
|
||||
|
||||
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,7 +37,10 @@ EXTRA_DIST = \
|
||||
sources/tutorial-hpack.rst \
|
||||
sources/nghttpx-howto.rst \
|
||||
sources/h2load-howto.rst \
|
||||
sources/libnghttp2_asio.rst \
|
||||
sources/python-apiref.rst \
|
||||
sources/building-android-binary.rst \
|
||||
sources/contribute.rst \
|
||||
_themes/sphinx_rtd_theme/footer.html \
|
||||
_themes/sphinx_rtd_theme/theme.conf \
|
||||
_themes/sphinx_rtd_theme/layout_old.html \
|
||||
|
||||
8
doc/_themes/sphinx_rtd_theme/layout.html
vendored
8
doc/_themes/sphinx_rtd_theme/layout.html
vendored
@@ -23,7 +23,7 @@
|
||||
{% endif %}
|
||||
|
||||
{# CSS #}
|
||||
<link href='https://fonts.googleapis.com/css?family=Lato:400,700|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' rel='stylesheet' type='text/css'>
|
||||
|
||||
{# OPENSEARCH #}
|
||||
{% if not embedded %}
|
||||
@@ -82,7 +82,9 @@
|
||||
{# SIDE NAV, TOGGLES ON MOBILE #}
|
||||
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
|
||||
<div class="wy-side-nav-search">
|
||||
<a href="{{ pathto(master_doc) }}" class="fa fa-home"> {{ project }}</a>
|
||||
{% block sidebartitle %}
|
||||
<a href="{{ pathto(master_doc) }}" class="fa fa-home"> {{ project }}</a>
|
||||
{% endblock %}
|
||||
{% include "searchbox.html" %}
|
||||
</div>
|
||||
|
||||
@@ -111,7 +113,7 @@
|
||||
<div class="wy-nav-content">
|
||||
<div class="rst-content">
|
||||
{% include "breadcrumbs.html" %}
|
||||
<div role="main">
|
||||
<div role="main" class="document">
|
||||
{% block body %}{% endblock %}
|
||||
</div>
|
||||
{% include "footer.html" %}
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
.fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-weight:normal;font-style:normal;src:url("../font/fontawesome_webfont.eot");src:url("../font/fontawesome_webfont.eot?#iefix") format("embedded-opentype"),url("../font/fontawesome_webfont.woff") format("woff"),url("../font/fontawesome_webfont.ttf") format("truetype"),url("../font/fontawesome_webfont.svg#FontAwesome") format("svg")}.fa:before{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa{display:inline-block;text-decoration:inherit}li .fa{display:inline-block}li .fa-large:before,li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-0.8em}ul.fas li .fa{width:0.8em}ul.fas li .fa-large:before,ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before{content:"\f02d"}.icon-book:before{content:"\f02d"}.fa-caret-down:before{content:"\f0d7"}.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{width:100%;height:auto}}
|
||||
.fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-weight:normal;font-style:normal;src:url("../font/fontawesome_webfont.eot");src:url("../font/fontawesome_webfont.eot?#iefix") format("embedded-opentype"),url("../font/fontawesome_webfont.woff") format("woff"),url("../font/fontawesome_webfont.ttf") format("truetype"),url("../font/fontawesome_webfont.svg#FontAwesome") format("svg")}.fa:before{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa{display:inline-block;text-decoration:inherit}li .fa{display:inline-block}li .fa-large:before,li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-0.8em}ul.fas li .fa{width:0.8em}ul.fas li .fa-large:before,ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before{content:""}.icon-book:before{content:""}.fa-caret-down:before{content:""}.icon-caret-down:before{content:""}.fa-caret-up:before{content:""}.icon-caret-up:before{content:""}.fa-caret-left:before{content:""}.icon-caret-left:before{content:""}.fa-caret-right:before{content:""}.icon-caret-right:before{content:""}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980B9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27AE60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#E74C3C;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#F1C40F;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{width:100%;height:auto}}
|
||||
/*# sourceMappingURL=badge_only.css.map */
|
||||
|
||||
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
|
||||
functions if :type:`nghttp2_session` is configured as client.
|
||||
Similarly, `nghttp2_session_recv()` and `nghttp2_session_mem_recv()`
|
||||
do not consume client connection preface. The applications are
|
||||
responsible to receive it before calling these functions if
|
||||
:type:`nghttp2_session` is configured as server.
|
||||
do not consume client connection preface unless
|
||||
`nghttp2_option_set_recv_client_preface()` is used with nonzero option
|
||||
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
|
||||
1
doc/building-android-binary.rst.in
Normal file
1
doc/building-android-binary.rst.in
Normal file
@@ -0,0 +1 @@
|
||||
.. include:: @top_srcdir@/doc/sources/building-android-binary.rst
|
||||
@@ -155,7 +155,7 @@ html_theme_path = ['@top_srcdir@/doc/_themes']
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#html_use_smartypants = True
|
||||
html_use_smartypants = False
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
html_sidebars = {
|
||||
|
||||
1
doc/contribute.rst.in
Normal file
1
doc/contribute.rst.in
Normal file
@@ -0,0 +1 @@
|
||||
.. include:: @top_srcdir@/doc/sources/contribute.rst
|
||||
24
doc/h2load.1
24
doc/h2load.1
@@ -1,10 +1,10 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.45.1.
|
||||
.TH H2LOAD "1" "July 2014" "h2load nghttp2/0.5.0" "User Commands"
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.4.
|
||||
.TH H2LOAD "1" "January 2015" "h2load nghttp2/0.7.0" "User Commands"
|
||||
.SH NAME
|
||||
h2load \- HTTP/2 benchmarking tool
|
||||
.SH SYNOPSIS
|
||||
.B h2load
|
||||
[\fI\,OPTIONS\/\fR]... \fI\,<URI>\/\fR...
|
||||
[\fI\,OPTIONS\/\fR]... [\fI\,URI\/\fR]...
|
||||
.SH DESCRIPTION
|
||||
benchmarking tool for HTTP/2 and SPDY server
|
||||
.TP
|
||||
@@ -26,6 +26,17 @@ Number of concurrent clients. Default: 1
|
||||
\fB\-t\fR, \fB\-\-threads=\fR<N>
|
||||
Number of native threads. Default: 1
|
||||
.TP
|
||||
\fB\-i\fR, \fB\-\-input\-file=\fR<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.
|
||||
.TP
|
||||
\fB\-m\fR, \fB\-\-max\-concurrent\-streams=\fR(auto|<N>)
|
||||
Max concurrent streams to issue per session. If
|
||||
"auto" is given, the number of given URIs is
|
||||
@@ -41,12 +52,15 @@ Sets the connection level initial window size to
|
||||
than 16, this option is ignored. Otherwise
|
||||
2**<N> is used for SPDY.
|
||||
.TP
|
||||
\fB\-H\fR, \fB\-\-header=\fR<HEADER>
|
||||
Add/Override a header to the requests.
|
||||
.TP
|
||||
\fB\-p\fR, \fB\-\-no\-tls\-proto=\fR<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\-13
|
||||
Default: h2c\-13
|
||||
h2c\-14
|
||||
Default: h2c\-14
|
||||
.TP
|
||||
\fB\-v\fR, \fB\-\-verbose\fR
|
||||
Output debug information.
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
.. DO NOT MODIFY THIS FILE! It was generated by man2rst.py
|
||||
|
||||
.. program:: h2load
|
||||
|
||||
h2load(1)
|
||||
@@ -9,7 +11,7 @@ h2load - HTTP/2 benchmarking tool
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
**h2load** [OPTIONS]... <URI>...
|
||||
**h2load** [OPTIONS]... [URI]...
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@@ -42,6 +44,19 @@ OPTIONS
|
||||
|
||||
Number of native threads. 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>)
|
||||
|
||||
|
||||
@@ -63,14 +78,19 @@ OPTIONS
|
||||
than 16, this option is ignored. Otherwise
|
||||
2\*\*<N> is used for SPDY.
|
||||
|
||||
.. option:: -H, --header=<HEADER>
|
||||
|
||||
|
||||
Add/Override a header to the requests.
|
||||
|
||||
.. 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-13
|
||||
Default: h2c-13
|
||||
h2c-14
|
||||
Default: h2c-14
|
||||
|
||||
.. option:: -v, --verbose
|
||||
|
||||
|
||||
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
|
||||
31
doc/nghttp.1
31
doc/nghttp.1
@@ -1,5 +1,5 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.45.1.
|
||||
.TH NGHTTP "1" "July 2014" "nghttp nghttp2/0.5.0" "User Commands"
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.4.
|
||||
.TH NGHTTP "1" "January 2015" "nghttp nghttp2/0.7.0" "User Commands"
|
||||
.SH NAME
|
||||
nghttp \- HTTP/2 experimental client
|
||||
.SH SYNOPSIS
|
||||
@@ -14,7 +14,9 @@ Specify URI to access.
|
||||
.TP
|
||||
\fB\-v\fR, \fB\-\-verbose\fR
|
||||
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.
|
||||
.TP
|
||||
\fB\-n\fR, \fB\-\-null\-out\fR
|
||||
Discard downloaded data.
|
||||
@@ -40,13 +42,18 @@ Sets the connection level initial window size to
|
||||
Download assets such as stylesheets, images and
|
||||
script files linked from the downloaded resource.
|
||||
Only links whose origins are the same with the
|
||||
linking resource will be downloaded.
|
||||
linking resource 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.
|
||||
.TP
|
||||
\fB\-s\fR, \fB\-\-stat\fR
|
||||
Print statistics.
|
||||
.TP
|
||||
\fB\-H\fR, \fB\-\-header\fR
|
||||
Add a header to the requests.
|
||||
\fB\-H\fR, \fB\-\-header=\fR<HEADER>
|
||||
Add a header to the requests. Example:
|
||||
\fB\-H\fR':method: PUT'
|
||||
.TP
|
||||
\fB\-\-cert=\fR<CERT>
|
||||
Use the specified client certificate file. The
|
||||
@@ -89,6 +96,10 @@ Specify decoder header table size.
|
||||
Add at most <N> bytes to a frame payload as
|
||||
padding. Specify 0 to disable padding.
|
||||
.TP
|
||||
\fB\-r\fR, \fB\-\-har=\fR<FILE>
|
||||
Output HTTP transactions <FILE> in HAR format.
|
||||
If '\-' is given, data is written to stdout.
|
||||
.TP
|
||||
\fB\-\-color\fR
|
||||
Force colored log output.
|
||||
.TP
|
||||
@@ -98,6 +109,14 @@ Send large header to test CONTINUATION.
|
||||
\fB\-\-no\-content\-length\fR
|
||||
Don't send content\-length header field.
|
||||
.TP
|
||||
\fB\-\-no\-dep\fR
|
||||
Don't send dependency based priority hint to
|
||||
server.
|
||||
.TP
|
||||
\fB\-\-dep\-idle\fR
|
||||
Use idle streams as anchor nodes to express
|
||||
priority.
|
||||
.TP
|
||||
\fB\-\-version\fR
|
||||
Display version information and exit.
|
||||
.TP
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
.. DO NOT MODIFY THIS FILE! It was generated by man2rst.py
|
||||
|
||||
.. program:: nghttp
|
||||
|
||||
nghttp(1)
|
||||
@@ -26,7 +28,9 @@ OPTIONS
|
||||
|
||||
|
||||
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
|
||||
|
||||
@@ -38,7 +42,7 @@ OPTIONS
|
||||
|
||||
Save download data in the current directory. The
|
||||
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 yet.
|
||||
|
||||
.. option:: -t, --timeout=<N>
|
||||
@@ -64,17 +68,22 @@ OPTIONS
|
||||
Download assets such as stylesheets, images and
|
||||
script files linked from the downloaded resource.
|
||||
Only links whose origins are the same with the
|
||||
linking resource will be downloaded.
|
||||
linking resource 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.
|
||||
|
||||
.. option:: -s, --stat
|
||||
|
||||
|
||||
Print statistics.
|
||||
|
||||
.. option:: -H, --header
|
||||
.. option:: -H, --header=<HEADER>
|
||||
|
||||
|
||||
Add a header to the requests.
|
||||
Add a header to the requests. Example:
|
||||
-H':method: PUT'
|
||||
|
||||
.. option:: --cert=<CERT>
|
||||
|
||||
@@ -135,6 +144,12 @@ OPTIONS
|
||||
Add at most <N> bytes to a frame payload as
|
||||
padding. Specify 0 to disable padding.
|
||||
|
||||
.. option:: -r, --har=<FILE>
|
||||
|
||||
|
||||
Output HTTP transactions <FILE> in HAR format.
|
||||
If '-' is given, data is written to stdout.
|
||||
|
||||
.. option:: --color
|
||||
|
||||
|
||||
@@ -150,6 +165,18 @@ OPTIONS
|
||||
|
||||
Don't send content-length header field.
|
||||
|
||||
.. 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
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.45.1.
|
||||
.TH NGHTTPD "1" "July 2014" "nghttpd nghttp2/0.5.0" "User Commands"
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.4.
|
||||
.TH NGHTTPD "1" "January 2015" "nghttpd nghttp2/0.7.0" "User Commands"
|
||||
.SH NAME
|
||||
nghttpd \- HTTP/2 experimental server
|
||||
.SH SYNOPSIS
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
.. DO NOT MODIFY THIS FILE! It was generated by man2rst.py
|
||||
|
||||
.. program:: nghttpd
|
||||
|
||||
nghttpd(1)
|
||||
|
||||
244
doc/nghttpx.1
244
doc/nghttpx.1
@@ -1,5 +1,5 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.45.1.
|
||||
.TH NGHTTPX "1" "July 2014" "nghttpx nghttp2/0.5.0" "User Commands"
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.4.
|
||||
.TH NGHTTPX "1" "January 2015" "nghttpx nghttp2/0.7.0" "User Commands"
|
||||
.SH NAME
|
||||
nghttpx \- HTTP/2 experimental proxy
|
||||
.SH SYNOPSIS
|
||||
@@ -23,7 +23,11 @@ The options are categorized into several groups.
|
||||
.SS "Connections:"
|
||||
.TP
|
||||
\fB\-b\fR, \fB\-\-backend=\fR<HOST,PORT>
|
||||
Set backend host and port.
|
||||
Set backend host and port. For HTTP/1 backend,
|
||||
multiple backend addresses are accepted by
|
||||
repeating this option. HTTP/2 backend does not
|
||||
support multiple backend addresses and the first
|
||||
occurrence of this option is used.
|
||||
Default: '127.0.0.1,80'
|
||||
.TP
|
||||
\fB\-f\fR, \fB\-\-frontend=\fR<HOST,PORT>
|
||||
@@ -35,76 +39,13 @@ Default: '*,3000'
|
||||
\fB\-\-backlog=\fR<NUM>
|
||||
Set listen backlog size. If \fB\-1\fR is given,
|
||||
libevent will choose suitable value.
|
||||
Default: \fB\-1\fR
|
||||
Default: 128
|
||||
.TP
|
||||
\fB\-\-backend\-ipv4\fR
|
||||
Resolve backend hostname to IPv4 address only.
|
||||
.TP
|
||||
\fB\-\-backend\-ipv6\fR
|
||||
Resolve backend hostname to IPv6 address only.
|
||||
.SS "Performance:"
|
||||
.TP
|
||||
\fB\-n\fR, \fB\-\-workers=\fR<CORES>
|
||||
Set the number of worker threads.
|
||||
Default: 1
|
||||
.TP
|
||||
\fB\-\-worker\-read\-rate=\fR<RATE>
|
||||
Set maximum average read rate on frontend
|
||||
connection per worker. Setting 0 to this option
|
||||
means read rate is unlimited.
|
||||
Default: 0
|
||||
.TP
|
||||
\fB\-\-worker\-read\-burst=\fR<SIZE>
|
||||
Set maximum read burst size on frontend
|
||||
connection per worker. Setting 0 to this option
|
||||
means read burst size is unlimited.
|
||||
Default: 0
|
||||
.TP
|
||||
\fB\-\-worker\-write\-rate=\fR<RATE>
|
||||
Set maximum average write rate on frontend
|
||||
connection per worker. Setting 0 to this option
|
||||
means write rate is unlimited.
|
||||
Default: 0
|
||||
.TP
|
||||
\fB\-\-worker\-write\-burst=\fR<SIZE>
|
||||
Set maximum write burst size on frontend
|
||||
connection per worker. Setting 0 to this option
|
||||
means write burst size is unlimited.
|
||||
Default: 0
|
||||
.TP
|
||||
\fB\-\-worker\-frontend\-connections=\fR<NUM>
|
||||
Set maximum number of simultaneous connections
|
||||
frontend accepts. Setting 0 means unlimited.
|
||||
Default: 0
|
||||
.SS "Timeout:"
|
||||
.TP
|
||||
\fB\-\-frontend\-http2\-read\-timeout=\fR<SEC>
|
||||
Specify read timeout for HTTP/2 and SPDY frontend
|
||||
connection.
|
||||
Default: 180
|
||||
.TP
|
||||
\fB\-\-frontend\-read\-timeout=\fR<SEC>
|
||||
Specify read timeout for HTTP/1.1 frontend
|
||||
connection.
|
||||
Default: 180
|
||||
.TP
|
||||
\fB\-\-frontend\-write\-timeout=\fR<SEC>
|
||||
Specify write timeout for all frontend
|
||||
connections.
|
||||
Default: 60
|
||||
.TP
|
||||
\fB\-\-backend\-read\-timeout=\fR<SEC>
|
||||
Specify read timeout for backend connection.
|
||||
Default: 900
|
||||
.TP
|
||||
\fB\-\-backend\-write\-timeout=\fR<SEC>
|
||||
Specify write timeout for backend connection.
|
||||
Default: 60
|
||||
.TP
|
||||
\fB\-\-backend\-keep\-alive\-timeout=\fR<SEC>
|
||||
Specify keep\-alive timeout for backend
|
||||
connection.
|
||||
Default: 60
|
||||
.TP
|
||||
\fB\-\-backend\-http\-proxy\-uri=\fR<URI>
|
||||
Specify proxy URI in the form
|
||||
@@ -121,6 +62,130 @@ The timeouts when connecting and making CONNECT
|
||||
request can be specified by
|
||||
\fB\-\-backend\-read\-timeout\fR and
|
||||
\fB\-\-backend\-write\-timeout\fR options.
|
||||
.SS "Performance:"
|
||||
.TP
|
||||
\fB\-n\fR, \fB\-\-workers=\fR<CORES>
|
||||
Set the number of worker threads.
|
||||
Default: 1
|
||||
.TP
|
||||
\fB\-\-read\-rate=\fR<RATE>
|
||||
Set maximum average read rate on frontend
|
||||
connection. Setting 0 to this option means read
|
||||
rate is unlimited.
|
||||
Default: 0
|
||||
.TP
|
||||
\fB\-\-read\-burst=\fR<SIZE>
|
||||
Set maximum read burst size on frontend
|
||||
connection. Setting 0 to this option means read
|
||||
burst size is unlimited.
|
||||
Default: 0
|
||||
.TP
|
||||
\fB\-\-write\-rate=\fR<RATE>
|
||||
Set maximum average write rate on frontend
|
||||
connection. Setting 0 to this option means write
|
||||
rate is unlimited.
|
||||
Default: 0
|
||||
.TP
|
||||
\fB\-\-write\-burst=\fR<SIZE>
|
||||
Set maximum write burst size on frontend
|
||||
connection. Setting 0 to this option means write
|
||||
burst size is unlimited.
|
||||
Default: 0
|
||||
.TP
|
||||
\fB\-\-worker\-read\-rate=\fR<RATE>
|
||||
Set maximum average read rate on frontend
|
||||
connection per worker. Setting 0 to this option
|
||||
means read rate is unlimited. Not implemented
|
||||
yet.
|
||||
Default: 0
|
||||
.TP
|
||||
\fB\-\-worker\-read\-burst=\fR<SIZE>
|
||||
Set maximum read burst size on frontend
|
||||
connection per worker. Setting 0 to this option
|
||||
means read burst size is unlimited. Not
|
||||
implemented yet.
|
||||
Default: 0
|
||||
.TP
|
||||
\fB\-\-worker\-write\-rate=\fR<RATE>
|
||||
Set maximum average write rate on frontend
|
||||
connection per worker. Setting 0 to this option
|
||||
means write rate is unlimited. Not implemented
|
||||
yet.
|
||||
Default: 0
|
||||
.TP
|
||||
\fB\-\-worker\-write\-burst=\fR<SIZE>
|
||||
Set maximum write burst size on frontend
|
||||
connection per worker. Setting 0 to this option
|
||||
means write burst size is unlimited. Not
|
||||
implemented yet.
|
||||
Default: 0
|
||||
.TP
|
||||
\fB\-\-worker\-frontend\-connections=\fR<NUM>
|
||||
Set maximum number of simultaneous connections
|
||||
frontend accepts. Setting 0 means unlimited.
|
||||
Default: 0
|
||||
.TP
|
||||
\fB\-\-backend\-http1\-connections\-per\-host=\fR<NUM>
|
||||
Set maximum number of backend concurrent HTTP/1
|
||||
connections per host. This option is meaningful
|
||||
when \fB\-s\fR option is used. To limit the number of
|
||||
connections per frontend for default mode, use
|
||||
\fB\-\-backend\-http1\-connections\-per\-frontend\fR.
|
||||
Default: 8
|
||||
.TP
|
||||
\fB\-\-backend\-http1\-connections\-per\-frontend=\fR<NUM>
|
||||
Set maximum number of backend concurrent HTTP/1
|
||||
connections per frontend. This option is only
|
||||
used for default mode. 0 means unlimited. To
|
||||
limit the number of connections per host for
|
||||
HTTP/2 or SPDY proxy mode (\fB\-s\fR option), use
|
||||
\fB\-\-backend\-http1\-connections\-per\-host\fR.
|
||||
Default: 0
|
||||
.SS "Timeout:"
|
||||
.TP
|
||||
\fB\-\-frontend\-http2\-read\-timeout=\fR<SEC>
|
||||
Specify read timeout for HTTP/2 and SPDY frontend
|
||||
connection.
|
||||
Default: 180
|
||||
.TP
|
||||
\fB\-\-frontend\-read\-timeout=\fR<SEC>
|
||||
Specify read timeout for HTTP/1.1 frontend
|
||||
connection.
|
||||
Default: 180
|
||||
.TP
|
||||
\fB\-\-frontend\-write\-timeout=\fR<SEC>
|
||||
Specify write timeout for all frontend
|
||||
connections.
|
||||
Default: 30
|
||||
.TP
|
||||
\fB\-\-stream\-read\-timeout=\fR<SEC>
|
||||
Specify read timeout for HTTP/2 and SPDY streams.
|
||||
0 means no timeout.
|
||||
Default: 0
|
||||
.TP
|
||||
\fB\-\-stream\-write\-timeout=\fR<SEC>
|
||||
Specify write timeout for HTTP/2 and SPDY
|
||||
streams. 0 means no timeout.
|
||||
Default: 0
|
||||
.TP
|
||||
\fB\-\-backend\-read\-timeout=\fR<SEC>
|
||||
Specify read timeout for backend connection.
|
||||
Default: 180
|
||||
.TP
|
||||
\fB\-\-backend\-write\-timeout=\fR<SEC>
|
||||
Specify write timeout for backend connection.
|
||||
Default: 30
|
||||
.TP
|
||||
\fB\-\-backend\-keep\-alive\-timeout=\fR<SEC>
|
||||
Specify keep\-alive timeout for backend
|
||||
connection.
|
||||
Default: 600
|
||||
.TP
|
||||
\fB\-\-listener\-disable\-timeout=\fR<SEC>
|
||||
After accepting connection failed, connection
|
||||
listener is disabled for a given time in seconds.
|
||||
Specifying 0 disables this feature.
|
||||
Default: 0
|
||||
.SS "SSL/TLS:"
|
||||
.TP
|
||||
\fB\-\-ciphers=\fR<SUITE>
|
||||
@@ -171,7 +236,7 @@ most desirable protocol comes first. This is
|
||||
used in both ALPN and NPN. The parameter must be
|
||||
delimited by a single comma only and any white
|
||||
spaces are treated as a part of protocol string.
|
||||
Default: h2\-13,spdy/3.1,spdy/3,spdy/2,http/1.1
|
||||
Default: h2\-16,h2\-14,spdy/3.1,http/1.1
|
||||
.TP
|
||||
\fB\-\-verify\-client\fR
|
||||
Require and verify client certificate.
|
||||
@@ -193,8 +258,8 @@ used in backend client authentication.
|
||||
\fB\-\-tls\-proto\-list=\fR<LIST>
|
||||
Comma delimited list of SSL/TLS protocol to be
|
||||
enabled. The following protocols are available:
|
||||
TLSv1.2, TLSv1.1, TLSv1.0 and SSLv3. The name
|
||||
matching is done in case\-insensitive manner. The
|
||||
TLSv1.2, TLSv1.1 and TLSv1.0. The name matching
|
||||
is done in case\-insensitive manner. The
|
||||
parameter must be delimited by a single comma
|
||||
only and any white spaces are treated as a part
|
||||
of protocol string.
|
||||
@@ -279,8 +344,9 @@ URI, suitable for use as a forward proxy.
|
||||
.TP
|
||||
\fB\-L\fR, \fB\-\-log\-level=\fR<LEVEL>
|
||||
Set the severity level of log output. <LEVEL>
|
||||
must be one of INFO, WARNING, ERROR and FATAL.
|
||||
Default: WARNING
|
||||
must be one of INFO, NOTICE, WARN, ERROR and
|
||||
FATAL.
|
||||
Default: NOTICE
|
||||
.TP
|
||||
\fB\-\-accesslog\-file=\fR<PATH>
|
||||
Set path to write access log. To reopen file,
|
||||
@@ -290,6 +356,29 @@ send USR1 signal to nghttpx.
|
||||
Send access log to syslog. If this option is
|
||||
used, \fB\-\-access\-file\fR option is ignored.
|
||||
.TP
|
||||
\fB\-\-accesslog\-format=\fR<FORMAT>
|
||||
Specify format string for access log. The
|
||||
default format is combined format. The following
|
||||
variables are available:
|
||||
$remote_addr: client IP address.
|
||||
$time_local: local time in Common Log format.
|
||||
$time_iso8601: local time in ISO 8601 format.
|
||||
$request: HTTP request line.
|
||||
$status: HTTP response status code.
|
||||
$body_bytes_sent: the number of bytes sent to
|
||||
client as response body.
|
||||
$http_<VAR>: value of HTTP request header <VAR>
|
||||
where '_' in <VAR> is replaced with '\-'.
|
||||
$remote_port: client port.
|
||||
$server_port: server port.
|
||||
$request_time: request processing time in
|
||||
seconds with milliseconds resolution.
|
||||
$pid: PID of the running process.
|
||||
$alpn: ALPN identifier of the protocol which
|
||||
generates the response. For HTTP/1, ALPN is
|
||||
always http/1.1, regardless of minor version.
|
||||
Default: $remote_addr \- \- [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"
|
||||
.TP
|
||||
\fB\-\-errorlog\-file=\fR<PATH>
|
||||
Set path to write error log. To reopen file,
|
||||
send USR1 signal to nghttpx.
|
||||
@@ -308,10 +397,21 @@ Default: daemon
|
||||
Append X\-Forwarded\-For header field to the
|
||||
downstream request.
|
||||
.TP
|
||||
\fB\-\-strip\-incoming\-x\-forwarded\-for\fR
|
||||
Strip X\-Forwarded\-For header field from inbound
|
||||
client requests.
|
||||
.TP
|
||||
\fB\-\-no\-via\fR
|
||||
Don't append to Via header field. If Via header
|
||||
field is received, it is left unaltered.
|
||||
.TP
|
||||
\fB\-\-no\-location\-rewrite\fR
|
||||
Don't rewrite location header field on
|
||||
\fB\-\-http2\-bridge\fR, \fB\-\-client\fR and default mode. For
|
||||
\fB\-\-http2\-proxy\fR and \fB\-\-client\-proxy\fR mode, location
|
||||
header field will not be altered regardless of
|
||||
this option.
|
||||
.TP
|
||||
\fB\-\-altsvc=\fR<PROTOID,PORT[,HOST,[ORIGIN]]>
|
||||
Specify protocol ID, port, host and origin of
|
||||
alternative service. <HOST> and <ORIGIN> are
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
.. DO NOT MODIFY THIS FILE! It was generated by man2rst.py
|
||||
|
||||
.. program:: nghttpx
|
||||
|
||||
nghttpx(1)
|
||||
@@ -38,7 +40,11 @@ Connections
|
||||
.. option:: -b, --backend=<HOST,PORT>
|
||||
|
||||
|
||||
Set backend host and port.
|
||||
Set backend host and port. For HTTP/1 backend,
|
||||
multiple backend addresses are accepted by
|
||||
repeating this option. HTTP/2 backend does not
|
||||
support multiple backend addresses and the first
|
||||
occurrence of this option is used.
|
||||
Default: '127.0.0.1,80'
|
||||
|
||||
.. option:: -f, --frontend=<HOST,PORT>
|
||||
@@ -54,7 +60,7 @@ Connections
|
||||
|
||||
Set listen backlog size. If -1 is given,
|
||||
libevent will choose suitable value.
|
||||
Default: -1
|
||||
Default: 128
|
||||
|
||||
.. option:: --backend-ipv4
|
||||
|
||||
@@ -66,6 +72,24 @@ Connections
|
||||
|
||||
Resolve backend hostname to IPv6 address only.
|
||||
|
||||
.. option:: --backend-http-proxy-uri=<URI>
|
||||
|
||||
|
||||
Specify proxy URI in the form
|
||||
http://[<USER>:<PASS>@]<PROXY>:<PORT>. If a
|
||||
proxy requires authentication, specify <USER> and
|
||||
<PASS>. Note that they must be properly
|
||||
percent-encoded. This proxy is used when the
|
||||
backend connection is HTTP/2. First, make a
|
||||
CONNECT request to the proxy and it connects to
|
||||
the backend on behalf of nghttpx. This forms
|
||||
tunnel. After that, nghttpx performs SSL/TLS
|
||||
handshake with the downstream through the tunnel.
|
||||
The timeouts when connecting and making CONNECT
|
||||
request can be specified by
|
||||
:option:`--backend-read-timeout` and
|
||||
:option:`--backend-write-timeout` options.
|
||||
|
||||
Performance
|
||||
^^^^^^^^^^^
|
||||
|
||||
@@ -75,12 +99,45 @@ Performance
|
||||
Set the number of worker threads.
|
||||
Default: 1
|
||||
|
||||
.. option:: --read-rate=<RATE>
|
||||
|
||||
|
||||
Set maximum average read rate on frontend
|
||||
connection. Setting 0 to this option means read
|
||||
rate is unlimited.
|
||||
Default: 0
|
||||
|
||||
.. option:: --read-burst=<SIZE>
|
||||
|
||||
|
||||
Set maximum read burst size on frontend
|
||||
connection. Setting 0 to this option means read
|
||||
burst size is unlimited.
|
||||
Default: 0
|
||||
|
||||
.. option:: --write-rate=<RATE>
|
||||
|
||||
|
||||
Set maximum average write rate on frontend
|
||||
connection. Setting 0 to this option means write
|
||||
rate is unlimited.
|
||||
Default: 0
|
||||
|
||||
.. option:: --write-burst=<SIZE>
|
||||
|
||||
|
||||
Set maximum write burst size on frontend
|
||||
connection. Setting 0 to this option means write
|
||||
burst size is unlimited.
|
||||
Default: 0
|
||||
|
||||
.. option:: --worker-read-rate=<RATE>
|
||||
|
||||
|
||||
Set maximum average read rate on frontend
|
||||
connection per worker. Setting 0 to this option
|
||||
means read rate is unlimited.
|
||||
means read rate is unlimited. Not implemented
|
||||
yet.
|
||||
Default: 0
|
||||
|
||||
.. option:: --worker-read-burst=<SIZE>
|
||||
@@ -88,7 +145,8 @@ Performance
|
||||
|
||||
Set maximum read burst size on frontend
|
||||
connection per worker. Setting 0 to this option
|
||||
means read burst size is unlimited.
|
||||
means read burst size is unlimited. Not
|
||||
implemented yet.
|
||||
Default: 0
|
||||
|
||||
.. option:: --worker-write-rate=<RATE>
|
||||
@@ -96,7 +154,8 @@ Performance
|
||||
|
||||
Set maximum average write rate on frontend
|
||||
connection per worker. Setting 0 to this option
|
||||
means write rate is unlimited.
|
||||
means write rate is unlimited. Not implemented
|
||||
yet.
|
||||
Default: 0
|
||||
|
||||
.. option:: --worker-write-burst=<SIZE>
|
||||
@@ -104,7 +163,8 @@ Performance
|
||||
|
||||
Set maximum write burst size on frontend
|
||||
connection per worker. Setting 0 to this option
|
||||
means write burst size is unlimited.
|
||||
means write burst size is unlimited. Not
|
||||
implemented yet.
|
||||
Default: 0
|
||||
|
||||
.. option:: --worker-frontend-connections=<NUM>
|
||||
@@ -114,6 +174,27 @@ Performance
|
||||
frontend accepts. Setting 0 means unlimited.
|
||||
Default: 0
|
||||
|
||||
.. option:: --backend-http1-connections-per-host=<NUM>
|
||||
|
||||
|
||||
Set maximum number of backend concurrent HTTP/1
|
||||
connections per host. This option is meaningful
|
||||
when :option:`-s` option is used. To limit the number of
|
||||
connections per frontend for default mode, use
|
||||
:option:`--backend-http1-connections-per-frontend`.
|
||||
Default: 8
|
||||
|
||||
.. option:: --backend-http1-connections-per-frontend=<NUM>
|
||||
|
||||
|
||||
Set maximum number of backend concurrent HTTP/1
|
||||
connections per frontend. This option is only
|
||||
used for default mode. 0 means unlimited. To
|
||||
limit the number of connections per host for
|
||||
HTTP/2 or SPDY proxy mode (:option:`-s` option), use
|
||||
:option:`--backend-http1-connections-per-host`.
|
||||
Default: 0
|
||||
|
||||
Timeout
|
||||
^^^^^^^
|
||||
|
||||
@@ -136,44 +217,48 @@ Timeout
|
||||
|
||||
Specify write timeout for all frontend
|
||||
connections.
|
||||
Default: 60
|
||||
Default: 30
|
||||
|
||||
.. option:: --stream-read-timeout=<SEC>
|
||||
|
||||
|
||||
Specify read timeout for HTTP/2 and SPDY streams.
|
||||
0 means no timeout.
|
||||
Default: 0
|
||||
|
||||
.. option:: --stream-write-timeout=<SEC>
|
||||
|
||||
|
||||
Specify write timeout for HTTP/2 and SPDY
|
||||
streams. 0 means no timeout.
|
||||
Default: 0
|
||||
|
||||
.. option:: --backend-read-timeout=<SEC>
|
||||
|
||||
|
||||
Specify read timeout for backend connection.
|
||||
Default: 900
|
||||
Default: 180
|
||||
|
||||
.. option:: --backend-write-timeout=<SEC>
|
||||
|
||||
|
||||
Specify write timeout for backend connection.
|
||||
Default: 60
|
||||
Default: 30
|
||||
|
||||
.. option:: --backend-keep-alive-timeout=<SEC>
|
||||
|
||||
|
||||
Specify keep-alive timeout for backend
|
||||
connection.
|
||||
Default: 60
|
||||
Default: 600
|
||||
|
||||
.. option:: --backend-http-proxy-uri=<URI>
|
||||
.. option:: --listener-disable-timeout=<SEC>
|
||||
|
||||
|
||||
Specify proxy URI in the form
|
||||
http://[<USER>:<PASS>@]<PROXY>:<PORT>. If a
|
||||
proxy requires authentication, specify <USER> and
|
||||
<PASS>. Note that they must be properly
|
||||
percent-encoded. This proxy is used when the
|
||||
backend connection is HTTP/2. First, make a
|
||||
CONNECT request to the proxy and it connects to
|
||||
the backend on behalf of nghttpx. This forms
|
||||
tunnel. After that, nghttpx performs SSL/TLS
|
||||
handshake with the downstream through the tunnel.
|
||||
The timeouts when connecting and making CONNECT
|
||||
request can be specified by
|
||||
:option:`--backend-read-timeout` and
|
||||
:option:`--backend-write-timeout` options.
|
||||
After accepting connection failed, connection
|
||||
listener is disabled for a given time in seconds.
|
||||
Specifying 0 disables this feature.
|
||||
Default: 0
|
||||
|
||||
SSL/TLS
|
||||
^^^^^^^
|
||||
@@ -242,7 +327,7 @@ SSL/TLS
|
||||
used in both ALPN and NPN. The parameter must be
|
||||
delimited by a single comma only and any white
|
||||
spaces are treated as a part of protocol string.
|
||||
Default: h2-13,spdy/3.1,spdy/3,spdy/2,http/1.1
|
||||
Default: h2-16,h2-14,spdy/3.1,http/1.1
|
||||
|
||||
.. option:: --verify-client
|
||||
|
||||
@@ -274,8 +359,8 @@ SSL/TLS
|
||||
|
||||
Comma delimited list of SSL/TLS protocol to be
|
||||
enabled. The following protocols are available:
|
||||
TLSv1.2, TLSv1.1, TLSv1.0 and SSLv3. The name
|
||||
matching is done in case-insensitive manner. The
|
||||
TLSv1.2, TLSv1.1 and TLSv1.0. The name matching
|
||||
is done in case-insensitive manner. The
|
||||
parameter must be delimited by a single comma
|
||||
only and any white spaces are treated as a part
|
||||
of protocol string.
|
||||
@@ -395,8 +480,9 @@ Logging
|
||||
|
||||
|
||||
Set the severity level of log output. <LEVEL>
|
||||
must be one of INFO, WARNING, ERROR and FATAL.
|
||||
Default: WARNING
|
||||
must be one of INFO, NOTICE, WARN, ERROR and
|
||||
FATAL.
|
||||
Default: NOTICE
|
||||
|
||||
.. option:: --accesslog-file=<PATH>
|
||||
|
||||
@@ -410,6 +496,31 @@ Logging
|
||||
Send access log to syslog. If this option is
|
||||
used, :option:`--access-file` option is ignored.
|
||||
|
||||
.. option:: --accesslog-format=<FORMAT>
|
||||
|
||||
|
||||
Specify format string for access log. The
|
||||
default format is combined format. The following
|
||||
variables are available:
|
||||
$remote_addr: client IP address.
|
||||
$time_local: local time in Common Log format.
|
||||
$time_iso8601: local time in ISO 8601 format.
|
||||
$request: HTTP request line.
|
||||
$status: HTTP response status code.
|
||||
$body_bytes_sent: the number of bytes sent to
|
||||
client as response body.
|
||||
$http_<VAR>: value of HTTP request header <VAR>
|
||||
where '_' in <VAR> is replaced with '-'.
|
||||
$remote_port: client port.
|
||||
$server_port: server port.
|
||||
$request_time: request processing time in
|
||||
seconds with milliseconds resolution.
|
||||
$pid: PID of the running process.
|
||||
$alpn: ALPN identifier of the protocol which
|
||||
generates the response. For HTTP/1, ALPN is
|
||||
always http/1.1, regardless of minor version.
|
||||
Default: $remote_addr - - [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"
|
||||
|
||||
.. option:: --errorlog-file=<PATH>
|
||||
|
||||
|
||||
@@ -438,12 +549,27 @@ Misc
|
||||
Append X-Forwarded-For header field to the
|
||||
downstream request.
|
||||
|
||||
.. option:: --strip-incoming-x-forwarded-for
|
||||
|
||||
|
||||
Strip X-Forwarded-For header field from inbound
|
||||
client requests.
|
||||
|
||||
.. option:: --no-via
|
||||
|
||||
|
||||
Don't append to Via header field. If Via header
|
||||
field is received, it is left unaltered.
|
||||
|
||||
.. option:: --no-location-rewrite
|
||||
|
||||
|
||||
Don't rewrite location header field on
|
||||
:option:`--http2-bridge`, :option:`--client` and default mode. For
|
||||
:option:`--http2-proxy` and :option:`--client-proxy` mode, location
|
||||
header field will not be altered regardless of
|
||||
this option.
|
||||
|
||||
.. option:: --altsvc=<PROTOID,PORT[,HOST,[ORIGIN]]>
|
||||
|
||||
|
||||
|
||||
130
doc/sources/building-android-binary.rst
Normal file
130
doc/sources/building-android-binary.rst
Normal file
@@ -0,0 +1,130 @@
|
||||
Building Android binary
|
||||
=======================
|
||||
|
||||
In this article, we briefly describe how to build Android binary using
|
||||
`Android NDK <http://developer.android.com/tools/sdk/ndk/index.html>`_
|
||||
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
|
||||
build easier. To make these script work, NDK toolchain must be
|
||||
installed in the following way. First, let us introduce
|
||||
``ANDROID_HOME`` environment variable. We need to install toolchain
|
||||
under ``$ANDROID_HOME/toolchain``. An user can freely choose the path
|
||||
for ``ANDROID_HOME``. For example, to install toolchain under
|
||||
``$ANDROID_HOME/toolchain``, do this in the the directory where NDK is
|
||||
unpacked::
|
||||
|
||||
$ build/tools/make-standalone-toolchain.sh \
|
||||
--install-dir=$ANDROID_HOME/toolchain \
|
||||
--toolchain=arm-linux-androideabi-4.8
|
||||
|
||||
The additional flag ``--system=linux-x86_64`` may be required if you
|
||||
are using x86_64 system.
|
||||
|
||||
The platform level is not important here because we don't use Android
|
||||
specific C/C++ API.
|
||||
|
||||
The dependent libraries, such as OpenSSL and libevent should be built
|
||||
with the toolchain and installed under ``$ANDROID_HOME/usr/local``.
|
||||
We recommend to build these libraries as static library to make the
|
||||
deployment easier. libxml2 support is currently disabled.
|
||||
|
||||
We use zlib which comes with Android NDK, so we don't have to build it
|
||||
by ourselves.
|
||||
|
||||
If SPDY support is required for nghttpx and h2load, build and install
|
||||
spdylay as well.
|
||||
|
||||
Before running ``android-config`` and ``android-make``,
|
||||
``ANDROID_HOME`` environment variable must be set to point to the
|
||||
correct path. Also add ``$ANDROID_HOME/toolchain/bin`` to ``PATH``::
|
||||
|
||||
$ export PATH=$PATH:$ANDROID_HOME/toolchain/bin
|
||||
|
||||
To configure OpenSSL, use the following script:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
#!/bin/sh
|
||||
|
||||
if [ -z "$ANDROID_HOME" ]; then
|
||||
echo 'No $ANDROID_HOME specified.'
|
||||
exit 1
|
||||
fi
|
||||
PREFIX=$ANDROID_HOME/usr/local
|
||||
TOOLCHAIN=$ANDROID_HOME/toolchain
|
||||
PATH=$TOOLCHAIN/bin:$PATH
|
||||
|
||||
export CROSS_COMPILE=$TOOLCHAIN/bin/arm-linux-androideabi-
|
||||
./Configure --prefix=$PREFIX android
|
||||
|
||||
And run ``make install`` to build and install.
|
||||
|
||||
To configure libevent, use the following script:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
#!/bin/sh
|
||||
|
||||
if [ -z "$ANDROID_HOME" ]; then
|
||||
echo 'No $ANDROID_HOME specified.'
|
||||
exit 1
|
||||
fi
|
||||
PREFIX=$ANDROID_HOME/usr/local
|
||||
TOOLCHAIN=$ANDROID_HOME/toolchain
|
||||
PATH=$TOOLCHAIN/bin:$PATH
|
||||
|
||||
./configure \
|
||||
--host=arm-linux-androideabi \
|
||||
--build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \
|
||||
--prefix=$PREFIX \
|
||||
--disable-shared \
|
||||
--enable-static \
|
||||
CPPFLAGS=-I$PREFIX/include \
|
||||
LDFLAGS=-L$PREFIX/lib
|
||||
|
||||
And run ``make install`` to build and install.
|
||||
|
||||
To configure spdylay, use the following script:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
if [ -z "$ANDROID_HOME" ]; then
|
||||
echo 'No $ANDROID_HOME specified.'
|
||||
exit 1
|
||||
fi
|
||||
PREFIX=$ANDROID_HOME/usr/local
|
||||
TOOLCHAIN=$ANDROID_HOME/toolchain
|
||||
PATH=$TOOLCHAIN/bin:$PATH
|
||||
|
||||
./configure \
|
||||
--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"
|
||||
|
||||
And run ``make install`` to build and install. After spdylay
|
||||
installation, edit $ANDROID_HOME/usr/local/lib/pkgconfig/libspdylay.pc
|
||||
and remove the following line::
|
||||
|
||||
Requires.private: zlib
|
||||
|
||||
After prerequisite libraries are prepared, run ``android-config`` and
|
||||
then ``android-make`` to compile nghttp2 source files.
|
||||
|
||||
If all went well, application binaries, such as nghttpx, are created
|
||||
under src directory. Strip debugging information from the binary
|
||||
using the following command::
|
||||
|
||||
$ arm-linux-androideabi-strip src/nghttpx
|
||||
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,8 @@ Contents:
|
||||
:maxdepth: 2
|
||||
|
||||
package_README
|
||||
contribute
|
||||
building-android-binary
|
||||
tutorial-client
|
||||
tutorial-server
|
||||
tutorial-hpack
|
||||
@@ -27,24 +29,22 @@ Contents:
|
||||
nghttpx-howto
|
||||
h2load-howto
|
||||
apiref
|
||||
libnghttp2_asio
|
||||
python-apiref
|
||||
nghttp2.h
|
||||
nghttp2ver.h
|
||||
asio_http2.h
|
||||
Source <https://github.com/tatsuhiro-t/nghttp2>
|
||||
Issues <https://github.com/tatsuhiro-t/nghttp2/issues>
|
||||
nghttp2.org <https://nghttp2.org/>
|
||||
|
||||
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>`_
|
||||
* `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>`_
|
||||
|
||||
https://github.com/tatsuhiro-t/nghttp2/releases
|
||||
|
||||
Resources
|
||||
---------
|
||||
|
||||
* http://tools.ietf.org/html/draft-ietf-httpbis-http2-13
|
||||
* http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-08
|
||||
* http://tools.ietf.org/html/draft-ietf-httpbis-http2-14
|
||||
* http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-09
|
||||
|
||||
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
|
||||
absolute URI in request line.
|
||||
|
||||
By default, frontend connection is encrypted, this mode is also called
|
||||
secure proxy. If nghttpx is linked with spdylay, it supports SPDY
|
||||
protocols and it works as so called SPDY proxy.
|
||||
By default, frontend connection is encrypted. So this mode is also
|
||||
called secure proxy. If nghttpx is linked with spdylay, it supports
|
||||
SPDY protocols and it works as so called SPDY proxy.
|
||||
|
||||
With ``--frontend-no-tls`` option, SSL/TLS is turned off in frontend
|
||||
connection, so the connection gets insecure.
|
||||
|
||||
The backend must be HTTP/1 proxy server. nghttpx only supports 1
|
||||
backend server address. It translates incoming requests to HTTP/1
|
||||
request to backend server. The backend server performs real proxy
|
||||
work for each request, for example, dispatching requests to the origin
|
||||
server and caching contents.
|
||||
The backend must be HTTP/1 proxy server. nghttpx only supports
|
||||
multiple backend server addresses. It translates incoming requests to
|
||||
HTTP/1 request to backend server. The backend server performs real
|
||||
proxy work for each request, for example, dispatching requests to the
|
||||
origin server and caching contents.
|
||||
|
||||
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
|
||||
@@ -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
|
||||
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
|
||||
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
|
||||
|
||||
Squid may work as out-of-box. Traffic server requires to be
|
||||
configured as forward proxy. Here is the minimum configuration items
|
||||
to edit::
|
||||
As HTTP/1 proxy server, Squid may work as out-of-box. Traffic server
|
||||
requires to be configured as forward proxy. Here is the minimum
|
||||
configuration items to edit::
|
||||
|
||||
CONFIG proxy.config.reverse_proxy.enabled INT 0
|
||||
CONFIG proxy.config.url_remap.remap_required INT 0
|
||||
@@ -197,9 +197,9 @@ connection, so the connection gets insecure. To disable SSL/TLS in
|
||||
backend connection, use ``--backend-no-tls`` option.
|
||||
|
||||
The backend server is supporsed to be a HTTP/2 web server or HTTP/2
|
||||
proxy. Since HTTP/2 requests opaque between proxied and non-proxied
|
||||
request, the backend server may be proxy or just web server depending
|
||||
on the context of incoming requests.
|
||||
proxy. If backend server is HTTP/2 proxy, use
|
||||
``--no-location-rewrite`` option to disable rewriting location header
|
||||
field.
|
||||
|
||||
The use-case of this mode is aggregate the incoming connections to one
|
||||
HTTP/2 connection. One backend HTTP/2 connection is created per
|
||||
@@ -235,20 +235,13 @@ Read/write rate limit
|
||||
---------------------
|
||||
|
||||
nghttpx supports transfer rate limiting on frontend connections. You
|
||||
can do rate limit per connection or per worker (thread) for reading
|
||||
and writeing individually.
|
||||
|
||||
To rate limit per connection for reading, use ``--read-rate`` and
|
||||
``--read-burst`` options. For writing, use ``--write-rate`` and
|
||||
``--write-burst`` options.
|
||||
can do rate limit per worker (thread) for reading and writeing
|
||||
individually.
|
||||
|
||||
To rate limit per worker (thread), use ``--worker-read-rate`` and
|
||||
``--worker-read-burst`` options. For writing, use
|
||||
``--worker-write-rate`` and ``--worker-write-burst``.
|
||||
|
||||
If both per connection and per worker rate limit configurations are
|
||||
specified, the lower rate is used.
|
||||
|
||||
Please note that rate limit is performed on top of TCP and nothing to
|
||||
do with HTTP/2 flow control.
|
||||
|
||||
@@ -269,3 +262,34 @@ used in frontend, and host is replaced with which appears in
|
||||
precedence. If the above conditions are not met with the host value
|
||||
in :authority header field, rewrite is retried with the value in host
|
||||
header field.
|
||||
|
||||
Hot deploy
|
||||
----------
|
||||
|
||||
nghttpx supports hot deploy feature using signals. The hot deploy in
|
||||
nghttpx is multi step process. First send USR2 signal to nghttpx
|
||||
process. It will do fork and execute new executable, using same
|
||||
command-line arguments and environment variables. At this point, both
|
||||
current and new processes can accept requests. To gracefully shutdown
|
||||
current process, send QUIT signal to current nghttpx process. When
|
||||
all existing frontend connections are done, the current process will
|
||||
exit. At this point, only new nghttpx process exists and serves
|
||||
incoming requests.
|
||||
|
||||
Re-opening log files
|
||||
--------------------
|
||||
|
||||
When rotating log files, it is desirable to re-open log files after
|
||||
log rotation daemon renamed existing log files. To tell nghttpx to
|
||||
re-open log files, send USR1 signal to nghttpx process. It will
|
||||
re-open files specified by ``--accesslog-file`` and
|
||||
``--errorlog-file`` options.
|
||||
|
||||
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,12 +22,10 @@ protocol over the SSL/TLS transport. In this tutorial, we use
|
||||
`nghttp2_select_next_protocol()` function to select the HTTP/2
|
||||
protocol the library supports::
|
||||
|
||||
static int select_next_proto_cb(SSL* ssl,
|
||||
unsigned char **out, unsigned char *outlen,
|
||||
const unsigned char *in, unsigned int inlen,
|
||||
void *arg)
|
||||
{
|
||||
if(nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) {
|
||||
static int select_next_proto_cb(SSL *ssl _U_, unsigned char **out,
|
||||
unsigned char *outlen, const unsigned char *in,
|
||||
unsigned int inlen, void *arg _U_) {
|
||||
if (nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) {
|
||||
errx(1, "Server did not advertise " NGHTTP2_PROTO_VERSION_ID);
|
||||
}
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
@@ -36,17 +34,17 @@ protocol the library supports::
|
||||
The callback is set to the SSL_CTX object using
|
||||
``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_new(SSLv23_client_method());
|
||||
if(!ssl_ctx) {
|
||||
if (!ssl_ctx) {
|
||||
errx(1, "Could not create SSL/TLS context: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
SSL_CTX_set_options(ssl_ctx,
|
||||
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_COMPRESSION |
|
||||
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
|
||||
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
|
||||
SSL_OP_NO_COMPRESSION |
|
||||
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
|
||||
SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL);
|
||||
return ssl_ctx;
|
||||
}
|
||||
@@ -91,25 +89,22 @@ respectively.
|
||||
Then we call function ``initiate_connection()`` to start connecting to
|
||||
the remote server::
|
||||
|
||||
static void initiate_connection(struct event_base *evbase,
|
||||
SSL_CTX *ssl_ctx,
|
||||
static void initiate_connection(struct event_base *evbase, SSL_CTX *ssl_ctx,
|
||||
const char *host, uint16_t port,
|
||||
http2_session_data *session_data)
|
||||
{
|
||||
http2_session_data *session_data) {
|
||||
int rv;
|
||||
struct bufferevent *bev;
|
||||
SSL *ssl;
|
||||
|
||||
ssl = create_ssl(ssl_ctx);
|
||||
bev = bufferevent_openssl_socket_new(evbase, -1, ssl,
|
||||
BUFFEREVENT_SSL_CONNECTING,
|
||||
BEV_OPT_DEFER_CALLBACKS |
|
||||
BEV_OPT_CLOSE_ON_FREE);
|
||||
bev = bufferevent_openssl_socket_new(
|
||||
evbase, -1, ssl, BUFFEREVENT_SSL_CONNECTING,
|
||||
BEV_OPT_DEFER_CALLBACKS | BEV_OPT_CLOSE_ON_FREE);
|
||||
bufferevent_setcb(bev, readcb, writecb, eventcb, session_data);
|
||||
rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase,
|
||||
AF_UNSPEC, host, port);
|
||||
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
errx(1, "Could not connect to the remote host %s", host);
|
||||
}
|
||||
session_data->bev = bev;
|
||||
@@ -122,10 +117,9 @@ The ``eventcb()`` is invoked by libevent event loop when an event
|
||||
(e.g., connection has been established, timeout, etc) happens on the
|
||||
underlying network socket::
|
||||
|
||||
static void eventcb(struct bufferevent *bev, short events, void *ptr)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
||||
if(events & BEV_EVENT_CONNECTED) {
|
||||
static void eventcb(struct bufferevent *bev, short events, void *ptr) {
|
||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||
if (events & BEV_EVENT_CONNECTED) {
|
||||
int fd = bufferevent_getfd(bev);
|
||||
int val = 1;
|
||||
fprintf(stderr, "Connected\n");
|
||||
@@ -133,16 +127,16 @@ underlying network socket::
|
||||
initialize_nghttp2_session(session_data);
|
||||
send_client_connection_header(session_data);
|
||||
submit_request(session_data);
|
||||
if(session_send(session_data) != 0) {
|
||||
if (session_send(session_data) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(events & BEV_EVENT_EOF) {
|
||||
if (events & BEV_EVENT_EOF) {
|
||||
warnx("Disconnected from the remote host");
|
||||
} else if(events & BEV_EVENT_ERROR) {
|
||||
} else if (events & BEV_EVENT_ERROR) {
|
||||
warnx("Network error");
|
||||
} else if(events & BEV_EVENT_TIMEOUT) {
|
||||
} else if (events & BEV_EVENT_TIMEOUT) {
|
||||
warnx("Timeout");
|
||||
}
|
||||
delete_http2_session_data(session_data);
|
||||
@@ -154,17 +148,31 @@ event, we just simply tear down the connection. The
|
||||
finished successfully. We first initialize nghttp2 session object in
|
||||
``initialize_nghttp2_session()`` function::
|
||||
|
||||
static void initialize_nghttp2_session(http2_session_data *session_data)
|
||||
{
|
||||
nghttp2_session_callbacks callbacks = {0};
|
||||
static void initialize_nghttp2_session(http2_session_data *session_data) {
|
||||
nghttp2_session_callbacks *callbacks;
|
||||
|
||||
callbacks.send_callback = send_callback;
|
||||
callbacks.on_frame_recv_callback = on_frame_recv_callback;
|
||||
callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback;
|
||||
callbacks.on_stream_close_callback = on_stream_close_callback;
|
||||
callbacks.on_header_callback = on_header_callback;
|
||||
callbacks.on_begin_headers_callback = on_begin_headers_callback;
|
||||
nghttp2_session_client_new(&session_data->session, &callbacks, session_data);
|
||||
nghttp2_session_callbacks_new(&callbacks);
|
||||
|
||||
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
|
||||
on_frame_recv_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
|
||||
callbacks, on_data_chunk_recv_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_stream_close_callback(
|
||||
callbacks, on_stream_close_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_header_callback(callbacks,
|
||||
on_header_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_begin_headers_callback(
|
||||
callbacks, on_begin_headers_callback);
|
||||
|
||||
nghttp2_session_client_new(&session_data->session, callbacks, session_data);
|
||||
|
||||
nghttp2_session_callbacks_del(callbacks);
|
||||
}
|
||||
|
||||
Since we are creating client, we use `nghttp2_session_client_new()` to
|
||||
@@ -181,19 +189,16 @@ which is 24 bytes magic byte sequence
|
||||
transmission of client connection header is done in
|
||||
``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_MAX_CONCURRENT_STREAMS, 100 }
|
||||
};
|
||||
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
|
||||
int rv;
|
||||
|
||||
bufferevent_write(session_data->bev,
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
||||
bufferevent_write(session_data->bev, NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
|
||||
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE,
|
||||
iv, ARRLEN(iv));
|
||||
if(rv != 0) {
|
||||
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
|
||||
ARRLEN(iv));
|
||||
if (rv != 0) {
|
||||
errx(1, "Could not submit SETTINGS: %s", nghttp2_strerror(rv));
|
||||
}
|
||||
}
|
||||
@@ -210,24 +215,22 @@ used, which is described about later.
|
||||
After the transmission of client connection header, we enqueue HTTP
|
||||
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;
|
||||
http2_stream_data *stream_data = session_data->stream_data;
|
||||
const char *uri = stream_data->uri;
|
||||
const struct http_parser_url *u = stream_data->u;
|
||||
nghttp2_nv hdrs[] = {
|
||||
MAKE_NV2(":method", "GET"),
|
||||
MAKE_NV(":scheme",
|
||||
&uri[u->field_data[UF_SCHEMA].off], u->field_data[UF_SCHEMA].len),
|
||||
MAKE_NV(":authority", stream_data->authority, stream_data->authoritylen),
|
||||
MAKE_NV(":path", stream_data->path, stream_data->pathlen)
|
||||
};
|
||||
MAKE_NV2(":method", "GET"),
|
||||
MAKE_NV(":scheme", &uri[u->field_data[UF_SCHEMA].off],
|
||||
u->field_data[UF_SCHEMA].len),
|
||||
MAKE_NV(":authority", stream_data->authority, stream_data->authoritylen),
|
||||
MAKE_NV(":path", stream_data->path, stream_data->pathlen)};
|
||||
fprintf(stderr, "Request headers:\n");
|
||||
print_headers(stderr, hdrs, ARRLEN(hdrs));
|
||||
stream_id = nghttp2_submit_request(session_data->session, NULL,
|
||||
hdrs, ARRLEN(hdrs), NULL, stream_data);
|
||||
if(stream_id < 0) {
|
||||
stream_id = nghttp2_submit_request(session_data->session, NULL, hdrs,
|
||||
ARRLEN(hdrs), NULL, stream_data);
|
||||
if (stream_id < 0) {
|
||||
errx(1, "Could not submit HTTP request: %s", nghttp2_strerror(stream_id));
|
||||
}
|
||||
|
||||
@@ -246,26 +249,25 @@ this request.
|
||||
The next bufferevent callback is ``readcb()``, which is invoked when
|
||||
data is available to read in the bufferevent input buffer::
|
||||
|
||||
static void readcb(struct bufferevent *bev, void *ptr)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
||||
static void readcb(struct bufferevent *bev, void *ptr) {
|
||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||
ssize_t readlen;
|
||||
struct evbuffer *input = bufferevent_get_input(bev);
|
||||
size_t datalen = evbuffer_get_length(input);
|
||||
unsigned char *data = evbuffer_pullup(input, -1);
|
||||
|
||||
readlen = nghttp2_session_mem_recv(session_data->session, data, datalen);
|
||||
if(readlen < 0) {
|
||||
if (readlen < 0) {
|
||||
warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
if(evbuffer_drain(input, readlen) != 0) {
|
||||
if (evbuffer_drain(input, readlen) != 0) {
|
||||
warnx("Fatal error: evbuffer_drain failed");
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
if(session_send(session_data) != 0) {
|
||||
if (session_send(session_data) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
@@ -278,12 +280,11 @@ invoke nghttp2 callbacks and also queue frames. Since there may be
|
||||
pending frames, we call ``session_send()`` function to send those
|
||||
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;
|
||||
|
||||
rv = nghttp2_session_send(session_data->session);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||
return -1;
|
||||
}
|
||||
@@ -291,17 +292,13 @@ frames. The ``session_send()`` function is defined as follows::
|
||||
}
|
||||
|
||||
The `nghttp2_session_send()` function serializes the frame into wire
|
||||
format and call :member:`nghttp2_session_callbacks.send_callback` with
|
||||
it. We set ``send_callback()`` function to
|
||||
:member:`nghttp2_session_callbacks.send_callback` in
|
||||
``initialize_nghttp2_session()`` function described earlier. It is
|
||||
defined as follows::
|
||||
format and call ``send_callback()`` function of type
|
||||
:type:`nghttp2_send_callback`. The ``send_callback()`` is defined as
|
||||
follows::
|
||||
|
||||
static ssize_t send_callback(nghttp2_session *session,
|
||||
const uint8_t *data, size_t length,
|
||||
int flags, void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
|
||||
size_t length, int flags _U_, void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
struct bufferevent *bev = session_data->bev;
|
||||
bufferevent_write(bev, data, length);
|
||||
return length;
|
||||
@@ -311,25 +308,23 @@ Since we use bufferevent to abstract network I/O, we just write the
|
||||
data to the bufferevent object. Note that `nghttp2_session_send()`
|
||||
continues to write all frames queued so far. If we were writing the
|
||||
data to the non-blocking socket directly using ``write()`` system call
|
||||
in the :member:`nghttp2_session_callbacks.send_callback`, we will
|
||||
surely get ``EAGAIN`` or ``EWOULDBLOCK`` since the socket has limited
|
||||
send buffer. If that happens, we can return
|
||||
:macro:`NGHTTP2_ERR_WOULDBLOCK` to signal the nghttp2 library to stop
|
||||
sending further data. But writing to the bufferevent, we have to
|
||||
regulate the amount data to be buffered by ourselves to avoid possible
|
||||
huge memory consumption. In this example client, we do not limit
|
||||
anything. To see how to regulate the amount of buffered data, see the
|
||||
``send_callback()`` in the server tutorial.
|
||||
in the ``send_callback()``, we will surely get ``EAGAIN`` or
|
||||
``EWOULDBLOCK`` since the socket has limited send buffer. If that
|
||||
happens, we can return :macro:`NGHTTP2_ERR_WOULDBLOCK` to signal the
|
||||
nghttp2 library to stop sending further data. But writing to the
|
||||
bufferevent, we have to regulate the amount data to be buffered by
|
||||
ourselves to avoid possible huge memory consumption. In this example
|
||||
client, we do not limit anything. To see how to regulate the amount of
|
||||
buffered data, see the ``send_callback()`` in the server tutorial.
|
||||
|
||||
The third bufferevent callback is ``writecb()``, which is invoked when
|
||||
all data written in the bufferevent output buffer have been sent::
|
||||
|
||||
static void writecb(struct bufferevent *bev, void *ptr)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
||||
if(nghttp2_session_want_read(session_data->session) == 0 &&
|
||||
nghttp2_session_want_write(session_data->session) == 0 &&
|
||||
evbuffer_get_length(bufferevent_get_output(session_data->bev)) == 0) {
|
||||
static void writecb(struct bufferevent *bev _U_, void *ptr) {
|
||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||
if (nghttp2_session_want_read(session_data->session) == 0 &&
|
||||
nghttp2_session_want_write(session_data->session) == 0 &&
|
||||
evbuffer_get_length(bufferevent_get_output(session_data->bev)) == 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
}
|
||||
}
|
||||
@@ -355,18 +350,16 @@ Let's describe remaining nghttp2 callbacks we setup in
|
||||
Each request header name/value pair is emitted via
|
||||
``on_header_callback`` function::
|
||||
|
||||
static int on_header_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame,
|
||||
const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen,
|
||||
uint8_t flags,
|
||||
void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
switch(frame->hd.type) {
|
||||
static int on_header_callback(nghttp2_session *session _U_,
|
||||
const nghttp2_frame *frame, const uint8_t *name,
|
||||
size_t namelen, const uint8_t *value,
|
||||
size_t valuelen, uint8_t flags _U_,
|
||||
void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
switch (frame->hd.type) {
|
||||
case NGHTTP2_HEADERS:
|
||||
if(frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
|
||||
session_data->stream_data->stream_id == frame->hd.stream_id) {
|
||||
if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
|
||||
session_data->stream_data->stream_id == frame->hd.stream_id) {
|
||||
/* Print response headers for the initiated request. */
|
||||
print_header(stderr, name, namelen, value, valuelen);
|
||||
break;
|
||||
@@ -375,19 +368,18 @@ Each request header name/value pair is emitted via
|
||||
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,
|
||||
``on_frame_recv_callback`` function is called::
|
||||
|
||||
static int on_frame_recv_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame, void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
switch(frame->hd.type) {
|
||||
static int on_frame_recv_callback(nghttp2_session *session _U_,
|
||||
const nghttp2_frame *frame, void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
switch (frame->hd.type) {
|
||||
case NGHTTP2_HEADERS:
|
||||
if(frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
|
||||
session_data->stream_data->stream_id == frame->hd.stream_id) {
|
||||
if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
|
||||
session_data->stream_data->stream_id == frame->hd.stream_id) {
|
||||
fprintf(stderr, "All headers received\n");
|
||||
}
|
||||
break;
|
||||
@@ -403,13 +395,12 @@ its stream ID.
|
||||
The ``on_data_chunk_recv_callback()`` function is invoked when a chunk
|
||||
of data is received from the remote peer::
|
||||
|
||||
static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
||||
int32_t stream_id,
|
||||
static int on_data_chunk_recv_callback(nghttp2_session *session _U_,
|
||||
uint8_t flags _U_, int32_t stream_id,
|
||||
const uint8_t *data, size_t len,
|
||||
void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
if(session_data->stream_data->stream_id == stream_id) {
|
||||
void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
if (session_data->stream_data->stream_id == stream_id) {
|
||||
fwrite(data, len, 1, stdout);
|
||||
}
|
||||
return 0;
|
||||
@@ -423,19 +414,17 @@ some binary data.
|
||||
The ``on_stream_close_callback()`` function is invoked when the stream
|
||||
is about to close::
|
||||
|
||||
static int on_stream_close_callback(nghttp2_session *session,
|
||||
int32_t stream_id,
|
||||
static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||
nghttp2_error_code error_code,
|
||||
void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
int rv;
|
||||
|
||||
if(session_data->stream_data->stream_id == stream_id) {
|
||||
fprintf(stderr, "Stream %d closed with error_code=%d\n",
|
||||
stream_id, error_code);
|
||||
if (session_data->stream_data->stream_id == stream_id) {
|
||||
fprintf(stderr, "Stream %d closed with error_code=%d\n", stream_id,
|
||||
error_code);
|
||||
rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,18 +66,6 @@ size of buffer.
|
||||
To delete :type:`nghttp2_hd_deflater` object, use `nghttp2_hd_deflate_del()`
|
||||
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
|
||||
----------------------------
|
||||
|
||||
|
||||
@@ -3,44 +3,43 @@ Tutorial: HTTP/2 server
|
||||
|
||||
In this tutorial, we are going to write single-threaded, event-based
|
||||
HTTP/2 web server, which supports HTTPS only. It can handle
|
||||
concurrent multiple requests, but only GET method is supported. The
|
||||
concurrent multiple requests, but only the GET method is supported. The
|
||||
complete source code, `libevent-server.c`_, is attached at the end of
|
||||
this page. It also resides in examples directory in the archive or
|
||||
repository.
|
||||
|
||||
This simple server takes 3 arguments, a port number to listen to, a
|
||||
path to SSL/TLS private key file and certificate file. Its synopsis
|
||||
is like this::
|
||||
This simple server takes 3 arguments, a port number to listen to, a path to
|
||||
your SSL/TLS private key file and a path to your certificate file. Its
|
||||
synopsis is like this::
|
||||
|
||||
$ libevent-server PORT /path/to/server.key /path/to/server.crt
|
||||
|
||||
We use libevent in this tutorial to handle networking I/O. Please
|
||||
note that nghttp2 itself does not depend on libevent.
|
||||
|
||||
First we do some setup routine for libevent and OpenSSL library in
|
||||
function ``main()`` and ``run()``, which is not so relevant to nghttp2
|
||||
library use. The one thing you should look at is setup NPN callback.
|
||||
The NPN callback is used for the server to advertise the application
|
||||
protocols the server supports to a client. In this example program,
|
||||
when creating ``SSL_CTX`` object, we stores the application protocol
|
||||
name in the wire format of NPN in statically allocated buffer. This is
|
||||
safe because we only create 1 ``SSL_CTX`` object in the entire program
|
||||
life time::
|
||||
First we create a setup routine for libevent and OpenSSL in the functions
|
||||
``main()`` and ``run()``. One thing in there you should look at, is the setup
|
||||
of the NPN callback. The NPN callback is used for the server to advertise
|
||||
which application protocols the server supports to a client. In this example
|
||||
program, when creating ``SSL_CTX`` object, we store the application protocol
|
||||
name in the wire format of NPN in a statically allocated buffer. This is safe
|
||||
because we only create one ``SSL_CTX`` object in the program's entire life
|
||||
time::
|
||||
|
||||
static unsigned char next_proto_list[256];
|
||||
static size_t next_proto_list_len;
|
||||
|
||||
static int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len,
|
||||
void *arg)
|
||||
{
|
||||
static int next_proto_cb(SSL *s _U_, const unsigned char **data,
|
||||
unsigned int *len, void *arg _U_) {
|
||||
*data = next_proto_list;
|
||||
*len = (unsigned int)next_proto_list_len;
|
||||
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;
|
||||
EC_KEY *ecdh;
|
||||
|
||||
ssl_ctx = SSL_CTX_new(SSLv23_server_method());
|
||||
|
||||
...
|
||||
@@ -54,25 +53,25 @@ life time::
|
||||
return ssl_ctx;
|
||||
}
|
||||
|
||||
The wire format of NPN is a sequence of length prefixed string. The
|
||||
exactly one byte is used to specify the length of each protocol
|
||||
identifier. In this tutorial, we advertise the HTTP/2 protocol the
|
||||
nghttp2 library supports. The nghttp2 library exports its identifier
|
||||
in :macro:`NGHTTP2_PROTO_VERSION_ID`. The ``next_proto_cb()`` function
|
||||
is the server-side NPN callback. In OpenSSL implementation, we just
|
||||
assign the pointer to the NPN buffers we filled earlier. The NPN
|
||||
callback function is set to ``SSL_CTX`` object using
|
||||
The wire format of NPN is a sequence of length prefixed string. Exactly one
|
||||
byte is used to specify the length of each protocol identifier. In this
|
||||
tutorial, we advertise the specific HTTP/2 protocol version the current
|
||||
nghttp2 library supports. The nghttp2 library exports its identifier in
|
||||
:macro:`NGHTTP2_PROTO_VERSION_ID`. The ``next_proto_cb()`` function is the
|
||||
server-side NPN callback. In the OpenSSL implementation, we just assign the
|
||||
pointer to the NPN buffers we filled in earlier. The NPN callback function is
|
||||
set to the ``SSL_CTX`` object using
|
||||
``SSL_CTX_set_next_protos_advertised_cb()``.
|
||||
|
||||
We use ``app_content`` structure to store the application-wide data::
|
||||
We use the ``app_content`` structure to store application-wide data::
|
||||
|
||||
struct app_context {
|
||||
SSL_CTX *ssl_ctx;
|
||||
struct event_base *evbase;
|
||||
};
|
||||
|
||||
We use ``http2_session_data`` structure to store the session-level
|
||||
(which corresponds to 1 HTTP/2 connection) data::
|
||||
We use the ``http2_session_data`` structure to store session-level
|
||||
(which corresponds to one HTTP/2 connection) data::
|
||||
|
||||
typedef struct http2_session_data {
|
||||
struct http2_stream_data root;
|
||||
@@ -80,11 +79,9 @@ We use ``http2_session_data`` structure to store the session-level
|
||||
app_context *app_ctx;
|
||||
nghttp2_session *session;
|
||||
char *client_addr;
|
||||
size_t handshake_leftlen;
|
||||
} http2_session_data;
|
||||
|
||||
We use ``http2_stream_data`` structure to store the stream-level
|
||||
data::
|
||||
We use the ``http2_stream_data`` structure to store stream-level data::
|
||||
|
||||
typedef struct http2_stream_data {
|
||||
struct http2_stream_data *prev, *next;
|
||||
@@ -93,27 +90,21 @@ data::
|
||||
int fd;
|
||||
} http2_stream_data;
|
||||
|
||||
1 HTTP/2 session can have multiple streams. We manage these multiple
|
||||
streams by intrusive doubly linked list to add and remove the object
|
||||
in O(1). The first element of this list is pointed by the
|
||||
``root->next`` in ``http2_session_data``. Initially, ``root->next``
|
||||
is ``NULL``. The ``handshake_leftlen`` member of
|
||||
``http2_session_data`` is used to track the number of bytes remaining
|
||||
when receiving first client connection preface
|
||||
(:macro:`NGHTTP2_CLIENT_CONNECTION_PREFACE`), which is 24 bytes magic
|
||||
byte string, from the client. We use libevent's bufferevent structure
|
||||
to perform network I/O. Notice that bufferevent object is in
|
||||
``http2_session_data`` and not in ``http2_stream_data``. This is
|
||||
because ``http2_stream_data`` is just a logical stream multiplexed
|
||||
A single HTTP/2 session can have multiple streams. We manage these
|
||||
multiple streams with a doubly linked list. The first element of this
|
||||
list is pointed to by the ``root->next`` in ``http2_session_data``.
|
||||
Initially, ``root->next`` is ``NULL``. We use libevent's bufferevent
|
||||
structure to perform network I/O. Note that the bufferevent object is
|
||||
kept in ``http2_session_data`` and not in ``http2_stream_data``. This
|
||||
is because ``http2_stream_data`` is just a logical stream multiplexed
|
||||
over the single connection managed by bufferevent in
|
||||
``http2_session_data``.
|
||||
|
||||
We first create listener object to accept incoming connections.
|
||||
We use libevent's ``struct evconnlistener`` for this purpose::
|
||||
We first create a listener object to accept incoming connections. We use
|
||||
libevent's ``struct evconnlistener`` for this purpose::
|
||||
|
||||
static void start_listen(struct event_base *evbase, const char *service,
|
||||
app_context *app_ctx)
|
||||
{
|
||||
app_context *app_ctx) {
|
||||
int rv;
|
||||
struct addrinfo hints;
|
||||
struct addrinfo *res, *rp;
|
||||
@@ -124,200 +115,188 @@ We use libevent's ``struct evconnlistener`` for this purpose::
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
#ifdef AI_ADDRCONFIG
|
||||
hints.ai_flags |= AI_ADDRCONFIG;
|
||||
#endif // AI_ADDRCONFIG
|
||||
#endif /* AI_ADDRCONFIG */
|
||||
|
||||
rv = getaddrinfo(NULL, service, &hints, &res);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
errx(1, NULL);
|
||||
}
|
||||
for(rp = res; rp; rp = rp->ai_next) {
|
||||
for (rp = res; rp; rp = rp->ai_next) {
|
||||
struct evconnlistener *listener;
|
||||
listener = evconnlistener_new_bind(evbase, acceptcb, app_ctx,
|
||||
LEV_OPT_CLOSE_ON_FREE |
|
||||
LEV_OPT_REUSEABLE, -1,
|
||||
rp->ai_addr, rp->ai_addrlen);
|
||||
if(listener) {
|
||||
listener = evconnlistener_new_bind(
|
||||
evbase, acceptcb, app_ctx, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
|
||||
16, rp->ai_addr, rp->ai_addrlen);
|
||||
if (listener) {
|
||||
freeaddrinfo(res);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
errx(1, "Could not start listener");
|
||||
}
|
||||
|
||||
We specify ``acceptcb`` callback which is called when a new connection
|
||||
is accepted::
|
||||
We specify the ``acceptcb`` callback which is called when a new connection is
|
||||
accepted::
|
||||
|
||||
static void acceptcb(struct evconnlistener *listener, int fd,
|
||||
struct sockaddr *addr, int addrlen, void *arg)
|
||||
{
|
||||
app_context *app_ctx = (app_context*)arg;
|
||||
static void acceptcb(struct evconnlistener *listener _U_, int fd,
|
||||
struct sockaddr *addr, int addrlen, void *arg) {
|
||||
app_context *app_ctx = (app_context *)arg;
|
||||
http2_session_data *session_data;
|
||||
|
||||
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 ``http2_session_data`` object. The bufferevent for this
|
||||
connection is also initialized at this time. We specify 2 callbacks
|
||||
for the bufferevent: ``handshake_readcb`` and ``eventcb``.
|
||||
Here we create the ``http2_session_data`` object. The bufferevent for
|
||||
this connection is also initialized at this time. We specify three
|
||||
callbacks for the bufferevent: ``readcb``, ``writecb`` and
|
||||
``eventcb``.
|
||||
|
||||
The ``eventcb()`` is invoked by 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
|
||||
underlying network socket::
|
||||
|
||||
static void eventcb(struct bufferevent *bev, short events, void *ptr)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
||||
if(events & BEV_EVENT_CONNECTED) {
|
||||
static void eventcb(struct bufferevent *bev _U_, short events, void *ptr) {
|
||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||
if (events & BEV_EVENT_CONNECTED) {
|
||||
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;
|
||||
}
|
||||
if(events & BEV_EVENT_EOF) {
|
||||
if (events & BEV_EVENT_EOF) {
|
||||
fprintf(stderr, "%s EOF\n", session_data->client_addr);
|
||||
} else if(events & BEV_EVENT_ERROR) {
|
||||
} else if (events & BEV_EVENT_ERROR) {
|
||||
fprintf(stderr, "%s network error\n", session_data->client_addr);
|
||||
} else if(events & BEV_EVENT_TIMEOUT) {
|
||||
} else if (events & BEV_EVENT_TIMEOUT) {
|
||||
fprintf(stderr, "%s timeout\n", session_data->client_addr);
|
||||
}
|
||||
delete_http2_session_data(session_data);
|
||||
}
|
||||
|
||||
For ``BEV_EVENT_EOF``, ``BEV_EVENT_ERROR`` and ``BEV_EVENT_TIMEOUT``
|
||||
event, we just simply tear down the connection. The
|
||||
``delete_http2_session_data()`` function destroys
|
||||
``http2_session_data`` object and thus its bufferevent member. As a
|
||||
result, the underlying connection is closed. The
|
||||
For the ``BEV_EVENT_EOF``, ``BEV_EVENT_ERROR`` and
|
||||
``BEV_EVENT_TIMEOUT`` events, we just simply tear down the connection.
|
||||
The ``delete_http2_session_data()`` function destroys the
|
||||
``http2_session_data`` object and thus also its bufferevent member.
|
||||
As a result, the underlying connection is closed. The
|
||||
``BEV_EVENT_CONNECTED`` event is invoked when SSL/TLS handshake is
|
||||
finished successfully.
|
||||
finished successfully. Now we are ready to start the HTTP/2
|
||||
communication.
|
||||
|
||||
The ``handshake_readcb()`` is a callback function to handle 24 bytes
|
||||
magic byte string from a client, since 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 for starting HTTP/2 communication. First
|
||||
we change the callback functions for the bufferevent object. We use
|
||||
same ``eventcb`` as before. But we specify new ``readcb`` and
|
||||
``writecb`` function to handle HTTP/2 communication. We describe
|
||||
these 2 functions later.
|
||||
|
||||
We initialize nghttp2 session object which is done in
|
||||
We initialize a nghttp2 session object which is done in
|
||||
``initialize_nghttp2_session()``::
|
||||
|
||||
static void initialize_nghttp2_session(http2_session_data *session_data)
|
||||
{
|
||||
nghttp2_session_callbacks callbacks = {0};
|
||||
static void initialize_nghttp2_session(http2_session_data *session_data) {
|
||||
nghttp2_option *option;
|
||||
nghttp2_session_callbacks *callbacks;
|
||||
|
||||
callbacks.send_callback = send_callback;
|
||||
callbacks.on_frame_recv_callback = on_frame_recv_callback;
|
||||
callbacks.on_stream_close_callback = on_stream_close_callback;
|
||||
callbacks.on_header_callback = on_header_callback;
|
||||
callbacks.on_begin_headers_callback = on_begin_headers_callback;
|
||||
nghttp2_session_server_new(&session_data->session, &callbacks, session_data);
|
||||
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_set_send_callback(callbacks, send_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
|
||||
on_frame_recv_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_stream_close_callback(
|
||||
callbacks, on_stream_close_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_header_callback(callbacks,
|
||||
on_header_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_begin_headers_callback(
|
||||
callbacks, on_begin_headers_callback);
|
||||
|
||||
nghttp2_session_server_new2(&session_data->session, callbacks, session_data,
|
||||
option);
|
||||
|
||||
nghttp2_session_callbacks_del(callbacks);
|
||||
nghttp2_option_del(option);
|
||||
}
|
||||
|
||||
Since we are creating server, nghttp2 session object is created using
|
||||
`nghttp2_session_server_new()` function. We registers 5 callbacks to
|
||||
nghttp2 session object. We'll talk about these callbacks later.
|
||||
Since we are creating a server and uses options, the nghttp2 session
|
||||
object is created using `nghttp2_session_server_new2()` function. We
|
||||
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 nghttp2 session object, we are going to send
|
||||
server connection header in ``send_server_connection_header()``::
|
||||
After initialization of the nghttp2 session object, we are going to send
|
||||
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_MAX_CONCURRENT_STREAMS, 100 }
|
||||
};
|
||||
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
|
||||
int rv;
|
||||
|
||||
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE,
|
||||
iv, ARRLEN(iv));
|
||||
if(rv != 0) {
|
||||
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
|
||||
ARRLEN(iv));
|
||||
if (rv != 0) {
|
||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
The server connection header is SETTINGS frame. We specify
|
||||
SETTINGS_MAX_CONCURRENT_STREAMS to 100 in SETTINGS frame. To queue
|
||||
The server connection header is a SETTINGS frame. We specify
|
||||
SETTINGS_MAX_CONCURRENT_STREAMS to 100 in the SETTINGS frame. To queue
|
||||
the SETTINGS frame for the transmission, we use
|
||||
`nghttp2_submit_settings()`. Note that `nghttp2_submit_settings()`
|
||||
function only queues the frame and not actually send it. All
|
||||
``nghttp2_submit_*()`` family functions have this property. To
|
||||
actually send the frame, `nghttp2_session_send()` is used, which is
|
||||
described about later.
|
||||
function only queues the frame and it does not actually send it. All
|
||||
functions in the ``nghttp2_submit_*()`` family have this property. To
|
||||
actually send the frame, `nghttp2_session_send()` should be used, as
|
||||
described later.
|
||||
|
||||
Since bufferevent may buffer more than first 24 bytes from the client,
|
||||
we have to process them here since libevent won't invoke callback
|
||||
functions for these pending data. To process received data, we call
|
||||
Since bufferevent may buffer more than the first 24 bytes from the client, we
|
||||
have to process them here since libevent won't invoke callback functions for
|
||||
this pending data. To process the received data, we call the
|
||||
``session_recv()`` function::
|
||||
|
||||
static int session_recv(http2_session_data *session_data)
|
||||
{
|
||||
static int session_recv(http2_session_data *session_data) {
|
||||
ssize_t readlen;
|
||||
struct evbuffer *input = bufferevent_get_input(session_data->bev);
|
||||
size_t datalen = evbuffer_get_length(input);
|
||||
unsigned char *data = evbuffer_pullup(input, -1);
|
||||
|
||||
readlen = nghttp2_session_mem_recv(session_data->session, data, datalen);
|
||||
if(readlen < 0) {
|
||||
if (readlen < 0) {
|
||||
warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
|
||||
return -1;
|
||||
}
|
||||
if(evbuffer_drain(input, readlen) != 0) {
|
||||
if (evbuffer_drain(input, readlen) != 0) {
|
||||
warnx("Fatal error: evbuffer_drain failed");
|
||||
return -1;
|
||||
}
|
||||
if(session_send(session_data) != 0) {
|
||||
if (session_send(session_data) != 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
In this function, we feed all unprocessed, received data to nghttp2
|
||||
session object using `nghttp2_session_mem_recv()` function. The
|
||||
`nghttp2_session_mem_recv()` processes the received data and may
|
||||
invoke nghttp2 callbacks and also queue outgoing frames. Since there
|
||||
may be pending frames, we call ``session_send()`` function to send
|
||||
those frames. The ``session_send()`` function is defined as follows::
|
||||
In this function, we feed all unprocessed but already received data to the
|
||||
nghttp2 session object using the `nghttp2_session_mem_recv()` function. The
|
||||
`nghttp2_session_mem_recv()` function processes the data and may invoke the
|
||||
nghttp2 callbacks and also queue outgoing frames. Since there may be pending
|
||||
outgoing frames, we call ``session_send()`` function to send off those
|
||||
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;
|
||||
rv = nghttp2_session_send(session_data->session);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||
return -1;
|
||||
}
|
||||
@@ -325,21 +304,17 @@ those frames. The ``session_send()`` function is defined as follows::
|
||||
}
|
||||
|
||||
The `nghttp2_session_send()` function serializes the frame into wire
|
||||
format and call :member:`nghttp2_session_callbacks.send_callback` with
|
||||
it. We set ``send_callback()`` function to
|
||||
:member:`nghttp2_session_callbacks.send_callback` in
|
||||
``initialize_nghttp2_session()`` function described earlier. It is
|
||||
defined as follows::
|
||||
format and calls ``send_callback()`` of type
|
||||
:type:`nghttp2_send_callback`. The ``send_callback()`` is defined as
|
||||
follows::
|
||||
|
||||
static ssize_t send_callback(nghttp2_session *session,
|
||||
const uint8_t *data, size_t length,
|
||||
int flags, void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
|
||||
size_t length, int flags _U_, void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
struct bufferevent *bev = session_data->bev;
|
||||
/* Avoid excessive buffering in server side. */
|
||||
if(evbuffer_get_length(bufferevent_get_output(session_data->bev)) >=
|
||||
OUTPUT_WOULDBLOCK_THRESHOLD) {
|
||||
if (evbuffer_get_length(bufferevent_get_output(session_data->bev)) >=
|
||||
OUTPUT_WOULDBLOCK_THRESHOLD) {
|
||||
return NGHTTP2_ERR_WOULDBLOCK;
|
||||
}
|
||||
bufferevent_write(bev, data, length);
|
||||
@@ -349,26 +324,24 @@ defined as follows::
|
||||
Since we use bufferevent to abstract network I/O, we just write the
|
||||
data to the bufferevent object. Note that `nghttp2_session_send()`
|
||||
continues to write all frames queued so far. If we were writing the
|
||||
data to the non-blocking socket directly using ``write()`` system call
|
||||
in the :member:`nghttp2_session_callbacks.send_callback`, we will
|
||||
surely get ``EAGAIN`` or ``EWOULDBLOCK`` since the socket has limited
|
||||
send buffer. If that happens, we can return
|
||||
:macro:`NGHTTP2_ERR_WOULDBLOCK` to signal the nghttp2 library to stop
|
||||
sending further data. But writing to the bufferevent, we have to
|
||||
regulate the amount data to be buffered by ourselves to avoid possible
|
||||
huge memory consumption. To achieve this, we check the size of output
|
||||
buffer and if it is more than or equal to
|
||||
``OUTPUT_WOULDBLOCK_THRESHOLD`` bytes, we stop writing data and return
|
||||
:macro:`NGHTTP2_ERR_WOULDBLOCK` to tell the library to stop calling
|
||||
send_callback.
|
||||
data to a non-blocking socket directly using ``write()`` system call
|
||||
in the ``send_callback()``, we would surely get ``EAGAIN`` or
|
||||
``EWOULDBLOCK`` back since the socket has limited send buffer. If that
|
||||
happens, we can return :macro:`NGHTTP2_ERR_WOULDBLOCK` to signal the
|
||||
nghttp2 library to stop sending further data. But when writing to the
|
||||
bufferevent, we have to regulate the amount data to get buffered
|
||||
ourselves to avoid using huge amounts of memory. To achieve this, we
|
||||
check the size of the output buffer and if it reaches more than or
|
||||
equal to ``OUTPUT_WOULDBLOCK_THRESHOLD`` bytes, we stop writing data
|
||||
and return :macro:`NGHTTP2_ERR_WOULDBLOCK` to tell the library to stop
|
||||
calling send_callback.
|
||||
|
||||
The next bufferevent callback is ``readcb()``, which is invoked when
|
||||
data is available to read in the bufferevent input buffer::
|
||||
|
||||
static void readcb(struct bufferevent *bev, void *ptr)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
||||
if(session_recv(session_data) != 0) {
|
||||
static void readcb(struct bufferevent *bev _U_, void *ptr) {
|
||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||
if (session_recv(session_data) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
@@ -377,59 +350,56 @@ data is available to read in the bufferevent input buffer::
|
||||
In this function, we just call ``session_recv()`` to process incoming
|
||||
data.
|
||||
|
||||
The third bufferevent callback is ``writecb()``, which is invoked when
|
||||
all data written in the bufferevent output buffer have been sent::
|
||||
The third bufferevent callback is ``writecb()``, which is invoked when all
|
||||
data in the bufferevent output buffer has been sent::
|
||||
|
||||
static void writecb(struct bufferevent *bev, void *ptr)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
||||
if(evbuffer_get_length(bufferevent_get_output(bev)) > 0) {
|
||||
static void writecb(struct bufferevent *bev, void *ptr) {
|
||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||
if (evbuffer_get_length(bufferevent_get_output(bev)) > 0) {
|
||||
return;
|
||||
}
|
||||
if(nghttp2_session_want_read(session_data->session) == 0 &&
|
||||
nghttp2_session_want_write(session_data->session) == 0) {
|
||||
if (nghttp2_session_want_read(session_data->session) == 0 &&
|
||||
nghttp2_session_want_write(session_data->session) == 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
if(session_send(session_data) != 0) {
|
||||
if (session_send(session_data) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
First we check whether we should drop connection or not. The nghttp2
|
||||
session object keeps track of reception and transmission of GOAWAY
|
||||
frame and other error conditions as well. Using these information,
|
||||
nghttp2 session object will tell whether the connection should be
|
||||
dropped or not. More specifically, both `nghttp2_session_want_read()`
|
||||
and `nghttp2_session_want_write()` return 0, we have no business in
|
||||
the connection. But since we are using bufferevent and its deferred
|
||||
callback option, the bufferevent output buffer may contain the pending
|
||||
data when the ``writecb()`` is called. To handle this situation, we
|
||||
also check whether the output buffer is empty or not. If these
|
||||
conditions are met, we drop connection.
|
||||
First we check whether we should drop the connection or not. The nghttp2
|
||||
session object keeps track of reception and transmission of GOAWAY frames and
|
||||
other error conditions as well. Using this information, the nghttp2 session
|
||||
object will tell whether the connection should be dropped or not. More
|
||||
specifically, if both `nghttp2_session_want_read()` and
|
||||
`nghttp2_session_want_write()` return 0, we have no business left in the
|
||||
connection. But since we are using bufferevent and its deferred callback
|
||||
option, the bufferevent output buffer may contain pending data when the
|
||||
``writecb()`` is called. To handle this, we check whether the output buffer is
|
||||
empty or not. If all these conditions are met, we drop connection.
|
||||
|
||||
Otherwise, we call ``session_send()`` to process pending output
|
||||
data. Remember that in ``send_callback()``, we may not write all data
|
||||
to bufferevent to avoid excessive buffering. We continue process
|
||||
pending data when output buffer becomes empty.
|
||||
Otherwise, we call ``session_send()`` to process the pending output
|
||||
data. Remember that in ``send_callback()``, we must not write all data to
|
||||
bufferevent to avoid excessive buffering. We continue processing pending data
|
||||
when the output buffer becomes empty.
|
||||
|
||||
We have already described about nghttp2 callback ``send_callback()``.
|
||||
Let's describe remaining nghttp2 callbacks we setup in
|
||||
We have already described the nghttp2 callback ``send_callback()``. Let's
|
||||
learn about the remaining nghttp2 callbacks we setup in
|
||||
``initialize_nghttp2_setup()`` function.
|
||||
|
||||
The ``on_begin_headers_callback()`` function is invoked when reception
|
||||
of header block in HEADERS or PUSH_PROMISE frame is started::
|
||||
The ``on_begin_headers_callback()`` function is invoked when the reception of
|
||||
a header block in HEADERS or PUSH_PROMISE frame is started::
|
||||
|
||||
static int on_begin_headers_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame,
|
||||
void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
http2_stream_data *stream_data;
|
||||
|
||||
if(frame->hd.type != NGHTTP2_HEADERS ||
|
||||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
||||
if (frame->hd.type != NGHTTP2_HEADERS ||
|
||||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
||||
return 0;
|
||||
}
|
||||
stream_data = create_http2_stream_data(session_data, frame->hd.stream_id);
|
||||
@@ -438,40 +408,40 @@ of header block in HEADERS or PUSH_PROMISE frame is started::
|
||||
return 0;
|
||||
}
|
||||
|
||||
We only interested in HEADERS frame in this function. Since HEADERS
|
||||
frame has several roles in HTTP/2 protocol, we check that it is a
|
||||
request HEADERS, which opens new stream. If frame is request HEADERS,
|
||||
then we create ``http2_stream_data`` object to store stream related
|
||||
data. We associate created ``http2_stream_data`` object to the stream
|
||||
in nghttp2 session object using `nghttp2_set_stream_user_data()` in
|
||||
order to get the object without searching through doubly linked list.
|
||||
We are only interested in the HEADERS frame in this function. Since the
|
||||
HEADERS frame has several roles in the HTTP/2 protocol, we check that it is a
|
||||
request HEADERS, which opens new stream. If the frame is a request HEADERS, we
|
||||
create a ``http2_stream_data`` object to store the stream related data. We
|
||||
associate the created ``http2_stream_data`` object with the stream in the
|
||||
nghttp2 session object using `nghttp2_set_stream_user_data()` to get the
|
||||
object without searching through the doubly linked list.
|
||||
|
||||
In this example server, we want to serve files relative to the current
|
||||
working directory the program was invoked. Each header name/value pair
|
||||
is emitted via ``on_header_callback`` function, which is called after
|
||||
In this example server, we want to serve files relative to the current working
|
||||
directory in which the program was invoked. Each header name/value pair is
|
||||
emitted via ``on_header_callback`` function, which is called after
|
||||
``on_begin_headers_callback()``::
|
||||
|
||||
static int on_header_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame,
|
||||
const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen,
|
||||
void *user_data)
|
||||
{
|
||||
const nghttp2_frame *frame, const uint8_t *name,
|
||||
size_t namelen, const uint8_t *value,
|
||||
size_t valuelen, uint8_t flags _U_,
|
||||
void *user_data _U_) {
|
||||
http2_stream_data *stream_data;
|
||||
const char PATH[] = ":path";
|
||||
switch(frame->hd.type) {
|
||||
switch (frame->hd.type) {
|
||||
case NGHTTP2_HEADERS:
|
||||
if(frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
||||
if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
||||
break;
|
||||
}
|
||||
stream_data = nghttp2_session_get_stream_user_data(session,
|
||||
frame->hd.stream_id);
|
||||
if(!stream_data || stream_data->request_path) {
|
||||
stream_data =
|
||||
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
|
||||
if (!stream_data || stream_data->request_path) {
|
||||
break;
|
||||
}
|
||||
if(namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) {
|
||||
if (namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) {
|
||||
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);
|
||||
}
|
||||
break;
|
||||
@@ -479,29 +449,28 @@ is emitted via ``on_header_callback`` function, which is called after
|
||||
return 0;
|
||||
}
|
||||
|
||||
We search ``:path`` header field in request headers and keep the
|
||||
requested path in ``http2_stream_data`` object. In this example
|
||||
program, we ignore ``:method`` header field and always treat the
|
||||
request as GET request.
|
||||
We search for the ``:path`` header field among the request headers and store
|
||||
the requested path in the ``http2_stream_data`` object. In this example
|
||||
program, we ignore ``:method`` header field and always treat the request as a
|
||||
GET request.
|
||||
|
||||
The ``on_frame_recv_callback()`` function is invoked when a frame is
|
||||
fully received::
|
||||
|
||||
static int on_frame_recv_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame, void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
const nghttp2_frame *frame, void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
http2_stream_data *stream_data;
|
||||
switch(frame->hd.type) {
|
||||
switch (frame->hd.type) {
|
||||
case NGHTTP2_DATA:
|
||||
case NGHTTP2_HEADERS:
|
||||
/* Check that the client request has finished */
|
||||
if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||
stream_data = nghttp2_session_get_stream_user_data(session,
|
||||
frame->hd.stream_id);
|
||||
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||
stream_data =
|
||||
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
|
||||
/* For DATA and HEADERS frame, this callback may be called after
|
||||
on_stream_close_callback. Check that stream still alive. */
|
||||
if(!stream_data) {
|
||||
if (!stream_data) {
|
||||
return 0;
|
||||
}
|
||||
return on_request_recv(session, session_data, stream_data);
|
||||
@@ -513,78 +482,75 @@ fully received::
|
||||
return 0;
|
||||
}
|
||||
|
||||
First we retrieve ``http2_stream_data`` object associated to the
|
||||
stream in ``on_begin_headers_callback()``. It is done using
|
||||
`nghttp2_session_get_stream_user_data()`. If the requested path cannot
|
||||
be served for some reasons (e.g., file is not found), we send 404
|
||||
response, which is done in ``error_reply()``. Otherwise, we open
|
||||
requested file and send its content. We send 1 header field
|
||||
``:status`` as a response header.
|
||||
First we retrieve the ``http2_stream_data`` object associated with the stream
|
||||
in ``on_begin_headers_callback()``. It is done using
|
||||
`nghttp2_session_get_stream_user_data()`. If the requested path cannot be
|
||||
served for some reason (e.g., file is not found), we send a 404 response,
|
||||
which is done in ``error_reply()``. Otherwise, we open the requested file and
|
||||
send its content. We send the header field ``:status`` as a single response
|
||||
header.
|
||||
|
||||
Sending content of a 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,
|
||||
nghttp2_nv *nva, size_t nvlen, int fd)
|
||||
{
|
||||
nghttp2_nv *nva, size_t nvlen, int fd) {
|
||||
int rv;
|
||||
nghttp2_data_provider data_prd;
|
||||
data_prd.source.fd = fd;
|
||||
data_prd.read_callback = file_read_callback;
|
||||
|
||||
rv = nghttp2_submit_response(session, stream_id, nva, nvlen, &data_prd);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
The nghttp2 library uses :type:`nghttp2_data_provider` structure to
|
||||
The nghttp2 library uses the :type:`nghttp2_data_provider` structure to
|
||||
send entity body to the remote peer. The ``source`` member of this
|
||||
structure is a union and it can be either void pointer or int which is
|
||||
intended to be used as file descriptor. In this example server, we use
|
||||
file descriptor. We also set ``file_read_callback()`` callback
|
||||
function to read content of the file::
|
||||
the file descriptor. We also set the ``file_read_callback()`` callback
|
||||
function to read the contents of the file::
|
||||
|
||||
static ssize_t file_read_callback
|
||||
(nghttp2_session *session, int32_t stream_id,
|
||||
uint8_t *buf, size_t length, uint32_t *data_flags,
|
||||
nghttp2_data_source *source, void *user_data)
|
||||
{
|
||||
static ssize_t file_read_callback(nghttp2_session *session _U_,
|
||||
int32_t stream_id _U_, uint8_t *buf,
|
||||
size_t length, uint32_t *data_flags,
|
||||
nghttp2_data_source *source,
|
||||
void *user_data _U_) {
|
||||
int fd = source->fd;
|
||||
ssize_t r;
|
||||
while((r = read(fd, buf, length)) == -1 && errno == EINTR);
|
||||
if(r == -1) {
|
||||
while ((r = read(fd, buf, length)) == -1 && errno == EINTR)
|
||||
;
|
||||
if (r == -1) {
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
if(r == 0) {
|
||||
if (r == 0) {
|
||||
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
If error happens while reading file, we return
|
||||
If an error happens while reading the file, we return
|
||||
:macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. This tells the
|
||||
library to send RST_STREAM to the stream. When all data are read, set
|
||||
:macro:`NGHTTP2_DATA_FLAG_EOF` flag to ``*data_flags`` to tell the
|
||||
nghttp2 library that we have finished reading file.
|
||||
library to send RST_STREAM to the stream. When all data has been read, set
|
||||
the :macro:`NGHTTP2_DATA_FLAG_EOF` flag to ``*data_flags`` to tell the
|
||||
nghttp2 library that we have finished reading the file.
|
||||
|
||||
The `nghttp2_submit_response()` is used to send response to the remote
|
||||
peer.
|
||||
The `nghttp2_submit_response()` function is used to send the response to the
|
||||
remote peer.
|
||||
|
||||
The ``on_stream_close_callback()`` function is invoked when the stream
|
||||
is about to close::
|
||||
|
||||
static int on_stream_close_callback(nghttp2_session *session,
|
||||
int32_t stream_id,
|
||||
nghttp2_error_code error_code,
|
||||
void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||
uint32_t error_code _U_, void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
http2_stream_data *stream_data;
|
||||
|
||||
stream_data = nghttp2_session_get_stream_user_data(session, stream_id);
|
||||
if(!stream_data) {
|
||||
if (!stream_data) {
|
||||
return 0;
|
||||
}
|
||||
remove_stream(session_data, stream_data);
|
||||
@@ -592,5 +558,5 @@ is about to close::
|
||||
return 0;
|
||||
}
|
||||
|
||||
We destroy ``http2_stream_data`` object in this function since the
|
||||
stream is about to close and we no longer use that object.
|
||||
We destroy the ``http2_stream_data`` object in this function since the stream
|
||||
is about to close and we no longer use that object.
|
||||
|
||||
4
examples/.gitignore
vendored
4
examples/.gitignore
vendored
@@ -2,3 +2,7 @@ client
|
||||
libevent-client
|
||||
libevent-server
|
||||
deflate
|
||||
asio-sv
|
||||
tiny-nghttpd
|
||||
asio-sv2
|
||||
asio-sv3
|
||||
|
||||
@@ -23,10 +23,12 @@
|
||||
|
||||
if ENABLE_EXAMPLES
|
||||
|
||||
AM_CFLAGS = $(WARNCFLAGS)
|
||||
AM_CPPFLAGS = \
|
||||
-Wall \
|
||||
-I$(top_srcdir)/lib/includes \
|
||||
-I$(top_builddir)/lib/includes \
|
||||
-I$(top_srcdir)/src/includes \
|
||||
-I$(top_srcdir)/third-party \
|
||||
@LIBEVENT_OPENSSL_CFLAGS@ \
|
||||
@OPENSSL_CFLAGS@ \
|
||||
@@ -48,4 +50,37 @@ libevent_server_SOURCES = libevent-server.c
|
||||
|
||||
deflate_SOURCES = deflate.c
|
||||
|
||||
if ENABLE_TINY_NGHTTPD
|
||||
|
||||
noinst_PROGRAMS += tiny-nghttpd
|
||||
|
||||
tiny_nghttpd_SOURCES = tiny-nghttpd.c
|
||||
|
||||
endif # ENABLE_TINY_NGHTTPD
|
||||
|
||||
if ENABLE_ASIO_LIB
|
||||
|
||||
noinst_PROGRAMS += asio-sv asio-sv2 asio-sv3
|
||||
|
||||
ASIOCPPFLAGS = ${BOOST_CPPFLAGS} ${AM_CPPFLAGS}
|
||||
ASIOLDFLAGS = @JEMALLOC_LIBS@
|
||||
ASIOLDADD = $(top_builddir)/src/libnghttp2_asio.la
|
||||
|
||||
asio_sv_SOURCES = asio-sv.cc
|
||||
asio_sv_CPPFLAGS = ${ASIOCPPFLAGS}
|
||||
asio_sv_LDFLAGS = ${ASIOLDFLAGS}
|
||||
asio_sv_LDADD = ${ASIOLDADD}
|
||||
|
||||
asio_sv2_SOURCES = asio-sv2.cc
|
||||
asio_sv2_CPPFLAGS = ${ASIOCPPFLAGS}
|
||||
asio_sv2_LDFLAGS = ${ASIOLDFLAGS}
|
||||
asio_sv2_LDADD = ${ASIOLDADD}
|
||||
|
||||
asio_sv3_SOURCES = asio-sv3.cc
|
||||
asio_sv3_CPPFLAGS = ${ASIOCPPFLAGS}
|
||||
asio_sv3_LDFLAGS = ${ASIOLDFLAGS}
|
||||
asio_sv3_LDADD = ${ASIOLDADD}
|
||||
|
||||
endif # ENABLE_ASIO_LIB
|
||||
|
||||
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
|
||||
* intentionally made simple.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /* !HAVE_CONFIG_H */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
@@ -44,20 +48,21 @@
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/conf.h>
|
||||
|
||||
enum {
|
||||
IO_NONE,
|
||||
WANT_READ,
|
||||
WANT_WRITE
|
||||
};
|
||||
enum { IO_NONE, WANT_READ, WANT_WRITE };
|
||||
|
||||
#define MAKE_NV(NAME, VALUE) \
|
||||
{(uint8_t*)NAME, (uint8_t*)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \
|
||||
NGHTTP2_NV_FLAG_NONE}
|
||||
#define MAKE_NV(NAME, VALUE) \
|
||||
{ \
|
||||
(uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \
|
||||
NGHTTP2_NV_FLAG_NONE \
|
||||
}
|
||||
|
||||
#define MAKE_NV_CS(NAME, VALUE) \
|
||||
{(uint8_t*)NAME, (uint8_t*)VALUE, sizeof(NAME) - 1, strlen(VALUE), \
|
||||
NGHTTP2_NV_FLAG_NONE}
|
||||
#define MAKE_NV_CS(NAME, VALUE) \
|
||||
{ \
|
||||
(uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, strlen(VALUE), \
|
||||
NGHTTP2_NV_FLAG_NONE \
|
||||
}
|
||||
|
||||
struct Connection {
|
||||
SSL *ssl;
|
||||
@@ -98,10 +103,9 @@ struct URI {
|
||||
* Returns copy of string |s| with the length |len|. The returned
|
||||
* string is NULL-terminated.
|
||||
*/
|
||||
static char* strcopy(const char *s, size_t len)
|
||||
{
|
||||
static char *strcopy(const char *s, size_t len) {
|
||||
char *dst;
|
||||
dst = malloc(len+1);
|
||||
dst = malloc(len + 1);
|
||||
memcpy(dst, s, len);
|
||||
dst[len] = '\0';
|
||||
return dst;
|
||||
@@ -110,8 +114,7 @@ static char* strcopy(const char *s, size_t len)
|
||||
/*
|
||||
* Prints error message |msg| and exit.
|
||||
*/
|
||||
static void die(const char *msg)
|
||||
{
|
||||
static void die(const char *msg) {
|
||||
fprintf(stderr, "FATAL: %s\n", msg);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
@@ -120,8 +123,7 @@ static void die(const char *msg)
|
||||
* Prints error containing the function name |func| and message |msg|
|
||||
* 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);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
@@ -130,8 +132,7 @@ static void dief(const char *func, const char *msg)
|
||||
* Prints error containing the function name |func| and error code
|
||||
* |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,
|
||||
nghttp2_strerror(error_code));
|
||||
exit(EXIT_FAILURE);
|
||||
@@ -143,21 +144,19 @@ static void diec(const char *func, int error_code)
|
||||
* bytes actually written. See the documentation of
|
||||
* nghttp2_send_callback for the details.
|
||||
*/
|
||||
static ssize_t send_callback(nghttp2_session *session,
|
||||
const uint8_t *data, size_t length, int flags,
|
||||
void *user_data)
|
||||
{
|
||||
static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
|
||||
size_t length, int flags _U_, void *user_data) {
|
||||
struct Connection *connection;
|
||||
int rv;
|
||||
connection = (struct Connection*)user_data;
|
||||
connection = (struct Connection *)user_data;
|
||||
connection->want_io = IO_NONE;
|
||||
ERR_clear_error();
|
||||
rv = SSL_write(connection->ssl, data, (int)length);
|
||||
if(rv < 0) {
|
||||
if (rv <= 0) {
|
||||
int err = SSL_get_error(connection->ssl, rv);
|
||||
if(err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
|
||||
connection->want_io = (err == SSL_ERROR_WANT_READ ?
|
||||
WANT_READ : WANT_WRITE);
|
||||
if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
|
||||
connection->want_io =
|
||||
(err == SSL_ERROR_WANT_READ ? WANT_READ : WANT_WRITE);
|
||||
rv = NGHTTP2_ERR_WOULDBLOCK;
|
||||
} else {
|
||||
rv = NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
@@ -172,41 +171,39 @@ static ssize_t send_callback(nghttp2_session *session,
|
||||
* |length| bytes. Returns the number of bytes stored in |buf|. See
|
||||
* the documentation of nghttp2_recv_callback for the details.
|
||||
*/
|
||||
static ssize_t recv_callback(nghttp2_session *session,
|
||||
uint8_t *buf, size_t length, int flags,
|
||||
void *user_data)
|
||||
{
|
||||
static ssize_t recv_callback(nghttp2_session *session _U_, uint8_t *buf,
|
||||
size_t length, int flags _U_, void *user_data) {
|
||||
struct Connection *connection;
|
||||
int rv;
|
||||
connection = (struct Connection*)user_data;
|
||||
connection = (struct Connection *)user_data;
|
||||
connection->want_io = IO_NONE;
|
||||
ERR_clear_error();
|
||||
rv = SSL_read(connection->ssl, buf, (int)length);
|
||||
if(rv < 0) {
|
||||
if (rv < 0) {
|
||||
int err = SSL_get_error(connection->ssl, rv);
|
||||
if(err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
|
||||
connection->want_io = (err == SSL_ERROR_WANT_READ ?
|
||||
WANT_READ : WANT_WRITE);
|
||||
if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
|
||||
connection->want_io =
|
||||
(err == SSL_ERROR_WANT_READ ? WANT_READ : WANT_WRITE);
|
||||
rv = NGHTTP2_ERR_WOULDBLOCK;
|
||||
} else {
|
||||
rv = NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
} else if(rv == 0) {
|
||||
} else if (rv == 0) {
|
||||
rv = NGHTTP2_ERR_EOF;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
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;
|
||||
switch(frame->hd.type) {
|
||||
switch (frame->hd.type) {
|
||||
case NGHTTP2_HEADERS:
|
||||
if(nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)) {
|
||||
if (nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)) {
|
||||
const nghttp2_nv *nva = frame->headers.nva;
|
||||
printf("[INFO] C ----------------------------> S (HEADERS)\n");
|
||||
for(i = 0; i < frame->headers.nvlen; ++i) {
|
||||
for (i = 0; i < frame->headers.nvlen; ++i) {
|
||||
fwrite(nva[i].name, nva[i].namelen, 1, stdout);
|
||||
printf(": ");
|
||||
fwrite(nva[i].value, nva[i].valuelen, 1, stdout);
|
||||
@@ -225,18 +222,18 @@ static int on_frame_send_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;
|
||||
switch(frame->hd.type) {
|
||||
switch (frame->hd.type) {
|
||||
case NGHTTP2_HEADERS:
|
||||
if(frame->headers.cat == NGHTTP2_HCAT_RESPONSE) {
|
||||
if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE) {
|
||||
const nghttp2_nv *nva = frame->headers.nva;
|
||||
struct Request *req;
|
||||
req = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
|
||||
if(req) {
|
||||
if (req) {
|
||||
printf("[INFO] C <---------------------------- S (HEADERS)\n");
|
||||
for(i = 0; i < frame->headers.nvlen; ++i) {
|
||||
for (i = 0; i < frame->headers.nvlen; ++i) {
|
||||
fwrite(nva[i].name, nva[i].namelen, 1, stdout);
|
||||
printf(": ");
|
||||
fwrite(nva[i].value, nva[i].valuelen, 1, stdout);
|
||||
@@ -261,18 +258,16 @@ static int on_frame_recv_callback(nghttp2_session *session,
|
||||
* fetch 1 resource in this program, after reception of the response,
|
||||
* we submit GOAWAY and close the session.
|
||||
*/
|
||||
static int on_stream_close_callback(nghttp2_session *session,
|
||||
int32_t stream_id,
|
||||
nghttp2_error_code error_code,
|
||||
void *user_data)
|
||||
{
|
||||
static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||
uint32_t error_code _U_,
|
||||
void *user_data _U_) {
|
||||
struct Request *req;
|
||||
req = nghttp2_session_get_stream_user_data(session, stream_id);
|
||||
if(req) {
|
||||
if (req) {
|
||||
int rv;
|
||||
rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
|
||||
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
diec("nghttp2_session_terminate_session", rv);
|
||||
}
|
||||
}
|
||||
@@ -285,16 +280,16 @@ static int on_stream_close_callback(nghttp2_session *session,
|
||||
* The implementation of nghttp2_on_data_chunk_recv_callback type. We
|
||||
* use this function to print the received response body.
|
||||
*/
|
||||
static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
||||
int32_t stream_id,
|
||||
static int on_data_chunk_recv_callback(nghttp2_session *session,
|
||||
uint8_t flags _U_, int32_t stream_id,
|
||||
const uint8_t *data, size_t len,
|
||||
void *user_data)
|
||||
{
|
||||
void *user_data _U_) {
|
||||
struct Request *req;
|
||||
req = nghttp2_session_get_stream_user_data(session, stream_id);
|
||||
if(req) {
|
||||
if (req) {
|
||||
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);
|
||||
printf("\n");
|
||||
}
|
||||
@@ -307,15 +302,22 @@ static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
||||
* always required. Since we use nghttp2_session_recv(), the
|
||||
* recv_callback is also required.
|
||||
*/
|
||||
static void setup_nghttp2_callbacks(nghttp2_session_callbacks *callbacks)
|
||||
{
|
||||
memset(callbacks, 0, sizeof(nghttp2_session_callbacks));
|
||||
callbacks->send_callback = send_callback;
|
||||
callbacks->recv_callback = recv_callback;
|
||||
callbacks->on_frame_send_callback = on_frame_send_callback;
|
||||
callbacks->on_frame_recv_callback = on_frame_recv_callback;
|
||||
callbacks->on_stream_close_callback = on_stream_close_callback;
|
||||
callbacks->on_data_chunk_recv_callback = on_data_chunk_recv_callback;
|
||||
static void setup_nghttp2_callbacks(nghttp2_session_callbacks *callbacks) {
|
||||
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_recv_callback(callbacks, recv_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_frame_send_callback(callbacks,
|
||||
on_frame_send_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
|
||||
on_frame_recv_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_stream_close_callback(
|
||||
callbacks, on_stream_close_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
|
||||
callbacks, on_data_chunk_recv_callback);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -323,16 +325,14 @@ static void setup_nghttp2_callbacks(nghttp2_session_callbacks *callbacks)
|
||||
* HTTP/2 protocol, if server does not offer HTTP/2 the nghttp2
|
||||
* library supports, we terminate program.
|
||||
*/
|
||||
static int select_next_proto_cb(SSL* ssl,
|
||||
unsigned char **out, unsigned char *outlen,
|
||||
const unsigned char *in, unsigned int inlen,
|
||||
void *arg)
|
||||
{
|
||||
static int select_next_proto_cb(SSL *ssl _U_, unsigned char **out,
|
||||
unsigned char *outlen, const unsigned char *in,
|
||||
unsigned int inlen, void *arg _U_) {
|
||||
int rv;
|
||||
/* nghttp2_select_next_protocol() selects HTTP/2 protocol the
|
||||
nghttp2 library supports. */
|
||||
rv = nghttp2_select_next_protocol(out, outlen, in, inlen);
|
||||
if(rv <= 0) {
|
||||
if (rv <= 0) {
|
||||
die("Server did not advertise HTTP/2 protocol");
|
||||
}
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
@@ -341,25 +341,23 @@ static int select_next_proto_cb(SSL* ssl,
|
||||
/*
|
||||
* 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 */
|
||||
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_RELEASE_BUFFERS);
|
||||
/* Set NPN callback */
|
||||
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;
|
||||
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));
|
||||
}
|
||||
ERR_clear_error();
|
||||
rv = SSL_connect(ssl);
|
||||
if(rv <= 0) {
|
||||
if (rv <= 0) {
|
||||
dief("SSL_connect", ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
}
|
||||
@@ -368,8 +366,7 @@ static void ssl_handshake(SSL *ssl, int fd)
|
||||
* Connects to the host |host| and port |port|. This function returns
|
||||
* 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;
|
||||
int fd = -1;
|
||||
int rv;
|
||||
@@ -380,17 +377,18 @@ static int connect_to(const char *host, uint16_t port)
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
rv = getaddrinfo(host, service, &hints, &res);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
dief("getaddrinfo", gai_strerror(rv));
|
||||
}
|
||||
for(rp = res; rp; rp = rp->ai_next) {
|
||||
for (rp = res; rp; rp = rp->ai_next) {
|
||||
fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
||||
if(fd == -1) {
|
||||
if (fd == -1) {
|
||||
continue;
|
||||
}
|
||||
while((rv = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 &&
|
||||
errno == EINTR);
|
||||
if(rv == 0) {
|
||||
while ((rv = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 &&
|
||||
errno == EINTR)
|
||||
;
|
||||
if (rv == 0) {
|
||||
break;
|
||||
}
|
||||
close(fd);
|
||||
@@ -400,25 +398,25 @@ static int connect_to(const char *host, uint16_t port)
|
||||
return fd;
|
||||
}
|
||||
|
||||
static void make_non_block(int fd)
|
||||
{
|
||||
static void make_non_block(int fd) {
|
||||
int flags, rv;
|
||||
while((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR);
|
||||
if(flags == -1) {
|
||||
while ((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR)
|
||||
;
|
||||
if (flags == -1) {
|
||||
dief("fcntl", strerror(errno));
|
||||
}
|
||||
while((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR);
|
||||
if(rv == -1) {
|
||||
while ((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR)
|
||||
;
|
||||
if (rv == -1) {
|
||||
dief("fcntl", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static void set_tcp_nodelay(int fd)
|
||||
{
|
||||
static void set_tcp_nodelay(int fd) {
|
||||
int val = 1;
|
||||
int rv;
|
||||
rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val));
|
||||
if(rv == -1) {
|
||||
if (rv == -1) {
|
||||
dief("setsockopt", strerror(errno));
|
||||
}
|
||||
}
|
||||
@@ -426,15 +424,14 @@ static void set_tcp_nodelay(int fd)
|
||||
/*
|
||||
* 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;
|
||||
if(nghttp2_session_want_read(connection->session) ||
|
||||
connection->want_io == WANT_READ) {
|
||||
if (nghttp2_session_want_read(connection->session) ||
|
||||
connection->want_io == WANT_READ) {
|
||||
pollfd->events |= POLLIN;
|
||||
}
|
||||
if(nghttp2_session_want_write(connection->session) ||
|
||||
connection->want_io == WANT_WRITE) {
|
||||
if (nghttp2_session_want_write(connection->session) ||
|
||||
connection->want_io == WANT_WRITE) {
|
||||
pollfd->events |= POLLOUT;
|
||||
}
|
||||
}
|
||||
@@ -444,24 +441,20 @@ static void ctl_poll(struct pollfd *pollfd, struct Connection *connection)
|
||||
* function does not send packets; just append the request to the
|
||||
* 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;
|
||||
const nghttp2_nv nva[] = {
|
||||
/* Make sure that the last item is NULL */
|
||||
MAKE_NV(":method", "GET"),
|
||||
MAKE_NV_CS(":path", req->path),
|
||||
MAKE_NV(":scheme", "https"),
|
||||
MAKE_NV_CS(":authority", req->hostport),
|
||||
MAKE_NV("accept", "*/*"),
|
||||
MAKE_NV("user-agent", "nghttp2/"NGHTTP2_VERSION)
|
||||
};
|
||||
/* Make sure that the last item is NULL */
|
||||
const nghttp2_nv nva[] = {MAKE_NV(":method", "GET"),
|
||||
MAKE_NV_CS(":path", req->path),
|
||||
MAKE_NV(":scheme", "https"),
|
||||
MAKE_NV_CS(":authority", req->hostport),
|
||||
MAKE_NV("accept", "*/*"),
|
||||
MAKE_NV("user-agent", "nghttp2/" NGHTTP2_VERSION)};
|
||||
|
||||
stream_id = nghttp2_submit_request(connection->session, NULL,
|
||||
nva, sizeof(nva)/sizeof(nva[0]),
|
||||
NULL, req);
|
||||
stream_id = nghttp2_submit_request(connection->session, NULL, nva,
|
||||
sizeof(nva) / sizeof(nva[0]), NULL, req);
|
||||
|
||||
if(stream_id < 0) {
|
||||
if (stream_id < 0) {
|
||||
diec("nghttp2_submit_request", stream_id);
|
||||
}
|
||||
|
||||
@@ -472,21 +465,19 @@ static void submit_request(struct Connection *connection, struct Request *req)
|
||||
/*
|
||||
* Performs the network I/O.
|
||||
*/
|
||||
static void exec_io(struct Connection *connection)
|
||||
{
|
||||
static void exec_io(struct Connection *connection) {
|
||||
int rv;
|
||||
rv = nghttp2_session_recv(connection->session);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
diec("nghttp2_session_recv", rv);
|
||||
}
|
||||
rv = nghttp2_session_send(connection->session);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
diec("nghttp2_session_send", rv);
|
||||
}
|
||||
}
|
||||
|
||||
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->port = uri->port;
|
||||
req->path = strcopy(uri->path, uri->pathlen);
|
||||
@@ -494,8 +485,7 @@ static void request_init(struct Request *req, const struct URI *uri)
|
||||
req->stream_id = -1;
|
||||
}
|
||||
|
||||
static void request_free(struct Request *req)
|
||||
{
|
||||
static void request_free(struct Request *req) {
|
||||
free(req->host);
|
||||
free(req->path);
|
||||
free(req->hostport);
|
||||
@@ -504,9 +494,8 @@ static void request_free(struct Request *req)
|
||||
/*
|
||||
* Fetches the resource denoted by |uri|.
|
||||
*/
|
||||
static void fetch_uri(const struct URI *uri)
|
||||
{
|
||||
nghttp2_session_callbacks callbacks;
|
||||
static void fetch_uri(const struct URI *uri) {
|
||||
nghttp2_session_callbacks *callbacks;
|
||||
int fd;
|
||||
SSL_CTX *ssl_ctx;
|
||||
SSL *ssl;
|
||||
@@ -518,20 +507,18 @@ static void fetch_uri(const struct URI *uri)
|
||||
|
||||
request_init(&req, uri);
|
||||
|
||||
setup_nghttp2_callbacks(&callbacks);
|
||||
|
||||
/* Establish connection and setup SSL */
|
||||
fd = connect_to(req.host, req.port);
|
||||
if(fd == -1) {
|
||||
if (fd == -1) {
|
||||
die("Could not open file descriptor");
|
||||
}
|
||||
ssl_ctx = SSL_CTX_new(SSLv23_client_method());
|
||||
if(ssl_ctx == NULL) {
|
||||
if (ssl_ctx == NULL) {
|
||||
dief("SSL_CTX_new", ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
init_ssl_ctx(ssl_ctx);
|
||||
ssl = SSL_new(ssl_ctx);
|
||||
if(ssl == NULL) {
|
||||
if (ssl == NULL) {
|
||||
dief("SSL_new", ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
/* To simplify the program, we perform SSL/TLS handshake in blocking
|
||||
@@ -542,17 +529,32 @@ static void fetch_uri(const struct URI *uri)
|
||||
connection.want_io = IO_NONE;
|
||||
|
||||
/* Send connection header in blocking I/O mode */
|
||||
SSL_write(ssl, NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
|
||||
rv = SSL_write(ssl, NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
||||
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 */
|
||||
make_non_block(fd);
|
||||
set_tcp_nodelay(fd);
|
||||
|
||||
printf("[INFO] SSL/TLS handshake completed\n");
|
||||
rv = nghttp2_session_client_new(&connection.session, &callbacks,
|
||||
&connection);
|
||||
if(rv != 0) {
|
||||
|
||||
rv = nghttp2_session_callbacks_new(&callbacks);
|
||||
|
||||
if (rv != 0) {
|
||||
diec("nghttp2_session_callbacks_new", rv);
|
||||
}
|
||||
|
||||
setup_nghttp2_callbacks(callbacks);
|
||||
|
||||
rv = nghttp2_session_client_new(&connection.session, callbacks, &connection);
|
||||
|
||||
nghttp2_session_callbacks_del(callbacks);
|
||||
|
||||
if (rv != 0) {
|
||||
diec("nghttp2_session_client_new", rv);
|
||||
}
|
||||
|
||||
@@ -563,16 +565,16 @@ static void fetch_uri(const struct URI *uri)
|
||||
ctl_poll(pollfds, &connection);
|
||||
|
||||
/* Event loop */
|
||||
while(nghttp2_session_want_read(connection.session) ||
|
||||
nghttp2_session_want_write(connection.session)) {
|
||||
while (nghttp2_session_want_read(connection.session) ||
|
||||
nghttp2_session_want_write(connection.session)) {
|
||||
int nfds = poll(pollfds, npollfds, -1);
|
||||
if(nfds == -1) {
|
||||
if (nfds == -1) {
|
||||
dief("poll", strerror(errno));
|
||||
}
|
||||
if(pollfds[0].revents & (POLLIN | POLLOUT)) {
|
||||
if (pollfds[0].revents & (POLLIN | POLLOUT)) {
|
||||
exec_io(&connection);
|
||||
}
|
||||
if((pollfds[0].revents & POLLHUP) || (pollfds[0].revents & POLLERR)) {
|
||||
if ((pollfds[0].revents & POLLHUP) || (pollfds[0].revents & POLLERR)) {
|
||||
die("Connection error");
|
||||
}
|
||||
ctl_poll(pollfds, &connection);
|
||||
@@ -588,96 +590,94 @@ static void fetch_uri(const struct URI *uri)
|
||||
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 */
|
||||
size_t len, i, offset;
|
||||
int ipv6addr = 0;
|
||||
memset(res, 0, sizeof(struct URI));
|
||||
len = strlen(uri);
|
||||
if(len < 9 || memcmp("https://", uri, 8) != 0) {
|
||||
if (len < 9 || memcmp("https://", uri, 8) != 0) {
|
||||
return -1;
|
||||
}
|
||||
offset = 8;
|
||||
res->host = res->hostport = &uri[offset];
|
||||
res->hostlen = 0;
|
||||
if(uri[offset] == '[') {
|
||||
if (uri[offset] == '[') {
|
||||
/* IPv6 literal address */
|
||||
++offset;
|
||||
++res->host;
|
||||
ipv6addr = 1;
|
||||
for(i = offset; i < len; ++i) {
|
||||
if(uri[i] == ']') {
|
||||
res->hostlen = i-offset;
|
||||
offset = i+1;
|
||||
for (i = offset; i < len; ++i) {
|
||||
if (uri[i] == ']') {
|
||||
res->hostlen = i - offset;
|
||||
offset = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const char delims[] = ":/?#";
|
||||
for(i = offset; i < len; ++i) {
|
||||
if(strchr(delims, uri[i]) != NULL) {
|
||||
for (i = offset; i < len; ++i) {
|
||||
if (strchr(delims, uri[i]) != NULL) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
res->hostlen = i-offset;
|
||||
res->hostlen = i - offset;
|
||||
offset = i;
|
||||
}
|
||||
if(res->hostlen == 0) {
|
||||
if (res->hostlen == 0) {
|
||||
return -1;
|
||||
}
|
||||
/* Assuming https */
|
||||
res->port = 443;
|
||||
if(offset < len) {
|
||||
if(uri[offset] == ':') {
|
||||
if (offset < len) {
|
||||
if (uri[offset] == ':') {
|
||||
/* port */
|
||||
const char delims[] = "/?#";
|
||||
int port = 0;
|
||||
++offset;
|
||||
for(i = offset; i < len; ++i) {
|
||||
if(strchr(delims, uri[i]) != NULL) {
|
||||
for (i = offset; i < len; ++i) {
|
||||
if (strchr(delims, uri[i]) != NULL) {
|
||||
break;
|
||||
}
|
||||
if('0' <= uri[i] && uri[i] <= '9') {
|
||||
if ('0' <= uri[i] && uri[i] <= '9') {
|
||||
port *= 10;
|
||||
port += uri[i]-'0';
|
||||
if(port > 65535) {
|
||||
port += uri[i] - '0';
|
||||
if (port > 65535) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if(port == 0) {
|
||||
if (port == 0) {
|
||||
return -1;
|
||||
}
|
||||
offset = i;
|
||||
res->port = port;
|
||||
}
|
||||
}
|
||||
res->hostportlen = uri+offset+ipv6addr-res->host;
|
||||
for(i = offset; i < len; ++i) {
|
||||
if(uri[i] == '#') {
|
||||
res->hostportlen = uri + offset + ipv6addr - res->host;
|
||||
for (i = offset; i < len; ++i) {
|
||||
if (uri[i] == '#') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(i-offset == 0) {
|
||||
if (i - offset == 0) {
|
||||
res->path = "/";
|
||||
res->pathlen = 1;
|
||||
} else {
|
||||
res->path = &uri[offset];
|
||||
res->pathlen = i-offset;
|
||||
res->pathlen = i - offset;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int main(int argc, char **argv) {
|
||||
struct URI uri;
|
||||
struct sigaction act;
|
||||
int rv;
|
||||
|
||||
if(argc < 2) {
|
||||
if (argc < 2) {
|
||||
die("Specify a https URI");
|
||||
}
|
||||
|
||||
@@ -685,11 +685,13 @@ int main(int argc, char **argv)
|
||||
act.sa_handler = SIG_IGN;
|
||||
sigaction(SIGPIPE, &act, 0);
|
||||
|
||||
OPENSSL_config(NULL);
|
||||
OpenSSL_add_all_algorithms();
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
|
||||
rv = parse_uri(&uri, argv[1]);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
die("parse_uri failed");
|
||||
}
|
||||
fetch_uri(&uri);
|
||||
|
||||
@@ -22,48 +22,48 @@
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* 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 <string.h>
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
#define MAKE_NV(K, V) \
|
||||
{ (uint8_t*)K, (uint8_t*)V, sizeof(K) - 1, sizeof(V) - 1, \
|
||||
NGHTTP2_NV_FLAG_NONE }
|
||||
#define MAKE_NV(K, V) \
|
||||
{ \
|
||||
(uint8_t *) K, (uint8_t *)V, sizeof(K) - 1, sizeof(V) - 1, \
|
||||
NGHTTP2_NV_FLAG_NONE \
|
||||
}
|
||||
|
||||
static void deflate(nghttp2_hd_deflater *deflater,
|
||||
nghttp2_hd_inflater *inflater,
|
||||
const nghttp2_nv * const nva, size_t nvlen);
|
||||
nghttp2_hd_inflater *inflater, const nghttp2_nv *const nva,
|
||||
size_t nvlen);
|
||||
|
||||
static int inflate_header_block(nghttp2_hd_inflater *inflater,
|
||||
uint8_t *in, size_t inlen, int final);
|
||||
static int inflate_header_block(nghttp2_hd_inflater *inflater, uint8_t *in,
|
||||
size_t inlen, int final);
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int main(int argc _U_, char **argv _U_) {
|
||||
int rv;
|
||||
nghttp2_hd_deflater *deflater;
|
||||
nghttp2_hd_inflater *inflater;
|
||||
/* Define 1st header set. This is looks like a HTTP request. */
|
||||
nghttp2_nv nva1[] = {
|
||||
MAKE_NV(":scheme", "https"),
|
||||
MAKE_NV(":authority", "example.org"),
|
||||
MAKE_NV(":path", "/"),
|
||||
MAKE_NV("user-agent", "libnghttp2"),
|
||||
MAKE_NV("accept-encoding", "gzip, deflate")
|
||||
};
|
||||
MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "example.org"),
|
||||
MAKE_NV(":path", "/"), MAKE_NV("user-agent", "libnghttp2"),
|
||||
MAKE_NV("accept-encoding", "gzip, deflate")};
|
||||
/* Define 2nd header set */
|
||||
nghttp2_nv nva2[] = {
|
||||
MAKE_NV(":scheme", "https"),
|
||||
MAKE_NV(":authority", "example.org"),
|
||||
MAKE_NV(":path", "/stylesheet/style.css"),
|
||||
MAKE_NV("user-agent", "libnghttp2"),
|
||||
MAKE_NV("accept-encoding", "gzip, deflate"),
|
||||
MAKE_NV("referer", "https://example.org")
|
||||
};
|
||||
nghttp2_nv nva2[] = {MAKE_NV(":scheme", "https"),
|
||||
MAKE_NV(":authority", "example.org"),
|
||||
MAKE_NV(":path", "/stylesheet/style.css"),
|
||||
MAKE_NV("user-agent", "libnghttp2"),
|
||||
MAKE_NV("accept-encoding", "gzip, deflate"),
|
||||
MAKE_NV("referer", "https://example.org")};
|
||||
|
||||
rv = nghttp2_hd_deflate_new(&deflater, 4096);
|
||||
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
fprintf(stderr, "nghttp2_hd_deflate_init failed with error: %s\n",
|
||||
nghttp2_strerror(rv));
|
||||
exit(EXIT_FAILURE);
|
||||
@@ -71,7 +71,7 @@ int main(int argc, char **argv)
|
||||
|
||||
rv = nghttp2_hd_inflate_new(&inflater);
|
||||
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
fprintf(stderr, "nghttp2_hd_inflate_init failed with error: %s\n",
|
||||
nghttp2_strerror(rv));
|
||||
exit(EXIT_FAILURE);
|
||||
@@ -91,9 +91,8 @@ int main(int argc, char **argv)
|
||||
}
|
||||
|
||||
static void deflate(nghttp2_hd_deflater *deflater,
|
||||
nghttp2_hd_inflater *inflater,
|
||||
const nghttp2_nv * const nva, size_t nvlen)
|
||||
{
|
||||
nghttp2_hd_inflater *inflater, const nghttp2_nv *const nva,
|
||||
size_t nvlen) {
|
||||
ssize_t rv;
|
||||
uint8_t *buf;
|
||||
size_t buflen;
|
||||
@@ -103,13 +102,13 @@ static void deflate(nghttp2_hd_deflater *deflater,
|
||||
|
||||
sum = 0;
|
||||
|
||||
for(i = 0; i < nvlen; ++i) {
|
||||
for (i = 0; i < nvlen; ++i) {
|
||||
sum += nva[i].namelen + nva[i].valuelen;
|
||||
}
|
||||
|
||||
printf("Input (%zu byte(s)):\n\n", sum);
|
||||
|
||||
for(i = 0; i < nvlen; ++i) {
|
||||
for (i = 0; i < nvlen; ++i) {
|
||||
fwrite(nva[i].name, nva[i].namelen, 1, stdout);
|
||||
printf(": ");
|
||||
fwrite(nva[i].value, nva[i].valuelen, 1, stdout);
|
||||
@@ -121,7 +120,7 @@ static void deflate(nghttp2_hd_deflater *deflater,
|
||||
|
||||
rv = nghttp2_hd_deflate_hd(deflater, buf, buflen, nva, nvlen);
|
||||
|
||||
if(rv < 0) {
|
||||
if (rv < 0) {
|
||||
fprintf(stderr, "nghttp2_hd_deflate_hd() failed with error: %s\n",
|
||||
nghttp2_strerror((int)rv));
|
||||
|
||||
@@ -132,17 +131,17 @@ static void deflate(nghttp2_hd_deflater *deflater,
|
||||
|
||||
outlen = rv;
|
||||
|
||||
printf("\nDeflate (%zu byte(s), ratio %.02f):\n\n",
|
||||
outlen, sum == 0 ? 0 : (double)outlen / sum);
|
||||
printf("\nDeflate (%zu byte(s), ratio %.02f):\n\n", outlen,
|
||||
sum == 0 ? 0 : (double)outlen / sum);
|
||||
|
||||
for(i = 0; i < outlen; ++i) {
|
||||
if((i & 0x0fu) == 0) {
|
||||
for (i = 0; i < outlen; ++i) {
|
||||
if ((i & 0x0fu) == 0) {
|
||||
printf("%08zX: ", i);
|
||||
}
|
||||
|
||||
printf("%02X ", buf[i]);
|
||||
|
||||
if(((i + 1) & 0x0fu) == 0) {
|
||||
if (((i + 1) & 0x0fu) == 0) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
@@ -153,7 +152,7 @@ static void deflate(nghttp2_hd_deflater *deflater,
|
||||
header data. */
|
||||
rv = inflate_header_block(inflater, buf, outlen, 1);
|
||||
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
free(buf);
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
@@ -165,20 +164,18 @@ static void deflate(nghttp2_hd_deflater *deflater,
|
||||
free(buf);
|
||||
}
|
||||
|
||||
int inflate_header_block(nghttp2_hd_inflater *inflater,
|
||||
uint8_t *in, size_t inlen, int final)
|
||||
{
|
||||
int inflate_header_block(nghttp2_hd_inflater *inflater, uint8_t *in,
|
||||
size_t inlen, int final) {
|
||||
ssize_t rv;
|
||||
|
||||
for(;;) {
|
||||
for (;;) {
|
||||
nghttp2_nv nv;
|
||||
int inflate_flags = 0;
|
||||
size_t proclen;
|
||||
|
||||
rv = nghttp2_hd_inflate_hd(inflater, &nv, &inflate_flags,
|
||||
in, inlen, final);
|
||||
rv = nghttp2_hd_inflate_hd(inflater, &nv, &inflate_flags, in, inlen, final);
|
||||
|
||||
if(rv < 0) {
|
||||
if (rv < 0) {
|
||||
fprintf(stderr, "inflate failed with error code %zd", rv);
|
||||
return -1;
|
||||
}
|
||||
@@ -188,20 +185,19 @@ int inflate_header_block(nghttp2_hd_inflater *inflater,
|
||||
in += proclen;
|
||||
inlen -= proclen;
|
||||
|
||||
if(inflate_flags & NGHTTP2_HD_INFLATE_EMIT) {
|
||||
if (inflate_flags & NGHTTP2_HD_INFLATE_EMIT) {
|
||||
fwrite(nv.name, nv.namelen, 1, stderr);
|
||||
fprintf(stderr, ": ");
|
||||
fwrite(nv.value, nv.valuelen, 1, stderr);
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
if(inflate_flags & NGHTTP2_HD_INFLATE_FINAL) {
|
||||
if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) {
|
||||
nghttp2_hd_inflate_end_headers(inflater);
|
||||
break;
|
||||
}
|
||||
|
||||
if((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 &&
|
||||
inlen == 0) {
|
||||
if ((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && inlen == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,10 @@
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* 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 <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
@@ -32,6 +36,7 @@
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/conf.h>
|
||||
|
||||
#include <event.h>
|
||||
#include <event2/event.h>
|
||||
@@ -42,7 +47,7 @@
|
||||
|
||||
#include "http-parser/http_parser.h"
|
||||
|
||||
#define ARRLEN(x) (sizeof(x)/sizeof(x[0]))
|
||||
#define ARRLEN(x) (sizeof(x) / sizeof(x[0]))
|
||||
|
||||
typedef struct {
|
||||
/* The NULL-terminated URI string to retreive. */
|
||||
@@ -69,9 +74,8 @@ typedef struct {
|
||||
http2_stream_data *stream_data;
|
||||
} http2_session_data;
|
||||
|
||||
static http2_stream_data* create_http2_stream_data(const char *uri,
|
||||
struct http_parser_url *u)
|
||||
{
|
||||
static http2_stream_data *create_http2_stream_data(const char *uri,
|
||||
struct http_parser_url *u) {
|
||||
/* MAX 5 digits (max 65535) + 1 ':' + 1 NULL (because of snprintf) */
|
||||
size_t extra = 7;
|
||||
http2_stream_data *stream_data = malloc(sizeof(http2_stream_data));
|
||||
@@ -82,29 +86,29 @@ static http2_stream_data* create_http2_stream_data(const char *uri,
|
||||
|
||||
stream_data->authoritylen = u->field_data[UF_HOST].len;
|
||||
stream_data->authority = malloc(stream_data->authoritylen + extra);
|
||||
memcpy(stream_data->authority,
|
||||
&uri[u->field_data[UF_HOST].off], u->field_data[UF_HOST].len);
|
||||
if(u->field_set & (1 << UF_PORT)) {
|
||||
memcpy(stream_data->authority, &uri[u->field_data[UF_HOST].off],
|
||||
u->field_data[UF_HOST].len);
|
||||
if (u->field_set & (1 << UF_PORT)) {
|
||||
stream_data->authoritylen +=
|
||||
snprintf(stream_data->authority + u->field_data[UF_HOST].len, extra,
|
||||
":%u", u->port);
|
||||
snprintf(stream_data->authority + u->field_data[UF_HOST].len, extra,
|
||||
":%u", u->port);
|
||||
}
|
||||
|
||||
stream_data->pathlen = 0;
|
||||
if(u->field_set & (1 << UF_PATH)) {
|
||||
if (u->field_set & (1 << UF_PATH)) {
|
||||
stream_data->pathlen = u->field_data[UF_PATH].len;
|
||||
}
|
||||
if(u->field_set & (1 << UF_QUERY)) {
|
||||
if (u->field_set & (1 << UF_QUERY)) {
|
||||
/* +1 for '?' character */
|
||||
stream_data->pathlen += u->field_data[UF_QUERY].len + 1;
|
||||
}
|
||||
if(stream_data->pathlen > 0) {
|
||||
if (stream_data->pathlen > 0) {
|
||||
stream_data->path = malloc(stream_data->pathlen);
|
||||
if(u->field_set & (1 << UF_PATH)) {
|
||||
memcpy(stream_data->path,
|
||||
&uri[u->field_data[UF_PATH].off], u->field_data[UF_PATH].len);
|
||||
if (u->field_set & (1 << UF_PATH)) {
|
||||
memcpy(stream_data->path, &uri[u->field_data[UF_PATH].off],
|
||||
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,
|
||||
&uri[u->field_data[UF_QUERY].off], u->field_data[UF_QUERY].len);
|
||||
}
|
||||
@@ -114,16 +118,15 @@ static http2_stream_data* create_http2_stream_data(const char *uri,
|
||||
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->authority);
|
||||
free(stream_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));
|
||||
|
||||
memset(session_data, 0, sizeof(http2_session_data));
|
||||
@@ -131,11 +134,10 @@ static http2_session_data *create_http2_session_data(struct event_base *evbase)
|
||||
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);
|
||||
|
||||
if(ssl) {
|
||||
if (ssl) {
|
||||
SSL_shutdown(ssl);
|
||||
}
|
||||
bufferevent_free(session_data->bev);
|
||||
@@ -144,17 +146,15 @@ static void delete_http2_session_data(http2_session_data *session_data)
|
||||
session_data->dnsbase = NULL;
|
||||
nghttp2_session_del(session_data->session);
|
||||
session_data->session = NULL;
|
||||
if(session_data->stream_data) {
|
||||
if (session_data->stream_data) {
|
||||
delete_http2_stream_data(session_data->stream_data);
|
||||
session_data->stream_data = NULL;
|
||||
}
|
||||
free(session_data);
|
||||
}
|
||||
|
||||
static void print_header(FILE *f,
|
||||
const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen)
|
||||
{
|
||||
static void print_header(FILE *f, const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen) {
|
||||
fwrite(name, namelen, 1, f);
|
||||
fprintf(f, ": ");
|
||||
fwrite(value, valuelen, 1, f);
|
||||
@@ -164,13 +164,10 @@ static void print_header(FILE *f,
|
||||
/* Print HTTP headers to |f|. Please note that this function does not
|
||||
take into account that header name and value are sequence of
|
||||
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;
|
||||
for(i = 0; i < nvlen; ++i) {
|
||||
print_header(f,
|
||||
nva[i].name, nva[i].namelen,
|
||||
nva[i].value, nva[i].valuelen);
|
||||
for (i = 0; i < nvlen; ++i) {
|
||||
print_header(f, nva[i].name, nva[i].namelen, nva[i].value, nva[i].valuelen);
|
||||
}
|
||||
fprintf(f, "\n");
|
||||
}
|
||||
@@ -178,11 +175,9 @@ static void print_headers(FILE *f, nghttp2_nv *nva, size_t nvlen)
|
||||
/* nghttp2_send_callback. Here we transmit the |data|, |length| bytes,
|
||||
to the network. Because we are using libevent bufferevent, we just
|
||||
write those bytes into bufferevent buffer. */
|
||||
static ssize_t send_callback(nghttp2_session *session,
|
||||
const uint8_t *data, size_t length,
|
||||
int flags, void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
|
||||
size_t length, int flags _U_, void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
struct bufferevent *bev = session_data->bev;
|
||||
bufferevent_write(bev, data, length);
|
||||
return length;
|
||||
@@ -190,18 +185,16 @@ static ssize_t send_callback(nghttp2_session *session,
|
||||
|
||||
/* nghttp2_on_header_callback: Called when nghttp2 library emits
|
||||
single header name/value pair. */
|
||||
static int on_header_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame,
|
||||
const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen,
|
||||
uint8_t flags,
|
||||
void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
switch(frame->hd.type) {
|
||||
static int on_header_callback(nghttp2_session *session _U_,
|
||||
const nghttp2_frame *frame, const uint8_t *name,
|
||||
size_t namelen, const uint8_t *value,
|
||||
size_t valuelen, uint8_t flags _U_,
|
||||
void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
switch (frame->hd.type) {
|
||||
case NGHTTP2_HEADERS:
|
||||
if(frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
|
||||
session_data->stream_data->stream_id == frame->hd.stream_id) {
|
||||
if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
|
||||
session_data->stream_data->stream_id == frame->hd.stream_id) {
|
||||
/* Print response headers for the initiated request. */
|
||||
print_header(stderr, name, namelen, value, valuelen);
|
||||
break;
|
||||
@@ -212,15 +205,14 @@ static int on_header_callback(nghttp2_session *session,
|
||||
|
||||
/* nghttp2_on_begin_headers_callback: Called when nghttp2 library gets
|
||||
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,
|
||||
void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
switch(frame->hd.type) {
|
||||
void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
switch (frame->hd.type) {
|
||||
case NGHTTP2_HEADERS:
|
||||
if(frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
|
||||
session_data->stream_data->stream_id == frame->hd.stream_id) {
|
||||
if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
|
||||
session_data->stream_data->stream_id == frame->hd.stream_id) {
|
||||
fprintf(stderr, "Response headers for stream ID=%d:\n",
|
||||
frame->hd.stream_id);
|
||||
}
|
||||
@@ -231,14 +223,13 @@ static int on_begin_headers_callback(nghttp2_session *session,
|
||||
|
||||
/* nghttp2_on_frame_recv_callback: Called when nghttp2 library
|
||||
received a complete frame from the remote peer. */
|
||||
static int on_frame_recv_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame, void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
switch(frame->hd.type) {
|
||||
static int on_frame_recv_callback(nghttp2_session *session _U_,
|
||||
const nghttp2_frame *frame, void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
switch (frame->hd.type) {
|
||||
case NGHTTP2_HEADERS:
|
||||
if(frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
|
||||
session_data->stream_data->stream_id == frame->hd.stream_id) {
|
||||
if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
|
||||
session_data->stream_data->stream_id == frame->hd.stream_id) {
|
||||
fprintf(stderr, "All headers received\n");
|
||||
}
|
||||
break;
|
||||
@@ -251,13 +242,12 @@ static int on_frame_recv_callback(nghttp2_session *session,
|
||||
is meant to the stream we initiated, print the received data in
|
||||
stdout, so that the user can redirect its output to the file
|
||||
easily. */
|
||||
static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
||||
int32_t stream_id,
|
||||
static int on_data_chunk_recv_callback(nghttp2_session *session _U_,
|
||||
uint8_t flags _U_, int32_t stream_id,
|
||||
const uint8_t *data, size_t len,
|
||||
void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
if(session_data->stream_data->stream_id == stream_id) {
|
||||
void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
if (session_data->stream_data->stream_id == stream_id) {
|
||||
fwrite(data, len, 1, stdout);
|
||||
}
|
||||
return 0;
|
||||
@@ -267,19 +257,17 @@ static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
||||
closed. This example program only deals with 1 HTTP request (1
|
||||
stream), if it is closed, we send GOAWAY and tear down the
|
||||
session */
|
||||
static int on_stream_close_callback(nghttp2_session *session,
|
||||
int32_t stream_id,
|
||||
nghttp2_error_code error_code,
|
||||
void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||
uint32_t error_code,
|
||||
void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
int rv;
|
||||
|
||||
if(session_data->stream_data->stream_id == stream_id) {
|
||||
fprintf(stderr, "Stream %d closed with error_code=%d\n",
|
||||
stream_id, error_code);
|
||||
if (session_data->stream_data->stream_id == stream_id) {
|
||||
fprintf(stderr, "Stream %d closed with error_code=%d\n", stream_id,
|
||||
error_code);
|
||||
rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
}
|
||||
@@ -289,104 +277,112 @@ static int on_stream_close_callback(nghttp2_session *session,
|
||||
/* NPN TLS extension client callback. We check that server advertised
|
||||
the HTTP/2 protocol the nghttp2 library supports. If not, exit
|
||||
the program. */
|
||||
static int select_next_proto_cb(SSL* ssl,
|
||||
unsigned char **out, unsigned char *outlen,
|
||||
const unsigned char *in, unsigned int inlen,
|
||||
void *arg)
|
||||
{
|
||||
if(nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) {
|
||||
static int select_next_proto_cb(SSL *ssl _U_, unsigned char **out,
|
||||
unsigned char *outlen, const unsigned char *in,
|
||||
unsigned int inlen, void *arg _U_) {
|
||||
if (nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) {
|
||||
errx(1, "Server did not advertise " NGHTTP2_PROTO_VERSION_ID);
|
||||
}
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
|
||||
/* 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_new(SSLv23_client_method());
|
||||
if(!ssl_ctx) {
|
||||
if (!ssl_ctx) {
|
||||
errx(1, "Could not create SSL/TLS context: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
SSL_CTX_set_options(ssl_ctx,
|
||||
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_COMPRESSION |
|
||||
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
|
||||
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
|
||||
SSL_OP_NO_COMPRESSION |
|
||||
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
|
||||
SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL);
|
||||
return ssl_ctx;
|
||||
}
|
||||
|
||||
/* Create SSL object */
|
||||
static SSL* create_ssl(SSL_CTX *ssl_ctx)
|
||||
{
|
||||
static SSL *create_ssl(SSL_CTX *ssl_ctx) {
|
||||
SSL *ssl;
|
||||
ssl = SSL_new(ssl_ctx);
|
||||
if(!ssl) {
|
||||
if (!ssl) {
|
||||
errx(1, "Could not create SSL/TLS session object: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
return ssl;
|
||||
}
|
||||
|
||||
static void initialize_nghttp2_session(http2_session_data *session_data)
|
||||
{
|
||||
nghttp2_session_callbacks callbacks;
|
||||
static void initialize_nghttp2_session(http2_session_data *session_data) {
|
||||
nghttp2_session_callbacks *callbacks;
|
||||
|
||||
memset(&callbacks, 0, sizeof(callbacks));
|
||||
nghttp2_session_callbacks_new(&callbacks);
|
||||
|
||||
callbacks.send_callback = send_callback;
|
||||
callbacks.on_frame_recv_callback = on_frame_recv_callback;
|
||||
callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback;
|
||||
callbacks.on_stream_close_callback = on_stream_close_callback;
|
||||
callbacks.on_header_callback = on_header_callback;
|
||||
callbacks.on_begin_headers_callback = on_begin_headers_callback;
|
||||
nghttp2_session_client_new(&session_data->session, &callbacks, session_data);
|
||||
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
|
||||
on_frame_recv_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
|
||||
callbacks, on_data_chunk_recv_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_stream_close_callback(
|
||||
callbacks, on_stream_close_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_header_callback(callbacks,
|
||||
on_header_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_begin_headers_callback(
|
||||
callbacks, on_begin_headers_callback);
|
||||
|
||||
nghttp2_session_client_new(&session_data->session, callbacks, session_data);
|
||||
|
||||
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_MAX_CONCURRENT_STREAMS, 100 }
|
||||
};
|
||||
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
|
||||
int rv;
|
||||
|
||||
bufferevent_write(session_data->bev,
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
||||
bufferevent_write(session_data->bev, NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
|
||||
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE,
|
||||
iv, ARRLEN(iv));
|
||||
if(rv != 0) {
|
||||
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
|
||||
ARRLEN(iv));
|
||||
if (rv != 0) {
|
||||
errx(1, "Could not submit SETTINGS: %s", nghttp2_strerror(rv));
|
||||
}
|
||||
}
|
||||
|
||||
#define MAKE_NV(NAME, VALUE, VALUELEN) \
|
||||
{ (uint8_t*)NAME, (uint8_t*)VALUE, sizeof(NAME) - 1, VALUELEN, \
|
||||
NGHTTP2_NV_FLAG_NONE }
|
||||
#define MAKE_NV(NAME, VALUE, VALUELEN) \
|
||||
{ \
|
||||
(uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, VALUELEN, \
|
||||
NGHTTP2_NV_FLAG_NONE \
|
||||
}
|
||||
|
||||
#define MAKE_NV2(NAME, VALUE) \
|
||||
{ (uint8_t*)NAME, (uint8_t*)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \
|
||||
NGHTTP2_NV_FLAG_NONE }
|
||||
#define MAKE_NV2(NAME, VALUE) \
|
||||
{ \
|
||||
(uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \
|
||||
NGHTTP2_NV_FLAG_NONE \
|
||||
}
|
||||
|
||||
/* 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;
|
||||
http2_stream_data *stream_data = session_data->stream_data;
|
||||
const char *uri = stream_data->uri;
|
||||
const struct http_parser_url *u = stream_data->u;
|
||||
nghttp2_nv hdrs[] = {
|
||||
MAKE_NV2(":method", "GET"),
|
||||
MAKE_NV(":scheme",
|
||||
&uri[u->field_data[UF_SCHEMA].off], u->field_data[UF_SCHEMA].len),
|
||||
MAKE_NV(":authority", stream_data->authority, stream_data->authoritylen),
|
||||
MAKE_NV(":path", stream_data->path, stream_data->pathlen)
|
||||
};
|
||||
MAKE_NV2(":method", "GET"),
|
||||
MAKE_NV(":scheme", &uri[u->field_data[UF_SCHEMA].off],
|
||||
u->field_data[UF_SCHEMA].len),
|
||||
MAKE_NV(":authority", stream_data->authority, stream_data->authoritylen),
|
||||
MAKE_NV(":path", stream_data->path, stream_data->pathlen)};
|
||||
fprintf(stderr, "Request headers:\n");
|
||||
print_headers(stderr, hdrs, ARRLEN(hdrs));
|
||||
stream_id = nghttp2_submit_request(session_data->session, NULL,
|
||||
hdrs, ARRLEN(hdrs), NULL, stream_data);
|
||||
if(stream_id < 0) {
|
||||
stream_id = nghttp2_submit_request(session_data->session, NULL, hdrs,
|
||||
ARRLEN(hdrs), NULL, stream_data);
|
||||
if (stream_id < 0) {
|
||||
errx(1, "Could not submit HTTP request: %s", nghttp2_strerror(stream_id));
|
||||
}
|
||||
|
||||
@@ -395,12 +391,11 @@ static void submit_request(http2_session_data *session_data)
|
||||
|
||||
/* Serialize the frame and send (or buffer) the data to
|
||||
bufferevent. */
|
||||
static int session_send(http2_session_data *session_data)
|
||||
{
|
||||
static int session_send(http2_session_data *session_data) {
|
||||
int rv;
|
||||
|
||||
rv = nghttp2_session_send(session_data->session);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||
return -1;
|
||||
}
|
||||
@@ -411,26 +406,25 @@ static int session_send(http2_session_data *session_data)
|
||||
of bufferevent and feed them to nghttp2 library. This may invoke
|
||||
nghttp2 callbacks. It may also queues the frame in nghttp2 session
|
||||
context. To send them, we call session_send() in the end. */
|
||||
static void readcb(struct bufferevent *bev, void *ptr)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
||||
static void readcb(struct bufferevent *bev, void *ptr) {
|
||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||
ssize_t readlen;
|
||||
struct evbuffer *input = bufferevent_get_input(bev);
|
||||
size_t datalen = evbuffer_get_length(input);
|
||||
unsigned char *data = evbuffer_pullup(input, -1);
|
||||
|
||||
readlen = nghttp2_session_mem_recv(session_data->session, data, datalen);
|
||||
if(readlen < 0) {
|
||||
if (readlen < 0) {
|
||||
warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
if(evbuffer_drain(input, readlen) != 0) {
|
||||
if (evbuffer_drain(input, readlen) != 0) {
|
||||
warnx("Fatal error: evbuffer_drain failed");
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
if(session_send(session_data) != 0) {
|
||||
if (session_send(session_data) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
@@ -440,12 +434,11 @@ static void readcb(struct bufferevent *bev, void *ptr)
|
||||
receiving GOAWAY, we check the some conditions on the nghttp2
|
||||
library and output buffer of bufferevent. If it indicates we have
|
||||
no business to this session, tear down the connection. */
|
||||
static void writecb(struct bufferevent *bev, void *ptr)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
||||
if(nghttp2_session_want_read(session_data->session) == 0 &&
|
||||
nghttp2_session_want_write(session_data->session) == 0 &&
|
||||
evbuffer_get_length(bufferevent_get_output(session_data->bev)) == 0) {
|
||||
static void writecb(struct bufferevent *bev _U_, void *ptr) {
|
||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||
if (nghttp2_session_want_read(session_data->session) == 0 &&
|
||||
nghttp2_session_want_write(session_data->session) == 0 &&
|
||||
evbuffer_get_length(bufferevent_get_output(session_data->bev)) == 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
}
|
||||
}
|
||||
@@ -455,10 +448,9 @@ static void writecb(struct bufferevent *bev, void *ptr)
|
||||
peer verification. After SSL/TLS handshake is over, initialize
|
||||
nghttp2 library session, and send client connection header. Then
|
||||
send HTTP request. */
|
||||
static void eventcb(struct bufferevent *bev, short events, void *ptr)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
||||
if(events & BEV_EVENT_CONNECTED) {
|
||||
static void eventcb(struct bufferevent *bev, short events, void *ptr) {
|
||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||
if (events & BEV_EVENT_CONNECTED) {
|
||||
int fd = bufferevent_getfd(bev);
|
||||
int val = 1;
|
||||
fprintf(stderr, "Connected\n");
|
||||
@@ -466,41 +458,38 @@ static void eventcb(struct bufferevent *bev, short events, void *ptr)
|
||||
initialize_nghttp2_session(session_data);
|
||||
send_client_connection_header(session_data);
|
||||
submit_request(session_data);
|
||||
if(session_send(session_data) != 0) {
|
||||
if (session_send(session_data) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(events & BEV_EVENT_EOF) {
|
||||
if (events & BEV_EVENT_EOF) {
|
||||
warnx("Disconnected from the remote host");
|
||||
} else if(events & BEV_EVENT_ERROR) {
|
||||
} else if (events & BEV_EVENT_ERROR) {
|
||||
warnx("Network error");
|
||||
} else if(events & BEV_EVENT_TIMEOUT) {
|
||||
} else if (events & BEV_EVENT_TIMEOUT) {
|
||||
warnx("Timeout");
|
||||
}
|
||||
delete_http2_session_data(session_data);
|
||||
}
|
||||
|
||||
/* Start connecting to the remote peer |host:port| */
|
||||
static void initiate_connection(struct event_base *evbase,
|
||||
SSL_CTX *ssl_ctx,
|
||||
static void initiate_connection(struct event_base *evbase, SSL_CTX *ssl_ctx,
|
||||
const char *host, uint16_t port,
|
||||
http2_session_data *session_data)
|
||||
{
|
||||
http2_session_data *session_data) {
|
||||
int rv;
|
||||
struct bufferevent *bev;
|
||||
SSL *ssl;
|
||||
|
||||
ssl = create_ssl(ssl_ctx);
|
||||
bev = bufferevent_openssl_socket_new(evbase, -1, ssl,
|
||||
BUFFEREVENT_SSL_CONNECTING,
|
||||
BEV_OPT_DEFER_CALLBACKS |
|
||||
BEV_OPT_CLOSE_ON_FREE);
|
||||
bev = bufferevent_openssl_socket_new(
|
||||
evbase, -1, ssl, BUFFEREVENT_SSL_CONNECTING,
|
||||
BEV_OPT_DEFER_CALLBACKS | BEV_OPT_CLOSE_ON_FREE);
|
||||
bufferevent_setcb(bev, readcb, writecb, eventcb, session_data);
|
||||
rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase,
|
||||
AF_UNSPEC, host, port);
|
||||
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
errx(1, "Could not connect to the remote host %s", host);
|
||||
}
|
||||
session_data->bev = bev;
|
||||
@@ -508,8 +497,7 @@ static void initiate_connection(struct event_base *evbase,
|
||||
|
||||
/* Get resource denoted by the |uri|. The debug and error messages are
|
||||
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;
|
||||
char *host;
|
||||
uint16_t port;
|
||||
@@ -520,11 +508,11 @@ static void run(const char *uri)
|
||||
|
||||
/* Parse the |uri| and stores its components in |u| */
|
||||
rv = http_parser_parse_url(uri, strlen(uri), 0, &u);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
errx(1, "Could not parse URI %s", uri);
|
||||
}
|
||||
host = strndup(&uri[u.field_data[UF_HOST].off], u.field_data[UF_HOST].len);
|
||||
if(!(u.field_set & (1 << UF_PORT))) {
|
||||
if (!(u.field_set & (1 << UF_PORT))) {
|
||||
port = 443;
|
||||
} else {
|
||||
port = u.port;
|
||||
@@ -547,11 +535,10 @@ static void run(const char *uri)
|
||||
SSL_CTX_free(ssl_ctx);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int main(int argc, char **argv) {
|
||||
struct sigaction act;
|
||||
|
||||
if(argc < 2) {
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "Usage: libevent-client HTTPS_URI\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
@@ -560,6 +547,8 @@ int main(int argc, char **argv)
|
||||
act.sa_handler = SIG_IGN;
|
||||
sigaction(SIGPIPE, &act, NULL);
|
||||
|
||||
OPENSSL_config(NULL);
|
||||
OpenSSL_add_all_algorithms();
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
|
||||
|
||||
@@ -22,6 +22,10 @@
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* 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/socket.h>
|
||||
#include <netdb.h>
|
||||
@@ -36,6 +40,7 @@
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/conf.h>
|
||||
|
||||
#include <event.h>
|
||||
#include <event2/event.h>
|
||||
@@ -46,11 +51,13 @@
|
||||
|
||||
#define OUTPUT_WOULDBLOCK_THRESHOLD (1 << 16)
|
||||
|
||||
#define ARRLEN(x) (sizeof(x)/sizeof(x[0]))
|
||||
#define ARRLEN(x) (sizeof(x) / sizeof(x[0]))
|
||||
|
||||
#define MAKE_NV(NAME, VALUE) \
|
||||
{ (uint8_t*)NAME, (uint8_t*)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \
|
||||
NGHTTP2_NV_FLAG_NONE }
|
||||
#define MAKE_NV(NAME, VALUE) \
|
||||
{ \
|
||||
(uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \
|
||||
NGHTTP2_NV_FLAG_NONE \
|
||||
}
|
||||
|
||||
struct app_context;
|
||||
typedef struct app_context app_context;
|
||||
@@ -68,7 +75,6 @@ typedef struct http2_session_data {
|
||||
app_context *app_ctx;
|
||||
nghttp2_session *session;
|
||||
char *client_addr;
|
||||
size_t handshake_leftlen;
|
||||
} http2_session_data;
|
||||
|
||||
struct app_context {
|
||||
@@ -79,32 +85,40 @@ struct app_context {
|
||||
static unsigned char next_proto_list[256];
|
||||
static size_t next_proto_list_len;
|
||||
|
||||
static int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len,
|
||||
void *arg)
|
||||
{
|
||||
static int next_proto_cb(SSL *s _U_, const unsigned char **data,
|
||||
unsigned int *len, void *arg _U_) {
|
||||
*data = next_proto_list;
|
||||
*len = (unsigned int)next_proto_list_len;
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
EC_KEY *ecdh;
|
||||
|
||||
ssl_ctx = SSL_CTX_new(SSLv23_server_method());
|
||||
if(!ssl_ctx) {
|
||||
if (!ssl_ctx) {
|
||||
errx(1, "Could not create SSL/TLS context: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
SSL_CTX_set_options(ssl_ctx,
|
||||
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_COMPRESSION |
|
||||
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
|
||||
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
|
||||
SSL_OP_NO_COMPRESSION |
|
||||
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
|
||||
|
||||
if(SSL_CTX_use_PrivateKey_file(ssl_ctx, key_file,
|
||||
SSL_FILETYPE_PEM) != 1) {
|
||||
ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
|
||||
if (!ecdh) {
|
||||
errx(1, "EC_KEY_new_by_curv_name failed: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh);
|
||||
EC_KEY_free(ecdh);
|
||||
|
||||
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);
|
||||
}
|
||||
if(SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file) != 1) {
|
||||
if (SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file) != 1) {
|
||||
errx(1, "Could not read certificate file %s", cert_file);
|
||||
}
|
||||
|
||||
@@ -118,11 +132,10 @@ static SSL_CTX* create_ssl_ctx(const char *key_file, const char *cert_file)
|
||||
}
|
||||
|
||||
/* Create SSL object */
|
||||
static SSL* create_ssl(SSL_CTX *ssl_ctx)
|
||||
{
|
||||
static SSL *create_ssl(SSL_CTX *ssl_ctx) {
|
||||
SSL *ssl;
|
||||
ssl = SSL_new(ssl_ctx);
|
||||
if(!ssl) {
|
||||
if (!ssl) {
|
||||
errx(1, "Could not create SSL/TLS session object: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
@@ -130,28 +143,25 @@ static SSL* create_ssl(SSL_CTX *ssl_ctx)
|
||||
}
|
||||
|
||||
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;
|
||||
session_data->root.next = stream_data;
|
||||
stream_data->prev = &session_data->root;
|
||||
if(stream_data->next) {
|
||||
if (stream_data->next) {
|
||||
stream_data->next->prev = stream_data;
|
||||
}
|
||||
}
|
||||
|
||||
static void remove_stream(http2_session_data *session_data,
|
||||
http2_stream_data *stream_data)
|
||||
{
|
||||
static void remove_stream(http2_session_data *session_data _U_,
|
||||
http2_stream_data *stream_data) {
|
||||
stream_data->prev->next = stream_data->next;
|
||||
if(stream_data->next) {
|
||||
if (stream_data->next) {
|
||||
stream_data->next->prev = stream_data->prev;
|
||||
}
|
||||
}
|
||||
|
||||
static http2_stream_data* create_http2_stream_data
|
||||
(http2_session_data *session_data, int32_t stream_id)
|
||||
{
|
||||
static http2_stream_data *
|
||||
create_http2_stream_data(http2_session_data *session_data, int32_t stream_id) {
|
||||
http2_stream_data *stream_data;
|
||||
stream_data = malloc(sizeof(http2_stream_data));
|
||||
memset(stream_data, 0, sizeof(http2_stream_data));
|
||||
@@ -162,20 +172,18 @@ static http2_stream_data* create_http2_stream_data
|
||||
return stream_data;
|
||||
}
|
||||
|
||||
static void delete_http2_stream_data(http2_stream_data *stream_data)
|
||||
{
|
||||
if(stream_data->fd != -1) {
|
||||
static void delete_http2_stream_data(http2_stream_data *stream_data) {
|
||||
if (stream_data->fd != -1) {
|
||||
close(stream_data->fd);
|
||||
}
|
||||
free(stream_data->request_path);
|
||||
free(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,
|
||||
struct sockaddr *addr,
|
||||
int addrlen)
|
||||
{
|
||||
int addrlen) {
|
||||
int rv;
|
||||
http2_session_data *session_data;
|
||||
SSL *ssl;
|
||||
@@ -187,13 +195,11 @@ static http2_session_data* create_http2_session_data(app_context *app_ctx,
|
||||
memset(session_data, 0, sizeof(http2_session_data));
|
||||
session_data->app_ctx = app_ctx;
|
||||
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val));
|
||||
session_data->bev = bufferevent_openssl_socket_new
|
||||
(app_ctx->evbase, fd, ssl,
|
||||
BUFFEREVENT_SSL_ACCEPTING,
|
||||
BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
|
||||
session_data->handshake_leftlen = NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN;
|
||||
session_data->bev = bufferevent_openssl_socket_new(
|
||||
app_ctx->evbase, fd, ssl, BUFFEREVENT_SSL_ACCEPTING,
|
||||
BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
|
||||
rv = getnameinfo(addr, addrlen, host, sizeof(host), NULL, 0, NI_NUMERICHOST);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
session_data->client_addr = strdup("(unknown)");
|
||||
} else {
|
||||
session_data->client_addr = strdup(host);
|
||||
@@ -202,17 +208,16 @@ static http2_session_data* create_http2_session_data(app_context *app_ctx,
|
||||
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;
|
||||
SSL *ssl = bufferevent_openssl_get_ssl(session_data->bev);
|
||||
fprintf(stderr, "%s disconnected\n", session_data->client_addr);
|
||||
if(ssl) {
|
||||
if (ssl) {
|
||||
SSL_shutdown(ssl);
|
||||
}
|
||||
bufferevent_free(session_data->bev);
|
||||
nghttp2_session_del(session_data->session);
|
||||
for(stream_data = session_data->root.next; stream_data;) {
|
||||
for (stream_data = session_data->root.next; stream_data;) {
|
||||
http2_stream_data *next = stream_data->next;
|
||||
delete_http2_stream_data(stream_data);
|
||||
stream_data = next;
|
||||
@@ -223,11 +228,10 @@ static void delete_http2_session_data(http2_session_data *session_data)
|
||||
|
||||
/* Serialize the frame and send (or buffer) the data to
|
||||
bufferevent. */
|
||||
static int session_send(http2_session_data *session_data)
|
||||
{
|
||||
static int session_send(http2_session_data *session_data) {
|
||||
int rv;
|
||||
rv = nghttp2_session_send(session_data->session);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||
return -1;
|
||||
}
|
||||
@@ -238,37 +242,34 @@ static int session_send(http2_session_data *session_data)
|
||||
function. Invocation of nghttp2_session_mem_recv() may make
|
||||
additional pending frames, so call session_send() at the end of the
|
||||
function. */
|
||||
static int session_recv(http2_session_data *session_data)
|
||||
{
|
||||
static int session_recv(http2_session_data *session_data) {
|
||||
ssize_t readlen;
|
||||
struct evbuffer *input = bufferevent_get_input(session_data->bev);
|
||||
size_t datalen = evbuffer_get_length(input);
|
||||
unsigned char *data = evbuffer_pullup(input, -1);
|
||||
|
||||
readlen = nghttp2_session_mem_recv(session_data->session, data, datalen);
|
||||
if(readlen < 0) {
|
||||
if (readlen < 0) {
|
||||
warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
|
||||
return -1;
|
||||
}
|
||||
if(evbuffer_drain(input, readlen) != 0) {
|
||||
if (evbuffer_drain(input, readlen) != 0) {
|
||||
warnx("Fatal error: evbuffer_drain failed");
|
||||
return -1;
|
||||
}
|
||||
if(session_send(session_data) != 0) {
|
||||
if (session_send(session_data) != 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t send_callback(nghttp2_session *session,
|
||||
const uint8_t *data, size_t length,
|
||||
int flags, void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
|
||||
size_t length, int flags _U_, void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
struct bufferevent *bev = session_data->bev;
|
||||
/* Avoid excessive buffering in server side. */
|
||||
if(evbuffer_get_length(bufferevent_get_output(session_data->bev)) >=
|
||||
OUTPUT_WOULDBLOCK_THRESHOLD) {
|
||||
if (evbuffer_get_length(bufferevent_get_output(session_data->bev)) >=
|
||||
OUTPUT_WOULDBLOCK_THRESHOLD) {
|
||||
return NGHTTP2_ERR_WOULDBLOCK;
|
||||
}
|
||||
bufferevent_write(bev, data, length);
|
||||
@@ -276,26 +277,24 @@ static ssize_t send_callback(nghttp2_session *session,
|
||||
}
|
||||
|
||||
/* 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 sublen = strlen(sub);
|
||||
if(slen < sublen) {
|
||||
if (slen < sublen) {
|
||||
return 0;
|
||||
}
|
||||
return memcmp(s + slen - sublen, sub, sublen) == 0;
|
||||
}
|
||||
|
||||
/* Returns int value of hex string character |c| */
|
||||
static uint8_t hex_to_uint(uint8_t c)
|
||||
{
|
||||
if('0' <= c && c <= '9') {
|
||||
static uint8_t hex_to_uint(uint8_t c) {
|
||||
if ('0' <= c && c <= '9') {
|
||||
return c - '0';
|
||||
}
|
||||
if('A' <= c && c <= 'F') {
|
||||
if ('A' <= c && c <= 'F') {
|
||||
return c - 'A' + 10;
|
||||
}
|
||||
if('a' <= c && c <= 'f') {
|
||||
if ('a' <= c && c <= 'f') {
|
||||
return c - 'a' + 10;
|
||||
}
|
||||
return 0;
|
||||
@@ -305,16 +304,15 @@ static uint8_t hex_to_uint(uint8_t c)
|
||||
and returns the decoded byte string in allocated buffer. The return
|
||||
value is NULL terminated. The caller must free the returned
|
||||
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;
|
||||
|
||||
res = malloc(valuelen + 1);
|
||||
if(valuelen > 3) {
|
||||
if (valuelen > 3) {
|
||||
size_t i, j;
|
||||
for(i = 0, j = 0; i < valuelen - 2;) {
|
||||
if(value[i] != '%' ||
|
||||
!isxdigit(value[i + 1]) || !isxdigit(value[i + 2])) {
|
||||
for (i = 0, j = 0; i < valuelen - 2;) {
|
||||
if (value[i] != '%' || !isxdigit(value[i + 1]) ||
|
||||
!isxdigit(value[i + 2])) {
|
||||
res[j++] = value[i++];
|
||||
continue;
|
||||
}
|
||||
@@ -330,33 +328,33 @@ static char* percent_decode(const uint8_t *value, size_t valuelen)
|
||||
return res;
|
||||
}
|
||||
|
||||
static ssize_t file_read_callback
|
||||
(nghttp2_session *session, int32_t stream_id,
|
||||
uint8_t *buf, size_t length, uint32_t *data_flags,
|
||||
nghttp2_data_source *source, void *user_data)
|
||||
{
|
||||
static ssize_t file_read_callback(nghttp2_session *session _U_,
|
||||
int32_t stream_id _U_, uint8_t *buf,
|
||||
size_t length, uint32_t *data_flags,
|
||||
nghttp2_data_source *source,
|
||||
void *user_data _U_) {
|
||||
int fd = source->fd;
|
||||
ssize_t r;
|
||||
while((r = read(fd, buf, length)) == -1 && errno == EINTR);
|
||||
if(r == -1) {
|
||||
while ((r = read(fd, buf, length)) == -1 && errno == EINTR)
|
||||
;
|
||||
if (r == -1) {
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
if(r == 0) {
|
||||
if (r == 0) {
|
||||
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
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;
|
||||
nghttp2_data_provider data_prd;
|
||||
data_prd.source.fd = fd;
|
||||
data_prd.read_callback = file_read_callback;
|
||||
|
||||
rv = nghttp2_submit_response(session, stream_id, nva, nvlen, &data_prd);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||
return -1;
|
||||
}
|
||||
@@ -364,25 +362,22 @@ static int send_response(nghttp2_session *session, int32_t stream_id,
|
||||
}
|
||||
|
||||
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,
|
||||
http2_stream_data *stream_data)
|
||||
{
|
||||
http2_stream_data *stream_data) {
|
||||
int rv;
|
||||
ssize_t writelen;
|
||||
int pipefd[2];
|
||||
nghttp2_nv hdrs[] = {
|
||||
MAKE_NV(":status", "404")
|
||||
};
|
||||
nghttp2_nv hdrs[] = {MAKE_NV(":status", "404")};
|
||||
|
||||
rv = pipe(pipefd);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
warn("Could not create pipe");
|
||||
rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
|
||||
stream_data->stream_id,
|
||||
NGHTTP2_INTERNAL_ERROR);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||
return -1;
|
||||
}
|
||||
@@ -392,15 +387,15 @@ static int error_reply(nghttp2_session *session,
|
||||
writelen = write(pipefd[1], ERROR_HTML, sizeof(ERROR_HTML) - 1);
|
||||
close(pipefd[1]);
|
||||
|
||||
if(writelen != sizeof(ERROR_HTML) - 1) {
|
||||
if (writelen != sizeof(ERROR_HTML) - 1) {
|
||||
close(pipefd[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
stream_data->fd = pipefd[0];
|
||||
|
||||
if(send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs),
|
||||
pipefd[0]) != 0) {
|
||||
if (send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs),
|
||||
pipefd[0]) != 0) {
|
||||
close(pipefd[0]);
|
||||
return -1;
|
||||
}
|
||||
@@ -410,27 +405,26 @@ static int error_reply(nghttp2_session *session,
|
||||
/* nghttp2_on_header_callback: Called when nghttp2 library emits
|
||||
single header name/value pair. */
|
||||
static int on_header_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame,
|
||||
const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen,
|
||||
uint8_t flags,
|
||||
void *user_data)
|
||||
{
|
||||
const nghttp2_frame *frame, const uint8_t *name,
|
||||
size_t namelen, const uint8_t *value,
|
||||
size_t valuelen, uint8_t flags _U_,
|
||||
void *user_data _U_) {
|
||||
http2_stream_data *stream_data;
|
||||
const char PATH[] = ":path";
|
||||
switch(frame->hd.type) {
|
||||
switch (frame->hd.type) {
|
||||
case NGHTTP2_HEADERS:
|
||||
if(frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
||||
if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
||||
break;
|
||||
}
|
||||
stream_data = nghttp2_session_get_stream_user_data(session,
|
||||
frame->hd.stream_id);
|
||||
if(!stream_data || stream_data->request_path) {
|
||||
stream_data =
|
||||
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
|
||||
if (!stream_data || stream_data->request_path) {
|
||||
break;
|
||||
}
|
||||
if(namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) {
|
||||
if (namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) {
|
||||
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);
|
||||
}
|
||||
break;
|
||||
@@ -440,13 +434,12 @@ static int on_header_callback(nghttp2_session *session,
|
||||
|
||||
static int on_begin_headers_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame,
|
||||
void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
http2_stream_data *stream_data;
|
||||
|
||||
if(frame->hd.type != NGHTTP2_HEADERS ||
|
||||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
||||
if (frame->hd.type != NGHTTP2_HEADERS ||
|
||||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
||||
return 0;
|
||||
}
|
||||
stream_data = create_http2_stream_data(session_data, frame->hd.stream_id);
|
||||
@@ -457,52 +450,47 @@ static int on_begin_headers_callback(nghttp2_session *session,
|
||||
|
||||
/* Minimum check for directory traversal. Returns nonzero if it is
|
||||
safe. */
|
||||
static int check_path(const char *path)
|
||||
{
|
||||
static int check_path(const char *path) {
|
||||
/* We don't like '\' in url. */
|
||||
return path[0] && path[0] == '/' &&
|
||||
strchr(path, '\\') == NULL &&
|
||||
strstr(path, "/../") == NULL &&
|
||||
strstr(path, "/./") == NULL &&
|
||||
!ends_with(path, "/..") && !ends_with(path, "/.");
|
||||
return path[0] && path[0] == '/' && strchr(path, '\\') == NULL &&
|
||||
strstr(path, "/../") == NULL && strstr(path, "/./") == NULL &&
|
||||
!ends_with(path, "/..") && !ends_with(path, "/.");
|
||||
}
|
||||
|
||||
static int on_request_recv(nghttp2_session *session,
|
||||
http2_session_data *session_data,
|
||||
http2_stream_data *stream_data)
|
||||
{
|
||||
http2_stream_data *stream_data) {
|
||||
int fd;
|
||||
nghttp2_nv hdrs[] = {
|
||||
MAKE_NV(":status", "200")
|
||||
};
|
||||
nghttp2_nv hdrs[] = {MAKE_NV(":status", "200")};
|
||||
char *rel_path;
|
||||
|
||||
if(!stream_data->request_path) {
|
||||
if(error_reply(session, stream_data) != 0) {
|
||||
if (!stream_data->request_path) {
|
||||
if (error_reply(session, stream_data) != 0) {
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
fprintf(stderr, "%s GET %s\n", session_data->client_addr,
|
||||
stream_data->request_path);
|
||||
if(!check_path(stream_data->request_path)) {
|
||||
if(error_reply(session, stream_data) != 0) {
|
||||
if (!check_path(stream_data->request_path)) {
|
||||
if (error_reply(session, stream_data) != 0) {
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
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);
|
||||
if(fd == -1) {
|
||||
if(error_reply(session, stream_data) != 0) {
|
||||
if (fd == -1) {
|
||||
if (error_reply(session, stream_data) != 0) {
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
stream_data->fd = fd;
|
||||
|
||||
if(send_response(session, stream_data->stream_id, hdrs,
|
||||
ARRLEN(hdrs), fd) != 0) {
|
||||
if (send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs), fd) !=
|
||||
0) {
|
||||
close(fd);
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
@@ -510,20 +498,19 @@ static int on_request_recv(nghttp2_session *session,
|
||||
}
|
||||
|
||||
static int on_frame_recv_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame, void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
const nghttp2_frame *frame, void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
http2_stream_data *stream_data;
|
||||
switch(frame->hd.type) {
|
||||
switch (frame->hd.type) {
|
||||
case NGHTTP2_DATA:
|
||||
case NGHTTP2_HEADERS:
|
||||
/* Check that the client request has finished */
|
||||
if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||
stream_data = nghttp2_session_get_stream_user_data(session,
|
||||
frame->hd.stream_id);
|
||||
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||
stream_data =
|
||||
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
|
||||
/* For DATA and HEADERS frame, this callback may be called after
|
||||
on_stream_close_callback. Check that stream still alive. */
|
||||
if(!stream_data) {
|
||||
if (!stream_data) {
|
||||
return 0;
|
||||
}
|
||||
return on_request_recv(session, session_data, stream_data);
|
||||
@@ -535,16 +522,13 @@ static int on_frame_recv_callback(nghttp2_session *session,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_stream_close_callback(nghttp2_session *session,
|
||||
int32_t stream_id,
|
||||
nghttp2_error_code error_code,
|
||||
void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||
uint32_t error_code _U_, void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
http2_stream_data *stream_data;
|
||||
|
||||
stream_data = nghttp2_session_get_stream_user_data(session, stream_id);
|
||||
if(!stream_data) {
|
||||
if (!stream_data) {
|
||||
return 0;
|
||||
}
|
||||
remove_stream(session_data, stream_data);
|
||||
@@ -552,32 +536,49 @@ static int on_stream_close_callback(nghttp2_session *session,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void initialize_nghttp2_session(http2_session_data *session_data)
|
||||
{
|
||||
nghttp2_session_callbacks callbacks;
|
||||
static void initialize_nghttp2_session(http2_session_data *session_data) {
|
||||
nghttp2_option *option;
|
||||
nghttp2_session_callbacks *callbacks;
|
||||
|
||||
memset(&callbacks, 0, sizeof(callbacks));
|
||||
nghttp2_option_new(&option);
|
||||
|
||||
callbacks.send_callback = send_callback;
|
||||
callbacks.on_frame_recv_callback = on_frame_recv_callback;
|
||||
callbacks.on_stream_close_callback = on_stream_close_callback;
|
||||
callbacks.on_header_callback = on_header_callback;
|
||||
callbacks.on_begin_headers_callback = on_begin_headers_callback;
|
||||
nghttp2_session_server_new(&session_data->session, &callbacks, session_data);
|
||||
/* 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_set_send_callback(callbacks, send_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
|
||||
on_frame_recv_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_stream_close_callback(
|
||||
callbacks, on_stream_close_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_header_callback(callbacks,
|
||||
on_header_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_begin_headers_callback(
|
||||
callbacks, on_begin_headers_callback);
|
||||
|
||||
nghttp2_session_server_new2(&session_data->session, callbacks, session_data,
|
||||
option);
|
||||
|
||||
nghttp2_session_callbacks_del(callbacks);
|
||||
nghttp2_option_del(option);
|
||||
}
|
||||
|
||||
/* Send HTTP/2 client connection header, which includes 24 bytes
|
||||
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_MAX_CONCURRENT_STREAMS, 100 }
|
||||
};
|
||||
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
|
||||
int rv;
|
||||
|
||||
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE,
|
||||
iv, ARRLEN(iv));
|
||||
if(rv != 0) {
|
||||
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
|
||||
ARRLEN(iv));
|
||||
if (rv != 0) {
|
||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||
return -1;
|
||||
}
|
||||
@@ -586,10 +587,9 @@ static int send_server_connection_header(http2_session_data *session_data)
|
||||
|
||||
/* readcb for bufferevent after client connection header was
|
||||
checked. */
|
||||
static void readcb(struct bufferevent *bev, void *ptr)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
||||
if(session_recv(session_data) != 0) {
|
||||
static void readcb(struct bufferevent *bev _U_, void *ptr) {
|
||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||
if (session_recv(session_data) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
@@ -603,88 +603,60 @@ static void readcb(struct bufferevent *bev, void *ptr)
|
||||
process pending data in the output buffer. This is necessary
|
||||
because we have a threshold on the buffer size to avoid too much
|
||||
buffering. See send_callback(). */
|
||||
static void writecb(struct bufferevent *bev, void *ptr)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
||||
if(evbuffer_get_length(bufferevent_get_output(bev)) > 0) {
|
||||
static void writecb(struct bufferevent *bev, void *ptr) {
|
||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||
if (evbuffer_get_length(bufferevent_get_output(bev)) > 0) {
|
||||
return;
|
||||
}
|
||||
if(nghttp2_session_want_read(session_data->session) == 0 &&
|
||||
nghttp2_session_want_write(session_data->session) == 0) {
|
||||
if (nghttp2_session_want_read(session_data->session) == 0 &&
|
||||
nghttp2_session_want_write(session_data->session) == 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
if(session_send(session_data) != 0) {
|
||||
if (session_send(session_data) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* eventcb for bufferevent */
|
||||
static void eventcb(struct bufferevent *bev, short events, void *ptr)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
||||
if(events & BEV_EVENT_CONNECTED) {
|
||||
static void eventcb(struct bufferevent *bev _U_, short events, void *ptr) {
|
||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||
if (events & BEV_EVENT_CONNECTED) {
|
||||
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;
|
||||
}
|
||||
if(events & BEV_EVENT_EOF) {
|
||||
if (events & BEV_EVENT_EOF) {
|
||||
fprintf(stderr, "%s EOF\n", session_data->client_addr);
|
||||
} else if(events & BEV_EVENT_ERROR) {
|
||||
} else if (events & BEV_EVENT_ERROR) {
|
||||
fprintf(stderr, "%s network error\n", session_data->client_addr);
|
||||
} else if(events & BEV_EVENT_TIMEOUT) {
|
||||
} else if (events & BEV_EVENT_TIMEOUT) {
|
||||
fprintf(stderr, "%s timeout\n", session_data->client_addr);
|
||||
}
|
||||
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 */
|
||||
static void acceptcb(struct evconnlistener *listener, int fd,
|
||||
struct sockaddr *addr, int addrlen, void *arg)
|
||||
{
|
||||
app_context *app_ctx = (app_context*)arg;
|
||||
static void acceptcb(struct evconnlistener *listener _U_, int fd,
|
||||
struct sockaddr *addr, int addrlen, void *arg) {
|
||||
app_context *app_ctx = (app_context *)arg;
|
||||
http2_session_data *session_data;
|
||||
|
||||
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,
|
||||
app_context *app_ctx)
|
||||
{
|
||||
app_context *app_ctx) {
|
||||
int rv;
|
||||
struct addrinfo hints;
|
||||
struct addrinfo *res, *rp;
|
||||
@@ -698,16 +670,15 @@ static void start_listen(struct event_base *evbase, const char *service,
|
||||
#endif /* AI_ADDRCONFIG */
|
||||
|
||||
rv = getaddrinfo(NULL, service, &hints, &res);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
errx(1, NULL);
|
||||
}
|
||||
for(rp = res; rp; rp = rp->ai_next) {
|
||||
for (rp = res; rp; rp = rp->ai_next) {
|
||||
struct evconnlistener *listener;
|
||||
listener = evconnlistener_new_bind(evbase, acceptcb, app_ctx,
|
||||
LEV_OPT_CLOSE_ON_FREE |
|
||||
LEV_OPT_REUSEABLE, 16,
|
||||
rp->ai_addr, rp->ai_addrlen);
|
||||
if(listener) {
|
||||
listener = evconnlistener_new_bind(
|
||||
evbase, acceptcb, app_ctx, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
|
||||
16, rp->ai_addr, rp->ai_addrlen);
|
||||
if (listener) {
|
||||
freeaddrinfo(res);
|
||||
|
||||
return;
|
||||
@@ -717,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,
|
||||
struct event_base *evbase)
|
||||
{
|
||||
struct event_base *evbase) {
|
||||
memset(app_ctx, 0, sizeof(app_context));
|
||||
app_ctx->ssl_ctx = ssl_ctx;
|
||||
app_ctx->evbase = evbase;
|
||||
}
|
||||
|
||||
static void run(const char *service,
|
||||
const char *key_file, const char *cert_file)
|
||||
{
|
||||
static void run(const char *service, const char *key_file,
|
||||
const char *cert_file) {
|
||||
SSL_CTX *ssl_ctx;
|
||||
app_context app_ctx;
|
||||
struct event_base *evbase;
|
||||
@@ -742,11 +711,10 @@ static void run(const char *service,
|
||||
SSL_CTX_free(ssl_ctx);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int main(int argc, char **argv) {
|
||||
struct sigaction act;
|
||||
|
||||
if(argc < 4) {
|
||||
if (argc < 4) {
|
||||
fprintf(stderr, "Usage: libevent-server PORT KEY_FILE CERT_FILE\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
@@ -755,6 +723,8 @@ int main(int argc, char **argv)
|
||||
act.sa_handler = SIG_IGN;
|
||||
sigaction(SIGPIPE, &act, NULL);
|
||||
|
||||
OPENSSL_config(NULL);
|
||||
OpenSSL_add_all_algorithms();
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
|
||||
|
||||
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')
|
||||
97
genheaderfunc.py
Executable file
97
genheaderfunc.py
Executable file
@@ -0,0 +1,97 @@
|
||||
#!/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",
|
||||
# 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()
|
||||
@@ -22,7 +22,9 @@
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
SUBDIRS = includes
|
||||
|
||||
AM_CFLAGS = -Wall
|
||||
EXTRA_DIST = Makefile.msvc
|
||||
|
||||
AM_CFLAGS = $(WARNCFLAGS)
|
||||
AM_CPPFLAGS = -I$(srcdir)/includes -I$(builddir)/includes @DEFS@
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
@@ -41,7 +43,9 @@ OBJECTS = nghttp2_pq.c nghttp2_map.c nghttp2_queue.c \
|
||||
nghttp2_hd.c nghttp2_hd_huffman.c nghttp2_hd_huffman_data.c \
|
||||
nghttp2_version.c \
|
||||
nghttp2_priority_spec.c \
|
||||
nghttp2_option.c
|
||||
nghttp2_option.c \
|
||||
nghttp2_callbacks.c \
|
||||
nghttp2_mem.c
|
||||
|
||||
HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \
|
||||
nghttp2_frame.h \
|
||||
@@ -52,7 +56,9 @@ HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \
|
||||
nghttp2_net.h \
|
||||
nghttp2_hd.h nghttp2_hd_huffman.h \
|
||||
nghttp2_priority_spec.h \
|
||||
nghttp2_option.h
|
||||
nghttp2_option.h \
|
||||
nghttp2_callbacks.h \
|
||||
nghttp2_mem.h
|
||||
|
||||
libnghttp2_la_SOURCES = $(HFILES) $(OBJECTS)
|
||||
libnghttp2_la_LDFLAGS = -no-undefined \
|
||||
|
||||
266
lib/Makefile.msvc
Normal file
266
lib/Makefile.msvc
Normal file
@@ -0,0 +1,266 @@
|
||||
#
|
||||
# GNU Makefile for nghttp2 / MSVC.
|
||||
#
|
||||
# By G. Vanem <gvanem@yahoo.no> 2013
|
||||
# The MIT License apply.
|
||||
#
|
||||
|
||||
#
|
||||
# Choose your weapons:
|
||||
# Set 'ZLIB_ROOT' to the root of zlib.
|
||||
# Set 'USE_CYTHON=1' to build and install the 'nghttp2.pyd' Python extension.
|
||||
#
|
||||
ZLIB_ROOT = g:/MingW32/src/Compression/zlib-1.2.8
|
||||
USE_CYTHON = 1
|
||||
|
||||
_VERSION := $(shell grep AC_INIT ../configure.ac | cut -d'[' -f3 | sed -e 's/-DEV], //g')
|
||||
_VERSION := $(subst ., ,$(_VERSION))
|
||||
VER_MAJOR = $(word 1,$(_VERSION))
|
||||
VER_MINOR = $(word 2,$(_VERSION))
|
||||
VER_MICRO = $(word 3,$(_VERSION))
|
||||
VERSION = $(VER_MAJOR).$(VER_MINOR).$(VER_MICRO)
|
||||
VERSION_NUM = ($(VER_MAJOR) << 16) + ($(VER_MINOR) << 8) + $(VER_MICRO)
|
||||
|
||||
GENERATED = 'Generated by $(realpath Makefile.MSVC)'
|
||||
|
||||
#
|
||||
# Where to copy nghttp2.dll + lib + headers to.
|
||||
# Note: 'make install' is not in default targets. Do it explicitly.
|
||||
#
|
||||
VC_ROOT = $(realpath $(VCINSTALLDIR))
|
||||
INSTALL_BIN = $(VC_ROOT)/bin
|
||||
INSTALL_LIB = $(VC_ROOT)/lib
|
||||
INSTALL_HDR = $(VC_ROOT)/include
|
||||
|
||||
#
|
||||
# Build for DEBUG-model and RELEASE at the same time.
|
||||
#
|
||||
TARGETS = nghttp2.lib nghttp2.dll nghttp2_imp.lib \
|
||||
nghttp2d.lib nghttp2d.dll nghttp2d_imp.lib
|
||||
|
||||
EXT_LIBS = $(ZLIB_ROOT)/zlib.lib ws2_32.lib
|
||||
|
||||
OBJ_DIR = MSVC_obj
|
||||
|
||||
NGHTTP2_PDB_R = $(OBJ_DIR)/nghttp2.pdb
|
||||
NGHTTP2_PDB_D = $(OBJ_DIR)/nghttp2d.pdb
|
||||
|
||||
CC = cl
|
||||
CFLAGS = -I./includes -I$(ZLIB_ROOT) -DHAVE_WINSOCK2_H -Dssize_t=long
|
||||
|
||||
CFLAGS_R = -nologo -MD -W3 -Zi -Fd./$(NGHTTP2_PDB_R)
|
||||
CFLAGS_D = -nologo -MDd -W3 -Zi -Fd./$(NGHTTP2_PDB_D) \
|
||||
-Ot -D_DEBUG -GF -RTCs -RTCu # -RTCc -GS
|
||||
|
||||
LDFLAGS = -nologo -machine:i386 -map -debug -incremental:no # -verbose
|
||||
|
||||
NGHTTP2_SRC = nghttp2_buf.c \
|
||||
nghttp2_callbacks.c \
|
||||
nghttp2_frame.c \
|
||||
nghttp2_helper.c \
|
||||
nghttp2_hd.c \
|
||||
nghttp2_hd_huffman.c \
|
||||
nghttp2_hd_huffman_data.c \
|
||||
nghttp2_map.c \
|
||||
nghttp2_npn.c \
|
||||
nghttp2_option.c \
|
||||
nghttp2_outbound_item.c \
|
||||
nghttp2_priority_spec.c \
|
||||
nghttp2_pq.c \
|
||||
nghttp2_queue.c \
|
||||
nghttp2_session.c \
|
||||
nghttp2_stream.c \
|
||||
nghttp2_submit.c \
|
||||
nghttp2_version.c
|
||||
|
||||
NGHTTP2_OBJ_R = $(addprefix $(OBJ_DIR)/r_, $(notdir $(NGHTTP2_SRC:.c=.obj)))
|
||||
NGHTTP2_OBJ_D = $(addprefix $(OBJ_DIR)/d_, $(notdir $(NGHTTP2_SRC:.c=.obj)))
|
||||
|
||||
all: intro $(OBJ_DIR) $(TARGETS)
|
||||
@echo 'Welcome to NgHTTP2 (release + debug).'
|
||||
@echo 'Do a "make -f Makefile.MSVC install" at own risk!'
|
||||
|
||||
intro:
|
||||
@echo 'Building NgHTTP (MSVC) ver. "$(VERSION)".'
|
||||
|
||||
test_ver:
|
||||
@echo '$$(VERSION): "$(VERSION)".'
|
||||
@echo '$$(_VERSION): "$(_VERSION)".'
|
||||
@echo '$$(VER_MAJOR): "$(VER_MAJOR)".'
|
||||
@echo '$$(VER_MINOR): "$(VER_MINOR)".'
|
||||
@echo '$$(VER_MICRO): "$(VER_MICRO)".'
|
||||
|
||||
$(OBJ_DIR):
|
||||
- mkdir $(OBJ_DIR)
|
||||
|
||||
install: includes/nghttp2/nghttp2.h includes/nghttp2/nghttp2ver.h \
|
||||
nghttp2.dll nghttp2.lib nghttp2_imp.lib \
|
||||
nghttp2d.dll nghttp2d.lib nghttp2d_imp.lib \
|
||||
copy_headers_and_libs install_nghttp2_pyd_$(USE_CYTHON)
|
||||
|
||||
#
|
||||
# This MUST be done before using the 'install_nghttp2_pyd_1' rule.
|
||||
#
|
||||
copy_headers_and_libs:
|
||||
- mkdir $(INSTALL_HDR)/nghttp2
|
||||
cp --update $(addprefix includes/nghttp2/, nghttp2.h nghttp2ver.h) $(INSTALL_HDR)/nghttp2
|
||||
cp --update nghttp2.dll nghttp2d.dll $(NGHTTP2_PDB_R) $(NGHTTP2_PDB_D) $(INSTALL_BIN)
|
||||
cp --update nghttp2.lib nghttp2d.lib nghttp2_imp.lib nghttp2d_imp.lib $(INSTALL_LIB)
|
||||
@echo
|
||||
|
||||
nghttp2.lib: $(NGHTTP2_OBJ_R)
|
||||
lib -nologo -out:$@ $^
|
||||
@echo
|
||||
|
||||
nghttp2d.lib: $(NGHTTP2_OBJ_D)
|
||||
lib -nologo -out:$@ $^
|
||||
@echo
|
||||
|
||||
nghttp2.dll nghttp2_imp.lib: $(NGHTTP2_OBJ_R) $(OBJ_DIR)/r_nghttp2.res $(OBJ_DIR)/r_nghttp2.def
|
||||
link $(LDFLAGS) -dll -out:nghttp2.dll -implib:nghttp2_imp.lib -def:$(OBJ_DIR)/r_nghttp2.def \
|
||||
$(NGHTTP2_OBJ_R) $(OBJ_DIR)/r_nghttp2.res $(EXT_LIBS)
|
||||
@echo
|
||||
|
||||
nghttp2d.dll nghttp2d_imp.lib: $(NGHTTP2_OBJ_D) $(OBJ_DIR)/d_nghttp2.res $(OBJ_DIR)/d_nghttp2.def
|
||||
link $(LDFLAGS) -dll -out:nghttp2d.dll -implib:nghttp2d_imp.lib -def:$(OBJ_DIR)/d_nghttp2.def \
|
||||
$(NGHTTP2_OBJ_D) $(OBJ_DIR)/d_nghttp2.res $(EXT_LIBS)
|
||||
@echo
|
||||
|
||||
install_nghttp2_pyd_0: ;
|
||||
|
||||
install_nghttp2_pyd_1: $(addprefix ../python/, setup.py.in nghttp2.pyx)
|
||||
cd ../python ; \
|
||||
echo '# $(GENERATED). DO NOT EDIT.' > setup.py ; \
|
||||
sed -e 's/@top_srcdir@/../' \
|
||||
-e 's/@top_builddir@/../' \
|
||||
-e 's/@PACKAGE_VERSION@/$(VERSION)/' setup.py.in >> setup.py ; \
|
||||
cython -v nghttp2.pyx ; \
|
||||
python setup.py install
|
||||
|
||||
clean_nghttp2_pyd_0: ;
|
||||
|
||||
clean_nghttp2_pyd_1:
|
||||
cd ../python ; \
|
||||
rm -f setup.py nghttp2.c ; \
|
||||
rm -fR build/*
|
||||
|
||||
$(OBJ_DIR)/r_%.obj: %.c
|
||||
$(CC) $(CFLAGS_R) $(CFLAGS) -Fo$@ -c $<
|
||||
@echo
|
||||
|
||||
$(OBJ_DIR)/d_%.obj: %.c
|
||||
$(CC) $(CFLAGS_D) $(CFLAGS) -Fo$@ -c $<
|
||||
@echo
|
||||
|
||||
$(OBJ_DIR)/r_nghttp2.res: nghttp2.rc
|
||||
rc -nologo -D_RELEASE -Fo $@ $<
|
||||
@echo
|
||||
|
||||
$(OBJ_DIR)/d_nghttp2.res: nghttp2.rc
|
||||
rc -nologo -D_DEBUG -Fo $@ $<
|
||||
@echo
|
||||
|
||||
includes/nghttp2/nghttp2ver.h: includes/nghttp2/nghttp2ver.h.in
|
||||
sed < includes/nghttp2/nghttp2ver.h.in \
|
||||
-e 's/@PACKAGE_VERSION@/$(VERSION)/g' \
|
||||
-e 's/@PACKAGE_VERSION_NUM@/($(VERSION_NUM))/g' > $@
|
||||
touch --reference=includes/nghttp2/nghttp2ver.h.in $@
|
||||
|
||||
|
||||
define RES_FILE
|
||||
#include <winver.h>
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION $(VER_MAJOR), $(VER_MINOR), $(VER_MICRO), 0
|
||||
PRODUCTVERSION $(VER_MAJOR), $(VER_MINOR), $(VER_MICRO), 0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
FILEOS 0x40004L
|
||||
FILETYPE 0x2L
|
||||
FILESUBTYPE 0x0L
|
||||
#ifdef _DEBUG
|
||||
#define VER_STR "$(VERSION).0 (MSVC debug)"
|
||||
#define DBG "d"
|
||||
FILEFLAGS 0x1L
|
||||
#else
|
||||
#define VER_STR "$(VERSION).0 (MSVC release)"
|
||||
#define DBG ""
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "http://tatsuhiro-t.github.io/nghttp2/"
|
||||
VALUE "FileDescription", "nghttp2; HTTP/2 C library"
|
||||
VALUE "FileVersion", VER_STR
|
||||
VALUE "InternalName", "nghttp2" DBG
|
||||
VALUE "LegalCopyright", "The MIT License"
|
||||
VALUE "LegalTrademarks", ""
|
||||
VALUE "OriginalFilename", "nghttp2" DBG ".dll"
|
||||
VALUE "ProductName", "NGHTTP2."
|
||||
VALUE "ProductVersion", VER_STR
|
||||
VALUE "PrivateBuild", "The privat build of <gvanem@yahoo.no>."
|
||||
VALUE "SpecialBuild", ""
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
||||
endef
|
||||
|
||||
export RES_FILE
|
||||
|
||||
nghttp2.rc: Makefile.MSVC
|
||||
@echo 'Generating $@...'
|
||||
@echo ' /* $(GENERATED). DO NOT EDIT.' > $@
|
||||
@echo ' */' >> $@
|
||||
@echo "$$RES_FILE" >> $@
|
||||
|
||||
$(OBJ_DIR)/r_nghttp2.def: Makefile.MSVC
|
||||
@echo 'Generating $@...'
|
||||
@echo '; $(GENERATED). DO NOT EDIT.' > $@
|
||||
@echo ';' >> $@
|
||||
@echo 'LIBRARY nghttp2.dll' >> $@
|
||||
@echo 'EXPORTS' >> $@
|
||||
nm $(NGHTTP2_OBJ_R) | grep ' T .*_nghttp2' | sed 's/^.* _/ /' >> $@
|
||||
@echo 'NGHTTP2_STATIC_TABLE_LENGTH DATA' >> $@
|
||||
|
||||
$(OBJ_DIR)/d_nghttp2.def: Makefile.MSVC
|
||||
@echo 'Generating $@...'
|
||||
@echo '; $(GENERATED). DO NOT EDIT.' > $@
|
||||
@echo ';' >> $@
|
||||
@echo 'LIBRARY nghttp2d.dll' >> $@
|
||||
@echo 'EXPORTS' >> $@
|
||||
nm $(NGHTTP2_OBJ_D) | grep ' T .*_nghttp2' | sed 's/^.* _/ /' >> $@
|
||||
@echo 'NGHTTP2_STATIC_TABLE_LENGTH DATA' >> $@
|
||||
|
||||
clean:
|
||||
rm -f $(OBJ_DIR)/* nghttp2_imp.exp nghttp2_imp.exp \
|
||||
nghttp2.map nghttp2d.map nghttp2.rc includes/nghttp2/nghttp2ver.h
|
||||
@echo
|
||||
|
||||
vclean realclean: clean clean_nghttp2_pyd_$(USE_CYTHON)
|
||||
rm -f $(TARGETS) nghttp2.pdb nghttp2d.pdb nghttp2_imp.exp nghttp2d_imp.exp .depend.MSVC
|
||||
- rmdir $(OBJ_DIR)
|
||||
|
||||
#
|
||||
# Use gcc to generated the dependencies. No MSVC specific args please!
|
||||
#
|
||||
REPLACE_R = 's/\(.*\)\.o: /\n$$(OBJ_DIR)\/r_\1.obj: /'
|
||||
REPLACE_D = 's/\(.*\)\.o: /\n$$(OBJ_DIR)\/d_\1.obj: /'
|
||||
|
||||
depend: includes/nghttp2/nghttp2ver.h
|
||||
@echo '# $(GENERATED). DO NOT EDIT.' > .depend.MSVC
|
||||
gcc -MM $(CFLAGS) $(NGHTTP2_SRC) >> .depend.tmp
|
||||
@echo '#' >> .depend.MSVC
|
||||
@echo '# Release lib objects:' >> .depend.MSVC
|
||||
sed -e $(REPLACE_R) .depend.tmp >> .depend.MSVC
|
||||
@echo '#' >> .depend.MSVC
|
||||
@echo '# Debug lib objects:' >> .depend.MSVC
|
||||
sed -e $(REPLACE_D) .depend.tmp >> .depend.MSVC
|
||||
rm -f .depend.tmp
|
||||
|
||||
-include .depend.MSVC
|
||||
File diff suppressed because it is too large
Load Diff
@@ -28,8 +28,7 @@
|
||||
|
||||
#include "nghttp2_helper.h"
|
||||
|
||||
void nghttp2_buf_init(nghttp2_buf *buf)
|
||||
{
|
||||
void nghttp2_buf_init(nghttp2_buf *buf) {
|
||||
buf->begin = NULL;
|
||||
buf->end = NULL;
|
||||
buf->pos = NULL;
|
||||
@@ -37,32 +36,34 @@ void nghttp2_buf_init(nghttp2_buf *buf)
|
||||
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);
|
||||
return nghttp2_buf_reserve(buf, initial);
|
||||
return nghttp2_buf_reserve(buf, initial, mem);
|
||||
}
|
||||
|
||||
void nghttp2_buf_free(nghttp2_buf *buf)
|
||||
{
|
||||
free(buf->begin);
|
||||
void nghttp2_buf_free(nghttp2_buf *buf, nghttp2_mem *mem) {
|
||||
if (buf == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
nghttp2_mem_free(mem, buf->begin);
|
||||
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;
|
||||
size_t cap;
|
||||
|
||||
cap = nghttp2_buf_cap(buf);
|
||||
|
||||
if(cap >= new_cap) {
|
||||
if (cap >= new_cap) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
new_cap = nghttp2_max(new_cap, cap * 2);
|
||||
|
||||
ptr = realloc(buf->begin, new_cap);
|
||||
if(ptr == NULL) {
|
||||
ptr = nghttp2_mem_realloc(mem, buf->begin, new_cap);
|
||||
if (ptr == NULL) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
@@ -75,80 +76,67 @@ int nghttp2_buf_reserve(nghttp2_buf *buf, size_t new_cap)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nghttp2_buf_pos_reserve(nghttp2_buf *buf, size_t new_rel_cap)
|
||||
{
|
||||
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)
|
||||
{
|
||||
void nghttp2_buf_reset(nghttp2_buf *buf) {
|
||||
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->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;
|
||||
|
||||
*chain = malloc(sizeof(nghttp2_buf_chain));
|
||||
if(*chain == NULL) {
|
||||
*chain = nghttp2_mem_malloc(mem, sizeof(nghttp2_buf_chain));
|
||||
if (*chain == NULL) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
(*chain)->next = NULL;
|
||||
|
||||
rv = nghttp2_buf_init2(&(*chain)->buf, chunk_length);
|
||||
if(rv != 0) {
|
||||
free(*chain);
|
||||
rv = nghttp2_buf_init2(&(*chain)->buf, chunk_length, mem);
|
||||
if (rv != 0) {
|
||||
nghttp2_mem_free(mem, *chain);
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void buf_chain_del(nghttp2_buf_chain *chain)
|
||||
{
|
||||
nghttp2_buf_free(&chain->buf);
|
||||
free(chain);
|
||||
static void buf_chain_del(nghttp2_buf_chain *chain, nghttp2_mem *mem) {
|
||||
nghttp2_buf_free(&chain->buf, mem);
|
||||
nghttp2_mem_free(mem, chain);
|
||||
}
|
||||
|
||||
int nghttp2_bufs_init(nghttp2_bufs *bufs, size_t chunk_length,
|
||||
size_t max_chunk)
|
||||
{
|
||||
return nghttp2_bufs_init2(bufs, chunk_length, max_chunk, 0);
|
||||
int nghttp2_bufs_init(nghttp2_bufs *bufs, size_t chunk_length, size_t max_chunk,
|
||||
nghttp2_mem *mem) {
|
||||
return nghttp2_bufs_init2(bufs, chunk_length, max_chunk, 0, mem);
|
||||
}
|
||||
|
||||
int nghttp2_bufs_init2(nghttp2_bufs *bufs, size_t chunk_length,
|
||||
size_t max_chunk, size_t offset)
|
||||
{
|
||||
return nghttp2_bufs_init3(bufs, chunk_length, max_chunk, max_chunk, offset);
|
||||
size_t max_chunk, size_t offset, nghttp2_mem *mem) {
|
||||
return nghttp2_bufs_init3(bufs, chunk_length, max_chunk, max_chunk, offset,
|
||||
mem);
|
||||
}
|
||||
|
||||
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;
|
||||
nghttp2_buf_chain *chain;
|
||||
|
||||
if(chunk_keep == 0 || max_chunk < chunk_keep || chunk_length < offset) {
|
||||
if (chunk_keep == 0 || max_chunk < chunk_keep || chunk_length < offset) {
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
rv = buf_chain_new(&chain, chunk_length);
|
||||
if(rv != 0) {
|
||||
rv = buf_chain_new(&chain, chunk_length, mem);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
bufs->mem = mem;
|
||||
bufs->offset = offset;
|
||||
|
||||
bufs->head = chain;
|
||||
@@ -164,25 +152,56 @@ int nghttp2_bufs_init3(nghttp2_bufs *bufs, size_t chunk_length,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nghttp2_bufs_free(nghttp2_bufs *bufs)
|
||||
{
|
||||
int nghttp2_bufs_realloc(nghttp2_bufs *bufs, size_t chunk_length) {
|
||||
int rv;
|
||||
nghttp2_buf_chain *chain;
|
||||
|
||||
if (chunk_length < bufs->offset) {
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
rv = buf_chain_new(&chain, chunk_length, bufs->mem);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nghttp2_bufs_free(bufs);
|
||||
|
||||
bufs->head = chain;
|
||||
bufs->cur = bufs->head;
|
||||
|
||||
nghttp2_buf_shift_right(&bufs->cur->buf, bufs->offset);
|
||||
|
||||
bufs->chunk_length = chunk_length;
|
||||
bufs->chunk_used = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nghttp2_bufs_free(nghttp2_bufs *bufs) {
|
||||
nghttp2_buf_chain *chain, *next_chain;
|
||||
|
||||
for(chain = bufs->head; chain;) {
|
||||
if (bufs == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (chain = bufs->head; chain;) {
|
||||
next_chain = chain->next;
|
||||
|
||||
buf_chain_del(chain);
|
||||
buf_chain_del(chain, bufs->mem);
|
||||
|
||||
chain = next_chain;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
chain = malloc(sizeof(nghttp2_buf_chain));
|
||||
if(chain == NULL) {
|
||||
chain = nghttp2_mem_malloc(mem, sizeof(nghttp2_buf_chain));
|
||||
if (chain == NULL) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
@@ -190,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);
|
||||
|
||||
bufs->mem = mem;
|
||||
bufs->offset = 0;
|
||||
|
||||
bufs->head = chain;
|
||||
@@ -203,17 +223,20 @@ int nghttp2_bufs_wrap_init(nghttp2_bufs *bufs, uint8_t *begin, size_t len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nghttp2_bufs_wrap_free(nghttp2_bufs *bufs)
|
||||
{
|
||||
free(bufs->head);
|
||||
void nghttp2_bufs_wrap_free(nghttp2_bufs *bufs) {
|
||||
if (bufs == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
nghttp2_mem_free(bufs->mem, bufs->head);
|
||||
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;
|
||||
|
||||
for(ci = bufs->cur; ci; ci = ci->next) {
|
||||
if(nghttp2_buf_len(&ci->buf) == 0) {
|
||||
for (ci = bufs->cur; ci; ci = ci->next) {
|
||||
if (nghttp2_buf_len(&ci->buf) == 0) {
|
||||
return;
|
||||
} else {
|
||||
bufs->cur = ci;
|
||||
@@ -221,42 +244,40 @@ 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;
|
||||
ssize_t len;
|
||||
|
||||
len = 0;
|
||||
for(ci = bufs->head; ci; ci = ci->next) {
|
||||
for (ci = bufs->head; ci; ci = ci->next) {
|
||||
len += nghttp2_buf_len(&ci->buf);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t bufs_avail(nghttp2_bufs *bufs)
|
||||
{
|
||||
return (ssize_t)nghttp2_buf_avail(&bufs->cur->buf) +
|
||||
(bufs->chunk_length - bufs->offset) * (bufs->max_chunk - bufs->chunk_used);
|
||||
static ssize_t bufs_avail(nghttp2_bufs *bufs) {
|
||||
return (ssize_t)(nghttp2_buf_avail(&bufs->cur->buf) +
|
||||
(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;
|
||||
nghttp2_buf_chain *chain;
|
||||
|
||||
if(bufs->cur->next) {
|
||||
if (bufs->cur->next) {
|
||||
bufs->cur = bufs->cur->next;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(bufs->max_chunk == bufs->chunk_used) {
|
||||
if (bufs->max_chunk == bufs->chunk_used) {
|
||||
return NGHTTP2_ERR_BUFFER_ERROR;
|
||||
}
|
||||
|
||||
rv = buf_chain_new(&chain, bufs->chunk_length);
|
||||
if(rv != 0) {
|
||||
rv = buf_chain_new(&chain, bufs->chunk_length, bufs->mem);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
@@ -274,26 +295,25 @@ static int bufs_alloc_chain(nghttp2_bufs *bufs)
|
||||
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;
|
||||
size_t nwrite;
|
||||
nghttp2_buf *buf;
|
||||
const uint8_t *p;
|
||||
|
||||
if(bufs_avail(bufs) < (ssize_t)len) {
|
||||
if (bufs_avail(bufs) < (ssize_t)len) {
|
||||
return NGHTTP2_ERR_BUFFER_ERROR;
|
||||
}
|
||||
|
||||
p = data;
|
||||
|
||||
while(len) {
|
||||
while (len) {
|
||||
buf = &bufs->cur->buf;
|
||||
|
||||
nwrite = nghttp2_min((size_t)nghttp2_buf_avail(buf), len);
|
||||
if(nwrite == 0) {
|
||||
if (nwrite == 0) {
|
||||
rv = bufs_alloc_chain(bufs);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
continue;
|
||||
@@ -307,31 +327,29 @@ int nghttp2_bufs_add(nghttp2_bufs *bufs, const void *data, size_t len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bufs_ensure_addb(nghttp2_bufs *bufs)
|
||||
{
|
||||
static int bufs_ensure_addb(nghttp2_bufs *bufs) {
|
||||
int rv;
|
||||
nghttp2_buf *buf;
|
||||
|
||||
buf = &bufs->cur->buf;
|
||||
|
||||
if(nghttp2_buf_avail(buf) > 0) {
|
||||
if (nghttp2_buf_avail(buf) > 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rv = bufs_alloc_chain(bufs);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nghttp2_bufs_addb(nghttp2_bufs *bufs, uint8_t b)
|
||||
{
|
||||
int nghttp2_bufs_addb(nghttp2_bufs *bufs, uint8_t b) {
|
||||
int rv;
|
||||
|
||||
rv = bufs_ensure_addb(bufs);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
@@ -340,12 +358,11 @@ int nghttp2_bufs_addb(nghttp2_bufs *bufs, uint8_t b)
|
||||
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;
|
||||
|
||||
rv = bufs_ensure_addb(bufs);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
@@ -354,12 +371,11 @@ int nghttp2_bufs_addb_hold(nghttp2_bufs *bufs, uint8_t b)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nghttp2_bufs_orb(nghttp2_bufs *bufs, uint8_t b)
|
||||
{
|
||||
int nghttp2_bufs_orb(nghttp2_bufs *bufs, uint8_t b) {
|
||||
int rv;
|
||||
|
||||
rv = bufs_ensure_addb(bufs);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
@@ -368,12 +384,11 @@ int nghttp2_bufs_orb(nghttp2_bufs *bufs, uint8_t b)
|
||||
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;
|
||||
|
||||
rv = bufs_ensure_addb(bufs);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
@@ -382,8 +397,7 @@ int nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b)
|
||||
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;
|
||||
nghttp2_buf_chain *chain;
|
||||
nghttp2_buf *buf;
|
||||
@@ -392,28 +406,27 @@ ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out)
|
||||
|
||||
len = 0;
|
||||
|
||||
for(chain = bufs->head; chain; chain = chain->next) {
|
||||
for (chain = bufs->head; chain; chain = chain->next) {
|
||||
len += nghttp2_buf_len(&chain->buf);
|
||||
}
|
||||
|
||||
if(!len) {
|
||||
if (!len) {
|
||||
res = NULL;
|
||||
} else {
|
||||
res = malloc(len);
|
||||
res = nghttp2_mem_malloc(bufs->mem, len);
|
||||
|
||||
if(res == NULL) {
|
||||
if (res == NULL) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
nghttp2_buf_wrap_init(&resbuf, res, len);
|
||||
|
||||
for(chain = bufs->head; chain; chain = chain->next) {
|
||||
for (chain = bufs->head; chain; chain = chain->next) {
|
||||
buf = &chain->buf;
|
||||
|
||||
if(resbuf.last) {
|
||||
resbuf.last = nghttp2_cpymem(resbuf.last,
|
||||
buf->pos, nghttp2_buf_len(buf));
|
||||
if (resbuf.last) {
|
||||
resbuf.last = nghttp2_cpymem(resbuf.last, buf->pos, nghttp2_buf_len(buf));
|
||||
}
|
||||
|
||||
nghttp2_buf_reset(buf);
|
||||
@@ -427,30 +440,29 @@ ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out)
|
||||
return (ssize_t)len;
|
||||
}
|
||||
|
||||
void nghttp2_bufs_reset(nghttp2_bufs *bufs)
|
||||
{
|
||||
void nghttp2_bufs_reset(nghttp2_bufs *bufs) {
|
||||
nghttp2_buf_chain *chain, *ci;
|
||||
size_t k;
|
||||
|
||||
k = bufs->chunk_keep;
|
||||
|
||||
for(ci = bufs->head; ci; ci = ci->next) {
|
||||
for (ci = bufs->head; ci; ci = ci->next) {
|
||||
nghttp2_buf_reset(&ci->buf);
|
||||
nghttp2_buf_shift_right(&ci->buf, bufs->offset);
|
||||
|
||||
if(--k == 0) {
|
||||
if (--k == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(ci) {
|
||||
if (ci) {
|
||||
chain = ci->next;
|
||||
ci->next = NULL;
|
||||
|
||||
for(ci = chain; ci;) {
|
||||
for (ci = chain; ci;) {
|
||||
chain = ci->next;
|
||||
|
||||
buf_chain_del(ci);
|
||||
buf_chain_del(ci, bufs->mem);
|
||||
|
||||
ci = chain;
|
||||
}
|
||||
@@ -461,17 +473,12 @@ void nghttp2_bufs_reset(nghttp2_bufs *bufs)
|
||||
bufs->cur = bufs->head;
|
||||
}
|
||||
|
||||
int nghttp2_bufs_advance(nghttp2_bufs *bufs)
|
||||
{
|
||||
return bufs_alloc_chain(bufs);
|
||||
}
|
||||
int nghttp2_bufs_advance(nghttp2_bufs *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;
|
||||
|
||||
chain = bufs->cur->next;
|
||||
|
||||
return chain && nghttp2_buf_len(&chain->buf);
|
||||
}
|
||||
|
||||
|
||||
@@ -26,12 +26,13 @@
|
||||
#define NGHTTP2_BUF_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
#include "nghttp2_int.h"
|
||||
#include "nghttp2_mem.h"
|
||||
|
||||
typedef struct {
|
||||
/* This points to the beginning of the buffer. The effective range
|
||||
@@ -58,17 +59,17 @@ typedef struct {
|
||||
#define nghttp2_buf_pos_offset(BUF) ((ssize_t)((BUF)->pos - (BUF)->begin))
|
||||
#define nghttp2_buf_last_offset(BUF) ((ssize_t)((BUF)->last - (BUF)->begin))
|
||||
|
||||
#define nghttp2_buf_shift_right(BUF, AMT) \
|
||||
do { \
|
||||
(BUF)->pos += AMT; \
|
||||
(BUF)->last += AMT; \
|
||||
} while(0)
|
||||
#define nghttp2_buf_shift_right(BUF, AMT) \
|
||||
do { \
|
||||
(BUF)->pos += AMT; \
|
||||
(BUF)->last += AMT; \
|
||||
} while (0)
|
||||
|
||||
#define nghttp2_buf_shift_left(BUF, AMT) \
|
||||
do { \
|
||||
(BUF)->pos -= AMT; \
|
||||
(BUF)->last -= AMT; \
|
||||
} while(0)
|
||||
#define nghttp2_buf_shift_left(BUF, AMT) \
|
||||
do { \
|
||||
(BUF)->pos -= AMT; \
|
||||
(BUF)->last -= AMT; \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Initializes the |buf|. No memory is allocated in this function. Use
|
||||
@@ -76,7 +77,6 @@ typedef struct {
|
||||
*/
|
||||
void nghttp2_buf_init(nghttp2_buf *buf);
|
||||
|
||||
|
||||
/*
|
||||
* Initializes the |buf| and allocates at least |initial| bytes of
|
||||
* memory.
|
||||
@@ -87,12 +87,12 @@ void nghttp2_buf_init(nghttp2_buf *buf);
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* 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|.
|
||||
*/
|
||||
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
|
||||
@@ -105,23 +105,7 @@ void nghttp2_buf_free(nghttp2_buf *buf);
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
int nghttp2_buf_reserve(nghttp2_buf *buf, size_t new_cap);
|
||||
|
||||
/*
|
||||
* 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);
|
||||
int nghttp2_buf_reserve(nghttp2_buf *buf, size_t new_cap, nghttp2_mem *mem);
|
||||
|
||||
/*
|
||||
* Resets pos, last, mark member of |buf| to buf->begin.
|
||||
@@ -152,6 +136,8 @@ typedef struct {
|
||||
nghttp2_buf_chain *head;
|
||||
/* Buffer pointer where write occurs. */
|
||||
nghttp2_buf_chain *cur;
|
||||
/* Memory allocator */
|
||||
nghttp2_mem *mem;
|
||||
/* The buffer capacity of each buf */
|
||||
size_t chunk_length;
|
||||
/* 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
|
||||
* arguments and offset = 0.
|
||||
*/
|
||||
int nghttp2_bufs_init(nghttp2_bufs *bufs, size_t chunk_length,
|
||||
size_t max_chunk);
|
||||
int nghttp2_bufs_init(nghttp2_bufs *bufs, size_t chunk_length, size_t max_chunk,
|
||||
nghttp2_mem *mem);
|
||||
|
||||
/*
|
||||
* This is the same as calling nghttp2_bufs_init3 with the given
|
||||
* arguments and chunk_keep = max_chunk.
|
||||
*/
|
||||
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
|
||||
@@ -200,7 +186,8 @@ int nghttp2_bufs_init2(nghttp2_bufs *bufs, size_t chunk_length,
|
||||
* long.
|
||||
*/
|
||||
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|.
|
||||
@@ -220,7 +207,8 @@ void nghttp2_bufs_free(nghttp2_bufs *bufs);
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* 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
|
||||
@@ -228,6 +216,22 @@ int nghttp2_bufs_wrap_init(nghttp2_bufs *bufs, uint8_t *begin, size_t len);
|
||||
*/
|
||||
void nghttp2_bufs_wrap_free(nghttp2_bufs *bufs);
|
||||
|
||||
/*
|
||||
* Reallocates internal buffer using |chunk_length|. The max_chunk,
|
||||
* chunk_keep and offset do not change. After successful allocation
|
||||
* of new buffer, previous buffers are deallocated without copying
|
||||
* anything into new buffers. chunk_used is reset to 1.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
* NGHTTP2_ERR_INVALID_ARGUMENT
|
||||
* chunk_length < offset
|
||||
*/
|
||||
int nghttp2_bufs_realloc(nghttp2_bufs *bufs, size_t chunk_length);
|
||||
|
||||
/*
|
||||
* Appends the |data| of length |len| to the |bufs|. The write starts
|
||||
* at bufs->cur->buf.last. A new buffers will be allocated to store
|
||||
@@ -264,15 +268,15 @@ int nghttp2_bufs_addb(nghttp2_bufs *bufs, uint8_t b);
|
||||
*/
|
||||
int nghttp2_bufs_addb_hold(nghttp2_bufs *bufs, uint8_t b);
|
||||
|
||||
#define nghttp2_bufs_fast_addb(BUFS, B) \
|
||||
do { \
|
||||
*(BUFS)->cur->buf.last++ = B; \
|
||||
} while(0)
|
||||
#define nghttp2_bufs_fast_addb(BUFS, B) \
|
||||
do { \
|
||||
*(BUFS)->cur->buf.last++ = B; \
|
||||
} while (0)
|
||||
|
||||
#define nghttp2_bufs_fast_addb_hold(BUFS, B) \
|
||||
do { \
|
||||
*(BUFS)->cur->buf.last = B; \
|
||||
} while(0)
|
||||
#define nghttp2_bufs_fast_addb_hold(BUFS, B) \
|
||||
do { \
|
||||
*(BUFS)->cur->buf.last = B; \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Performs bitwise-OR of |b| at bufs->cur->buf.last. A new buffers
|
||||
@@ -294,15 +298,15 @@ int nghttp2_bufs_orb(nghttp2_bufs *bufs, uint8_t b);
|
||||
*/
|
||||
int nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b);
|
||||
|
||||
#define nghttp2_bufs_fast_orb(BUFS, B) \
|
||||
do { \
|
||||
*(BUFS)->cur->buf.last++ |= B; \
|
||||
} while(0)
|
||||
#define nghttp2_bufs_fast_orb(BUFS, B) \
|
||||
do { \
|
||||
*(BUFS)->cur->buf.last++ |= B; \
|
||||
} while (0)
|
||||
|
||||
#define nghttp2_bufs_fast_orb_hold(BUFS, B) \
|
||||
do { \
|
||||
*(BUFS)->cur->buf.last |= B; \
|
||||
} while(0)
|
||||
#define nghttp2_bufs_fast_orb_hold(BUFS, B) \
|
||||
do { \
|
||||
*(BUFS)->cur->buf.last |= B; \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Copies all data stored in |bufs| to the contagious buffer. This
|
||||
@@ -342,10 +346,10 @@ void nghttp2_bufs_reset(nghttp2_bufs *bufs);
|
||||
int nghttp2_bufs_advance(nghttp2_bufs *bufs);
|
||||
|
||||
/* Sets bufs->cur to bufs->head */
|
||||
#define nghttp2_bufs_rewind(BUFS) \
|
||||
do { \
|
||||
(BUFS)->cur = (BUFS)->head; \
|
||||
} while(0)
|
||||
#define nghttp2_bufs_rewind(BUFS) \
|
||||
do { \
|
||||
(BUFS)->cur = (BUFS)->head; \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Move bufs->cur, from the current position, using next member, to
|
||||
|
||||
123
lib/nghttp2_callbacks.c
Normal file
123
lib/nghttp2_callbacks.c
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* 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_callbacks.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
int nghttp2_session_callbacks_new(nghttp2_session_callbacks **callbacks_ptr) {
|
||||
*callbacks_ptr = calloc(1, sizeof(nghttp2_session_callbacks));
|
||||
|
||||
if (*callbacks_ptr == NULL) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nghttp2_session_callbacks_del(nghttp2_session_callbacks *callbacks) {
|
||||
free(callbacks);
|
||||
}
|
||||
|
||||
void nghttp2_session_callbacks_set_send_callback(
|
||||
nghttp2_session_callbacks *cbs, nghttp2_send_callback send_callback) {
|
||||
cbs->send_callback = send_callback;
|
||||
}
|
||||
|
||||
void nghttp2_session_callbacks_set_recv_callback(
|
||||
nghttp2_session_callbacks *cbs, nghttp2_recv_callback recv_callback) {
|
||||
cbs->recv_callback = recv_callback;
|
||||
}
|
||||
|
||||
void nghttp2_session_callbacks_set_on_frame_recv_callback(
|
||||
nghttp2_session_callbacks *cbs,
|
||||
nghttp2_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(
|
||||
nghttp2_session_callbacks *cbs,
|
||||
nghttp2_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(
|
||||
nghttp2_session_callbacks *cbs,
|
||||
nghttp2_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(
|
||||
nghttp2_session_callbacks *cbs,
|
||||
nghttp2_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(
|
||||
nghttp2_session_callbacks *cbs,
|
||||
nghttp2_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(
|
||||
nghttp2_session_callbacks *cbs,
|
||||
nghttp2_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(
|
||||
nghttp2_session_callbacks *cbs,
|
||||
nghttp2_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(
|
||||
nghttp2_session_callbacks *cbs,
|
||||
nghttp2_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(
|
||||
nghttp2_session_callbacks *cbs,
|
||||
nghttp2_on_header_callback on_header_callback) {
|
||||
cbs->on_header_callback = on_header_callback;
|
||||
}
|
||||
|
||||
void nghttp2_session_callbacks_set_select_padding_callback(
|
||||
nghttp2_session_callbacks *cbs,
|
||||
nghttp2_select_padding_callback select_padding_callback) {
|
||||
cbs->select_padding_callback = select_padding_callback;
|
||||
}
|
||||
|
||||
void nghttp2_session_callbacks_set_data_source_read_length_callback(
|
||||
nghttp2_session_callbacks *cbs,
|
||||
nghttp2_data_source_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(
|
||||
nghttp2_session_callbacks *cbs,
|
||||
nghttp2_on_begin_frame_callback on_begin_frame_callback) {
|
||||
cbs->on_begin_frame_callback = on_begin_frame_callback;
|
||||
}
|
||||
111
lib/nghttp2_callbacks.h
Normal file
111
lib/nghttp2_callbacks.h
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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_CALLBACKS_H
|
||||
#define NGHTTP2_CALLBACKS_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
/*
|
||||
* Callback functions.
|
||||
*/
|
||||
struct nghttp2_session_callbacks {
|
||||
/**
|
||||
* Callback function invoked when the session wants to send data to
|
||||
* the remote peer. This callback is not necessary if the
|
||||
* application uses solely `nghttp2_session_mem_send()` to serialize
|
||||
* data to transmit.
|
||||
*/
|
||||
nghttp2_send_callback send_callback;
|
||||
/**
|
||||
* Callback function invoked when the session wants to receive data
|
||||
* from the remote peer. This callback is not necessary if the
|
||||
* application uses solely `nghttp2_session_mem_recv()` to process
|
||||
* received data.
|
||||
*/
|
||||
nghttp2_recv_callback recv_callback;
|
||||
/**
|
||||
* Callback function invoked by `nghttp2_session_recv()` when a
|
||||
* frame is received.
|
||||
*/
|
||||
nghttp2_on_frame_recv_callback on_frame_recv_callback;
|
||||
/**
|
||||
* Callback function invoked by `nghttp2_session_recv()` when an
|
||||
* invalid non-DATA frame is received.
|
||||
*/
|
||||
nghttp2_on_invalid_frame_recv_callback on_invalid_frame_recv_callback;
|
||||
/**
|
||||
* Callback function invoked when a chunk of data in DATA frame is
|
||||
* received.
|
||||
*/
|
||||
nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback;
|
||||
/**
|
||||
* Callback function invoked before a non-DATA frame is sent.
|
||||
*/
|
||||
nghttp2_before_frame_send_callback before_frame_send_callback;
|
||||
/**
|
||||
* Callback function invoked after a frame is sent.
|
||||
*/
|
||||
nghttp2_on_frame_send_callback on_frame_send_callback;
|
||||
/**
|
||||
* The callback function invoked when a non-DATA frame is not sent
|
||||
* because of an error.
|
||||
*/
|
||||
nghttp2_on_frame_not_send_callback on_frame_not_send_callback;
|
||||
/**
|
||||
* Callback function invoked when the stream is closed.
|
||||
*/
|
||||
nghttp2_on_stream_close_callback on_stream_close_callback;
|
||||
/**
|
||||
* Callback function invoked when the reception of header block in
|
||||
* HEADERS or PUSH_PROMISE is started.
|
||||
*/
|
||||
nghttp2_on_begin_headers_callback on_begin_headers_callback;
|
||||
/**
|
||||
* Callback function invoked when a header name/value pair is
|
||||
* received.
|
||||
*/
|
||||
nghttp2_on_header_callback on_header_callback;
|
||||
/**
|
||||
* Callback function invoked when the library asks application how
|
||||
* many padding bytes are required for the transmission of the given
|
||||
* frame.
|
||||
*/
|
||||
nghttp2_select_padding_callback select_padding_callback;
|
||||
/**
|
||||
* The callback function used to determine the length allowed in
|
||||
* `nghttp2_data_source_read_callback()`
|
||||
*/
|
||||
nghttp2_data_source_read_length_callback read_length_callback;
|
||||
/**
|
||||
* Sets callback function invoked when a frame header is received.
|
||||
*/
|
||||
nghttp2_on_begin_frame_callback on_begin_frame_callback;
|
||||
};
|
||||
|
||||
#endif /* NGHTTP2_CALLBACKS_H */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -26,14 +26,13 @@
|
||||
#define NGHTTP2_FRAME_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
#include "nghttp2_hd.h"
|
||||
#include "nghttp2_buf.h"
|
||||
|
||||
#define NGHTTP2_FRAME_LENGTH_MASK ((1 << 14) - 1)
|
||||
#define NGHTTP2_STREAM_ID_MASK ((1u << 31) - 1)
|
||||
#define NGHTTP2_PRI_GROUP_ID_MASK ((1u << 31) - 1)
|
||||
#define NGHTTP2_PRIORITY_MASK ((1u << 31) - 1)
|
||||
@@ -41,23 +40,28 @@
|
||||
#define NGHTTP2_SETTINGS_ID_MASK ((1 << 24) - 1)
|
||||
|
||||
/* The number of bytes of frame header. */
|
||||
#define NGHTTP2_FRAME_HDLEN 8
|
||||
#define NGHTTP2_FRAME_HDLEN 9
|
||||
|
||||
#define NGHTTP2_MAX_PAYLOADLEN 16383
|
||||
#define NGHTTP2_MAX_FRAME_SIZE_MAX ((1 << 24) - 1)
|
||||
#define NGHTTP2_MAX_FRAME_SIZE_MIN (1 << 14)
|
||||
|
||||
#define NGHTTP2_MAX_PAYLOADLEN 16384
|
||||
/* The one frame buffer length for tranmission. We may use several of
|
||||
them to support CONTINUATION. To account for Pad Length field, we
|
||||
allocate extra 1 byte, which saves extra large memcopying. */
|
||||
#define NGHTTP2_FRAMEBUF_CHUNKLEN \
|
||||
#define NGHTTP2_FRAMEBUF_CHUNKLEN \
|
||||
(NGHTTP2_FRAME_HDLEN + 1 + NGHTTP2_MAX_PAYLOADLEN)
|
||||
|
||||
/* Number of inbound buffer */
|
||||
#define NGHTTP2_FRAMEBUF_MAX_NUM 5
|
||||
|
||||
/* The maximum length of DATA frame payload. */
|
||||
#define NGHTTP2_DATA_PAYLOADLEN 4096
|
||||
/* 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 */
|
||||
#define NGHTTP2_DATA_PAYLOADLEN \
|
||||
((NGHTTP2_MAX_FRAME_SIZE_MIN) - (NGHTTP2_FRAME_HDLEN))
|
||||
|
||||
/* Maximum headers payload length, calculated in compressed form.
|
||||
This applies to both transmission and reception. */
|
||||
This applies to transmission only. */
|
||||
#define NGHTTP2_MAX_HEADERSLEN 65536
|
||||
|
||||
/* The number of bytes for each SETTINGS entry */
|
||||
@@ -69,57 +73,22 @@
|
||||
/* Length of priority related fields in HEADERS/PRIORITY frames */
|
||||
#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
|
||||
|
||||
/* Category of frames. */
|
||||
typedef enum {
|
||||
/* non-DATA frame */
|
||||
NGHTTP2_CAT_CTRL,
|
||||
/* DATA frame */
|
||||
NGHTTP2_CAT_DATA
|
||||
} nghttp2_frame_category;
|
||||
/* Maximum length of padding in bytes. */
|
||||
#define NGHTTP2_MAX_PADLEN 256
|
||||
|
||||
/* Union of extension frame payload */
|
||||
typedef union {
|
||||
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);
|
||||
typedef union { nghttp2_ext_altsvc altsvc; } nghttp2_ext_frame_payload;
|
||||
|
||||
void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd);
|
||||
|
||||
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);
|
||||
|
||||
/**
|
||||
* Initializes frame header |hd| with given parameters. Reserved bit
|
||||
* is set to 0.
|
||||
*/
|
||||
void nghttp2_frame_hd_init(nghttp2_frame_hd *hd, size_t length, uint8_t type,
|
||||
uint8_t flags, int32_t stream_id);
|
||||
|
||||
/**
|
||||
* Returns the number of priority field depending on the |flags|. If
|
||||
@@ -142,8 +111,7 @@ void nghttp2_frame_pack_priority_spec(uint8_t *buf,
|
||||
* assumes the |payload| contains whole priority specification.
|
||||
*/
|
||||
void nghttp2_frame_unpack_priority_spec(nghttp2_priority_spec *pri_spec,
|
||||
uint8_t flags,
|
||||
const uint8_t *payload,
|
||||
uint8_t flags, const uint8_t *payload,
|
||||
size_t payloadlen);
|
||||
|
||||
/*
|
||||
@@ -172,8 +140,7 @@ size_t nghttp2_frame_headers_payload_nv_offset(nghttp2_headers *frame);
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
int nghttp2_frame_pack_headers(nghttp2_bufs *bufs,
|
||||
nghttp2_headers *frame,
|
||||
int nghttp2_frame_pack_headers(nghttp2_bufs *bufs, nghttp2_headers *frame,
|
||||
nghttp2_hd_deflater *deflater);
|
||||
|
||||
/*
|
||||
@@ -196,8 +163,7 @@ int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame,
|
||||
*
|
||||
* This function always succeeds and returns 0.
|
||||
*/
|
||||
int nghttp2_frame_pack_priority(nghttp2_bufs *bufs,
|
||||
nghttp2_priority *frame);
|
||||
int nghttp2_frame_pack_priority(nghttp2_bufs *bufs, nghttp2_priority *frame);
|
||||
|
||||
/*
|
||||
* Unpacks PRIORITY wire format into |frame|.
|
||||
@@ -265,7 +231,7 @@ void nghttp2_frame_unpack_settings_entry(nghttp2_settings_entry *iv,
|
||||
*/
|
||||
int nghttp2_frame_unpack_settings_payload(nghttp2_settings *frame,
|
||||
nghttp2_settings_entry *iv,
|
||||
size_t niv);
|
||||
size_t niv, nghttp2_mem *mem);
|
||||
|
||||
/*
|
||||
* Unpacks SETTINGS payload into |*iv_ptr|. The number of entries are
|
||||
@@ -282,7 +248,7 @@ int nghttp2_frame_unpack_settings_payload(nghttp2_settings *frame,
|
||||
int nghttp2_frame_unpack_settings_payload2(nghttp2_settings_entry **iv_ptr,
|
||||
size_t *niv_ptr,
|
||||
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
|
||||
@@ -385,7 +351,7 @@ void nghttp2_frame_unpack_goaway_payload(nghttp2_goaway *frame,
|
||||
*/
|
||||
int nghttp2_frame_unpack_goaway_payload2(nghttp2_goaway *frame,
|
||||
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
|
||||
@@ -406,72 +372,25 @@ void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame,
|
||||
const uint8_t *payload,
|
||||
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
|
||||
* ownership of |nva|, so caller must not free it. If |stream_id| is
|
||||
* not assigned yet, it must be -1.
|
||||
*/
|
||||
void nghttp2_frame_headers_init(nghttp2_headers *frame,
|
||||
uint8_t flags, int32_t stream_id,
|
||||
nghttp2_headers_category cat,
|
||||
void nghttp2_frame_headers_init(nghttp2_headers *frame, uint8_t flags,
|
||||
int32_t stream_id, nghttp2_headers_category cat,
|
||||
const nghttp2_priority_spec *pri_spec,
|
||||
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,
|
||||
const nghttp2_priority_spec *pri_spec);
|
||||
|
||||
void nghttp2_frame_priority_free(nghttp2_priority *frame);
|
||||
|
||||
void nghttp2_frame_rst_stream_init(nghttp2_rst_stream *frame,
|
||||
int32_t stream_id,
|
||||
nghttp2_error_code error_code);
|
||||
void nghttp2_frame_rst_stream_init(nghttp2_rst_stream *frame, int32_t stream_id,
|
||||
uint32_t error_code);
|
||||
|
||||
void nghttp2_frame_rst_stream_free(nghttp2_rst_stream *frame);
|
||||
|
||||
@@ -479,12 +398,13 @@ void nghttp2_frame_rst_stream_free(nghttp2_rst_stream *frame);
|
||||
* Initializes PUSH_PROMISE frame |frame| with given values. |frame|
|
||||
* takes ownership of |nva|, so caller must not free it.
|
||||
*/
|
||||
void nghttp2_frame_push_promise_init(nghttp2_push_promise *frame,
|
||||
uint8_t flags, int32_t stream_id,
|
||||
void nghttp2_frame_push_promise_init(nghttp2_push_promise *frame, uint8_t flags,
|
||||
int32_t stream_id,
|
||||
int32_t promised_stream_id,
|
||||
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
|
||||
@@ -494,7 +414,7 @@ void nghttp2_frame_push_promise_free(nghttp2_push_promise *frame);
|
||||
void nghttp2_frame_settings_init(nghttp2_settings *frame, uint8_t flags,
|
||||
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
|
||||
@@ -513,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.
|
||||
*/
|
||||
void nghttp2_frame_goaway_init(nghttp2_goaway *frame, int32_t last_stream_id,
|
||||
nghttp2_error_code error_code,
|
||||
uint8_t *opaque_data, size_t opaque_data_len);
|
||||
uint32_t error_code, uint8_t *opaque_data,
|
||||
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,
|
||||
uint8_t flags,
|
||||
int32_t stream_id,
|
||||
uint8_t flags, int32_t stream_id,
|
||||
int32_t window_size_increment);
|
||||
|
||||
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
|
||||
* padding length is given in the |padlen|. The returned value does
|
||||
@@ -554,20 +451,18 @@ void nghttp2_frame_data_init(nghttp2_data *frame, nghttp2_private_data *pdata);
|
||||
*/
|
||||
size_t nghttp2_frame_trail_padlen(nghttp2_frame *frame, size_t padlen);
|
||||
|
||||
void nghttp2_frame_private_data_init(nghttp2_private_data *frame,
|
||||
uint8_t flags,
|
||||
int32_t stream_id,
|
||||
const nghttp2_data_provider *data_prd);
|
||||
void nghttp2_frame_data_init(nghttp2_data *frame, uint8_t flags,
|
||||
int32_t stream_id);
|
||||
|
||||
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
|
||||
* entries in |iv|. This function returns the pointer to the copy if
|
||||
* it succeeds, or NULL.
|
||||
*/
|
||||
nghttp2_settings_entry* nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv,
|
||||
size_t niv);
|
||||
nghttp2_settings_entry *nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv,
|
||||
size_t niv, nghttp2_mem *mem);
|
||||
|
||||
/*
|
||||
* Sorts the |nva| in ascending order of name and value. If names are
|
||||
@@ -588,8 +483,8 @@ void nghttp2_nv_array_sort(nghttp2_nv *nva, size_t nvlen);
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr,
|
||||
const nghttp2_nv *nva, size_t nvlen);
|
||||
int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, const nghttp2_nv *nva,
|
||||
size_t nvlen, nghttp2_mem *mem);
|
||||
|
||||
/*
|
||||
* Returns nonzero if the name/value pair |a| equals to |b|. The name
|
||||
@@ -601,7 +496,7 @@ int nghttp2_nv_equal(const nghttp2_nv *a, const nghttp2_nv *b);
|
||||
/*
|
||||
* 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
|
||||
|
||||
1658
lib/nghttp2_hd.c
1658
lib/nghttp2_hd.c
File diff suppressed because it is too large
Load Diff
@@ -26,13 +26,14 @@
|
||||
#define NGHTTP2_HD_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
#include "nghttp2_hd_huffman.h"
|
||||
#include "nghttp2_buf.h"
|
||||
#include "nghttp2_mem.h"
|
||||
|
||||
#define NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE NGHTTP2_DEFAULT_HEADER_TABLE_SIZE
|
||||
#define NGHTTP2_HD_ENTRY_OVERHEAD 32
|
||||
@@ -47,10 +48,8 @@
|
||||
encoder only uses the memory up to this value. */
|
||||
#define NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE (1 << 12)
|
||||
|
||||
typedef enum {
|
||||
NGHTTP2_HD_ROLE_DEFLATE,
|
||||
NGHTTP2_HD_ROLE_INFLATE
|
||||
} nghttp2_hd_role;
|
||||
/* Exported for unit test */
|
||||
extern const size_t NGHTTP2_STATIC_TABLE_LENGTH;
|
||||
|
||||
typedef enum {
|
||||
NGHTTP2_HD_FLAG_NONE = 0,
|
||||
@@ -58,18 +57,12 @@ typedef enum {
|
||||
NGHTTP2_HD_FLAG_NAME_ALLOC = 1,
|
||||
/* Indicates value was dynamically allocated and must be freed */
|
||||
NGHTTP2_HD_FLAG_VALUE_ALLOC = 1 << 1,
|
||||
/* Indicates that the entry is in the reference set */
|
||||
NGHTTP2_HD_FLAG_REFSET = 1 << 2,
|
||||
/* Indicates that the entry is emitted in the current header
|
||||
processing. */
|
||||
NGHTTP2_HD_FLAG_EMIT = 1 << 3,
|
||||
NGHTTP2_HD_FLAG_IMPLICIT_EMIT = 1 << 4,
|
||||
/* Indicates that the name was gifted to the entry and no copying
|
||||
necessary. */
|
||||
NGHTTP2_HD_FLAG_NAME_GIFT = 1 << 5,
|
||||
NGHTTP2_HD_FLAG_NAME_GIFT = 1 << 2,
|
||||
/* Indicates that the value was gifted to the entry and no copying
|
||||
necessary. */
|
||||
NGHTTP2_HD_FLAG_VALUE_GIFT = 1 << 6
|
||||
NGHTTP2_HD_FLAG_VALUE_GIFT = 1 << 3
|
||||
} nghttp2_hd_flags;
|
||||
|
||||
typedef struct {
|
||||
@@ -102,7 +95,6 @@ typedef enum {
|
||||
|
||||
typedef enum {
|
||||
NGHTTP2_HD_STATE_OPCODE,
|
||||
NGHTTP2_HD_STATE_CLEAR_REFSET,
|
||||
NGHTTP2_HD_STATE_READ_TABLE_SIZE,
|
||||
NGHTTP2_HD_STATE_READ_INDEX,
|
||||
NGHTTP2_HD_STATE_NEWNAME_CHECK_NAMELEN,
|
||||
@@ -118,14 +110,14 @@ typedef enum {
|
||||
typedef struct {
|
||||
/* dynamic header table */
|
||||
nghttp2_hd_ringbuf hd_table;
|
||||
/* Memory allocator */
|
||||
nghttp2_mem *mem;
|
||||
/* Abstract buffer size of hd_table as described in the spec. This
|
||||
is the sum of length of name/value in hd_table +
|
||||
NGHTTP2_HD_ENTRY_OVERHEAD bytes overhead per each entry. */
|
||||
size_t hd_table_bufsize;
|
||||
/* The effective header table size. */
|
||||
size_t hd_table_bufsize_max;
|
||||
/* Role of this context; deflate or infalte */
|
||||
nghttp2_hd_role role;
|
||||
/* If inflate/deflate error occurred, this value is set to 1 and
|
||||
further invocation of inflate/deflate will fail with
|
||||
NGHTTP2_ERR_HEADER_COMP. */
|
||||
@@ -136,9 +128,8 @@ struct nghttp2_hd_deflater {
|
||||
nghttp2_hd_context ctx;
|
||||
/* The upper limit of the header table size the deflater accepts. */
|
||||
size_t deflate_hd_table_bufsize_max;
|
||||
/* Set to this nonzero to clear reference set on each deflation each
|
||||
time. */
|
||||
uint8_t no_refset;
|
||||
/* Minimum header table size notified in the next context update */
|
||||
size_t min_hd_table_bufsize_max;
|
||||
/* If nonzero, send header table size using encoding context update
|
||||
in the next deflate process */
|
||||
uint8_t notify_table_size_change;
|
||||
@@ -157,22 +148,18 @@ struct nghttp2_hd_inflater {
|
||||
/* Pointer to the name/value pair buffer which is used in the
|
||||
current header emission. */
|
||||
uint8_t *nv_keep;
|
||||
/* Pointers to the name/value pair which is referred as indexed
|
||||
name. This entry must be in header table. */
|
||||
nghttp2_hd_entry *ent_name;
|
||||
/* The number of bytes to read */
|
||||
ssize_t left;
|
||||
size_t left;
|
||||
/* The index in indexed repr or indexed name */
|
||||
size_t index;
|
||||
/* The index of header table to toggle off the entry from reference
|
||||
set at the end of decompression. */
|
||||
size_t end_headers_index;
|
||||
/* The length of new name encoded in literal. For huffman encoded
|
||||
string, this is the length after it is decoded. */
|
||||
size_t newnamelen;
|
||||
/* The maximum header table size the inflater supports. This is the
|
||||
same value transmitted in SETTINGS_HEADER_TABLE_SIZE */
|
||||
size_t settings_hd_table_bufsize_max;
|
||||
/* The number of next shift to decode integer */
|
||||
size_t shift;
|
||||
nghttp2_hd_opcode opcode;
|
||||
nghttp2_hd_inflate_state state;
|
||||
/* nonzero if string is huffman encoded */
|
||||
@@ -189,7 +176,9 @@ struct nghttp2_hd_inflater {
|
||||
* set in the |flags|, the content pointed by the |name| with length
|
||||
* |namelen| is copied. Likewise, if NGHTTP2_HD_FLAG_VALUE_ALLOC bit
|
||||
* set in the |flags|, the content pointed by the |value| with length
|
||||
* |valuelen| is copied.
|
||||
* |valuelen| is copied. The |name_hash| and |value_hash| are hash
|
||||
* value for |name| and |value| respectively. The hash function is
|
||||
* defined in nghttp2_hd.c.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
@@ -197,11 +186,12 @@ struct nghttp2_hd_inflater {
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t flags,
|
||||
uint8_t *name, size_t namelen,
|
||||
uint8_t *value, size_t valuelen);
|
||||
int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t flags, uint8_t *name,
|
||||
size_t namelen, uint8_t *value, size_t valuelen,
|
||||
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.
|
||||
@@ -217,7 +207,7 @@ void nghttp2_hd_entry_free(nghttp2_hd_entry *ent);
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* 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.
|
||||
@@ -233,7 +223,8 @@ int nghttp2_hd_deflate_init(nghttp2_hd_deflater *deflater);
|
||||
* Out of memory.
|
||||
*/
|
||||
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|.
|
||||
@@ -261,8 +252,8 @@ void nghttp2_hd_deflate_free(nghttp2_hd_deflater *deflater);
|
||||
* Out of buffer space.
|
||||
*/
|
||||
int nghttp2_hd_deflate_hd_bufs(nghttp2_hd_deflater *deflater,
|
||||
nghttp2_bufs *bufs,
|
||||
const nghttp2_nv *nva, size_t nvlen);
|
||||
nghttp2_bufs *bufs, const nghttp2_nv *nva,
|
||||
size_t nvlen);
|
||||
|
||||
/*
|
||||
* Initializes |inflater| for inflating name/values pairs.
|
||||
@@ -273,7 +264,7 @@ int nghttp2_hd_deflate_hd_bufs(nghttp2_hd_deflater *deflater,
|
||||
* :enum:`NGHTTP2_ERR_NOMEM`
|
||||
* 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|.
|
||||
@@ -292,9 +283,14 @@ int nghttp2_hd_emit_newname_block(nghttp2_bufs *bufs, nghttp2_nv *nv,
|
||||
int nghttp2_hd_emit_table_size(nghttp2_bufs *bufs, size_t table_size);
|
||||
|
||||
/* For unittesting purpose */
|
||||
nghttp2_hd_entry* nghttp2_hd_table_get(nghttp2_hd_context *context,
|
||||
nghttp2_hd_entry *nghttp2_hd_table_get(nghttp2_hd_context *context,
|
||||
size_t index);
|
||||
|
||||
/* For unittesting purpose */
|
||||
ssize_t nghttp2_hd_decode_length(uint32_t *res, size_t *shift_ptr, int *final,
|
||||
uint32_t initial, size_t shift, uint8_t *in,
|
||||
uint8_t *last, size_t prefix);
|
||||
|
||||
/* Huffman encoding/decoding functions */
|
||||
|
||||
/*
|
||||
@@ -318,8 +314,8 @@ size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len);
|
||||
* NGHTTP2_ERR_BUFFER_ERROR
|
||||
* Out of buffer space.
|
||||
*/
|
||||
int nghttp2_hd_huff_encode(nghttp2_bufs *bufs,
|
||||
const uint8_t *src, size_t srclen);
|
||||
int nghttp2_hd_huff_encode(nghttp2_bufs *bufs, const uint8_t *src,
|
||||
size_t srclen);
|
||||
|
||||
void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx);
|
||||
|
||||
@@ -347,7 +343,7 @@ void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx);
|
||||
* Decoding process has failed.
|
||||
*/
|
||||
ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx,
|
||||
nghttp2_bufs *bufs,
|
||||
const uint8_t *src, size_t srclen, int final);
|
||||
nghttp2_bufs *bufs, const uint8_t *src,
|
||||
size_t srclen, int final);
|
||||
|
||||
#endif /* NGHTTP2_HD_H */
|
||||
|
||||
@@ -41,78 +41,90 @@ extern const nghttp2_huff_decode huff_decode_table[][16];
|
||||
* unfilled bits in the pointed location is returned.
|
||||
*/
|
||||
static ssize_t huff_encode_sym(nghttp2_bufs *bufs, size_t *avail_ptr,
|
||||
size_t rembits,
|
||||
const nghttp2_huff_sym *sym)
|
||||
{
|
||||
size_t rembits, const nghttp2_huff_sym *sym) {
|
||||
int rv;
|
||||
size_t nbits = sym->nbits;
|
||||
uint32_t nbits = sym->nbits;
|
||||
uint32_t code = sym->code;
|
||||
|
||||
for(;;) {
|
||||
if(rembits > nbits) {
|
||||
if(*avail_ptr) {
|
||||
nghttp2_bufs_fast_orb_hold(bufs, sym->code << (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);
|
||||
}
|
||||
|
||||
rembits -= nbits;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if(*avail_ptr) {
|
||||
nghttp2_bufs_fast_orb(bufs, sym->code >> (nbits - rembits));
|
||||
--*avail_ptr;
|
||||
} else {
|
||||
rv = nghttp2_bufs_orb(bufs, sym->code >> (nbits - rembits));
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
*avail_ptr = nghttp2_bufs_cur_avail(bufs);
|
||||
}
|
||||
|
||||
nbits -= rembits;
|
||||
rembits = 8;
|
||||
|
||||
if(nbits == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(*avail_ptr) {
|
||||
nghttp2_bufs_fast_addb_hold(bufs, 0);
|
||||
} else {
|
||||
rv = nghttp2_bufs_addb_hold(bufs, 0);
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
*avail_ptr = nghttp2_bufs_cur_avail(bufs);
|
||||
}
|
||||
/* We assume that sym->nbits <= 32 */
|
||||
if (rembits > nbits) {
|
||||
nghttp2_bufs_fast_orb_hold(bufs, code << (rembits - nbits));
|
||||
return (ssize_t)(rembits - nbits);
|
||||
}
|
||||
return (ssize_t)rembits;
|
||||
|
||||
if (rembits == nbits) {
|
||||
nghttp2_bufs_fast_orb(bufs, code);
|
||||
--*avail_ptr;
|
||||
return 8;
|
||||
}
|
||||
|
||||
nghttp2_bufs_fast_orb(bufs, code >> (nbits - rembits));
|
||||
--*avail_ptr;
|
||||
|
||||
nbits -= rembits;
|
||||
if (nbits & 0x7) {
|
||||
/* align code to MSB byte boundary */
|
||||
code <<= 8 - (nbits & 0x7);
|
||||
}
|
||||
|
||||
/* we lose at most 3 bytes, but it is not critical in practice */
|
||||
if (*avail_ptr < (nbits + 7) / 8) {
|
||||
rv = nghttp2_bufs_advance(bufs);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
*avail_ptr = nghttp2_bufs_cur_avail(bufs);
|
||||
/* we assume that we at least 3 buffer space available */
|
||||
assert(*avail_ptr >= 3);
|
||||
}
|
||||
|
||||
/* 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 nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len) {
|
||||
size_t i;
|
||||
size_t nbits = 0;
|
||||
|
||||
for(i = 0; i < len; ++i) {
|
||||
for (i = 0; i < len; ++i) {
|
||||
nbits += huff_sym_table[src[i]].nbits;
|
||||
}
|
||||
/* pad the prefix of EOS (256) */
|
||||
return (nbits + 7) / 8;
|
||||
}
|
||||
|
||||
int nghttp2_hd_huff_encode(nghttp2_bufs *bufs,
|
||||
const uint8_t *src, size_t srclen)
|
||||
{
|
||||
int nghttp2_hd_huff_encode(nghttp2_bufs *bufs, const uint8_t *src,
|
||||
size_t srclen) {
|
||||
int rv;
|
||||
ssize_t rembits = 8;
|
||||
size_t i;
|
||||
@@ -120,52 +132,45 @@ int nghttp2_hd_huff_encode(nghttp2_bufs *bufs,
|
||||
|
||||
avail = nghttp2_bufs_cur_avail(bufs);
|
||||
|
||||
for(i = 0; i < srclen; ++i) {
|
||||
for (i = 0; i < srclen; ++i) {
|
||||
const nghttp2_huff_sym *sym = &huff_sym_table[src[i]];
|
||||
if(rembits == 8) {
|
||||
if(avail) {
|
||||
if (rembits == 8) {
|
||||
if (avail) {
|
||||
nghttp2_bufs_fast_addb_hold(bufs, 0);
|
||||
} else {
|
||||
rv = nghttp2_bufs_addb_hold(bufs, 0);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
avail = nghttp2_bufs_cur_avail(bufs);
|
||||
}
|
||||
}
|
||||
rembits = huff_encode_sym(bufs, &avail, rembits, sym);
|
||||
if(rembits < 0) {
|
||||
if (rembits < 0) {
|
||||
return (int)rembits;
|
||||
}
|
||||
}
|
||||
/* 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];
|
||||
|
||||
assert(avail);
|
||||
/* Caution we no longer adjust avail here */
|
||||
if(avail) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
nghttp2_bufs_fast_orb(bufs, sym->code >> (sym->nbits - rembits));
|
||||
}
|
||||
|
||||
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->accept = 1;
|
||||
}
|
||||
|
||||
ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx,
|
||||
nghttp2_bufs *bufs,
|
||||
const uint8_t *src, size_t srclen, int final)
|
||||
{
|
||||
nghttp2_bufs *bufs, const uint8_t *src,
|
||||
size_t srclen, int final) {
|
||||
size_t i, j;
|
||||
int rv;
|
||||
size_t avail;
|
||||
@@ -174,22 +179,22 @@ ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx,
|
||||
|
||||
/* We use the decoding algorithm described in
|
||||
http://graphics.ics.uci.edu/pub/Prefix.pdf */
|
||||
for(i = 0; i < srclen; ++i) {
|
||||
for (i = 0; i < srclen; ++i) {
|
||||
uint8_t in = src[i] >> 4;
|
||||
for(j = 0; j < 2; ++j) {
|
||||
for (j = 0; j < 2; ++j) {
|
||||
const nghttp2_huff_decode *t;
|
||||
|
||||
t = &huff_decode_table[ctx->state][in];
|
||||
if(t->flags & NGHTTP2_HUFF_FAIL) {
|
||||
if (t->flags & NGHTTP2_HUFF_FAIL) {
|
||||
return NGHTTP2_ERR_HEADER_COMP;
|
||||
}
|
||||
if(t->flags & NGHTTP2_HUFF_SYM) {
|
||||
if(avail) {
|
||||
if (t->flags & NGHTTP2_HUFF_SYM) {
|
||||
if (avail) {
|
||||
nghttp2_bufs_fast_addb(bufs, t->sym);
|
||||
--avail;
|
||||
} else {
|
||||
rv = nghttp2_bufs_addb(bufs, t->sym);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
avail = nghttp2_bufs_cur_avail(bufs);
|
||||
@@ -200,7 +205,7 @@ ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx,
|
||||
in = src[i] & 0xf;
|
||||
}
|
||||
}
|
||||
if(final && !ctx->accept) {
|
||||
if (final && !ctx->accept) {
|
||||
return NGHTTP2_ERR_HEADER_COMP;
|
||||
}
|
||||
return (ssize_t)i;
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#define NGHTTP2_HD_HUFFMAN_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -29,140 +29,212 @@
|
||||
|
||||
#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);
|
||||
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);
|
||||
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;
|
||||
memcpy(&n, data, sizeof(uint16_t));
|
||||
return ntohs(n);
|
||||
}
|
||||
|
||||
uint32_t nghttp2_get_uint32(const uint8_t *data)
|
||||
{
|
||||
uint32_t nghttp2_get_uint32(const uint8_t *data) {
|
||||
uint32_t n;
|
||||
memcpy(&n, data, sizeof(uint32_t));
|
||||
return ntohl(n);
|
||||
}
|
||||
|
||||
int nghttp2_reserve_buffer(uint8_t **buf_ptr, size_t *buflen_ptr,
|
||||
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, nghttp2_mem *mem) {
|
||||
void *dest;
|
||||
|
||||
void* nghttp2_memdup(const void* src, size_t n)
|
||||
{
|
||||
void* dest;
|
||||
|
||||
if(n == 0) {
|
||||
if (n == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dest = malloc(n);
|
||||
if(dest == NULL) {
|
||||
dest = nghttp2_mem_malloc(mem, n);
|
||||
if (dest == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
memcpy(dest, src, n);
|
||||
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;
|
||||
for(i = 0; i < len; ++i) {
|
||||
if('A' <= s[i] && s[i] <= 'Z') {
|
||||
s[i] += 'a'-'A';
|
||||
}
|
||||
for (i = 0; i < len; ++i) {
|
||||
s[i] = DOWNCASE_TBL[s[i]];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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,
|
||||
int32_t *recv_window_size_ptr,
|
||||
int32_t *recv_reduction_ptr,
|
||||
int32_t *delta_ptr)
|
||||
{
|
||||
if(*delta_ptr > 0) {
|
||||
int32_t *delta_ptr) {
|
||||
if (*delta_ptr > 0) {
|
||||
int32_t recv_reduction_delta;
|
||||
int32_t delta;
|
||||
int32_t new_recv_window_size =
|
||||
nghttp2_max(0, *recv_window_size_ptr) - *delta_ptr;
|
||||
if(new_recv_window_size < 0) {
|
||||
/* The delta size is strictly more than received bytes. Increase
|
||||
local_window_size by that difference. */
|
||||
int32_t recv_reduction_diff;
|
||||
if(*local_window_size_ptr >
|
||||
NGHTTP2_MAX_WINDOW_SIZE + new_recv_window_size) {
|
||||
return NGHTTP2_ERR_FLOW_CONTROL;
|
||||
}
|
||||
*local_window_size_ptr -= new_recv_window_size;
|
||||
/* If there is recv_reduction due to earlier window_size
|
||||
reduction, we have to adjust it too. */
|
||||
recv_reduction_diff = nghttp2_min(*recv_reduction_ptr,
|
||||
-new_recv_window_size);
|
||||
*recv_reduction_ptr -= recv_reduction_diff;
|
||||
if(*recv_window_size_ptr < 0) {
|
||||
*recv_window_size_ptr += recv_reduction_diff;
|
||||
} else {
|
||||
/* If *recv_window_size_ptr > 0, then those bytes are
|
||||
considered to be backed to the remote peer (by
|
||||
WINDOW_UPDATE with the adjusted *delta_ptr), so it is
|
||||
effectively 0 now. */
|
||||
*recv_window_size_ptr = recv_reduction_diff;
|
||||
}
|
||||
/* recv_reduction_diff must be paied from *delta_ptr, since it
|
||||
was added in window size reduction (see below). */
|
||||
*delta_ptr -= recv_reduction_diff;
|
||||
} else {
|
||||
nghttp2_max(0, *recv_window_size_ptr) - *delta_ptr;
|
||||
|
||||
if (new_recv_window_size >= 0) {
|
||||
*recv_window_size_ptr = new_recv_window_size;
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
if(*local_window_size_ptr + *delta_ptr < 0 ||
|
||||
*recv_window_size_ptr < INT32_MIN - *delta_ptr ||
|
||||
*recv_reduction_ptr > INT32_MAX + *delta_ptr) {
|
||||
|
||||
delta = -new_recv_window_size;
|
||||
|
||||
/* The delta size is strictly more than received bytes. Increase
|
||||
local_window_size by that difference |delta|. */
|
||||
if (*local_window_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - delta) {
|
||||
return NGHTTP2_ERR_FLOW_CONTROL;
|
||||
}
|
||||
/* Decreasing local window size. Note that we achieve this without
|
||||
noticing to the remote peer. To do this, we cut
|
||||
recv_window_size by -delta. This means that we don't send
|
||||
WINDOW_UPDATE for -delta bytes. */
|
||||
*local_window_size_ptr += *delta_ptr;
|
||||
*recv_window_size_ptr += *delta_ptr;
|
||||
*recv_reduction_ptr -= *delta_ptr;
|
||||
*delta_ptr = 0;
|
||||
*local_window_size_ptr += delta;
|
||||
/* If there is recv_reduction due to earlier window_size
|
||||
reduction, we have to adjust it too. */
|
||||
recv_reduction_delta = nghttp2_min(*recv_reduction_ptr, delta);
|
||||
*recv_reduction_ptr -= recv_reduction_delta;
|
||||
if (*recv_window_size_ptr < 0) {
|
||||
*recv_window_size_ptr += recv_reduction_delta;
|
||||
} else {
|
||||
/* If *recv_window_size_ptr > 0, then those bytes are going to
|
||||
be returned to the remote peer (by WINDOW_UPDATE with the
|
||||
adjusted *delta_ptr), so it is effectively 0 now. We set to
|
||||
*recv_reduction_delta, because caller does not take into
|
||||
account it in *delta_ptr. */
|
||||
*recv_window_size_ptr = recv_reduction_delta;
|
||||
}
|
||||
/* recv_reduction_delta must be paied from *delta_ptr, since it
|
||||
was added in window size reduction (see below). */
|
||||
*delta_ptr -= recv_reduction_delta;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (*local_window_size_ptr + *delta_ptr < 0 ||
|
||||
*recv_window_size_ptr < INT32_MIN - *delta_ptr ||
|
||||
*recv_reduction_ptr > INT32_MAX + *delta_ptr) {
|
||||
return NGHTTP2_ERR_FLOW_CONTROL;
|
||||
}
|
||||
/* Decreasing local window size. Note that we achieve this without
|
||||
noticing to the remote peer. To do this, we cut
|
||||
recv_window_size by -delta. This means that we don't send
|
||||
WINDOW_UPDATE for -delta bytes. */
|
||||
*local_window_size_ptr += *delta_ptr;
|
||||
*recv_window_size_ptr += *delta_ptr;
|
||||
*recv_reduction_ptr -= *delta_ptr;
|
||||
*delta_ptr = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
const char* nghttp2_strerror(int error_code)
|
||||
{
|
||||
switch(error_code) {
|
||||
const char *nghttp2_strerror(int error_code) {
|
||||
switch (error_code) {
|
||||
case 0:
|
||||
return "Success";
|
||||
case NGHTTP2_ERR_INVALID_ARGUMENT:
|
||||
@@ -225,184 +297,156 @@ const char* nghttp2_strerror(int error_code)
|
||||
return "Out of memory";
|
||||
case NGHTTP2_ERR_CALLBACK_FAILURE:
|
||||
return "The user callback function failed";
|
||||
case NGHTTP2_ERR_BAD_PREFACE:
|
||||
return "Received bad connection preface";
|
||||
default:
|
||||
return "Unknown error code";
|
||||
}
|
||||
}
|
||||
|
||||
void nghttp2_free(void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
/* Generated by gennmchartbl.py */
|
||||
static int VALID_HD_NAME_CHARS[] = {
|
||||
0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */,
|
||||
0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */,
|
||||
0 /* BS */, 0 /* HT */, 0 /* LF */, 0 /* VT */,
|
||||
0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */,
|
||||
0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
|
||||
0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */,
|
||||
0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */,
|
||||
0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */,
|
||||
0 /* SPC */, 1 /* ! */, 0 /* " */, 1 /* # */,
|
||||
1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
|
||||
0 /* ( */, 0 /* ) */, 1 /* * */, 1 /* + */,
|
||||
0 /* , */, 1 /* - */, 1 /* . */, 0 /* / */,
|
||||
1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */,
|
||||
1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */,
|
||||
1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */,
|
||||
0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */,
|
||||
0 /* @ */, 0 /* A */, 0 /* B */, 0 /* C */,
|
||||
0 /* D */, 0 /* E */, 0 /* F */, 0 /* G */,
|
||||
0 /* H */, 0 /* I */, 0 /* J */, 0 /* K */,
|
||||
0 /* L */, 0 /* M */, 0 /* N */, 0 /* O */,
|
||||
0 /* P */, 0 /* Q */, 0 /* R */, 0 /* S */,
|
||||
0 /* T */, 0 /* U */, 0 /* V */, 0 /* W */,
|
||||
0 /* X */, 0 /* Y */, 0 /* Z */, 0 /* [ */,
|
||||
0 /* \ */, 0 /* ] */, 1 /* ^ */, 1 /* _ */,
|
||||
1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
|
||||
1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */,
|
||||
1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */,
|
||||
1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */,
|
||||
1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */,
|
||||
1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
|
||||
1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */,
|
||||
1 /* | */, 0 /* } */, 1 /* ~ */, 0 /* DEL */,
|
||||
0 /* 0x80 */, 0 /* 0x81 */, 0 /* 0x82 */, 0 /* 0x83 */,
|
||||
0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, 0 /* 0x87 */,
|
||||
0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
|
||||
0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */,
|
||||
0 /* 0x90 */, 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */,
|
||||
0 /* 0x94 */, 0 /* 0x95 */, 0 /* 0x96 */, 0 /* 0x97 */,
|
||||
0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, 0 /* 0x9b */,
|
||||
0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
|
||||
0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */,
|
||||
0 /* 0xa4 */, 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */,
|
||||
0 /* 0xa8 */, 0 /* 0xa9 */, 0 /* 0xaa */, 0 /* 0xab */,
|
||||
0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, 0 /* 0xaf */,
|
||||
0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
|
||||
0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */,
|
||||
0 /* 0xb8 */, 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */,
|
||||
0 /* 0xbc */, 0 /* 0xbd */, 0 /* 0xbe */, 0 /* 0xbf */,
|
||||
0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, 0 /* 0xc3 */,
|
||||
0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
|
||||
0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */,
|
||||
0 /* 0xcc */, 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */,
|
||||
0 /* 0xd0 */, 0 /* 0xd1 */, 0 /* 0xd2 */, 0 /* 0xd3 */,
|
||||
0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, 0 /* 0xd7 */,
|
||||
0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
|
||||
0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */,
|
||||
0 /* 0xe0 */, 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */,
|
||||
0 /* 0xe4 */, 0 /* 0xe5 */, 0 /* 0xe6 */, 0 /* 0xe7 */,
|
||||
0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, 0 /* 0xeb */,
|
||||
0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
|
||||
0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */,
|
||||
0 /* 0xf4 */, 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */,
|
||||
0 /* 0xf8 */, 0 /* 0xf9 */, 0 /* 0xfa */, 0 /* 0xfb */,
|
||||
0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, 0 /* 0xff */
|
||||
0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */,
|
||||
0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */,
|
||||
0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */,
|
||||
0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
|
||||
0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */,
|
||||
0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */,
|
||||
0 /* RS */, 0 /* US */, 0 /* SPC */, 1 /* ! */, 0 /* " */,
|
||||
1 /* # */, 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
|
||||
0 /* ( */, 0 /* ) */, 1 /* * */, 1 /* + */, 0 /* , */,
|
||||
1 /* - */, 1 /* . */, 0 /* / */, 1 /* 0 */, 1 /* 1 */,
|
||||
1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */,
|
||||
1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */,
|
||||
0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */, 0 /* @ */,
|
||||
0 /* A */, 0 /* B */, 0 /* C */, 0 /* D */, 0 /* E */,
|
||||
0 /* F */, 0 /* G */, 0 /* H */, 0 /* I */, 0 /* J */,
|
||||
0 /* K */, 0 /* L */, 0 /* M */, 0 /* N */, 0 /* O */,
|
||||
0 /* P */, 0 /* Q */, 0 /* R */, 0 /* S */, 0 /* T */,
|
||||
0 /* U */, 0 /* V */, 0 /* W */, 0 /* X */, 0 /* Y */,
|
||||
0 /* Z */, 0 /* [ */, 0 /* \ */, 0 /* ] */, 1 /* ^ */,
|
||||
1 /* _ */, 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
|
||||
1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */,
|
||||
1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */,
|
||||
1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */,
|
||||
1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
|
||||
1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, 1 /* | */,
|
||||
0 /* } */, 1 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */,
|
||||
0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */,
|
||||
0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
|
||||
0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */,
|
||||
0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */,
|
||||
0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */,
|
||||
0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
|
||||
0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */,
|
||||
0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */,
|
||||
0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */,
|
||||
0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
|
||||
0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */,
|
||||
0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */,
|
||||
0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */,
|
||||
0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
|
||||
0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */,
|
||||
0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */,
|
||||
0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */,
|
||||
0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
|
||||
0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */,
|
||||
0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */,
|
||||
0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */,
|
||||
0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
|
||||
0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */,
|
||||
0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */,
|
||||
0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */,
|
||||
0 /* 0xff */
|
||||
};
|
||||
|
||||
int nghttp2_check_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;
|
||||
if(len == 0) {
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
if(*name == ':') {
|
||||
if(len == 1) {
|
||||
if (*name == ':') {
|
||||
if (len == 1) {
|
||||
return 0;
|
||||
}
|
||||
++name;
|
||||
--len;
|
||||
}
|
||||
for(last = name + len; name != last; ++name) {
|
||||
if(!VALID_HD_NAME_CHARS[*name]) {
|
||||
for (last = name + len; name != last; ++name) {
|
||||
if (!VALID_HD_NAME_CHARS[*name]) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Generated by genvchartbl.py */
|
||||
static int VALID_HD_VALUE_CHARS[] = {
|
||||
1 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */,
|
||||
0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */,
|
||||
0 /* BS */, 1 /* HT */, 0 /* LF */, 0 /* VT */,
|
||||
0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */,
|
||||
0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
|
||||
0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */,
|
||||
0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */,
|
||||
0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */,
|
||||
1 /* SPC */, 1 /* ! */, 1 /* " */, 1 /* # */,
|
||||
1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
|
||||
1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */,
|
||||
1 /* , */, 1 /* - */, 1 /* . */, 1 /* / */,
|
||||
1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */,
|
||||
1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */,
|
||||
1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */,
|
||||
1 /* < */, 1 /* = */, 1 /* > */, 1 /* ? */,
|
||||
1 /* @ */, 1 /* A */, 1 /* B */, 1 /* C */,
|
||||
1 /* D */, 1 /* E */, 1 /* F */, 1 /* G */,
|
||||
1 /* H */, 1 /* I */, 1 /* J */, 1 /* K */,
|
||||
1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */,
|
||||
1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */,
|
||||
1 /* T */, 1 /* U */, 1 /* V */, 1 /* W */,
|
||||
1 /* X */, 1 /* Y */, 1 /* Z */, 1 /* [ */,
|
||||
1 /* \ */, 1 /* ] */, 1 /* ^ */, 1 /* _ */,
|
||||
1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
|
||||
1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */,
|
||||
1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */,
|
||||
1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */,
|
||||
1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */,
|
||||
1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
|
||||
1 /* x */, 1 /* y */, 1 /* z */, 1 /* { */,
|
||||
1 /* | */, 1 /* } */, 1 /* ~ */, 0 /* DEL */,
|
||||
1 /* 0x80 */, 1 /* 0x81 */, 1 /* 0x82 */, 1 /* 0x83 */,
|
||||
1 /* 0x84 */, 1 /* 0x85 */, 1 /* 0x86 */, 1 /* 0x87 */,
|
||||
1 /* 0x88 */, 1 /* 0x89 */, 1 /* 0x8a */, 1 /* 0x8b */,
|
||||
1 /* 0x8c */, 1 /* 0x8d */, 1 /* 0x8e */, 1 /* 0x8f */,
|
||||
1 /* 0x90 */, 1 /* 0x91 */, 1 /* 0x92 */, 1 /* 0x93 */,
|
||||
1 /* 0x94 */, 1 /* 0x95 */, 1 /* 0x96 */, 1 /* 0x97 */,
|
||||
1 /* 0x98 */, 1 /* 0x99 */, 1 /* 0x9a */, 1 /* 0x9b */,
|
||||
1 /* 0x9c */, 1 /* 0x9d */, 1 /* 0x9e */, 1 /* 0x9f */,
|
||||
1 /* 0xa0 */, 1 /* 0xa1 */, 1 /* 0xa2 */, 1 /* 0xa3 */,
|
||||
1 /* 0xa4 */, 1 /* 0xa5 */, 1 /* 0xa6 */, 1 /* 0xa7 */,
|
||||
1 /* 0xa8 */, 1 /* 0xa9 */, 1 /* 0xaa */, 1 /* 0xab */,
|
||||
1 /* 0xac */, 1 /* 0xad */, 1 /* 0xae */, 1 /* 0xaf */,
|
||||
1 /* 0xb0 */, 1 /* 0xb1 */, 1 /* 0xb2 */, 1 /* 0xb3 */,
|
||||
1 /* 0xb4 */, 1 /* 0xb5 */, 1 /* 0xb6 */, 1 /* 0xb7 */,
|
||||
1 /* 0xb8 */, 1 /* 0xb9 */, 1 /* 0xba */, 1 /* 0xbb */,
|
||||
1 /* 0xbc */, 1 /* 0xbd */, 1 /* 0xbe */, 1 /* 0xbf */,
|
||||
1 /* 0xc0 */, 1 /* 0xc1 */, 1 /* 0xc2 */, 1 /* 0xc3 */,
|
||||
1 /* 0xc4 */, 1 /* 0xc5 */, 1 /* 0xc6 */, 1 /* 0xc7 */,
|
||||
1 /* 0xc8 */, 1 /* 0xc9 */, 1 /* 0xca */, 1 /* 0xcb */,
|
||||
1 /* 0xcc */, 1 /* 0xcd */, 1 /* 0xce */, 1 /* 0xcf */,
|
||||
1 /* 0xd0 */, 1 /* 0xd1 */, 1 /* 0xd2 */, 1 /* 0xd3 */,
|
||||
1 /* 0xd4 */, 1 /* 0xd5 */, 1 /* 0xd6 */, 1 /* 0xd7 */,
|
||||
1 /* 0xd8 */, 1 /* 0xd9 */, 1 /* 0xda */, 1 /* 0xdb */,
|
||||
1 /* 0xdc */, 1 /* 0xdd */, 1 /* 0xde */, 1 /* 0xdf */,
|
||||
1 /* 0xe0 */, 1 /* 0xe1 */, 1 /* 0xe2 */, 1 /* 0xe3 */,
|
||||
1 /* 0xe4 */, 1 /* 0xe5 */, 1 /* 0xe6 */, 1 /* 0xe7 */,
|
||||
1 /* 0xe8 */, 1 /* 0xe9 */, 1 /* 0xea */, 1 /* 0xeb */,
|
||||
1 /* 0xec */, 1 /* 0xed */, 1 /* 0xee */, 1 /* 0xef */,
|
||||
1 /* 0xf0 */, 1 /* 0xf1 */, 1 /* 0xf2 */, 1 /* 0xf3 */,
|
||||
1 /* 0xf4 */, 1 /* 0xf5 */, 1 /* 0xf6 */, 1 /* 0xf7 */,
|
||||
1 /* 0xf8 */, 1 /* 0xf9 */, 1 /* 0xfa */, 1 /* 0xfb */,
|
||||
1 /* 0xfc */, 1 /* 0xfd */, 1 /* 0xfe */, 1 /* 0xff */
|
||||
0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */,
|
||||
0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 1 /* HT */,
|
||||
0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */,
|
||||
0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
|
||||
0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */,
|
||||
0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */,
|
||||
0 /* RS */, 0 /* US */, 1 /* SPC */, 1 /* ! */, 1 /* " */,
|
||||
1 /* # */, 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
|
||||
1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */, 1 /* , */,
|
||||
1 /* - */, 1 /* . */, 1 /* / */, 1 /* 0 */, 1 /* 1 */,
|
||||
1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */,
|
||||
1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */,
|
||||
1 /* < */, 1 /* = */, 1 /* > */, 1 /* ? */, 1 /* @ */,
|
||||
1 /* A */, 1 /* B */, 1 /* C */, 1 /* D */, 1 /* E */,
|
||||
1 /* F */, 1 /* G */, 1 /* H */, 1 /* I */, 1 /* J */,
|
||||
1 /* K */, 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */,
|
||||
1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, 1 /* T */,
|
||||
1 /* U */, 1 /* V */, 1 /* W */, 1 /* X */, 1 /* Y */,
|
||||
1 /* Z */, 1 /* [ */, 1 /* \ */, 1 /* ] */, 1 /* ^ */,
|
||||
1 /* _ */, 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
|
||||
1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */,
|
||||
1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */,
|
||||
1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */,
|
||||
1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
|
||||
1 /* x */, 1 /* y */, 1 /* z */, 1 /* { */, 1 /* | */,
|
||||
1 /* } */, 1 /* ~ */, 0 /* DEL */, 1 /* 0x80 */, 1 /* 0x81 */,
|
||||
1 /* 0x82 */, 1 /* 0x83 */, 1 /* 0x84 */, 1 /* 0x85 */, 1 /* 0x86 */,
|
||||
1 /* 0x87 */, 1 /* 0x88 */, 1 /* 0x89 */, 1 /* 0x8a */, 1 /* 0x8b */,
|
||||
1 /* 0x8c */, 1 /* 0x8d */, 1 /* 0x8e */, 1 /* 0x8f */, 1 /* 0x90 */,
|
||||
1 /* 0x91 */, 1 /* 0x92 */, 1 /* 0x93 */, 1 /* 0x94 */, 1 /* 0x95 */,
|
||||
1 /* 0x96 */, 1 /* 0x97 */, 1 /* 0x98 */, 1 /* 0x99 */, 1 /* 0x9a */,
|
||||
1 /* 0x9b */, 1 /* 0x9c */, 1 /* 0x9d */, 1 /* 0x9e */, 1 /* 0x9f */,
|
||||
1 /* 0xa0 */, 1 /* 0xa1 */, 1 /* 0xa2 */, 1 /* 0xa3 */, 1 /* 0xa4 */,
|
||||
1 /* 0xa5 */, 1 /* 0xa6 */, 1 /* 0xa7 */, 1 /* 0xa8 */, 1 /* 0xa9 */,
|
||||
1 /* 0xaa */, 1 /* 0xab */, 1 /* 0xac */, 1 /* 0xad */, 1 /* 0xae */,
|
||||
1 /* 0xaf */, 1 /* 0xb0 */, 1 /* 0xb1 */, 1 /* 0xb2 */, 1 /* 0xb3 */,
|
||||
1 /* 0xb4 */, 1 /* 0xb5 */, 1 /* 0xb6 */, 1 /* 0xb7 */, 1 /* 0xb8 */,
|
||||
1 /* 0xb9 */, 1 /* 0xba */, 1 /* 0xbb */, 1 /* 0xbc */, 1 /* 0xbd */,
|
||||
1 /* 0xbe */, 1 /* 0xbf */, 1 /* 0xc0 */, 1 /* 0xc1 */, 1 /* 0xc2 */,
|
||||
1 /* 0xc3 */, 1 /* 0xc4 */, 1 /* 0xc5 */, 1 /* 0xc6 */, 1 /* 0xc7 */,
|
||||
1 /* 0xc8 */, 1 /* 0xc9 */, 1 /* 0xca */, 1 /* 0xcb */, 1 /* 0xcc */,
|
||||
1 /* 0xcd */, 1 /* 0xce */, 1 /* 0xcf */, 1 /* 0xd0 */, 1 /* 0xd1 */,
|
||||
1 /* 0xd2 */, 1 /* 0xd3 */, 1 /* 0xd4 */, 1 /* 0xd5 */, 1 /* 0xd6 */,
|
||||
1 /* 0xd7 */, 1 /* 0xd8 */, 1 /* 0xd9 */, 1 /* 0xda */, 1 /* 0xdb */,
|
||||
1 /* 0xdc */, 1 /* 0xdd */, 1 /* 0xde */, 1 /* 0xdf */, 1 /* 0xe0 */,
|
||||
1 /* 0xe1 */, 1 /* 0xe2 */, 1 /* 0xe3 */, 1 /* 0xe4 */, 1 /* 0xe5 */,
|
||||
1 /* 0xe6 */, 1 /* 0xe7 */, 1 /* 0xe8 */, 1 /* 0xe9 */, 1 /* 0xea */,
|
||||
1 /* 0xeb */, 1 /* 0xec */, 1 /* 0xed */, 1 /* 0xee */, 1 /* 0xef */,
|
||||
1 /* 0xf0 */, 1 /* 0xf1 */, 1 /* 0xf2 */, 1 /* 0xf3 */, 1 /* 0xf4 */,
|
||||
1 /* 0xf5 */, 1 /* 0xf6 */, 1 /* 0xf7 */, 1 /* 0xf8 */, 1 /* 0xf9 */,
|
||||
1 /* 0xfa */, 1 /* 0xfb */, 1 /* 0xfc */, 1 /* 0xfd */, 1 /* 0xfe */,
|
||||
1 /* 0xff */
|
||||
};
|
||||
|
||||
int nghttp2_check_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;
|
||||
for(last = value + len; value != last; ++value) {
|
||||
if(!VALID_HD_VALUE_CHARS[*value]) {
|
||||
for (last = value + len; value != last; ++value) {
|
||||
if (!VALID_HD_VALUE_CHARS[*value]) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
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);
|
||||
|
||||
return dest + len;
|
||||
|
||||
@@ -26,10 +26,11 @@
|
||||
#define NGHTTP2_HELPER_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
#include "nghttp2_mem.h"
|
||||
|
||||
#define nghttp2_min(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);
|
||||
|
||||
/*
|
||||
* 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
|
||||
* |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
|
||||
* 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);
|
||||
|
||||
@@ -118,19 +100,11 @@ int nghttp2_adjust_local_window_size(int32_t *local_window_size_ptr,
|
||||
int nghttp2_should_send_window_update(int32_t local_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
|
||||
* by the |dest|, assuming that the |dest| is at lest |len| bytes long
|
||||
* . Returns dest + len.
|
||||
*/
|
||||
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);
|
||||
|
||||
#endif /* NGHTTP2_HELPER_H */
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#define NGHTTP2_INT_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
/* Macros, types and constants for internal use */
|
||||
@@ -34,7 +34,9 @@
|
||||
#ifdef DEBUGBUILD
|
||||
#define DEBUGF(x) x
|
||||
#else
|
||||
#define DEBUGF(x) do { } while(0)
|
||||
#define DEBUGF(x) \
|
||||
do { \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
typedef int (*nghttp2_compar)(const void *lhs, const void *rhs);
|
||||
|
||||
@@ -26,13 +26,14 @@
|
||||
|
||||
#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->table = calloc(map->tablelen, sizeof(nghttp2_map_entry*));
|
||||
if(map->table == NULL) {
|
||||
map->table =
|
||||
nghttp2_mem_calloc(mem, map->tablelen, sizeof(nghttp2_map_entry *));
|
||||
if (map->table == NULL) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
@@ -41,19 +42,17 @@ int nghttp2_map_init(nghttp2_map *map)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nghttp2_map_free(nghttp2_map *map)
|
||||
{
|
||||
free(map->table);
|
||||
void nghttp2_map_free(nghttp2_map *map) {
|
||||
nghttp2_mem_free(map->mem, map->table);
|
||||
}
|
||||
|
||||
void nghttp2_map_each_free(nghttp2_map *map,
|
||||
int (*func)(nghttp2_map_entry *entry, void *ptr),
|
||||
void *ptr)
|
||||
{
|
||||
void *ptr) {
|
||||
size_t i;
|
||||
for(i = 0; i < map->tablelen; ++i) {
|
||||
for (i = 0; i < map->tablelen; ++i) {
|
||||
nghttp2_map_entry *entry;
|
||||
for(entry = map->table[i]; entry;) {
|
||||
for (entry = map->table[i]; entry;) {
|
||||
nghttp2_map_entry *next = entry->next;
|
||||
func(entry, ptr);
|
||||
entry = next;
|
||||
@@ -64,15 +63,14 @@ void nghttp2_map_each_free(nghttp2_map *map,
|
||||
|
||||
int nghttp2_map_each(nghttp2_map *map,
|
||||
int (*func)(nghttp2_map_entry *entry, void *ptr),
|
||||
void *ptr)
|
||||
{
|
||||
void *ptr) {
|
||||
int rv;
|
||||
size_t i;
|
||||
for(i = 0; i < map->tablelen; ++i) {
|
||||
for (i = 0; i < map->tablelen; ++i) {
|
||||
nghttp2_map_entry *entry;
|
||||
for(entry = map->table[i]; entry; entry = entry->next) {
|
||||
for (entry = map->table[i]; entry; entry = entry->next) {
|
||||
rv = func(entry, ptr);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
@@ -80,32 +78,29 @@ int nghttp2_map_each(nghttp2_map *map,
|
||||
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->next = NULL;
|
||||
}
|
||||
|
||||
/* Same hash function in android HashMap source code. */
|
||||
/* 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 >> 7) ^ (h >> 4);
|
||||
return h & (mod - 1);
|
||||
}
|
||||
|
||||
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);
|
||||
if(table[h] == NULL) {
|
||||
if (table[h] == NULL) {
|
||||
table[h] = entry;
|
||||
} else {
|
||||
nghttp2_map_entry *p;
|
||||
/* We won't allow duplicated key, so check it out. */
|
||||
for(p = table[h]; p; p = p->next) {
|
||||
if(p->key == entry->key) {
|
||||
for (p = table[h]; p; p = p->next) {
|
||||
if (p->key == entry->key) {
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
}
|
||||
@@ -116,18 +111,19 @@ static int insert(nghttp2_map_entry **table, size_t tablelen,
|
||||
}
|
||||
|
||||
/* 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;
|
||||
nghttp2_map_entry **new_table;
|
||||
new_table = calloc(new_tablelen, sizeof(nghttp2_map_entry*));
|
||||
if(new_table == NULL) {
|
||||
|
||||
new_table =
|
||||
nghttp2_mem_calloc(map->mem, new_tablelen, sizeof(nghttp2_map_entry *));
|
||||
if (new_table == NULL) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
for(i = 0; i < map->tablelen; ++i) {
|
||||
for (i = 0; i < map->tablelen; ++i) {
|
||||
nghttp2_map_entry *entry;
|
||||
for(entry = map->table[i]; entry;) {
|
||||
for (entry = map->table[i]; entry;) {
|
||||
nghttp2_map_entry *next = entry->next;
|
||||
entry->next = NULL;
|
||||
/* This function must succeed */
|
||||
@@ -135,52 +131,50 @@ static int resize(nghttp2_map *map, size_t new_tablelen)
|
||||
entry = next;
|
||||
}
|
||||
}
|
||||
free(map->table);
|
||||
nghttp2_mem_free(map->mem, map->table);
|
||||
map->tablelen = new_tablelen;
|
||||
map->table = new_table;
|
||||
|
||||
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;
|
||||
/* Load factor is 0.75 */
|
||||
if((map->size + 1) * 4 > map->tablelen * 3) {
|
||||
if ((map->size + 1) * 4 > map->tablelen * 3) {
|
||||
rv = resize(map, map->tablelen * 2);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
rv = insert(map->table, map->tablelen, new_entry);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
++map->size;
|
||||
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;
|
||||
nghttp2_map_entry *entry;
|
||||
h = hash(key, map->tablelen);
|
||||
for(entry = map->table[h]; entry; entry = entry->next) {
|
||||
if(entry->key == key) {
|
||||
for (entry = map->table[h]; entry; entry = entry->next) {
|
||||
if (entry->key == key) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
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;
|
||||
nghttp2_map_entry *entry, *prev;
|
||||
h = hash(key, map->tablelen);
|
||||
prev = NULL;
|
||||
for(entry = map->table[h]; entry; entry = entry->next) {
|
||||
if(entry->key == key) {
|
||||
if(prev == NULL) {
|
||||
for (entry = map->table[h]; entry; entry = entry->next) {
|
||||
if (entry->key == key) {
|
||||
if (prev == NULL) {
|
||||
map->table[h] = entry->next;
|
||||
} else {
|
||||
prev->next = entry->next;
|
||||
@@ -193,7 +187,4 @@ int nghttp2_map_remove(nghttp2_map *map, key_type key)
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
size_t nghttp2_map_size(nghttp2_map *map)
|
||||
{
|
||||
return map->size;
|
||||
}
|
||||
size_t nghttp2_map_size(nghttp2_map *map) { return map->size; }
|
||||
|
||||
@@ -26,11 +26,12 @@
|
||||
#define NGHTTP2_MAP_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
#include "nghttp2_int.h"
|
||||
#include "nghttp2_mem.h"
|
||||
|
||||
/* Implementation of unordered map */
|
||||
|
||||
@@ -43,6 +44,7 @@ typedef struct nghttp2_map_entry {
|
||||
|
||||
typedef struct {
|
||||
nghttp2_map_entry **table;
|
||||
nghttp2_mem *mem;
|
||||
size_t tablelen;
|
||||
size_t size;
|
||||
} nghttp2_map;
|
||||
@@ -56,7 +58,7 @@ typedef struct {
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* 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
|
||||
@@ -98,7 +100,7 @@ int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_entry *entry);
|
||||
* Returns the entry associated by the key |key|. If there is no such
|
||||
* entry, this function returns NULL.
|
||||
*/
|
||||
nghttp2_map_entry* nghttp2_map_find(nghttp2_map *map, key_type key);
|
||||
nghttp2_map_entry *nghttp2_map_find(nghttp2_map *map, key_type key);
|
||||
|
||||
/*
|
||||
* Removes the entry associated by the key |key| from the |map|. The
|
||||
|
||||
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) {
|
||||
return 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 */
|
||||
@@ -26,19 +26,19 @@
|
||||
#define NGHTTP2_NET_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#ifdef HAVE_ARPA_INET_H
|
||||
# include <arpa/inet.h>
|
||||
#include <arpa/inet.h>
|
||||
#endif /* HAVE_ARPA_INET_H */
|
||||
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
# include <netinet/in.h>
|
||||
#include <netinet/in.h>
|
||||
#endif /* HAVE_NETINET_IN_H */
|
||||
|
||||
#ifdef HAVE_WINSOCK2_H
|
||||
# include <winsock2.h>
|
||||
#include <winsock2.h>
|
||||
#endif /* HAVE_WINSOCK2_H */
|
||||
|
||||
#endif /* NGHTTP2_NET_H */
|
||||
|
||||
@@ -27,28 +27,26 @@
|
||||
#include <string.h>
|
||||
|
||||
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;
|
||||
unsigned int i = 0;
|
||||
for(; i < inlen; i += in[i]+1) {
|
||||
if(in[i] == NGHTTP2_PROTO_VERSION_ID_LEN &&
|
||||
i + 1 + in[i] <= inlen &&
|
||||
memcmp(&in[i+1], NGHTTP2_PROTO_VERSION_ID, in[i]) == 0) {
|
||||
*out = (unsigned char*)&in[i+1];
|
||||
for (; i < inlen; i += in [i] + 1) {
|
||||
if (in[i] == NGHTTP2_PROTO_VERSION_ID_LEN && i + 1 + in[i] <= inlen &&
|
||||
memcmp(&in[i + 1], NGHTTP2_PROTO_VERSION_ID, in[i]) == 0) {
|
||||
*out = (unsigned char *)&in[i + 1];
|
||||
*outlen = in[i];
|
||||
return 1;
|
||||
}
|
||||
if(in[i] == 8 && i + 1 + in[i] <= inlen &&
|
||||
memcmp(&in[i+1], "http/1.1", in[i]) == 0) {
|
||||
if (in[i] == 8 && i + 1 + in[i] <= inlen &&
|
||||
memcmp(&in[i + 1], "http/1.1", in[i]) == 0) {
|
||||
http_selected = 1;
|
||||
*out = (unsigned char*)&in[i+1];
|
||||
*out = (unsigned char *)&in[i + 1];
|
||||
*outlen = in[i];
|
||||
/* Go through to the next iteration, because "HTTP/2" may be
|
||||
there */
|
||||
}
|
||||
}
|
||||
if(http_selected) {
|
||||
if (http_selected) {
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#define NGHTTP2_NPN_H
|
||||
|
||||
#ifdef HAVE_CONFIG
|
||||
# include <config.h>
|
||||
#include <config.h>
|
||||
#endif /* HAVE_CONFIG */
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
@@ -24,39 +24,30 @@
|
||||
*/
|
||||
#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));
|
||||
|
||||
if(*option_ptr == NULL) {
|
||||
if (*option_ptr == NULL) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nghttp2_option_del(nghttp2_option *option)
|
||||
{
|
||||
free(option);
|
||||
}
|
||||
void nghttp2_option_del(nghttp2_option *option) { free(option); }
|
||||
|
||||
void nghttp2_option_set_no_auto_stream_window_update(nghttp2_option *option,
|
||||
int val)
|
||||
{
|
||||
option->opt_set_mask |= NGHTTP2_OPT_NO_AUTO_STREAM_WINDOW_UPDATE;
|
||||
option->no_auto_stream_window_update = val;
|
||||
}
|
||||
|
||||
void nghttp2_option_set_no_auto_connection_window_update
|
||||
(nghttp2_option *option, int val)
|
||||
{
|
||||
option->opt_set_mask |= NGHTTP2_OPT_NO_AUTO_CONNECTION_WINDOW_UPDATE;
|
||||
option->no_auto_connection_window_update = 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->no_auto_window_update = val;
|
||||
}
|
||||
|
||||
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->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;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#define NGHTTP2_OPTION_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
@@ -37,22 +37,12 @@
|
||||
typedef enum {
|
||||
/**
|
||||
* This option prevents the library from sending WINDOW_UPDATE for a
|
||||
* stream automatically. If this option is set to nonzero, the
|
||||
* library won't send WINDOW_UPDATE for a stream and the application
|
||||
* is responsible for sending WINDOW_UPDATE using
|
||||
* `nghttp2_submit_window_update`. By default, this option is set to
|
||||
* zero.
|
||||
* connection automatically. If this option is set to nonzero, the
|
||||
* library won't send WINDOW_UPDATE for DATA until application calls
|
||||
* nghttp2_session_consume() to indicate the amount of consumed
|
||||
* DATA. By default, this option is set to zero.
|
||||
*/
|
||||
NGHTTP2_OPT_NO_AUTO_STREAM_WINDOW_UPDATE = 1,
|
||||
/**
|
||||
* This option prevents the library from sending WINDOW_UPDATE for a
|
||||
* connection automatically. If this option is set to nonzero, the
|
||||
* library won't send WINDOW_UPDATE for a connection and the
|
||||
* application is responsible for sending WINDOW_UPDATE with stream
|
||||
* ID 0 using `nghttp2_submit_window_update`. By default, this
|
||||
* option is set to zero.
|
||||
*/
|
||||
NGHTTP2_OPT_NO_AUTO_CONNECTION_WINDOW_UPDATE = 1 << 1,
|
||||
NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE = 1,
|
||||
/**
|
||||
* This option sets the SETTINGS_MAX_CONCURRENT_STREAMS value of
|
||||
* remote endpoint as if it is received in SETTINGS frame. Without
|
||||
@@ -66,7 +56,8 @@ typedef enum {
|
||||
* will be overwritten if the local endpoint receives
|
||||
* SETTINGS_MAX_CONCURRENT_STREAMS from the remote endpoint.
|
||||
*/
|
||||
NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS = 1 << 2
|
||||
NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS = 1 << 1,
|
||||
NGHTTP2_OPT_RECV_CLIENT_PREFACE = 1 << 2,
|
||||
} nghttp2_option_flag;
|
||||
|
||||
/**
|
||||
@@ -83,13 +74,13 @@ struct nghttp2_option {
|
||||
*/
|
||||
uint32_t peer_max_concurrent_streams;
|
||||
/**
|
||||
* NGHTTP2_OPT_NO_AUTO_STREAM_WINDOW_UPDATE
|
||||
* NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE
|
||||
*/
|
||||
uint8_t no_auto_stream_window_update;
|
||||
uint8_t no_auto_window_update;
|
||||
/**
|
||||
* NGHTTP2_OPT_NO_AUTO_CONNECTION_WINDOW_UPDATE
|
||||
* NGHTTP2_OPT_RECV_CLIENT_PREFACE
|
||||
*/
|
||||
uint8_t no_auto_connection_window_update;
|
||||
uint8_t recv_client_preface;
|
||||
};
|
||||
|
||||
#endif /* NGHTTP2_OPTION_H */
|
||||
|
||||
@@ -26,55 +26,42 @@
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
void nghttp2_outbound_item_free(nghttp2_outbound_item *item)
|
||||
{
|
||||
if(item == NULL) {
|
||||
void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem) {
|
||||
nghttp2_frame *frame;
|
||||
|
||||
if (item == NULL) {
|
||||
return;
|
||||
}
|
||||
if(item->frame_cat == NGHTTP2_CAT_CTRL) {
|
||||
nghttp2_frame *frame;
|
||||
frame = nghttp2_outbound_item_get_ctrl_frame(item);
|
||||
switch(frame->hd.type) {
|
||||
case NGHTTP2_HEADERS:
|
||||
nghttp2_frame_headers_free(&frame->headers);
|
||||
if(item->aux_data) {
|
||||
free(((nghttp2_headers_aux_data*)item->aux_data)->data_prd);
|
||||
}
|
||||
break;
|
||||
case NGHTTP2_PRIORITY:
|
||||
nghttp2_frame_priority_free(&frame->priority);
|
||||
break;
|
||||
case NGHTTP2_RST_STREAM:
|
||||
nghttp2_frame_rst_stream_free(&frame->rst_stream);
|
||||
break;
|
||||
case NGHTTP2_SETTINGS:
|
||||
nghttp2_frame_settings_free(&frame->settings);
|
||||
break;
|
||||
case NGHTTP2_PUSH_PROMISE:
|
||||
nghttp2_frame_push_promise_free(&frame->push_promise);
|
||||
break;
|
||||
case NGHTTP2_PING:
|
||||
nghttp2_frame_ping_free(&frame->ping);
|
||||
break;
|
||||
case NGHTTP2_GOAWAY:
|
||||
nghttp2_frame_goaway_free(&frame->goaway);
|
||||
break;
|
||||
case NGHTTP2_WINDOW_UPDATE:
|
||||
nghttp2_frame_window_update_free(&frame->window_update);
|
||||
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);
|
||||
|
||||
frame = &item->frame;
|
||||
|
||||
switch (frame->hd.type) {
|
||||
case NGHTTP2_DATA:
|
||||
nghttp2_frame_data_free(&frame->data);
|
||||
break;
|
||||
case NGHTTP2_HEADERS:
|
||||
nghttp2_frame_headers_free(&frame->headers, mem);
|
||||
break;
|
||||
case NGHTTP2_PRIORITY:
|
||||
nghttp2_frame_priority_free(&frame->priority);
|
||||
break;
|
||||
case NGHTTP2_RST_STREAM:
|
||||
nghttp2_frame_rst_stream_free(&frame->rst_stream);
|
||||
break;
|
||||
case NGHTTP2_SETTINGS:
|
||||
nghttp2_frame_settings_free(&frame->settings, mem);
|
||||
break;
|
||||
case NGHTTP2_PUSH_PROMISE:
|
||||
nghttp2_frame_push_promise_free(&frame->push_promise, mem);
|
||||
break;
|
||||
case NGHTTP2_PING:
|
||||
nghttp2_frame_ping_free(&frame->ping);
|
||||
break;
|
||||
case NGHTTP2_GOAWAY:
|
||||
nghttp2_frame_goaway_free(&frame->goaway, mem);
|
||||
break;
|
||||
case NGHTTP2_WINDOW_UPDATE:
|
||||
nghttp2_frame_window_update_free(&frame->window_update);
|
||||
break;
|
||||
}
|
||||
free(item->frame);
|
||||
free(item->aux_data);
|
||||
}
|
||||
|
||||
@@ -26,11 +26,12 @@
|
||||
#define NGHTTP2_OUTBOUND_ITEM_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
#include "nghttp2_frame.h"
|
||||
#include "nghttp2_mem.h"
|
||||
|
||||
/* A bit higher weight for non-DATA frames */
|
||||
#define NGHTTP2_OB_EX_WEIGHT 300
|
||||
@@ -39,21 +40,57 @@
|
||||
/* Highest weight for PING */
|
||||
#define NGHTTP2_OB_PING_WEIGHT 302
|
||||
|
||||
/* struct used for HEADERS and PUSH_PROMISE frame */
|
||||
typedef struct {
|
||||
nghttp2_data_provider *data_prd;
|
||||
nghttp2_data_provider data_prd;
|
||||
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;
|
||||
|
||||
/* struct used for DATA frame */
|
||||
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;
|
||||
|
||||
/* struct used for GOAWAY frame */
|
||||
typedef struct {
|
||||
/* nonzero if session should be terminated after the transmission of
|
||||
this frame. */
|
||||
int terminate_on_send;
|
||||
} 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;
|
||||
/* Reset count of weight. See comment for last_cycle in
|
||||
nghttp2_session.h */
|
||||
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
|
||||
ealier. */
|
||||
int32_t weight;
|
||||
@@ -65,13 +102,6 @@ typedef struct {
|
||||
* Deallocates resource for |item|. If |item| is NULL, this function
|
||||
* does nothing.
|
||||
*/
|
||||
void nghttp2_outbound_item_free(nghttp2_outbound_item *item);
|
||||
|
||||
/* 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)
|
||||
void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem);
|
||||
|
||||
#endif /* NGHTTP2_OUTBOUND_ITEM_H */
|
||||
|
||||
@@ -24,11 +24,11 @@
|
||||
*/
|
||||
#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->q = malloc(pq->capacity * sizeof(void*));
|
||||
if(pq->q == NULL) {
|
||||
pq->q = nghttp2_mem_malloc(mem, pq->capacity * sizeof(void *));
|
||||
if (pq->q == NULL) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
pq->length = 0;
|
||||
@@ -36,38 +36,35 @@ int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_compar compar)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nghttp2_pq_free(nghttp2_pq *pq)
|
||||
{
|
||||
free(pq->q);
|
||||
void nghttp2_pq_free(nghttp2_pq *pq) {
|
||||
nghttp2_mem_free(pq->mem, pq->q);
|
||||
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];
|
||||
pq->q[i] = pq->q[j];
|
||||
pq->q[j] = t;
|
||||
}
|
||||
|
||||
static void bubble_up(nghttp2_pq *pq, size_t index)
|
||||
{
|
||||
if(index == 0) {
|
||||
static void bubble_up(nghttp2_pq *pq, size_t index) {
|
||||
if (index == 0) {
|
||||
return;
|
||||
} else {
|
||||
size_t parent = (index-1)/2;
|
||||
if(pq->compar(pq->q[parent], pq->q[index]) > 0) {
|
||||
size_t parent = (index - 1) / 2;
|
||||
if (pq->compar(pq->q[parent], pq->q[index]) > 0) {
|
||||
swap(pq, parent, index);
|
||||
bubble_up(pq, parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int nghttp2_pq_push(nghttp2_pq *pq, void *item)
|
||||
{
|
||||
if(pq->capacity <= pq->length) {
|
||||
int nghttp2_pq_push(nghttp2_pq *pq, void *item) {
|
||||
if (pq->capacity <= pq->length) {
|
||||
void *nq;
|
||||
nq = realloc(pq->q, (pq->capacity*2) * sizeof(void*));
|
||||
if(nq == NULL) {
|
||||
nq = nghttp2_mem_realloc(pq->mem, pq->q,
|
||||
(pq->capacity * 2) * sizeof(void *));
|
||||
if (nq == NULL) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
pq->capacity *= 2;
|
||||
@@ -75,70 +72,60 @@ int nghttp2_pq_push(nghttp2_pq *pq, void *item)
|
||||
}
|
||||
pq->q[pq->length] = item;
|
||||
++pq->length;
|
||||
bubble_up(pq, pq->length-1);
|
||||
bubble_up(pq, pq->length - 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* nghttp2_pq_top(nghttp2_pq *pq)
|
||||
{
|
||||
if(pq->length == 0) {
|
||||
void *nghttp2_pq_top(nghttp2_pq *pq) {
|
||||
if (pq->length == 0) {
|
||||
return NULL;
|
||||
} else {
|
||||
return pq->q[0];
|
||||
}
|
||||
}
|
||||
|
||||
static void bubble_down(nghttp2_pq *pq, size_t index)
|
||||
{
|
||||
size_t lchild = index*2+1;
|
||||
static void bubble_down(nghttp2_pq *pq, size_t index) {
|
||||
size_t lchild = index * 2 + 1;
|
||||
size_t minindex = index;
|
||||
size_t i, j;
|
||||
for(i = 0; i < 2; ++i) {
|
||||
j = lchild+i;
|
||||
if(j >= pq->length) {
|
||||
for (i = 0; i < 2; ++i) {
|
||||
j = lchild + i;
|
||||
if (j >= pq->length) {
|
||||
break;
|
||||
}
|
||||
if(pq->compar(pq->q[minindex], pq->q[j]) > 0) {
|
||||
if (pq->compar(pq->q[minindex], pq->q[j]) > 0) {
|
||||
minindex = j;
|
||||
}
|
||||
}
|
||||
if(minindex != index) {
|
||||
if (minindex != index) {
|
||||
swap(pq, index, minindex);
|
||||
bubble_down(pq, minindex);
|
||||
}
|
||||
}
|
||||
|
||||
void nghttp2_pq_pop(nghttp2_pq *pq)
|
||||
{
|
||||
if(pq->length > 0) {
|
||||
pq->q[0] = pq->q[pq->length-1];
|
||||
void nghttp2_pq_pop(nghttp2_pq *pq) {
|
||||
if (pq->length > 0) {
|
||||
pq->q[0] = pq->q[pq->length - 1];
|
||||
--pq->length;
|
||||
bubble_down(pq, 0);
|
||||
}
|
||||
}
|
||||
|
||||
int nghttp2_pq_empty(nghttp2_pq *pq)
|
||||
{
|
||||
return pq->length == 0;
|
||||
}
|
||||
int nghttp2_pq_empty(nghttp2_pq *pq) { return pq->length == 0; }
|
||||
|
||||
size_t nghttp2_pq_size(nghttp2_pq *pq)
|
||||
{
|
||||
return pq->length;
|
||||
}
|
||||
size_t nghttp2_pq_size(nghttp2_pq *pq) { 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;
|
||||
int rv = 0;
|
||||
if(pq->length == 0) {
|
||||
if (pq->length == 0) {
|
||||
return;
|
||||
}
|
||||
for(i = 0; i < pq->length; ++i) {
|
||||
for (i = 0; i < pq->length; ++i) {
|
||||
rv |= (*fun)(pq->q[i], arg);
|
||||
}
|
||||
if(rv) {
|
||||
for(i = pq->length; i > 0; --i) {
|
||||
if (rv) {
|
||||
for (i = pq->length; i > 0; --i) {
|
||||
bubble_down(pq, i - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,17 +26,20 @@
|
||||
#define NGHTTP2_PQ_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
#include "nghttp2_int.h"
|
||||
#include "nghttp2_mem.h"
|
||||
|
||||
/* Implementation of priority queue */
|
||||
|
||||
typedef struct {
|
||||
/* The pointer to the pointer to the item stored */
|
||||
void **q;
|
||||
/* Memory allocator */
|
||||
nghttp2_mem *mem;
|
||||
/* The number of items sotred */
|
||||
size_t length;
|
||||
/* The maximum number of items this pq can store. This is
|
||||
@@ -55,7 +58,7 @@ typedef struct {
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* 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
|
||||
@@ -78,7 +81,7 @@ int nghttp2_pq_push(nghttp2_pq *pq, void *item);
|
||||
* Returns item at the top of the queue |pq|. If the queue is empty,
|
||||
* this function returns NULL.
|
||||
*/
|
||||
void* nghttp2_pq_top(nghttp2_pq *pq);
|
||||
void *nghttp2_pq_top(nghttp2_pq *pq);
|
||||
|
||||
/*
|
||||
* Pops item at the top of the queue |pq|. The popped item is not
|
||||
|
||||
@@ -26,23 +26,19 @@
|
||||
|
||||
void nghttp2_priority_spec_init(nghttp2_priority_spec *pri_spec,
|
||||
int32_t stream_id, int32_t weight,
|
||||
int exclusive)
|
||||
{
|
||||
int exclusive) {
|
||||
pri_spec->stream_id = stream_id;
|
||||
pri_spec->weight = weight;
|
||||
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->weight = NGHTTP2_DEFAULT_WEIGHT;
|
||||
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 &&
|
||||
pri_spec->weight == NGHTTP2_DEFAULT_WEIGHT &&
|
||||
pri_spec->exclusive == 0;
|
||||
pri_spec->weight == NGHTTP2_DEFAULT_WEIGHT && pri_spec->exclusive == 0;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#define NGHTTP2_PRIORITY_SPEC_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
@@ -27,18 +27,16 @@
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
void nghttp2_queue_init(nghttp2_queue *queue)
|
||||
{
|
||||
void nghttp2_queue_init(nghttp2_queue *queue) {
|
||||
queue->front = queue->back = NULL;
|
||||
}
|
||||
|
||||
void nghttp2_queue_free(nghttp2_queue *queue)
|
||||
{
|
||||
if(!queue) {
|
||||
void nghttp2_queue_free(nghttp2_queue *queue) {
|
||||
if (!queue) {
|
||||
return;
|
||||
} else {
|
||||
nghttp2_queue_cell *p = queue->front;
|
||||
while(p) {
|
||||
while (p) {
|
||||
nghttp2_queue_cell *next = p->next;
|
||||
free(p);
|
||||
p = next;
|
||||
@@ -46,16 +44,15 @@ void nghttp2_queue_free(nghttp2_queue *queue)
|
||||
}
|
||||
}
|
||||
|
||||
int nghttp2_queue_push(nghttp2_queue *queue, void *data)
|
||||
{
|
||||
nghttp2_queue_cell *new_cell = (nghttp2_queue_cell*)malloc
|
||||
(sizeof(nghttp2_queue_cell));
|
||||
if(!new_cell) {
|
||||
int nghttp2_queue_push(nghttp2_queue *queue, void *data) {
|
||||
nghttp2_queue_cell *new_cell =
|
||||
(nghttp2_queue_cell *)malloc(sizeof(nghttp2_queue_cell));
|
||||
if (!new_cell) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
new_cell->data = data;
|
||||
new_cell->next = NULL;
|
||||
if(queue->back) {
|
||||
if (queue->back) {
|
||||
queue->back->next = new_cell;
|
||||
queue->back = new_cell;
|
||||
|
||||
@@ -65,30 +62,24 @@ int nghttp2_queue_push(nghttp2_queue *queue, void *data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nghttp2_queue_pop(nghttp2_queue *queue)
|
||||
{
|
||||
void nghttp2_queue_pop(nghttp2_queue *queue) {
|
||||
nghttp2_queue_cell *front = queue->front;
|
||||
assert(front);
|
||||
queue->front = front->next;
|
||||
if(front == queue->back) {
|
||||
if (front == queue->back) {
|
||||
queue->back = NULL;
|
||||
}
|
||||
free(front);
|
||||
}
|
||||
|
||||
void* nghttp2_queue_front(nghttp2_queue *queue)
|
||||
{
|
||||
void *nghttp2_queue_front(nghttp2_queue *queue) {
|
||||
assert(queue->front);
|
||||
return queue->front->data;
|
||||
}
|
||||
|
||||
void* nghttp2_queue_back(nghttp2_queue *queue)
|
||||
{
|
||||
void *nghttp2_queue_back(nghttp2_queue *queue) {
|
||||
assert(queue->back);
|
||||
return queue->back->data;
|
||||
}
|
||||
|
||||
int nghttp2_queue_empty(nghttp2_queue *queue)
|
||||
{
|
||||
return queue->front == NULL;
|
||||
}
|
||||
int nghttp2_queue_empty(nghttp2_queue *queue) { return queue->front == NULL; }
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#define NGHTTP2_QUEUE_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#include "config.h"
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
@@ -36,16 +36,14 @@ typedef struct nghttp2_queue_cell {
|
||||
struct nghttp2_queue_cell *next;
|
||||
} nghttp2_queue_cell;
|
||||
|
||||
typedef struct {
|
||||
nghttp2_queue_cell *front, *back;
|
||||
} nghttp2_queue;
|
||||
typedef struct { nghttp2_queue_cell *front, *back; } nghttp2_queue;
|
||||
|
||||
void nghttp2_queue_init(nghttp2_queue *queue);
|
||||
void nghttp2_queue_free(nghttp2_queue *queue);
|
||||
int nghttp2_queue_push(nghttp2_queue *queue, void *data);
|
||||
void nghttp2_queue_pop(nghttp2_queue *queue);
|
||||
void* nghttp2_queue_front(nghttp2_queue *queue);
|
||||
void* nghttp2_queue_back(nghttp2_queue *queue);
|
||||
void *nghttp2_queue_front(nghttp2_queue *queue);
|
||||
void *nghttp2_queue_back(nghttp2_queue *queue);
|
||||
int nghttp2_queue_empty(nghttp2_queue *queue);
|
||||
|
||||
#endif /* NGHTTP2_QUEUE_H */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -26,7 +26,7 @@
|
||||
#define NGHTTP2_SESSION_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
@@ -38,13 +38,15 @@
|
||||
#include "nghttp2_outbound_item.h"
|
||||
#include "nghttp2_int.h"
|
||||
#include "nghttp2_buf.h"
|
||||
#include "nghttp2_callbacks.h"
|
||||
#include "nghttp2_mem.h"
|
||||
|
||||
/*
|
||||
* Option flags.
|
||||
*/
|
||||
typedef enum {
|
||||
NGHTTP2_OPTMASK_NO_AUTO_STREAM_WINDOW_UPDATE = 1 << 0,
|
||||
NGHTTP2_OPTMASK_NO_AUTO_CONNECTION_WINDOW_UPDATE = 1 << 1
|
||||
NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE = 1 << 0,
|
||||
NGHTTP2_OPTMASK_RECV_CLIENT_PREFACE = 1 << 1,
|
||||
} nghttp2_optmask;
|
||||
|
||||
typedef enum {
|
||||
@@ -65,6 +67,8 @@ typedef struct {
|
||||
/* Internal state when receiving incoming frame */
|
||||
typedef enum {
|
||||
/* Receiving frame header */
|
||||
NGHTTP2_IB_READ_CLIENT_PREFACE,
|
||||
NGHTTP2_IB_READ_FIRST_SETTINGS,
|
||||
NGHTTP2_IB_READ_HEAD,
|
||||
NGHTTP2_IB_READ_NBYTE,
|
||||
NGHTTP2_IB_READ_HEADER_BLOCK,
|
||||
@@ -73,7 +77,6 @@ typedef enum {
|
||||
NGHTTP2_IB_FRAME_SIZE_ERROR,
|
||||
NGHTTP2_IB_READ_SETTINGS,
|
||||
NGHTTP2_IB_READ_GOAWAY_DEBUG,
|
||||
NGHTTP2_IB_READ_ALTSVC,
|
||||
NGHTTP2_IB_EXPECT_CONTINUATION,
|
||||
NGHTTP2_IB_IGN_CONTINUATION,
|
||||
NGHTTP2_IB_READ_PAD_DATA,
|
||||
@@ -81,7 +84,7 @@ typedef enum {
|
||||
NGHTTP2_IB_IGN_DATA
|
||||
} nghttp2_inbound_state;
|
||||
|
||||
#define NGHTTP2_INBOUND_NUM_IV 5
|
||||
#define NGHTTP2_INBOUND_NUM_IV 7
|
||||
|
||||
typedef struct {
|
||||
nghttp2_frame frame;
|
||||
@@ -105,11 +108,11 @@ typedef struct {
|
||||
size_t payloadleft;
|
||||
/* padding length for the current frame */
|
||||
size_t padlen;
|
||||
/* Sum of payload of (HEADERS | PUSH_PROMISE) + possible
|
||||
CONTINUATION received so far. */
|
||||
size_t headers_payload_length;
|
||||
nghttp2_inbound_state state;
|
||||
uint8_t raw_sbuf[8];
|
||||
/* Small buffer. Currently the largest contiguous chunk to buffer
|
||||
is frame header. We buffer part of payload, but they are smaller
|
||||
than frame header. */
|
||||
uint8_t raw_sbuf[NGHTTP2_FRAME_HDLEN];
|
||||
} nghttp2_inbound_frame;
|
||||
|
||||
typedef struct {
|
||||
@@ -117,30 +120,35 @@ typedef struct {
|
||||
uint32_t enable_push;
|
||||
uint32_t max_concurrent_streams;
|
||||
uint32_t initial_window_size;
|
||||
uint32_t max_frame_size;
|
||||
uint32_t max_header_list_size;
|
||||
} nghttp2_settings_storage;
|
||||
|
||||
typedef enum {
|
||||
NGHTTP2_GOAWAY_NONE = 0,
|
||||
/* Flag means GOAWAY frame is sent to the remote peer. */
|
||||
NGHTTP2_GOAWAY_SEND = 0x1,
|
||||
/* Flag means GOAWAY frame is received from the remote peer. */
|
||||
NGHTTP2_GOAWAY_RECV = 0x2,
|
||||
/* Flag means connection should be dropped after sending GOAWAY. */
|
||||
NGHTTP2_GOAWAY_FAIL_ON_SEND = 0x4
|
||||
/* Flag means that connection should be terminated after sending GOAWAY. */
|
||||
NGHTTP2_GOAWAY_TERM_ON_SEND = 0x1,
|
||||
/* Flag means GOAWAY to terminate session has been sent */
|
||||
NGHTTP2_GOAWAY_TERM_SENT = 0x2,
|
||||
} nghttp2_goaway_flag;
|
||||
|
||||
struct nghttp2_session {
|
||||
nghttp2_map /* <nghttp2_stream*> */ streams;
|
||||
nghttp2_stream_roots roots;
|
||||
/* Queue for outbound frames other than stream-creating HEADERS */
|
||||
/* Queue for outbound frames other than stream-creating HEADERS and
|
||||
DATA */
|
||||
nghttp2_pq /* <nghttp2_outbound_item*> */ ob_pq;
|
||||
/* Queue for outbound stream-creating HEADERS frame */
|
||||
nghttp2_pq /* <nghttp2_outbound_item*> */ ob_ss_pq;
|
||||
/* QUeue for DATA frame */
|
||||
nghttp2_pq /* <nghttp2_outbound_item*> */ ob_da_pq;
|
||||
nghttp2_active_outbound_item aob;
|
||||
nghttp2_inbound_frame iframe;
|
||||
nghttp2_hd_deflater hd_deflater;
|
||||
nghttp2_hd_inflater hd_inflater;
|
||||
nghttp2_session_callbacks callbacks;
|
||||
/* Memory allocator */
|
||||
nghttp2_mem mem;
|
||||
/* Sequence number of outbound frame to maintain the order of
|
||||
enqueue if priority is equal. */
|
||||
int64_t next_seq;
|
||||
@@ -165,6 +173,12 @@ struct nghttp2_session {
|
||||
/* Points to the oldest closed stream. NULL if there is no closed
|
||||
stream. Only used when session is initialized as server. */
|
||||
nghttp2_stream *closed_stream_tail;
|
||||
/* Points to the latest idle stream. NULL if there is no idle
|
||||
stream. Only used when session is initialized as server .*/
|
||||
nghttp2_stream *idle_stream_head;
|
||||
/* Points to the oldest idle stream. NULL if there is no idle
|
||||
stream. Only used when session is initialized as erver. */
|
||||
nghttp2_stream *idle_stream_tail;
|
||||
/* In-flight SETTINGS values. NULL does not necessarily mean there
|
||||
is no in-flight SETTINGS. */
|
||||
nghttp2_settings_entry *inflight_iv;
|
||||
@@ -182,6 +196,11 @@ struct nghttp2_session {
|
||||
|closed_stream_head|. The current implementation only keeps
|
||||
incoming streams and session is initialized as server. */
|
||||
size_t num_closed_streams;
|
||||
/* The number of idle streams kept in |streams| hash. The idle
|
||||
streams can be accessed through doubly linked list
|
||||
|idle_stream_head|. The current implementation only keeps idle
|
||||
streams if session is initialized as server. */
|
||||
size_t num_idle_streams;
|
||||
/* The number of bytes allocated for nvbuf */
|
||||
size_t nvbuflen;
|
||||
/* Next Stream ID. Made unsigned int to detect >= (1 << 31). */
|
||||
@@ -206,15 +225,10 @@ struct nghttp2_session {
|
||||
WINDOW_UPDATE. This could be negative after submitting negative
|
||||
value to WINDOW_UPDATE. */
|
||||
int32_t recv_window_size;
|
||||
/* The number of bytes in ignored DATA frame received without
|
||||
connection-level WINDOW_UPDATE. Since we do not call
|
||||
on_data_chunk_recv_callback for ignored DATA chunk, if
|
||||
nghttp2_option_set_no_auto_connection_window_update is used,
|
||||
application may not have a chance to send connection
|
||||
WINDOW_UPDATE. To fix this, we accumulate those received bytes,
|
||||
and if it exceeds certain number, we automatically send
|
||||
connection-level WINDOW_UPDATE. */
|
||||
int32_t recv_ign_window_size;
|
||||
/* The number of bytes consumed by the application and now is
|
||||
subject to WINDOW_UPDATE. This is only used when auto
|
||||
WINDOW_UPDATE is turned off. */
|
||||
int32_t consumed_size;
|
||||
/* The amount of recv_window_size cut using submitting negative
|
||||
value to WINDOW_UPDATE */
|
||||
int32_t recv_reduction;
|
||||
@@ -247,6 +261,17 @@ typedef struct {
|
||||
int32_t new_window_size, old_window_size;
|
||||
} nghttp2_update_window_size_arg;
|
||||
|
||||
typedef struct {
|
||||
nghttp2_session *session;
|
||||
/* linked list of streams to close */
|
||||
nghttp2_stream *head;
|
||||
int32_t last_stream_id;
|
||||
/* nonzero if GOAWAY is sent to peer, which means we are going to
|
||||
close incoming streams. zero if GOAWAY is received from peer and
|
||||
we are going to close outgoing streams. */
|
||||
int incoming;
|
||||
} nghttp2_close_stream_on_goaway_arg;
|
||||
|
||||
/* TODO stream timeout etc */
|
||||
|
||||
/*
|
||||
@@ -257,24 +282,28 @@ int nghttp2_session_is_my_stream_id(nghttp2_session *session,
|
||||
int32_t stream_id);
|
||||
|
||||
/*
|
||||
* Adds frame |frame| to the outbound queue in |session|. The
|
||||
* |frame_cat| must be either NGHTTP2_CTRL or NGHTTP2_DATA. If the
|
||||
* |frame_cat| is NGHTTP2_CTRL, the |frame| must be a pointer to
|
||||
* nghttp2_frame. If the |frame_cat| is NGHTTP2_DATA, it must be a
|
||||
* pointer to nghttp2_private_data. |aux_data| is a pointer to the arbitrary
|
||||
* data. Its interpretation is defined per the type of the frame. When
|
||||
* this function succeeds, it takes ownership of |frame| and
|
||||
* |aux_data|, so caller must not free them on success.
|
||||
* Initializes |item|. No memory allocation is done in this function.
|
||||
* Don't call nghttp2_outbound_item_free() until frame member is
|
||||
* initialized.
|
||||
*/
|
||||
void nghttp2_session_outbound_item_init(nghttp2_session *session,
|
||||
nghttp2_outbound_item *item);
|
||||
|
||||
/*
|
||||
* Adds |item| to the outbound queue in |session|. When this function
|
||||
* succeeds, it takes ownership of |item|. So caller must not free it
|
||||
* on success.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
* NGHTTP2_ERR_STREAM_CLOSED
|
||||
* Stream already closed (DATA frame only)
|
||||
*/
|
||||
int nghttp2_session_add_frame(nghttp2_session *session,
|
||||
nghttp2_frame_category frame_cat,
|
||||
void *abs_frame, void *aux_data);
|
||||
int nghttp2_session_add_item(nghttp2_session *session,
|
||||
nghttp2_outbound_item *item);
|
||||
|
||||
/*
|
||||
* Adds RST_STREAM frame for the stream |stream_id| with the error
|
||||
@@ -291,9 +320,8 @@ int nghttp2_session_add_frame(nghttp2_session *session,
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
int nghttp2_session_add_rst_stream(nghttp2_session *session,
|
||||
int32_t stream_id,
|
||||
nghttp2_error_code error_code);
|
||||
int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id,
|
||||
uint32_t error_code);
|
||||
|
||||
/*
|
||||
* Adds PING frame. This is a convenient functin built on top of
|
||||
@@ -325,11 +353,9 @@ int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags,
|
||||
* NGHTTP2_ERR_INVALID_ARGUMENT
|
||||
* The |opaque_data_len| is too large.
|
||||
*/
|
||||
int nghttp2_session_add_goaway(nghttp2_session *session,
|
||||
int32_t last_stream_id,
|
||||
nghttp2_error_code error_code,
|
||||
const uint8_t *opaque_data,
|
||||
size_t opaque_data_len);
|
||||
int nghttp2_session_add_goaway(nghttp2_session *session, int32_t last_stream_id,
|
||||
uint32_t error_code, const uint8_t *opaque_data,
|
||||
size_t opaque_data_len, int terminate_on_send);
|
||||
|
||||
/*
|
||||
* Adds WINDOW_UPDATE frame with stream ID |stream_id| and
|
||||
@@ -371,9 +397,8 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags,
|
||||
* This function returns a pointer to created new stream object, or
|
||||
* NULL.
|
||||
*/
|
||||
nghttp2_stream* nghttp2_session_open_stream(nghttp2_session *session,
|
||||
int32_t stream_id,
|
||||
uint8_t flags,
|
||||
nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session,
|
||||
int32_t stream_id, uint8_t flags,
|
||||
nghttp2_priority_spec *pri_spec,
|
||||
nghttp2_stream_state initial_state,
|
||||
void *stream_user_data);
|
||||
@@ -399,7 +424,7 @@ nghttp2_stream* nghttp2_session_open_stream(nghttp2_session *session,
|
||||
* The callback function failed.
|
||||
*/
|
||||
int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id,
|
||||
nghttp2_error_code error_code);
|
||||
uint32_t error_code);
|
||||
|
||||
/*
|
||||
* Deletes |stream| from memory. After this function returns, stream
|
||||
@@ -418,6 +443,20 @@ void nghttp2_session_destroy_stream(nghttp2_session *session,
|
||||
void nghttp2_session_keep_closed_stream(nghttp2_session *session,
|
||||
nghttp2_stream *stream);
|
||||
|
||||
/*
|
||||
* Appends |stream| to linked list |session->idle_stream_head|. We
|
||||
* apply fixed limit for list size. To fit into that limit, one or
|
||||
* more oldest streams are removed from list as necessary.
|
||||
*/
|
||||
void nghttp2_session_keep_idle_stream(nghttp2_session *session,
|
||||
nghttp2_stream *stream);
|
||||
|
||||
/*
|
||||
* Detaches |stream| from idle streams linked list.
|
||||
*/
|
||||
void nghttp2_session_detach_idle_stream(nghttp2_session *session,
|
||||
nghttp2_stream *stream);
|
||||
|
||||
/*
|
||||
* Deletes closed stream to ensure that number of incoming streams
|
||||
* including active and closed is in the maximum number of allowed
|
||||
@@ -428,6 +467,12 @@ void nghttp2_session_keep_closed_stream(nghttp2_session *session,
|
||||
void nghttp2_session_adjust_closed_stream(nghttp2_session *session,
|
||||
ssize_t offset);
|
||||
|
||||
/*
|
||||
* Deletes idle stream to ensure that number of idle streams is in
|
||||
* certain limit.
|
||||
*/
|
||||
void nghttp2_session_adjust_idle_stream(nghttp2_session *session);
|
||||
|
||||
/*
|
||||
* If further receptions and transmissions over the stream |stream_id|
|
||||
* are disallowed, close the stream with error code NGHTTP2_NO_ERROR.
|
||||
@@ -441,7 +486,6 @@ void nghttp2_session_adjust_closed_stream(nghttp2_session *session,
|
||||
int nghttp2_session_close_stream_if_shut_rdwr(nghttp2_session *session,
|
||||
nghttp2_stream *stream);
|
||||
|
||||
|
||||
int nghttp2_session_end_request_headers_received(nghttp2_session *session,
|
||||
nghttp2_frame *frame,
|
||||
nghttp2_stream *stream);
|
||||
@@ -485,7 +529,6 @@ int nghttp2_session_on_headers_received(nghttp2_session *session,
|
||||
nghttp2_frame *frame,
|
||||
nghttp2_stream *stream);
|
||||
|
||||
|
||||
/*
|
||||
* Called when PRIORITY is received, assuming |frame| is properly
|
||||
* initialized.
|
||||
@@ -531,8 +574,7 @@ int nghttp2_session_on_rst_stream_received(nghttp2_session *session,
|
||||
* The read_callback failed
|
||||
*/
|
||||
int nghttp2_session_on_settings_received(nghttp2_session *session,
|
||||
nghttp2_frame *frame,
|
||||
int noack);
|
||||
nghttp2_frame *frame, int noack);
|
||||
|
||||
/*
|
||||
* Called when PUSH_PROMISE is received, assuming |frame| is properly
|
||||
@@ -597,21 +639,6 @@ int nghttp2_session_on_goaway_received(nghttp2_session *session,
|
||||
int nghttp2_session_on_window_update_received(nghttp2_session *session,
|
||||
nghttp2_frame *frame);
|
||||
|
||||
/*
|
||||
* Called when ALTSVC is received, assuming |frame| is properly
|
||||
* initialized.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
* NGHTTP2_ERR_CALLBACK_FAILURE
|
||||
* The callback function failed.
|
||||
*/
|
||||
int nghttp2_session_on_altsvc_received(nghttp2_session *session,
|
||||
nghttp2_frame *frame);
|
||||
|
||||
/*
|
||||
* Called when DATA is received, assuming |frame| is properly
|
||||
* initialized.
|
||||
@@ -632,25 +659,21 @@ int nghttp2_session_on_data_received(nghttp2_session *session,
|
||||
* could be NULL if such stream does not exist. This function returns
|
||||
* NULL if stream is marked as closed.
|
||||
*/
|
||||
nghttp2_stream* nghttp2_session_get_stream(nghttp2_session *session,
|
||||
nghttp2_stream *nghttp2_session_get_stream(nghttp2_session *session,
|
||||
int32_t stream_id);
|
||||
|
||||
/*
|
||||
* This function behaves like nghttp2_session_get_stream(), but it
|
||||
* returns stream object even if it is marked as closed.
|
||||
* returns stream object even if it is marked as closed or in
|
||||
* NGHTTP2_STREAM_IDLE state.
|
||||
*/
|
||||
nghttp2_stream* nghttp2_session_get_stream_raw(nghttp2_session *session,
|
||||
nghttp2_stream *nghttp2_session_get_stream_raw(nghttp2_session *session,
|
||||
int32_t stream_id);
|
||||
|
||||
/*
|
||||
* Packs DATA frame |frame| in wire frame format and stores it in
|
||||
* |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr|
|
||||
* length. This function expands |*buf_ptr| as necessary to store
|
||||
* given |frame|. It packs header in first 8 bytes starting
|
||||
* |*bufoff_ptr| offset. The |*bufoff_ptr| is calculated based on
|
||||
* usage of padding. Remaining bytes are the DATA apyload and are
|
||||
* filled using |frame->data_prd|. The length of payload is at most
|
||||
* |datamax| bytes.
|
||||
* |bufs|. Payload will be read using |aux_data->data_prd|. The
|
||||
* length of payload is at most |datamax| bytes.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
@@ -664,16 +687,15 @@ nghttp2_stream* nghttp2_session_get_stream_raw(nghttp2_session *session,
|
||||
* NGHTTP2_ERR_CALLBACK_FAILURE
|
||||
* The read_callback failed (session error).
|
||||
*/
|
||||
int nghttp2_session_pack_data(nghttp2_session *session,
|
||||
nghttp2_bufs *bufs,
|
||||
size_t datamax,
|
||||
nghttp2_private_data *frame);
|
||||
int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs,
|
||||
size_t datamax, nghttp2_frame *frame,
|
||||
nghttp2_data_aux_data *aux_data);
|
||||
|
||||
/*
|
||||
* Returns top of outbound frame queue. This function returns NULL if
|
||||
* queue is empty.
|
||||
*/
|
||||
nghttp2_outbound_item* nghttp2_session_get_ob_pq_top(nghttp2_session *session);
|
||||
nghttp2_outbound_item *nghttp2_session_get_ob_pq_top(nghttp2_session *session);
|
||||
|
||||
/*
|
||||
* Pops and returns next item to send. If there is no such item,
|
||||
@@ -682,8 +704,8 @@ nghttp2_outbound_item* nghttp2_session_get_ob_pq_top(nghttp2_session *session);
|
||||
* session->ob_ss_pq has item and max concurrent streams is reached,
|
||||
* then this function returns NULL.
|
||||
*/
|
||||
nghttp2_outbound_item* nghttp2_session_pop_next_ob_item
|
||||
(nghttp2_session *session);
|
||||
nghttp2_outbound_item *
|
||||
nghttp2_session_pop_next_ob_item(nghttp2_session *session);
|
||||
|
||||
/*
|
||||
* Returns next item to send. If there is no such item, this function
|
||||
@@ -692,8 +714,8 @@ nghttp2_outbound_item* nghttp2_session_pop_next_ob_item
|
||||
* session->ob_ss_pq has item and max concurrent streams is reached,
|
||||
* then this function returns NULL.
|
||||
*/
|
||||
nghttp2_outbound_item* nghttp2_session_get_next_ob_item
|
||||
(nghttp2_session *session);
|
||||
nghttp2_outbound_item *
|
||||
nghttp2_session_get_next_ob_item(nghttp2_session *session);
|
||||
|
||||
/*
|
||||
* Updates local settings with the |iv|. The number of elements in the
|
||||
@@ -725,9 +747,9 @@ int nghttp2_session_update_local_settings(nghttp2_session *session,
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
int nghttp2_session_reprioritize_stream
|
||||
(nghttp2_session *session, nghttp2_stream *stream,
|
||||
const nghttp2_priority_spec *pri_spec);
|
||||
int nghttp2_session_reprioritize_stream(nghttp2_session *session,
|
||||
nghttp2_stream *stream,
|
||||
const nghttp2_priority_spec *pri_spec);
|
||||
|
||||
/*
|
||||
* Terminates current |session| with the |error_code|. The |reason|
|
||||
@@ -741,7 +763,8 @@ int nghttp2_session_reprioritize_stream
|
||||
* NGHTTP2_ERR_INVALID_ARGUMENT
|
||||
* The |reason| is too long.
|
||||
*/
|
||||
int nghttp2_session_terminate_session_with_reason
|
||||
(nghttp2_session *session, nghttp2_error_code error_code, const char *reason);
|
||||
int nghttp2_session_terminate_session_with_reason(nghttp2_session *session,
|
||||
uint32_t error_code,
|
||||
const char *reason);
|
||||
|
||||
#endif /* NGHTTP2_SESSION_H */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -26,7 +26,7 @@
|
||||
#define NGHTTP2_STREAM_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
@@ -65,7 +65,10 @@ typedef enum {
|
||||
memory. */
|
||||
NGHTTP2_STREAM_CLOSING,
|
||||
/* PUSH_PROMISE is received or sent */
|
||||
NGHTTP2_STREAM_RESERVED
|
||||
NGHTTP2_STREAM_RESERVED,
|
||||
/* Stream is created in this state if it is used as anchor in
|
||||
dependency tree. */
|
||||
NGHTTP2_STREAM_IDLE
|
||||
} nghttp2_stream_state;
|
||||
|
||||
typedef enum {
|
||||
@@ -85,19 +88,19 @@ typedef enum {
|
||||
NGHTTP2_STREAM_FLAG_PUSH = 0x01,
|
||||
/* Indicates that this stream was closed */
|
||||
NGHTTP2_STREAM_FLAG_CLOSED = 0x02,
|
||||
/* Indicates the DATA is deferred due to flow control. */
|
||||
/* Indicates the item is deferred due to flow control. */
|
||||
NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL = 0x04,
|
||||
/* Indicates the DATA is deferred by user callback */
|
||||
/* Indicates the item is deferred by user callback */
|
||||
NGHTTP2_STREAM_FLAG_DEFERRED_USER = 0x08,
|
||||
/* bitwise OR of NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL and
|
||||
NGHTTP2_STREAM_FLAG_DEFERRED_USER. */
|
||||
NGHTTP2_STREAM_FLAG_DEFERRED_ALL = 0x0c
|
||||
NGHTTP2_STREAM_FLAG_DEFERRED_ALL = 0x0c
|
||||
|
||||
} nghttp2_stream_flag;
|
||||
|
||||
typedef enum {
|
||||
NGHTTP2_STREAM_DPRI_NONE = 0,
|
||||
NGHTTP2_STREAM_DPRI_NO_DATA = 0x01,
|
||||
NGHTTP2_STREAM_DPRI_NO_ITEM = 0x01,
|
||||
NGHTTP2_STREAM_DPRI_TOP = 0x02,
|
||||
NGHTTP2_STREAM_DPRI_REST = 0x04
|
||||
} nghttp2_stream_dpri;
|
||||
@@ -126,21 +129,21 @@ struct nghttp2_stream {
|
||||
doubly-linked list and first element is pointed by
|
||||
roots->head. */
|
||||
nghttp2_stream *root_prev, *root_next;
|
||||
/* When stream is kept after closure, it may be kept in single
|
||||
/* When stream is kept after closure, it may be kept in doubly
|
||||
linked list pointed by nghttp2_session closed_stream_head.
|
||||
closed_next points to the next stream object if it is the element
|
||||
of the list. */
|
||||
nghttp2_stream *closed_next;
|
||||
nghttp2_stream *closed_prev, *closed_next;
|
||||
/* pointer to roots, which tracks dependency tree roots */
|
||||
nghttp2_stream_roots *roots;
|
||||
/* The arbitrary data provided by user for this stream. */
|
||||
void *stream_user_data;
|
||||
/* DATA frame item */
|
||||
nghttp2_outbound_item *data_item;
|
||||
/* Item to send */
|
||||
nghttp2_outbound_item *item;
|
||||
/* stream ID */
|
||||
int32_t stream_id;
|
||||
/* categorized priority of this stream. Only stream bearing
|
||||
NGHTTP2_STREAM_DPRI_TOP can send DATA frame. */
|
||||
NGHTTP2_STREAM_DPRI_TOP can send item. */
|
||||
nghttp2_stream_dpri dpri;
|
||||
/* the number of streams in subtree */
|
||||
size_t num_substreams;
|
||||
@@ -151,6 +154,10 @@ struct nghttp2_stream {
|
||||
WINDOW_UPDATE. This could be negative after submitting negative
|
||||
value to WINDOW_UPDATE */
|
||||
int32_t recv_window_size;
|
||||
/* The number of bytes consumed by the application and now is
|
||||
subject to WINDOW_UPDATE. This is only used when auto
|
||||
WINDOW_UPDATE is turned off. */
|
||||
int32_t consumed_size;
|
||||
/* The amount of recv_window_size cut using submitting negative
|
||||
value to WINDOW_UPDATE */
|
||||
int32_t recv_reduction;
|
||||
@@ -168,21 +175,19 @@ struct nghttp2_stream {
|
||||
descendant with dpri == NGHTTP2_STREAM_DPRI_TOP. We use this
|
||||
value to calculate effective weight. */
|
||||
int32_t sum_norest_weight;
|
||||
/* sum of weight of direct descendants whose dpri value is
|
||||
NGHTTP2_STREAM_DPRI_TOP */
|
||||
int32_t sum_top_weight;
|
||||
nghttp2_stream_state state;
|
||||
/* This is bitwise-OR of 0 or more of nghttp2_stream_flag. */
|
||||
uint8_t flags;
|
||||
/* Bitwise OR of zero or more nghttp2_shut_flag values */
|
||||
uint8_t shut_flags;
|
||||
/* nonzero if blocked was sent and remote_window_size is still 0 or
|
||||
negative */
|
||||
uint8_t blocked_sent;
|
||||
};
|
||||
|
||||
void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
|
||||
uint8_t flags,
|
||||
nghttp2_stream_state initial_state,
|
||||
int32_t weight,
|
||||
nghttp2_stream_roots *roots,
|
||||
uint8_t flags, nghttp2_stream_state initial_state,
|
||||
int32_t weight, nghttp2_stream_roots *roots,
|
||||
int32_t remote_initial_window_size,
|
||||
int32_t local_initial_window_size,
|
||||
void *stream_user_data);
|
||||
@@ -196,9 +201,9 @@ void nghttp2_stream_free(nghttp2_stream *stream);
|
||||
void nghttp2_stream_shutdown(nghttp2_stream *stream, nghttp2_shut_flag flag);
|
||||
|
||||
/*
|
||||
* Defer DATA frame |stream->data_item|. We won't call this function
|
||||
* in the situation where |stream->data_item| == NULL. If |flags| is
|
||||
* bitwise OR of zero or more of NGHTTP2_STREAM_FLAG_DEFERRED_USER and
|
||||
* Defer |stream->item|. We won't call this function in the situation
|
||||
* where |stream->item| == NULL. The |flags| is bitwise OR of zero or
|
||||
* more of NGHTTP2_STREAM_FLAG_DEFERRED_USER and
|
||||
* NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL. The |flags| indicates
|
||||
* the reason of this action.
|
||||
*
|
||||
@@ -208,25 +213,27 @@ void nghttp2_stream_shutdown(nghttp2_stream *stream, nghttp2_shut_flag flag);
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
int nghttp2_stream_defer_data(nghttp2_stream *stream, uint8_t flags,
|
||||
nghttp2_pq *pq, uint64_t cycle);
|
||||
int nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags,
|
||||
nghttp2_session *session);
|
||||
|
||||
/*
|
||||
* Detaches deferred data in this stream and it is back to active
|
||||
* state. The flags NGHTTP2_STREAM_FLAG_DEFERRED_USER and
|
||||
* NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL are cleared if they are
|
||||
* set.
|
||||
* Put back deferred data in this stream to active state. The |flags|
|
||||
* are one or more of bitwise OR of the following values:
|
||||
* NGHTTP2_STREAM_FLAG_DEFERRED_USER and
|
||||
* NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL and given masks are
|
||||
* cleared if they are set. So even if this function is called, if
|
||||
* one of flag is still set, data does not become active.
|
||||
*/
|
||||
int nghttp2_stream_resume_deferred_data(nghttp2_stream *stream,
|
||||
nghttp2_pq *pq, uint64_t cycle);
|
||||
int nghttp2_stream_resume_deferred_item(nghttp2_stream *stream, uint8_t flags,
|
||||
nghttp2_session *session);
|
||||
|
||||
/*
|
||||
* Returns nonzero if data item is deferred by whatever reason.
|
||||
* Returns nonzero if item is deferred by whatever reason.
|
||||
*/
|
||||
int nghttp2_stream_check_deferred_data(nghttp2_stream *stream);
|
||||
int nghttp2_stream_check_deferred_item(nghttp2_stream *stream);
|
||||
|
||||
/*
|
||||
* Returns nonzero if data item is deferred by flow control.
|
||||
* Returns nonzero if item is deferred by flow control.
|
||||
*/
|
||||
int nghttp2_stream_check_deferred_by_flow_control(nghttp2_stream *stream);
|
||||
|
||||
@@ -238,10 +245,9 @@ int nghttp2_stream_check_deferred_by_flow_control(nghttp2_stream *stream);
|
||||
* This function returns 0 if it succeeds or -1. The failure is due to
|
||||
* overflow.
|
||||
*/
|
||||
int nghttp2_stream_update_remote_initial_window_size
|
||||
(nghttp2_stream *stream,
|
||||
int32_t new_initial_window_size,
|
||||
int32_t old_initial_window_size);
|
||||
int nghttp2_stream_update_remote_initial_window_size(
|
||||
nghttp2_stream *stream, int32_t new_initial_window_size,
|
||||
int32_t old_initial_window_size);
|
||||
|
||||
/*
|
||||
* Updates the local window size with the new value
|
||||
@@ -251,10 +257,9 @@ int nghttp2_stream_update_remote_initial_window_size
|
||||
* This function returns 0 if it succeeds or -1. The failure is due to
|
||||
* overflow.
|
||||
*/
|
||||
int nghttp2_stream_update_local_initial_window_size
|
||||
(nghttp2_stream *stream,
|
||||
int32_t new_initial_window_size,
|
||||
int32_t old_initial_window_size);
|
||||
int nghttp2_stream_update_local_initial_window_size(
|
||||
nghttp2_stream *stream, int32_t new_initial_window_size,
|
||||
int32_t old_initial_window_size);
|
||||
|
||||
/*
|
||||
* Call this function if promised stream |stream| is replied with
|
||||
@@ -267,7 +272,7 @@ void nghttp2_stream_promise_fulfilled(nghttp2_stream *stream);
|
||||
* Returns the stream positioned in root of the dependency tree the
|
||||
* |stream| belongs to.
|
||||
*/
|
||||
nghttp2_stream* nghttp2_stream_get_dep_root(nghttp2_stream *stream);
|
||||
nghttp2_stream *nghttp2_stream_get_dep_root(nghttp2_stream *stream);
|
||||
|
||||
/*
|
||||
* Returns nonzero if |target| is found in subtree of |stream|.
|
||||
@@ -290,8 +295,8 @@ int32_t nghttp2_stream_dep_distributed_weight(nghttp2_stream *stream,
|
||||
* rather than stream->weight. This function is used to determine
|
||||
* weight in dependency tree.
|
||||
*/
|
||||
int32_t nghttp2_stream_dep_distributed_effective_weight
|
||||
(nghttp2_stream *stream, int32_t weight);
|
||||
int32_t nghttp2_stream_dep_distributed_effective_weight(nghttp2_stream *stream,
|
||||
int32_t weight);
|
||||
|
||||
/*
|
||||
* Makes the |stream| depend on the |dep_stream|. This dependency is
|
||||
@@ -308,8 +313,7 @@ void nghttp2_stream_dep_insert(nghttp2_stream *dep_stream,
|
||||
* not exclusive. This function assumes |stream->data| is NULL and no
|
||||
* dpri members are changed in this dependency tree.
|
||||
*/
|
||||
void nghttp2_stream_dep_add(nghttp2_stream *dep_stream,
|
||||
nghttp2_stream *stream);
|
||||
void nghttp2_stream_dep_add(nghttp2_stream *dep_stream, nghttp2_stream *stream);
|
||||
|
||||
/*
|
||||
* Removes the |stream| from the current dependency tree. This
|
||||
@@ -318,7 +322,7 @@ void nghttp2_stream_dep_add(nghttp2_stream *dep_stream,
|
||||
void nghttp2_stream_dep_remove(nghttp2_stream *stream);
|
||||
|
||||
/*
|
||||
* Attaches |data_item| to |stream|. Updates dpri members in this
|
||||
* Attaches |item| to |stream|. Updates dpri members in this
|
||||
* dependency tree.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
@@ -327,15 +331,14 @@ void nghttp2_stream_dep_remove(nghttp2_stream *stream);
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
int nghttp2_stream_attach_data(nghttp2_stream *stream,
|
||||
nghttp2_outbound_item *data_item,
|
||||
nghttp2_pq *pq,
|
||||
uint64_t cycle);
|
||||
int nghttp2_stream_attach_item(nghttp2_stream *stream,
|
||||
nghttp2_outbound_item *item,
|
||||
nghttp2_session *session);
|
||||
|
||||
/*
|
||||
* Detaches |stream->data_item|. Updates dpri members in this
|
||||
* dependency tree. This function does not free |stream->data_item|.
|
||||
* The caller must free it.
|
||||
* Detaches |stream->item|. Updates dpri members in this dependency
|
||||
* tree. This function does not free |stream->item|. The caller must
|
||||
* free it.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
@@ -343,9 +346,8 @@ int nghttp2_stream_attach_data(nghttp2_stream *stream,
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
int nghttp2_stream_detach_data(nghttp2_stream *stream, nghttp2_pq *pq,
|
||||
uint64_t cycle);
|
||||
|
||||
int nghttp2_stream_detach_item(nghttp2_stream *stream,
|
||||
nghttp2_session *session);
|
||||
|
||||
/*
|
||||
* Makes the |stream| depend on the |dep_stream|. This dependency is
|
||||
@@ -359,8 +361,7 @@ int nghttp2_stream_detach_data(nghttp2_stream *stream, nghttp2_pq *pq,
|
||||
*/
|
||||
int nghttp2_stream_dep_insert_subtree(nghttp2_stream *dep_stream,
|
||||
nghttp2_stream *stream,
|
||||
nghttp2_pq *pq,
|
||||
uint64_t cycle);
|
||||
nghttp2_session *session);
|
||||
|
||||
/*
|
||||
* Makes the |stream| depend on the |dep_stream|. This dependency is
|
||||
@@ -374,8 +375,7 @@ int nghttp2_stream_dep_insert_subtree(nghttp2_stream *dep_stream,
|
||||
*/
|
||||
int nghttp2_stream_dep_add_subtree(nghttp2_stream *dep_stream,
|
||||
nghttp2_stream *stream,
|
||||
nghttp2_pq *pq,
|
||||
uint64_t cycle);
|
||||
nghttp2_session *session);
|
||||
|
||||
/*
|
||||
* Removes subtree whose root stream is |stream|. Removing subtree
|
||||
@@ -400,8 +400,8 @@ void nghttp2_stream_dep_remove_subtree(nghttp2_stream *stream);
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
int nghttp2_stream_dep_make_root(nghttp2_stream *stream, nghttp2_pq *pq,
|
||||
uint64_t cycle);
|
||||
int nghttp2_stream_dep_make_root(nghttp2_stream *stream,
|
||||
nghttp2_session *session);
|
||||
|
||||
/*
|
||||
* Makes the |stream| as root and all existing root streams become
|
||||
@@ -413,8 +413,9 @@ int nghttp2_stream_dep_make_root(nghttp2_stream *stream, nghttp2_pq *pq,
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
int nghttp2_stream_dep_all_your_stream_are_belong_to_us
|
||||
(nghttp2_stream *stream, nghttp2_pq *pq, uint64_t cycle);
|
||||
int
|
||||
nghttp2_stream_dep_all_your_stream_are_belong_to_us(nghttp2_stream *stream,
|
||||
nghttp2_session *session);
|
||||
|
||||
/*
|
||||
* Returns nonzero if |stream| is in any dependency tree.
|
||||
|
||||
@@ -35,59 +35,47 @@
|
||||
/* This function takes ownership of |nva_copy|. Regardless of the
|
||||
return value, the caller must not free |nva_copy| after this
|
||||
function returns. */
|
||||
static int32_t submit_headers_shared
|
||||
(nghttp2_session *session,
|
||||
uint8_t flags,
|
||||
int32_t stream_id,
|
||||
const nghttp2_priority_spec *pri_spec,
|
||||
nghttp2_nv *nva_copy,
|
||||
size_t nvlen,
|
||||
const nghttp2_data_provider *data_prd,
|
||||
void *stream_user_data)
|
||||
{
|
||||
static int32_t submit_headers_shared(nghttp2_session *session, uint8_t flags,
|
||||
int32_t stream_id,
|
||||
const nghttp2_priority_spec *pri_spec,
|
||||
nghttp2_nv *nva_copy, size_t nvlen,
|
||||
const nghttp2_data_provider *data_prd,
|
||||
void *stream_user_data,
|
||||
uint8_t attach_stream) {
|
||||
int rv;
|
||||
uint8_t flags_copy;
|
||||
nghttp2_outbound_item *item = NULL;
|
||||
nghttp2_frame *frame = NULL;
|
||||
nghttp2_data_provider *data_prd_copy = NULL;
|
||||
nghttp2_headers_aux_data *aux_data = NULL;
|
||||
nghttp2_headers_category hcat;
|
||||
nghttp2_mem *mem;
|
||||
|
||||
if(stream_id == 0) {
|
||||
mem = &session->mem;
|
||||
|
||||
if (stream_id == 0) {
|
||||
rv = NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if(data_prd != NULL && data_prd->read_callback != NULL) {
|
||||
data_prd_copy = malloc(sizeof(nghttp2_data_provider));
|
||||
if(data_prd_copy == NULL) {
|
||||
rv = NGHTTP2_ERR_NOMEM;
|
||||
goto fail;
|
||||
}
|
||||
*data_prd_copy = *data_prd;
|
||||
}
|
||||
if(data_prd || stream_user_data) {
|
||||
aux_data = malloc(sizeof(nghttp2_headers_aux_data));
|
||||
if(aux_data == NULL) {
|
||||
rv = NGHTTP2_ERR_NOMEM;
|
||||
goto fail;
|
||||
}
|
||||
aux_data->data_prd = data_prd_copy;
|
||||
aux_data->stream_user_data = stream_user_data;
|
||||
}
|
||||
frame = malloc(sizeof(nghttp2_frame));
|
||||
if(frame == NULL) {
|
||||
item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
|
||||
if (item == NULL) {
|
||||
rv = NGHTTP2_ERR_NOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
flags_copy =
|
||||
(flags & (NGHTTP2_FLAG_END_STREAM |
|
||||
NGHTTP2_FLAG_END_SEGMENT |
|
||||
NGHTTP2_FLAG_PRIORITY)) |
|
||||
NGHTTP2_FLAG_END_HEADERS;
|
||||
nghttp2_session_outbound_item_init(session, item);
|
||||
|
||||
if(stream_id == -1) {
|
||||
if(session->next_stream_id > INT32_MAX) {
|
||||
if (data_prd != NULL && data_prd->read_callback != NULL) {
|
||||
item->aux_data.headers.data_prd = *data_prd;
|
||||
}
|
||||
|
||||
item->aux_data.headers.stream_user_data = stream_user_data;
|
||||
item->aux_data.headers.attach_stream = attach_stream;
|
||||
|
||||
flags_copy = (flags & (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_PRIORITY)) |
|
||||
NGHTTP2_FLAG_END_HEADERS;
|
||||
|
||||
if (stream_id == -1) {
|
||||
if (session->next_stream_id > INT32_MAX) {
|
||||
rv = NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE;
|
||||
goto fail;
|
||||
}
|
||||
@@ -101,112 +89,110 @@ static int32_t submit_headers_shared
|
||||
hcat = NGHTTP2_HCAT_HEADERS;
|
||||
}
|
||||
|
||||
nghttp2_frame_headers_init(&frame->headers, flags_copy, stream_id,
|
||||
hcat, pri_spec, nva_copy, nvlen);
|
||||
frame = &item->frame;
|
||||
|
||||
nghttp2_frame_headers_init(&frame->headers, flags_copy, stream_id, hcat,
|
||||
pri_spec, nva_copy, nvlen);
|
||||
|
||||
rv = nghttp2_session_add_frame(session, NGHTTP2_CAT_CTRL, frame,
|
||||
aux_data);
|
||||
rv = nghttp2_session_add_item(session, item);
|
||||
|
||||
if(rv != 0) {
|
||||
nghttp2_frame_headers_free(&frame->headers);
|
||||
if (rv != 0) {
|
||||
nghttp2_frame_headers_free(&frame->headers, mem);
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
if(hcat == NGHTTP2_HCAT_REQUEST) {
|
||||
if (hcat == NGHTTP2_HCAT_REQUEST) {
|
||||
return stream_id;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
fail:
|
||||
/* nghttp2_frame_headers_init() takes ownership of nva_copy. */
|
||||
nghttp2_nv_array_del(nva_copy);
|
||||
fail2:
|
||||
free(frame);
|
||||
free(aux_data);
|
||||
free(data_prd_copy);
|
||||
nghttp2_nv_array_del(nva_copy, mem);
|
||||
fail2:
|
||||
nghttp2_mem_free(mem, item);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static void adjust_priority_spec_weight(nghttp2_priority_spec *pri_spec)
|
||||
{
|
||||
if(pri_spec->weight < NGHTTP2_MIN_WEIGHT) {
|
||||
static void adjust_priority_spec_weight(nghttp2_priority_spec *pri_spec) {
|
||||
if (pri_spec->weight < NGHTTP2_MIN_WEIGHT) {
|
||||
pri_spec->weight = NGHTTP2_MIN_WEIGHT;
|
||||
} else if(pri_spec->weight > NGHTTP2_MAX_WEIGHT) {
|
||||
} else if (pri_spec->weight > NGHTTP2_MAX_WEIGHT) {
|
||||
pri_spec->weight = NGHTTP2_MAX_WEIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t submit_headers_shared_nva
|
||||
(nghttp2_session *session,
|
||||
uint8_t flags,
|
||||
int32_t stream_id,
|
||||
const nghttp2_priority_spec *pri_spec,
|
||||
const nghttp2_nv *nva,
|
||||
size_t nvlen,
|
||||
const nghttp2_data_provider *data_prd,
|
||||
void *stream_user_data)
|
||||
{
|
||||
static int32_t submit_headers_shared_nva(nghttp2_session *session,
|
||||
uint8_t flags, int32_t stream_id,
|
||||
const nghttp2_priority_spec *pri_spec,
|
||||
const nghttp2_nv *nva, size_t nvlen,
|
||||
const nghttp2_data_provider *data_prd,
|
||||
void *stream_user_data,
|
||||
uint8_t attach_stream) {
|
||||
int rv;
|
||||
nghttp2_nv *nva_copy;
|
||||
nghttp2_priority_spec copy_pri_spec;
|
||||
nghttp2_mem *mem;
|
||||
|
||||
if(pri_spec) {
|
||||
mem = &session->mem;
|
||||
|
||||
if (pri_spec) {
|
||||
copy_pri_spec = *pri_spec;
|
||||
adjust_priority_spec_weight(©_pri_spec);
|
||||
} else {
|
||||
nghttp2_priority_spec_default_init(©_pri_spec);
|
||||
}
|
||||
|
||||
rv = nghttp2_nv_array_copy(&nva_copy, nva, nvlen);
|
||||
if(rv < 0) {
|
||||
rv = nghttp2_nv_array_copy(&nva_copy, nva, nvlen, mem);
|
||||
if (rv < 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return submit_headers_shared(session, flags, stream_id,
|
||||
©_pri_spec, nva_copy, nvlen, data_prd,
|
||||
stream_user_data);
|
||||
return submit_headers_shared(session, flags, stream_id, ©_pri_spec,
|
||||
nva_copy, nvlen, data_prd, stream_user_data,
|
||||
attach_stream);
|
||||
}
|
||||
|
||||
int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags,
|
||||
int32_t stream_id,
|
||||
const nghttp2_priority_spec *pri_spec,
|
||||
const nghttp2_nv *nva, size_t nvlen,
|
||||
void *stream_user_data)
|
||||
{
|
||||
void *stream_user_data) {
|
||||
flags &= NGHTTP2_FLAG_END_STREAM;
|
||||
|
||||
if(pri_spec && !nghttp2_priority_spec_check_default(pri_spec)) {
|
||||
if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec)) {
|
||||
flags |= NGHTTP2_FLAG_PRIORITY;
|
||||
} else {
|
||||
pri_spec = NULL;
|
||||
}
|
||||
|
||||
return submit_headers_shared_nva(session, flags, stream_id, pri_spec,
|
||||
nva, nvlen, NULL, stream_user_data);
|
||||
return submit_headers_shared_nva(session, flags, stream_id, pri_spec, nva,
|
||||
nvlen, NULL, stream_user_data, 0);
|
||||
}
|
||||
|
||||
|
||||
int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags,
|
||||
const uint8_t *opaque_data)
|
||||
{
|
||||
int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags _U_,
|
||||
const uint8_t *opaque_data) {
|
||||
return nghttp2_session_add_ping(session, NGHTTP2_FLAG_NONE, opaque_data);
|
||||
}
|
||||
|
||||
int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags,
|
||||
int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags _U_,
|
||||
int32_t stream_id,
|
||||
const nghttp2_priority_spec *pri_spec)
|
||||
{
|
||||
const nghttp2_priority_spec *pri_spec) {
|
||||
int rv;
|
||||
nghttp2_outbound_item *item;
|
||||
nghttp2_frame *frame;
|
||||
nghttp2_priority_spec copy_pri_spec;
|
||||
nghttp2_mem *mem;
|
||||
|
||||
if(stream_id == 0 || pri_spec == NULL) {
|
||||
mem = &session->mem;
|
||||
|
||||
if (stream_id == 0 || pri_spec == NULL) {
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if(stream_id == pri_spec->stream_id) {
|
||||
if (stream_id == pri_spec->stream_id) {
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
@@ -214,19 +200,23 @@ int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags,
|
||||
|
||||
adjust_priority_spec_weight(©_pri_spec);
|
||||
|
||||
frame = malloc(sizeof(nghttp2_frame));
|
||||
item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
|
||||
|
||||
if(frame == NULL) {
|
||||
if (item == NULL) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
nghttp2_session_outbound_item_init(session, item);
|
||||
|
||||
frame = &item->frame;
|
||||
|
||||
nghttp2_frame_priority_init(&frame->priority, stream_id, ©_pri_spec);
|
||||
|
||||
rv = nghttp2_session_add_frame(session, NGHTTP2_CAT_CTRL, frame, NULL);
|
||||
rv = nghttp2_session_add_item(session, item);
|
||||
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
nghttp2_frame_priority_free(&frame->priority);
|
||||
free(frame);
|
||||
nghttp2_mem_free(mem, item);
|
||||
|
||||
return rv;
|
||||
}
|
||||
@@ -234,95 +224,84 @@ int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nghttp2_submit_rst_stream(nghttp2_session *session, uint8_t flags,
|
||||
int32_t stream_id,
|
||||
nghttp2_error_code error_code)
|
||||
{
|
||||
if(stream_id == 0) {
|
||||
int nghttp2_submit_rst_stream(nghttp2_session *session, uint8_t flags _U_,
|
||||
int32_t stream_id, uint32_t error_code) {
|
||||
if (stream_id == 0) {
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
return nghttp2_session_add_rst_stream(session, stream_id, error_code);
|
||||
}
|
||||
|
||||
int nghttp2_submit_goaway(nghttp2_session *session, uint8_t flags,
|
||||
int32_t last_stream_id,
|
||||
nghttp2_error_code error_code,
|
||||
const uint8_t *opaque_data, size_t opaque_data_len)
|
||||
{
|
||||
return nghttp2_session_add_goaway(session, last_stream_id,
|
||||
error_code, opaque_data, opaque_data_len);
|
||||
int nghttp2_submit_goaway(nghttp2_session *session, uint8_t flags _U_,
|
||||
int32_t last_stream_id, uint32_t error_code,
|
||||
const uint8_t *opaque_data, size_t opaque_data_len) {
|
||||
return nghttp2_session_add_goaway(session, last_stream_id, error_code,
|
||||
opaque_data, opaque_data_len, 0);
|
||||
}
|
||||
|
||||
int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags,
|
||||
const nghttp2_settings_entry *iv, size_t niv)
|
||||
{
|
||||
int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags _U_,
|
||||
const nghttp2_settings_entry *iv, size_t niv) {
|
||||
return nghttp2_session_add_settings(session, NGHTTP2_FLAG_NONE, iv, niv);
|
||||
}
|
||||
|
||||
int32_t nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags,
|
||||
int32_t stream_id,
|
||||
const nghttp2_nv *nva, size_t nvlen,
|
||||
void *promised_stream_user_data)
|
||||
{
|
||||
int32_t nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags _U_,
|
||||
int32_t stream_id, const nghttp2_nv *nva,
|
||||
size_t nvlen,
|
||||
void *promised_stream_user_data) {
|
||||
nghttp2_outbound_item *item;
|
||||
nghttp2_frame *frame;
|
||||
nghttp2_nv *nva_copy;
|
||||
uint8_t flags_copy;
|
||||
nghttp2_headers_aux_data *aux_data = NULL;
|
||||
int32_t promised_stream_id;
|
||||
int rv;
|
||||
nghttp2_mem *mem;
|
||||
|
||||
if(stream_id == 0) {
|
||||
mem = &session->mem;
|
||||
|
||||
if (stream_id == 0 || nghttp2_session_is_my_stream_id(session, stream_id)) {
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if(!session->server) {
|
||||
if (!session->server) {
|
||||
return NGHTTP2_ERR_PROTO;
|
||||
}
|
||||
|
||||
frame = malloc(sizeof(nghttp2_frame));
|
||||
if(frame == NULL) {
|
||||
/* All 32bit signed stream IDs are spent. */
|
||||
if (session->next_stream_id > INT32_MAX) {
|
||||
return NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
|
||||
if (item == NULL) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
if(promised_stream_user_data) {
|
||||
aux_data = malloc(sizeof(nghttp2_headers_aux_data));
|
||||
if(aux_data == NULL) {
|
||||
free(frame);
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
aux_data->data_prd = NULL;
|
||||
aux_data->stream_user_data = promised_stream_user_data;
|
||||
}
|
||||
rv = nghttp2_nv_array_copy(&nva_copy, nva, nvlen);
|
||||
if(rv < 0) {
|
||||
free(aux_data);
|
||||
free(frame);
|
||||
|
||||
nghttp2_session_outbound_item_init(session, item);
|
||||
|
||||
item->aux_data.headers.stream_user_data = promised_stream_user_data;
|
||||
|
||||
frame = &item->frame;
|
||||
|
||||
rv = nghttp2_nv_array_copy(&nva_copy, nva, nvlen, mem);
|
||||
if (rv < 0) {
|
||||
nghttp2_mem_free(mem, item);
|
||||
return rv;
|
||||
}
|
||||
|
||||
flags_copy = NGHTTP2_FLAG_END_HEADERS;
|
||||
|
||||
/* All 32bit signed stream IDs are spent. */
|
||||
if(session->next_stream_id > INT32_MAX) {
|
||||
free(aux_data);
|
||||
free(frame);
|
||||
|
||||
return NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
promised_stream_id = session->next_stream_id;
|
||||
session->next_stream_id += 2;
|
||||
|
||||
nghttp2_frame_push_promise_init(&frame->push_promise, flags_copy,
|
||||
stream_id, promised_stream_id,
|
||||
nva_copy, nvlen);
|
||||
nghttp2_frame_push_promise_init(&frame->push_promise, flags_copy, stream_id,
|
||||
promised_stream_id, nva_copy, nvlen);
|
||||
|
||||
rv = nghttp2_session_add_frame(session, NGHTTP2_CAT_CTRL, frame, aux_data);
|
||||
rv = nghttp2_session_add_item(session, item);
|
||||
|
||||
if(rv != 0) {
|
||||
nghttp2_frame_push_promise_free(&frame->push_promise);
|
||||
free(aux_data);
|
||||
free(frame);
|
||||
if (rv != 0) {
|
||||
nghttp2_frame_push_promise_free(&frame->push_promise, mem);
|
||||
nghttp2_mem_free(mem, item);
|
||||
|
||||
return rv;
|
||||
}
|
||||
@@ -332,158 +311,66 @@ int32_t nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags,
|
||||
|
||||
int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags,
|
||||
int32_t stream_id,
|
||||
int32_t window_size_increment)
|
||||
{
|
||||
int32_t window_size_increment) {
|
||||
int rv;
|
||||
nghttp2_stream *stream;
|
||||
if(window_size_increment == 0) {
|
||||
nghttp2_stream *stream = 0;
|
||||
if (window_size_increment == 0) {
|
||||
return 0;
|
||||
}
|
||||
flags = 0;
|
||||
if(stream_id == 0) {
|
||||
rv = nghttp2_adjust_local_window_size(&session->local_window_size,
|
||||
&session->recv_window_size,
|
||||
&session->recv_reduction,
|
||||
&window_size_increment);
|
||||
if(rv != 0) {
|
||||
if (stream_id == 0) {
|
||||
rv = nghttp2_adjust_local_window_size(
|
||||
&session->local_window_size, &session->recv_window_size,
|
||||
&session->recv_reduction, &window_size_increment);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* recv_ign_window_size keeps track of ignored DATA bytes before
|
||||
any connection-level WINDOW_UPDATE therefore, we can reset it
|
||||
here. */
|
||||
session->recv_ign_window_size = 0;
|
||||
} else {
|
||||
stream = nghttp2_session_get_stream(session, stream_id);
|
||||
if(stream) {
|
||||
rv = nghttp2_adjust_local_window_size(&stream->local_window_size,
|
||||
&stream->recv_window_size,
|
||||
&stream->recv_reduction,
|
||||
&window_size_increment);
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
} else {
|
||||
if (!stream) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rv = nghttp2_adjust_local_window_size(
|
||||
&stream->local_window_size, &stream->recv_window_size,
|
||||
&stream->recv_reduction, &window_size_increment);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
if(window_size_increment > 0) {
|
||||
if (window_size_increment > 0) {
|
||||
if (stream_id == 0) {
|
||||
session->consumed_size =
|
||||
nghttp2_max(0, session->consumed_size - window_size_increment);
|
||||
} else {
|
||||
stream->consumed_size =
|
||||
nghttp2_max(0, stream->consumed_size - window_size_increment);
|
||||
}
|
||||
|
||||
return nghttp2_session_add_window_update(session, flags, stream_id,
|
||||
window_size_increment);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nghttp2_submit_altsvc(nghttp2_session *session, uint8_t flags,
|
||||
int32_t stream_id,
|
||||
uint32_t max_age, uint16_t port,
|
||||
const uint8_t *protocol_id, size_t protocol_id_len,
|
||||
const uint8_t *host, size_t host_len,
|
||||
const uint8_t *origin, size_t origin_len)
|
||||
{
|
||||
int rv;
|
||||
size_t varlen;
|
||||
uint8_t *var, *varp;
|
||||
nghttp2_frame *frame;
|
||||
nghttp2_ext_altsvc *altsvc;
|
||||
uint8_t *copy_protocol_id, *copy_host, *copy_origin;
|
||||
|
||||
if(!session->server) {
|
||||
return NGHTTP2_ERR_PROTO;
|
||||
}
|
||||
|
||||
varlen = protocol_id_len + host_len + origin_len;
|
||||
|
||||
/* 9 = fixed part 8 bytes + HOST_LEN 1 byte */
|
||||
if(varlen + 9 > NGHTTP2_MAX_PAYLOADLEN) {
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
altsvc = malloc(sizeof(nghttp2_ext_altsvc));
|
||||
|
||||
if(altsvc == NULL) {
|
||||
rv = NGHTTP2_ERR_NOMEM;
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if(varlen == 0) {
|
||||
var = NULL;
|
||||
copy_protocol_id = NULL;
|
||||
copy_host = NULL;
|
||||
copy_origin = NULL;
|
||||
} else {
|
||||
var = malloc(varlen);
|
||||
|
||||
if(var == NULL) {
|
||||
rv = NGHTTP2_ERR_NOMEM;
|
||||
|
||||
goto var_alloc_fail;
|
||||
}
|
||||
|
||||
varp = var;
|
||||
|
||||
memcpy(varp, protocol_id, protocol_id_len);
|
||||
|
||||
copy_protocol_id = varp;
|
||||
varp += protocol_id_len;
|
||||
|
||||
memcpy(varp, host, host_len);
|
||||
|
||||
copy_host = varp;
|
||||
varp += host_len;
|
||||
|
||||
memcpy(varp, origin, origin_len);
|
||||
|
||||
copy_origin = varp;
|
||||
}
|
||||
|
||||
frame = malloc(sizeof(nghttp2_frame));
|
||||
|
||||
if(frame == NULL) {
|
||||
rv = NGHTTP2_ERR_NOMEM;
|
||||
|
||||
goto frame_alloc_fail;
|
||||
}
|
||||
|
||||
frame->ext.payload = altsvc;
|
||||
|
||||
nghttp2_frame_altsvc_init(&frame->ext, stream_id, max_age, port,
|
||||
copy_protocol_id, protocol_id_len,
|
||||
copy_host, host_len, copy_origin, origin_len);
|
||||
|
||||
rv = nghttp2_session_add_frame(session, NGHTTP2_CAT_CTRL, frame, NULL);
|
||||
|
||||
if(rv != 0) {
|
||||
nghttp2_frame_altsvc_free(&frame->ext);
|
||||
free(frame);
|
||||
free(altsvc);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
int nghttp2_submit_altsvc(nghttp2_session *session _U_, uint8_t flags _U_,
|
||||
int32_t stream_id _U_, uint32_t max_age _U_,
|
||||
uint16_t port _U_, const uint8_t *protocol_id _U_,
|
||||
size_t protocol_id_len _U_, const uint8_t *host _U_,
|
||||
size_t host_len _U_, const uint8_t *origin _U_,
|
||||
size_t origin_len _U_) {
|
||||
return 0;
|
||||
|
||||
frame_alloc_fail:
|
||||
free(var);
|
||||
|
||||
var_alloc_fail:
|
||||
free(altsvc);
|
||||
|
||||
fail:
|
||||
return rv;
|
||||
}
|
||||
|
||||
static uint8_t set_request_flags(const nghttp2_priority_spec *pri_spec,
|
||||
const nghttp2_data_provider *data_prd)
|
||||
{
|
||||
const nghttp2_data_provider *data_prd) {
|
||||
uint8_t flags = NGHTTP2_FLAG_NONE;
|
||||
if(data_prd == NULL || data_prd->read_callback == NULL) {
|
||||
if (data_prd == NULL || data_prd->read_callback == NULL) {
|
||||
flags |= NGHTTP2_FLAG_END_STREAM;
|
||||
}
|
||||
|
||||
if(pri_spec) {
|
||||
if (pri_spec) {
|
||||
flags |= NGHTTP2_FLAG_PRIORITY;
|
||||
}
|
||||
|
||||
@@ -494,78 +381,84 @@ int32_t nghttp2_submit_request(nghttp2_session *session,
|
||||
const nghttp2_priority_spec *pri_spec,
|
||||
const nghttp2_nv *nva, size_t nvlen,
|
||||
const nghttp2_data_provider *data_prd,
|
||||
void *stream_user_data)
|
||||
{
|
||||
void *stream_user_data) {
|
||||
uint8_t flags;
|
||||
|
||||
if(pri_spec && nghttp2_priority_spec_check_default(pri_spec)) {
|
||||
if (pri_spec && nghttp2_priority_spec_check_default(pri_spec)) {
|
||||
pri_spec = NULL;
|
||||
}
|
||||
|
||||
flags = set_request_flags(pri_spec, data_prd);
|
||||
|
||||
return submit_headers_shared_nva(session, flags, -1, pri_spec,
|
||||
nva, nvlen,
|
||||
data_prd, stream_user_data);
|
||||
return submit_headers_shared_nva(session, flags, -1, pri_spec, nva, nvlen,
|
||||
data_prd, stream_user_data, 0);
|
||||
}
|
||||
|
||||
static uint8_t set_response_flags(const nghttp2_data_provider *data_prd)
|
||||
{
|
||||
static uint8_t set_response_flags(const nghttp2_data_provider *data_prd) {
|
||||
uint8_t flags = NGHTTP2_FLAG_NONE;
|
||||
if(data_prd == NULL || data_prd->read_callback == NULL) {
|
||||
if (data_prd == NULL || data_prd->read_callback == NULL) {
|
||||
flags |= NGHTTP2_FLAG_END_STREAM;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
int nghttp2_submit_response(nghttp2_session *session,
|
||||
int32_t stream_id,
|
||||
int nghttp2_submit_response(nghttp2_session *session, int32_t stream_id,
|
||||
const nghttp2_nv *nva, size_t nvlen,
|
||||
const nghttp2_data_provider *data_prd)
|
||||
{
|
||||
const nghttp2_data_provider *data_prd) {
|
||||
uint8_t flags = set_response_flags(data_prd);
|
||||
return submit_headers_shared_nva(session, flags, stream_id,
|
||||
NULL, nva, nvlen,
|
||||
data_prd, NULL);
|
||||
return submit_headers_shared_nva(session, flags, stream_id, NULL, nva, nvlen,
|
||||
data_prd, NULL, 1);
|
||||
}
|
||||
|
||||
int nghttp2_submit_data(nghttp2_session *session, uint8_t flags,
|
||||
int32_t stream_id,
|
||||
const nghttp2_data_provider *data_prd)
|
||||
{
|
||||
const nghttp2_data_provider *data_prd) {
|
||||
int rv;
|
||||
nghttp2_private_data *data_frame;
|
||||
uint8_t nflags = flags & (NGHTTP2_FLAG_END_STREAM |
|
||||
NGHTTP2_FLAG_END_SEGMENT);
|
||||
nghttp2_outbound_item *item;
|
||||
nghttp2_frame *frame;
|
||||
nghttp2_data_aux_data *aux_data;
|
||||
uint8_t nflags = flags & NGHTTP2_FLAG_END_STREAM;
|
||||
nghttp2_mem *mem;
|
||||
|
||||
if(stream_id == 0) {
|
||||
mem = &session->mem;
|
||||
|
||||
if (stream_id == 0) {
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
data_frame = malloc(sizeof(nghttp2_private_data));
|
||||
if(data_frame == NULL) {
|
||||
item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
|
||||
if (item == NULL) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
nghttp2_frame_private_data_init(data_frame, nflags, stream_id, data_prd);
|
||||
rv = nghttp2_session_add_frame(session, NGHTTP2_CAT_DATA, data_frame, NULL);
|
||||
if(rv != 0) {
|
||||
nghttp2_frame_private_data_free(data_frame);
|
||||
free(data_frame);
|
||||
|
||||
nghttp2_session_outbound_item_init(session, item);
|
||||
|
||||
frame = &item->frame;
|
||||
aux_data = &item->aux_data.data;
|
||||
aux_data->data_prd = *data_prd;
|
||||
aux_data->eof = 0;
|
||||
aux_data->flags = nflags;
|
||||
|
||||
/* flags are sent on transmission */
|
||||
nghttp2_frame_data_init(&frame->data, NGHTTP2_FLAG_NONE, stream_id);
|
||||
|
||||
rv = nghttp2_session_add_item(session, item);
|
||||
if (rv != 0) {
|
||||
nghttp2_frame_data_free(&frame->data);
|
||||
nghttp2_mem_free(mem, item);
|
||||
return rv;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t nghttp2_pack_settings_payload(uint8_t *buf,
|
||||
size_t buflen,
|
||||
ssize_t nghttp2_pack_settings_payload(uint8_t *buf, size_t buflen,
|
||||
const nghttp2_settings_entry *iv,
|
||||
size_t niv)
|
||||
{
|
||||
if(!nghttp2_iv_check(iv, niv)) {
|
||||
size_t niv) {
|
||||
if (!nghttp2_iv_check(iv, niv)) {
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if(buflen < (niv * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH)) {
|
||||
if (buflen < (niv * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH)) {
|
||||
return NGHTTP2_ERR_INSUFF_BUFSIZE;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#define NGHTTP2_SUBMIT_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
@@ -23,21 +23,16 @@
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
static nghttp2_info version = {
|
||||
NGHTTP2_VERSION_AGE,
|
||||
NGHTTP2_VERSION_NUM,
|
||||
NGHTTP2_VERSION,
|
||||
NGHTTP2_PROTO_VERSION_ID
|
||||
};
|
||||
static nghttp2_info version = {NGHTTP2_VERSION_AGE, NGHTTP2_VERSION_NUM,
|
||||
NGHTTP2_VERSION, NGHTTP2_PROTO_VERSION_ID};
|
||||
|
||||
nghttp2_info *nghttp2_version(int least_version)
|
||||
{
|
||||
if(least_version > NGHTTP2_VERSION_NUM)
|
||||
nghttp2_info *nghttp2_version(int least_version) {
|
||||
if (least_version > NGHTTP2_VERSION_NUM)
|
||||
return NULL;
|
||||
return &version;
|
||||
}
|
||||
|
||||
110
m4/ax_boost_asio.m4
Normal file
110
m4/ax_boost_asio.m4
Normal file
@@ -0,0 +1,110 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_boost_asio.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_BOOST_ASIO
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Test for Asio library from the Boost C++ libraries. The macro requires a
|
||||
# preceding call to AX_BOOST_BASE. Further documentation is available at
|
||||
# <http://randspringer.de/boost/index.html>.
|
||||
#
|
||||
# This macro calls:
|
||||
#
|
||||
# AC_SUBST(BOOST_ASIO_LIB)
|
||||
#
|
||||
# And sets:
|
||||
#
|
||||
# HAVE_BOOST_ASIO
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Thomas Porschberg <thomas@randspringer.de>
|
||||
# Copyright (c) 2008 Pete Greenwell <pete@mu.org>
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 16
|
||||
|
||||
AC_DEFUN([AX_BOOST_ASIO],
|
||||
[
|
||||
AC_ARG_WITH([boost-asio],
|
||||
AS_HELP_STRING([--with-boost-asio@<:@=special-lib@:>@],
|
||||
[use the ASIO library from boost - it is possible to specify a certain library for the linker
|
||||
e.g. --with-boost-asio=boost_system-gcc41-mt-1_34 ]),
|
||||
[
|
||||
if test "$withval" = "no"; then
|
||||
want_boost="no"
|
||||
elif test "$withval" = "yes"; then
|
||||
want_boost="yes"
|
||||
ax_boost_user_asio_lib=""
|
||||
else
|
||||
want_boost="yes"
|
||||
ax_boost_user_asio_lib="$withval"
|
||||
fi
|
||||
],
|
||||
[want_boost="yes"]
|
||||
)
|
||||
|
||||
if test "x$want_boost" = "xyes"; then
|
||||
AC_REQUIRE([AC_PROG_CC])
|
||||
CPPFLAGS_SAVED="$CPPFLAGS"
|
||||
CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
|
||||
export CPPFLAGS
|
||||
|
||||
LDFLAGS_SAVED="$LDFLAGS"
|
||||
LDFLAGS="$LDFLAGS $BOOST_LDFLAGS"
|
||||
export LDFLAGS
|
||||
|
||||
AC_CACHE_CHECK(whether the Boost::ASIO library is available,
|
||||
ax_cv_boost_asio,
|
||||
[AC_LANG_PUSH([C++])
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ @%:@include <boost/asio.hpp>
|
||||
]],
|
||||
[[
|
||||
|
||||
boost::asio::io_service io;
|
||||
boost::system::error_code timer_result;
|
||||
boost::asio::deadline_timer t(io);
|
||||
t.cancel();
|
||||
io.run_one();
|
||||
return 0;
|
||||
]])],
|
||||
ax_cv_boost_asio=yes, ax_cv_boost_asio=no)
|
||||
AC_LANG_POP([C++])
|
||||
])
|
||||
if test "x$ax_cv_boost_asio" = "xyes"; then
|
||||
AC_DEFINE(HAVE_BOOST_ASIO,,[define if the Boost::ASIO library is available])
|
||||
BN=boost_system
|
||||
BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'`
|
||||
if test "x$ax_boost_user_asio_lib" = "x"; then
|
||||
for ax_lib in `ls $BOOSTLIBDIR/libboost_system*.so* $BOOSTLIBDIR/libboost_system*.dylib* $BOOSTLIBDIR/libboost_system*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_system.*\)\.so.*$;\1;' -e 's;^lib\(boost_system.*\)\.dylib.*$;\1;' -e 's;^lib\(boost_system.*\)\.a.*$;\1;' ` ; do
|
||||
AC_CHECK_LIB($ax_lib, main, [BOOST_ASIO_LIB="-l$ax_lib" AC_SUBST(BOOST_ASIO_LIB) link_thread="yes" break],
|
||||
[link_thread="no"])
|
||||
done
|
||||
else
|
||||
for ax_lib in $ax_boost_user_asio_lib $BN-$ax_boost_user_asio_lib; do
|
||||
AC_CHECK_LIB($ax_lib, main,
|
||||
[BOOST_ASIO_LIB="-l$ax_lib" AC_SUBST(BOOST_ASIO_LIB) link_asio="yes" break],
|
||||
[link_asio="no"])
|
||||
done
|
||||
|
||||
fi
|
||||
if test "x$ax_lib" = "x"; then
|
||||
AC_MSG_ERROR(Could not find a version of the library!)
|
||||
fi
|
||||
if test "x$link_asio" = "xno"; then
|
||||
AC_MSG_ERROR(Could not link against $ax_lib !)
|
||||
fi
|
||||
fi
|
||||
|
||||
CPPFLAGS="$CPPFLAGS_SAVED"
|
||||
LDFLAGS="$LDFLAGS_SAVED"
|
||||
fi
|
||||
])
|
||||
275
m4/ax_boost_base.m4
Normal file
275
m4/ax_boost_base.m4
Normal file
@@ -0,0 +1,275 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_boost_base.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_BOOST_BASE([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Test for the Boost C++ libraries of a particular version (or newer)
|
||||
#
|
||||
# If no path to the installed boost library is given the macro searchs
|
||||
# under /usr, /usr/local, /opt and /opt/local and evaluates the
|
||||
# $BOOST_ROOT environment variable. Further documentation is available at
|
||||
# <http://randspringer.de/boost/index.html>.
|
||||
#
|
||||
# This macro calls:
|
||||
#
|
||||
# AC_SUBST(BOOST_CPPFLAGS) / AC_SUBST(BOOST_LDFLAGS)
|
||||
#
|
||||
# And sets:
|
||||
#
|
||||
# HAVE_BOOST
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Thomas Porschberg <thomas@randspringer.de>
|
||||
# Copyright (c) 2009 Peter Adolphs
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 25
|
||||
|
||||
AC_DEFUN([AX_BOOST_BASE],
|
||||
[
|
||||
AC_ARG_WITH([boost],
|
||||
[AS_HELP_STRING([--with-boost@<:@=ARG@:>@],
|
||||
[use Boost library from a standard location (ARG=yes),
|
||||
from the specified location (ARG=<path>),
|
||||
or disable it (ARG=no)
|
||||
@<:@ARG=yes@:>@ ])],
|
||||
[
|
||||
if test "$withval" = "no"; then
|
||||
want_boost="no"
|
||||
elif test "$withval" = "yes"; then
|
||||
want_boost="yes"
|
||||
ac_boost_path=""
|
||||
else
|
||||
want_boost="yes"
|
||||
ac_boost_path="$withval"
|
||||
fi
|
||||
],
|
||||
[want_boost="yes"])
|
||||
|
||||
|
||||
AC_ARG_WITH([boost-libdir],
|
||||
AS_HELP_STRING([--with-boost-libdir=LIB_DIR],
|
||||
[Force given directory for boost libraries. Note that this will override library path detection, so use this parameter only if default library detection fails and you know exactly where your boost libraries are located.]),
|
||||
[
|
||||
if test -d "$withval"
|
||||
then
|
||||
ac_boost_lib_path="$withval"
|
||||
else
|
||||
AC_MSG_ERROR(--with-boost-libdir expected directory name)
|
||||
fi
|
||||
],
|
||||
[ac_boost_lib_path=""]
|
||||
)
|
||||
|
||||
if test "x$want_boost" = "xyes"; then
|
||||
boost_lib_version_req=ifelse([$1], ,1.20.0,$1)
|
||||
boost_lib_version_req_shorten=`expr $boost_lib_version_req : '\([[0-9]]*\.[[0-9]]*\)'`
|
||||
boost_lib_version_req_major=`expr $boost_lib_version_req : '\([[0-9]]*\)'`
|
||||
boost_lib_version_req_minor=`expr $boost_lib_version_req : '[[0-9]]*\.\([[0-9]]*\)'`
|
||||
boost_lib_version_req_sub_minor=`expr $boost_lib_version_req : '[[0-9]]*\.[[0-9]]*\.\([[0-9]]*\)'`
|
||||
if test "x$boost_lib_version_req_sub_minor" = "x" ; then
|
||||
boost_lib_version_req_sub_minor="0"
|
||||
fi
|
||||
WANT_BOOST_VERSION=`expr $boost_lib_version_req_major \* 100000 \+ $boost_lib_version_req_minor \* 100 \+ $boost_lib_version_req_sub_minor`
|
||||
AC_MSG_CHECKING(for boostlib >= $boost_lib_version_req)
|
||||
succeeded=no
|
||||
|
||||
dnl On 64-bit systems check for system libraries in both lib64 and lib.
|
||||
dnl The former is specified by FHS, but e.g. Debian does not adhere to
|
||||
dnl this (as it rises problems for generic multi-arch support).
|
||||
dnl The last entry in the list is chosen by default when no libraries
|
||||
dnl are found, e.g. when only header-only libraries are installed!
|
||||
libsubdirs="lib"
|
||||
ax_arch=`uname -m`
|
||||
case $ax_arch in
|
||||
x86_64)
|
||||
libsubdirs="lib64 libx32 lib lib64"
|
||||
;;
|
||||
ppc64|s390x|sparc64|aarch64|ppc64le)
|
||||
libsubdirs="lib64 lib lib64 ppc64le"
|
||||
;;
|
||||
esac
|
||||
|
||||
dnl allow for real multi-arch paths e.g. /usr/lib/x86_64-linux-gnu. Give
|
||||
dnl them priority over the other paths since, if libs are found there, they
|
||||
dnl are almost assuredly the ones desired.
|
||||
AC_REQUIRE([AC_CANONICAL_HOST])
|
||||
libsubdirs="lib/${host_cpu}-${host_os} $libsubdirs"
|
||||
|
||||
case ${host_cpu} in
|
||||
i?86)
|
||||
libsubdirs="lib/i386-${host_os} $libsubdirs"
|
||||
;;
|
||||
esac
|
||||
|
||||
dnl first we check the system location for boost libraries
|
||||
dnl this location ist chosen if boost libraries are installed with the --layout=system option
|
||||
dnl or if you install boost with RPM
|
||||
if test "$ac_boost_path" != ""; then
|
||||
BOOST_CPPFLAGS="-I$ac_boost_path/include"
|
||||
for ac_boost_path_tmp in $libsubdirs; do
|
||||
if test -d "$ac_boost_path"/"$ac_boost_path_tmp" ; then
|
||||
BOOST_LDFLAGS="-L$ac_boost_path/$ac_boost_path_tmp"
|
||||
break
|
||||
fi
|
||||
done
|
||||
elif test "$cross_compiling" != yes; then
|
||||
for ac_boost_path_tmp in /usr /usr/local /opt /opt/local ; do
|
||||
if test -d "$ac_boost_path_tmp/include/boost" && test -r "$ac_boost_path_tmp/include/boost"; then
|
||||
for libsubdir in $libsubdirs ; do
|
||||
if ls "$ac_boost_path_tmp/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi
|
||||
done
|
||||
BOOST_LDFLAGS="-L$ac_boost_path_tmp/$libsubdir"
|
||||
BOOST_CPPFLAGS="-I$ac_boost_path_tmp/include"
|
||||
break;
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
dnl overwrite ld flags if we have required special directory with
|
||||
dnl --with-boost-libdir parameter
|
||||
if test "$ac_boost_lib_path" != ""; then
|
||||
BOOST_LDFLAGS="-L$ac_boost_lib_path"
|
||||
fi
|
||||
|
||||
CPPFLAGS_SAVED="$CPPFLAGS"
|
||||
CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
|
||||
export CPPFLAGS
|
||||
|
||||
LDFLAGS_SAVED="$LDFLAGS"
|
||||
LDFLAGS="$LDFLAGS $BOOST_LDFLAGS"
|
||||
export LDFLAGS
|
||||
|
||||
AC_REQUIRE([AC_PROG_CXX])
|
||||
AC_LANG_PUSH(C++)
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
|
||||
@%:@include <boost/version.hpp>
|
||||
]], [[
|
||||
#if BOOST_VERSION >= $WANT_BOOST_VERSION
|
||||
// Everything is okay
|
||||
#else
|
||||
# error Boost version is too old
|
||||
#endif
|
||||
]])],[
|
||||
AC_MSG_RESULT(yes)
|
||||
succeeded=yes
|
||||
found_system=yes
|
||||
],[
|
||||
])
|
||||
AC_LANG_POP([C++])
|
||||
|
||||
|
||||
|
||||
dnl if we found no boost with system layout we search for boost libraries
|
||||
dnl built and installed without the --layout=system option or for a staged(not installed) version
|
||||
if test "x$succeeded" != "xyes"; then
|
||||
_version=0
|
||||
if test "$ac_boost_path" != ""; then
|
||||
if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then
|
||||
for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do
|
||||
_version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'`
|
||||
V_CHECK=`expr $_version_tmp \> $_version`
|
||||
if test "$V_CHECK" = "1" ; then
|
||||
_version=$_version_tmp
|
||||
fi
|
||||
VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'`
|
||||
BOOST_CPPFLAGS="-I$ac_boost_path/include/boost-$VERSION_UNDERSCORE"
|
||||
done
|
||||
fi
|
||||
else
|
||||
if test "$cross_compiling" != yes; then
|
||||
for ac_boost_path in /usr /usr/local /opt /opt/local ; do
|
||||
if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then
|
||||
for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do
|
||||
_version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'`
|
||||
V_CHECK=`expr $_version_tmp \> $_version`
|
||||
if test "$V_CHECK" = "1" ; then
|
||||
_version=$_version_tmp
|
||||
best_path=$ac_boost_path
|
||||
fi
|
||||
done
|
||||
fi
|
||||
done
|
||||
|
||||
VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'`
|
||||
BOOST_CPPFLAGS="-I$best_path/include/boost-$VERSION_UNDERSCORE"
|
||||
if test "$ac_boost_lib_path" = ""; then
|
||||
for libsubdir in $libsubdirs ; do
|
||||
if ls "$best_path/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi
|
||||
done
|
||||
BOOST_LDFLAGS="-L$best_path/$libsubdir"
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "x$BOOST_ROOT" != "x"; then
|
||||
for libsubdir in $libsubdirs ; do
|
||||
if ls "$BOOST_ROOT/stage/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi
|
||||
done
|
||||
if test -d "$BOOST_ROOT" && test -r "$BOOST_ROOT" && test -d "$BOOST_ROOT/stage/$libsubdir" && test -r "$BOOST_ROOT/stage/$libsubdir"; then
|
||||
version_dir=`expr //$BOOST_ROOT : '.*/\(.*\)'`
|
||||
stage_version=`echo $version_dir | sed 's/boost_//' | sed 's/_/./g'`
|
||||
stage_version_shorten=`expr $stage_version : '\([[0-9]]*\.[[0-9]]*\)'`
|
||||
V_CHECK=`expr $stage_version_shorten \>\= $_version`
|
||||
if test "$V_CHECK" = "1" -a "$ac_boost_lib_path" = "" ; then
|
||||
AC_MSG_NOTICE(We will use a staged boost library from $BOOST_ROOT)
|
||||
BOOST_CPPFLAGS="-I$BOOST_ROOT"
|
||||
BOOST_LDFLAGS="-L$BOOST_ROOT/stage/$libsubdir"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
|
||||
export CPPFLAGS
|
||||
LDFLAGS="$LDFLAGS $BOOST_LDFLAGS"
|
||||
export LDFLAGS
|
||||
|
||||
AC_LANG_PUSH(C++)
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
|
||||
@%:@include <boost/version.hpp>
|
||||
]], [[
|
||||
#if BOOST_VERSION >= $WANT_BOOST_VERSION
|
||||
// Everything is okay
|
||||
#else
|
||||
# error Boost version is too old
|
||||
#endif
|
||||
]])],[
|
||||
AC_MSG_RESULT(yes)
|
||||
succeeded=yes
|
||||
found_system=yes
|
||||
],[
|
||||
])
|
||||
AC_LANG_POP([C++])
|
||||
fi
|
||||
|
||||
if test "$succeeded" != "yes" ; then
|
||||
if test "$_version" = "0" ; then
|
||||
AC_MSG_NOTICE([[We could not detect the boost libraries (version $boost_lib_version_req_shorten or higher). If you have a staged boost library (still not installed) please specify \$BOOST_ROOT in your environment and do not give a PATH to --with-boost option. If you are sure you have boost installed, then check your version number looking in <boost/version.hpp>. See http://randspringer.de/boost for more documentation.]])
|
||||
else
|
||||
AC_MSG_NOTICE([Your boost libraries seems to old (version $_version).])
|
||||
fi
|
||||
# execute ACTION-IF-NOT-FOUND (if present):
|
||||
ifelse([$3], , :, [$3])
|
||||
else
|
||||
AC_SUBST(BOOST_CPPFLAGS)
|
||||
AC_SUBST(BOOST_LDFLAGS)
|
||||
AC_DEFINE(HAVE_BOOST,,[define if the Boost library is available])
|
||||
# execute ACTION-IF-FOUND (if present):
|
||||
ifelse([$2], , :, [$2])
|
||||
fi
|
||||
|
||||
CPPFLAGS="$CPPFLAGS_SAVED"
|
||||
LDFLAGS="$LDFLAGS_SAVED"
|
||||
fi
|
||||
|
||||
])
|
||||
120
m4/ax_boost_system.m4
Normal file
120
m4/ax_boost_system.m4
Normal file
@@ -0,0 +1,120 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_boost_system.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_BOOST_SYSTEM
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Test for System library from the Boost C++ libraries. The macro requires
|
||||
# a preceding call to AX_BOOST_BASE. Further documentation is available at
|
||||
# <http://randspringer.de/boost/index.html>.
|
||||
#
|
||||
# This macro calls:
|
||||
#
|
||||
# AC_SUBST(BOOST_SYSTEM_LIB)
|
||||
#
|
||||
# And sets:
|
||||
#
|
||||
# HAVE_BOOST_SYSTEM
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Thomas Porschberg <thomas@randspringer.de>
|
||||
# Copyright (c) 2008 Michael Tindal
|
||||
# Copyright (c) 2008 Daniel Casimiro <dan.casimiro@gmail.com>
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 17
|
||||
|
||||
AC_DEFUN([AX_BOOST_SYSTEM],
|
||||
[
|
||||
AC_ARG_WITH([boost-system],
|
||||
AS_HELP_STRING([--with-boost-system@<:@=special-lib@:>@],
|
||||
[use the System library from boost - it is possible to specify a certain library for the linker
|
||||
e.g. --with-boost-system=boost_system-gcc-mt ]),
|
||||
[
|
||||
if test "$withval" = "no"; then
|
||||
want_boost="no"
|
||||
elif test "$withval" = "yes"; then
|
||||
want_boost="yes"
|
||||
ax_boost_user_system_lib=""
|
||||
else
|
||||
want_boost="yes"
|
||||
ax_boost_user_system_lib="$withval"
|
||||
fi
|
||||
],
|
||||
[want_boost="yes"]
|
||||
)
|
||||
|
||||
if test "x$want_boost" = "xyes"; then
|
||||
AC_REQUIRE([AC_PROG_CC])
|
||||
AC_REQUIRE([AC_CANONICAL_BUILD])
|
||||
CPPFLAGS_SAVED="$CPPFLAGS"
|
||||
CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
|
||||
export CPPFLAGS
|
||||
|
||||
LDFLAGS_SAVED="$LDFLAGS"
|
||||
LDFLAGS="$LDFLAGS $BOOST_LDFLAGS"
|
||||
export LDFLAGS
|
||||
|
||||
AC_CACHE_CHECK(whether the Boost::System library is available,
|
||||
ax_cv_boost_system,
|
||||
[AC_LANG_PUSH([C++])
|
||||
CXXFLAGS_SAVE=$CXXFLAGS
|
||||
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include <boost/system/error_code.hpp>]],
|
||||
[[boost::system::system_category]])],
|
||||
ax_cv_boost_system=yes, ax_cv_boost_system=no)
|
||||
CXXFLAGS=$CXXFLAGS_SAVE
|
||||
AC_LANG_POP([C++])
|
||||
])
|
||||
if test "x$ax_cv_boost_system" = "xyes"; then
|
||||
AC_SUBST(BOOST_CPPFLAGS)
|
||||
|
||||
AC_DEFINE(HAVE_BOOST_SYSTEM,,[define if the Boost::System library is available])
|
||||
BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'`
|
||||
|
||||
LDFLAGS_SAVE=$LDFLAGS
|
||||
if test "x$ax_boost_user_system_lib" = "x"; then
|
||||
for libextension in `ls -r $BOOSTLIBDIR/libboost_system* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'` ; do
|
||||
ax_lib=${libextension}
|
||||
AC_CHECK_LIB($ax_lib, exit,
|
||||
[BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break],
|
||||
[link_system="no"])
|
||||
done
|
||||
if test "x$link_system" != "xyes"; then
|
||||
for libextension in `ls -r $BOOSTLIBDIR/boost_system* 2>/dev/null | sed 's,.*/,,' | sed -e 's,\..*,,'` ; do
|
||||
ax_lib=${libextension}
|
||||
AC_CHECK_LIB($ax_lib, exit,
|
||||
[BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break],
|
||||
[link_system="no"])
|
||||
done
|
||||
fi
|
||||
|
||||
else
|
||||
for ax_lib in $ax_boost_user_system_lib boost_system-$ax_boost_user_system_lib; do
|
||||
AC_CHECK_LIB($ax_lib, exit,
|
||||
[BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break],
|
||||
[link_system="no"])
|
||||
done
|
||||
|
||||
fi
|
||||
if test "x$ax_lib" = "x"; then
|
||||
AC_MSG_ERROR(Could not find a version of the library!)
|
||||
fi
|
||||
if test "x$link_system" = "xno"; then
|
||||
AC_MSG_ERROR(Could not link against $ax_lib !)
|
||||
fi
|
||||
fi
|
||||
|
||||
CPPFLAGS="$CPPFLAGS_SAVED"
|
||||
LDFLAGS="$LDFLAGS_SAVED"
|
||||
fi
|
||||
])
|
||||
149
m4/ax_boost_thread.m4
Normal file
149
m4/ax_boost_thread.m4
Normal file
@@ -0,0 +1,149 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_boost_thread.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_BOOST_THREAD
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Test for Thread library from the Boost C++ libraries. The macro requires
|
||||
# a preceding call to AX_BOOST_BASE. Further documentation is available at
|
||||
# <http://randspringer.de/boost/index.html>.
|
||||
#
|
||||
# This macro calls:
|
||||
#
|
||||
# AC_SUBST(BOOST_THREAD_LIB)
|
||||
#
|
||||
# And sets:
|
||||
#
|
||||
# HAVE_BOOST_THREAD
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2009 Thomas Porschberg <thomas@randspringer.de>
|
||||
# Copyright (c) 2009 Michael Tindal
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 27
|
||||
|
||||
AC_DEFUN([AX_BOOST_THREAD],
|
||||
[
|
||||
AC_ARG_WITH([boost-thread],
|
||||
AS_HELP_STRING([--with-boost-thread@<:@=special-lib@:>@],
|
||||
[use the Thread library from boost - it is possible to specify a certain library for the linker
|
||||
e.g. --with-boost-thread=boost_thread-gcc-mt ]),
|
||||
[
|
||||
if test "$withval" = "no"; then
|
||||
want_boost="no"
|
||||
elif test "$withval" = "yes"; then
|
||||
want_boost="yes"
|
||||
ax_boost_user_thread_lib=""
|
||||
else
|
||||
want_boost="yes"
|
||||
ax_boost_user_thread_lib="$withval"
|
||||
fi
|
||||
],
|
||||
[want_boost="yes"]
|
||||
)
|
||||
|
||||
if test "x$want_boost" = "xyes"; then
|
||||
AC_REQUIRE([AC_PROG_CC])
|
||||
AC_REQUIRE([AC_CANONICAL_BUILD])
|
||||
CPPFLAGS_SAVED="$CPPFLAGS"
|
||||
CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
|
||||
export CPPFLAGS
|
||||
|
||||
LDFLAGS_SAVED="$LDFLAGS"
|
||||
LDFLAGS="$LDFLAGS $BOOST_LDFLAGS"
|
||||
export LDFLAGS
|
||||
|
||||
AC_CACHE_CHECK(whether the Boost::Thread library is available,
|
||||
ax_cv_boost_thread,
|
||||
[AC_LANG_PUSH([C++])
|
||||
CXXFLAGS_SAVE=$CXXFLAGS
|
||||
|
||||
if test "x$host_os" = "xsolaris" ; then
|
||||
CXXFLAGS="-pthreads $CXXFLAGS"
|
||||
elif test "x$host_os" = "xmingw32" ; then
|
||||
CXXFLAGS="-mthreads $CXXFLAGS"
|
||||
else
|
||||
CXXFLAGS="-pthread $CXXFLAGS"
|
||||
fi
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include <boost/thread/thread.hpp>]],
|
||||
[[boost::thread_group thrds;
|
||||
return 0;]])],
|
||||
ax_cv_boost_thread=yes, ax_cv_boost_thread=no)
|
||||
CXXFLAGS=$CXXFLAGS_SAVE
|
||||
AC_LANG_POP([C++])
|
||||
])
|
||||
if test "x$ax_cv_boost_thread" = "xyes"; then
|
||||
if test "x$host_os" = "xsolaris" ; then
|
||||
BOOST_CPPFLAGS="-pthreads $BOOST_CPPFLAGS"
|
||||
elif test "x$host_os" = "xmingw32" ; then
|
||||
BOOST_CPPFLAGS="-mthreads $BOOST_CPPFLAGS"
|
||||
else
|
||||
BOOST_CPPFLAGS="-pthread $BOOST_CPPFLAGS"
|
||||
fi
|
||||
|
||||
AC_SUBST(BOOST_CPPFLAGS)
|
||||
|
||||
AC_DEFINE(HAVE_BOOST_THREAD,,[define if the Boost::Thread library is available])
|
||||
BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'`
|
||||
|
||||
LDFLAGS_SAVE=$LDFLAGS
|
||||
case "x$host_os" in
|
||||
*bsd* )
|
||||
LDFLAGS="-pthread $LDFLAGS"
|
||||
break;
|
||||
;;
|
||||
esac
|
||||
if test "x$ax_boost_user_thread_lib" = "x"; then
|
||||
for libextension in `ls -r $BOOSTLIBDIR/libboost_thread* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'`; do
|
||||
ax_lib=${libextension}
|
||||
AC_CHECK_LIB($ax_lib, exit,
|
||||
[BOOST_THREAD_LIB="-l$ax_lib"; AC_SUBST(BOOST_THREAD_LIB) link_thread="yes"; break],
|
||||
[link_thread="no"])
|
||||
done
|
||||
if test "x$link_thread" != "xyes"; then
|
||||
for libextension in `ls -r $BOOSTLIBDIR/boost_thread* 2>/dev/null | sed 's,.*/,,' | sed 's,\..*,,'`; do
|
||||
ax_lib=${libextension}
|
||||
AC_CHECK_LIB($ax_lib, exit,
|
||||
[BOOST_THREAD_LIB="-l$ax_lib"; AC_SUBST(BOOST_THREAD_LIB) link_thread="yes"; break],
|
||||
[link_thread="no"])
|
||||
done
|
||||
fi
|
||||
|
||||
else
|
||||
for ax_lib in $ax_boost_user_thread_lib boost_thread-$ax_boost_user_thread_lib; do
|
||||
AC_CHECK_LIB($ax_lib, exit,
|
||||
[BOOST_THREAD_LIB="-l$ax_lib"; AC_SUBST(BOOST_THREAD_LIB) link_thread="yes"; break],
|
||||
[link_thread="no"])
|
||||
done
|
||||
|
||||
fi
|
||||
if test "x$ax_lib" = "x"; then
|
||||
AC_MSG_ERROR(Could not find a version of the library!)
|
||||
fi
|
||||
if test "x$link_thread" = "xno"; then
|
||||
AC_MSG_ERROR(Could not link against $ax_lib !)
|
||||
else
|
||||
case "x$host_os" in
|
||||
*bsd* )
|
||||
BOOST_LDFLAGS="-pthread $BOOST_LDFLAGS"
|
||||
break;
|
||||
;;
|
||||
esac
|
||||
|
||||
fi
|
||||
fi
|
||||
|
||||
CPPFLAGS="$CPPFLAGS_SAVED"
|
||||
LDFLAGS="$LDFLAGS_SAVED"
|
||||
fi
|
||||
])
|
||||
104
m4/ax_have_epoll.m4
Normal file
104
m4/ax_have_epoll.m4
Normal file
@@ -0,0 +1,104 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_have_epoll.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_HAVE_EPOLL([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
|
||||
# AX_HAVE_EPOLL_PWAIT([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# This macro determines whether the system supports the epoll I/O event
|
||||
# interface. A neat usage example would be:
|
||||
#
|
||||
# AX_HAVE_EPOLL(
|
||||
# [AX_CONFIG_FEATURE_ENABLE(epoll)],
|
||||
# [AX_CONFIG_FEATURE_DISABLE(epoll)])
|
||||
# AX_CONFIG_FEATURE(
|
||||
# [epoll], [This platform supports epoll(7)],
|
||||
# [HAVE_EPOLL], [This platform supports epoll(7).])
|
||||
#
|
||||
# The epoll interface was added to the Linux kernel in version 2.5.45, and
|
||||
# the macro verifies that a kernel newer than this is installed. This
|
||||
# check is somewhat unreliable if <linux/version.h> doesn't match the
|
||||
# running kernel, but it is necessary regardless, because glibc comes with
|
||||
# stubs for the epoll_create(), epoll_wait(), etc. that allow programs to
|
||||
# compile and link even if the kernel is too old; the problem would then
|
||||
# be detected only at runtime.
|
||||
#
|
||||
# Linux kernel version 2.6.19 adds the epoll_pwait() call in addition to
|
||||
# epoll_wait(). The availability of that function can be tested with the
|
||||
# second macro. Generally speaking, it is safe to assume that
|
||||
# AX_HAVE_EPOLL would succeed if AX_HAVE_EPOLL_PWAIT has, but not the
|
||||
# other way round.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Peter Simons <simons@cryp.to>
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 10
|
||||
|
||||
AC_DEFUN([AX_HAVE_EPOLL], [dnl
|
||||
ax_have_epoll_cppflags="${CPPFLAGS}"
|
||||
AC_CHECK_HEADER([linux/version.h], [CPPFLAGS="${CPPFLAGS} -DHAVE_LINUX_VERSION_H"])
|
||||
AC_MSG_CHECKING([for Linux epoll(7) interface])
|
||||
AC_CACHE_VAL([ax_cv_have_epoll], [dnl
|
||||
AC_LINK_IFELSE([dnl
|
||||
AC_LANG_PROGRAM([dnl
|
||||
#include <sys/epoll.h>
|
||||
#ifdef HAVE_LINUX_VERSION_H
|
||||
# include <linux/version.h>
|
||||
# if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,45)
|
||||
# error linux kernel version is too old to have epoll
|
||||
# endif
|
||||
#endif
|
||||
], [dnl
|
||||
int fd, rc;
|
||||
struct epoll_event ev;
|
||||
fd = epoll_create(128);
|
||||
rc = epoll_wait(fd, &ev, 1, 0);])],
|
||||
[ax_cv_have_epoll=yes],
|
||||
[ax_cv_have_epoll=no])])
|
||||
CPPFLAGS="${ax_have_epoll_cppflags}"
|
||||
AS_IF([test "${ax_cv_have_epoll}" = "yes"],
|
||||
[AC_MSG_RESULT([yes])
|
||||
$1],[AC_MSG_RESULT([no])
|
||||
$2])
|
||||
])dnl
|
||||
|
||||
AC_DEFUN([AX_HAVE_EPOLL_PWAIT], [dnl
|
||||
ax_have_epoll_cppflags="${CPPFLAGS}"
|
||||
AC_CHECK_HEADER([linux/version.h],
|
||||
[CPPFLAGS="${CPPFLAGS} -DHAVE_LINUX_VERSION_H"])
|
||||
AC_MSG_CHECKING([for Linux epoll(7) interface with signals extension])
|
||||
AC_CACHE_VAL([ax_cv_have_epoll_pwait], [dnl
|
||||
AC_LINK_IFELSE([dnl
|
||||
AC_LANG_PROGRAM([dnl
|
||||
#ifdef HAVE_LINUX_VERSION_H
|
||||
# include <linux/version.h>
|
||||
# if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
|
||||
# error linux kernel version is too old to have epoll_pwait
|
||||
# endif
|
||||
#endif
|
||||
#include <sys/epoll.h>
|
||||
#include <signal.h>
|
||||
], [dnl
|
||||
int fd, rc;
|
||||
struct epoll_event ev;
|
||||
fd = epoll_create(128);
|
||||
rc = epoll_wait(fd, &ev, 1, 0);
|
||||
rc = epoll_pwait(fd, &ev, 1, 0, (sigset_t const *)(0));])],
|
||||
[ax_cv_have_epoll_pwait=yes],
|
||||
[ax_cv_have_epoll_pwait=no])])
|
||||
CPPFLAGS="${ax_have_epoll_cppflags}"
|
||||
AS_IF([test "${ax_cv_have_epoll_pwait}" = "yes"],
|
||||
[AC_MSG_RESULT([yes])
|
||||
$1],[AC_MSG_RESULT([no])
|
||||
$2])
|
||||
])dnl
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user