mirror of
https://github.com/nghttp2/nghttp2.git
synced 2025-12-07 02:28:53 +08:00
Compare commits
1152 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5a8d5e5dd9 | ||
|
|
9bcb0ffdef | ||
|
|
1945d0f02a | ||
|
|
4870edb33d | ||
|
|
3abb162969 | ||
|
|
4242478f39 | ||
|
|
4fa2ffe292 | ||
|
|
301df2a856 | ||
|
|
f3b7f4140b | ||
|
|
878b873c69 | ||
|
|
2ca8cf36b7 | ||
|
|
2224b98c9c | ||
|
|
a7031da364 | ||
|
|
cebfdacc5a | ||
|
|
39f89f4a60 | ||
|
|
46b70c1db8 | ||
|
|
5537503172 | ||
|
|
81cc550eb9 | ||
|
|
0a6de0d378 | ||
|
|
25d1de0278 | ||
|
|
39eb8b8a6b | ||
|
|
c050fca177 | ||
|
|
19309823aa | ||
|
|
90bcdb0dda | ||
|
|
17ec30e45c | ||
|
|
07f763be49 | ||
|
|
69119f47c4 | ||
|
|
b0b792e735 | ||
|
|
8aab74ad36 | ||
|
|
f418d1239f | ||
|
|
5b51320dc5 | ||
|
|
d2e81da9e2 | ||
|
|
a61054e1f7 | ||
|
|
749f5d6a32 | ||
|
|
f09bd821ac | ||
|
|
6d5c00b8eb | ||
|
|
532bffdb01 | ||
|
|
c6c7145167 | ||
|
|
b5717cd288 | ||
|
|
d4d7597efb | ||
|
|
2952706b53 | ||
|
|
9b0ccdef34 | ||
|
|
41dd5f6897 | ||
|
|
f9c60d5e9d | ||
|
|
c2ca04c1db | ||
|
|
5fbb99e3d0 | ||
|
|
ba34313f9f | ||
|
|
aa6699435a | ||
|
|
e857e99df8 | ||
|
|
6d537c419e | ||
|
|
4894e24dc8 | ||
|
|
bbbddedb8f | ||
|
|
163dfb3150 | ||
|
|
4f3d20e024 | ||
|
|
b7ba1baf48 | ||
|
|
00efa86fb6 | ||
|
|
43b3640836 | ||
|
|
708eb2a217 | ||
|
|
590a5c3ff3 | ||
|
|
28b528dc9e | ||
|
|
a50f6e5d50 | ||
|
|
1534edd3f3 | ||
|
|
892c159d8f | ||
|
|
0cb4750e3c | ||
|
|
ee4d53a9e4 | ||
|
|
ad150c3ab1 | ||
|
|
7eafebfeb9 | ||
|
|
3d59c6c0b7 | ||
|
|
34efc6b7a4 | ||
|
|
7582640fd5 | ||
|
|
ad265aa9d0 | ||
|
|
1f7e6ea3fe | ||
|
|
a0a5f4f93e | ||
|
|
59e6272ba4 | ||
|
|
323fc8c552 | ||
|
|
cbb10aa80f | ||
|
|
abe4c7c92a | ||
|
|
326b4c467b | ||
|
|
7e51a87111 | ||
|
|
5fdb36239a | ||
|
|
9f99cad9ab | ||
|
|
7c7d4700f1 | ||
|
|
8e5c8430f1 | ||
|
|
92ecd3c0fb | ||
|
|
90eac0709d | ||
|
|
791660ef8d | ||
|
|
1c06cfd29f | ||
|
|
f0379aa428 | ||
|
|
9a0b9428da | ||
|
|
b20abfc11a | ||
|
|
d983dd81ec | ||
|
|
bcf9e66dbc | ||
|
|
890a10b216 | ||
|
|
28adb2dad3 | ||
|
|
c795018f29 | ||
|
|
c1a663b577 | ||
|
|
2b450f26ba | ||
|
|
1ddff5bbf9 | ||
|
|
3a4c8bc8f1 | ||
|
|
fe752174a9 | ||
|
|
494ed221b6 | ||
|
|
14f971d71f | ||
|
|
ddee5d3896 | ||
|
|
447e346b1e | ||
|
|
553d741f03 | ||
|
|
6bd728b3c2 | ||
|
|
a99085891a | ||
|
|
68d3724fad | ||
|
|
fe39ec8697 | ||
|
|
c896118747 | ||
|
|
b89140c311 | ||
|
|
a869c39a2c | ||
|
|
0b27f005e0 | ||
|
|
92a20c76e6 | ||
|
|
42ccea806c | ||
|
|
805f36d134 | ||
|
|
e2c0a3e43b | ||
|
|
3572e7c634 | ||
|
|
0479f833fc | ||
|
|
252aeb43e1 | ||
|
|
24fe24b37d | ||
|
|
32603d7eff | ||
|
|
53bfc70c9e | ||
|
|
c4068cd404 | ||
|
|
0aa17f64c1 | ||
|
|
38cfc5c47c | ||
|
|
91c8f085ef | ||
|
|
5da49989f8 | ||
|
|
260131966d | ||
|
|
6862f66c23 | ||
|
|
2f2a7ace81 | ||
|
|
132719f752 | ||
|
|
64b1aae567 | ||
|
|
a8625e15f0 | ||
|
|
e63d6e490a | ||
|
|
7f60de0c51 | ||
|
|
3a46a2c0a4 | ||
|
|
fbff101165 | ||
|
|
526d2c727d | ||
|
|
6c232da679 | ||
|
|
1241c951d6 | ||
|
|
73e79130d1 | ||
|
|
1a1902350b | ||
|
|
eec65826cf | ||
|
|
fc17c0a618 | ||
|
|
2620992003 | ||
|
|
5a2069b55c | ||
|
|
14adcb2d81 | ||
|
|
13660edef2 | ||
|
|
98034286ac | ||
|
|
2a37a28d72 | ||
|
|
7bb154f768 | ||
|
|
eb96aa261f | ||
|
|
232d359cbb | ||
|
|
2d5d9d5d04 | ||
|
|
7ecca39025 | ||
|
|
bc0190c19f | ||
|
|
9a162b81f0 | ||
|
|
a67a8fabff | ||
|
|
989d381aab | ||
|
|
1d65d82cb5 | ||
|
|
4be4d875f3 | ||
|
|
1ab707713f | ||
|
|
cc46d363c5 | ||
|
|
fa082cbdd0 | ||
|
|
016d40ea0f | ||
|
|
fe6d065bb4 | ||
|
|
b4e8bea4b5 | ||
|
|
555d5abac9 | ||
|
|
3137dc4a70 | ||
|
|
4bba4bf66c | ||
|
|
7b3a33a313 | ||
|
|
ee52290de7 | ||
|
|
8f0899a190 | ||
|
|
f6cfd082c7 | ||
|
|
9d81be4b35 | ||
|
|
a62778d6b0 | ||
|
|
ea612a2dce | ||
|
|
026521b097 | ||
|
|
9dc5259593 | ||
|
|
ea8a566d98 | ||
|
|
8c6f9e899f | ||
|
|
552f675466 | ||
|
|
f9a50333d2 | ||
|
|
de4735092a | ||
|
|
1c4df1832b | ||
|
|
1ad1fe6005 | ||
|
|
f05a4830c5 | ||
|
|
9e1b068a4b | ||
|
|
54bff91762 | ||
|
|
63630690a8 | ||
|
|
dbc613e0d0 | ||
|
|
ee354ee6c8 | ||
|
|
df707df21b | ||
|
|
2436acbd23 | ||
|
|
b41835f19b | ||
|
|
42b2430fe1 | ||
|
|
c8f67788e0 | ||
|
|
bbdff112a3 | ||
|
|
02468b1ca1 | ||
|
|
c41f413978 | ||
|
|
e38dd37667 | ||
|
|
f2cf2b625c | ||
|
|
eb05777d88 | ||
|
|
db4a68454a | ||
|
|
6b0b8ea7d5 | ||
|
|
c925c32233 | ||
|
|
514558afc0 | ||
|
|
b9f602b9a2 | ||
|
|
1a99bcc860 | ||
|
|
6b8aa36c98 | ||
|
|
86ddda5c0e | ||
|
|
58b7f4a096 | ||
|
|
4069aab4f7 | ||
|
|
5595ba643e | ||
|
|
77c556901c | ||
|
|
4928959213 | ||
|
|
a200bb1084 | ||
|
|
92a1ca5917 | ||
|
|
97648d257f | ||
|
|
5937b4b6f7 | ||
|
|
787d40129b | ||
|
|
91ad7e150e | ||
|
|
28bde2cef0 | ||
|
|
a9b54a1bfa | ||
|
|
80f0e99f00 | ||
|
|
102ea7c0bb | ||
|
|
85671a69bf | ||
|
|
a3fa257473 | ||
|
|
c4e994c97d | ||
|
|
0b41e20d54 | ||
|
|
cfabce6e70 | ||
|
|
b948c5457d | ||
|
|
3bdf78e8af | ||
|
|
436595df98 | ||
|
|
d3561a63b1 | ||
|
|
1a12a9b397 | ||
|
|
57644e0256 | ||
|
|
7323d4c639 | ||
|
|
e23225689f | ||
|
|
e6ad2eb14f | ||
|
|
d4a22edeb3 | ||
|
|
8f4e2d941f | ||
|
|
1a8da6caec | ||
|
|
dc335b9025 | ||
|
|
93afbc7d2f | ||
|
|
82e2c5bd22 | ||
|
|
53bcafb39f | ||
|
|
59f8397659 | ||
|
|
061732adf0 | ||
|
|
5c2ca28706 | ||
|
|
a8ea86cfe5 | ||
|
|
7451a73def | ||
|
|
889e705f35 | ||
|
|
14d4979c54 | ||
|
|
095bc178f3 | ||
|
|
308738025c | ||
|
|
97366bf55c | ||
|
|
87cadca3d8 | ||
|
|
9803f92e9c | ||
|
|
cbfa021095 | ||
|
|
03746884a4 | ||
|
|
6ed710adbd | ||
|
|
44b4cda200 | ||
|
|
69a4f3bf42 | ||
|
|
5334cb5acc | ||
|
|
6ca24264e4 | ||
|
|
37a631278f | ||
|
|
5c42687759 | ||
|
|
b636e9744f | ||
|
|
b873930802 | ||
|
|
bc53c81616 | ||
|
|
09c485e712 | ||
|
|
d247470da2 | ||
|
|
90bfea77e0 | ||
|
|
cf0576253f | ||
|
|
4aca2f0b59 | ||
|
|
9d711f65f7 | ||
|
|
0eab08a7cf | ||
|
|
b9c4757d21 | ||
|
|
1fcd881395 | ||
|
|
cd0564ddfa | ||
|
|
dd435b51ab | ||
|
|
ff60cc6b71 | ||
|
|
80743ddc7b | ||
|
|
36a8f24559 | ||
|
|
b25e19e876 | ||
|
|
e9660c3558 | ||
|
|
8c3b379b66 | ||
|
|
b2bb6f1db1 | ||
|
|
59e3783f3f | ||
|
|
d56ecd7414 | ||
|
|
ef4e39be55 | ||
|
|
d42f31ca78 | ||
|
|
084e4487ed | ||
|
|
24897aa50d | ||
|
|
3e50ef439d | ||
|
|
87602e5d72 | ||
|
|
d0c27d5229 | ||
|
|
ebf214c8fc | ||
|
|
250ea53e4b | ||
|
|
01af6ea70c | ||
|
|
7522d50d1a | ||
|
|
cc03a12b75 | ||
|
|
9eff511c5e | ||
|
|
a96ac6f0a0 | ||
|
|
213a63d97d | ||
|
|
8d4548b9c1 | ||
|
|
44f2e6ae1d | ||
|
|
0cd7d268f3 | ||
|
|
f3a9041851 | ||
|
|
776a8c64f6 | ||
|
|
524a1f9498 | ||
|
|
28cfae162a | ||
|
|
b0123448a4 | ||
|
|
2d15dca096 | ||
|
|
5f05135d1b | ||
|
|
3b6b4ff066 | ||
|
|
0c8ec7bfa6 | ||
|
|
2c05b8d6f0 | ||
|
|
45c4187d8f | ||
|
|
87029e05af | ||
|
|
363914c3f7 | ||
|
|
1316065c14 | ||
|
|
2b6a181bef | ||
|
|
bf68df9ef4 | ||
|
|
e7f6089b0e | ||
|
|
a1500b0ee3 | ||
|
|
14001052f8 | ||
|
|
95ab54c79d | ||
|
|
5e84f767f0 | ||
|
|
c47855085b | ||
|
|
1442b1bd0a | ||
|
|
763293a050 | ||
|
|
cc94632b29 | ||
|
|
5df770b9c1 | ||
|
|
4bc9afe20a | ||
|
|
522faeb24f | ||
|
|
362ff09183 | ||
|
|
ccea4d42b5 | ||
|
|
ebbe065716 | ||
|
|
3a97f21383 | ||
|
|
12ced1cddc | ||
|
|
3576f20e5a | ||
|
|
7ae6e6b4c5 | ||
|
|
a2486daee1 | ||
|
|
8bf440b89c | ||
|
|
e9cdb9c896 | ||
|
|
c4804ee50b | ||
|
|
95cb284e27 | ||
|
|
c4ccc376df | ||
|
|
6133110386 | ||
|
|
6f58434d89 | ||
|
|
3fd5d0af79 | ||
|
|
1a6855ea7d | ||
|
|
0312521ac9 | ||
|
|
67b0e0c2d6 | ||
|
|
966e3a1308 | ||
|
|
fd8f8e2708 | ||
|
|
d7fdcbb3d6 | ||
|
|
de2c2ad65c | ||
|
|
862a0ee66b | ||
|
|
c2510a01a5 | ||
|
|
dc85623060 | ||
|
|
8afbb6ca26 | ||
|
|
ed79637737 | ||
|
|
faa2c4467a | ||
|
|
c87fae463e | ||
|
|
29b4b11e78 | ||
|
|
3b24be3bcd | ||
|
|
ece8289aaf | ||
|
|
4042ff0fc4 | ||
|
|
d3d6c5e314 | ||
|
|
125e32eb56 | ||
|
|
94bf8dcd4e | ||
|
|
89b8039466 | ||
|
|
72843b33d0 | ||
|
|
03e699e013 | ||
|
|
fcf99fa8fc | ||
|
|
661fb2eb0e | ||
|
|
42496d638b | ||
|
|
c0a6a0a6d1 | ||
|
|
b7bda783c5 | ||
|
|
6893608ae2 | ||
|
|
ef913bc929 | ||
|
|
a5ed70bcfe | ||
|
|
9bf2ca6916 | ||
|
|
57729d0a23 | ||
|
|
e86b81ec10 | ||
|
|
076eefbed6 | ||
|
|
58a735dc68 | ||
|
|
948d4d43d5 | ||
|
|
ef581e94bb | ||
|
|
ad84af2b2b | ||
|
|
0e65e1254d | ||
|
|
08ec5b3fc0 | ||
|
|
8c491d5917 | ||
|
|
4219fe7822 | ||
|
|
d4eb2b2c75 | ||
|
|
8ea26fddfd | ||
|
|
98add63cdf | ||
|
|
7b90404072 | ||
|
|
de0543f684 | ||
|
|
46e3be7b5b | ||
|
|
7c675b1505 | ||
|
|
d287ea986f | ||
|
|
399328cb49 | ||
|
|
d46e50b112 | ||
|
|
0f87cedc2d | ||
|
|
d34095cf49 | ||
|
|
6039bacb1b | ||
|
|
4877f72a75 | ||
|
|
274b3a2296 | ||
|
|
93013f4205 | ||
|
|
5789987ca3 | ||
|
|
a0524ef05d | ||
|
|
0e3ae63965 | ||
|
|
3e14261ebf | ||
|
|
446de923f3 | ||
|
|
c5860fc6f4 | ||
|
|
6b714030dd | ||
|
|
8483225839 | ||
|
|
585af93828 | ||
|
|
41e266181e | ||
|
|
6b7e6166f9 | ||
|
|
2a4f347dbc | ||
|
|
bff5a28d12 | ||
|
|
5c31c130bd | ||
|
|
b9d6fff962 | ||
|
|
9ffbc45ba6 | ||
|
|
961f41d7ce | ||
|
|
928d3e5f3f | ||
|
|
42eeebc7f6 | ||
|
|
991baf9e69 | ||
|
|
6ad63a06b0 | ||
|
|
60c2fe5a2e | ||
|
|
41b5077330 | ||
|
|
d10dc4bb9b | ||
|
|
c4b487be05 | ||
|
|
c976a0fcd1 | ||
|
|
5b5034f19c | ||
|
|
dd9e829ee1 | ||
|
|
80a7523b49 | ||
|
|
4c55a2340b | ||
|
|
2f2a535113 | ||
|
|
505a300d93 | ||
|
|
4236fa603f | ||
|
|
196d7da53f | ||
|
|
6824319fe6 | ||
|
|
66d00954a5 | ||
|
|
76eb3193ab | ||
|
|
66f5438dc9 | ||
|
|
45164b6761 | ||
|
|
ceefddd332 | ||
|
|
a043230371 | ||
|
|
c2f49fa478 | ||
|
|
fe17a20f84 | ||
|
|
773b108eeb | ||
|
|
36a9cbf41f | ||
|
|
f8182333b4 | ||
|
|
c3265de625 | ||
|
|
b24bd3d8cb | ||
|
|
fa21b95274 | ||
|
|
5dccc88a7c | ||
|
|
2cadd38b6b | ||
|
|
d7cfe464a2 | ||
|
|
018e9eaf6d | ||
|
|
94ca9705ef | ||
|
|
d6f810d91a | ||
|
|
d8cf29c202 | ||
|
|
bf46539d10 | ||
|
|
2ed47cdd19 | ||
|
|
a3f79232c6 | ||
|
|
421bf85808 | ||
|
|
05a847b6b8 | ||
|
|
1e3afe0646 | ||
|
|
38788d707b | ||
|
|
44c0d32a1b | ||
|
|
36aae8c310 | ||
|
|
34ac90443f | ||
|
|
992c14e354 | ||
|
|
529fc937dc | ||
|
|
42c174e803 | ||
|
|
05b8d8c5b7 | ||
|
|
a1c937a007 | ||
|
|
8baec366f0 | ||
|
|
c64bb62ffe | ||
|
|
6020b727d8 | ||
|
|
7f04968950 | ||
|
|
ca1f43dfca | ||
|
|
9efb62f40b | ||
|
|
f1c7d3edfd | ||
|
|
7ff38eeb2e | ||
|
|
e4ce595ebb | ||
|
|
8accf3898a | ||
|
|
690a1622aa | ||
|
|
aaef798566 | ||
|
|
0753fcd6e5 | ||
|
|
a14029744c | ||
|
|
7e46d0b142 | ||
|
|
726e6c087d | ||
|
|
ff0eaf8399 | ||
|
|
7fb7575f78 | ||
|
|
44642af227 | ||
|
|
e7d052100c | ||
|
|
fc2170e488 | ||
|
|
0cda2282dd | ||
|
|
566baab307 | ||
|
|
6753b6d61e | ||
|
|
35817876fe | ||
|
|
7d753d779e | ||
|
|
f6f908a541 | ||
|
|
b0c1986a46 | ||
|
|
9671eaa850 | ||
|
|
062b42918c | ||
|
|
2fa28e790d | ||
|
|
76eab3faa0 | ||
|
|
70ea774f23 | ||
|
|
e15d302985 | ||
|
|
b2196f215a | ||
|
|
8afd75ca47 | ||
|
|
0676535caf | ||
|
|
dd741a58ae | ||
|
|
26304546c4 | ||
|
|
838fb33892 | ||
|
|
7f802b623d | ||
|
|
c2426bc732 | ||
|
|
08d0cdfd31 | ||
|
|
1fd44b1567 | ||
|
|
a2a9f15307 | ||
|
|
291c27c940 | ||
|
|
f86c11f07d | ||
|
|
dbd0f032ce | ||
|
|
6a2e6b744f | ||
|
|
4de8db523f | ||
|
|
cf5ac0f363 | ||
|
|
185ebd7b79 | ||
|
|
38153e0f6e | ||
|
|
f36f3ae1fa | ||
|
|
94d6320376 | ||
|
|
f3a0ab6552 | ||
|
|
568b744374 | ||
|
|
00e687506e | ||
|
|
1c2c5bdd05 | ||
|
|
30acb41561 | ||
|
|
3715a89655 | ||
|
|
ef090d425e | ||
|
|
e1bb06d8ab | ||
|
|
d1793e3b5a | ||
|
|
1bdf664f4d | ||
|
|
05b8901d69 | ||
|
|
1c0d617742 | ||
|
|
b161dfe573 | ||
|
|
c6d019da5f | ||
|
|
b773d63b92 | ||
|
|
66b6787fbe | ||
|
|
f2a498e3c4 | ||
|
|
1a2bccd71c | ||
|
|
8417275368 | ||
|
|
1646352f3c | ||
|
|
c98cf045d6 | ||
|
|
933b9636e5 | ||
|
|
d10bce3dc8 | ||
|
|
814c7e68e0 | ||
|
|
c5c58ccd78 | ||
|
|
1468bcd7b4 | ||
|
|
8b533e19bb | ||
|
|
a55a080b9c | ||
|
|
df32a534fc | ||
|
|
e583a25a8b | ||
|
|
4430b06c71 | ||
|
|
6051ff63e0 | ||
|
|
da2376effd | ||
|
|
0c4ae3dea5 | ||
|
|
e457c9a414 | ||
|
|
997f9233bc | ||
|
|
49781da8f0 | ||
|
|
7f9de007d0 | ||
|
|
c506386997 | ||
|
|
3144f7de72 | ||
|
|
c84a190ac7 | ||
|
|
a26a597453 | ||
|
|
20a689ef44 | ||
|
|
62928ddbcd | ||
|
|
04ef26473a | ||
|
|
7897f5b94b | ||
|
|
9302e3edf4 | ||
|
|
ab93a700ce | ||
|
|
2fc1dd77d2 | ||
|
|
e45c523dc7 | ||
|
|
b3846d6c27 | ||
|
|
dec115cff3 | ||
|
|
fe84ec5e8b | ||
|
|
b39aa43537 | ||
|
|
2216fd2bc1 | ||
|
|
83d9ee1fd1 | ||
|
|
4424cf5b65 | ||
|
|
00a83a44b4 | ||
|
|
83952ef0af | ||
|
|
3dbd2d31bd | ||
|
|
f755ea3894 | ||
|
|
50c4aa061f | ||
|
|
6657966334 | ||
|
|
512aa8942a | ||
|
|
b371331297 | ||
|
|
489b4f307c | ||
|
|
b157d4ebb2 | ||
|
|
9845729709 | ||
|
|
dbfc02cba0 | ||
|
|
d1a1e882bf | ||
|
|
799778af69 | ||
|
|
d45f5a51e4 | ||
|
|
e479abc4ab | ||
|
|
37706c2930 | ||
|
|
bb856fe4c5 | ||
|
|
f4c0a243e7 | ||
|
|
ba9ea1831c | ||
|
|
c7126663df | ||
|
|
aa1339d9c0 | ||
|
|
f87aaf1fbf | ||
|
|
685dabc2d6 | ||
|
|
4cafdb7670 | ||
|
|
a8889971db | ||
|
|
1fbaae837c | ||
|
|
e8c294b701 | ||
|
|
9c30211da9 | ||
|
|
58d3b5b4a0 | ||
|
|
442572c1f4 | ||
|
|
011e3b325d | ||
|
|
a473641e3f | ||
|
|
6f4f252907 | ||
|
|
0d2bbead9d | ||
|
|
16475f6613 | ||
|
|
0bdacd3e77 | ||
|
|
57a50f981b | ||
|
|
115d7133a0 | ||
|
|
eb94603c51 | ||
|
|
7d4a6aa179 | ||
|
|
354de30874 | ||
|
|
c462093555 | ||
|
|
364772f508 | ||
|
|
ae0100a9ab | ||
|
|
756e2b3e9b | ||
|
|
57d02f5c57 | ||
|
|
73b999a662 | ||
|
|
4401f697e5 | ||
|
|
f360b5c1e3 | ||
|
|
07fdaaba45 | ||
|
|
7fa62c9904 | ||
|
|
3e2714810a | ||
|
|
68866f53b0 | ||
|
|
ad8e9a4741 | ||
|
|
3c5d8f446b | ||
|
|
2e425e3cb6 | ||
|
|
1b00bc1929 | ||
|
|
6b28e033de | ||
|
|
eec8870ac1 | ||
|
|
d151759f8a | ||
|
|
807d39abe3 | ||
|
|
7b81136bb3 | ||
|
|
75d7e5abe0 | ||
|
|
4d47c31ebe | ||
|
|
9e723b6b1d | ||
|
|
7aff00496a | ||
|
|
0efdeab1db | ||
|
|
b01d0c88fe | ||
|
|
502b552b68 | ||
|
|
8c90e5314d | ||
|
|
af960f1982 | ||
|
|
8b4291edcb | ||
|
|
45a47936e0 | ||
|
|
7c09d5eb8d | ||
|
|
88f0bc70c4 | ||
|
|
b14cfaf308 | ||
|
|
8dd8d68b83 | ||
|
|
c55d7343ca | ||
|
|
4dea318b5b | ||
|
|
795a22a320 | ||
|
|
b4b2ddad3b | ||
|
|
6ff67ae869 | ||
|
|
33879219ff | ||
|
|
4956bdc4da | ||
|
|
b165775811 | ||
|
|
90746cdd0e | ||
|
|
f93a2b71a1 | ||
|
|
2dd6353e24 | ||
|
|
208abd8cc5 | ||
|
|
4cda09beff | ||
|
|
9a2d36fc6c | ||
|
|
ab6663c785 | ||
|
|
54851ef7a6 | ||
|
|
f8f9b36acd | ||
|
|
6774fa6e07 | ||
|
|
7baf6f781e | ||
|
|
2349a03882 | ||
|
|
3904550d5d | ||
|
|
be3ee91e90 | ||
|
|
d4f87ce29f | ||
|
|
1216d7d912 | ||
|
|
f3b247e4c8 | ||
|
|
7c75d9db98 | ||
|
|
b2fb888363 | ||
|
|
a4d729d36b | ||
|
|
83200f3080 | ||
|
|
a14c614c10 | ||
|
|
a68c4c1e3c | ||
|
|
82f90f9030 | ||
|
|
b707cfe986 | ||
|
|
d37fc8f3a6 | ||
|
|
9f5f724147 | ||
|
|
e2bbc94616 | ||
|
|
928b49a916 | ||
|
|
56c2fd6c5b | ||
|
|
267f877255 | ||
|
|
36e216d24a | ||
|
|
73d231b1bb | ||
|
|
cabb7c73cd | ||
|
|
3a37ed97f4 | ||
|
|
0f14c93fa4 | ||
|
|
f321ee5a61 | ||
|
|
e9eae3fb61 | ||
|
|
17de036d85 | ||
|
|
a91e0de06c | ||
|
|
bbc34904c1 | ||
|
|
f1049a66e2 | ||
|
|
5a497b9f30 | ||
|
|
b4ad0a30af | ||
|
|
1816738b3c | ||
|
|
4b0b036d3b | ||
|
|
0a0618baac | ||
|
|
e03f36eeeb | ||
|
|
6b1ef95d3f | ||
|
|
147bc45658 | ||
|
|
00555dc7bb | ||
|
|
d1a4002b22 | ||
|
|
8ddad1a53d | ||
|
|
96e66b1a81 | ||
|
|
19429abd07 | ||
|
|
243a8135a6 | ||
|
|
3167aa4081 | ||
|
|
10a4926648 | ||
|
|
67f50770f5 | ||
|
|
93daa98608 | ||
|
|
63675f0a47 | ||
|
|
f8765be817 | ||
|
|
f0c7839f25 | ||
|
|
6a39de0ae5 | ||
|
|
402ebb277f | ||
|
|
4e68ca8233 | ||
|
|
95b3e2f140 | ||
|
|
f412ae442b | ||
|
|
01313c1241 | ||
|
|
99afea05b9 | ||
|
|
b99b83812b | ||
|
|
0b48448270 | ||
|
|
2a56a3d9ea | ||
|
|
1883bdaf1d | ||
|
|
82cd23e40b | ||
|
|
d56e167c54 | ||
|
|
3e5765831f | ||
|
|
b9c6162cd5 | ||
|
|
68510f1282 | ||
|
|
4b58b25c19 | ||
|
|
516a2f0efc | ||
|
|
fbd9bcb00e | ||
|
|
682db00ba9 | ||
|
|
434e80dc7b | ||
|
|
5c93a113f3 | ||
|
|
6b4b7bef23 | ||
|
|
8d5c893929 | ||
|
|
b943fbb002 | ||
|
|
9a89db575a | ||
|
|
0fcfe16dc5 | ||
|
|
990f9ed4de | ||
|
|
0f4b0966ef | ||
|
|
ecfd593076 | ||
|
|
3c6b75fb2b | ||
|
|
d1f06b09cd | ||
|
|
3a94ad709c | ||
|
|
7ea8037ee1 | ||
|
|
064bfcc9d2 | ||
|
|
a0028ea8ef | ||
|
|
d174ffeb00 | ||
|
|
5a93bedf72 | ||
|
|
4a0dba08b9 | ||
|
|
b685747643 | ||
|
|
76a97b9718 | ||
|
|
ac1cc56fbb | ||
|
|
f604cbae70 | ||
|
|
91ae5291cc | ||
|
|
5770c6bd04 | ||
|
|
7492f628aa | ||
|
|
2f788aa214 | ||
|
|
cee22df073 | ||
|
|
e219d6a94f | ||
|
|
41e72064e0 | ||
|
|
f302f1a830 | ||
|
|
c0fc726955 | ||
|
|
041d9863c2 | ||
|
|
3c5ca63b6f | ||
|
|
53a41c953d | ||
|
|
c39f2b2c91 | ||
|
|
603b0ae501 | ||
|
|
9938a4e952 | ||
|
|
8997e4369d | ||
|
|
5a6d6ccbd4 | ||
|
|
426fbda799 | ||
|
|
16e91746d9 | ||
|
|
b9a9a23b1e | ||
|
|
8059380fb0 | ||
|
|
039d9db5a3 | ||
|
|
b9f41e34ef | ||
|
|
ca7288ae38 | ||
|
|
a789008f17 | ||
|
|
53142222fe | ||
|
|
f1bec6f05c | ||
|
|
506177e1bd | ||
|
|
91151f1f56 | ||
|
|
daec7c16d3 | ||
|
|
6e446934d4 | ||
|
|
9ab71305d1 | ||
|
|
ae1aac26a7 | ||
|
|
8eb2160890 | ||
|
|
a440bdf15e | ||
|
|
8004ea9726 | ||
|
|
5436c95788 | ||
|
|
467565589c | ||
|
|
8f45bf7b9e | ||
|
|
09939cf6bc | ||
|
|
e8053ac931 | ||
|
|
fff785178d | ||
|
|
af24f8394e | ||
|
|
62b9e4bb56 | ||
|
|
441f1cc282 | ||
|
|
f92110c54c | ||
|
|
1cb6d5cb6d | ||
|
|
3817798905 | ||
|
|
13a14ecda8 | ||
|
|
2b458666ba | ||
|
|
06a8d684a6 | ||
|
|
18d42b411b | ||
|
|
cbd878bbd5 | ||
|
|
49eeed8420 | ||
|
|
5f36d91afd | ||
|
|
a08ce38bcf | ||
|
|
5d204fc3aa | ||
|
|
84ead30e58 | ||
|
|
b11e1afc91 | ||
|
|
c3aa02f003 | ||
|
|
d142830109 | ||
|
|
22e41bab3f | ||
|
|
b0078a2379 | ||
|
|
50109bb307 | ||
|
|
aa1c8d1fa4 | ||
|
|
1de20c1232 | ||
|
|
8fe093de1d | ||
|
|
f004361ef2 | ||
|
|
d6db38a318 | ||
|
|
c88a5291b7 | ||
|
|
0d614cf103 | ||
|
|
29d6cfae80 | ||
|
|
c48a6e73e8 | ||
|
|
956c11388c | ||
|
|
5e8eb926f2 | ||
|
|
1e4f8f27fd | ||
|
|
0ea041e8cb | ||
|
|
e048deb64c | ||
|
|
8ece08e1a3 | ||
|
|
bdfb2b9a0d | ||
|
|
3fd37462bb | ||
|
|
1164e931c5 | ||
|
|
0e8afdb050 | ||
|
|
23119a6f12 | ||
|
|
8001f226b9 | ||
|
|
1c0fa46dfa | ||
|
|
f7455d48cc | ||
|
|
a86d78216c | ||
|
|
47c0b0326d | ||
|
|
0ca979b453 | ||
|
|
c87631c2e6 | ||
|
|
987aa2dd85 | ||
|
|
91b40d1e84 | ||
|
|
bc17f95c80 | ||
|
|
0069ca9ce8 | ||
|
|
fe750240c8 | ||
|
|
1795d3ea27 | ||
|
|
b933ee8e78 | ||
|
|
a3dcf1e004 | ||
|
|
b2411d949e | ||
|
|
19101f7f4a | ||
|
|
a2e4a1eb26 | ||
|
|
742d80aac4 | ||
|
|
473f1d71ff | ||
|
|
8f40bd4675 | ||
|
|
7add262721 | ||
|
|
4a218f1b79 | ||
|
|
419c03daa2 | ||
|
|
019f1e9fc7 | ||
|
|
53604782e5 | ||
|
|
689e8c0ee3 | ||
|
|
0173929538 | ||
|
|
f570757701 | ||
|
|
5473e870eb | ||
|
|
a41b7baf81 | ||
|
|
fcddb5c06c | ||
|
|
5d3544185c | ||
|
|
5dce9501a6 | ||
|
|
b313068cab | ||
|
|
08e8cc1915 | ||
|
|
52f3572d5b | ||
|
|
a804117c83 | ||
|
|
2b14e4a617 | ||
|
|
ad57435953 | ||
|
|
7702d38699 | ||
|
|
90914b38f1 | ||
|
|
8bfd900be5 | ||
|
|
40e8eaf5fd | ||
|
|
d3a606e9d9 | ||
|
|
2fb3d5fd1f | ||
|
|
ba795d86f0 | ||
|
|
e8107b68c8 | ||
|
|
94e69d5e30 | ||
|
|
d1391f1db7 | ||
|
|
b9174b828e | ||
|
|
a6cf6c00ea | ||
|
|
f6097ce6d0 | ||
|
|
1474f755cf | ||
|
|
dcdbd5ab20 | ||
|
|
cbc02bbc4c | ||
|
|
b5796c6b96 | ||
|
|
d80952a2bc | ||
|
|
2e8544f48d | ||
|
|
f26d436ee6 | ||
|
|
6052a86df1 | ||
|
|
9854e3746f | ||
|
|
7a50299cb0 | ||
|
|
7dba426db4 | ||
|
|
fcf0ceeac6 | ||
|
|
e253d8f6db | ||
|
|
8fb544523c | ||
|
|
3ae44ef2f3 | ||
|
|
730d47f7ad | ||
|
|
ea0ab938c4 | ||
|
|
9763ea768d | ||
|
|
8e3406ad20 | ||
|
|
aaf0dc825d | ||
|
|
8729d1e4c2 | ||
|
|
db6eec653b | ||
|
|
c654549d35 | ||
|
|
645897503d | ||
|
|
fe8f2a4603 | ||
|
|
0a4330ee3c | ||
|
|
d157744fb2 | ||
|
|
82320d6e55 | ||
|
|
7db1864766 | ||
|
|
a55a07940c | ||
|
|
37c01a0a4d | ||
|
|
2003be8dc5 | ||
|
|
bfac015d61 | ||
|
|
cd7258a7cd | ||
|
|
c3215af5f6 | ||
|
|
ba0f4d77a0 | ||
|
|
eb681c827d | ||
|
|
e82c53803f | ||
|
|
781d570ec1 | ||
|
|
fef9530ca5 | ||
|
|
ab1dd11705 | ||
|
|
7bf5be9c17 | ||
|
|
ac11ba32ee | ||
|
|
a20ad03f7b | ||
|
|
292f219900 | ||
|
|
55e550f08f | ||
|
|
32943a74b2 | ||
|
|
89291e4010 | ||
|
|
a8a66843db | ||
|
|
32b34c4b80 | ||
|
|
45d0d731eb | ||
|
|
125f62b71e | ||
|
|
eb6ba2a703 | ||
|
|
e559168bd7 | ||
|
|
959d05e6f8 | ||
|
|
e60183313b | ||
|
|
7c0a0c495d | ||
|
|
39eb1e4753 | ||
|
|
079682f313 | ||
|
|
e8513b3241 | ||
|
|
8bac2087cf | ||
|
|
ce1bf11d4b | ||
|
|
eb8138bfbd | ||
|
|
5180eb93e5 | ||
|
|
27fa4d310a | ||
|
|
adc22ec80b | ||
|
|
4bc9f2422b | ||
|
|
d695d2ccc0 | ||
|
|
5fa9dd7cd5 | ||
|
|
b352ae03a9 | ||
|
|
c93da867e0 | ||
|
|
556811ec64 | ||
|
|
403ece66e3 | ||
|
|
9a35dbc4ab | ||
|
|
6f70a53da6 | ||
|
|
93ee9e30d8 | ||
|
|
a84c319d62 | ||
|
|
467b419947 | ||
|
|
ee158fb0aa | ||
|
|
280c9dfcf3 | ||
|
|
e4c59dd164 | ||
|
|
f705b2aec4 | ||
|
|
dce20c3e6a | ||
|
|
e0a2353c56 | ||
|
|
c0ffed7788 | ||
|
|
21b48d24e4 | ||
|
|
e13c9102b8 | ||
|
|
14d8894b40 | ||
|
|
b607a22076 | ||
|
|
b8dafbdf5e | ||
|
|
3b03ff626a | ||
|
|
9614611969 | ||
|
|
f178b78816 | ||
|
|
381488a9bd | ||
|
|
0cbc78656e | ||
|
|
e180d8e594 | ||
|
|
cbb5da5285 | ||
|
|
03877c3752 | ||
|
|
54a3209cf5 | ||
|
|
9eb554a843 | ||
|
|
7036859823 | ||
|
|
60cb3f67f2 | ||
|
|
6b59609f9b | ||
|
|
a3c5ac4730 | ||
|
|
ed1ea91a6f | ||
|
|
209d1b0946 | ||
|
|
761cb97090 | ||
|
|
dcc7b23980 | ||
|
|
b9667fd209 | ||
|
|
d23105ccb7 | ||
|
|
30499005f8 | ||
|
|
f2cd057e89 | ||
|
|
2b465ee65f | ||
|
|
7e092a7658 | ||
|
|
d39b56adaa | ||
|
|
152a20a416 | ||
|
|
7ff0797535 | ||
|
|
ca57c2f6b6 | ||
|
|
d75ba74bbd | ||
|
|
9ff1925538 | ||
|
|
1915408096 | ||
|
|
45801883ba | ||
|
|
382a328ead | ||
|
|
babfa41424 | ||
|
|
204ff787fa | ||
|
|
2a68cc7076 | ||
|
|
b1f807abd1 | ||
|
|
2c830a4698 | ||
|
|
87ce5068bb | ||
|
|
7c794b8d93 | ||
|
|
0a406eab94 | ||
|
|
c67ccad74d | ||
|
|
5d59adc52b | ||
|
|
770cfcaae9 | ||
|
|
04dae32509 | ||
|
|
3e0813d407 | ||
|
|
c8b83d7024 | ||
|
|
d1285255eb | ||
|
|
dfbc6e6a57 | ||
|
|
2eab5d03fd | ||
|
|
fe634e5326 | ||
|
|
bac44d7ffb | ||
|
|
51b59bc8f0 | ||
|
|
2d10e31931 | ||
|
|
4cf7b3cebd | ||
|
|
e11834d1c9 | ||
|
|
8f22ff3032 | ||
|
|
8e94551881 | ||
|
|
6e1470c9d3 | ||
|
|
9416bf9079 | ||
|
|
24f83eef7c | ||
|
|
b7c0576eb5 | ||
|
|
5a48750e16 | ||
|
|
ae93f6345c | ||
|
|
9bba616426 | ||
|
|
1fe50f272b | ||
|
|
93023acc6c | ||
|
|
daf659c64e | ||
|
|
de2a855572 | ||
|
|
df401f57a2 | ||
|
|
6e178653a5 | ||
|
|
9cf1a0c77c | ||
|
|
ebdbe88bc0 | ||
|
|
74ec1d3377 | ||
|
|
1e4f288a7c | ||
|
|
6a21a6f148 | ||
|
|
661b99ec17 | ||
|
|
ecd143fcc2 | ||
|
|
bd9389b956 | ||
|
|
1a09cef0ef | ||
|
|
aa3a6d5916 | ||
|
|
bded1d1115 | ||
|
|
fa4f03525e | ||
|
|
4ee89e62fc | ||
|
|
ee65dea8af | ||
|
|
27609327ee | ||
|
|
958cd0de64 | ||
|
|
1d7601edfb | ||
|
|
8a0fdcfea9 | ||
|
|
75a1ad8bdb | ||
|
|
5bac7f0ca6 | ||
|
|
08fea5705a | ||
|
|
8e5b5d00e1 | ||
|
|
97e8482348 | ||
|
|
0cc73e279e | ||
|
|
54232c6542 | ||
|
|
ee2856f9bc | ||
|
|
737cea0b38 | ||
|
|
a8eeea0b18 | ||
|
|
a2bc88f6db | ||
|
|
5ce8ae79f0 | ||
|
|
d98e9a63d0 | ||
|
|
8e30adbca0 | ||
|
|
9adfd08848 | ||
|
|
2e3419ccbb | ||
|
|
5b6b6dc1b7 | ||
|
|
d0271a90b5 | ||
|
|
20ffe2b2a0 | ||
|
|
94c80e2507 | ||
|
|
efa344be98 | ||
|
|
2ba9a009fe | ||
|
|
c59ffa09e0 | ||
|
|
72f5e028d0 | ||
|
|
80b361dbb0 | ||
|
|
58254adb11 | ||
|
|
7f60e8a307 | ||
|
|
c31be5af4d | ||
|
|
292c01fda2 | ||
|
|
91f7d43e84 | ||
|
|
ce71e65aee | ||
|
|
1119701071 | ||
|
|
42122c270a | ||
|
|
1348621d9e | ||
|
|
8c5ea61376 | ||
|
|
c410f4055f | ||
|
|
1e86635572 | ||
|
|
62ede05c09 | ||
|
|
a067eb02a5 | ||
|
|
154876a17b | ||
|
|
f8c70993c0 | ||
|
|
03a2828fcf | ||
|
|
2fc0056ada | ||
|
|
9a33116526 | ||
|
|
29fcd7c946 | ||
|
|
189f122dd7 | ||
|
|
69c708be44 |
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
|
||||||
|
...
|
||||||
|
|
||||||
47
.gitignore
vendored
47
.gitignore
vendored
@@ -1,14 +1,18 @@
|
|||||||
|
# emacs backup file
|
||||||
*~
|
*~
|
||||||
*.o
|
|
||||||
*.lo
|
# autotools
|
||||||
*.la
|
*.la
|
||||||
depcomp
|
*.lo
|
||||||
*.m4
|
*.m4
|
||||||
|
*.o
|
||||||
|
.deps/
|
||||||
|
.libs/
|
||||||
|
INSTALL
|
||||||
Makefile
|
Makefile
|
||||||
Makefile.in
|
Makefile.in
|
||||||
libtool
|
|
||||||
missing
|
|
||||||
autom4te.cache/
|
autom4te.cache/
|
||||||
|
compile
|
||||||
config.guess
|
config.guess
|
||||||
config.h
|
config.h
|
||||||
config.h.in
|
config.h.in
|
||||||
@@ -16,33 +20,14 @@ config.log
|
|||||||
config.status
|
config.status
|
||||||
config.sub
|
config.sub
|
||||||
configure
|
configure
|
||||||
|
depcomp
|
||||||
install-sh
|
install-sh
|
||||||
.deps/
|
libtool
|
||||||
.libs
|
|
||||||
lib/includes/nghttp2/nghttp2ver.h
|
|
||||||
lib/libnghttp2.pc
|
|
||||||
src/libnghttp2_asio.pc
|
|
||||||
ltmain.sh
|
ltmain.sh
|
||||||
|
missing
|
||||||
stamp-h1
|
stamp-h1
|
||||||
.deps/
|
|
||||||
INSTALL
|
|
||||||
.DS_STORE
|
|
||||||
compile
|
|
||||||
test-driver
|
test-driver
|
||||||
.dirstamp
|
|
||||||
doc/index.rst
|
# test logs generated by `make check`
|
||||||
doc/nghttp2.h.rst
|
*.log
|
||||||
doc/nghttp2ver.h.rst
|
*.trs
|
||||||
doc/package_README.rst
|
|
||||||
doc/tutorial-client.rst
|
|
||||||
doc/tutorial-server.rst
|
|
||||||
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
|
|
||||||
python/setup.py
|
|
||||||
python/dist
|
|
||||||
python/MANIFEST
|
|
||||||
|
|||||||
47
.travis.yml
47
.travis.yml
@@ -1,32 +1,31 @@
|
|||||||
language: cpp
|
language: cpp
|
||||||
compiler:
|
compiler:
|
||||||
- clang
|
- clang
|
||||||
#Disable gcc build for the moment...
|
- gcc
|
||||||
# - gcc
|
sudo: false
|
||||||
python:
|
addons:
|
||||||
- "3.4"
|
apt:
|
||||||
|
sources:
|
||||||
|
- ubuntu-toolchain-r-test
|
||||||
|
packages:
|
||||||
|
- g++-4.9
|
||||||
|
- libstdc++-4.9-dev
|
||||||
|
- autoconf
|
||||||
|
- automake
|
||||||
|
- autotools-dev
|
||||||
|
- libtool
|
||||||
|
- pkg-config
|
||||||
|
- zlib1g-dev
|
||||||
|
- libcunit1-dev
|
||||||
|
- libssl-dev
|
||||||
|
- libxml2-dev
|
||||||
|
- libev-dev
|
||||||
|
- libevent-dev
|
||||||
|
- libjansson-dev
|
||||||
|
- libjemalloc-dev
|
||||||
before_install:
|
before_install:
|
||||||
- $CC --version
|
- $CC --version
|
||||||
- sudo add-apt-repository --yes ppa:ubuntu-toolchain-r/test
|
- if [ "$CXX" = "g++" ]; then export CXX="g++-4.9" CC="gcc-4.9"; fi
|
||||||
- sudo apt-get update -qq
|
|
||||||
#Install and use gcc-4.8 (don't build with gcc-4.6)
|
|
||||||
#libstdc++-4.8 is needed by Clang to build too
|
|
||||||
- sudo apt-get -qq install g++-4.8 libstdc++-4.8-dev
|
|
||||||
- >
|
|
||||||
sudo apt-get install --no-install-recommends -qq
|
|
||||||
autoconf
|
|
||||||
automake
|
|
||||||
autotools-dev
|
|
||||||
libtool
|
|
||||||
pkg-config
|
|
||||||
zlib1g-dev
|
|
||||||
libcunit1-dev
|
|
||||||
libssl-dev
|
|
||||||
libxml2-dev
|
|
||||||
libevent-dev
|
|
||||||
libjansson-dev
|
|
||||||
libjemalloc-dev
|
|
||||||
- if [ "$CXX" = "g++" ]; then export CXX="g++-4.8" CC="gcc-4.8"; fi
|
|
||||||
- $CC --version
|
- $CC --version
|
||||||
before_script:
|
before_script:
|
||||||
- autoreconf -i
|
- autoreconf -i
|
||||||
|
|||||||
18
CONTRIBUTION
Normal file
18
CONTRIBUTION
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
[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!
|
||||||
21
COPYING
21
COPYING
@@ -1,6 +1,6 @@
|
|||||||
The MIT License
|
The MIT License
|
||||||
|
|
||||||
Copyright (c) 2012, 2014 Tatsuhiro Tsujikawa
|
Copyright (c) 2012, 2014, 2015 Tatsuhiro Tsujikawa
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
a copy of this software and associated documentation files (the
|
a copy of this software and associated documentation files (the
|
||||||
@@ -20,22 +20,3 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|||||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
[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!
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# vim: ft=dockerfile:
|
||||||
# Dockerfile to build nghttp2 android binary
|
# Dockerfile to build nghttp2 android binary
|
||||||
#
|
#
|
||||||
# $ sudo docker build -t nghttp2-android - < Dockerfile.android
|
# $ sudo docker build -t nghttp2-android - < Dockerfile.android
|
||||||
@@ -9,7 +10,7 @@
|
|||||||
#
|
#
|
||||||
# $ sudo docker run -v /path/to/dest:/out nghttp2-android cp /root/build/nghttp2/src/nghttpx /out
|
# $ sudo docker run -v /path/to/dest:/out nghttp2-android cp /root/build/nghttp2/src/nghttpx /out
|
||||||
|
|
||||||
FROM ubuntu
|
FROM ubuntu:vivid
|
||||||
|
|
||||||
MAINTAINER Tatsuhiro Tsujikawa
|
MAINTAINER Tatsuhiro Tsujikawa
|
||||||
|
|
||||||
@@ -29,11 +30,12 @@ RUN apt-get install -y make binutils autoconf automake autotools-dev libtool \
|
|||||||
genisoimage libc6-i386 lib32stdc++6
|
genisoimage libc6-i386 lib32stdc++6
|
||||||
|
|
||||||
WORKDIR /root/build
|
WORKDIR /root/build
|
||||||
RUN curl -L -O http://dl.google.com/android/ndk/android-ndk-r10c-linux-x86_64.bin
|
RUN curl -L -O http://dl.google.com/android/ndk/android-ndk-r10d-linux-x86_64.bin && \
|
||||||
RUN chmod a+x android-ndk-r10c-linux-x86_64.bin
|
chmod a+x android-ndk-r10d-linux-x86_64.bin && \
|
||||||
RUN ./android-ndk-r10c-linux-x86_64.bin
|
./android-ndk-r10d-linux-x86_64.bin && \
|
||||||
|
rm android-ndk-r10d-linux-x86_64.bin
|
||||||
|
|
||||||
WORKDIR /root/build/android-ndk-r10c
|
WORKDIR /root/build/android-ndk-r10d
|
||||||
RUN /bin/bash build/tools/make-standalone-toolchain.sh \
|
RUN /bin/bash build/tools/make-standalone-toolchain.sh \
|
||||||
--install-dir=$ANDROID_HOME/toolchain \
|
--install-dir=$ANDROID_HOME/toolchain \
|
||||||
--toolchain=arm-linux-androideabi-4.9 --llvm-version=3.5 \
|
--toolchain=arm-linux-androideabi-4.9 --llvm-version=3.5 \
|
||||||
@@ -57,18 +59,24 @@ RUN autoreconf -i && \
|
|||||||
make install
|
make install
|
||||||
|
|
||||||
WORKDIR /root/build
|
WORKDIR /root/build
|
||||||
RUN curl -L -O https://www.openssl.org/source/openssl-1.0.1j.tar.gz
|
RUN curl -L -O https://www.openssl.org/source/openssl-1.0.2a.tar.gz && \
|
||||||
RUN tar xf openssl-1.0.1j.tar.gz
|
tar xf openssl-1.0.2a.tar.gz && \
|
||||||
WORKDIR /root/build/openssl-1.0.1j
|
rm openssl-1.0.2a.tar.gz
|
||||||
|
|
||||||
|
WORKDIR /root/build/openssl-1.0.2a
|
||||||
RUN export CROSS_COMPILE=$TOOLCHAIN/bin/arm-linux-androideabi- && \
|
RUN export CROSS_COMPILE=$TOOLCHAIN/bin/arm-linux-androideabi- && \
|
||||||
./Configure --prefix=$PREFIX android && \
|
./Configure --prefix=$PREFIX android && \
|
||||||
make && make install_sw
|
make && make install_sw
|
||||||
|
|
||||||
WORKDIR /root/build
|
WORKDIR /root/build
|
||||||
RUN curl -L -O https://github.com/downloads/libevent/libevent/libevent-2.0.21-stable.tar.gz
|
RUN curl -L -O http://dist.schmorp.de/libev/libev-4.19.tar.gz && \
|
||||||
RUN tar xf libevent-2.0.21-stable.tar.gz
|
curl -L -O https://gist.github.com/tatsuhiro-t/48c45f08950f587180ed/raw/80a8f003b5d1091eae497c5995bbaa68096e739b/libev-4.19-android.patch && \
|
||||||
WORKDIR /root/build/libevent-2.0.21-stable
|
tar xf libev-4.19.tar.gz && \
|
||||||
RUN ./configure \
|
rm libev-4.19.tar.gz
|
||||||
|
|
||||||
|
WORKDIR /root/build/libev-4.19
|
||||||
|
RUN patch -p1 < ../libev-4.19-android.patch && \
|
||||||
|
./configure \
|
||||||
--host=arm-linux-androideabi \
|
--host=arm-linux-androideabi \
|
||||||
--build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \
|
--build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \
|
||||||
--prefix=$PREFIX \
|
--prefix=$PREFIX \
|
||||||
@@ -78,6 +86,25 @@ RUN ./configure \
|
|||||||
LDFLAGS=-L$PREFIX/lib && \
|
LDFLAGS=-L$PREFIX/lib && \
|
||||||
make install
|
make install
|
||||||
|
|
||||||
|
WORKDIR /root/build
|
||||||
|
RUN curl -L -O http://zlib.net/zlib-1.2.8.tar.gz && \
|
||||||
|
tar xf zlib-1.2.8.tar.gz && \
|
||||||
|
rm zlib-1.2.8.tar.gz
|
||||||
|
|
||||||
|
WORKDIR /root/build/zlib-1.2.8
|
||||||
|
RUN HOST=arm-linux-androideabi \
|
||||||
|
CC=$HOST-gcc \
|
||||||
|
AR=$HOST-ar \
|
||||||
|
LD=$HOST-ld \
|
||||||
|
RANLIB=$HOST-ranlib \
|
||||||
|
STRIP=$HOST-strip \
|
||||||
|
./configure \
|
||||||
|
--prefix=$PREFIX \
|
||||||
|
--libdir=$PREFIX/lib \
|
||||||
|
--includedir=$PREFIX/include \
|
||||||
|
--static && \
|
||||||
|
make install
|
||||||
|
|
||||||
WORKDIR /root/build
|
WORKDIR /root/build
|
||||||
RUN git clone https://github.com/tatsuhiro-t/nghttp2
|
RUN git clone https://github.com/tatsuhiro-t/nghttp2
|
||||||
WORKDIR /root/build/nghttp2
|
WORKDIR /root/build/nghttp2
|
||||||
@@ -93,8 +120,9 @@ RUN autoreconf -i && \
|
|||||||
--disable-threads \
|
--disable-threads \
|
||||||
LIBSPDYLAY_CFLAGS=-I$PREFIX/usr/local/include \
|
LIBSPDYLAY_CFLAGS=-I$PREFIX/usr/local/include \
|
||||||
LIBSPDYLAY_LIBS="-L$PREFIX/usr/local/lib -lspdylay" \
|
LIBSPDYLAY_LIBS="-L$PREFIX/usr/local/lib -lspdylay" \
|
||||||
CPPFLAGS="-I$PREFIX/include" \
|
CPPFLAGS="-fPIE -I$PREFIX/include" \
|
||||||
|
CXXFLAGS="-fno-strict-aliasing" \
|
||||||
PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \
|
PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \
|
||||||
LDFLAGS="-L$PREFIX/lib" && \
|
LDFLAGS="-fPIE -pie -L$PREFIX/lib" && \
|
||||||
make && \
|
make && \
|
||||||
arm-linux-androideabi-strip src/nghttpx src/nghttpd src/nghttp
|
arm-linux-androideabi-strip src/nghttpx src/nghttpd src/nghttp
|
||||||
|
|||||||
15
Makefile.am
15
Makefile.am
@@ -20,7 +20,8 @@
|
|||||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
SUBDIRS = lib third-party src examples python tests doc contrib
|
SUBDIRS = lib third-party src examples python tests integration-tests \
|
||||||
|
doc contrib script
|
||||||
|
|
||||||
ACLOCAL_AMFLAGS = -I m4
|
ACLOCAL_AMFLAGS = -I m4
|
||||||
|
|
||||||
@@ -28,3 +29,15 @@ 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
|
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}
|
||||||
|
|||||||
954
README.rst
954
README.rst
File diff suppressed because it is too large
Load Diff
@@ -39,7 +39,9 @@ PATH=$TOOLCHAIN/bin:$PATH
|
|||||||
--without-libxml2 \
|
--without-libxml2 \
|
||||||
--disable-python-bindings \
|
--disable-python-bindings \
|
||||||
--disable-examples \
|
--disable-examples \
|
||||||
--disable-threads \
|
--enable-werror \
|
||||||
CPPFLAGS="-I$PREFIX/include" \
|
CC=clang \
|
||||||
|
CXX=clang++ \
|
||||||
|
CPPFLAGS="-fPIE -I$PREFIX/include" \
|
||||||
PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \
|
PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \
|
||||||
LDFLAGS="-L$PREFIX/lib"
|
LDFLAGS="-fPIE -pie -L$PREFIX/lib"
|
||||||
|
|||||||
335
configure.ac
335
configure.ac
@@ -1,6 +1,6 @@
|
|||||||
dnl nghttp2 - HTTP/2 C Library
|
dnl nghttp2 - HTTP/2 C Library
|
||||||
|
|
||||||
dnl Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa
|
dnl Copyright (c) 2012, 2013, 2014, 2015 Tatsuhiro Tsujikawa
|
||||||
|
|
||||||
dnl Permission is hereby granted, free of charge, to any person obtaining
|
dnl Permission is hereby granted, free of charge, to any person obtaining
|
||||||
dnl a copy of this software and associated documentation files (the
|
dnl a copy of this software and associated documentation files (the
|
||||||
@@ -20,15 +20,36 @@ dnl NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|||||||
dnl LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
dnl LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
dnl OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
dnl OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
dnl WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
dnl WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
dnl Do not change user variables!
|
||||||
|
dnl http://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html
|
||||||
|
|
||||||
AC_PREREQ(2.61)
|
AC_PREREQ(2.61)
|
||||||
AC_INIT([nghttp2], [0.6.5], [t-tujikawa@users.sourceforge.net])
|
AC_INIT([nghttp2], [1.0.4], [t-tujikawa@users.sourceforge.net])
|
||||||
|
AC_USE_SYSTEM_EXTENSIONS
|
||||||
|
|
||||||
LT_PREREQ([2.2.6])
|
LT_PREREQ([2.2.6])
|
||||||
LT_INIT()
|
LT_INIT()
|
||||||
|
|
||||||
|
AC_CANONICAL_BUILD
|
||||||
|
AC_CANONICAL_HOST
|
||||||
|
AC_CANONICAL_TARGET
|
||||||
|
|
||||||
|
AM_INIT_AUTOMAKE([subdir-objects])
|
||||||
|
# comment out for now since this requires automake 1.13 or higher and
|
||||||
|
# travis has older one.
|
||||||
|
# AM_EXTRA_RECURSIVE_TARGETS([it])
|
||||||
|
|
||||||
|
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||||
|
|
||||||
|
AC_CONFIG_MACRO_DIR([m4])
|
||||||
|
AC_CONFIG_HEADERS([config.h])
|
||||||
|
|
||||||
dnl See versioning rule:
|
dnl See versioning rule:
|
||||||
dnl http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
dnl http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
||||||
AC_SUBST(LT_CURRENT, 7)
|
AC_SUBST(LT_CURRENT, 14)
|
||||||
AC_SUBST(LT_REVISION, 0)
|
AC_SUBST(LT_REVISION, 4)
|
||||||
AC_SUBST(LT_AGE, 2)
|
AC_SUBST(LT_AGE, 0)
|
||||||
|
|
||||||
major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"`
|
major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"`
|
||||||
minor=`echo $PACKAGE_VERSION |cut -d. -f2 | sed -e "s/[^0-9]//g"`
|
minor=`echo $PACKAGE_VERSION |cut -d. -f2 | sed -e "s/[^0-9]//g"`
|
||||||
@@ -38,18 +59,6 @@ PACKAGE_VERSION_NUM=`printf "0x%02x%02x%02x" "$major" "$minor" "$patch"`
|
|||||||
|
|
||||||
AC_SUBST(PACKAGE_VERSION_NUM)
|
AC_SUBST(PACKAGE_VERSION_NUM)
|
||||||
|
|
||||||
AC_CANONICAL_BUILD
|
|
||||||
AC_CANONICAL_HOST
|
|
||||||
AC_CANONICAL_TARGET
|
|
||||||
|
|
||||||
AC_CONFIG_MACRO_DIR([m4])
|
|
||||||
|
|
||||||
AM_INIT_AUTOMAKE([subdir-objects])
|
|
||||||
|
|
||||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
|
||||||
|
|
||||||
AC_CONFIG_HEADERS([config.h])
|
|
||||||
|
|
||||||
dnl Checks for command-line options
|
dnl Checks for command-line options
|
||||||
AC_ARG_ENABLE([werror],
|
AC_ARG_ENABLE([werror],
|
||||||
[AS_HELP_STRING([--enable-werror],
|
[AS_HELP_STRING([--enable-werror],
|
||||||
@@ -92,9 +101,9 @@ AC_ARG_ENABLE([python-bindings],
|
|||||||
[request_python_bindings=$enableval], [request_python_bindings=check])
|
[request_python_bindings=$enableval], [request_python_bindings=check])
|
||||||
|
|
||||||
AC_ARG_ENABLE([failmalloc],
|
AC_ARG_ENABLE([failmalloc],
|
||||||
[AS_HELP_STRING([--enable-failmalloc],
|
[AS_HELP_STRING([--disable-failmalloc],
|
||||||
[Build failmalloc test program [default=no]])],
|
[Do not build failmalloc test program])],
|
||||||
[request_failmalloc=$enableval], [request_failmalloc=no])
|
[request_failmalloc=$enableval], [request_failmalloc=yes])
|
||||||
|
|
||||||
AC_ARG_WITH([libxml2],
|
AC_ARG_WITH([libxml2],
|
||||||
[AS_HELP_STRING([--with-libxml2],
|
[AS_HELP_STRING([--with-libxml2],
|
||||||
@@ -122,14 +131,17 @@ AC_ARG_VAR([CYTHON], [the Cython executable])
|
|||||||
dnl Checks for programs
|
dnl Checks for programs
|
||||||
AC_PROG_CC
|
AC_PROG_CC
|
||||||
AC_PROG_CXX
|
AC_PROG_CXX
|
||||||
|
AC_PROG_CPP
|
||||||
AC_PROG_INSTALL
|
AC_PROG_INSTALL
|
||||||
AC_PROG_LN_S
|
AC_PROG_LN_S
|
||||||
AC_PROG_MAKE_SET
|
AC_PROG_MAKE_SET
|
||||||
AM_PROG_CC_C_O
|
AC_PROG_MKDIR_P
|
||||||
|
|
||||||
PKG_PROG_PKG_CONFIG([0.20])
|
PKG_PROG_PKG_CONFIG([0.20])
|
||||||
|
|
||||||
|
AM_PATH_PYTHON([2.7],, [:])
|
||||||
|
|
||||||
if [test "x$request_python_bindings" != "xno"]; then
|
if [test "x$request_python_bindings" != "xno"]; then
|
||||||
AM_PATH_PYTHON([2.7],, [:])
|
|
||||||
AX_PYTHON_DEVEL([>= '2.7'])
|
AX_PYTHON_DEVEL([>= '2.7'])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -140,6 +152,20 @@ else
|
|||||||
AC_SUBST([CYTHON])
|
AC_SUBST([CYTHON])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# If we're running GCC or clang define _U_ to be "__attribute__((unused))"
|
||||||
|
# so we can use _U_ to flag unused function parameters and not get warnings
|
||||||
|
# about them. Otherwise, define _U_ to be an empty string so that _U_ used
|
||||||
|
# to flag an unused function parameters will compile with other compilers.
|
||||||
|
#
|
||||||
|
# XXX - similar hints for other compilers?
|
||||||
|
#
|
||||||
|
if test "x$GCC" = "xyes" -o "x$CC" = "xclang" ; then
|
||||||
|
AC_DEFINE([_U_], [__attribute__((unused))], [Hint to the compiler that a function parameters is not used])
|
||||||
|
else
|
||||||
|
AC_DEFINE([_U_], , [Hint to the compiler that a function parameters is not used])
|
||||||
|
fi
|
||||||
|
|
||||||
AX_CXX_COMPILE_STDCXX_11([noext], [optional])
|
AX_CXX_COMPILE_STDCXX_11([noext], [optional])
|
||||||
|
|
||||||
AC_LANG_PUSH(C++)
|
AC_LANG_PUSH(C++)
|
||||||
@@ -179,53 +205,49 @@ std::vector<std::future<int>> v;
|
|||||||
[have_std_future=no
|
[have_std_future=no
|
||||||
AC_MSG_RESULT([no])])
|
AC_MSG_RESULT([no])])
|
||||||
|
|
||||||
|
# Check that std::map::emplace is available for g++-4.7.
|
||||||
|
AC_MSG_CHECKING([whether std::map::emplace is available])
|
||||||
|
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
|
||||||
|
[[
|
||||||
|
#include <map>
|
||||||
|
]],
|
||||||
|
[[
|
||||||
|
std::map<int, int>().emplace(1, 2);
|
||||||
|
]])],
|
||||||
|
[AC_DEFINE([HAVE_STD_MAP_EMPLACE], [1],
|
||||||
|
[Define to 1 if you have the `std::map::emplace`.])
|
||||||
|
have_std_map_emplace=yes
|
||||||
|
AC_MSG_RESULT([yes])],
|
||||||
|
[have_std_map_emplace=no
|
||||||
|
AC_MSG_RESULT([no])])
|
||||||
|
|
||||||
AC_LANG_POP()
|
AC_LANG_POP()
|
||||||
|
|
||||||
# Checks for libraries.
|
# Checks for libraries.
|
||||||
|
|
||||||
# Additional libraries required for tests.
|
# Additional libraries required for tests.
|
||||||
TESTS_LIBS=
|
TESTLDADD=
|
||||||
|
|
||||||
# Additional libraries required for programs under src directory.
|
# Additional libraries required for programs under src directory.
|
||||||
SRC_LIBS=
|
APPLDFLAGS=
|
||||||
|
|
||||||
LIBS_OLD=$LIBS
|
|
||||||
# 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"
|
|
||||||
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"
|
|
||||||
LIBS=$LIBS_OLD
|
|
||||||
|
|
||||||
case "$host" in
|
case "$host" in
|
||||||
*android*)
|
*android*)
|
||||||
android_build=yes
|
android_build=yes
|
||||||
# android does not need -pthread, but needs followng 2 libs for C++
|
# android does not need -pthread, but needs followng 3 libs for C++
|
||||||
SRC_LIBS="$SRC_LIBS -lstdc++ -lsupc++"
|
APPLDFLAGS="$APPLDFLAGS -lstdc++ -latomic -lsupc++"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
PTHREAD_LDFLAGS="-pthread"
|
PTHREAD_LDFLAGS="-pthread"
|
||||||
SRC_LIBS="$SRC_LIBS $PTHREAD_LDFLAGS"
|
APPLDFLAGS="$APPLDFLAGS $PTHREAD_LDFLAGS"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
# zlib
|
# zlib
|
||||||
if test "x$android_build" = "xyes"; then
|
PKG_CHECK_MODULES([ZLIB], [zlib >= 1.2.3], [have_zlib=yes], [have_zlib=no])
|
||||||
# Use zlib provided by NDK
|
|
||||||
SRC_LIBS="-lz $SRC_LIBS"
|
|
||||||
have_zlib=yes
|
|
||||||
else
|
|
||||||
PKG_CHECK_MODULES([ZLIB], [zlib >= 1.2.3], [have_zlib=yes], [have_zlib=no])
|
|
||||||
|
|
||||||
if test "x${have_zlib}" = "xno"; then
|
if test "x${have_zlib}" = "xno"; then
|
||||||
AC_MSG_NOTICE($ZLIB_PKG_ERRORS)
|
AC_MSG_NOTICE($ZLIB_PKG_ERRORS)
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# cunit
|
# cunit
|
||||||
@@ -257,6 +279,22 @@ fi
|
|||||||
|
|
||||||
AM_CONDITIONAL([HAVE_CUNIT], [ test "x${have_cunit}" = "xyes" ])
|
AM_CONDITIONAL([HAVE_CUNIT], [ test "x${have_cunit}" = "xyes" ])
|
||||||
|
|
||||||
|
# libev (for src)
|
||||||
|
# libev does not have pkg-config file. Check it in an old way.
|
||||||
|
LIBS_OLD=$LIBS
|
||||||
|
# android requires -lm for floor
|
||||||
|
AC_CHECK_LIB([ev], [ev_time], [have_libev=yes], [have_libev=no], [-lm])
|
||||||
|
if test "x${have_libev}" = "xyes"; then
|
||||||
|
AC_CHECK_HEADER([ev.h], [have_libev=yes], [have_libev=no])
|
||||||
|
if test "x${have_libev}" = "xyes"; then
|
||||||
|
LIBEV_LIBS=-lev
|
||||||
|
LIBEV_CFLAGS=
|
||||||
|
AC_SUBST([LIBEV_LIBS])
|
||||||
|
AC_SUBST([LIBEV_CFLAGS])
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
LIBS=$LIBS_OLD
|
||||||
|
|
||||||
# openssl (for src)
|
# openssl (for src)
|
||||||
PKG_CHECK_MODULES([OPENSSL], [openssl >= 1.0.1],
|
PKG_CHECK_MODULES([OPENSSL], [openssl >= 1.0.1],
|
||||||
[have_openssl=yes], [have_openssl=no])
|
[have_openssl=yes], [have_openssl=no])
|
||||||
@@ -264,7 +302,7 @@ if test "x${have_openssl}" = "xno"; then
|
|||||||
AC_MSG_NOTICE($OPENSSL_PKG_ERRORS)
|
AC_MSG_NOTICE($OPENSSL_PKG_ERRORS)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# libevent_openssl (for src)
|
# libevent_openssl (for examples)
|
||||||
# 2.0.8 is required because we use evconnlistener_set_error_cb()
|
# 2.0.8 is required because we use evconnlistener_set_error_cb()
|
||||||
PKG_CHECK_MODULES([LIBEVENT_OPENSSL], [libevent_openssl >= 2.0.8],
|
PKG_CHECK_MODULES([LIBEVENT_OPENSSL], [libevent_openssl >= 2.0.8],
|
||||||
[have_libevent_openssl=yes], [have_libevent_openssl=no])
|
[have_libevent_openssl=yes], [have_libevent_openssl=no])
|
||||||
@@ -272,10 +310,13 @@ if test "x${have_libevent_openssl}" = "xno"; then
|
|||||||
AC_MSG_NOTICE($LIBEVENT_OPENSSL_PKG_ERRORS)
|
AC_MSG_NOTICE($LIBEVENT_OPENSSL_PKG_ERRORS)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# jansson (for hdtest/deflatehd and hdtest/inflatehd)
|
# jansson (for src/nghttp, src/deflatehd and src/inflatehd)
|
||||||
PKG_CHECK_MODULES([JANSSON], [jansson >= 2.5],
|
PKG_CHECK_MODULES([JANSSON], [jansson >= 2.5],
|
||||||
[have_jansson=yes], [have_jansson=no])
|
[have_jansson=yes], [have_jansson=no])
|
||||||
if test "x${have_jansson}" == "xno"; then
|
if test "x${have_jansson}" = "xyes"; then
|
||||||
|
AC_DEFINE([HAVE_JANSSON], [1],
|
||||||
|
[Define to 1 if you have `libjansson` library.])
|
||||||
|
else
|
||||||
AC_MSG_NOTICE($JANSSON_PKG_ERRORS)
|
AC_MSG_NOTICE($JANSSON_PKG_ERRORS)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -302,9 +343,23 @@ if test "x${request_jemalloc}" != "xno"; then
|
|||||||
AC_SEARCH_LIBS([malloc_stats_print], [jemalloc], [have_jemalloc=yes], [],
|
AC_SEARCH_LIBS([malloc_stats_print], [jemalloc], [have_jemalloc=yes], [],
|
||||||
[$PTHREAD_LDFLAGS])
|
[$PTHREAD_LDFLAGS])
|
||||||
LIBS=$LIBS_OLD
|
LIBS=$LIBS_OLD
|
||||||
|
|
||||||
|
if test "x${have_jemalloc}" = "xyes"; then
|
||||||
|
jemalloc_libs=${ac_cv_search_malloc_stats_print}
|
||||||
|
else
|
||||||
|
# On Darwin, malloc_stats_print is je_malloc_stats_print
|
||||||
|
AC_SEARCH_LIBS([je_malloc_stats_print], [jemalloc], [have_jemalloc=yes], [],
|
||||||
|
[$PTHREAD_LDFLAGS])
|
||||||
|
LIBS=$LIBS_OLD
|
||||||
|
|
||||||
|
if test "x${have_jemalloc}" = "xyes"; then
|
||||||
|
jemalloc_libs=${ac_cv_search_je_malloc_stats_print}
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
if test "x${have_jemalloc}" = "xyes" &&
|
if test "x${have_jemalloc}" = "xyes" &&
|
||||||
test "x${ac_cv_search_malloc_stats_print}" != "xnone required"; then
|
test "x${jemalloc_libs}" != "xnone required"; then
|
||||||
JEMALLOC_LIBS=${ac_cv_search_malloc_stats_print}
|
JEMALLOC_LIBS=${jemalloc_libs}
|
||||||
AC_SUBST([JEMALLOC_LIBS])
|
AC_SUBST([JEMALLOC_LIBS])
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
@@ -317,7 +372,7 @@ fi
|
|||||||
# spdylay (for src/nghttpx and src/h2load)
|
# spdylay (for src/nghttpx and src/h2load)
|
||||||
have_spdylay=no
|
have_spdylay=no
|
||||||
if test "x${request_spdylay}" != "xno"; then
|
if test "x${request_spdylay}" != "xno"; then
|
||||||
PKG_CHECK_MODULES([LIBSPDYLAY], [libspdylay >= 1.3.0],
|
PKG_CHECK_MODULES([LIBSPDYLAY], [libspdylay >= 1.3.2],
|
||||||
[have_spdylay=yes], [have_spdylay=no])
|
[have_spdylay=yes], [have_spdylay=no])
|
||||||
if test "x${have_spdylay}" = "xyes"; then
|
if test "x${have_spdylay}" = "xyes"; then
|
||||||
AC_DEFINE([HAVE_SPDYLAY], [1], [Define to 1 if you have `spdylay` library.])
|
AC_DEFINE([HAVE_SPDYLAY], [1], [Define to 1 if you have `spdylay` library.])
|
||||||
@@ -354,12 +409,12 @@ if test "x${request_asio_lib}" = "xyes"; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# The nghttp, nghttpd and nghttpx under src depend on zlib, OpenSSL
|
# The nghttp, nghttpd and nghttpx under src depend on zlib, OpenSSL
|
||||||
# and libevent_openssl
|
# and libev
|
||||||
enable_app=no
|
enable_app=no
|
||||||
if test "x${request_app}" != "xno" &&
|
if test "x${request_app}" != "xno" &&
|
||||||
test "x${have_zlib}" = "xyes" &&
|
test "x${have_zlib}" = "xyes" &&
|
||||||
test "x${have_openssl}" = "xyes" &&
|
test "x${have_openssl}" = "xyes" &&
|
||||||
test "x${have_libevent_openssl}" = "xyes"; then
|
test "x${have_libev}" = "xyes"; then
|
||||||
enable_app=yes
|
enable_app=yes
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -409,6 +464,17 @@ fi
|
|||||||
|
|
||||||
AM_CONDITIONAL([ENABLE_EXAMPLES], [ test "x${enable_examples}" = "xyes" ])
|
AM_CONDITIONAL([ENABLE_EXAMPLES], [ test "x${enable_examples}" = "xyes" ])
|
||||||
|
|
||||||
|
# third-party only be built when needed
|
||||||
|
|
||||||
|
enable_third_party=no
|
||||||
|
if test "x${enable_examples}" = "xyes" ||
|
||||||
|
test "x${enable_app}" = "xyes" ||
|
||||||
|
test "x${enable_asio_lib}" = "xyes"; then
|
||||||
|
enable_third_party=yes
|
||||||
|
fi
|
||||||
|
|
||||||
|
AM_CONDITIONAL([ENABLE_THIRD_PARTY], [ test "x${enable_third_party}" = "xyes" ])
|
||||||
|
|
||||||
# Python bindings
|
# Python bindings
|
||||||
enable_python_bindings=no
|
enable_python_bindings=no
|
||||||
if test "x${request_python_bindings}" != "xno" &&
|
if test "x${request_python_bindings}" != "xno" &&
|
||||||
@@ -431,29 +497,34 @@ AM_CONDITIONAL([ENABLE_PYTHON_BINDINGS],
|
|||||||
AM_CONDITIONAL([HAVE_CYTHON], [test "x${CYTHON}" != "x"])
|
AM_CONDITIONAL([HAVE_CYTHON], [test "x${CYTHON}" != "x"])
|
||||||
|
|
||||||
# failmalloc tests
|
# failmalloc tests
|
||||||
|
enable_failmalloc=no
|
||||||
|
if test "x${request_failmalloc}" = "xyes"; then
|
||||||
|
enable_failmalloc=yes
|
||||||
|
fi
|
||||||
|
|
||||||
AM_CONDITIONAL([ENABLE_FAILMALLOC], [ test "x${enable_failmalloc}" = "xyes" ])
|
AM_CONDITIONAL([ENABLE_FAILMALLOC], [ test "x${enable_failmalloc}" = "xyes" ])
|
||||||
|
|
||||||
# Checks for header files.
|
# Checks for header files.
|
||||||
AC_HEADER_ASSERT
|
AC_HEADER_ASSERT
|
||||||
AC_CHECK_HEADERS([ \
|
AC_CHECK_HEADERS([ \
|
||||||
arpa/inet.h \
|
arpa/inet.h \
|
||||||
|
fcntl.h \
|
||||||
|
inttypes.h \
|
||||||
|
limits.h \
|
||||||
|
netdb.h \
|
||||||
netinet/in.h \
|
netinet/in.h \
|
||||||
pwd.h \
|
pwd.h \
|
||||||
stddef.h \
|
stddef.h \
|
||||||
stdint.h \
|
stdint.h \
|
||||||
stdlib.h \
|
stdlib.h \
|
||||||
string.h \
|
string.h \
|
||||||
|
sys/socket.h \
|
||||||
|
sys/time.h \
|
||||||
|
syslog.h \
|
||||||
time.h \
|
time.h \
|
||||||
unistd.h \
|
unistd.h \
|
||||||
])
|
])
|
||||||
|
|
||||||
case "${host}" in
|
|
||||||
*mingw*)
|
|
||||||
# For ntohl, ntohs in Windows
|
|
||||||
AC_CHECK_HEADERS([winsock2.h])
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# Checks for typedefs, structures, and compiler characteristics.
|
# Checks for typedefs, structures, and compiler characteristics.
|
||||||
AC_TYPE_SIZE_T
|
AC_TYPE_SIZE_T
|
||||||
AC_TYPE_SSIZE_T
|
AC_TYPE_SSIZE_T
|
||||||
@@ -461,19 +532,62 @@ AC_TYPE_UINT8_T
|
|||||||
AC_TYPE_UINT16_T
|
AC_TYPE_UINT16_T
|
||||||
AC_TYPE_UINT32_T
|
AC_TYPE_UINT32_T
|
||||||
AC_TYPE_UINT64_T
|
AC_TYPE_UINT64_T
|
||||||
|
AC_TYPE_INT8_T
|
||||||
|
AC_TYPE_INT16_T
|
||||||
|
AC_TYPE_INT32_T
|
||||||
|
AC_TYPE_INT64_T
|
||||||
|
AC_TYPE_OFF_T
|
||||||
|
AC_TYPE_PID_T
|
||||||
|
AC_TYPE_UID_T
|
||||||
AC_CHECK_TYPES([ptrdiff_t])
|
AC_CHECK_TYPES([ptrdiff_t])
|
||||||
AC_C_BIGENDIAN
|
AC_C_BIGENDIAN
|
||||||
|
AC_C_INLINE
|
||||||
AC_SYS_LARGEFILE
|
AC_SYS_LARGEFILE
|
||||||
|
|
||||||
|
AC_CHECK_MEMBER([struct tm.tm_gmtoff], [have_struct_tm_tm_gmtoff=yes],
|
||||||
|
[have_struct_tm_tm_gmtoff=no], [[#include <time.h>]])
|
||||||
|
|
||||||
|
if test "x$have_struct_tm_tm_gmtoff" = "xyes"; then
|
||||||
|
AC_DEFINE([HAVE_STRUCT_TM_TM_GMTOFF], [1],
|
||||||
|
[Define to 1 if you have `struct tm.tm_gmtoff` member.])
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check size of pointer to decide we need 8 bytes alignment
|
||||||
|
# adjustment.
|
||||||
|
AC_CHECK_SIZEOF([int *])
|
||||||
|
|
||||||
# Checks for library functions.
|
# Checks for library functions.
|
||||||
if test "x$cross_compiling" != "xyes"; then
|
if test "x$cross_compiling" != "xyes"; then
|
||||||
AC_FUNC_MALLOC
|
AC_FUNC_MALLOC
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
AC_FUNC_CHOWN
|
||||||
|
AC_FUNC_ERROR_AT_LINE
|
||||||
|
AC_FUNC_FORK
|
||||||
|
# Don't check realloc, since LeakSanitizer detects memory leak during check
|
||||||
|
# AC_FUNC_REALLOC
|
||||||
|
AC_FUNC_STRERROR_R
|
||||||
|
AC_FUNC_STRNLEN
|
||||||
|
|
||||||
AC_CHECK_FUNCS([ \
|
AC_CHECK_FUNCS([ \
|
||||||
_Exit \
|
_Exit \
|
||||||
|
accept4 \
|
||||||
|
dup2 \
|
||||||
|
getcwd \
|
||||||
getpwnam \
|
getpwnam \
|
||||||
|
localtime_r \
|
||||||
|
memchr \
|
||||||
memmove \
|
memmove \
|
||||||
memset \
|
memset \
|
||||||
|
socket \
|
||||||
|
sqrt \
|
||||||
|
strchr \
|
||||||
|
strdup \
|
||||||
|
strerror \
|
||||||
|
strndup \
|
||||||
|
strstr \
|
||||||
|
strtol \
|
||||||
|
strtoul \
|
||||||
timegm \
|
timegm \
|
||||||
])
|
])
|
||||||
|
|
||||||
@@ -490,12 +604,8 @@ AM_CONDITIONAL([ENABLE_TINY_NGHTTPD],
|
|||||||
[ test "x${have_epoll}" = "xyes" &&
|
[ test "x${have_epoll}" = "xyes" &&
|
||||||
test "x${have_timerfd_create}" = "xyes"])
|
test "x${have_timerfd_create}" = "xyes"])
|
||||||
|
|
||||||
dnl Windows library for winsock2
|
ac_save_CFLAGS=$CFLAGS
|
||||||
case "${host}" in
|
CFLAGS=
|
||||||
*mingw*)
|
|
||||||
LIBS="$LIBS -lws2_32"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
if test "x$werror" != "xno"; then
|
if test "x$werror" != "xno"; then
|
||||||
AX_CHECK_COMPILE_FLAG([-Wall], [CFLAGS="$CFLAGS -Wall"])
|
AX_CHECK_COMPILE_FLAG([-Wall], [CFLAGS="$CFLAGS -Wall"])
|
||||||
@@ -518,7 +628,6 @@ if test "x$werror" != "xno"; then
|
|||||||
AX_CHECK_COMPILE_FLAG([-Wcast-align], [CFLAGS="$CFLAGS -Wcast-align"])
|
AX_CHECK_COMPILE_FLAG([-Wcast-align], [CFLAGS="$CFLAGS -Wcast-align"])
|
||||||
AX_CHECK_COMPILE_FLAG([-Wclobbered], [CFLAGS="$CFLAGS -Wclobbered"])
|
AX_CHECK_COMPILE_FLAG([-Wclobbered], [CFLAGS="$CFLAGS -Wclobbered"])
|
||||||
AX_CHECK_COMPILE_FLAG([-Wvla], [CFLAGS="$CFLAGS -Wvla"])
|
AX_CHECK_COMPILE_FLAG([-Wvla], [CFLAGS="$CFLAGS -Wvla"])
|
||||||
AX_CHECK_COMPILE_FLAG([-Wno-unused-parameter], [CFLAGS="$CFLAGS -Wno-unused-parameter"])
|
|
||||||
AX_CHECK_COMPILE_FLAG([-Wpragmas], [CFLAGS="$CFLAGS -Wpragmas"])
|
AX_CHECK_COMPILE_FLAG([-Wpragmas], [CFLAGS="$CFLAGS -Wpragmas"])
|
||||||
AX_CHECK_COMPILE_FLAG([-Wunreachable-code], [CFLAGS="$CFLAGS -Wunreachable-code"])
|
AX_CHECK_COMPILE_FLAG([-Wunreachable-code], [CFLAGS="$CFLAGS -Wunreachable-code"])
|
||||||
AX_CHECK_COMPILE_FLAG([-Waddress], [CFLAGS="$CFLAGS -Waddress"])
|
AX_CHECK_COMPILE_FLAG([-Waddress], [CFLAGS="$CFLAGS -Waddress"])
|
||||||
@@ -530,18 +639,26 @@ if test "x$werror" != "xno"; then
|
|||||||
AX_CHECK_COMPILE_FLAG([-Wheader-guard], [CFLAGS="$CFLAGS -Wheader-guard"])
|
AX_CHECK_COMPILE_FLAG([-Wheader-guard], [CFLAGS="$CFLAGS -Wheader-guard"])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
WARNCFLAGS=$CFLAGS
|
||||||
|
CFLAGS=$ac_save_CFLAGS
|
||||||
|
|
||||||
|
AC_SUBST([WARNCFLAGS])
|
||||||
|
|
||||||
if test "x$debug" != "xno"; then
|
if test "x$debug" != "xno"; then
|
||||||
AC_DEFINE([DEBUGBUILD], [1], [Define to 1 to enable debug output.])
|
AC_DEFINE([DEBUGBUILD], [1], [Define to 1 to enable debug output.])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
enable_threads=yes
|
||||||
# Some platform does not have working std::future. We disable
|
# Some platform does not have working std::future. We disable
|
||||||
# threading for those platforms to exclude std::future use.
|
# threading for those platforms.
|
||||||
if test "x$threads" != "xyes" || test "x$have_std_future" != "xyes"; then
|
if test "x$threads" != "xyes" ||
|
||||||
|
test "x$have_std_future" != "xyes"; then
|
||||||
|
enable_threads=no
|
||||||
AC_DEFINE([NOTHREADS], [1], [Define to 1 if you want to disable threads.])
|
AC_DEFINE([NOTHREADS], [1], [Define to 1 if you want to disable threads.])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
AC_SUBST([TESTS_LIBS])
|
AC_SUBST([TESTLDADD])
|
||||||
AC_SUBST([SRC_LIBS])
|
AC_SUBST([APPLDFLAGS])
|
||||||
|
|
||||||
AC_CONFIG_FILES([
|
AC_CONFIG_FILES([
|
||||||
Makefile
|
Makefile
|
||||||
@@ -558,6 +675,9 @@ AC_CONFIG_FILES([
|
|||||||
examples/Makefile
|
examples/Makefile
|
||||||
python/Makefile
|
python/Makefile
|
||||||
python/setup.py
|
python/setup.py
|
||||||
|
integration-tests/Makefile
|
||||||
|
integration-tests/config.go
|
||||||
|
integration-tests/setenv
|
||||||
doc/Makefile
|
doc/Makefile
|
||||||
doc/conf.py
|
doc/conf.py
|
||||||
doc/index.rst
|
doc/index.rst
|
||||||
@@ -573,7 +693,11 @@ AC_CONFIG_FILES([
|
|||||||
doc/nghttp2.h.rst
|
doc/nghttp2.h.rst
|
||||||
doc/nghttp2ver.h.rst
|
doc/nghttp2ver.h.rst
|
||||||
doc/asio_http2.h.rst
|
doc/asio_http2.h.rst
|
||||||
|
doc/asio_http2_server.h.rst
|
||||||
|
doc/asio_http2_client.h.rst
|
||||||
|
doc/contribute.rst
|
||||||
contrib/Makefile
|
contrib/Makefile
|
||||||
|
script/Makefile
|
||||||
])
|
])
|
||||||
AC_OUTPUT
|
AC_OUTPUT
|
||||||
|
|
||||||
@@ -584,6 +708,7 @@ AC_MSG_NOTICE([summary of build options:
|
|||||||
Install prefix: ${prefix}
|
Install prefix: ${prefix}
|
||||||
C compiler: ${CC}
|
C compiler: ${CC}
|
||||||
CFLAGS: ${CFLAGS}
|
CFLAGS: ${CFLAGS}
|
||||||
|
WARNCFLAGS: ${WARNCFLAGS}
|
||||||
LDFLAGS: ${LDFLAGS}
|
LDFLAGS: ${LDFLAGS}
|
||||||
LIBS: ${LIBS}
|
LIBS: ${LIBS}
|
||||||
CPPFLAGS: ${CPPFLAGS}
|
CPPFLAGS: ${CPPFLAGS}
|
||||||
@@ -592,29 +717,37 @@ AC_MSG_NOTICE([summary of build options:
|
|||||||
CXXFLAGS: ${CXXFLAGS}
|
CXXFLAGS: ${CXXFLAGS}
|
||||||
CXXCPP: ${CXXCPP}
|
CXXCPP: ${CXXCPP}
|
||||||
Library types: Shared=${enable_shared}, Static=${enable_static}
|
Library types: Shared=${enable_shared}, Static=${enable_static}
|
||||||
Python: ${PYTHON}
|
Python:
|
||||||
PYTHON_VERSION: ${PYTHON_VERSION}
|
Python: ${PYTHON}
|
||||||
pyexecdir: ${pyexecdir}
|
PYTHON_VERSION: ${PYTHON_VERSION}
|
||||||
Python-dev: ${have_python_dev}
|
pyexecdir: ${pyexecdir}
|
||||||
PYTHON_CPPFLAGS:${PYTHON_CPPFLAGS}
|
Python-dev: ${have_python_dev}
|
||||||
PYTHON_LDFLAGS: ${PYTHON_LDFLAGS}
|
PYTHON_CPPFLAGS:${PYTHON_CPPFLAGS}
|
||||||
Cython: ${CYTHON}
|
PYTHON_LDFLAGS: ${PYTHON_LDFLAGS}
|
||||||
CUnit: ${have_cunit}
|
Cython: ${CYTHON}
|
||||||
OpenSSL: ${have_openssl}
|
Test:
|
||||||
Libxml2: ${have_libxml2}
|
CUnit: ${have_cunit}
|
||||||
Libevent(SSL): ${have_libevent_openssl}
|
Failmalloc: ${enable_failmalloc}
|
||||||
Spdylay: ${have_spdylay}
|
Libs:
|
||||||
Jansson: ${have_jansson}
|
OpenSSL: ${have_openssl}
|
||||||
Jemalloc: ${have_jemalloc}
|
Libxml2: ${have_libxml2}
|
||||||
Boost CPPFLAGS: ${BOOST_CPPFLAGS}
|
Libev: ${have_libev}
|
||||||
Boost LDFLAGS: ${BOOST_LDFLAGS}
|
Libevent(SSL): ${have_libevent_openssl}
|
||||||
Boost::ASIO: ${BOOST_ASIO_LIB}
|
Spdylay: ${have_spdylay}
|
||||||
Boost::System: ${BOOST_SYSTEM_LIB}
|
Jansson: ${have_jansson}
|
||||||
Boost::Thread: ${BOOST_THREAD_LIB}
|
Jemalloc: ${have_jemalloc}
|
||||||
Applications: ${enable_app}
|
Zlib: ${have_zlib}
|
||||||
HPACK tools: ${enable_hpack_tools}
|
Boost CPPFLAGS: ${BOOST_CPPFLAGS}
|
||||||
Libnghttp2_asio:${enable_asio_lib}
|
Boost LDFLAGS: ${BOOST_LDFLAGS}
|
||||||
Examples: ${enable_examples}
|
Boost::ASIO: ${BOOST_ASIO_LIB}
|
||||||
Python bindings:${enable_python_bindings}
|
Boost::System: ${BOOST_SYSTEM_LIB}
|
||||||
Failmalloc: ${request_failmalloc}
|
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}
|
||||||
|
Threading: ${enable_threads}
|
||||||
|
Third-party: ${enable_third_party}
|
||||||
])
|
])
|
||||||
|
|||||||
3
contrib/.gitignore
vendored
Normal file
3
contrib/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
nghttpx-init
|
||||||
|
nghttpx.service
|
||||||
|
nghttpx-upstart.conf
|
||||||
@@ -21,19 +21,24 @@
|
|||||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
EXTRA_DIST = nghttpx-init.in nghttpx-logrotate
|
configfiles = nghttpx-init nghttpx.service nghttpx-upstart.conf
|
||||||
|
|
||||||
|
EXTRA_DIST = $(configfiles:%=%.in) nghttpx-logrotate
|
||||||
|
|
||||||
edit = sed -e 's|@bindir[@]|$(bindir)|g'
|
edit = sed -e 's|@bindir[@]|$(bindir)|g'
|
||||||
|
|
||||||
nghttpx-init: Makefile
|
nghttpx-init: %: $(srcdir)/%.in
|
||||||
rm -f $@ $@.tmp
|
rm -f $@ $@.tmp
|
||||||
$(edit) $(srcdir)/$@.in > $@.tmp
|
$(edit) $< > $@.tmp
|
||||||
chmod +x $@.tmp
|
chmod +x $@.tmp
|
||||||
mv $@.tmp $@
|
mv $@.tmp $@
|
||||||
|
|
||||||
nghttpx-init: $(srcdir)/nghttpx-init.in
|
nghttpx.service nghttpx-upstart.conf: %: $(srcdir)/%.in
|
||||||
|
$(edit) $< > $@
|
||||||
|
|
||||||
all-local: nghttpx-init
|
$(configfiles): Makefile
|
||||||
|
|
||||||
|
all-local: $(configfiles)
|
||||||
|
|
||||||
clean-local:
|
clean-local:
|
||||||
-rm -f nghttpx-init nghttpx-init.tmp
|
-rm -f nghttpx-init.tmp $(configfiles)
|
||||||
|
|||||||
@@ -1,18 +1,11 @@
|
|||||||
/var/log/nghttpx/*.log {
|
/var/log/nghttpx/*.log {
|
||||||
weekly
|
weekly
|
||||||
missingok
|
rotate 52
|
||||||
rotate 52
|
missingok
|
||||||
compress
|
compress
|
||||||
delaycompress
|
delaycompress
|
||||||
notifempty
|
notifempty
|
||||||
create 0640 www-data adm
|
postrotate
|
||||||
sharedscripts
|
killall -USR1 nghttpx 2> /dev/null || true
|
||||||
prerotate
|
endscript
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|||||||
8
contrib/nghttpx-upstart.conf.in
Normal file
8
contrib/nghttpx-upstart.conf.in
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# vim: ft=upstart:
|
||||||
|
|
||||||
|
description "HTTP/2 reverse proxy"
|
||||||
|
|
||||||
|
start on runlevel [2]
|
||||||
|
stop on runlevel [016]
|
||||||
|
|
||||||
|
exec @bindir@/nghttpx
|
||||||
10
contrib/nghttpx.service.in
Normal file
10
contrib/nghttpx.service.in
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=HTTP/2 experimental proxy
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
ExecStart=@bindir@/nghttpx --errorlog-syslog
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
23
doc/.gitignore
vendored
23
doc/.gitignore
vendored
@@ -1,3 +1,24 @@
|
|||||||
|
# generated files
|
||||||
apiref.rst
|
apiref.rst
|
||||||
|
asio_http2.h.rst
|
||||||
|
asio_http2_client.h.rst
|
||||||
|
asio_http2_server.h.rst
|
||||||
|
building-android-binary.rst
|
||||||
conf.py
|
conf.py
|
||||||
manual
|
contribute.rst
|
||||||
|
enums.rst
|
||||||
|
h2load-howto.rst
|
||||||
|
index.rst
|
||||||
|
libnghttp2_asio.rst
|
||||||
|
macros.rst
|
||||||
|
manual/
|
||||||
|
nghttp2.h.rst
|
||||||
|
nghttp2_*.rst
|
||||||
|
nghttp2ver.h.rst
|
||||||
|
nghttpx-howto.rst
|
||||||
|
package_README.rst
|
||||||
|
python-apiref.rst
|
||||||
|
tutorial-client.rst
|
||||||
|
tutorial-hpack.rst
|
||||||
|
tutorial-server.rst
|
||||||
|
types.rst
|
||||||
|
|||||||
163
doc/Makefile.am
163
doc/Makefile.am
@@ -23,10 +23,111 @@
|
|||||||
|
|
||||||
man_MANS = nghttp.1 nghttpd.1 nghttpx.1 h2load.1
|
man_MANS = nghttp.1 nghttpd.1 nghttpx.1 h2load.1
|
||||||
|
|
||||||
|
APIDOCS= \
|
||||||
|
apiref.rst \
|
||||||
|
macros.rst \
|
||||||
|
enums.rst \
|
||||||
|
types.rst \
|
||||||
|
nghttp2_check_header_name.rst \
|
||||||
|
nghttp2_check_header_value.rst \
|
||||||
|
nghttp2_hd_deflate_bound.rst \
|
||||||
|
nghttp2_hd_deflate_change_table_size.rst \
|
||||||
|
nghttp2_hd_deflate_del.rst \
|
||||||
|
nghttp2_hd_deflate_hd.rst \
|
||||||
|
nghttp2_hd_deflate_new.rst \
|
||||||
|
nghttp2_hd_deflate_new2.rst \
|
||||||
|
nghttp2_hd_inflate_change_table_size.rst \
|
||||||
|
nghttp2_hd_inflate_del.rst \
|
||||||
|
nghttp2_hd_inflate_end_headers.rst \
|
||||||
|
nghttp2_hd_inflate_hd.rst \
|
||||||
|
nghttp2_hd_inflate_new.rst \
|
||||||
|
nghttp2_hd_inflate_new2.rst \
|
||||||
|
nghttp2_is_fatal.rst \
|
||||||
|
nghttp2_nv_compare_name.rst \
|
||||||
|
nghttp2_option_del.rst \
|
||||||
|
nghttp2_option_new.rst \
|
||||||
|
nghttp2_option_set_no_auto_window_update.rst \
|
||||||
|
nghttp2_option_set_no_http_messaging.rst \
|
||||||
|
nghttp2_option_set_peer_max_concurrent_streams.rst \
|
||||||
|
nghttp2_option_set_no_recv_client_magic.rst \
|
||||||
|
nghttp2_pack_settings_payload.rst \
|
||||||
|
nghttp2_priority_spec_check_default.rst \
|
||||||
|
nghttp2_priority_spec_default_init.rst \
|
||||||
|
nghttp2_priority_spec_init.rst \
|
||||||
|
nghttp2_select_next_protocol.rst \
|
||||||
|
nghttp2_session_callbacks_del.rst \
|
||||||
|
nghttp2_session_callbacks_new.rst \
|
||||||
|
nghttp2_session_callbacks_set_before_frame_send_callback.rst \
|
||||||
|
nghttp2_session_callbacks_set_data_source_read_length_callback.rst \
|
||||||
|
nghttp2_session_callbacks_set_on_begin_frame_callback.rst \
|
||||||
|
nghttp2_session_callbacks_set_on_begin_headers_callback.rst \
|
||||||
|
nghttp2_session_callbacks_set_on_data_chunk_recv_callback.rst \
|
||||||
|
nghttp2_session_callbacks_set_on_frame_not_send_callback.rst \
|
||||||
|
nghttp2_session_callbacks_set_on_frame_recv_callback.rst \
|
||||||
|
nghttp2_session_callbacks_set_on_frame_send_callback.rst \
|
||||||
|
nghttp2_session_callbacks_set_on_header_callback.rst \
|
||||||
|
nghttp2_session_callbacks_set_on_invalid_frame_recv_callback.rst \
|
||||||
|
nghttp2_session_callbacks_set_on_stream_close_callback.rst \
|
||||||
|
nghttp2_session_callbacks_set_recv_callback.rst \
|
||||||
|
nghttp2_session_callbacks_set_select_padding_callback.rst \
|
||||||
|
nghttp2_session_callbacks_set_send_callback.rst \
|
||||||
|
nghttp2_session_callbacks_set_send_data_callback.rst \
|
||||||
|
nghttp2_session_client_new.rst \
|
||||||
|
nghttp2_session_client_new2.rst \
|
||||||
|
nghttp2_session_client_new3.rst \
|
||||||
|
nghttp2_session_consume.rst \
|
||||||
|
nghttp2_session_consume_connection.rst \
|
||||||
|
nghttp2_session_consume_stream.rst \
|
||||||
|
nghttp2_session_del.rst \
|
||||||
|
nghttp2_session_get_effective_local_window_size.rst \
|
||||||
|
nghttp2_session_get_effective_recv_data_length.rst \
|
||||||
|
nghttp2_session_get_last_proc_stream_id.rst \
|
||||||
|
nghttp2_session_get_next_stream_id.rst \
|
||||||
|
nghttp2_session_get_outbound_queue_size.rst \
|
||||||
|
nghttp2_session_get_remote_settings.rst \
|
||||||
|
nghttp2_session_get_remote_window_size.rst \
|
||||||
|
nghttp2_session_get_stream_effective_local_window_size.rst \
|
||||||
|
nghttp2_session_get_stream_effective_recv_data_length.rst \
|
||||||
|
nghttp2_session_get_stream_local_close.rst \
|
||||||
|
nghttp2_session_get_stream_remote_close.rst \
|
||||||
|
nghttp2_session_get_stream_remote_window_size.rst \
|
||||||
|
nghttp2_session_get_stream_user_data.rst \
|
||||||
|
nghttp2_session_mem_recv.rst \
|
||||||
|
nghttp2_session_mem_send.rst \
|
||||||
|
nghttp2_session_recv.rst \
|
||||||
|
nghttp2_session_resume_data.rst \
|
||||||
|
nghttp2_session_send.rst \
|
||||||
|
nghttp2_session_server_new.rst \
|
||||||
|
nghttp2_session_server_new2.rst \
|
||||||
|
nghttp2_session_server_new3.rst \
|
||||||
|
nghttp2_session_set_next_stream_id.rst \
|
||||||
|
nghttp2_session_set_stream_user_data.rst \
|
||||||
|
nghttp2_session_terminate_session.rst \
|
||||||
|
nghttp2_session_terminate_session2.rst \
|
||||||
|
nghttp2_session_upgrade.rst \
|
||||||
|
nghttp2_session_want_read.rst \
|
||||||
|
nghttp2_session_want_write.rst \
|
||||||
|
nghttp2_strerror.rst \
|
||||||
|
nghttp2_submit_data.rst \
|
||||||
|
nghttp2_submit_goaway.rst \
|
||||||
|
nghttp2_submit_headers.rst \
|
||||||
|
nghttp2_submit_ping.rst \
|
||||||
|
nghttp2_submit_priority.rst \
|
||||||
|
nghttp2_submit_push_promise.rst \
|
||||||
|
nghttp2_submit_request.rst \
|
||||||
|
nghttp2_submit_response.rst \
|
||||||
|
nghttp2_submit_rst_stream.rst \
|
||||||
|
nghttp2_submit_settings.rst \
|
||||||
|
nghttp2_submit_shutdown_notice.rst \
|
||||||
|
nghttp2_submit_trailer.rst \
|
||||||
|
nghttp2_submit_window_update.rst \
|
||||||
|
nghttp2_version.rst
|
||||||
|
|
||||||
EXTRA_DIST = \
|
EXTRA_DIST = \
|
||||||
mkapiref.py \
|
mkapiref.py \
|
||||||
README.rst \
|
README.rst \
|
||||||
apiref-header.rst \
|
programmers-guide.rst \
|
||||||
|
$(APIDOCS) \
|
||||||
nghttp.1.rst \
|
nghttp.1.rst \
|
||||||
nghttpd.1.rst \
|
nghttpd.1.rst \
|
||||||
nghttpx.1.rst \
|
nghttpx.1.rst \
|
||||||
@@ -40,24 +141,29 @@ EXTRA_DIST = \
|
|||||||
sources/libnghttp2_asio.rst \
|
sources/libnghttp2_asio.rst \
|
||||||
sources/python-apiref.rst \
|
sources/python-apiref.rst \
|
||||||
sources/building-android-binary.rst \
|
sources/building-android-binary.rst \
|
||||||
_themes/sphinx_rtd_theme/footer.html \
|
sources/contribute.rst \
|
||||||
_themes/sphinx_rtd_theme/theme.conf \
|
|
||||||
_themes/sphinx_rtd_theme/layout_old.html \
|
|
||||||
_themes/sphinx_rtd_theme/__init__.py \
|
_themes/sphinx_rtd_theme/__init__.py \
|
||||||
_themes/sphinx_rtd_theme/layout.html \
|
|
||||||
_themes/sphinx_rtd_theme/search.html \
|
|
||||||
_themes/sphinx_rtd_theme/breadcrumbs.html \
|
_themes/sphinx_rtd_theme/breadcrumbs.html \
|
||||||
_themes/sphinx_rtd_theme/versions.html \
|
_themes/sphinx_rtd_theme/footer.html \
|
||||||
|
_themes/sphinx_rtd_theme/layout.html \
|
||||||
|
_themes/sphinx_rtd_theme/layout_old.html \
|
||||||
|
_themes/sphinx_rtd_theme/search.html \
|
||||||
_themes/sphinx_rtd_theme/searchbox.html \
|
_themes/sphinx_rtd_theme/searchbox.html \
|
||||||
_themes/sphinx_rtd_theme/static/fonts/FontAwesome.otf \
|
|
||||||
_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg \
|
|
||||||
_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.woff \
|
|
||||||
_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.eot \
|
|
||||||
_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.ttf \
|
|
||||||
_themes/sphinx_rtd_theme/static/js/theme.js \
|
|
||||||
_themes/sphinx_rtd_theme/static/css/theme.css \
|
|
||||||
_themes/sphinx_rtd_theme/static/css/badge_only.css \
|
_themes/sphinx_rtd_theme/static/css/badge_only.css \
|
||||||
$(man_MANS)
|
_themes/sphinx_rtd_theme/static/css/theme.css \
|
||||||
|
_themes/sphinx_rtd_theme/static/fonts/FontAwesome.otf \
|
||||||
|
_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.eot \
|
||||||
|
_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg \
|
||||||
|
_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.ttf \
|
||||||
|
_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.woff \
|
||||||
|
_themes/sphinx_rtd_theme/static/js/theme.js \
|
||||||
|
_themes/sphinx_rtd_theme/theme.conf \
|
||||||
|
_themes/sphinx_rtd_theme/versions.html \
|
||||||
|
$(man_MANS) \
|
||||||
|
bash_completion/nghttp \
|
||||||
|
bash_completion/nghttpd \
|
||||||
|
bash_completion/nghttpx \
|
||||||
|
bash_completion/h2load
|
||||||
|
|
||||||
# Makefile for Sphinx documentation
|
# Makefile for Sphinx documentation
|
||||||
#
|
#
|
||||||
@@ -94,16 +200,33 @@ help:
|
|||||||
@echo " linkcheck to check all external links for integrity"
|
@echo " linkcheck to check all external links for integrity"
|
||||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||||
|
|
||||||
apiref.rst: $(top_builddir)/lib/includes/nghttp2/nghttp2ver.h \
|
apiref.rst macros.rst enums.rst types.rst: \
|
||||||
|
$(top_builddir)/lib/includes/nghttp2/nghttp2ver.h \
|
||||||
$(top_builddir)/lib/includes/nghttp2/nghttp2.h
|
$(top_builddir)/lib/includes/nghttp2/nghttp2.h
|
||||||
$(PYTHON) $(top_srcdir)/doc/mkapiref.py \
|
$(PYTHON) $(top_srcdir)/doc/mkapiref.py \
|
||||||
--header $(top_srcdir)/doc/apiref-header.rst $^ > $@
|
$@ macros.rst enums.rst types.rst . $^
|
||||||
|
|
||||||
|
# Inspired by
|
||||||
|
# http://www.gnu.org/savannah-checkouts/gnu/automake/manual/html_node/Multiple-Outputs.html
|
||||||
|
apidoc.stamp: $(top_builddir)/lib/includes/nghttp2/nghttp2ver.h \
|
||||||
|
$(top_builddir)/lib/includes/nghttp2/nghttp2.h
|
||||||
|
@rm -f apidoc.tmp
|
||||||
|
@touch apidoc.tmp
|
||||||
|
$(PYTHON) $(top_srcdir)/doc/mkapiref.py \
|
||||||
|
$@ macros.rst enums.rst types.rst . $^
|
||||||
|
@mv -f apidoc.tmp $@
|
||||||
|
$(APIDOC): apidoc.stamp
|
||||||
|
## Recover from the removal of $@
|
||||||
|
@if test -f $@; then :; else \
|
||||||
|
rm -f apidoc.stamp; \
|
||||||
|
$(MAKE) $(AM_MAKEFLAGS) apidoc.stamp; \
|
||||||
|
fi
|
||||||
|
|
||||||
clean-local:
|
clean-local:
|
||||||
-rm apiref.rst
|
-rm $(APIDOCS)
|
||||||
-rm -rf $(BUILDDIR)/*
|
-rm -rf $(BUILDDIR)/*
|
||||||
|
|
||||||
html: apiref.rst
|
html-local: apiref.rst
|
||||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||||
@echo
|
@echo
|
||||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||||
@@ -175,7 +298,7 @@ text:
|
|||||||
@echo
|
@echo
|
||||||
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
||||||
|
|
||||||
man:
|
man: apiref.rst
|
||||||
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
||||||
@echo
|
@echo
|
||||||
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
||||||
|
|||||||
2
doc/_themes/sphinx_rtd_theme/__init__.py
vendored
2
doc/_themes/sphinx_rtd_theme/__init__.py
vendored
@@ -5,7 +5,7 @@ From https://github.com/ryan-roemer/sphinx-bootstrap-theme.
|
|||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
|
|
||||||
VERSION = (0, 1, 5)
|
VERSION = (0, 1, 8)
|
||||||
|
|
||||||
__version__ = ".".join(str(v) for v in VERSION)
|
__version__ = ".".join(str(v) for v in VERSION)
|
||||||
__version_full__ = __version__
|
__version_full__ = __version__
|
||||||
|
|||||||
16
doc/_themes/sphinx_rtd_theme/breadcrumbs.html
vendored
16
doc/_themes/sphinx_rtd_theme/breadcrumbs.html
vendored
@@ -6,12 +6,16 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
<li>{{ title }}</li>
|
<li>{{ title }}</li>
|
||||||
<li class="wy-breadcrumbs-aside">
|
<li class="wy-breadcrumbs-aside">
|
||||||
{% if display_github %}
|
{% if pagename != "search" %}
|
||||||
<a href="https://github.com/{{ github_user }}/{{ github_repo }}/blob/{{ github_version }}{{ conf_py_path }}{{ pagename }}{{ source_suffix }}" class="fa fa-github"> Edit on GitHub</a>
|
{% if display_github %}
|
||||||
{% elif display_bitbucket %}
|
<a href="https://{{ github_host|default("github.com") }}/{{ github_user }}/{{ github_repo }}/blob/{{ github_version }}{{ conf_py_path }}{{ pagename }}{{ source_suffix }}" class="fa fa-github"> Edit on GitHub</a>
|
||||||
<a href="https://bitbucket.org/{{ bitbucket_user }}/{{ bitbucket_repo }}/src/{{ bitbucket_version}}{{ conf_py_path }}{{ pagename }}{{ source_suffix }}" class="fa fa-bitbucket"> Edit on Bitbucket</a>
|
{% elif display_bitbucket %}
|
||||||
{% elif show_source and has_source and sourcename %}
|
<a href="https://bitbucket.org/{{ bitbucket_user }}/{{ bitbucket_repo }}/src/{{ bitbucket_version}}{{ conf_py_path }}{{ pagename }}{{ source_suffix }}" class="fa fa-bitbucket"> Edit on Bitbucket</a>
|
||||||
<a href="{{ pathto('_sources/' + sourcename, true)|e }}" rel="nofollow"> View page source</a>
|
{% elif show_source and source_url_prefix %}
|
||||||
|
<a href="{{ source_url_prefix }}{{ pagename }}{{ source_suffix }}">View page source</a>
|
||||||
|
{% elif show_source and has_source and sourcename %}
|
||||||
|
<a href="{{ pathto('_sources/' + sourcename, true)|e }}" rel="nofollow"> View page source</a>
|
||||||
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
10
doc/_themes/sphinx_rtd_theme/footer.html
vendored
10
doc/_themes/sphinx_rtd_theme/footer.html
vendored
@@ -2,10 +2,10 @@
|
|||||||
{% if next or prev %}
|
{% if next or prev %}
|
||||||
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
|
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
|
||||||
{% if next %}
|
{% if next %}
|
||||||
<a href="{{ next.link|e }}" class="btn btn-neutral float-right" title="{{ next.title|striptags|e }}">Next <span class="fa fa-arrow-circle-right"></span></a>
|
<a href="{{ next.link|e }}" class="btn btn-neutral float-right" title="{{ next.title|striptags|e }}" accesskey="n">Next <span class="fa fa-arrow-circle-right"></span></a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if prev %}
|
{% if prev %}
|
||||||
<a href="{{ prev.link|e }}" class="btn btn-neutral" title="{{ prev.title|striptags|e }}"><span class="fa fa-arrow-circle-left"></span> Previous</a>
|
<a href="{{ prev.link|e }}" class="btn btn-neutral" title="{{ prev.title|striptags|e }}" accesskey="p"><span class="fa fa-arrow-circle-left"></span> Previous</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -28,5 +28,9 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% trans %}<a href="https://github.com/snide/sphinx_rtd_theme">Sphinx theme</a> provided by <a href="https://readthedocs.org">Read the Docs</a>{% endtrans %}
|
{%- if show_sphinx %}
|
||||||
|
{% trans %}Built with <a href="http://sphinx-doc.org/">Sphinx</a> using a <a href="https://github.com/snide/sphinx_rtd_theme">theme</a> provided by <a href="https://readthedocs.org">Read the Docs</a>{% endtrans %}.
|
||||||
|
{%- endif %}
|
||||||
|
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
|||||||
41
doc/_themes/sphinx_rtd_theme/layout.html
vendored
41
doc/_themes/sphinx_rtd_theme/layout.html
vendored
@@ -12,6 +12,7 @@
|
|||||||
<!--[if gt IE 8]><!--> <html class="no-js" lang="en" > <!--<![endif]-->
|
<!--[if gt IE 8]><!--> <html class="no-js" lang="en" > <!--<![endif]-->
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
|
{{ metatags }}
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
{% block htmltitle %}
|
{% block htmltitle %}
|
||||||
<title>{{ title|striptags|e }}{{ titlesuffix }}</title>
|
<title>{{ title|striptags|e }}{{ titlesuffix }}</title>
|
||||||
@@ -23,7 +24,6 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{# CSS #}
|
{# CSS #}
|
||||||
<link href='https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic|Roboto+Slab:400,700|Inconsolata:400,700' rel='stylesheet' type='text/css'>
|
|
||||||
|
|
||||||
{# OPENSEARCH #}
|
{# OPENSEARCH #}
|
||||||
{% if not embedded %}
|
{% if not embedded %}
|
||||||
@@ -42,6 +42,10 @@
|
|||||||
<link rel="stylesheet" href="{{ pathto(cssfile, 1) }}" type="text/css" />
|
<link rel="stylesheet" href="{{ pathto(cssfile, 1) }}" type="text/css" />
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
{% for cssfile in extra_css_files %}
|
||||||
|
<link rel="stylesheet" href="{{ pathto(cssfile, 1) }}" type="text/css" />
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
{%- block linktags %}
|
{%- block linktags %}
|
||||||
{%- if hasdoc('about') %}
|
{%- if hasdoc('about') %}
|
||||||
<link rel="author" title="{{ _('About these documents') }}"
|
<link rel="author" title="{{ _('About these documents') }}"
|
||||||
@@ -71,7 +75,7 @@
|
|||||||
{%- block extrahead %} {% endblock %}
|
{%- block extrahead %} {% endblock %}
|
||||||
|
|
||||||
{# Keep modernizr in head - http://modernizr.com/docs/#installing #}
|
{# Keep modernizr in head - http://modernizr.com/docs/#installing #}
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/modernizr/2.6.2/modernizr.min.js"></script>
|
<script src="_static/js/modernizr.min.js"></script>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
@@ -83,19 +87,34 @@
|
|||||||
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
|
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
|
||||||
<div class="wy-side-nav-search">
|
<div class="wy-side-nav-search">
|
||||||
{% block sidebartitle %}
|
{% block sidebartitle %}
|
||||||
<a href="{{ pathto(master_doc) }}" class="fa fa-home"> {{ project }}</a>
|
|
||||||
{% endblock %}
|
{% if logo and theme_logo_only %}
|
||||||
|
<a href="{{ pathto(master_doc) }}">
|
||||||
|
{% else %}
|
||||||
|
<a href="{{ pathto(master_doc) }}" class="icon icon-home"> {{ project }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if logo %}
|
||||||
|
{# Not strictly valid HTML, but it's the only way to display/scale it properly, without weird scripting or heaps of work #}
|
||||||
|
<img src="{{ pathto('_static/' + logo, 1) }}" class="logo" />
|
||||||
|
{% endif %}
|
||||||
|
</a>
|
||||||
|
|
||||||
{% include "searchbox.html" %}
|
{% include "searchbox.html" %}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
|
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
|
||||||
{% set toctree = toctree(maxdepth=2, collapse=False, includehidden=True) %}
|
{% block menu %}
|
||||||
{% if toctree %}
|
{% set toctree = toctree(maxdepth=4, collapse=False, includehidden=True) %}
|
||||||
{{ toctree }}
|
{% if toctree %}
|
||||||
{% else %}
|
{{ toctree }}
|
||||||
<!-- Local TOC -->
|
{% else %}
|
||||||
<div class="local-toc">{{ toc }}</div>
|
<!-- Local TOC -->
|
||||||
{% endif %}
|
<div class="local-toc">{{ toc }}</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
4
doc/_themes/sphinx_rtd_theme/searchbox.html
vendored
4
doc/_themes/sphinx_rtd_theme/searchbox.html
vendored
@@ -1,7 +1,9 @@
|
|||||||
|
{%- if builder != 'singlehtml' %}
|
||||||
<div role="search">
|
<div role="search">
|
||||||
<form id ="rtd-search-form" class="wy-form" action="{{ pathto('search') }}" method="get">
|
<form id="rtd-search-form" class="wy-form" action="{{ pathto('search') }}" method="get">
|
||||||
<input type="text" name="q" placeholder="Search docs" />
|
<input type="text" name="q" placeholder="Search docs" />
|
||||||
<input type="hidden" name="check_keywords" value="yes" />
|
<input type="hidden" name="check_keywords" value="yes" />
|
||||||
<input type="hidden" name="area" value="default" />
|
<input type="hidden" name="area" value="default" />
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
{%- endif %}
|
||||||
|
|||||||
7
doc/_themes/sphinx_rtd_theme/static/css/badge_only.css.map
vendored
Normal file
7
doc/_themes/sphinx_rtd_theme/static/css/badge_only.css.map
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"version": 3,
|
||||||
|
"mappings": "CAyDA,SAAY,EACV,qBAAsB,EAAE,UAAW,EAqDrC,QAAS,EARP,IAAK,EAAE,AAAC,EACR,+BAAS,EAEP,MAAO,EAAE,IAAK,EACd,MAAO,EAAE,CAAE,EACb,cAAO,EACL,IAAK,EAAE,GAAI,EC1Gb,SAkBC,EAjBC,UAAW,ECFJ,UAAW,EDGlB,UAAW,EAHqC,KAAM,EAItD,SAAU,EAJsD,KAAM,EAapE,EAAG,EAAE,qCAAwB,EAC7B,EAAG,EAAE,0PAAyE,ECZpF,SAAU,EACR,MAAO,EAAE,WAAY,EACrB,UAAW,EAAE,UAAW,EACxB,SAAU,EAAE,KAAM,EAClB,UAAW,EAAE,KAAM,EACnB,UAAW,EAAE,AAAC,EACd,cAAe,EAAE,MAAO,EAG1B,IAAK,EACH,MAAO,EAAE,WAAY,EACrB,cAAe,EAAE,MAAO,EAIxB,KAAG,EACD,MAAO,EAAE,WAAY,EACvB,sCAAiB,EAGf,IAAK,EAAE,MAAY,EAEvB,KAAM,EACJ,cAAe,EAAE,GAAI,EACrB,UAAW,EAAE,EAAG,EAChB,UAAW,EAAE,KAAM,EAEjB,YAAG,EACD,IAAK,EAAE,IAAI,EACb,oDAAiB,EAGf,aAAc,EAAE,OAAQ,EAG9B,cAAe,EACb,MAAO,EAAE,EAAO,EAElB,gBAAiB,EACf,MAAO,EAAE,EAAO,EAElB,oBAAqB,EACnB,MAAO,EAAE,EAAO,EAElB,sBAAuB,EACrB,MAAO,EAAE,EAAO,EAElB,kBAAmB,EACjB,MAAO,EAAE,EAAO,EAElB,oBAAqB,EACnB,MAAO,EAAE,EAAO,EAElB,oBAAqB,EACnB,MAAO,EAAE,EAAO,EAElB,sBAAuB,EACrB,MAAO,EAAE,EAAO,EAElB,qBAAsB,EACpB,MAAO,EAAE,EAAO,EAElB,uBAAwB,EACtB,MAAO,EAAE,EAAO,ECnElB,YAAa,EACX,OAAQ,EAAE,IAAK,EACf,KAAM,EAAE,AAAC,EACT,GAAI,EAAE,AAAC,EACP,IAAK,EC6E+B,IAAK,ED5EzC,IAAK,ECE+B,MAAyB,EDD7D,SAAU,EAAE,MAAkC,EAC9C,SAAU,EAAE,iBAAiC,EAC7C,UAAW,EEAyB,sDAAM,EFC1C,MAAO,EC+E6B,EAAG,ED9EvC,cAAC,EACC,IAAK,ECqE6B,MAAW,EDpE7C,cAAe,EAAE,GAAI,EACvB,6BAAgB,EACd,MAAO,EAAE,GAAI,EACf,iCAAoB,EAClB,MAAO,EAAE,GAAqB,EAC9B,eAAgB,EAAE,MAAkC,EACpD,MAAO,EAAE,IAAK,EACd,SAAU,EAAE,IAAK,EACjB,QAAS,EAAE,EAAG,EACd,KAAM,EAAE,MAAO,EACf,IAAK,ECiD6B,MAAM,EJgC1C,IAAK,EAAE,AAAC,EACR,iFAAS,EAEP,MAAO,EAAE,IAAK,EACd,MAAO,EAAE,CAAE,EACb,uCAAO,EACL,IAAK,EAAE,GAAI,EGrFX,qCAAG,EACD,IAAK,EClB2B,MAAyB,EDmB3D,0CAAQ,EACN,IAAK,EAAE,GAAI,EACb,4CAAU,EACR,IAAK,EAAE,GAAI,EACb,iDAAiB,EACf,eAAgB,ECQgB,MAAI,EDPpC,IAAK,EC0B2B,GAAM,EDzBxC,wDAAwB,EACtB,eAAgB,ECXgB,MAAO,EDYvC,IAAK,ECzB2B,GAAI,ED0BxC,yCAA8B,EAC5B,MAAO,EAAE,IAAK,EAChB,gCAAmB,EACjB,QAAS,EAAE,EAAG,EACd,MAAO,EAAE,GAAqB,EAC9B,IAAK,ECE6B,GAAwB,EDD1D,MAAO,EAAE,GAAI,EACb,mCAAE,EACA,MAAO,EAAE,IAAK,EACd,KAAM,EAAE,EAAG,EACX,KAAM,EAAE,AAAC,EACT,KAAM,EAAE,KAAM,EACd,MAAO,EAAE,AAAC,EACV,SAAU,EAAE,gBAA6C,EAC3D,mCAAE,EACA,MAAO,EAAE,WAAY,EACrB,KAAM,EAAE,AAAC,EACT,qCAAC,EACC,MAAO,EAAE,WAAY,EACrB,MAAO,EAAE,EAAqB,EAC9B,IAAK,ECjDyB,MAAyB,EDkD7D,sBAAW,EACT,IAAK,EAAE,GAAI,EACX,KAAM,EAAE,GAAI,EACZ,IAAK,EAAE,GAAI,EACX,GAAI,EAAE,GAAI,EACV,KAAM,EAAE,GAAI,EACZ,QAAS,ECkByB,IAAK,EDjBvC,iCAAU,EACR,IAAK,EAAE,GAAI,EACb,+BAAQ,EACN,IAAK,EAAE,GAAI,EACb,oDAA+B,EAC7B,SAAU,EAAE,IAAK,EACjB,6DAAQ,EACN,IAAK,EAAE,GAAI,EACb,+DAAU,EACR,IAAK,EAAE,GAAI,EACf,2CAAoB,EAClB,IAAK,EAAE,GAAI,EACX,KAAM,EAAE,GAAI,EACZ,UAAW,EAAE,GAAI,EACjB,MAAO,EAAE,IAAuB,EAChC,MAAO,EAAE,IAAK,EACd,SAAU,EAAE,KAAM,EGhDpB,mCAAsB,EHmDxB,YAAa,EACX,IAAK,EAAE,EAAG,EACV,MAAO,EAAE,GAAI,EACb,kBAAO,EACL,MAAO,EAAE,IAAK,EAClB,EAAG,EACD,IAAK,EAAE,GAAI,EACX,KAAM,EAAE,GAAI",
|
||||||
|
"sources": ["../../../bower_components/wyrm/sass/wyrm_core/_mixin.sass","../../../bower_components/bourbon/dist/css3/_font-face.scss","../../../sass/_theme_badge_fa.sass","../../../sass/_theme_badge.sass","../../../bower_components/wyrm/sass/wyrm_core/_wy_variables.sass","../../../sass/_theme_variables.sass","../../../bower_components/neat/app/assets/stylesheets/grid/_media.scss"],
|
||||||
|
"names": [],
|
||||||
|
"file": "badge_only.css"
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
7
doc/_themes/sphinx_rtd_theme/static/css/theme.css.map
vendored
Normal file
7
doc/_themes/sphinx_rtd_theme/static/css/theme.css.map
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
doc/_themes/sphinx_rtd_theme/static/fonts/Inconsolata-Bold.ttf
vendored
Normal file
BIN
doc/_themes/sphinx_rtd_theme/static/fonts/Inconsolata-Bold.ttf
vendored
Normal file
Binary file not shown.
BIN
doc/_themes/sphinx_rtd_theme/static/fonts/Inconsolata.ttf
vendored
Normal file
BIN
doc/_themes/sphinx_rtd_theme/static/fonts/Inconsolata.ttf
vendored
Normal file
Binary file not shown.
BIN
doc/_themes/sphinx_rtd_theme/static/fonts/Lato-Bold.ttf
vendored
Normal file
BIN
doc/_themes/sphinx_rtd_theme/static/fonts/Lato-Bold.ttf
vendored
Normal file
Binary file not shown.
BIN
doc/_themes/sphinx_rtd_theme/static/fonts/Lato-Regular.ttf
vendored
Normal file
BIN
doc/_themes/sphinx_rtd_theme/static/fonts/Lato-Regular.ttf
vendored
Normal file
Binary file not shown.
BIN
doc/_themes/sphinx_rtd_theme/static/fonts/RobotoSlab-Bold.ttf
vendored
Normal file
BIN
doc/_themes/sphinx_rtd_theme/static/fonts/RobotoSlab-Bold.ttf
vendored
Normal file
Binary file not shown.
BIN
doc/_themes/sphinx_rtd_theme/static/fonts/RobotoSlab-Regular.ttf
vendored
Normal file
BIN
doc/_themes/sphinx_rtd_theme/static/fonts/RobotoSlab-Regular.ttf
vendored
Normal file
Binary file not shown.
4
doc/_themes/sphinx_rtd_theme/static/js/modernizr.min.js
vendored
Normal file
4
doc/_themes/sphinx_rtd_theme/static/js/modernizr.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
108
doc/_themes/sphinx_rtd_theme/static/js/theme.js
vendored
108
doc/_themes/sphinx_rtd_theme/static/js/theme.js
vendored
@@ -1,47 +1,113 @@
|
|||||||
$( document ).ready(function() {
|
function toggleCurrent (elem) {
|
||||||
|
var parent_li = elem.closest('li');
|
||||||
|
parent_li.siblings('li.current').removeClass('current');
|
||||||
|
parent_li.siblings().find('li.current').removeClass('current');
|
||||||
|
parent_li.find('> ul li.current').removeClass('current');
|
||||||
|
parent_li.toggleClass('current');
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
// Shift nav in mobile when clicking the menu.
|
// Shift nav in mobile when clicking the menu.
|
||||||
$(document).on('click', "[data-toggle='wy-nav-top']", function() {
|
$(document).on('click', "[data-toggle='wy-nav-top']", function() {
|
||||||
$("[data-toggle='wy-nav-shift']").toggleClass("shift");
|
$("[data-toggle='wy-nav-shift']").toggleClass("shift");
|
||||||
$("[data-toggle='rst-versions']").toggleClass("shift");
|
$("[data-toggle='rst-versions']").toggleClass("shift");
|
||||||
});
|
});
|
||||||
// Close menu when you click a link.
|
// Nav menu link click operations
|
||||||
$(document).on('click', ".wy-menu-vertical .current ul li a", function() {
|
$(document).on('click', ".wy-menu-vertical .current ul li a", function() {
|
||||||
$("[data-toggle='wy-nav-shift']").removeClass("shift");
|
var target = $(this);
|
||||||
$("[data-toggle='rst-versions']").toggleClass("shift");
|
// Close menu when you click a link.
|
||||||
|
$("[data-toggle='wy-nav-shift']").removeClass("shift");
|
||||||
|
$("[data-toggle='rst-versions']").toggleClass("shift");
|
||||||
|
// Handle dynamic display of l3 and l4 nav lists
|
||||||
|
toggleCurrent(target);
|
||||||
|
if (typeof(window.SphinxRtdTheme) != 'undefined') {
|
||||||
|
window.SphinxRtdTheme.StickyNav.hashChange();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
$(document).on('click', "[data-toggle='rst-current-version']", function() {
|
$(document).on('click', "[data-toggle='rst-current-version']", function() {
|
||||||
$("[data-toggle='rst-versions']").toggleClass("shift-up");
|
$("[data-toggle='rst-versions']").toggleClass("shift-up");
|
||||||
});
|
});
|
||||||
// Make tables responsive
|
// Make tables responsive
|
||||||
$("table.docutils:not(.field-list)").wrap("<div class='wy-table-responsive'></div>");
|
$("table.docutils:not(.field-list)").wrap("<div class='wy-table-responsive'></div>");
|
||||||
|
|
||||||
|
// Add expand links to all parents of nested ul
|
||||||
|
$('.wy-menu-vertical ul').siblings('a').each(function () {
|
||||||
|
var link = $(this);
|
||||||
|
expand = $('<span class="toctree-expand"></span>');
|
||||||
|
expand.on('click', function (ev) {
|
||||||
|
toggleCurrent(link);
|
||||||
|
ev.stopPropagation();
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
link.prepend(expand);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Sphinx theme state
|
||||||
window.SphinxRtdTheme = (function (jquery) {
|
window.SphinxRtdTheme = (function (jquery) {
|
||||||
var stickyNav = (function () {
|
var stickyNav = (function () {
|
||||||
var navBar,
|
var navBar,
|
||||||
win,
|
win,
|
||||||
stickyNavCssClass = 'stickynav',
|
winScroll = false,
|
||||||
applyStickNav = function () {
|
linkScroll = false,
|
||||||
if (navBar.height() <= win.height()) {
|
winPosition = 0,
|
||||||
navBar.addClass(stickyNavCssClass);
|
|
||||||
} else {
|
|
||||||
navBar.removeClass(stickyNavCssClass);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
enable = function () {
|
enable = function () {
|
||||||
applyStickNav();
|
init();
|
||||||
win.on('resize', applyStickNav);
|
reset();
|
||||||
|
win.on('hashchange', reset);
|
||||||
|
|
||||||
|
// Set scrolling
|
||||||
|
win.on('scroll', function () {
|
||||||
|
if (!linkScroll) {
|
||||||
|
winScroll = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setInterval(function () {
|
||||||
|
if (winScroll) {
|
||||||
|
winScroll = false;
|
||||||
|
var newWinPosition = win.scrollTop(),
|
||||||
|
navPosition = navBar.scrollTop(),
|
||||||
|
newNavPosition = navPosition + (newWinPosition - winPosition);
|
||||||
|
navBar.scrollTop(newNavPosition);
|
||||||
|
winPosition = newWinPosition;
|
||||||
|
}
|
||||||
|
}, 25);
|
||||||
},
|
},
|
||||||
init = function () {
|
init = function () {
|
||||||
navBar = jquery('nav.wy-nav-side:first');
|
navBar = jquery('nav.wy-nav-side:first');
|
||||||
win = jquery(window);
|
win = jquery(window);
|
||||||
|
},
|
||||||
|
reset = function () {
|
||||||
|
// Get anchor from URL and open up nested nav
|
||||||
|
var anchor = encodeURI(window.location.hash);
|
||||||
|
if (anchor) {
|
||||||
|
try {
|
||||||
|
var link = $('.wy-menu-vertical')
|
||||||
|
.find('[href="' + anchor + '"]');
|
||||||
|
$('.wy-menu-vertical li.toctree-l1 li.current')
|
||||||
|
.removeClass('current');
|
||||||
|
link.closest('li.toctree-l2').addClass('current');
|
||||||
|
link.closest('li.toctree-l3').addClass('current');
|
||||||
|
link.closest('li.toctree-l4').addClass('current');
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
console.log("Error expanding nav for anchor", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hashChange = function () {
|
||||||
|
linkScroll = true;
|
||||||
|
win.one('hashchange', function () {
|
||||||
|
linkScroll = false;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
jquery(init);
|
jquery(init);
|
||||||
return {
|
return {
|
||||||
enable : enable
|
enable: enable,
|
||||||
|
hashChange: hashChange
|
||||||
};
|
};
|
||||||
}());
|
}());
|
||||||
return {
|
return {
|
||||||
StickyNav : stickyNav
|
StickyNav: stickyNav
|
||||||
};
|
};
|
||||||
}($));
|
}($));
|
||||||
|
|||||||
1
doc/_themes/sphinx_rtd_theme/theme.conf
vendored
1
doc/_themes/sphinx_rtd_theme/theme.conf
vendored
@@ -6,3 +6,4 @@ stylesheet = css/theme.css
|
|||||||
typekit_id = hiw1hhg
|
typekit_id = hiw1hhg
|
||||||
analytics_id =
|
analytics_id =
|
||||||
sticky_navigation = False
|
sticky_navigation = False
|
||||||
|
logo_only =
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
API Reference
|
|
||||||
=============
|
|
||||||
|
|
||||||
Includes
|
|
||||||
--------
|
|
||||||
|
|
||||||
To use the public APIs, include ``nghttp2/nghttp2.h``::
|
|
||||||
|
|
||||||
#include <nghttp2/nghttp2.h>
|
|
||||||
|
|
||||||
The header files are also available online: :doc:`nghttp2.h` and
|
|
||||||
:doc:`nghttp2ver.h`.
|
|
||||||
|
|
||||||
Remarks
|
|
||||||
-------
|
|
||||||
|
|
||||||
Do not call `nghttp2_session_send()`, `nghttp2_session_mem_send()`,
|
|
||||||
`nghttp2_session_recv()` or `nghttp2_session_mem_recv()` from the
|
|
||||||
nghttp2 callback functions directly or indirectly. It will lead to the
|
|
||||||
crash. You can submit requests or frames in the callbacks then call
|
|
||||||
these functions outside the callbacks.
|
|
||||||
|
|
||||||
Currently, `nghttp2_session_send()` and `nghttp2_session_mem_send()`
|
|
||||||
do not send client connection preface
|
|
||||||
(:macro:`NGHTTP2_CLIENT_CONNECTION_PREFACE`). The applications are
|
|
||||||
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 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.
|
|
||||||
@@ -2,3 +2,4 @@ asio_http2.h
|
|||||||
============
|
============
|
||||||
|
|
||||||
.. literalinclude:: @top_srcdir@/src/includes/nghttp2/asio_http2.h
|
.. literalinclude:: @top_srcdir@/src/includes/nghttp2/asio_http2.h
|
||||||
|
:language: cpp
|
||||||
|
|||||||
5
doc/asio_http2_client.h.rst.in
Normal file
5
doc/asio_http2_client.h.rst.in
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
asio_http2_client.h
|
||||||
|
===================
|
||||||
|
|
||||||
|
.. literalinclude:: @top_srcdir@/src/includes/nghttp2/asio_http2_client.h
|
||||||
|
:language: cpp
|
||||||
5
doc/asio_http2_server.h.rst.in
Normal file
5
doc/asio_http2_server.h.rst.in
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
asio_http2_server.h
|
||||||
|
===================
|
||||||
|
|
||||||
|
.. literalinclude:: @top_srcdir@/src/includes/nghttp2/asio_http2_server.h
|
||||||
|
:language: cpp
|
||||||
19
doc/bash_completion/h2load
Normal file
19
doc/bash_completion/h2load
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
_h2load()
|
||||||
|
{
|
||||||
|
local cur prev split=false
|
||||||
|
COMPREPLY=()
|
||||||
|
COMP_WORDBREAKS=${COMP_WORDBREAKS//=}
|
||||||
|
|
||||||
|
cmd=${COMP_WORDS[0]}
|
||||||
|
_get_comp_words_by_ref cur prev
|
||||||
|
case $cur in
|
||||||
|
-*)
|
||||||
|
COMPREPLY=( $( compgen -W '--threads --connection-window-bits --input-file --help --requests --data --verbose --version --window-bits --clients --no-tls-proto --header --max-concurrent-streams ' -- "$cur" ) )
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
_filedir
|
||||||
|
return 0
|
||||||
|
esac
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
complete -F _h2load h2load
|
||||||
77
doc/bash_completion/make_bash_completion.py
Executable file
77
doc/bash_completion/make_bash_completion.py
Executable file
@@ -0,0 +1,77 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
from __future__ import print_function
|
||||||
|
import subprocess
|
||||||
|
import io
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import os.path
|
||||||
|
|
||||||
|
class Option:
|
||||||
|
def __init__(self, long_opt, short_opt):
|
||||||
|
self.long_opt = long_opt
|
||||||
|
self.short_opt = short_opt
|
||||||
|
|
||||||
|
def get_all_options(cmd):
|
||||||
|
opt_pattern = re.compile(r' (?:(-.), )?(--[^\s\[=]+)(\[)?')
|
||||||
|
proc = subprocess.Popen([cmd, "--help"], stdout=subprocess.PIPE)
|
||||||
|
stdoutdata, _ = proc.communicate()
|
||||||
|
cur_option = None
|
||||||
|
opts = {}
|
||||||
|
for line in io.StringIO(stdoutdata.decode('utf-8')):
|
||||||
|
match = opt_pattern.match(line)
|
||||||
|
if not match:
|
||||||
|
continue
|
||||||
|
long_opt = match.group(2)
|
||||||
|
short_opt = match.group(1)
|
||||||
|
opts[long_opt] = Option(long_opt, short_opt)
|
||||||
|
|
||||||
|
return opts
|
||||||
|
|
||||||
|
def output_case(out, name, opts):
|
||||||
|
out.write('''\
|
||||||
|
_{name}()
|
||||||
|
{{
|
||||||
|
local cur prev split=false
|
||||||
|
COMPREPLY=()
|
||||||
|
COMP_WORDBREAKS=${{COMP_WORDBREAKS//=}}
|
||||||
|
|
||||||
|
cmd=${{COMP_WORDS[0]}}
|
||||||
|
_get_comp_words_by_ref cur prev
|
||||||
|
'''.format(name=name))
|
||||||
|
|
||||||
|
# Complete option name.
|
||||||
|
out.write('''\
|
||||||
|
case $cur in
|
||||||
|
-*)
|
||||||
|
COMPREPLY=( $( compgen -W '\
|
||||||
|
''')
|
||||||
|
for opt in opts.values():
|
||||||
|
out.write(opt.long_opt)
|
||||||
|
out.write(' ')
|
||||||
|
|
||||||
|
out.write('''\
|
||||||
|
' -- "$cur" ) )
|
||||||
|
;;
|
||||||
|
''')
|
||||||
|
# If no option found for completion then complete with files.
|
||||||
|
out.write('''\
|
||||||
|
*)
|
||||||
|
_filedir
|
||||||
|
return 0
|
||||||
|
esac
|
||||||
|
return 0
|
||||||
|
}}
|
||||||
|
complete -F _{name} {name}
|
||||||
|
'''.format(name=name))
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
print("Generates bash_completion using `/path/to/cmd --help'")
|
||||||
|
print("Usage: make_bash_completion.py /path/to/cmd")
|
||||||
|
exit(1)
|
||||||
|
name = os.path.basename(sys.argv[1])
|
||||||
|
opts = get_all_options(sys.argv[1])
|
||||||
|
output_case(sys.stdout, name, opts)
|
||||||
19
doc/bash_completion/nghttp
Normal file
19
doc/bash_completion/nghttp
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
_nghttp()
|
||||||
|
{
|
||||||
|
local cur prev split=false
|
||||||
|
COMPREPLY=()
|
||||||
|
COMP_WORDBREAKS=${COMP_WORDBREAKS//=}
|
||||||
|
|
||||||
|
cmd=${COMP_WORDS[0]}
|
||||||
|
_get_comp_words_by_ref cur prev
|
||||||
|
case $cur in
|
||||||
|
-*)
|
||||||
|
COMPREPLY=( $( compgen -W '--no-push --verbose --no-dep --get-assets --har --header-table-size --multiply --padding --hexdump --continuation --connection-window-bits --peer-max-concurrent-streams --timeout --data --no-content-length --version --color --cert --upgrade --remote-name --trailer --weight --help --key --null-out --window-bits --stat --header ' -- "$cur" ) )
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
_filedir
|
||||||
|
return 0
|
||||||
|
esac
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
complete -F _nghttp nghttp
|
||||||
19
doc/bash_completion/nghttpd
Normal file
19
doc/bash_completion/nghttpd
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
_nghttpd()
|
||||||
|
{
|
||||||
|
local cur prev split=false
|
||||||
|
COMPREPLY=()
|
||||||
|
COMP_WORDBREAKS=${COMP_WORDBREAKS//=}
|
||||||
|
|
||||||
|
cmd=${COMP_WORDS[0]}
|
||||||
|
_get_comp_words_by_ref cur prev
|
||||||
|
case $cur in
|
||||||
|
-*)
|
||||||
|
COMPREPLY=( $( compgen -W '--error-gzip --push --header-table-size --trailer --htdocs --address --padding --verbose --version --help --hexdump --dh-param-file --daemon --verify-client --echo-upload --workers --no-tls --color --early-response --max-concurrent-streams ' -- "$cur" ) )
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
_filedir
|
||||||
|
return 0
|
||||||
|
esac
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
complete -F _nghttpd nghttpd
|
||||||
19
doc/bash_completion/nghttpx
Normal file
19
doc/bash_completion/nghttpx
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
_nghttpx()
|
||||||
|
{
|
||||||
|
local cur prev split=false
|
||||||
|
COMPREPLY=()
|
||||||
|
COMP_WORDBREAKS=${COMP_WORDBREAKS//=}
|
||||||
|
|
||||||
|
cmd=${COMP_WORDS[0]}
|
||||||
|
_get_comp_words_by_ref cur prev
|
||||||
|
case $cur in
|
||||||
|
-*)
|
||||||
|
COMPREPLY=( $( compgen -W '--worker-read-rate --frontend-no-tls --frontend-http2-dump-response-header --backend-http1-connections-per-frontend --tls-ticket-key-file --verify-client-cacert --backend-request-buffer --backend-http2-connection-window-bits --conf --worker-write-burst --npn-list --fetch-ocsp-response-file --stream-read-timeout --accesslog-syslog --frontend-http2-read-timeout --listener-disable-timeout --frontend-http2-connection-window-bits --ciphers --strip-incoming-x-forwarded-for --daemon --backend-keep-alive-timeout --backend-http-proxy-uri --backend-http1-connections-per-host --rlimit-nofile --no-via --ocsp-update-interval --backend-write-timeout --client --http2-no-cookie-crumbling --worker-read-burst --client-proxy --http2-bridge --accesslog-format --errorlog-syslog --errorlog-file --http2-max-concurrent-streams --frontend-write-timeout --read-burst --backend-ipv4 --backend-ipv6 --backend --insecure --log-level --tls-proto-list --backend-http2-connections-per-worker --dh-param-file --worker-frontend-connections --header-field-buffer --no-server-push --no-location-rewrite --no-ocsp --backend-response-buffer --workers --frontend-http2-window-bits --no-host-rewrite --worker-write-rate --add-request-header --backend-tls-sni-field --subcert --help --frontend-frame-debug --pid-file --frontend-http2-dump-request-header --private-key-passwd-file --write-rate --altsvc --user --add-x-forwarded-for --syslog-facility --frontend-read-timeout --backlog --write-burst --backend-http2-window-bits --padding --stream-write-timeout --cacert --version --verify-client --backend-read-timeout --frontend --accesslog-file --http2-proxy --max-header-fields --backend-no-tls --client-private-key-file --client-cert-file --add-response-header --read-rate ' -- "$cur" ) )
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
_filedir
|
||||||
|
return 0
|
||||||
|
esac
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
complete -F _nghttpx nghttpx
|
||||||
@@ -64,7 +64,7 @@ master_doc = 'index'
|
|||||||
|
|
||||||
# General information about the project.
|
# General information about the project.
|
||||||
project = u'nghttp2'
|
project = u'nghttp2'
|
||||||
copyright = u'2012, 2014, Tatsuhiro Tsujikawa'
|
copyright = u'2012, 2015, Tatsuhiro Tsujikawa'
|
||||||
|
|
||||||
# The version info for the project you're documenting, acts as replacement for
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
# |version| and |release|, also used in various other places throughout the
|
# |version| and |release|, also used in various other places throughout the
|
||||||
@@ -242,6 +242,12 @@ latex_documents = [
|
|||||||
# One entry per manual page. List of tuples
|
# One entry per manual page. List of tuples
|
||||||
# (source start file, name, description, authors, manual section).
|
# (source start file, name, description, authors, manual section).
|
||||||
man_pages = [
|
man_pages = [
|
||||||
('index', 'nghttp2', u'nghttp2 Documentation',
|
('nghttp.1', 'nghttp', u'HTTP/2 experimental client',
|
||||||
|
[u'Tatsuhiro Tsujikawa'], 1),
|
||||||
|
('nghttpd.1', 'nghttpd', u'HTTP/2 experimental server',
|
||||||
|
[u'Tatsuhiro Tsujikawa'], 1),
|
||||||
|
('nghttpx.1', 'nghttpx', u'HTTP/2 experimental proxy',
|
||||||
|
[u'Tatsuhiro Tsujikawa'], 1),
|
||||||
|
('h2load.1', 'h2load', u'HTTP/2 benchmarking tool',
|
||||||
[u'Tatsuhiro Tsujikawa'], 1)
|
[u'Tatsuhiro Tsujikawa'], 1)
|
||||||
]
|
]
|
||||||
|
|||||||
1
doc/contribute.rst.in
Normal file
1
doc/contribute.rst.in
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.. include:: @top_srcdir@/doc/sources/contribute.rst
|
||||||
304
doc/h2load.1
304
doc/h2load.1
@@ -1,75 +1,273 @@
|
|||||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.4.
|
.\" Man page generated from reStructuredText.
|
||||||
.TH H2LOAD "1" "October 2014" "h2load nghttp2/0.6.5" "User Commands"
|
.
|
||||||
|
.TH "H2LOAD" "1" "June 23, 2015" "1.0.4" "nghttp2"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
h2load \- HTTP/2 benchmarking tool
|
h2load \- HTTP/2 benchmarking tool
|
||||||
|
.
|
||||||
|
.nr rst2man-indent-level 0
|
||||||
|
.
|
||||||
|
.de1 rstReportMargin
|
||||||
|
\\$1 \\n[an-margin]
|
||||||
|
level \\n[rst2man-indent-level]
|
||||||
|
level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||||
|
-
|
||||||
|
\\n[rst2man-indent0]
|
||||||
|
\\n[rst2man-indent1]
|
||||||
|
\\n[rst2man-indent2]
|
||||||
|
..
|
||||||
|
.de1 INDENT
|
||||||
|
.\" .rstReportMargin pre:
|
||||||
|
. RS \\$1
|
||||||
|
. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
|
||||||
|
. nr rst2man-indent-level +1
|
||||||
|
.\" .rstReportMargin post:
|
||||||
|
..
|
||||||
|
.de UNINDENT
|
||||||
|
. RE
|
||||||
|
.\" indent \\n[an-margin]
|
||||||
|
.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||||
|
.nr rst2man-indent-level -1
|
||||||
|
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||||
|
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||||
|
..
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
.B h2load
|
.sp
|
||||||
[\fI\,OPTIONS\/\fR]... [\fI\,URI\/\fR]...
|
\fBh2load\fP [OPTIONS]... [URI]...
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
|
.sp
|
||||||
benchmarking tool for HTTP/2 and SPDY server
|
benchmarking tool for HTTP/2 and SPDY server
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
<URI>
|
.B <URI>
|
||||||
Specify URI to access. Multiple URIs can be
|
Specify URI to access. Multiple URIs can be specified.
|
||||||
specified. URIs are used in this order for each
|
URIs are used in this order for each client. All URIs
|
||||||
client. All URIs are used, then first URI is
|
are used, then first URI is used and then 2nd URI, and
|
||||||
used and then 2nd URI, and so on. The scheme,
|
so on. The scheme, host and port in the subsequent
|
||||||
host and port in the subsequent URIs, if present,
|
URIs, if present, are ignored. Those in the first URI
|
||||||
are ignored. Those in the first URI are used
|
are used solely.
|
||||||
solely.
|
.UNINDENT
|
||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
.HP
|
.INDENT 0.0
|
||||||
\fB\-n\fR, \fB\-\-requests=\fR<N> Number of requests. Default: 1
|
|
||||||
.TP
|
.TP
|
||||||
\fB\-c\fR, \fB\-\-clients=\fR<N>
|
.B \-n, \-\-requests=<N>
|
||||||
Number of concurrent clients. Default: 1
|
Number of requests.
|
||||||
|
.sp
|
||||||
|
Default: \fB1\fP
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-t\fR, \fB\-\-threads=\fR<N>
|
.B \-c, \-\-clients=<N>
|
||||||
Number of native threads. Default: 1
|
Number of concurrent clients.
|
||||||
|
.sp
|
||||||
|
Default: \fB1\fP
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-i\fR, \fB\-\-input\-file=\fR<FILE>
|
.B \-t, \-\-threads=<N>
|
||||||
Path of a file with multiple URIs are seperated
|
Number of native threads.
|
||||||
by EOLs. This option will disable URIs getting
|
.sp
|
||||||
from command\-line. If '\-' is given as <FILE>,
|
Default: \fB1\fP
|
||||||
URIs will be read from stdin. URIs are used in
|
.UNINDENT
|
||||||
this order for each client. All URIs are used,
|
.INDENT 0.0
|
||||||
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
|
.TP
|
||||||
\fB\-m\fR, \fB\-\-max\-concurrent\-streams=\fR(auto|<N>)
|
.B \-i, \-\-input\-file=<FILE>
|
||||||
Max concurrent streams to issue per session. If
|
Path of a file with multiple URIs are separated by EOLs.
|
||||||
"auto" is given, the number of given URIs is
|
This option will disable URIs getting from command\-line.
|
||||||
used. Default: auto
|
If \(aq\-\(aq is given as <FILE>, URIs will be read from stdin.
|
||||||
|
URIs are used in this order for each client. All URIs
|
||||||
|
are used, then first URI is used and then 2nd URI, and
|
||||||
|
so on. The scheme, host and port in the subsequent
|
||||||
|
URIs, if present, are ignored. Those in the first URI
|
||||||
|
are used solely.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-w\fR, \fB\-\-window\-bits=\fR<N>
|
.B \-m, \-\-max\-concurrent\-streams=(auto|<N>)
|
||||||
Sets the stream level initial window size to
|
Max concurrent streams to issue per session. If "auto"
|
||||||
(2**<N>)\-1. For SPDY, 2**<N> is used instead.
|
is given, the number of given URIs is used.
|
||||||
|
.sp
|
||||||
|
Default: \fBauto\fP
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-W\fR, \fB\-\-connection\-window\-bits=\fR<N>
|
.B \-w, \-\-window\-bits=<N>
|
||||||
Sets the connection level initial window size to
|
Sets the stream level initial window size to (2**<N>)\-1.
|
||||||
(2**<N>)\-1. For SPDY, if <N> is strictly less
|
For SPDY, 2**<N> is used instead.
|
||||||
than 16, this option is ignored. Otherwise
|
.sp
|
||||||
2**<N> is used for SPDY.
|
Default: \fB30\fP
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-H\fR, \fB\-\-header=\fR<HEADER>
|
.B \-W, \-\-connection\-window\-bits=<N>
|
||||||
|
Sets the connection level initial window size to
|
||||||
|
(2**<N>)\-1. For SPDY, if <N> is strictly less than 16,
|
||||||
|
this option is ignored. Otherwise 2**<N> is used for
|
||||||
|
SPDY.
|
||||||
|
.sp
|
||||||
|
Default: \fB30\fP
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
|
.TP
|
||||||
|
.B \-H, \-\-header=<HEADER>
|
||||||
Add/Override a header to the requests.
|
Add/Override a header to the requests.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-p\fR, \fB\-\-no\-tls\-proto=\fR<PROTOID>
|
.B \-p, \-\-no\-tls\-proto=<PROTOID>
|
||||||
Specify ALPN identifier of the protocol to be
|
Specify ALPN identifier of the protocol to be used when
|
||||||
used when accessing http URI without SSL/TLS.
|
accessing http URI without SSL/TLS.
|
||||||
Available protocols: spdy/2, spdy/3, spdy/3.1 and
|
Available protocols: spdy/2, spdy/3, spdy/3.1 and h2c
|
||||||
h2c\-14
|
.sp
|
||||||
Default: h2c\-14
|
Default: \fBh2c\fP
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-v\fR, \fB\-\-verbose\fR
|
.B \-d, \-\-data=<FILE>
|
||||||
|
Post FILE to server. The request method is changed to
|
||||||
|
POST.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
|
.TP
|
||||||
|
.B \-v, \-\-verbose
|
||||||
Output debug information.
|
Output debug information.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-version\fR
|
.B \-\-version
|
||||||
Display version information and exit.
|
Display version information and exit.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-h\fR, \fB\-\-help\fR
|
.B \-h, \-\-help
|
||||||
Display this help and exit.
|
Display this help and exit.
|
||||||
.SH "SEE ALSO"
|
.UNINDENT
|
||||||
|
.SH OUTPUT
|
||||||
nghttp(1), nghttpd(1), nghttpx(1)
|
.INDENT 0.0
|
||||||
|
.TP
|
||||||
|
.B requests
|
||||||
|
.INDENT 7.0
|
||||||
|
.TP
|
||||||
|
.B total
|
||||||
|
The number of requests h2load was instructed to make.
|
||||||
|
.TP
|
||||||
|
.B started
|
||||||
|
The number of requests h2load has started.
|
||||||
|
.TP
|
||||||
|
.B done
|
||||||
|
The number of requests completed.
|
||||||
|
.TP
|
||||||
|
.B succeeded
|
||||||
|
The number of requests completed successfully. Only HTTP status
|
||||||
|
code 2xx or3xx are considered as success.
|
||||||
|
.TP
|
||||||
|
.B failed
|
||||||
|
The number of requests failed, including HTTP level failures
|
||||||
|
(non\-successful HTTP status code).
|
||||||
|
.TP
|
||||||
|
.B errored
|
||||||
|
The number of requests failed, except for HTTP level failures.
|
||||||
|
status code. This is the subset of the number reported in
|
||||||
|
\fBfailed\fP and most likely the network level failures or stream
|
||||||
|
was reset by RST_STREAM.
|
||||||
|
.UNINDENT
|
||||||
|
.TP
|
||||||
|
.B status codes
|
||||||
|
The number of status code h2load received.
|
||||||
|
.TP
|
||||||
|
.B traffic
|
||||||
|
.INDENT 7.0
|
||||||
|
.TP
|
||||||
|
.B total
|
||||||
|
The number of bytes received from the server "on the wire". If
|
||||||
|
requests were made via TLS, this value is the number of decrpyted
|
||||||
|
bytes.
|
||||||
|
.TP
|
||||||
|
.B headers
|
||||||
|
The number of response header bytes from the server without
|
||||||
|
decompression. For HTTP/2, this is the sum of the payload of
|
||||||
|
HEADERS frame. For SPDY, this is the sum of the payload of
|
||||||
|
SYN_REPLY frame.
|
||||||
|
.TP
|
||||||
|
.B data
|
||||||
|
The number of response body bytes received from the server.
|
||||||
|
.UNINDENT
|
||||||
|
.TP
|
||||||
|
.B time for request
|
||||||
|
.INDENT 7.0
|
||||||
|
.TP
|
||||||
|
.B min
|
||||||
|
The minimum time taken for request and response.
|
||||||
|
.TP
|
||||||
|
.B max
|
||||||
|
The maximum time taken for request and response.
|
||||||
|
.TP
|
||||||
|
.B mean
|
||||||
|
The mean time taken for request and response.
|
||||||
|
.TP
|
||||||
|
.B sd
|
||||||
|
The standard deviation of the time taken for request and response.
|
||||||
|
.TP
|
||||||
|
.B +/\- sd
|
||||||
|
The fraction of the number of requests within standard deviation
|
||||||
|
range (mean +/\- sd) against total number of successful requests.
|
||||||
|
.UNINDENT
|
||||||
|
.TP
|
||||||
|
.B time for connect
|
||||||
|
.INDENT 7.0
|
||||||
|
.TP
|
||||||
|
.B min
|
||||||
|
The minimum time taken to connect to a server.
|
||||||
|
.TP
|
||||||
|
.B max
|
||||||
|
The maximum time taken to connect to a server.
|
||||||
|
.TP
|
||||||
|
.B mean
|
||||||
|
The mean time taken to connect to a server.
|
||||||
|
.TP
|
||||||
|
.B sd
|
||||||
|
The standard deviation of the time taken to connect to a server.
|
||||||
|
.TP
|
||||||
|
.B +/\- sd
|
||||||
|
The fraction of the number of connections within standard
|
||||||
|
deviation range (mean +/\- sd) against total number of successful
|
||||||
|
connections.
|
||||||
|
.UNINDENT
|
||||||
|
.TP
|
||||||
|
.B time for 1st byte (of (decrypted in case of TLS) application data)
|
||||||
|
.INDENT 7.0
|
||||||
|
.TP
|
||||||
|
.B min
|
||||||
|
The minimum time taken to get 1st byte from a server.
|
||||||
|
.TP
|
||||||
|
.B max
|
||||||
|
The maximum time taken to get 1st byte from a server.
|
||||||
|
.TP
|
||||||
|
.B mean
|
||||||
|
The mean time taken to get 1st byte from a server.
|
||||||
|
.TP
|
||||||
|
.B sd
|
||||||
|
The standard deviation of the time taken to get 1st byte from a
|
||||||
|
server.
|
||||||
|
.TP
|
||||||
|
.B +/\- sd
|
||||||
|
The fraction of the number of connections within standard
|
||||||
|
deviation range (mean +/\- sd) against total number of successful
|
||||||
|
connections.
|
||||||
|
.UNINDENT
|
||||||
|
.UNINDENT
|
||||||
|
.SH FLOW CONTROL
|
||||||
|
.sp
|
||||||
|
h2load sets large flow control window by default, and effectively
|
||||||
|
disables flow control to avoid under utilization of server
|
||||||
|
performance. To set smaller flow control window, use \fI\%\-w\fP and
|
||||||
|
\fI\%\-W\fP options. For example, use \fB\-w16 \-W16\fP to set default
|
||||||
|
window size described in HTTP/2 and SPDY protocol specification.
|
||||||
|
.SH SEE ALSO
|
||||||
|
.sp
|
||||||
|
\fInghttp(1)\fP, \fInghttpd(1)\fP, \fInghttpx(1)\fP
|
||||||
|
.SH AUTHOR
|
||||||
|
Tatsuhiro Tsujikawa
|
||||||
|
.SH COPYRIGHT
|
||||||
|
2012, 2015, Tatsuhiro Tsujikawa
|
||||||
|
.\" Generated by docutils manpage writer.
|
||||||
|
.
|
||||||
|
|||||||
193
doc/h2load.1.rst
193
doc/h2load.1.rst
@@ -1,113 +1,204 @@
|
|||||||
.. DO NOT MODIFY THIS FILE! It was generated by man2rst.py
|
|
||||||
|
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
|
||||||
|
|
||||||
.. program:: h2load
|
.. program:: h2load
|
||||||
|
|
||||||
h2load(1)
|
h2load(1)
|
||||||
=========
|
=========
|
||||||
|
|
||||||
NAME
|
|
||||||
----
|
|
||||||
h2load - HTTP/2 benchmarking tool
|
|
||||||
|
|
||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
--------
|
--------
|
||||||
|
|
||||||
**h2load** [OPTIONS]... [URI]...
|
**h2load** [OPTIONS]... [URI]...
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
benchmarking tool for HTTP/2 and SPDY server
|
benchmarking tool for HTTP/2 and SPDY server
|
||||||
|
|
||||||
.. option:: URI
|
.. describe:: <URI>
|
||||||
|
|
||||||
Specify URI to access. Multiple URIs can be
|
Specify URI to access. Multiple URIs can be specified.
|
||||||
specified. URIs are used in this order for each
|
URIs are used in this order for each client. All URIs
|
||||||
client. All URIs are used, then first URI is
|
are used, then first URI is used and then 2nd URI, and
|
||||||
used and then 2nd URI, and so on. The scheme,
|
so on. The scheme, host and port in the subsequent
|
||||||
host and port in the subsequent URIs, if present,
|
URIs, if present, are ignored. Those in the first URI
|
||||||
are ignored. Those in the first URI are used
|
are used solely.
|
||||||
solely.
|
|
||||||
|
|
||||||
OPTIONS
|
OPTIONS
|
||||||
-------
|
-------
|
||||||
|
|
||||||
.. option:: -n, --requests=<N>
|
.. option:: -n, --requests=<N>
|
||||||
|
|
||||||
Number of requests. Default: 1
|
Number of requests.
|
||||||
|
|
||||||
|
Default: ``1``
|
||||||
|
|
||||||
.. option:: -c, --clients=<N>
|
.. option:: -c, --clients=<N>
|
||||||
|
|
||||||
|
Number of concurrent clients.
|
||||||
Number of concurrent clients. Default: 1
|
|
||||||
|
Default: ``1``
|
||||||
|
|
||||||
.. option:: -t, --threads=<N>
|
.. option:: -t, --threads=<N>
|
||||||
|
|
||||||
|
Number of native threads.
|
||||||
Number of native threads. Default: 1
|
|
||||||
|
Default: ``1``
|
||||||
|
|
||||||
.. option:: -i, --input-file=<FILE>
|
.. option:: -i, --input-file=<FILE>
|
||||||
|
|
||||||
|
Path of a file with multiple URIs are separated by EOLs.
|
||||||
Path of a file with multiple URIs are seperated
|
This option will disable URIs getting from command-line.
|
||||||
by EOLs. This option will disable URIs getting
|
If '-' is given as <FILE>, URIs will be read from stdin.
|
||||||
from command-line. If '-' is given as <FILE>,
|
URIs are used in this order for each client. All URIs
|
||||||
URIs will be read from stdin. URIs are used in
|
are used, then first URI is used and then 2nd URI, and
|
||||||
this order for each client. All URIs are used,
|
so on. The scheme, host and port in the subsequent
|
||||||
then first URI is used and then 2nd URI, and so
|
URIs, if present, are ignored. Those in the first URI
|
||||||
on. The scheme, host and port in the subsequent
|
are used solely.
|
||||||
URIs, if present, are ignored. Those in the
|
|
||||||
first URI are used solely.
|
|
||||||
|
|
||||||
.. option:: -m, --max-concurrent-streams=(auto|<N>)
|
.. option:: -m, --max-concurrent-streams=(auto|<N>)
|
||||||
|
|
||||||
|
Max concurrent streams to issue per session. If "auto"
|
||||||
Max concurrent streams to issue per session. If
|
is given, the number of given URIs is used.
|
||||||
"auto" is given, the number of given URIs is
|
|
||||||
used. Default: auto
|
Default: ``auto``
|
||||||
|
|
||||||
.. option:: -w, --window-bits=<N>
|
.. option:: -w, --window-bits=<N>
|
||||||
|
|
||||||
|
Sets the stream level initial window size to (2\*\*<N>)-1.
|
||||||
Sets the stream level initial window size to
|
For SPDY, 2**<N> is used instead.
|
||||||
(2\*\*<N>)-1. For SPDY, 2\*\*<N> is used instead.
|
|
||||||
|
Default: ``30``
|
||||||
|
|
||||||
.. option:: -W, --connection-window-bits=<N>
|
.. option:: -W, --connection-window-bits=<N>
|
||||||
|
|
||||||
|
Sets the connection level initial window size to
|
||||||
Sets the connection level initial window size to
|
(2**<N>)-1. For SPDY, if <N> is strictly less than 16,
|
||||||
(2\*\*<N>)-1. For SPDY, if <N> is strictly less
|
this option is ignored. Otherwise 2\*\*<N> is used for
|
||||||
than 16, this option is ignored. Otherwise
|
SPDY.
|
||||||
2\*\*<N> is used for SPDY.
|
|
||||||
|
Default: ``30``
|
||||||
|
|
||||||
.. option:: -H, --header=<HEADER>
|
.. option:: -H, --header=<HEADER>
|
||||||
|
|
||||||
|
|
||||||
Add/Override a header to the requests.
|
Add/Override a header to the requests.
|
||||||
|
|
||||||
.. option:: -p, --no-tls-proto=<PROTOID>
|
.. option:: -p, --no-tls-proto=<PROTOID>
|
||||||
|
|
||||||
|
Specify ALPN identifier of the protocol to be used when
|
||||||
Specify ALPN identifier of the protocol to be
|
accessing http URI without SSL/TLS.
|
||||||
used when accessing http URI without SSL/TLS.
|
Available protocols: spdy/2, spdy/3, spdy/3.1 and h2c
|
||||||
Available protocols: spdy/2, spdy/3, spdy/3.1 and
|
|
||||||
h2c-14
|
Default: ``h2c``
|
||||||
Default: h2c-14
|
|
||||||
|
.. option:: -d, --data=<FILE>
|
||||||
|
|
||||||
|
Post FILE to server. The request method is changed to
|
||||||
|
POST.
|
||||||
|
|
||||||
.. option:: -v, --verbose
|
.. option:: -v, --verbose
|
||||||
|
|
||||||
|
|
||||||
Output debug information.
|
Output debug information.
|
||||||
|
|
||||||
.. option:: --version
|
.. option:: --version
|
||||||
|
|
||||||
|
|
||||||
Display version information and exit.
|
Display version information and exit.
|
||||||
|
|
||||||
.. option:: -h, --help
|
.. option:: -h, --help
|
||||||
|
|
||||||
|
|
||||||
Display this help and exit.
|
Display this help and exit.
|
||||||
|
|
||||||
|
OUTPUT
|
||||||
|
------
|
||||||
|
|
||||||
|
requests
|
||||||
|
total
|
||||||
|
The number of requests h2load was instructed to make.
|
||||||
|
started
|
||||||
|
The number of requests h2load has started.
|
||||||
|
done
|
||||||
|
The number of requests completed.
|
||||||
|
succeeded
|
||||||
|
The number of requests completed successfully. Only HTTP status
|
||||||
|
code 2xx or3xx are considered as success.
|
||||||
|
failed
|
||||||
|
The number of requests failed, including HTTP level failures
|
||||||
|
(non-successful HTTP status code).
|
||||||
|
errored
|
||||||
|
The number of requests failed, except for HTTP level failures.
|
||||||
|
status code. This is the subset of the number reported in
|
||||||
|
``failed`` and most likely the network level failures or stream
|
||||||
|
was reset by RST_STREAM.
|
||||||
|
|
||||||
|
status codes
|
||||||
|
The number of status code h2load received.
|
||||||
|
|
||||||
|
traffic
|
||||||
|
total
|
||||||
|
The number of bytes received from the server "on the wire". If
|
||||||
|
requests were made via TLS, this value is the number of decrpyted
|
||||||
|
bytes.
|
||||||
|
headers
|
||||||
|
The number of response header bytes from the server without
|
||||||
|
decompression. For HTTP/2, this is the sum of the payload of
|
||||||
|
HEADERS frame. For SPDY, this is the sum of the payload of
|
||||||
|
SYN_REPLY frame.
|
||||||
|
data
|
||||||
|
The number of response body bytes received from the server.
|
||||||
|
|
||||||
|
time for request
|
||||||
|
min
|
||||||
|
The minimum time taken for request and response.
|
||||||
|
max
|
||||||
|
The maximum time taken for request and response.
|
||||||
|
mean
|
||||||
|
The mean time taken for request and response.
|
||||||
|
sd
|
||||||
|
The standard deviation of the time taken for request and response.
|
||||||
|
+/- sd
|
||||||
|
The fraction of the number of requests within standard deviation
|
||||||
|
range (mean +/- sd) against total number of successful requests.
|
||||||
|
|
||||||
|
time for connect
|
||||||
|
min
|
||||||
|
The minimum time taken to connect to a server.
|
||||||
|
max
|
||||||
|
The maximum time taken to connect to a server.
|
||||||
|
mean
|
||||||
|
The mean time taken to connect to a server.
|
||||||
|
sd
|
||||||
|
The standard deviation of the time taken to connect to a server.
|
||||||
|
+/- sd
|
||||||
|
The fraction of the number of connections within standard
|
||||||
|
deviation range (mean +/- sd) against total number of successful
|
||||||
|
connections.
|
||||||
|
|
||||||
|
time for 1st byte (of (decrypted in case of TLS) application data)
|
||||||
|
min
|
||||||
|
The minimum time taken to get 1st byte from a server.
|
||||||
|
max
|
||||||
|
The maximum time taken to get 1st byte from a server.
|
||||||
|
mean
|
||||||
|
The mean time taken to get 1st byte from a server.
|
||||||
|
sd
|
||||||
|
The standard deviation of the time taken to get 1st byte from a
|
||||||
|
server.
|
||||||
|
+/- sd
|
||||||
|
The fraction of the number of connections within standard
|
||||||
|
deviation range (mean +/- sd) against total number of successful
|
||||||
|
connections.
|
||||||
|
|
||||||
|
FLOW CONTROL
|
||||||
|
------------
|
||||||
|
|
||||||
|
h2load sets large flow control window by default, and effectively
|
||||||
|
disables flow control to avoid under utilization of server
|
||||||
|
performance. To set smaller flow control window, use :option:`-w` and
|
||||||
|
:option:`-W` options. For example, use ``-w16 -W16`` to set default
|
||||||
|
window size described in HTTP/2 and SPDY protocol specification.
|
||||||
|
|
||||||
SEE ALSO
|
SEE ALSO
|
||||||
--------
|
--------
|
||||||
|
|
||||||
nghttp(1), nghttpd(1), nghttpx(1)
|
:manpage:`nghttp(1)`, :manpage:`nghttpd(1)`, :manpage:`nghttpx(1)`
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
[SEE ALSO]
|
|
||||||
|
|
||||||
nghttp(1), nghttpd(1), nghttpx(1)
|
|
||||||
93
doc/h2load.h2r
Normal file
93
doc/h2load.h2r
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
OUTPUT
|
||||||
|
------
|
||||||
|
|
||||||
|
requests
|
||||||
|
total
|
||||||
|
The number of requests h2load was instructed to make.
|
||||||
|
started
|
||||||
|
The number of requests h2load has started.
|
||||||
|
done
|
||||||
|
The number of requests completed.
|
||||||
|
succeeded
|
||||||
|
The number of requests completed successfully. Only HTTP status
|
||||||
|
code 2xx or3xx are considered as success.
|
||||||
|
failed
|
||||||
|
The number of requests failed, including HTTP level failures
|
||||||
|
(non-successful HTTP status code).
|
||||||
|
errored
|
||||||
|
The number of requests failed, except for HTTP level failures.
|
||||||
|
status code. This is the subset of the number reported in
|
||||||
|
``failed`` and most likely the network level failures or stream
|
||||||
|
was reset by RST_STREAM.
|
||||||
|
|
||||||
|
status codes
|
||||||
|
The number of status code h2load received.
|
||||||
|
|
||||||
|
traffic
|
||||||
|
total
|
||||||
|
The number of bytes received from the server "on the wire". If
|
||||||
|
requests were made via TLS, this value is the number of decrpyted
|
||||||
|
bytes.
|
||||||
|
headers
|
||||||
|
The number of response header bytes from the server without
|
||||||
|
decompression. For HTTP/2, this is the sum of the payload of
|
||||||
|
HEADERS frame. For SPDY, this is the sum of the payload of
|
||||||
|
SYN_REPLY frame.
|
||||||
|
data
|
||||||
|
The number of response body bytes received from the server.
|
||||||
|
|
||||||
|
time for request
|
||||||
|
min
|
||||||
|
The minimum time taken for request and response.
|
||||||
|
max
|
||||||
|
The maximum time taken for request and response.
|
||||||
|
mean
|
||||||
|
The mean time taken for request and response.
|
||||||
|
sd
|
||||||
|
The standard deviation of the time taken for request and response.
|
||||||
|
+/- sd
|
||||||
|
The fraction of the number of requests within standard deviation
|
||||||
|
range (mean +/- sd) against total number of successful requests.
|
||||||
|
|
||||||
|
time for connect
|
||||||
|
min
|
||||||
|
The minimum time taken to connect to a server.
|
||||||
|
max
|
||||||
|
The maximum time taken to connect to a server.
|
||||||
|
mean
|
||||||
|
The mean time taken to connect to a server.
|
||||||
|
sd
|
||||||
|
The standard deviation of the time taken to connect to a server.
|
||||||
|
+/- sd
|
||||||
|
The fraction of the number of connections within standard
|
||||||
|
deviation range (mean +/- sd) against total number of successful
|
||||||
|
connections.
|
||||||
|
|
||||||
|
time for 1st byte (of (decrypted in case of TLS) application data)
|
||||||
|
min
|
||||||
|
The minimum time taken to get 1st byte from a server.
|
||||||
|
max
|
||||||
|
The maximum time taken to get 1st byte from a server.
|
||||||
|
mean
|
||||||
|
The mean time taken to get 1st byte from a server.
|
||||||
|
sd
|
||||||
|
The standard deviation of the time taken to get 1st byte from a
|
||||||
|
server.
|
||||||
|
+/- sd
|
||||||
|
The fraction of the number of connections within standard
|
||||||
|
deviation range (mean +/- sd) against total number of successful
|
||||||
|
connections.
|
||||||
|
|
||||||
|
FLOW CONTROL
|
||||||
|
------------
|
||||||
|
|
||||||
|
h2load sets large flow control window by default, and effectively
|
||||||
|
disables flow control to avoid under utilization of server
|
||||||
|
performance. To set smaller flow control window, use :option:`-w` and
|
||||||
|
:option:`-W` options. For example, use ``-w16 -W16`` to set default
|
||||||
|
window size described in HTTP/2 and SPDY protocol specification.
|
||||||
|
|
||||||
|
SEE ALSO
|
||||||
|
--------
|
||||||
|
|
||||||
|
:manpage:`nghttp(1)`, :manpage:`nghttpd(1)`, :manpage:`nghttpx(1)`
|
||||||
128
doc/mkapiref.py
128
doc/mkapiref.py
@@ -1,4 +1,5 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
# nghttp2 - HTTP/2 C Library
|
# nghttp2 - HTTP/2 C Library
|
||||||
|
|
||||||
# Copyright (c) 2012 Tatsuhiro Tsujikawa
|
# Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||||
@@ -23,20 +24,24 @@
|
|||||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
# Generates API reference from C source code.
|
# Generates API reference from C source code.
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
from __future__ import print_function # At least python 2.6 is required
|
from __future__ import print_function # At least python 2.6 is required
|
||||||
import re, sys, argparse
|
import re, sys, argparse, os.path
|
||||||
|
|
||||||
class FunctionDoc:
|
class FunctionDoc:
|
||||||
def __init__(self, name, content, domain):
|
def __init__(self, name, content, domain):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.content = content
|
self.content = content
|
||||||
self.domain = domain
|
self.domain = domain
|
||||||
|
if self.domain == 'function':
|
||||||
|
self.funcname = re.search(r'(nghttp2_[^ )]+)\(', self.name).group(1)
|
||||||
|
|
||||||
def write(self, out):
|
def write(self, out):
|
||||||
print('''.. {}:: {}'''.format(self.domain, self.name))
|
out.write('.. {}:: {}\n'.format(self.domain, self.name))
|
||||||
print('')
|
out.write('\n')
|
||||||
for line in self.content:
|
for line in self.content:
|
||||||
print(' {}'.format(line))
|
out.write(' {}\n'.format(line))
|
||||||
|
|
||||||
class StructDoc:
|
class StructDoc:
|
||||||
def __init__(self, name, content, members, member_domain):
|
def __init__(self, name, content, members, member_domain):
|
||||||
@@ -47,17 +52,17 @@ class StructDoc:
|
|||||||
|
|
||||||
def write(self, out):
|
def write(self, out):
|
||||||
if self.name:
|
if self.name:
|
||||||
print('''.. type:: {}'''.format(self.name))
|
out.write('.. type:: {}\n'.format(self.name))
|
||||||
print('')
|
out.write('\n')
|
||||||
for line in self.content:
|
for line in self.content:
|
||||||
print(' {}'.format(line))
|
out.write(' {}\n'.format(line))
|
||||||
print('')
|
out.write('\n')
|
||||||
for name, content in self.members:
|
for name, content in self.members:
|
||||||
print(''' .. {}:: {}'''.format(self.member_domain, name))
|
out.write(' .. {}:: {}\n'.format(self.member_domain, name))
|
||||||
print('')
|
out.write('\n')
|
||||||
for line in content:
|
for line in content:
|
||||||
print(''' {}'''.format(line))
|
out.write(' {}\n'.format(line))
|
||||||
print('')
|
out.write('\n')
|
||||||
|
|
||||||
class MacroDoc:
|
class MacroDoc:
|
||||||
def __init__(self, name, content):
|
def __init__(self, name, content):
|
||||||
@@ -65,10 +70,10 @@ class MacroDoc:
|
|||||||
self.content = content
|
self.content = content
|
||||||
|
|
||||||
def write(self, out):
|
def write(self, out):
|
||||||
print('''.. macro:: {}'''.format(self.name))
|
out.write('''.. macro:: {}\n'''.format(self.name))
|
||||||
print('')
|
out.write('\n')
|
||||||
for line in self.content:
|
for line in self.content:
|
||||||
print(' {}'.format(line))
|
out.write(' {}\n'.format(line))
|
||||||
|
|
||||||
def make_api_ref(infiles):
|
def make_api_ref(infiles):
|
||||||
macros = []
|
macros = []
|
||||||
@@ -93,19 +98,65 @@ def make_api_ref(infiles):
|
|||||||
enums.append(process_enum(infile))
|
enums.append(process_enum(infile))
|
||||||
elif doctype == '@macro':
|
elif doctype == '@macro':
|
||||||
macros.append(process_macro(infile))
|
macros.append(process_macro(infile))
|
||||||
|
return macros, enums, types, functions
|
||||||
|
|
||||||
alldocs = [('Macros', macros),
|
alldocs = [('Macros', macros),
|
||||||
('Enums', enums),
|
('Enums', enums),
|
||||||
('Types (structs, unions and typedefs)', types),
|
('Types (structs, unions and typedefs)', types),
|
||||||
('Functions', functions)]
|
('Functions', functions)]
|
||||||
for title, docs in alldocs:
|
|
||||||
if not docs:
|
def output(
|
||||||
continue
|
indexfile, macrosfile, enumsfile, typesfile, funcsdir,
|
||||||
print(title)
|
macros, enums, types, functions):
|
||||||
print('-'*len(title))
|
indexfile.write('''
|
||||||
for doc in docs:
|
API Reference
|
||||||
doc.write(sys.stdout)
|
=============
|
||||||
print('')
|
|
||||||
print('')
|
.. toctree::
|
||||||
|
:maxdepth: 1
|
||||||
|
|
||||||
|
macros
|
||||||
|
enums
|
||||||
|
types
|
||||||
|
''')
|
||||||
|
|
||||||
|
for doc in functions:
|
||||||
|
indexfile.write(' {}\n'.format(doc.funcname))
|
||||||
|
|
||||||
|
macrosfile.write('''
|
||||||
|
Macros
|
||||||
|
======
|
||||||
|
''')
|
||||||
|
for doc in macros:
|
||||||
|
doc.write(macrosfile)
|
||||||
|
|
||||||
|
enumsfile.write('''
|
||||||
|
Enums
|
||||||
|
=====
|
||||||
|
''')
|
||||||
|
for doc in enums:
|
||||||
|
doc.write(enumsfile)
|
||||||
|
|
||||||
|
typesfile.write('''
|
||||||
|
Types (structs, unions and typedefs)
|
||||||
|
====================================
|
||||||
|
''')
|
||||||
|
for doc in types:
|
||||||
|
doc.write(typesfile)
|
||||||
|
|
||||||
|
for doc in functions:
|
||||||
|
with open(os.path.join(funcsdir, doc.funcname + '.rst'), 'w') as f:
|
||||||
|
f.write('''
|
||||||
|
{funcname}
|
||||||
|
{secul}
|
||||||
|
|
||||||
|
Synopsis
|
||||||
|
--------
|
||||||
|
|
||||||
|
*#include <nghttp2/nghttp2.h>*
|
||||||
|
|
||||||
|
'''.format(funcname=doc.funcname, secul='='*len(doc.funcname)))
|
||||||
|
doc.write(f)
|
||||||
|
|
||||||
def process_macro(infile):
|
def process_macro(infile):
|
||||||
content = read_content(infile)
|
content = read_content(infile)
|
||||||
@@ -174,6 +225,7 @@ def process_function(domain, infile):
|
|||||||
func_proto = ''.join(func_proto)
|
func_proto = ''.join(func_proto)
|
||||||
func_proto = re.sub(r';\n$', '', func_proto)
|
func_proto = re.sub(r';\n$', '', func_proto)
|
||||||
func_proto = re.sub(r'\s+', ' ', func_proto)
|
func_proto = re.sub(r'\s+', ' ', func_proto)
|
||||||
|
func_proto = re.sub(r'NGHTTP2_EXTERN ', '', func_proto)
|
||||||
return FunctionDoc(func_proto, content, domain)
|
return FunctionDoc(func_proto, content, domain)
|
||||||
|
|
||||||
def read_content(infile):
|
def read_content(infile):
|
||||||
@@ -199,12 +251,30 @@ def transform_content(content):
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
parser = argparse.ArgumentParser(description="Generate API reference")
|
parser = argparse.ArgumentParser(description="Generate API reference")
|
||||||
parser.add_argument('--header', type=argparse.FileType('r'),
|
parser.add_argument('index', type=argparse.FileType('w'),
|
||||||
help='header inserted at the top of the page')
|
help='index output file')
|
||||||
|
parser.add_argument('macros', type=argparse.FileType('w'),
|
||||||
|
help='macros section output file. The filename should be macros.rst')
|
||||||
|
parser.add_argument('enums', type=argparse.FileType('w'),
|
||||||
|
help='enums section output file. The filename should be enums.rst')
|
||||||
|
parser.add_argument('types', type=argparse.FileType('w'),
|
||||||
|
help='types section output file. The filename should be types.rst')
|
||||||
|
parser.add_argument('funcsdir',
|
||||||
|
help='functions doc output dir')
|
||||||
parser.add_argument('files', nargs='+', type=argparse.FileType('r'),
|
parser.add_argument('files', nargs='+', type=argparse.FileType('r'),
|
||||||
help='source file')
|
help='source file')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
if args.header:
|
macros = []
|
||||||
print(args.header.read())
|
enums = []
|
||||||
|
types = []
|
||||||
|
funcs = []
|
||||||
for infile in args.files:
|
for infile in args.files:
|
||||||
make_api_ref(args.files)
|
m, e, t, f = make_api_ref(args.files)
|
||||||
|
macros.extend(m)
|
||||||
|
enums.extend(e)
|
||||||
|
types.extend(t)
|
||||||
|
funcs.extend(f)
|
||||||
|
funcs.sort(key=lambda x: x.funcname)
|
||||||
|
output(
|
||||||
|
args.index, args.macros, args.enums, args.types, args.funcsdir,
|
||||||
|
macros, enums, types, funcs)
|
||||||
|
|||||||
326
doc/nghttp.1
326
doc/nghttp.1
@@ -1,113 +1,289 @@
|
|||||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.4.
|
.\" Man page generated from reStructuredText.
|
||||||
.TH NGHTTP "1" "October 2014" "nghttp nghttp2/0.6.5" "User Commands"
|
.
|
||||||
|
.TH "NGHTTP" "1" "June 23, 2015" "1.0.4" "nghttp2"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
nghttp \- HTTP/2 experimental client
|
nghttp \- HTTP/2 experimental client
|
||||||
|
.
|
||||||
|
.nr rst2man-indent-level 0
|
||||||
|
.
|
||||||
|
.de1 rstReportMargin
|
||||||
|
\\$1 \\n[an-margin]
|
||||||
|
level \\n[rst2man-indent-level]
|
||||||
|
level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||||
|
-
|
||||||
|
\\n[rst2man-indent0]
|
||||||
|
\\n[rst2man-indent1]
|
||||||
|
\\n[rst2man-indent2]
|
||||||
|
..
|
||||||
|
.de1 INDENT
|
||||||
|
.\" .rstReportMargin pre:
|
||||||
|
. RS \\$1
|
||||||
|
. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
|
||||||
|
. nr rst2man-indent-level +1
|
||||||
|
.\" .rstReportMargin post:
|
||||||
|
..
|
||||||
|
.de UNINDENT
|
||||||
|
. RE
|
||||||
|
.\" indent \\n[an-margin]
|
||||||
|
.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||||
|
.nr rst2man-indent-level -1
|
||||||
|
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||||
|
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||||
|
..
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
.B nghttp
|
.sp
|
||||||
[\fI\,OPTIONS\/\fR]... \fI\,<URI>\/\fR...
|
\fBnghttp\fP [OPTIONS]... <URI>...
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
|
.sp
|
||||||
HTTP/2 experimental client
|
HTTP/2 experimental client
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
<URI>
|
.B <URI>
|
||||||
Specify URI to access.
|
Specify URI to access.
|
||||||
|
.UNINDENT
|
||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-v\fR, \fB\-\-verbose\fR
|
.B \-v, \-\-verbose
|
||||||
Print debug information such as reception and
|
Print debug information such as reception and
|
||||||
transmission of frames and name/value pairs.
|
transmission of frames and name/value pairs. Specifying
|
||||||
|
this option multiple times increases verbosity.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-n\fR, \fB\-\-null\-out\fR
|
.B \-n, \-\-null\-out
|
||||||
Discard downloaded data.
|
Discard downloaded data.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-O\fR, \fB\-\-remote\-name\fR
|
.B \-O, \-\-remote\-name
|
||||||
Save download data in the current directory. The
|
Save download data in the current directory. The
|
||||||
filename is dereived from URI. If URI ends with
|
filename is dereived from URI. If URI ends with \(aq\fI/\fP\(aq,
|
||||||
\&'/', 'index.html' is used as a filename. Not
|
\(aqindex.html\(aq is used as a filename. Not implemented
|
||||||
implemented yet.
|
yet.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-t\fR, \fB\-\-timeout=\fR<N>
|
.B \-t, \-\-timeout=<DURATION>
|
||||||
Timeout each request after <N> seconds.
|
Timeout each request after <DURATION>. Set 0 to disable
|
||||||
|
timeout.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-w\fR, \fB\-\-window\-bits=\fR<N>
|
.B \-w, \-\-window\-bits=<N>
|
||||||
Sets the stream level initial window size to
|
Sets the stream level initial window size to 2**<N>\-1.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
|
.TP
|
||||||
|
.B \-W, \-\-connection\-window\-bits=<N>
|
||||||
|
Sets the connection level initial window size to
|
||||||
2**<N>\-1.
|
2**<N>\-1.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-W\fR, \fB\-\-connection\-window\-bits=\fR<N>
|
.B \-a, \-\-get\-assets
|
||||||
Sets the connection level initial window size to
|
Download assets such as stylesheets, images and script
|
||||||
2**<N>\-1.
|
files linked from the downloaded resource. Only links
|
||||||
|
whose origins are the same with the 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.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-a\fR, \fB\-\-get\-assets\fR
|
.B \-s, \-\-stat
|
||||||
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. 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.
|
Print statistics.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-H\fR, \fB\-\-header=\fR<HEADER>
|
.B \-H, \-\-header=<HEADER>
|
||||||
Add a header to the requests. Example:
|
Add a header to the requests. Example: \fI\%\-H\fP\(aq:method: PUT\(aq
|
||||||
\fB\-H\fR':method: PUT'
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-cert=\fR<CERT>
|
.B \-\-trailer=<HEADER>
|
||||||
Use the specified client certificate file. The
|
Add a trailer header to the requests. <HEADER> must not
|
||||||
file must be in PEM format.
|
include pseudo header field (header field name starting
|
||||||
|
with \(aq:\(aq). To send trailer, one must use \fI\%\-d\fP option to
|
||||||
|
send request body. Example: \fI\%\-\-trailer\fP \(aqfoo: bar\(aq.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-key=\fR<KEY>
|
.B \-\-cert=<CERT>
|
||||||
Use the client private key file. The file must
|
Use the specified client certificate file. The file
|
||||||
be in PEM format.
|
must be in PEM format.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-d\fR, \fB\-\-data=\fR<FILE>
|
.B \-\-key=<KEY>
|
||||||
Post FILE to server. If '\-' is given, data will
|
Use the client private key file. The file must be in
|
||||||
be read from stdin.
|
PEM format.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-m\fR, \fB\-\-multiply=\fR<N> Request each URI <N> times.
|
.B \-d, \-\-data=<FILE>
|
||||||
By default, same URI
|
Post FILE to server. If \(aq\-\(aq is given, data will be read
|
||||||
is not requested twice. This option disables it
|
from stdin.
|
||||||
too.
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-u\fR, \fB\-\-upgrade\fR
|
.B \-m, \-\-multiply=<N>
|
||||||
Perform HTTP Upgrade for HTTP/2. This option is
|
Request each URI <N> times. By default, same URI is not
|
||||||
ignored if the request URI has https scheme. If
|
requested twice. This option disables it too.
|
||||||
\fB\-d\fR is used, the HTTP upgrade request is performed
|
.UNINDENT
|
||||||
with OPTIONS method.
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-p\fR, \fB\-\-weight=\fR<WEIGHT>
|
.B \-u, \-\-upgrade
|
||||||
Sets priority group weight. The valid value
|
Perform HTTP Upgrade for HTTP/2. This option is ignored
|
||||||
range is [1, 256], inclusive.
|
if the request URI has https scheme. If \fI\%\-d\fP is used, the
|
||||||
Default: 16
|
HTTP upgrade request is performed with OPTIONS method.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-M\fR, \fB\-\-peer\-max\-concurrent\-streams=\fR<N>
|
.B \-p, \-\-weight=<WEIGHT>
|
||||||
Use <N> as SETTINGS_MAX_CONCURRENT_STREAMS value
|
Sets priority group weight. The valid value range is
|
||||||
of remote endpoint as if it is received in
|
[1, 256], inclusive.
|
||||||
SETTINGS frame. The default is large enough as
|
.sp
|
||||||
it is seen as unlimited.
|
Default: \fB16\fP
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-c\fR, \fB\-\-header\-table\-size=\fR<N>
|
.B \-M, \-\-peer\-max\-concurrent\-streams=<N>
|
||||||
|
Use <N> as SETTINGS_MAX_CONCURRENT_STREAMS value of
|
||||||
|
remote endpoint as if it is received in SETTINGS frame.
|
||||||
|
The default is large enough as it is seen as unlimited.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
|
.TP
|
||||||
|
.B \-c, \-\-header\-table\-size=<SIZE>
|
||||||
Specify decoder header table size.
|
Specify decoder header table size.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-b\fR, \fB\-\-padding=\fR<N>
|
.B \-b, \-\-padding=<N>
|
||||||
Add at most <N> bytes to a frame payload as
|
Add at most <N> bytes to a frame payload as padding.
|
||||||
padding. Specify 0 to disable padding.
|
Specify 0 to disable padding.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-color\fR
|
.B \-r, \-\-har=<FILE>
|
||||||
|
Output HTTP transactions <FILE> in HAR format. If \(aq\-\(aq
|
||||||
|
is given, data is written to stdout.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
|
.TP
|
||||||
|
.B \-\-color
|
||||||
Force colored log output.
|
Force colored log output.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-continuation\fR
|
.B \-\-continuation
|
||||||
Send large header to test CONTINUATION.
|
Send large header to test CONTINUATION.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-no\-content\-length\fR
|
.B \-\-no\-content\-length
|
||||||
Don't send content\-length header field.
|
Don\(aqt send content\-length header field.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-version\fR
|
.B \-\-no\-dep
|
||||||
|
Don\(aqt send dependency based priority hint to server.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
|
.TP
|
||||||
|
.B \-\-hexdump
|
||||||
|
Display the incoming traffic in hexadecimal (Canonical
|
||||||
|
hex+ASCII display). If SSL/TLS is used, decrypted data
|
||||||
|
are used.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
|
.TP
|
||||||
|
.B \-\-no\-push
|
||||||
|
Disable server push.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
|
.TP
|
||||||
|
.B \-\-version
|
||||||
Display version information and exit.
|
Display version information and exit.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-h\fR, \fB\-\-help\fR
|
.B \-h, \-\-help
|
||||||
Display this help and exit.
|
Display this help and exit.
|
||||||
.SH "SEE ALSO"
|
.UNINDENT
|
||||||
|
.sp
|
||||||
nghttpd(1), nghttpx(1), h2load(1)
|
The <SIZE> argument is an integer and an optional unit (e.g., 10K is
|
||||||
|
10 * 1024). Units are K, M and G (powers of 1024).
|
||||||
|
.sp
|
||||||
|
The <DURATION> argument is an integer and an optional unit (e.g., 1s
|
||||||
|
is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms
|
||||||
|
(hours, minutes, seconds and milliseconds, respectively). If a unit
|
||||||
|
is omitted, a second is used as unit.
|
||||||
|
.SH DEPENDENCY BASED PRIORITY
|
||||||
|
.sp
|
||||||
|
nghttp sends priority hints to server by default unless
|
||||||
|
\fI\%\-\-no\-dep\fP is used. nghttp mimics the way Firefox employs to
|
||||||
|
manages dependency using idle streams. We follows the behaviour of
|
||||||
|
Firefox Nightly as of April, 2015, and nghttp\(aqs behaviour is very
|
||||||
|
static and could be different from Firefox in detail. But reproducing
|
||||||
|
the same behaviour of Firefox is not our goal. The goal is provide
|
||||||
|
the easy way to test out the dependency priority in server
|
||||||
|
implementation.
|
||||||
|
.sp
|
||||||
|
When connection is established, nghttp sends 5 PRIORITY frames to idle
|
||||||
|
streams 3, 5, 7, 9 and 11 to create "anchor" nodes in dependency
|
||||||
|
tree:
|
||||||
|
.INDENT 0.0
|
||||||
|
.INDENT 3.5
|
||||||
|
.sp
|
||||||
|
.nf
|
||||||
|
.ft C
|
||||||
|
+\-\-\-\-\-+
|
||||||
|
|id=0 |
|
||||||
|
+\-\-\-\-\-+
|
||||||
|
^ ^ ^
|
||||||
|
w=201 / | \e w=1
|
||||||
|
/ | \e
|
||||||
|
/ w=101| \e
|
||||||
|
+\-\-\-\-\-+ +\-\-\-\-\-+ +\-\-\-\-\-+
|
||||||
|
|id=3 | |id=5 | |id=7 |
|
||||||
|
+\-\-\-\-\-+ +\-\-\-\-\-+ +\-\-\-\-\-+
|
||||||
|
^ ^
|
||||||
|
w=1 | w=1 |
|
||||||
|
| |
|
||||||
|
+\-\-\-\-\-+ +\-\-\-\-\-+
|
||||||
|
|id=11| |id=9 |
|
||||||
|
+\-\-\-\-\-+ +\-\-\-\-\-+
|
||||||
|
.ft P
|
||||||
|
.fi
|
||||||
|
.UNINDENT
|
||||||
|
.UNINDENT
|
||||||
|
.sp
|
||||||
|
In the above figure, \fBid\fP means stream ID, and \fBw\fP means weight.
|
||||||
|
The stream 0 is non\-existence stream, and forms the root of the tree.
|
||||||
|
The stream 7 and 9 are not used for now.
|
||||||
|
.sp
|
||||||
|
The URIs given in the command\-line depend on stream 11 with the weight
|
||||||
|
given in \fI\%\-p\fP option, which defaults to 16.
|
||||||
|
.sp
|
||||||
|
If \fI\%\-a\fP option is used, nghttp parses the resource pointed by
|
||||||
|
URI given in command\-line as html, and extracts resource links from
|
||||||
|
it. When requesting those resources, nghttp uses dependency according
|
||||||
|
to its resource type.
|
||||||
|
.sp
|
||||||
|
For CSS, and Javascript files inside "head" element, they depend on
|
||||||
|
stream 3 with the weight 2. The Javascript files outside "head"
|
||||||
|
element depend on stream 5 with the weight 2. The mages depend on
|
||||||
|
stream 11 with the weight 12. The other resources (e.g., icon) depend
|
||||||
|
on stream 11 with the weight 2.
|
||||||
|
.SH SEE ALSO
|
||||||
|
.sp
|
||||||
|
\fInghttpd(1)\fP, \fInghttpx(1)\fP, \fIh2load(1)\fP
|
||||||
|
.SH AUTHOR
|
||||||
|
Tatsuhiro Tsujikawa
|
||||||
|
.SH COPYRIGHT
|
||||||
|
2012, 2015, Tatsuhiro Tsujikawa
|
||||||
|
.\" Generated by docutils manpage writer.
|
||||||
|
.
|
||||||
|
|||||||
208
doc/nghttp.1.rst
208
doc/nghttp.1.rst
@@ -1,23 +1,22 @@
|
|||||||
.. DO NOT MODIFY THIS FILE! It was generated by man2rst.py
|
|
||||||
|
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
|
||||||
|
|
||||||
.. program:: nghttp
|
.. program:: nghttp
|
||||||
|
|
||||||
nghttp(1)
|
nghttp(1)
|
||||||
=========
|
=========
|
||||||
|
|
||||||
NAME
|
|
||||||
----
|
|
||||||
nghttp - HTTP/2 experimental client
|
|
||||||
|
|
||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
--------
|
--------
|
||||||
|
|
||||||
**nghttp** [OPTIONS]... <URI>...
|
**nghttp** [OPTIONS]... <URI>...
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
HTTP/2 experimental client
|
HTTP/2 experimental client
|
||||||
|
|
||||||
.. option:: URI
|
.. describe:: <URI>
|
||||||
|
|
||||||
Specify URI to access.
|
Specify URI to access.
|
||||||
|
|
||||||
@@ -26,148 +25,209 @@ OPTIONS
|
|||||||
|
|
||||||
.. option:: -v, --verbose
|
.. option:: -v, --verbose
|
||||||
|
|
||||||
|
Print debug information such as reception and
|
||||||
Print debug information such as reception and
|
transmission of frames and name/value pairs. Specifying
|
||||||
transmission of frames and name/value pairs.
|
this option multiple times increases verbosity.
|
||||||
|
|
||||||
.. option:: -n, --null-out
|
.. option:: -n, --null-out
|
||||||
|
|
||||||
|
|
||||||
Discard downloaded data.
|
Discard downloaded data.
|
||||||
|
|
||||||
.. option:: -O, --remote-name
|
.. option:: -O, --remote-name
|
||||||
|
|
||||||
|
Save download data in the current directory. The
|
||||||
Save download data in the current directory. The
|
filename is dereived from URI. If URI ends with '*/*',
|
||||||
filename is dereived from URI. If URI ends with
|
'index.html' is used as a filename. Not implemented
|
||||||
'/', 'index.html' is used as a filename. Not
|
yet.
|
||||||
implemented yet.
|
|
||||||
|
|
||||||
.. option:: -t, --timeout=<N>
|
.. option:: -t, --timeout=<DURATION>
|
||||||
|
|
||||||
|
Timeout each request after <DURATION>. Set 0 to disable
|
||||||
Timeout each request after <N> seconds.
|
timeout.
|
||||||
|
|
||||||
.. option:: -w, --window-bits=<N>
|
.. option:: -w, --window-bits=<N>
|
||||||
|
|
||||||
|
Sets the stream level initial window size to 2\*\*<N>-1.
|
||||||
Sets the stream level initial window size to
|
|
||||||
2\*\*<N>-1.
|
|
||||||
|
|
||||||
.. option:: -W, --connection-window-bits=<N>
|
.. option:: -W, --connection-window-bits=<N>
|
||||||
|
|
||||||
|
Sets the connection level initial window size to
|
||||||
Sets the connection level initial window size to
|
|
||||||
2\*\*<N>-1.
|
2\*\*<N>-1.
|
||||||
|
|
||||||
.. option:: -a, --get-assets
|
.. option:: -a, --get-assets
|
||||||
|
|
||||||
|
Download assets such as stylesheets, images and script
|
||||||
Download assets such as stylesheets, images and
|
files linked from the downloaded resource. Only links
|
||||||
script files linked from the downloaded resource.
|
whose origins are the same with the linking resource
|
||||||
Only links whose origins are the same with the
|
will be downloaded. nghttp prioritizes resources using
|
||||||
linking resource will be downloaded. nghttp
|
HTTP/2 dependency based priority. The priority order,
|
||||||
prioritizes resources using HTTP/2 dependency
|
from highest to lowest, is html itself, css, javascript
|
||||||
based priority. The priority order, from highest
|
and images.
|
||||||
to lowest, is html itself, css, javascript and
|
|
||||||
images.
|
|
||||||
|
|
||||||
.. option:: -s, --stat
|
.. option:: -s, --stat
|
||||||
|
|
||||||
|
|
||||||
Print statistics.
|
Print statistics.
|
||||||
|
|
||||||
.. option:: -H, --header=<HEADER>
|
.. option:: -H, --header=<HEADER>
|
||||||
|
|
||||||
|
Add a header to the requests. Example: :option:`-H`\':method: PUT'
|
||||||
Add a header to the requests. Example:
|
|
||||||
-H':method: PUT'
|
.. option:: --trailer=<HEADER>
|
||||||
|
|
||||||
|
Add a trailer header to the requests. <HEADER> must not
|
||||||
|
include pseudo header field (header field name starting
|
||||||
|
with ':'). To send trailer, one must use :option:`-d` option to
|
||||||
|
send request body. Example: :option:`--trailer` 'foo: bar'.
|
||||||
|
|
||||||
.. option:: --cert=<CERT>
|
.. option:: --cert=<CERT>
|
||||||
|
|
||||||
|
Use the specified client certificate file. The file
|
||||||
Use the specified client certificate file. The
|
must be in PEM format.
|
||||||
file must be in PEM format.
|
|
||||||
|
|
||||||
.. option:: --key=<KEY>
|
.. option:: --key=<KEY>
|
||||||
|
|
||||||
|
Use the client private key file. The file must be in
|
||||||
Use the client private key file. The file must
|
PEM format.
|
||||||
be in PEM format.
|
|
||||||
|
|
||||||
.. option:: -d, --data=<FILE>
|
.. option:: -d, --data=<FILE>
|
||||||
|
|
||||||
|
Post FILE to server. If '-' is given, data will be read
|
||||||
Post FILE to server. If '-' is given, data will
|
from stdin.
|
||||||
be read from stdin.
|
|
||||||
|
|
||||||
.. option:: -m, --multiply=<N>
|
.. option:: -m, --multiply=<N>
|
||||||
|
|
||||||
Request each URI <N> times.
|
Request each URI <N> times. By default, same URI is not
|
||||||
By default, same URI
|
requested twice. This option disables it too.
|
||||||
is not requested twice. This option disables it
|
|
||||||
too.
|
|
||||||
|
|
||||||
.. option:: -u, --upgrade
|
.. option:: -u, --upgrade
|
||||||
|
|
||||||
|
Perform HTTP Upgrade for HTTP/2. This option is ignored
|
||||||
Perform HTTP Upgrade for HTTP/2. This option is
|
if the request URI has https scheme. If :option:`-d` is used, the
|
||||||
ignored if the request URI has https scheme. If
|
HTTP upgrade request is performed with OPTIONS method.
|
||||||
:option:`-d` is used, the HTTP upgrade request is performed
|
|
||||||
with OPTIONS method.
|
|
||||||
|
|
||||||
.. option:: -p, --weight=<WEIGHT>
|
.. option:: -p, --weight=<WEIGHT>
|
||||||
|
|
||||||
|
Sets priority group weight. The valid value range is
|
||||||
Sets priority group weight. The valid value
|
[1, 256], inclusive.
|
||||||
range is [1, 256], inclusive.
|
|
||||||
Default: 16
|
Default: ``16``
|
||||||
|
|
||||||
.. option:: -M, --peer-max-concurrent-streams=<N>
|
.. option:: -M, --peer-max-concurrent-streams=<N>
|
||||||
|
|
||||||
|
Use <N> as SETTINGS_MAX_CONCURRENT_STREAMS value of
|
||||||
Use <N> as SETTINGS_MAX_CONCURRENT_STREAMS value
|
remote endpoint as if it is received in SETTINGS frame.
|
||||||
of remote endpoint as if it is received in
|
The default is large enough as it is seen as unlimited.
|
||||||
SETTINGS frame. The default is large enough as
|
|
||||||
it is seen as unlimited.
|
|
||||||
|
|
||||||
.. option:: -c, --header-table-size=<N>
|
.. option:: -c, --header-table-size=<SIZE>
|
||||||
|
|
||||||
|
|
||||||
Specify decoder header table size.
|
Specify decoder header table size.
|
||||||
|
|
||||||
.. option:: -b, --padding=<N>
|
.. option:: -b, --padding=<N>
|
||||||
|
|
||||||
|
Add at most <N> bytes to a frame payload as padding.
|
||||||
Add at most <N> bytes to a frame payload as
|
Specify 0 to disable padding.
|
||||||
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
|
.. option:: --color
|
||||||
|
|
||||||
|
|
||||||
Force colored log output.
|
Force colored log output.
|
||||||
|
|
||||||
.. option:: --continuation
|
.. option:: --continuation
|
||||||
|
|
||||||
|
|
||||||
Send large header to test CONTINUATION.
|
Send large header to test CONTINUATION.
|
||||||
|
|
||||||
.. option:: --no-content-length
|
.. option:: --no-content-length
|
||||||
|
|
||||||
|
|
||||||
Don't send content-length header field.
|
Don't send content-length header field.
|
||||||
|
|
||||||
|
.. option:: --no-dep
|
||||||
|
|
||||||
|
Don't send dependency based priority hint to server.
|
||||||
|
|
||||||
|
.. option:: --hexdump
|
||||||
|
|
||||||
|
Display the incoming traffic in hexadecimal (Canonical
|
||||||
|
hex+ASCII display). If SSL/TLS is used, decrypted data
|
||||||
|
are used.
|
||||||
|
|
||||||
|
.. option:: --no-push
|
||||||
|
|
||||||
|
Disable server push.
|
||||||
|
|
||||||
.. option:: --version
|
.. option:: --version
|
||||||
|
|
||||||
|
|
||||||
Display version information and exit.
|
Display version information and exit.
|
||||||
|
|
||||||
.. option:: -h, --help
|
.. option:: -h, --help
|
||||||
|
|
||||||
|
|
||||||
Display this help and exit.
|
Display this help and exit.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
The <SIZE> argument is an integer and an optional unit (e.g., 10K is
|
||||||
|
10 * 1024). Units are K, M and G (powers of 1024).
|
||||||
|
|
||||||
|
The <DURATION> argument is an integer and an optional unit (e.g., 1s
|
||||||
|
is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms
|
||||||
|
(hours, minutes, seconds and milliseconds, respectively). If a unit
|
||||||
|
is omitted, a second is used as unit.
|
||||||
|
|
||||||
|
DEPENDENCY BASED PRIORITY
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
nghttp sends priority hints to server by default unless
|
||||||
|
:option:`--no-dep` is used. nghttp mimics the way Firefox employs to
|
||||||
|
manages dependency using idle streams. We follows the behaviour of
|
||||||
|
Firefox Nightly as of April, 2015, and nghttp's behaviour is very
|
||||||
|
static and could be different from Firefox in detail. But reproducing
|
||||||
|
the same behaviour of Firefox is not our goal. The goal is provide
|
||||||
|
the easy way to test out the dependency priority in server
|
||||||
|
implementation.
|
||||||
|
|
||||||
|
When connection is established, nghttp sends 5 PRIORITY frames to idle
|
||||||
|
streams 3, 5, 7, 9 and 11 to create "anchor" nodes in dependency
|
||||||
|
tree::
|
||||||
|
|
||||||
|
+-----+
|
||||||
|
|id=0 |
|
||||||
|
+-----+
|
||||||
|
^ ^ ^
|
||||||
|
w=201 / | \ w=1
|
||||||
|
/ | \
|
||||||
|
/ w=101| \
|
||||||
|
+-----+ +-----+ +-----+
|
||||||
|
|id=3 | |id=5 | |id=7 |
|
||||||
|
+-----+ +-----+ +-----+
|
||||||
|
^ ^
|
||||||
|
w=1 | w=1 |
|
||||||
|
| |
|
||||||
|
+-----+ +-----+
|
||||||
|
|id=11| |id=9 |
|
||||||
|
+-----+ +-----+
|
||||||
|
|
||||||
|
In the above figure, ``id`` means stream ID, and ``w`` means weight.
|
||||||
|
The stream 0 is non-existence stream, and forms the root of the tree.
|
||||||
|
The stream 7 and 9 are not used for now.
|
||||||
|
|
||||||
|
The URIs given in the command-line depend on stream 11 with the weight
|
||||||
|
given in :option:`-p` option, which defaults to 16.
|
||||||
|
|
||||||
|
If :option:`-a` option is used, nghttp parses the resource pointed by
|
||||||
|
URI given in command-line as html, and extracts resource links from
|
||||||
|
it. When requesting those resources, nghttp uses dependency according
|
||||||
|
to its resource type.
|
||||||
|
|
||||||
|
For CSS, and Javascript files inside "head" element, they depend on
|
||||||
|
stream 3 with the weight 2. The Javascript files outside "head"
|
||||||
|
element depend on stream 5 with the weight 2. The mages depend on
|
||||||
|
stream 11 with the weight 12. The other resources (e.g., icon) depend
|
||||||
|
on stream 11 with the weight 2.
|
||||||
|
|
||||||
SEE ALSO
|
SEE ALSO
|
||||||
--------
|
--------
|
||||||
|
|
||||||
nghttpd(1), nghttpx(1), h2load(1)
|
:manpage:`nghttpd(1)`, :manpage:`nghttpx(1)`, :manpage:`h2load(1)`
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
[SEE ALSO]
|
|
||||||
|
|
||||||
nghttpd(1), nghttpx(1), h2load(1)
|
|
||||||
55
doc/nghttp.h2r
Normal file
55
doc/nghttp.h2r
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
DEPENDENCY BASED PRIORITY
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
nghttp sends priority hints to server by default unless
|
||||||
|
:option:`--no-dep` is used. nghttp mimics the way Firefox employs to
|
||||||
|
manages dependency using idle streams. We follows the behaviour of
|
||||||
|
Firefox Nightly as of April, 2015, and nghttp's behaviour is very
|
||||||
|
static and could be different from Firefox in detail. But reproducing
|
||||||
|
the same behaviour of Firefox is not our goal. The goal is provide
|
||||||
|
the easy way to test out the dependency priority in server
|
||||||
|
implementation.
|
||||||
|
|
||||||
|
When connection is established, nghttp sends 5 PRIORITY frames to idle
|
||||||
|
streams 3, 5, 7, 9 and 11 to create "anchor" nodes in dependency
|
||||||
|
tree::
|
||||||
|
|
||||||
|
+-----+
|
||||||
|
|id=0 |
|
||||||
|
+-----+
|
||||||
|
^ ^ ^
|
||||||
|
w=201 / | \ w=1
|
||||||
|
/ | \
|
||||||
|
/ w=101| \
|
||||||
|
+-----+ +-----+ +-----+
|
||||||
|
|id=3 | |id=5 | |id=7 |
|
||||||
|
+-----+ +-----+ +-----+
|
||||||
|
^ ^
|
||||||
|
w=1 | w=1 |
|
||||||
|
| |
|
||||||
|
+-----+ +-----+
|
||||||
|
|id=11| |id=9 |
|
||||||
|
+-----+ +-----+
|
||||||
|
|
||||||
|
In the above figure, ``id`` means stream ID, and ``w`` means weight.
|
||||||
|
The stream 0 is non-existence stream, and forms the root of the tree.
|
||||||
|
The stream 7 and 9 are not used for now.
|
||||||
|
|
||||||
|
The URIs given in the command-line depend on stream 11 with the weight
|
||||||
|
given in :option:`-p` option, which defaults to 16.
|
||||||
|
|
||||||
|
If :option:`-a` option is used, nghttp parses the resource pointed by
|
||||||
|
URI given in command-line as html, and extracts resource links from
|
||||||
|
it. When requesting those resources, nghttp uses dependency according
|
||||||
|
to its resource type.
|
||||||
|
|
||||||
|
For CSS, and Javascript files inside "head" element, they depend on
|
||||||
|
stream 3 with the weight 2. The Javascript files outside "head"
|
||||||
|
element depend on stream 5 with the weight 2. The mages depend on
|
||||||
|
stream 11 with the weight 12. The other resources (e.g., icon) depend
|
||||||
|
on stream 11 with the weight 2.
|
||||||
|
|
||||||
|
SEE ALSO
|
||||||
|
--------
|
||||||
|
|
||||||
|
:manpage:`nghttpd(1)`, :manpage:`nghttpx(1)`, :manpage:`h2load(1)`
|
||||||
223
doc/nghttpd.1
223
doc/nghttpd.1
@@ -1,93 +1,194 @@
|
|||||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.4.
|
.\" Man page generated from reStructuredText.
|
||||||
.TH NGHTTPD "1" "October 2014" "nghttpd nghttp2/0.6.5" "User Commands"
|
.
|
||||||
|
.TH "NGHTTPD" "1" "June 23, 2015" "1.0.4" "nghttp2"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
nghttpd \- HTTP/2 experimental server
|
nghttpd \- HTTP/2 experimental server
|
||||||
|
.
|
||||||
|
.nr rst2man-indent-level 0
|
||||||
|
.
|
||||||
|
.de1 rstReportMargin
|
||||||
|
\\$1 \\n[an-margin]
|
||||||
|
level \\n[rst2man-indent-level]
|
||||||
|
level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||||
|
-
|
||||||
|
\\n[rst2man-indent0]
|
||||||
|
\\n[rst2man-indent1]
|
||||||
|
\\n[rst2man-indent2]
|
||||||
|
..
|
||||||
|
.de1 INDENT
|
||||||
|
.\" .rstReportMargin pre:
|
||||||
|
. RS \\$1
|
||||||
|
. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
|
||||||
|
. nr rst2man-indent-level +1
|
||||||
|
.\" .rstReportMargin post:
|
||||||
|
..
|
||||||
|
.de UNINDENT
|
||||||
|
. RE
|
||||||
|
.\" indent \\n[an-margin]
|
||||||
|
.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||||
|
.nr rst2man-indent-level -1
|
||||||
|
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||||
|
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||||
|
..
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
.B nghttpd
|
.sp
|
||||||
[\fI\,OPTION\/\fR]... \fI\,<PORT> <PRIVATE_KEY> <CERT>\/\fR
|
\fBnghttpd\fP [OPTION]... <PORT> [<PRIVATE_KEY> <CERT>]
|
||||||
.br
|
|
||||||
.B nghttpd
|
|
||||||
\fI\,--no-tls \/\fR[\fI\,OPTION\/\fR]... \fI\,<PORT>\/\fR
|
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
|
.sp
|
||||||
HTTP/2 experimental server
|
HTTP/2 experimental server
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
<PORT>
|
.B <PORT>
|
||||||
Specify listening port number.
|
Specify listening port number.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
<PRIVATE_KEY>
|
.B <PRIVATE_KEY>
|
||||||
Set path to server's private key. Required
|
Set path to server\(aqs private key. Required unless
|
||||||
unless \fB\-\-no\-tls\fR is specified.
|
\fI\%\-\-no\-tls\fP is specified.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
<CERT>
|
.B <CERT>
|
||||||
Set path to server's certificate. Required
|
Set path to server\(aqs certificate. Required unless
|
||||||
unless \fB\-\-no\-tls\fR is specified.
|
\fI\%\-\-no\-tls\fP is specified.
|
||||||
|
.UNINDENT
|
||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-D\fR, \fB\-\-daemon\fR
|
.B \-a, \-\-address=<ADDR>
|
||||||
Run in a background. If \fB\-D\fR is used, the current
|
The address to bind to. If not specified the default IP
|
||||||
working directory is changed to '/'. Therefore
|
address determined by getaddrinfo is used.
|
||||||
if this option is used, \fB\-d\fR option must be
|
.UNINDENT
|
||||||
specified.
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-V\fR, \fB\-\-verify\-client\fR
|
.B \-D, \-\-daemon
|
||||||
The server sends a client certificate request.
|
Run in a background. If \fI\%\-D\fP is used, the current working
|
||||||
If the client did not return a certificate, the
|
directory is changed to \(aq\fI/\fP\(aq. Therefore if this option
|
||||||
handshake is terminated. Currently, this option
|
is used, \fI\%\-d\fP option must be specified.
|
||||||
just requests a client certificate and does not
|
.UNINDENT
|
||||||
verify it.
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-d\fR, \fB\-\-htdocs=\fR<PATH>
|
.B \-V, \-\-verify\-client
|
||||||
Specify document root. If this option is not
|
The server sends a client certificate request. If the
|
||||||
specified, the document root is the current
|
client did not return a certificate, the handshake is
|
||||||
working directory.
|
terminated. Currently, this option just requests a
|
||||||
|
client certificate and does not verify it.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-v\fR, \fB\-\-verbose\fR
|
.B \-d, \-\-htdocs=<PATH>
|
||||||
Print debug information such as reception/
|
Specify document root. If this option is not specified,
|
||||||
transmission of frames and name/value pairs.
|
the document root is the current working directory.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-no\-tls\fR
|
.B \-v, \-\-verbose
|
||||||
|
Print debug information such as reception/ transmission
|
||||||
|
of frames and name/value pairs.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
|
.TP
|
||||||
|
.B \-\-no\-tls
|
||||||
Disable SSL/TLS.
|
Disable SSL/TLS.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-c\fR, \fB\-\-header\-table\-size=\fR<N>
|
.B \-c, \-\-header\-table\-size=<SIZE>
|
||||||
Specify decoder header table size.
|
Specify decoder header table size.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-color\fR
|
.B \-\-color
|
||||||
Force colored log output.
|
Force colored log output.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-p\fR, \fB\-\-push=\fR<PATH>=<PUSH_PATH,...>
|
.B \-p, \-\-push=<PATH>=<PUSH_PATH,...>
|
||||||
Push resources <PUSH_PATH>s when <PATH> is
|
Push resources <PUSH_PATH>s when <PATH> is requested.
|
||||||
requested. This option can be used repeatedly to
|
This option can be used repeatedly to specify multiple
|
||||||
specify multiple push configurations. <PATH> and
|
push configurations. <PATH> and <PUSH_PATH>s are
|
||||||
<PUSH_PATH>s are relative to document root. See
|
relative to document root. See \fI\%\-\-htdocs\fP option.
|
||||||
\fB\-\-htdocs\fR option. Example: \fB\-p\fR/=/foo.png
|
Example: \fI\%\-p\fP/=/foo.png \fI\%\-p\fP/doc=/bar.css
|
||||||
\fB\-p\fR/doc=/bar.css
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-b\fR, \fB\-\-padding=\fR<N>
|
.B \-b, \-\-padding=<N>
|
||||||
Add at most <N> bytes to a frame payload as
|
Add at most <N> bytes to a frame payload as padding.
|
||||||
padding. Specify 0 to disable padding.
|
Specify 0 to disable padding.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-n\fR, \fB\-\-workers=\fR<CORE>
|
.B \-m, \-\-max\-concurrent\-streams=<N>
|
||||||
|
Set the maximum number of the concurrent streams in one
|
||||||
|
HTTP/2 session.
|
||||||
|
.sp
|
||||||
|
Default: \fB100\fP
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
|
.TP
|
||||||
|
.B \-n, \-\-workers=<N>
|
||||||
Set the number of worker threads.
|
Set the number of worker threads.
|
||||||
Default: 1
|
.sp
|
||||||
|
Default: \fB1\fP
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-e\fR, \fB\-\-error\-gzip\fR
|
.B \-e, \-\-error\-gzip
|
||||||
Make error response gzipped.
|
Make error response gzipped.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-dh\-param\-file=\fR<PATH>
|
.B \-\-dh\-param\-file=<PATH>
|
||||||
Path to file that contains DH parameters in PEM
|
Path to file that contains DH parameters in PEM format.
|
||||||
format. Without this option, DHE cipher suites
|
Without this option, DHE cipher suites are not
|
||||||
are not available.
|
available.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-early\-response\fR
|
.B \-\-early\-response
|
||||||
Start sending response when request HEADERS is
|
Start sending response when request HEADERS is received,
|
||||||
received, rather than complete request is
|
rather than complete request is received.
|
||||||
received.
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-version\fR
|
.B \-\-trailer=<HEADER>
|
||||||
|
Add a trailer header to a response. <HEADER> must not
|
||||||
|
include pseudo header field (header field name starting
|
||||||
|
with \(aq:\(aq). The trailer is sent only if a response has
|
||||||
|
body part. Example: \fI\%\-\-trailer\fP \(aqfoo: bar\(aq.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
|
.TP
|
||||||
|
.B \-\-hexdump
|
||||||
|
Display the incoming traffic in hexadecimal (Canonical
|
||||||
|
hex+ASCII display). If SSL/TLS is used, decrypted data
|
||||||
|
are used.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
|
.TP
|
||||||
|
.B \-\-echo\-upload
|
||||||
|
Send back uploaded content if method is POST or PUT.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
|
.TP
|
||||||
|
.B \-\-version
|
||||||
Display version information and exit.
|
Display version information and exit.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
\fB\-h\fR, \fB\-\-help\fR
|
.B \-h, \-\-help
|
||||||
Display this help and exit.
|
Display this help and exit.
|
||||||
.SH "SEE ALSO"
|
.UNINDENT
|
||||||
|
.sp
|
||||||
nghttp(1), nghttpx(1), h2load(1)
|
The <SIZE> argument is an integer and an optional unit (e.g., 10K is
|
||||||
|
10 * 1024). Units are K, M and G (powers of 1024).
|
||||||
|
.SH SEE ALSO
|
||||||
|
.sp
|
||||||
|
\fInghttp(1)\fP, \fInghttpx(1)\fP, \fIh2load(1)\fP
|
||||||
|
.SH AUTHOR
|
||||||
|
Tatsuhiro Tsujikawa
|
||||||
|
.SH COPYRIGHT
|
||||||
|
2012, 2015, Tatsuhiro Tsujikawa
|
||||||
|
.\" Generated by docutils manpage writer.
|
||||||
|
.
|
||||||
|
|||||||
@@ -1,138 +1,151 @@
|
|||||||
.. DO NOT MODIFY THIS FILE! It was generated by man2rst.py
|
|
||||||
|
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
|
||||||
|
|
||||||
.. program:: nghttpd
|
.. program:: nghttpd
|
||||||
|
|
||||||
nghttpd(1)
|
nghttpd(1)
|
||||||
==========
|
==========
|
||||||
|
|
||||||
NAME
|
|
||||||
----
|
|
||||||
nghttpd - HTTP/2 experimental server
|
|
||||||
|
|
||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
--------
|
--------
|
||||||
**nghttpd** [OPTION]... <PORT> <PRIVATE_KEY> <CERT>
|
|
||||||
|
|
||||||
**nghttpd** --no-tls [OPTION]... <PORT>
|
**nghttpd** [OPTION]... <PORT> [<PRIVATE_KEY> <CERT>]
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
HTTP/2 experimental server
|
HTTP/2 experimental server
|
||||||
|
|
||||||
.. option:: PORT
|
.. describe:: <PORT>
|
||||||
|
|
||||||
Specify listening port number.
|
Specify listening port number.
|
||||||
|
|
||||||
.. option:: PRIVATE_KEY
|
.. describe:: <PRIVATE_KEY>
|
||||||
|
|
||||||
Set path to server's private key. Required
|
|
||||||
unless :option:`--no-tls` is specified.
|
Set path to server's private key. Required unless
|
||||||
|
:option:`--no-tls` is specified.
|
||||||
|
|
||||||
.. option:: CERT
|
.. describe:: <CERT>
|
||||||
|
|
||||||
Set path to server's certificate. Required
|
Set path to server's certificate. Required unless
|
||||||
unless :option:`--no-tls` is specified.
|
:option:`--no-tls` is specified.
|
||||||
|
|
||||||
OPTIONS
|
OPTIONS
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
.. option:: -a, --address=<ADDR>
|
||||||
|
|
||||||
|
The address to bind to. If not specified the default IP
|
||||||
|
address determined by getaddrinfo is used.
|
||||||
|
|
||||||
.. option:: -D, --daemon
|
.. option:: -D, --daemon
|
||||||
|
|
||||||
|
Run in a background. If :option:`-D` is used, the current working
|
||||||
Run in a background. If :option:`-D` is used, the current
|
directory is changed to '*/*'. Therefore if this option
|
||||||
working directory is changed to '/'. Therefore
|
is used, :option:`-d` option must be specified.
|
||||||
if this option is used, :option:`-d` option must be
|
|
||||||
specified.
|
|
||||||
|
|
||||||
.. option:: -V, --verify-client
|
.. option:: -V, --verify-client
|
||||||
|
|
||||||
|
The server sends a client certificate request. If the
|
||||||
The server sends a client certificate request.
|
client did not return a certificate, the handshake is
|
||||||
If the client did not return a certificate, the
|
terminated. Currently, this option just requests a
|
||||||
handshake is terminated. Currently, this option
|
client certificate and does not verify it.
|
||||||
just requests a client certificate and does not
|
|
||||||
verify it.
|
|
||||||
|
|
||||||
.. option:: -d, --htdocs=<PATH>
|
.. option:: -d, --htdocs=<PATH>
|
||||||
|
|
||||||
|
Specify document root. If this option is not specified,
|
||||||
Specify document root. If this option is not
|
the document root is the current working directory.
|
||||||
specified, the document root is the current
|
|
||||||
working directory.
|
|
||||||
|
|
||||||
.. option:: -v, --verbose
|
.. option:: -v, --verbose
|
||||||
|
|
||||||
|
Print debug information such as reception/ transmission
|
||||||
Print debug information such as reception/
|
of frames and name/value pairs.
|
||||||
transmission of frames and name/value pairs.
|
|
||||||
|
|
||||||
.. option:: --no-tls
|
.. option:: --no-tls
|
||||||
|
|
||||||
|
|
||||||
Disable SSL/TLS.
|
Disable SSL/TLS.
|
||||||
|
|
||||||
.. option:: -c, --header-table-size=<N>
|
.. option:: -c, --header-table-size=<SIZE>
|
||||||
|
|
||||||
|
|
||||||
Specify decoder header table size.
|
Specify decoder header table size.
|
||||||
|
|
||||||
.. option:: --color
|
.. option:: --color
|
||||||
|
|
||||||
|
|
||||||
Force colored log output.
|
Force colored log output.
|
||||||
|
|
||||||
.. option:: -p, --push=<PATH>=<PUSH_PATH,...>
|
.. option:: -p, --push=<PATH>=<PUSH_PATH,...>
|
||||||
|
|
||||||
|
Push resources <PUSH_PATH>s when <PATH> is requested.
|
||||||
Push resources <PUSH_PATH>s when <PATH> is
|
This option can be used repeatedly to specify multiple
|
||||||
requested. This option can be used repeatedly to
|
push configurations. <PATH> and <PUSH_PATH>s are
|
||||||
specify multiple push configurations. <PATH> and
|
relative to document root. See :option:`--htdocs` option.
|
||||||
<PUSH_PATH>s are relative to document root. See
|
Example: :option:`-p`\/=/foo.png :option:`-p`\/doc=/bar.css
|
||||||
:option:`--htdocs` option. Example: -p/=/foo.png
|
|
||||||
-p/doc=/bar.css
|
|
||||||
|
|
||||||
.. option:: -b, --padding=<N>
|
.. option:: -b, --padding=<N>
|
||||||
|
|
||||||
|
Add at most <N> bytes to a frame payload as padding.
|
||||||
Add at most <N> bytes to a frame payload as
|
Specify 0 to disable padding.
|
||||||
padding. Specify 0 to disable padding.
|
|
||||||
|
|
||||||
.. option:: -n, --workers=<CORE>
|
.. option:: -m, --max-concurrent-streams=<N>
|
||||||
|
|
||||||
|
Set the maximum number of the concurrent streams in one
|
||||||
|
HTTP/2 session.
|
||||||
|
|
||||||
|
Default: ``100``
|
||||||
|
|
||||||
|
.. option:: -n, --workers=<N>
|
||||||
|
|
||||||
|
|
||||||
Set the number of worker threads.
|
Set the number of worker threads.
|
||||||
Default: 1
|
|
||||||
|
Default: ``1``
|
||||||
|
|
||||||
.. option:: -e, --error-gzip
|
.. option:: -e, --error-gzip
|
||||||
|
|
||||||
|
|
||||||
Make error response gzipped.
|
Make error response gzipped.
|
||||||
|
|
||||||
.. option:: --dh-param-file=<PATH>
|
.. option:: --dh-param-file=<PATH>
|
||||||
|
|
||||||
|
Path to file that contains DH parameters in PEM format.
|
||||||
Path to file that contains DH parameters in PEM
|
Without this option, DHE cipher suites are not
|
||||||
format. Without this option, DHE cipher suites
|
available.
|
||||||
are not available.
|
|
||||||
|
|
||||||
.. option:: --early-response
|
.. option:: --early-response
|
||||||
|
|
||||||
|
Start sending response when request HEADERS is received,
|
||||||
Start sending response when request HEADERS is
|
rather than complete request is received.
|
||||||
received, rather than complete request is
|
|
||||||
received.
|
.. option:: --trailer=<HEADER>
|
||||||
|
|
||||||
|
Add a trailer header to a response. <HEADER> must not
|
||||||
|
include pseudo header field (header field name starting
|
||||||
|
with ':'). The trailer is sent only if a response has
|
||||||
|
body part. Example: :option:`--trailer` 'foo: bar'.
|
||||||
|
|
||||||
|
.. option:: --hexdump
|
||||||
|
|
||||||
|
Display the incoming traffic in hexadecimal (Canonical
|
||||||
|
hex+ASCII display). If SSL/TLS is used, decrypted data
|
||||||
|
are used.
|
||||||
|
|
||||||
|
.. option:: --echo-upload
|
||||||
|
|
||||||
|
Send back uploaded content if method is POST or PUT.
|
||||||
|
|
||||||
.. option:: --version
|
.. option:: --version
|
||||||
|
|
||||||
|
|
||||||
Display version information and exit.
|
Display version information and exit.
|
||||||
|
|
||||||
.. option:: -h, --help
|
.. option:: -h, --help
|
||||||
|
|
||||||
|
|
||||||
Display this help and exit.
|
Display this help and exit.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
The <SIZE> argument is an integer and an optional unit (e.g., 10K is
|
||||||
|
10 * 1024). Units are K, M and G (powers of 1024).
|
||||||
|
|
||||||
SEE ALSO
|
SEE ALSO
|
||||||
--------
|
--------
|
||||||
|
|
||||||
nghttp(1), nghttpx(1), h2load(1)
|
:manpage:`nghttp(1)`, :manpage:`nghttpx(1)`, :manpage:`h2load(1)`
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
[SEE ALSO]
|
|
||||||
|
|
||||||
nghttp(1), nghttpx(1), h2load(1)
|
|
||||||
4
doc/nghttpd.h2r
Normal file
4
doc/nghttpd.h2r
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
SEE ALSO
|
||||||
|
--------
|
||||||
|
|
||||||
|
:manpage:`nghttp(1)`, :manpage:`nghttpx(1)`, :manpage:`h2load(1)`
|
||||||
1145
doc/nghttpx.1
1145
doc/nghttpx.1
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,3 +0,0 @@
|
|||||||
[SEE ALSO]
|
|
||||||
|
|
||||||
nghttp(1), nghttpd(1), h2load(1)
|
|
||||||
102
doc/nghttpx.h2r
Normal file
102
doc/nghttpx.h2r
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
FILES
|
||||||
|
-----
|
||||||
|
|
||||||
|
*/etc/nghttpx/nghttpx.conf*
|
||||||
|
The default configuration file path nghttpx searches at startup.
|
||||||
|
The configuration file path can be changed using :option:`--conf`
|
||||||
|
option.
|
||||||
|
|
||||||
|
Those lines which are staring ``#`` are treated as comment.
|
||||||
|
|
||||||
|
The option name in the configuration file is the long command-line
|
||||||
|
option name with leading ``--`` stripped (e.g., ``frontend``). Put
|
||||||
|
``=`` between option name and value. Don't put extra leading or
|
||||||
|
trailing spaces.
|
||||||
|
|
||||||
|
The options which do not take argument in the command-line *take*
|
||||||
|
argument in the configuration file. Specify ``yes`` as an argument
|
||||||
|
(e.g., ``http2-proxy=yes``). If other string is given, it is
|
||||||
|
ignored.
|
||||||
|
|
||||||
|
To specify private key and certificate file which are given as
|
||||||
|
positional arguments in command-line, use ``private-key-file`` and
|
||||||
|
``certificate-file``.
|
||||||
|
|
||||||
|
:option:`--conf` option cannot be used in the configuration file and
|
||||||
|
will be ignored if specified.
|
||||||
|
|
||||||
|
SIGNALS
|
||||||
|
-------
|
||||||
|
|
||||||
|
SIGQUIT
|
||||||
|
Shutdown gracefully. First accept pending connections and stop
|
||||||
|
accepting connection. After all connections are handled, nghttpx
|
||||||
|
exits.
|
||||||
|
|
||||||
|
SIGUSR1
|
||||||
|
Reopen log files.
|
||||||
|
|
||||||
|
SIGUSR2
|
||||||
|
Fork and execute nghttpx. It will execute the binary in the same
|
||||||
|
path with same command-line arguments and environment variables.
|
||||||
|
After new process comes up, sending SIGQUIT to the original process
|
||||||
|
to perform hot swapping.
|
||||||
|
|
||||||
|
SERVER PUSH
|
||||||
|
-----------
|
||||||
|
|
||||||
|
nghttpx supports HTTP/2 server push in default mode. nghttpx looks
|
||||||
|
for Link header field (`RFC 5988
|
||||||
|
<http://tools.ietf.org/html/rfc5988>`_) in response headers from
|
||||||
|
backend server and extracts URI-reference with parameter
|
||||||
|
``rel=preload`` (see `preload
|
||||||
|
<http://w3c.github.io/preload/#interoperability-with-http-link-header>`_)
|
||||||
|
and pushes those URIs to the frontend client. Here is a sample Link
|
||||||
|
header field to initiate server push:
|
||||||
|
|
||||||
|
.. code-block:: http
|
||||||
|
|
||||||
|
Link: </fonts/font.woff>; rel=preload
|
||||||
|
Link: </css/theme.css>; rel=preload
|
||||||
|
|
||||||
|
Currently, the following restrictions are applied for server push:
|
||||||
|
|
||||||
|
1. URI-reference must not contain authority. If it exists, it is not
|
||||||
|
pushed. ``/fonts/font.woff`` and ``css/theme.css`` are eligible to
|
||||||
|
be pushed. ``https://example.org/fonts/font.woff`` and
|
||||||
|
``//example.org/css/theme.css`` are not.
|
||||||
|
|
||||||
|
2. The associated stream must have method "GET" or "POST". The
|
||||||
|
associated stream's status code must be 200.
|
||||||
|
|
||||||
|
These limitations may be loosened in the future release.
|
||||||
|
|
||||||
|
UNIX DOMAIN SOCKET
|
||||||
|
------------------
|
||||||
|
|
||||||
|
nghttpx supports UNIX domain socket with a filename for both frontend
|
||||||
|
and backend connections.
|
||||||
|
|
||||||
|
Please note that current nghttpx implementation does not delete a
|
||||||
|
socket with a filename. And on start up, if nghttpx detects that the
|
||||||
|
specified socket already exists in the file system, nghttpx first
|
||||||
|
deletes it. However, if SIGUSR2 is used to execute new binary and
|
||||||
|
both old and new configurations use same filename, new binary does not
|
||||||
|
delete the socket and continues to use it.
|
||||||
|
|
||||||
|
OCSP STAPLING
|
||||||
|
-------------
|
||||||
|
|
||||||
|
OCSP query is done using external Python script
|
||||||
|
``fetch-ocsp-response``, which has been originally developed in Perl
|
||||||
|
as part of h2o project (https://github.com/h2o/h2o), and was
|
||||||
|
translated into Python.
|
||||||
|
|
||||||
|
The script file is usually installed under
|
||||||
|
``$(prefix)/share/nghttp2/`` directory. The actual path to script can
|
||||||
|
be customized using :option:`--fetch-ocsp-response-file` option.
|
||||||
|
|
||||||
|
SEE ALSO
|
||||||
|
--------
|
||||||
|
|
||||||
|
:manpage:`nghttp(1)`, :manpage:`nghttpd(1)`, :manpage:`h2load(1)`
|
||||||
105
doc/programmers-guide.rst
Normal file
105
doc/programmers-guide.rst
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
Programmers' Guide
|
||||||
|
==================
|
||||||
|
|
||||||
|
Includes
|
||||||
|
--------
|
||||||
|
|
||||||
|
To use the public APIs, include ``nghttp2/nghttp2.h``::
|
||||||
|
|
||||||
|
#include <nghttp2/nghttp2.h>
|
||||||
|
|
||||||
|
The header files are also available online: :doc:`nghttp2.h` and
|
||||||
|
:doc:`nghttp2ver.h`.
|
||||||
|
|
||||||
|
Remarks
|
||||||
|
-------
|
||||||
|
|
||||||
|
Do not call `nghttp2_session_send()`, `nghttp2_session_mem_send()`,
|
||||||
|
`nghttp2_session_recv()` or `nghttp2_session_mem_recv()` from the
|
||||||
|
nghttp2 callback functions directly or indirectly. It will lead to the
|
||||||
|
crash. You can submit requests or frames in the callbacks then call
|
||||||
|
these functions outside the callbacks.
|
||||||
|
|
||||||
|
`nghttp2_session_send()` and `nghttp2_session_mem_send()` send first
|
||||||
|
24 bytes of client magic string (MAGIC)
|
||||||
|
(:macro:`NGHTTP2_CLIENT_MAGIC`) on client configuration. The
|
||||||
|
applications are responsible to send SETTINGS frame as part of
|
||||||
|
connection preface using `nghttp2_submit_settings()`. Similarly,
|
||||||
|
`nghttp2_session_recv()` and `nghttp2_session_mem_recv()` consume
|
||||||
|
MAGIC on server configuration unless
|
||||||
|
`nghttp2_option_set_no_recv_client_magic()` is used with nonzero
|
||||||
|
option value.
|
||||||
|
|
||||||
|
.. _http-messaging:
|
||||||
|
|
||||||
|
HTTP Messaging
|
||||||
|
--------------
|
||||||
|
|
||||||
|
By default, nghttp2 library checks HTTP messaging rules described in
|
||||||
|
`HTTP/2 specification, section 8
|
||||||
|
<https://tools.ietf.org/html/draft-ietf-httpbis-http2-17#section-8>`_.
|
||||||
|
Everything described in that section is not validated however. We
|
||||||
|
briefly describe what the library does in this area. In the following
|
||||||
|
description, without loss of generality we omit CONTINUATION frame
|
||||||
|
since they must follow HEADERS frame and are processed atomically. In
|
||||||
|
other words, they are just one big HEADERS frame. To disable these
|
||||||
|
validations, use `nghttp2_option_set_no_http_messaging()`.
|
||||||
|
|
||||||
|
For HTTP request, including those carried by PUSH_PROMISE, HTTP
|
||||||
|
message starts with one HEADERS frame containing request headers. It
|
||||||
|
is followed by zero or more DATA frames containing request body, which
|
||||||
|
is followed by zero or one HEADERS containing trailer headers. The
|
||||||
|
request headers must include ":scheme", ":method" and ":path" pseudo
|
||||||
|
header fields unless ":method" is not "CONNECT". ":authority" is
|
||||||
|
optional, but nghttp2 requires either ":authority" or "Host" header
|
||||||
|
field must be present. If ":method" is "CONNECT", the request headers
|
||||||
|
must include ":method" and ":authority" and must omit ":scheme" and
|
||||||
|
":path".
|
||||||
|
|
||||||
|
For HTTP response, HTTP message starts with zero or more HEADERS
|
||||||
|
frames containing non-final response (status code 1xx). They are
|
||||||
|
followed by one HEADERS frame containing final response headers
|
||||||
|
(non-1xx). It is followed by zero or more DATA frames containing
|
||||||
|
response body, which is followed by zero or one HEADERS containing
|
||||||
|
trailer headers. The non-final and final response headers must
|
||||||
|
contain ":status" pseudo header field containing 3 digits only.
|
||||||
|
|
||||||
|
All request and response headers must include exactly one valid value
|
||||||
|
for each pseudo header field. Additionally nghttp2 requires all
|
||||||
|
request headers must not include more than one "Host" header field.
|
||||||
|
|
||||||
|
HTTP/2 prohibits connection-specific header fields. The following
|
||||||
|
header fields must not appear: "Connection", "Keep-Alive",
|
||||||
|
"Proxy-Connection", "Transfer-Encoding" and "Upgrade". Additionally,
|
||||||
|
"TE" header field must not include any value other than "trailers".
|
||||||
|
|
||||||
|
Each header field name and value must obey the field-name and
|
||||||
|
field-value production rules described in `RFC 7230, section
|
||||||
|
3.2. <https://tools.ietf.org/html/rfc7230#section-3.2>`_.
|
||||||
|
Additionally, all field name must be lower cased. While the pseudo
|
||||||
|
header fields must satisfy these rules, we just ignore illegal regular
|
||||||
|
headers (this means that these header fields are not passed to
|
||||||
|
application callback). This is because these illegal header fields
|
||||||
|
are floating around in existing internet and resetting stream just
|
||||||
|
because of this may break many web sites. This is especially true if
|
||||||
|
we forward to or translate from HTTP/1 traffic.
|
||||||
|
|
||||||
|
For "http" or "https" URIs, ":path" pseudo header fields must start
|
||||||
|
with "/". The only exception is OPTIONS request, in that case, "*" is
|
||||||
|
allowed in ":path" pseudo header field to represent system-wide
|
||||||
|
OPTIONS request.
|
||||||
|
|
||||||
|
With the above validations, nghttp2 library guarantees that header
|
||||||
|
field name passed to `nghttp2_on_header_callback()` is not empty.
|
||||||
|
Also required pseudo headers are all present and not empty.
|
||||||
|
|
||||||
|
nghttp2 enforces "Content-Length" validation as well. All request or
|
||||||
|
response headers must not contain more than one "Content-Length"
|
||||||
|
header field. If "Content-Length" header field is present, it must be
|
||||||
|
parsed as 64 bit signed integer. The sum of data length in the
|
||||||
|
following DATA frames must match with the number in "Content-Length"
|
||||||
|
header field if it is present (this does not include padding bytes).
|
||||||
|
|
||||||
|
Any deviation results in stream error of type PROTOCOL_ERROR. If
|
||||||
|
error is found in PUSH_PROMISE frame, stream error is raised against
|
||||||
|
promised stream.
|
||||||
@@ -21,7 +21,9 @@ unpacked::
|
|||||||
|
|
||||||
$ build/tools/make-standalone-toolchain.sh \
|
$ build/tools/make-standalone-toolchain.sh \
|
||||||
--install-dir=$ANDROID_HOME/toolchain \
|
--install-dir=$ANDROID_HOME/toolchain \
|
||||||
--toolchain=arm-linux-androideabi-4.8
|
--toolchain=arm-linux-androideabi-4.9 \
|
||||||
|
--llvm-version=3.5 \
|
||||||
|
--platform=android-16
|
||||||
|
|
||||||
The additional flag ``--system=linux-x86_64`` may be required if you
|
The additional flag ``--system=linux-x86_64`` may be required if you
|
||||||
are using x86_64 system.
|
are using x86_64 system.
|
||||||
@@ -29,13 +31,14 @@ are using x86_64 system.
|
|||||||
The platform level is not important here because we don't use Android
|
The platform level is not important here because we don't use Android
|
||||||
specific C/C++ API.
|
specific C/C++ API.
|
||||||
|
|
||||||
The dependent libraries, such as OpenSSL and libevent should be built
|
The dependent libraries, such as OpenSSL and libev should be built
|
||||||
with the toolchain and installed under ``$ANDROID_HOME/usr/local``.
|
with the toolchain and installed under ``$ANDROID_HOME/usr/local``.
|
||||||
We recommend to build these libraries as static library to make the
|
We recommend to build these libraries as static library to make the
|
||||||
deployment easier. libxml2 support is currently disabled.
|
deployment easier. libxml2 support is currently disabled.
|
||||||
|
|
||||||
We use zlib which comes with Android NDK, so we don't have to build it
|
Although zlib comes with Android NDK, it seems not to be a part of
|
||||||
by ourselves.
|
public API, so we have to built it for our own. That also provides us
|
||||||
|
proper .pc file as a bonus.
|
||||||
|
|
||||||
If SPDY support is required for nghttpx and h2load, build and install
|
If SPDY support is required for nghttpx and h2load, build and install
|
||||||
spdylay as well.
|
spdylay as well.
|
||||||
@@ -46,7 +49,9 @@ correct path. Also add ``$ANDROID_HOME/toolchain/bin`` to ``PATH``::
|
|||||||
|
|
||||||
$ export PATH=$PATH:$ANDROID_HOME/toolchain/bin
|
$ export PATH=$PATH:$ANDROID_HOME/toolchain/bin
|
||||||
|
|
||||||
To configure OpenSSL, use the following script::
|
To configure OpenSSL, use the following script:
|
||||||
|
|
||||||
|
.. code-block:: sh
|
||||||
|
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
@@ -63,7 +68,12 @@ To configure OpenSSL, use the following script::
|
|||||||
|
|
||||||
And run ``make install`` to build and install.
|
And run ``make install`` to build and install.
|
||||||
|
|
||||||
To configure libevent, use the following script::
|
We cannot compile libev without modification. Apply `this patch
|
||||||
|
<https://gist.github.com/tatsuhiro-t/48c45f08950f587180ed>`_ before
|
||||||
|
configuring libev. This patch is for libev-4.19. After applying the
|
||||||
|
patch, to configure libev, use the following script:
|
||||||
|
|
||||||
|
.. code-block:: sh
|
||||||
|
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
@@ -86,7 +96,40 @@ To configure libevent, use the following script::
|
|||||||
|
|
||||||
And run ``make install`` to build and install.
|
And run ``make install`` to build and install.
|
||||||
|
|
||||||
To configure spdylay, use the following script::
|
To configure zlib, use the following script:
|
||||||
|
|
||||||
|
.. code-block:: sh
|
||||||
|
|
||||||
|
#!/bin/sh -e
|
||||||
|
|
||||||
|
if [ -z "$ANDROID_HOME" ]; then
|
||||||
|
echo 'No $ANDROID_HOME specified.'
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
PREFIX=$ANDROID_HOME/usr/local
|
||||||
|
TOOLCHAIN=$ANDROID_HOME/toolchain
|
||||||
|
PATH=$TOOLCHAIN/bin:$PATH
|
||||||
|
|
||||||
|
HOST=arm-linux-androideabi
|
||||||
|
|
||||||
|
CC=$HOST-gcc \
|
||||||
|
AR=$HOST-ar \
|
||||||
|
LD=$HOST-ld \
|
||||||
|
RANLIB=$HOST-ranlib \
|
||||||
|
STRIP=$HOST-strip \
|
||||||
|
./configure \
|
||||||
|
--prefix=$PREFIX \
|
||||||
|
--libdir=$PREFIX/lib \
|
||||||
|
--includedir=$PREFIX/include \
|
||||||
|
--static
|
||||||
|
|
||||||
|
And run ``make install`` to build and install.
|
||||||
|
|
||||||
|
To configure spdylay, use the following script:
|
||||||
|
|
||||||
|
.. code-block:: sh
|
||||||
|
|
||||||
|
#!/bin/sh -e
|
||||||
|
|
||||||
if [ -z "$ANDROID_HOME" ]; then
|
if [ -z "$ANDROID_HOME" ]; then
|
||||||
echo 'No $ANDROID_HOME specified.'
|
echo 'No $ANDROID_HOME specified.'
|
||||||
@@ -108,11 +151,7 @@ To configure spdylay, use the following script::
|
|||||||
PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \
|
PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \
|
||||||
LDFLAGS="-L$PREFIX/lib"
|
LDFLAGS="-L$PREFIX/lib"
|
||||||
|
|
||||||
And run ``make install`` to build and install. After spdylay
|
And run ``make install`` to build and install.
|
||||||
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
|
After prerequisite libraries are prepared, run ``android-config`` and
|
||||||
then ``android-make`` to compile nghttp2 source files.
|
then ``android-make`` to compile nghttp2 source files.
|
||||||
|
|||||||
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.
|
||||||
@@ -50,8 +50,9 @@ Flow Control
|
|||||||
------------
|
------------
|
||||||
|
|
||||||
HTTP/2 and SPDY/3 or later employ flow control and it may affect
|
HTTP/2 and SPDY/3 or later employ flow control and it may affect
|
||||||
benchmarking results. To adjust receiver flow control window size,
|
benchmarking results. By default, h2load uses large enough flow
|
||||||
there is following options:
|
control window, which effectively disables flow control. To adjust
|
||||||
|
receiver flow control window size, there are following options:
|
||||||
|
|
||||||
``-w``
|
``-w``
|
||||||
Sets the stream level initial window size to
|
Sets the stream level initial window size to
|
||||||
@@ -86,5 +87,5 @@ If multiple URIs are specified, they are used in round robin manner.
|
|||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
Please note that h2load uses sheme, host and port in the first URI
|
Please note that h2load uses scheme, host and port in the first URI
|
||||||
and ignores those parts in the rest of the URIs.
|
and ignores those parts in the rest of the URIs.
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ Contents:
|
|||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
|
||||||
package_README
|
package_README
|
||||||
|
contribute
|
||||||
building-android-binary
|
building-android-binary
|
||||||
tutorial-client
|
tutorial-client
|
||||||
tutorial-server
|
tutorial-server
|
||||||
@@ -27,14 +28,18 @@ Contents:
|
|||||||
h2load.1
|
h2load.1
|
||||||
nghttpx-howto
|
nghttpx-howto
|
||||||
h2load-howto
|
h2load-howto
|
||||||
|
programmers-guide
|
||||||
apiref
|
apiref
|
||||||
libnghttp2_asio
|
libnghttp2_asio
|
||||||
python-apiref
|
python-apiref
|
||||||
nghttp2.h
|
nghttp2.h
|
||||||
nghttp2ver.h
|
nghttp2ver.h
|
||||||
|
asio_http2_server.h
|
||||||
|
asio_http2_client.h
|
||||||
asio_http2.h
|
asio_http2.h
|
||||||
Source <https://github.com/tatsuhiro-t/nghttp2>
|
Source <https://github.com/tatsuhiro-t/nghttp2>
|
||||||
Issues <https://github.com/tatsuhiro-t/nghttp2/issues>
|
Issues <https://github.com/tatsuhiro-t/nghttp2/issues>
|
||||||
|
nghttp2.org <https://nghttp2.org/>
|
||||||
|
|
||||||
Released Versions
|
Released Versions
|
||||||
=================
|
=================
|
||||||
@@ -44,6 +49,5 @@ https://github.com/tatsuhiro-t/nghttp2/releases
|
|||||||
Resources
|
Resources
|
||||||
---------
|
---------
|
||||||
|
|
||||||
* http://tools.ietf.org/html/draft-ietf-httpbis-http2-14
|
* HTTP/2 https://tools.ietf.org/html/rfc7540
|
||||||
* http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-09
|
* HPACK https://tools.ietf.org/html/rfc7541
|
||||||
* http://tools.ietf.org/html/draft-ietf-httpbis-alt-svc-04
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ libnghttp2_asio: High level HTTP/2 C++ library
|
|||||||
libnghttp2_asio is C++ library built on top of libnghttp2 and provides
|
libnghttp2_asio is C++ library built on top of libnghttp2 and provides
|
||||||
high level abstraction API to build HTTP/2 applications. It depends
|
high level abstraction API to build HTTP/2 applications. It depends
|
||||||
on Boost::ASIO library and OpenSSL. Currently libnghttp2_asio
|
on Boost::ASIO library and OpenSSL. Currently libnghttp2_asio
|
||||||
provides server side API.
|
provides server and client side API.
|
||||||
|
|
||||||
libnghttp2_asio is not built by default. Use ``--enable-asio-lib``
|
libnghttp2_asio is not built by default. Use ``--enable-asio-lib``
|
||||||
configure flag to build libnghttp2_asio. The required Boost libraries
|
configure flag to build libnghttp2_asio. The required Boost libraries
|
||||||
@@ -14,46 +14,64 @@ are:
|
|||||||
* Boost::System
|
* Boost::System
|
||||||
* Boost::Thread
|
* Boost::Thread
|
||||||
|
|
||||||
To use libnghttp2_asio, first include following header file:
|
We have 3 header files for this library:
|
||||||
|
|
||||||
.. code-block:: cpp
|
* :doc:`asio_http2_server.h`
|
||||||
|
* :doc:`asio_http2_client.h`
|
||||||
|
* :doc:`asio_http2.h`
|
||||||
|
|
||||||
#include <nghttp2/asio_http2.h>
|
asio_http2.h is included from the other two files.
|
||||||
|
|
||||||
Also take a look at that header file :doc:`asio_http2.h`.
|
To build a program with libnghttp2_asio, link to the following
|
||||||
|
libraries::
|
||||||
|
|
||||||
|
-lnghttp2_asio -lboost_system
|
||||||
|
|
||||||
|
If ``boost::asio::ssl`` is used in application code, OpenSSL is also
|
||||||
|
required in link line::
|
||||||
|
|
||||||
|
-lnghttp2_asio -lboost_system -lssl -lcrypto
|
||||||
|
|
||||||
Server API
|
Server API
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
To use server API, first include following header file:
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
#include <nghttp2/asio_http2_server.h>
|
||||||
|
|
||||||
|
Also take a look at that header file :doc:`asio_http2_server.h`.
|
||||||
|
|
||||||
Server API is designed to build HTTP/2 server very easily to utilize
|
Server API is designed to build HTTP/2 server very easily to utilize
|
||||||
C++11 anonymous function and closure. The bare minimum example of
|
C++11 anonymous function and closure. The bare minimum example of
|
||||||
HTTP/2 server looks like this:
|
HTTP/2 server looks like this:
|
||||||
|
|
||||||
.. code-block:: cpp
|
.. code-block:: cpp
|
||||||
|
|
||||||
#include <nghttp2/asio_http2.h>
|
|
||||||
|
|
||||||
using namespace nghttp2::asio_http2;
|
using namespace nghttp2::asio_http2;
|
||||||
using namespace nghttp2::asio_http2::server;
|
using namespace nghttp2::asio_http2::server;
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char *argv[]) {
|
||||||
{
|
boost::system::error_code ec;
|
||||||
http2 server;
|
http2 server;
|
||||||
|
|
||||||
server.listen
|
server.handle("/", [](const request &req, const response &res) {
|
||||||
("*", 3000,
|
res.write_head(200);
|
||||||
[](const std::shared_ptr<request>& req,
|
res.end("hello, world\n");
|
||||||
const std::shared_ptr<response>& res)
|
});
|
||||||
{
|
|
||||||
res->write_head(200);
|
if (server.listen_and_serve(ec, "localhost", "3000")) {
|
||||||
res->end("hello, world");
|
std::cerr << "error: " << ec.message() << std::endl;
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
First we instantiate ``nghttp2::asio_http2::server::http2`` object.
|
First we instantiate ``nghttp2::asio_http2::server::http2`` object.
|
||||||
Then call ``nghttp2::asio_http2::server::http2::listen`` function with
|
``nghttp2::asio_http2::server::http2::handle`` function registers
|
||||||
address and port to listen to and callback function, namely "request
|
pattern and its handler function. In this example, we register "/" as
|
||||||
callback", invoked when request arrives.
|
pattern, which matches all requests. Then call
|
||||||
|
``nghttp2::asio_http2::server::http2::listen_and_serve`` function with
|
||||||
|
address and port to listen to.
|
||||||
|
|
||||||
The ``req`` and ``res`` represent HTTP request and response
|
The ``req`` and ``res`` represent HTTP request and response
|
||||||
respectively. ``nghttp2::asio_http2_::server::response::write_head``
|
respectively. ``nghttp2::asio_http2_::server::response::write_head``
|
||||||
@@ -62,9 +80,13 @@ status code, in the above example, which is 200. The second argument,
|
|||||||
which is omitted in the above example, is additional header fields to
|
which is omitted in the above example, is additional header fields to
|
||||||
send.
|
send.
|
||||||
|
|
||||||
``nghttp2::asio_http2::server::response::end`` sends responde body.
|
``nghttp2::asio_http2::server::response::end`` sends response body.
|
||||||
In the above example, we send string "hello, world".
|
In the above example, we send string "hello, world".
|
||||||
|
|
||||||
|
The life time of req and res object ends after the callback set by
|
||||||
|
``nghttp2::asio_http2::server::response::on_close`` function.
|
||||||
|
Application must not use those objects after this call.
|
||||||
|
|
||||||
Serving static files and enabling SSL/TLS
|
Serving static files and enabling SSL/TLS
|
||||||
+++++++++++++++++++++++++++++++++++++++++
|
+++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
@@ -73,44 +95,47 @@ SSL/TLS.
|
|||||||
|
|
||||||
.. code-block:: cpp
|
.. code-block:: cpp
|
||||||
|
|
||||||
#include <nghttp2/asio_http2.h>
|
#include <nghttp2/asio_http2_server.h>
|
||||||
|
|
||||||
using namespace nghttp2::asio_http2;
|
using namespace nghttp2::asio_http2;
|
||||||
using namespace nghttp2::asio_http2::server;
|
using namespace nghttp2::asio_http2::server;
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char *argv[]) {
|
||||||
{
|
boost::system::error_code ec;
|
||||||
|
boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23);
|
||||||
|
|
||||||
|
tls.use_private_key_file("server.key", boost::asio::ssl::context::pem);
|
||||||
|
tls.use_certificate_chain_file("server.crt");
|
||||||
|
|
||||||
|
configure_tls_context_easy(ec, tls);
|
||||||
|
|
||||||
http2 server;
|
http2 server;
|
||||||
|
|
||||||
server.tls("server.key", "server.crt");
|
server.handle("/index.html", [](const request &req, const response &res) {
|
||||||
|
res.write_head(200);
|
||||||
|
res.end(file_generator("index.html"));
|
||||||
|
});
|
||||||
|
|
||||||
server.listen
|
if (server.listen_and_serve(ec, tls, "localhost", "3000")) {
|
||||||
("*", 3000,
|
std::cerr << "error: " << ec.message() << std::endl;
|
||||||
[](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
|
We first create ``boost::asio::ssl::context`` object and set path to
|
||||||
``nghttp2::asio_http2::server::http2::tls`` will enable SSL/TLS. Both
|
private key file and certificate file.
|
||||||
files must be in PEM format.
|
``nghttp2::asio_http2::server::configure_tls_context_easy`` function
|
||||||
|
configures SSL/TLS context object for HTTP/2 server use, including NPN
|
||||||
|
callbacks.
|
||||||
|
|
||||||
In the above example, if request path is either "/" or "/index.html",
|
In the above example, if request path is "/index.html", we serve
|
||||||
we serve index.html file in the current working directory.
|
index.html file in the current working directory.
|
||||||
``nghttp2::asio_http2::server::response::end`` has overload to take
|
``nghttp2::asio_http2::server::response::end`` has overload to take
|
||||||
function of type ``nghttp2::asio_http2::read_cb`` and application pass
|
function of type ``nghttp2::asio_http2::generator_cb`` and application
|
||||||
its implementation to generate response body. For the convenience,
|
pass its implementation to generate response body. For the
|
||||||
libnghttp2_asio library provides ``nghttp2::asio_http2::file_reader``
|
convenience, libnghttp2_asio library provides
|
||||||
function to generate function to server static file.
|
``nghttp2::asio_http2::file_generator`` function to generate function
|
||||||
|
to server static file. If other resource is requested, server
|
||||||
|
automatically responds with 404 status code.
|
||||||
|
|
||||||
Server push
|
Server push
|
||||||
+++++++++++
|
+++++++++++
|
||||||
@@ -119,48 +144,56 @@ Server push is also supported.
|
|||||||
|
|
||||||
.. code-block:: cpp
|
.. code-block:: cpp
|
||||||
|
|
||||||
#include <nghttp2/asio_http2.h>
|
#include <nghttp2/asio_http2_server.h>
|
||||||
|
|
||||||
using namespace nghttp2::asio_http2;
|
using namespace nghttp2::asio_http2;
|
||||||
using namespace nghttp2::asio_http2::server;
|
using namespace nghttp2::asio_http2::server;
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char *argv[]) {
|
||||||
{
|
boost::system::error_code ec;
|
||||||
|
boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23);
|
||||||
|
|
||||||
|
tls.use_private_key_file("server.key", boost::asio::ssl::context::pem);
|
||||||
|
tls.use_certificate_chain_file("server.crt");
|
||||||
|
|
||||||
|
configure_tls_context_easy(ec, tls);
|
||||||
|
|
||||||
http2 server;
|
http2 server;
|
||||||
|
|
||||||
server.tls("server.key", "server.crt");
|
std::string style_css = "h1 { color: green; }";
|
||||||
|
|
||||||
server.listen
|
server.handle("/", [&style_css](const request &req, const response &res) {
|
||||||
("*", 3000,
|
boost::system::error_code ec;
|
||||||
[](const std::shared_ptr<request>& req,
|
auto push = res.push(ec, "GET", "/style.css");
|
||||||
const std::shared_ptr<response>& res)
|
push->write_head(200);
|
||||||
{
|
push->end(style_css);
|
||||||
if(req->path() == "/") {
|
|
||||||
req->push("GET", "/my.css");
|
|
||||||
|
|
||||||
res->write_head(200);
|
res.write_head(200);
|
||||||
res->end(file_reader("index.html"));
|
res.end(R"(
|
||||||
|
<!DOCTYPE html><html lang="en">
|
||||||
|
<title>HTTP/2 FTW</title><body>
|
||||||
|
<link href="/style.css" rel="stylesheet" type="text/css">
|
||||||
|
<h1>This should be green</h1>
|
||||||
|
</body></html>
|
||||||
|
)");
|
||||||
|
});
|
||||||
|
|
||||||
return;
|
server.handle("/style.css",
|
||||||
}
|
[&style_css](const request &req, const response &res) {
|
||||||
|
res.write_head(200);
|
||||||
|
res.end(style_css);
|
||||||
|
});
|
||||||
|
|
||||||
if(req->path() == "/my.css") {
|
if (server.listen_and_serve(ec, tls, "localhost", "3000")) {
|
||||||
res->write_head(200);
|
std::cerr << "error: " << ec.message() << std::endl;
|
||||||
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
|
When client requested any resource other than "/style.css", we push
|
||||||
``nghttp2::asio_http2::server::request::push`` function with desired
|
"/style.css". To push resource, call
|
||||||
method and path. Later, the callback will be called with the pushed
|
``nghttp2::asio_http2::server::response::push`` function with desired
|
||||||
resource "/my.css".
|
method and path. It returns another response object and use its
|
||||||
|
functions to send push response.
|
||||||
|
|
||||||
Enable multi-threading
|
Enable multi-threading
|
||||||
++++++++++++++++++++++
|
++++++++++++++++++++++
|
||||||
@@ -176,73 +209,225 @@ desired number of threads:
|
|||||||
// Use 4 native threads
|
// Use 4 native threads
|
||||||
server.num_threads(4);
|
server.num_threads(4);
|
||||||
|
|
||||||
Run blocking tasks in background thread
|
Client API
|
||||||
+++++++++++++++++++++++++++++++++++++++
|
----------
|
||||||
|
|
||||||
The request callback is called in the same thread where HTTP request
|
To use client API, first include following header file:
|
||||||
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
|
.. code-block:: cpp
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <nghttp2/asio_http2_client.h>
|
||||||
#include <nghttp2/asio_http2.h>
|
|
||||||
|
Also take a look at that header file :doc:`asio_http2_client.h`.
|
||||||
|
|
||||||
|
Here is the sample client code to access HTTP/2 server and print out
|
||||||
|
response header fields and response body to the console screen:
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <nghttp2/asio_http2_client.h>
|
||||||
|
|
||||||
|
using boost::asio::ip::tcp;
|
||||||
|
|
||||||
using namespace nghttp2::asio_http2;
|
using namespace nghttp2::asio_http2;
|
||||||
using namespace nghttp2::asio_http2::server;
|
using namespace nghttp2::asio_http2::client;
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char *argv[]) {
|
||||||
{
|
boost::system::error_code ec;
|
||||||
http2 server;
|
boost::asio::io_service io_service;
|
||||||
|
|
||||||
server.num_concurrent_tasks(16);
|
// connect to localhost:3000
|
||||||
|
session sess(io_service, "localhost", "3000");
|
||||||
|
|
||||||
server.listen
|
sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) {
|
||||||
("*", 3000,
|
boost::system::error_code ec;
|
||||||
[](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
|
auto req = sess.submit(ec, "GET", "http://localhost:3000/");
|
||||||
// value is safe because it is std::shared_ptr.
|
|
||||||
|
|
||||||
sleep(1);
|
req->on_response([](const response &res) {
|
||||||
|
// print status code and response header fields.
|
||||||
|
std::cerr << "HTTP/2 " << res.status_code() << std::endl;
|
||||||
|
for (auto &kv : res.header()) {
|
||||||
|
std::cerr << kv.first << ": " << kv.second.value << "\n";
|
||||||
|
}
|
||||||
|
std::cerr << std::endl;
|
||||||
|
|
||||||
channel.post
|
res.on_data([](const uint8_t *data, std::size_t len) {
|
||||||
([res]()
|
std::cerr.write(reinterpret_cast<const char *>(data), len);
|
||||||
{
|
std::cerr << std::endl;
|
||||||
// executed in the same thread where request callback
|
});
|
||||||
// was executed.
|
});
|
||||||
res->write_head(200);
|
|
||||||
res->end("hello, world");
|
req->on_close([&sess](uint32_t error_code) {
|
||||||
});
|
// shutdown session after first request was done.
|
||||||
});
|
sess.shutdown();
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
sess.on_error([](const boost::system::error_code &ec) {
|
||||||
|
std::cerr << "error: " << ec.message() << std::endl;
|
||||||
|
});
|
||||||
|
|
||||||
|
io_service.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
First we set the number of background threads which run tasks. By
|
``nghttp2::asio_http2::client::session`` object takes
|
||||||
default it is set to 1. In this example, we set it to 16, so at most
|
``boost::asio::io_service`` object and remote server address. When
|
||||||
16 tasks can be executed concurrently without blocking handling new
|
connection is made, the callback function passed to
|
||||||
requests.
|
``nghttp2::asio_http2::client::on_connect`` is invoked with connected
|
||||||
|
address as its parameter. After this callback call, use
|
||||||
|
``nghttp2::asio_http2::session::submit`` to send request to the
|
||||||
|
server. You can submit multiple requests at once without waiting for
|
||||||
|
the completion of previous request.
|
||||||
|
|
||||||
We call ``req->run_task()`` to execute task in background thread. In
|
The life time of req and res object ends after the callback set by
|
||||||
the passed callback, we just simply sleeps 1 second. After sleep is
|
``nghttp2::asio_http2::server::request::on_close`` function.
|
||||||
over, we schedule another callback to send response to the client.
|
Application must not use those objects after this call.
|
||||||
Since the callback passed to ``req->run_task()`` is executed in the
|
|
||||||
different thread from the thread where request callback is called,
|
Normally, client does not stop even after all requests are done unless
|
||||||
using ``req`` or ``res`` object directly there may cause undefined
|
connection is lost. To stop client, call
|
||||||
behaviour. To avoid this issue, we can use
|
``nghttp2::asio_http2::server::session::shutdown()``.
|
||||||
``nghttp2::asio_http2::channel::post`` by supplying a callback which
|
|
||||||
in turn get called in the same thread where request callback was
|
Recieve server push and enable SSL/TLS
|
||||||
called.
|
++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <nghttp2/asio_http2_client.h>
|
||||||
|
|
||||||
|
using boost::asio::ip::tcp;
|
||||||
|
|
||||||
|
using namespace nghttp2::asio_http2;
|
||||||
|
using namespace nghttp2::asio_http2::client;
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
boost::system::error_code ec;
|
||||||
|
boost::asio::io_service io_service;
|
||||||
|
|
||||||
|
boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23);
|
||||||
|
tls.set_default_verify_paths();
|
||||||
|
// disabled to make development easier...
|
||||||
|
// tls_ctx.set_verify_mode(boost::asio::ssl::verify_peer);
|
||||||
|
configure_tls_context(ec, tls);
|
||||||
|
|
||||||
|
// connect to localhost:3000
|
||||||
|
session sess(io_service, tls, "localhost", "3000");
|
||||||
|
|
||||||
|
sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) {
|
||||||
|
boost::system::error_code ec;
|
||||||
|
|
||||||
|
auto req = sess.submit(ec, "GET", "http://localhost:3000/");
|
||||||
|
|
||||||
|
req->on_response([&sess](const response &res) {
|
||||||
|
std::cerr << "response received!" << std::endl;
|
||||||
|
res.on_data([&sess](const uint8_t *data, std::size_t len) {
|
||||||
|
std::cerr.write(reinterpret_cast<const char *>(data), len);
|
||||||
|
std::cerr << std::endl;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
req->on_push([](const request &push) {
|
||||||
|
std::cerr << "push request received!" << std::endl;
|
||||||
|
push.on_response([](const response &res) {
|
||||||
|
std::cerr << "push response received!" << std::endl;
|
||||||
|
res.on_data([](const uint8_t *data, std::size_t len) {
|
||||||
|
std::cerr.write(reinterpret_cast<const char *>(data), len);
|
||||||
|
std::cerr << std::endl;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
sess.on_error([](const boost::system::error_code &ec) {
|
||||||
|
std::cerr << "error: " << ec.message() << std::endl;
|
||||||
|
});
|
||||||
|
|
||||||
|
io_service.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
The above sample code demonstrates how to enable SSL/TLS and receive
|
||||||
|
server push. Currently,
|
||||||
|
``nghttp2::asio_http2::client::configure_tls_context`` function setups
|
||||||
|
NPN callbacks for SSL/TLS context for HTTP/2 use.
|
||||||
|
|
||||||
|
To receive server push, use
|
||||||
|
``nghttp2::asio_http2::client::request::on_push`` function to set
|
||||||
|
callback function which is invoked when server push request is
|
||||||
|
arrived. The callback function takes
|
||||||
|
``nghttp2::asio_http2::client::request`` object, which contains the
|
||||||
|
pushed request. To get server push response, set callback using
|
||||||
|
``nghttp2::asio_http2::client::request::on_response``.
|
||||||
|
|
||||||
|
As stated in the previous section, client does not stop automatically
|
||||||
|
as long as HTTP/2 session is fine and connection is alive. We don't
|
||||||
|
call ``nghttp2::asio_http2::client::session::shutdown`` in this
|
||||||
|
example, so the program does not terminate after all responses are
|
||||||
|
received. Hit Ctrl-C to terminate the program.
|
||||||
|
|
||||||
|
Multiple concurrent requests
|
||||||
|
++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <nghttp2/asio_http2_client.h>
|
||||||
|
|
||||||
|
using boost::asio::ip::tcp;
|
||||||
|
|
||||||
|
using namespace nghttp2::asio_http2;
|
||||||
|
using namespace nghttp2::asio_http2::client;
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
boost::system::error_code ec;
|
||||||
|
boost::asio::io_service io_service;
|
||||||
|
|
||||||
|
// connect to localhost:3000
|
||||||
|
session sess(io_service, "localhost", "3000");
|
||||||
|
|
||||||
|
sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) {
|
||||||
|
boost::system::error_code ec;
|
||||||
|
|
||||||
|
auto printer = [](const response &res) {
|
||||||
|
res.on_data([](const uint8_t *data, std::size_t len) {
|
||||||
|
std::cerr.write(reinterpret_cast<const char *>(data), len);
|
||||||
|
std::cerr << std::endl;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
std::size_t num = 3;
|
||||||
|
auto count = std::make_shared<int>(num);
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < num; ++i) {
|
||||||
|
auto req = sess.submit(ec, "GET",
|
||||||
|
"http://localhost:3000/" + std::to_string(i + 1));
|
||||||
|
|
||||||
|
req->on_response(printer);
|
||||||
|
req->on_close([&sess, count](uint32_t error_code) {
|
||||||
|
if (--*count == 0) {
|
||||||
|
// shutdown session after |num| requests were done.
|
||||||
|
sess.shutdown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
sess.on_error([](const boost::system::error_code &ec) {
|
||||||
|
std::cerr << "error: " << ec.message() << std::endl;
|
||||||
|
});
|
||||||
|
|
||||||
|
io_service.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
Here is the sample to send 3 requests at once. Depending on the
|
||||||
|
server settings, these requests are processed out-of-order. In this
|
||||||
|
example, we have a trick to shutdown session after all requests were
|
||||||
|
done. We made ``count`` object which is shared pointer to int and is
|
||||||
|
initialized to 3. On each request closure (the invocation of the
|
||||||
|
callback set by ``nghttp2::asio_http2::client::request::on_close``),
|
||||||
|
we decrement the count. If count becomes 0, we are sure that all
|
||||||
|
requests have been done and initiate shutdown.
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ SSL/TLS, the frontend also supports SPDY protocol.
|
|||||||
By default, this mode's frontend connection is encrypted using
|
By default, this mode's frontend connection is encrypted using
|
||||||
SSL/TLS. So server's private key and certificate must be supplied to
|
SSL/TLS. So server's private key and certificate must be supplied to
|
||||||
the command line (or through configuration file). In this case, the
|
the command line (or through configuration file). In this case, the
|
||||||
fontend protocol selection will is done via ALPN or NPN.
|
frontend protocol selection will is done via ALPN or NPN.
|
||||||
|
|
||||||
With ``--frontend-no-tls`` option, user can turn off SSL/TLS in
|
With ``--frontend-no-tls`` option, user can turn off SSL/TLS in
|
||||||
frontend connection. In this case, SPDY protocol is not available
|
frontend connection. In this case, SPDY protocol is not available
|
||||||
@@ -52,15 +52,15 @@ like forward proxy and assumes the backend is HTTP/1 proxy server
|
|||||||
(e.g., squid, traffic server). So HTTP/1 request must include
|
(e.g., squid, traffic server). So HTTP/1 request must include
|
||||||
absolute URI in request line.
|
absolute URI in request line.
|
||||||
|
|
||||||
By default, frontend connection is encrypted, this mode is also called
|
By default, frontend connection is encrypted. So this mode is also
|
||||||
secure proxy. If nghttpx is linked with spdylay, it supports SPDY
|
called secure proxy. If nghttpx is linked with spdylay, it supports
|
||||||
protocols and it works as so called SPDY proxy.
|
SPDY protocols and it works as so called SPDY proxy.
|
||||||
|
|
||||||
With ``--frontend-no-tls`` option, SSL/TLS is turned off in frontend
|
With ``--frontend-no-tls`` option, SSL/TLS is turned off in frontend
|
||||||
connection, so the connection gets insecure.
|
connection, so the connection gets insecure.
|
||||||
|
|
||||||
The backend must be HTTP/1 proxy server. nghttpx only supports 1
|
The backend must be HTTP/1 proxy server. nghttpx supports multiple
|
||||||
backend server address. It translates incoming requests to HTTP/1
|
backend server addresses. It translates incoming requests to HTTP/1
|
||||||
request to backend server. The backend server performs real proxy
|
request to backend server. The backend server performs real proxy
|
||||||
work for each request, for example, dispatching requests to the origin
|
work for each request, for example, dispatching requests to the origin
|
||||||
server and caching contents.
|
server and caching contents.
|
||||||
@@ -87,7 +87,7 @@ proxy, user has to create proxy.pac script file like this:
|
|||||||
|
|
||||||
``SERVERADDR`` and ``PORT`` is the hostname/address and port of the
|
``SERVERADDR`` and ``PORT`` is the hostname/address and port of the
|
||||||
machine nghttpx is running. Please note that both Firefox nightly and
|
machine nghttpx is running. Please note that both Firefox nightly and
|
||||||
Chromium requires valid certificate for secure proxy.
|
Chromium require valid certificate for secure proxy.
|
||||||
|
|
||||||
For Firefox nightly, open Preference window and select Advanced then
|
For Firefox nightly, open Preference window and select Advanced then
|
||||||
click Network tab. Clicking Connection Settings button will show the
|
click Network tab. Clicking Connection Settings button will show the
|
||||||
@@ -100,9 +100,9 @@ For Chromium, use following command-line::
|
|||||||
|
|
||||||
$ google-chrome --proxy-pac-url=file:///path/to/proxy.pac --use-npn
|
$ google-chrome --proxy-pac-url=file:///path/to/proxy.pac --use-npn
|
||||||
|
|
||||||
Squid may work as out-of-box. Traffic server requires to be
|
As HTTP/1 proxy server, Squid may work as out-of-box. Traffic server
|
||||||
configured as forward proxy. Here is the minimum configuration items
|
requires to be configured as forward proxy. Here is the minimum
|
||||||
to edit::
|
configuration items to edit::
|
||||||
|
|
||||||
CONFIG proxy.config.reverse_proxy.enabled INT 0
|
CONFIG proxy.config.reverse_proxy.enabled INT 0
|
||||||
CONFIG proxy.config.url_remap.remap_required INT 0
|
CONFIG proxy.config.url_remap.remap_required INT 0
|
||||||
@@ -124,7 +124,9 @@ HTTP/1 frontend connection can be upgraded to HTTP/2 using HTTP
|
|||||||
Upgrade. To disable SSL/TLS in backend connection, use
|
Upgrade. To disable SSL/TLS in backend connection, use
|
||||||
``--backend-no-tls`` option.
|
``--backend-no-tls`` option.
|
||||||
|
|
||||||
The backend connection is created one per worker (thread).
|
By default, the number of backend HTTP/2 connections per worker
|
||||||
|
(thread) is determined by number of ``-b`` option. To adjust this
|
||||||
|
value, use ``--backend-http2-connections-per-worker`` option.
|
||||||
|
|
||||||
The backend server is supporsed to be a HTTP/2 web server (e.g.,
|
The backend server is supporsed to be a HTTP/2 web server (e.g.,
|
||||||
nghttpd). The one use-case of this mode is utilize existing HTTP/1
|
nghttpd). The one use-case of this mode is utilize existing HTTP/1
|
||||||
@@ -156,7 +158,9 @@ HTTP/1 frontend connection can be upgraded to HTTP/2 using HTTP
|
|||||||
Upgrade. To disable SSL/TLS in backend connection, use
|
Upgrade. To disable SSL/TLS in backend connection, use
|
||||||
``--backend-no-tls`` option.
|
``--backend-no-tls`` option.
|
||||||
|
|
||||||
The backend connection is created one per worker (thread).
|
By default, the number of backend HTTP/2 connections per worker
|
||||||
|
(thread) is determined by number of ``-b`` option. To adjust this
|
||||||
|
value, use ``--backend-http2-connections-per-worker`` option.
|
||||||
|
|
||||||
The backend server must be a HTTP/2 proxy. You can use nghttpx in
|
The backend server must be a HTTP/2 proxy. You can use nghttpx in
|
||||||
`HTTP/2 proxy mode`_ as backend server. The one use-case of this mode
|
`HTTP/2 proxy mode`_ as backend server. The one use-case of this mode
|
||||||
@@ -196,10 +200,14 @@ With ``--frontend-no-tls`` option, SSL/TLS is turned off in frontend
|
|||||||
connection, so the connection gets insecure. To disable SSL/TLS in
|
connection, so the connection gets insecure. To disable SSL/TLS in
|
||||||
backend connection, use ``--backend-no-tls`` option.
|
backend connection, use ``--backend-no-tls`` option.
|
||||||
|
|
||||||
|
By default, the number of backend HTTP/2 connections per worker
|
||||||
|
(thread) is determined by number of ``-b`` option. To adjust this
|
||||||
|
value, use ``--backend-http2-connections-per-worker`` option.
|
||||||
|
|
||||||
The backend server is supporsed to be a HTTP/2 web server or HTTP/2
|
The backend server is supporsed to be a HTTP/2 web server or HTTP/2
|
||||||
proxy. If backend server is HTTP/2 proxy, use
|
proxy. If backend server is HTTP/2 proxy, use
|
||||||
``--no-location-rewrite`` option to disable rewriting location header
|
``--no-location-rewrite`` and ``--no-host-rewrite`` options to disable
|
||||||
field.
|
rewriting location, host and :authority header field.
|
||||||
|
|
||||||
The use-case of this mode is aggregate the incoming connections to one
|
The use-case of this mode is aggregate the incoming connections to one
|
||||||
HTTP/2 connection. One backend HTTP/2 connection is created per
|
HTTP/2 connection. One backend HTTP/2 connection is created per
|
||||||
@@ -235,12 +243,12 @@ Read/write rate limit
|
|||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
nghttpx supports transfer rate limiting on frontend connections. You
|
nghttpx supports transfer rate limiting on frontend connections. You
|
||||||
can do rate limit per worker (thread) for reading and writeing
|
can do rate limit per frontend connection for reading and writing
|
||||||
individually.
|
individually.
|
||||||
|
|
||||||
To rate limit per worker (thread), use ``--worker-read-rate`` and
|
To perform rate limit for reading, use ``--read-rate`` and
|
||||||
``--worker-read-burst`` options. For writing, use
|
``--read-burst`` options. For writing, use ``--write-rate`` and
|
||||||
``--worker-write-rate`` and ``--worker-write-burst``.
|
``--write-burst``.
|
||||||
|
|
||||||
Please note that rate limit is performed on top of TCP and nothing to
|
Please note that rate limit is performed on top of TCP and nothing to
|
||||||
do with HTTP/2 flow control.
|
do with HTTP/2 flow control.
|
||||||
@@ -263,10 +271,10 @@ precedence. If the above conditions are not met with the host value
|
|||||||
in :authority header field, rewrite is retried with the value in host
|
in :authority header field, rewrite is retried with the value in host
|
||||||
header field.
|
header field.
|
||||||
|
|
||||||
Hot deploy
|
Hot swapping
|
||||||
----------
|
------------
|
||||||
|
|
||||||
nghttpx supports hot deploy feature using signals. The hot deploy in
|
nghttpx supports hot swapping using signals. The hot swapping in
|
||||||
nghttpx is multi step process. First send USR2 signal to nghttpx
|
nghttpx is multi step process. First send USR2 signal to nghttpx
|
||||||
process. It will do fork and execute new executable, using same
|
process. It will do fork and execute new executable, using same
|
||||||
command-line arguments and environment variables. At this point, both
|
command-line arguments and environment variables. At this point, both
|
||||||
@@ -284,3 +292,12 @@ log rotation daemon renamed existing log files. To tell nghttpx to
|
|||||||
re-open log files, send USR1 signal to nghttpx process. It will
|
re-open log files, send USR1 signal to nghttpx process. It will
|
||||||
re-open files specified by ``--accesslog-file`` and
|
re-open files specified by ``--accesslog-file`` and
|
||||||
``--errorlog-file`` options.
|
``--errorlog-file`` options.
|
||||||
|
|
||||||
|
Multiple backend addresses
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
nghttpx supports multiple 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``. For HTTP/2 backend, see also
|
||||||
|
``--backend-http2-connections-per-worker`` option.
|
||||||
|
|||||||
@@ -220,6 +220,10 @@ HTTP/2 servers
|
|||||||
|
|
||||||
This is a value of ``:path`` header field.
|
This is a value of ``:path`` header field.
|
||||||
|
|
||||||
|
.. py:attribute:: headers
|
||||||
|
|
||||||
|
Request header fields.
|
||||||
|
|
||||||
A :py:class:`BaseRequestHandler` has the following methods:
|
A :py:class:`BaseRequestHandler` has the following methods:
|
||||||
|
|
||||||
.. py:method:: on_headers()
|
.. py:method:: on_headers()
|
||||||
@@ -249,10 +253,28 @@ HTTP/2 servers
|
|||||||
Send response. The *status* is HTTP status code. The *headers*
|
Send response. The *status* is HTTP status code. The *headers*
|
||||||
is additional response headers. The *:status* header field will
|
is additional response headers. The *:status* header field will
|
||||||
be appended by the library. The *body* is the response body.
|
be appended by the library. The *body* is the response body.
|
||||||
It could be ``None`` if response body is empty. Or it must be
|
It could be ``None`` if response body is empty. Or it must be
|
||||||
instance of either ``str``, ``bytes`` or :py:class:`io.IOBase`.
|
instance of either ``str``, ``bytes``, :py:class:`io.IOBase` or
|
||||||
If instance of ``str`` is specified, it will be encoded using
|
callable, called body generator, which takes one parameter,
|
||||||
UTF-8.
|
size. The body generator generates response body. It can pause
|
||||||
|
generation of response so that it can wait for slow backend data
|
||||||
|
generation. When invoked, it should return tuple, byte string
|
||||||
|
at most size length and flag. The flag is either
|
||||||
|
:py:data:`DATA_OK`, :py:data:`DATA_EOF` or
|
||||||
|
:py:data:`DATA_DEFERRED`. For non-empty byte string and it is
|
||||||
|
not the last chunk of response, :py:data:`DATA_OK` must be
|
||||||
|
returned as flag. If this is the last chunk of the response
|
||||||
|
(byte string could be ``None``), :py:data:`DATA_EOF` must be
|
||||||
|
returned as flag. If there is no data available right now, but
|
||||||
|
additional data are anticipated, return tuple (``None``,
|
||||||
|
:py:data:`DATA_DEFERRED`). When data arrived, call
|
||||||
|
:py:meth:`resume()` and restart response body transmission.
|
||||||
|
|
||||||
|
Only the body generator can pause response body generation;
|
||||||
|
instance of :py:class:`io.IOBase` must not block.
|
||||||
|
|
||||||
|
If instance of ``str`` is specified as *body*, it will be
|
||||||
|
encoded using UTF-8.
|
||||||
|
|
||||||
The *headers* is a list of tuple of the form ``(name,
|
The *headers* is a list of tuple of the form ``(name,
|
||||||
value)``. The ``name`` and ``value`` can be either byte string
|
value)``. The ``name`` and ``value`` can be either byte string
|
||||||
@@ -273,10 +295,8 @@ HTTP/2 servers
|
|||||||
|
|
||||||
The *status* is HTTP status code. The *headers* is additional
|
The *status* is HTTP status code. The *headers* is additional
|
||||||
response headers. The ``:status`` header field is appended by
|
response headers. The ``:status`` header field is appended by
|
||||||
the library. The *body* is the response body. It could be
|
the library. The *body* is the response body. It has the same
|
||||||
``None`` if response body is empty. Or it must be instance of
|
semantics of *body* parameter of :py:meth:`send_response()`.
|
||||||
either ``str``, ``bytes`` or ``io.IOBase``. If instance of
|
|
||||||
``str`` is specified, it is encoded using UTF-8.
|
|
||||||
|
|
||||||
The headers and request_headers are a list of tuple of the form
|
The headers and request_headers are a list of tuple of the form
|
||||||
``(name, value)``. The ``name`` and ``value`` can be either byte
|
``(name, value)``. The ``name`` and ``value`` can be either byte
|
||||||
@@ -288,6 +308,27 @@ HTTP/2 servers
|
|||||||
|
|
||||||
Raises the exception if any error occurs.
|
Raises the exception if any error occurs.
|
||||||
|
|
||||||
|
.. py:method:: resume()
|
||||||
|
|
||||||
|
Signals the restarting of response body transmission paused by
|
||||||
|
``DATA_DEFERRED`` from the body generator (see
|
||||||
|
:py:meth:`send_response()` about the body generator). It is not
|
||||||
|
an error calling this method while response body transmission is
|
||||||
|
not paused.
|
||||||
|
|
||||||
|
.. py:data:: DATA_OK
|
||||||
|
|
||||||
|
``DATA_OK`` indicates non empty data is generated from body generator.
|
||||||
|
|
||||||
|
.. py:data:: DATA_EOF
|
||||||
|
|
||||||
|
``DATA_EOF`` indicates the end of response body.
|
||||||
|
|
||||||
|
.. py:data:: DATA_DEFERRED
|
||||||
|
|
||||||
|
``DATA_DEFERRED`` indicates that data are not available right now
|
||||||
|
and response should be paused.
|
||||||
|
|
||||||
The following example illustrates :py:class:`HTTP2Server` and
|
The following example illustrates :py:class:`HTTP2Server` and
|
||||||
:py:class:`BaseRequestHandler` usage:
|
:py:class:`BaseRequestHandler` usage:
|
||||||
|
|
||||||
@@ -296,6 +337,7 @@ The following example illustrates :py:class:`HTTP2Server` and
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
import io, ssl
|
import io, ssl
|
||||||
|
|
||||||
import nghttp2
|
import nghttp2
|
||||||
|
|
||||||
class Handler(nghttp2.BaseRequestHandler):
|
class Handler(nghttp2.BaseRequestHandler):
|
||||||
@@ -311,9 +353,85 @@ The following example illustrates :py:class:`HTTP2Server` and
|
|||||||
body=io.BytesIO(b'nghttp2-python FTW'))
|
body=io.BytesIO(b'nghttp2-python FTW'))
|
||||||
|
|
||||||
ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
|
ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
|
||||||
ctx.options = ssl.OP_ALL | ssl.OP_NO_SSLv2
|
ctx.options = ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3
|
||||||
ctx.load_cert_chain('server.crt', 'server.key')
|
ctx.load_cert_chain('server.crt', 'server.key')
|
||||||
|
|
||||||
# give None to ssl to make the server non-SSL/TLS
|
# give None to ssl to make the server non-SSL/TLS
|
||||||
server = nghttp2.HTTP2Server(('127.0.0.1', 8443), Handler, ssl=ctx)
|
server = nghttp2.HTTP2Server(('127.0.0.1', 8443), Handler, ssl=ctx)
|
||||||
server.serve_forever()
|
server.serve_forever()
|
||||||
|
|
||||||
|
The following example illustrates HTTP/2 server using asynchronous
|
||||||
|
response body generation. This is simplified reverse proxy:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import ssl
|
||||||
|
import os
|
||||||
|
import urllib
|
||||||
|
import asyncio
|
||||||
|
import io
|
||||||
|
|
||||||
|
import nghttp2
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def get_http_header(handler, url):
|
||||||
|
url = urllib.parse.urlsplit(url)
|
||||||
|
ssl = url.scheme == 'https'
|
||||||
|
if url.port == None:
|
||||||
|
if url.scheme == 'https':
|
||||||
|
port = 443
|
||||||
|
else:
|
||||||
|
port = 80
|
||||||
|
else:
|
||||||
|
port = url.port
|
||||||
|
|
||||||
|
connect = asyncio.open_connection(url.hostname, port, ssl=ssl)
|
||||||
|
reader, writer = yield from connect
|
||||||
|
req = 'GET {path} HTTP/1.0\r\n\r\n'.format(path=url.path or '/')
|
||||||
|
writer.write(req.encode('utf-8'))
|
||||||
|
# skip response header fields
|
||||||
|
while True:
|
||||||
|
line = yield from reader.readline()
|
||||||
|
line = line.rstrip()
|
||||||
|
if not line:
|
||||||
|
break
|
||||||
|
# read body
|
||||||
|
while True:
|
||||||
|
b = yield from reader.read(4096)
|
||||||
|
if not b:
|
||||||
|
break
|
||||||
|
handler.buf.write(b)
|
||||||
|
writer.close()
|
||||||
|
handler.buf.seek(0)
|
||||||
|
handler.eof = True
|
||||||
|
handler.resume()
|
||||||
|
|
||||||
|
class Body:
|
||||||
|
def __init__(self, handler):
|
||||||
|
self.handler = handler
|
||||||
|
self.handler.eof = False
|
||||||
|
self.handler.buf = io.BytesIO()
|
||||||
|
|
||||||
|
def generate(self, n):
|
||||||
|
buf = self.handler.buf
|
||||||
|
data = buf.read1(n)
|
||||||
|
if not data and not self.handler.eof:
|
||||||
|
return None, nghttp2.DATA_DEFERRED
|
||||||
|
return data, nghttp2.DATA_EOF if self.handler.eof else nghttp2.DATA_OK
|
||||||
|
|
||||||
|
class Handler(nghttp2.BaseRequestHandler):
|
||||||
|
|
||||||
|
def on_headers(self):
|
||||||
|
body = Body(self)
|
||||||
|
asyncio.async(get_http_header(
|
||||||
|
self, 'http://localhost' + self.path.decode('utf-8')))
|
||||||
|
self.send_response(status=200, body=body.generate)
|
||||||
|
|
||||||
|
ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
|
||||||
|
ctx.options = ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3
|
||||||
|
ctx.load_cert_chain('server.crt', 'server.key')
|
||||||
|
|
||||||
|
server = nghttp2.HTTP2Server(('127.0.0.1', 8443), Handler, ssl=ctx)
|
||||||
|
server.serve_forever()
|
||||||
|
|||||||
@@ -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
|
`nghttp2_select_next_protocol()` function to select the HTTP/2
|
||||||
protocol the library supports::
|
protocol the library supports::
|
||||||
|
|
||||||
static int select_next_proto_cb(SSL* ssl,
|
static int select_next_proto_cb(SSL *ssl _U_, unsigned char **out,
|
||||||
unsigned char **out, unsigned char *outlen,
|
unsigned char *outlen, const unsigned char *in,
|
||||||
const unsigned char *in, unsigned int inlen,
|
unsigned int inlen, void *arg _U_) {
|
||||||
void *arg)
|
if (nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) {
|
||||||
{
|
|
||||||
if(nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) {
|
|
||||||
errx(1, "Server did not advertise " NGHTTP2_PROTO_VERSION_ID);
|
errx(1, "Server did not advertise " NGHTTP2_PROTO_VERSION_ID);
|
||||||
}
|
}
|
||||||
return SSL_TLSEXT_ERR_OK;
|
return SSL_TLSEXT_ERR_OK;
|
||||||
@@ -36,17 +34,17 @@ protocol the library supports::
|
|||||||
The callback is set to the SSL_CTX object using
|
The callback is set to the SSL_CTX object using
|
||||||
``SSL_CTX_set_next_proto_select_cb()`` function::
|
``SSL_CTX_set_next_proto_select_cb()`` function::
|
||||||
|
|
||||||
static SSL_CTX* create_ssl_ctx(void)
|
static SSL_CTX *create_ssl_ctx(void) {
|
||||||
{
|
|
||||||
SSL_CTX *ssl_ctx;
|
SSL_CTX *ssl_ctx;
|
||||||
ssl_ctx = SSL_CTX_new(SSLv23_client_method());
|
ssl_ctx = SSL_CTX_new(SSLv23_client_method());
|
||||||
if(!ssl_ctx) {
|
if (!ssl_ctx) {
|
||||||
errx(1, "Could not create SSL/TLS context: %s",
|
errx(1, "Could not create SSL/TLS context: %s",
|
||||||
ERR_error_string(ERR_get_error(), NULL));
|
ERR_error_string(ERR_get_error(), NULL));
|
||||||
}
|
}
|
||||||
SSL_CTX_set_options(ssl_ctx,
|
SSL_CTX_set_options(ssl_ctx,
|
||||||
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_COMPRESSION |
|
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
|
||||||
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
|
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);
|
SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL);
|
||||||
return ssl_ctx;
|
return ssl_ctx;
|
||||||
}
|
}
|
||||||
@@ -67,11 +65,11 @@ its stream specific data in ``http2_stream_data`` structure and the
|
|||||||
defined as follows::
|
defined as follows::
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
/* The NULL-terminated URI string to retreive. */
|
/* The NULL-terminated URI string to retrieve. */
|
||||||
const char *uri;
|
const char *uri;
|
||||||
/* Parsed result of the |uri| */
|
/* Parsed result of the |uri| */
|
||||||
struct http_parser_url *u;
|
struct http_parser_url *u;
|
||||||
/* The authroity portion of the |uri|, not NULL-terminated */
|
/* The authority portion of the |uri|, not NULL-terminated */
|
||||||
char *authority;
|
char *authority;
|
||||||
/* The path portion of the |uri|, including query, not
|
/* The path portion of the |uri|, including query, not
|
||||||
NULL-terminated */
|
NULL-terminated */
|
||||||
@@ -91,25 +89,22 @@ respectively.
|
|||||||
Then we call function ``initiate_connection()`` to start connecting to
|
Then we call function ``initiate_connection()`` to start connecting to
|
||||||
the remote server::
|
the remote server::
|
||||||
|
|
||||||
static void initiate_connection(struct event_base *evbase,
|
static void initiate_connection(struct event_base *evbase, SSL_CTX *ssl_ctx,
|
||||||
SSL_CTX *ssl_ctx,
|
|
||||||
const char *host, uint16_t port,
|
const char *host, uint16_t port,
|
||||||
http2_session_data *session_data)
|
http2_session_data *session_data) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
struct bufferevent *bev;
|
struct bufferevent *bev;
|
||||||
SSL *ssl;
|
SSL *ssl;
|
||||||
|
|
||||||
ssl = create_ssl(ssl_ctx);
|
ssl = create_ssl(ssl_ctx);
|
||||||
bev = bufferevent_openssl_socket_new(evbase, -1, ssl,
|
bev = bufferevent_openssl_socket_new(
|
||||||
BUFFEREVENT_SSL_CONNECTING,
|
evbase, -1, ssl, BUFFEREVENT_SSL_CONNECTING,
|
||||||
BEV_OPT_DEFER_CALLBACKS |
|
BEV_OPT_DEFER_CALLBACKS | BEV_OPT_CLOSE_ON_FREE);
|
||||||
BEV_OPT_CLOSE_ON_FREE);
|
|
||||||
bufferevent_setcb(bev, readcb, writecb, eventcb, session_data);
|
bufferevent_setcb(bev, readcb, writecb, eventcb, session_data);
|
||||||
rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase,
|
rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase,
|
||||||
AF_UNSPEC, host, port);
|
AF_UNSPEC, host, port);
|
||||||
|
|
||||||
if(rv != 0) {
|
if (rv != 0) {
|
||||||
errx(1, "Could not connect to the remote host %s", host);
|
errx(1, "Could not connect to the remote host %s", host);
|
||||||
}
|
}
|
||||||
session_data->bev = bev;
|
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
|
(e.g., connection has been established, timeout, etc) happens on the
|
||||||
underlying network socket::
|
underlying network socket::
|
||||||
|
|
||||||
static void eventcb(struct bufferevent *bev, short events, void *ptr)
|
static void eventcb(struct bufferevent *bev, short events, void *ptr) {
|
||||||
{
|
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
if (events & BEV_EVENT_CONNECTED) {
|
||||||
if(events & BEV_EVENT_CONNECTED) {
|
|
||||||
int fd = bufferevent_getfd(bev);
|
int fd = bufferevent_getfd(bev);
|
||||||
int val = 1;
|
int val = 1;
|
||||||
fprintf(stderr, "Connected\n");
|
fprintf(stderr, "Connected\n");
|
||||||
@@ -133,16 +127,16 @@ underlying network socket::
|
|||||||
initialize_nghttp2_session(session_data);
|
initialize_nghttp2_session(session_data);
|
||||||
send_client_connection_header(session_data);
|
send_client_connection_header(session_data);
|
||||||
submit_request(session_data);
|
submit_request(session_data);
|
||||||
if(session_send(session_data) != 0) {
|
if (session_send(session_data) != 0) {
|
||||||
delete_http2_session_data(session_data);
|
delete_http2_session_data(session_data);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(events & BEV_EVENT_EOF) {
|
if (events & BEV_EVENT_EOF) {
|
||||||
warnx("Disconnected from the remote host");
|
warnx("Disconnected from the remote host");
|
||||||
} else if(events & BEV_EVENT_ERROR) {
|
} else if (events & BEV_EVENT_ERROR) {
|
||||||
warnx("Network error");
|
warnx("Network error");
|
||||||
} else if(events & BEV_EVENT_TIMEOUT) {
|
} else if (events & BEV_EVENT_TIMEOUT) {
|
||||||
warnx("Timeout");
|
warnx("Timeout");
|
||||||
}
|
}
|
||||||
delete_http2_session_data(session_data);
|
delete_http2_session_data(session_data);
|
||||||
@@ -154,28 +148,27 @@ event, we just simply tear down the connection. The
|
|||||||
finished successfully. We first initialize nghttp2 session object in
|
finished successfully. We first initialize nghttp2 session object in
|
||||||
``initialize_nghttp2_session()`` function::
|
``initialize_nghttp2_session()`` function::
|
||||||
|
|
||||||
static void initialize_nghttp2_session(http2_session_data *session_data)
|
static void initialize_nghttp2_session(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
nghttp2_session_callbacks *callbacks;
|
nghttp2_session_callbacks *callbacks;
|
||||||
|
|
||||||
nghttp2_session_callbacks_new(&callbacks);
|
nghttp2_session_callbacks_new(&callbacks);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_frame_recv_callback
|
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
|
||||||
(callbacks, on_frame_recv_callback);
|
on_frame_recv_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_data_chunk_recv_callback
|
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
|
||||||
(callbacks, on_data_chunk_recv_callback);
|
callbacks, on_data_chunk_recv_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_stream_close_callback
|
nghttp2_session_callbacks_set_on_stream_close_callback(
|
||||||
(callbacks, on_stream_close_callback);
|
callbacks, on_stream_close_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_header_callback
|
nghttp2_session_callbacks_set_on_header_callback(callbacks,
|
||||||
(callbacks, on_header_callback);
|
on_header_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_begin_headers_callback
|
nghttp2_session_callbacks_set_on_begin_headers_callback(
|
||||||
(callbacks, on_begin_headers_callback);
|
callbacks, on_begin_headers_callback);
|
||||||
|
|
||||||
nghttp2_session_client_new(&session_data->session, callbacks, session_data);
|
nghttp2_session_client_new(&session_data->session, callbacks, session_data);
|
||||||
|
|
||||||
@@ -191,30 +184,26 @@ its bufferevent, so it closes underlying connection as well. It also
|
|||||||
calls `nghttp2_session_del()` to delete nghttp2 session object.
|
calls `nghttp2_session_del()` to delete nghttp2 session object.
|
||||||
|
|
||||||
We begin HTTP/2 communication by sending client connection preface,
|
We begin HTTP/2 communication by sending client connection preface,
|
||||||
which is 24 bytes magic byte sequence
|
which is 24 bytes magic byte string (:macro:`NGHTTP2_CLIENT_MAGIC`)
|
||||||
(:macro:`NGHTTP2_CLIENT_CONNECTION_PREFACE`) and SETTINGS frame. The
|
followed by SETTINGS frame. First 24 bytes magic string is
|
||||||
transmission of client connection header is done in
|
automatically sent by nghttp2 library. We send SETTINGS frame in
|
||||||
``send_client_connection_header()``::
|
``send_client_connection_header()``::
|
||||||
|
|
||||||
static void send_client_connection_header(http2_session_data *session_data)
|
static void send_client_connection_header(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
nghttp2_settings_entry iv[1] = {
|
nghttp2_settings_entry iv[1] = {
|
||||||
{ NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 }
|
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
|
||||||
};
|
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
bufferevent_write(session_data->bev,
|
/* client 24 bytes magic string will be sent by nghttp2 library */
|
||||||
NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
|
||||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
|
ARRLEN(iv));
|
||||||
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE,
|
if (rv != 0) {
|
||||||
iv, ARRLEN(iv));
|
|
||||||
if(rv != 0) {
|
|
||||||
errx(1, "Could not submit SETTINGS: %s", nghttp2_strerror(rv));
|
errx(1, "Could not submit SETTINGS: %s", nghttp2_strerror(rv));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Here we specify SETTINGS_MAX_CONCURRENT_STREAMS to 100, which is
|
Here we specify SETTINGS_MAX_CONCURRENT_STREAMS to 100, which is
|
||||||
really not needed for this tiny example progoram, but we are
|
really not needed for this tiny example program, but we are
|
||||||
demonstrating the use of SETTINGS frame. To queue the SETTINGS frame
|
demonstrating the use of SETTINGS frame. To queue the SETTINGS frame
|
||||||
for the transmission, we use `nghttp2_submit_settings()`. Note that
|
for the transmission, we use `nghttp2_submit_settings()`. Note that
|
||||||
`nghttp2_submit_settings()` function only queues the frame and not
|
`nghttp2_submit_settings()` function only queues the frame and not
|
||||||
@@ -225,24 +214,22 @@ used, which is described about later.
|
|||||||
After the transmission of client connection header, we enqueue HTTP
|
After the transmission of client connection header, we enqueue HTTP
|
||||||
request in ``submit_request()`` function::
|
request in ``submit_request()`` function::
|
||||||
|
|
||||||
static void submit_request(http2_session_data *session_data)
|
static void submit_request(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
int32_t stream_id;
|
int32_t stream_id;
|
||||||
http2_stream_data *stream_data = session_data->stream_data;
|
http2_stream_data *stream_data = session_data->stream_data;
|
||||||
const char *uri = stream_data->uri;
|
const char *uri = stream_data->uri;
|
||||||
const struct http_parser_url *u = stream_data->u;
|
const struct http_parser_url *u = stream_data->u;
|
||||||
nghttp2_nv hdrs[] = {
|
nghttp2_nv hdrs[] = {
|
||||||
MAKE_NV2(":method", "GET"),
|
MAKE_NV2(":method", "GET"),
|
||||||
MAKE_NV(":scheme",
|
MAKE_NV(":scheme", &uri[u->field_data[UF_SCHEMA].off],
|
||||||
&uri[u->field_data[UF_SCHEMA].off], u->field_data[UF_SCHEMA].len),
|
u->field_data[UF_SCHEMA].len),
|
||||||
MAKE_NV(":authority", stream_data->authority, stream_data->authoritylen),
|
MAKE_NV(":authority", stream_data->authority, stream_data->authoritylen),
|
||||||
MAKE_NV(":path", stream_data->path, stream_data->pathlen)
|
MAKE_NV(":path", stream_data->path, stream_data->pathlen)};
|
||||||
};
|
|
||||||
fprintf(stderr, "Request headers:\n");
|
fprintf(stderr, "Request headers:\n");
|
||||||
print_headers(stderr, hdrs, ARRLEN(hdrs));
|
print_headers(stderr, hdrs, ARRLEN(hdrs));
|
||||||
stream_id = nghttp2_submit_request(session_data->session, NULL,
|
stream_id = nghttp2_submit_request(session_data->session, NULL, hdrs,
|
||||||
hdrs, ARRLEN(hdrs), NULL, stream_data);
|
ARRLEN(hdrs), NULL, stream_data);
|
||||||
if(stream_id < 0) {
|
if (stream_id < 0) {
|
||||||
errx(1, "Could not submit HTTP request: %s", nghttp2_strerror(stream_id));
|
errx(1, "Could not submit HTTP request: %s", nghttp2_strerror(stream_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,26 +248,25 @@ this request.
|
|||||||
The next bufferevent callback is ``readcb()``, which is invoked when
|
The next bufferevent callback is ``readcb()``, which is invoked when
|
||||||
data is available to read in the bufferevent input buffer::
|
data is available to read in the bufferevent input buffer::
|
||||||
|
|
||||||
static void readcb(struct bufferevent *bev, void *ptr)
|
static void readcb(struct bufferevent *bev, void *ptr) {
|
||||||
{
|
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
|
||||||
ssize_t readlen;
|
ssize_t readlen;
|
||||||
struct evbuffer *input = bufferevent_get_input(bev);
|
struct evbuffer *input = bufferevent_get_input(bev);
|
||||||
size_t datalen = evbuffer_get_length(input);
|
size_t datalen = evbuffer_get_length(input);
|
||||||
unsigned char *data = evbuffer_pullup(input, -1);
|
unsigned char *data = evbuffer_pullup(input, -1);
|
||||||
|
|
||||||
readlen = nghttp2_session_mem_recv(session_data->session, data, datalen);
|
readlen = nghttp2_session_mem_recv(session_data->session, data, datalen);
|
||||||
if(readlen < 0) {
|
if (readlen < 0) {
|
||||||
warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
|
warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
|
||||||
delete_http2_session_data(session_data);
|
delete_http2_session_data(session_data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(evbuffer_drain(input, readlen) != 0) {
|
if (evbuffer_drain(input, readlen) != 0) {
|
||||||
warnx("Fatal error: evbuffer_drain failed");
|
warnx("Fatal error: evbuffer_drain failed");
|
||||||
delete_http2_session_data(session_data);
|
delete_http2_session_data(session_data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(session_send(session_data) != 0) {
|
if (session_send(session_data) != 0) {
|
||||||
delete_http2_session_data(session_data);
|
delete_http2_session_data(session_data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -293,12 +279,11 @@ invoke nghttp2 callbacks and also queue frames. Since there may be
|
|||||||
pending frames, we call ``session_send()`` function to send those
|
pending frames, we call ``session_send()`` function to send those
|
||||||
frames. The ``session_send()`` function is defined as follows::
|
frames. The ``session_send()`` function is defined as follows::
|
||||||
|
|
||||||
static int session_send(http2_session_data *session_data)
|
static int session_send(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
rv = nghttp2_session_send(session_data->session);
|
rv = nghttp2_session_send(session_data->session);
|
||||||
if(rv != 0) {
|
if (rv != 0) {
|
||||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -310,11 +295,9 @@ format and call ``send_callback()`` function of type
|
|||||||
:type:`nghttp2_send_callback`. The ``send_callback()`` is defined as
|
:type:`nghttp2_send_callback`. The ``send_callback()`` is defined as
|
||||||
follows::
|
follows::
|
||||||
|
|
||||||
static ssize_t send_callback(nghttp2_session *session,
|
static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
|
||||||
const uint8_t *data, size_t length,
|
size_t length, int flags _U_, void *user_data) {
|
||||||
int flags, void *user_data)
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
|
||||||
struct bufferevent *bev = session_data->bev;
|
struct bufferevent *bev = session_data->bev;
|
||||||
bufferevent_write(bev, data, length);
|
bufferevent_write(bev, data, length);
|
||||||
return length;
|
return length;
|
||||||
@@ -336,12 +319,11 @@ buffered data, see the ``send_callback()`` in the server tutorial.
|
|||||||
The third bufferevent callback is ``writecb()``, which is invoked when
|
The third bufferevent callback is ``writecb()``, which is invoked when
|
||||||
all data written in the bufferevent output buffer have been sent::
|
all data written in the bufferevent output buffer have been sent::
|
||||||
|
|
||||||
static void writecb(struct bufferevent *bev, void *ptr)
|
static void writecb(struct bufferevent *bev _U_, void *ptr) {
|
||||||
{
|
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
if (nghttp2_session_want_read(session_data->session) == 0 &&
|
||||||
if(nghttp2_session_want_read(session_data->session) == 0 &&
|
nghttp2_session_want_write(session_data->session) == 0 &&
|
||||||
nghttp2_session_want_write(session_data->session) == 0 &&
|
evbuffer_get_length(bufferevent_get_output(session_data->bev)) == 0) {
|
||||||
evbuffer_get_length(bufferevent_get_output(session_data->bev)) == 0) {
|
|
||||||
delete_http2_session_data(session_data);
|
delete_http2_session_data(session_data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -367,18 +349,16 @@ Let's describe remaining nghttp2 callbacks we setup in
|
|||||||
Each request header name/value pair is emitted via
|
Each request header name/value pair is emitted via
|
||||||
``on_header_callback`` function::
|
``on_header_callback`` function::
|
||||||
|
|
||||||
static int on_header_callback(nghttp2_session *session,
|
static int on_header_callback(nghttp2_session *session _U_,
|
||||||
const nghttp2_frame *frame,
|
const nghttp2_frame *frame, const uint8_t *name,
|
||||||
const uint8_t *name, size_t namelen,
|
size_t namelen, const uint8_t *value,
|
||||||
const uint8_t *value, size_t valuelen,
|
size_t valuelen, uint8_t flags _U_,
|
||||||
uint8_t flags,
|
void *user_data) {
|
||||||
void *user_data)
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
{
|
switch (frame->hd.type) {
|
||||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
|
||||||
switch(frame->hd.type) {
|
|
||||||
case NGHTTP2_HEADERS:
|
case NGHTTP2_HEADERS:
|
||||||
if(frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
|
if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
|
||||||
session_data->stream_data->stream_id == frame->hd.stream_id) {
|
session_data->stream_data->stream_id == frame->hd.stream_id) {
|
||||||
/* Print response headers for the initiated request. */
|
/* Print response headers for the initiated request. */
|
||||||
print_header(stderr, name, namelen, value, valuelen);
|
print_header(stderr, name, namelen, value, valuelen);
|
||||||
break;
|
break;
|
||||||
@@ -387,19 +367,18 @@ Each request header name/value pair is emitted via
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
In this turotial, we just print the name/value pair.
|
In this tutorial, we just print the name/value pair.
|
||||||
|
|
||||||
After all name/value pairs are emitted for a frame,
|
After all name/value pairs are emitted for a frame,
|
||||||
``on_frame_recv_callback`` function is called::
|
``on_frame_recv_callback`` function is called::
|
||||||
|
|
||||||
static int on_frame_recv_callback(nghttp2_session *session,
|
static int on_frame_recv_callback(nghttp2_session *session _U_,
|
||||||
const nghttp2_frame *frame, void *user_data)
|
const nghttp2_frame *frame, void *user_data) {
|
||||||
{
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
switch (frame->hd.type) {
|
||||||
switch(frame->hd.type) {
|
|
||||||
case NGHTTP2_HEADERS:
|
case NGHTTP2_HEADERS:
|
||||||
if(frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
|
if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
|
||||||
session_data->stream_data->stream_id == frame->hd.stream_id) {
|
session_data->stream_data->stream_id == frame->hd.stream_id) {
|
||||||
fprintf(stderr, "All headers received\n");
|
fprintf(stderr, "All headers received\n");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -408,46 +387,43 @@ After all name/value pairs are emitted for a frame,
|
|||||||
}
|
}
|
||||||
|
|
||||||
In this tutorial, we are just interested in the HTTP response
|
In this tutorial, we are just interested in the HTTP response
|
||||||
HEADERS. We check te frame type and its category (it should be
|
HEADERS. We check the frame type and its category (it should be
|
||||||
:macro:`NGHTTP2_HCAT_RESPONSE` for HTTP response HEADERS). Also check
|
:macro:`NGHTTP2_HCAT_RESPONSE` for HTTP response HEADERS). Also check
|
||||||
its stream ID.
|
its stream ID.
|
||||||
|
|
||||||
The ``on_data_chunk_recv_callback()`` function is invoked when a chunk
|
The ``on_data_chunk_recv_callback()`` function is invoked when a chunk
|
||||||
of data is received from the remote peer::
|
of data is received from the remote peer::
|
||||||
|
|
||||||
static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
static int on_data_chunk_recv_callback(nghttp2_session *session _U_,
|
||||||
int32_t stream_id,
|
uint8_t flags _U_, int32_t stream_id,
|
||||||
const uint8_t *data, size_t len,
|
const uint8_t *data, size_t len,
|
||||||
void *user_data)
|
void *user_data) {
|
||||||
{
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
if (session_data->stream_data->stream_id == stream_id) {
|
||||||
if(session_data->stream_data->stream_id == stream_id) {
|
|
||||||
fwrite(data, len, 1, stdout);
|
fwrite(data, len, 1, stdout);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
In our case, a chunk of data is response body. After checking stream
|
In our case, a chunk of data is response body. After checking stream
|
||||||
ID, we just write the recieved data to the stdout. Note that the
|
ID, we just write the received data to the stdout. Note that the
|
||||||
output in the terminal may be corrupted if the response body contains
|
output in the terminal may be corrupted if the response body contains
|
||||||
some binary data.
|
some binary data.
|
||||||
|
|
||||||
The ``on_stream_close_callback()`` function is invoked when the stream
|
The ``on_stream_close_callback()`` function is invoked when the stream
|
||||||
is about to close::
|
is about to close::
|
||||||
|
|
||||||
static int on_stream_close_callback(nghttp2_session *session,
|
static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||||
int32_t stream_id,
|
|
||||||
nghttp2_error_code error_code,
|
nghttp2_error_code error_code,
|
||||||
void *user_data)
|
void *user_data) {
|
||||||
{
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
if(session_data->stream_data->stream_id == stream_id) {
|
if (session_data->stream_data->stream_id == stream_id) {
|
||||||
fprintf(stderr, "Stream %d closed with error_code=%d\n",
|
fprintf(stderr, "Stream %d closed with error_code=%d\n", stream_id,
|
||||||
stream_id, error_code);
|
error_code);
|
||||||
rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
|
rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
|
||||||
if(rv != 0) {
|
if (rv != 0) {
|
||||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ bound of encoded result, use `nghttp2_hd_deflate_bound()` function::
|
|||||||
size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater,
|
size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater,
|
||||||
const nghttp2_nv *nva, size_t nvlen);
|
const nghttp2_nv *nva, size_t nvlen);
|
||||||
|
|
||||||
Pass this function with the same paramters *deflater*, *nva* and
|
Pass this function with the same parameters *deflater*, *nva* and
|
||||||
*nvlen* which will be passed to `nghttp2_hd_deflate_hd()`.
|
*nvlen* which will be passed to `nghttp2_hd_deflate_hd()`.
|
||||||
|
|
||||||
The subsequent call of `nghttp2_hd_deflate_hd()` will use current
|
The subsequent call of `nghttp2_hd_deflate_hd()` will use current
|
||||||
|
|||||||
@@ -29,17 +29,17 @@ time::
|
|||||||
static unsigned char next_proto_list[256];
|
static unsigned char next_proto_list[256];
|
||||||
static size_t next_proto_list_len;
|
static size_t next_proto_list_len;
|
||||||
|
|
||||||
static int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len,
|
static int next_proto_cb(SSL *s _U_, const unsigned char **data,
|
||||||
void *arg)
|
unsigned int *len, void *arg _U_) {
|
||||||
{
|
|
||||||
*data = next_proto_list;
|
*data = next_proto_list;
|
||||||
*len = (unsigned int)next_proto_list_len;
|
*len = (unsigned int)next_proto_list_len;
|
||||||
return SSL_TLSEXT_ERR_OK;
|
return SSL_TLSEXT_ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static SSL_CTX* create_ssl_ctx(const char *key_file, const char *cert_file)
|
static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) {
|
||||||
{
|
|
||||||
SSL_CTX *ssl_ctx;
|
SSL_CTX *ssl_ctx;
|
||||||
|
EC_KEY *ecdh;
|
||||||
|
|
||||||
ssl_ctx = SSL_CTX_new(SSLv23_server_method());
|
ssl_ctx = SSL_CTX_new(SSLv23_server_method());
|
||||||
|
|
||||||
...
|
...
|
||||||
@@ -104,8 +104,7 @@ We first create a listener object to accept incoming connections. We use
|
|||||||
libevent's ``struct evconnlistener`` for this purpose::
|
libevent's ``struct evconnlistener`` for this purpose::
|
||||||
|
|
||||||
static void start_listen(struct event_base *evbase, const char *service,
|
static void start_listen(struct event_base *evbase, const char *service,
|
||||||
app_context *app_ctx)
|
app_context *app_ctx) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
struct addrinfo hints;
|
struct addrinfo hints;
|
||||||
struct addrinfo *res, *rp;
|
struct addrinfo *res, *rp;
|
||||||
@@ -116,19 +115,20 @@ libevent's ``struct evconnlistener`` for this purpose::
|
|||||||
hints.ai_flags = AI_PASSIVE;
|
hints.ai_flags = AI_PASSIVE;
|
||||||
#ifdef AI_ADDRCONFIG
|
#ifdef AI_ADDRCONFIG
|
||||||
hints.ai_flags |= AI_ADDRCONFIG;
|
hints.ai_flags |= AI_ADDRCONFIG;
|
||||||
#endif // AI_ADDRCONFIG
|
#endif /* AI_ADDRCONFIG */
|
||||||
|
|
||||||
rv = getaddrinfo(NULL, service, &hints, &res);
|
rv = getaddrinfo(NULL, service, &hints, &res);
|
||||||
if(rv != 0) {
|
if (rv != 0) {
|
||||||
errx(1, NULL);
|
errx(1, NULL);
|
||||||
}
|
}
|
||||||
for(rp = res; rp; rp = rp->ai_next) {
|
for (rp = res; rp; rp = rp->ai_next) {
|
||||||
struct evconnlistener *listener;
|
struct evconnlistener *listener;
|
||||||
listener = evconnlistener_new_bind(evbase, acceptcb, app_ctx,
|
listener = evconnlistener_new_bind(
|
||||||
LEV_OPT_CLOSE_ON_FREE |
|
evbase, acceptcb, app_ctx, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
|
||||||
LEV_OPT_REUSEABLE, -1,
|
16, rp->ai_addr, rp->ai_addrlen);
|
||||||
rp->ai_addr, rp->ai_addrlen);
|
if (listener) {
|
||||||
if(listener) {
|
freeaddrinfo(res);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,10 +138,9 @@ libevent's ``struct evconnlistener`` for this purpose::
|
|||||||
We specify the ``acceptcb`` callback which is called when a new connection is
|
We specify the ``acceptcb`` callback which is called when a new connection is
|
||||||
accepted::
|
accepted::
|
||||||
|
|
||||||
static void acceptcb(struct evconnlistener *listener, int fd,
|
static void acceptcb(struct evconnlistener *listener _U_, int fd,
|
||||||
struct sockaddr *addr, int addrlen, void *arg)
|
struct sockaddr *addr, int addrlen, void *arg) {
|
||||||
{
|
app_context *app_ctx = (app_context *)arg;
|
||||||
app_context *app_ctx = (app_context*)arg;
|
|
||||||
http2_session_data *session_data;
|
http2_session_data *session_data;
|
||||||
|
|
||||||
session_data = create_http2_session_data(app_ctx, fd, addr, addrlen);
|
session_data = create_http2_session_data(app_ctx, fd, addr, addrlen);
|
||||||
@@ -158,26 +157,25 @@ The ``eventcb()`` callback is invoked by the libevent event loop when an event
|
|||||||
(e.g., connection has been established, timeout, etc) happens on the
|
(e.g., connection has been established, timeout, etc) happens on the
|
||||||
underlying network socket::
|
underlying network socket::
|
||||||
|
|
||||||
static void eventcb(struct bufferevent *bev, short events, void *ptr)
|
static void eventcb(struct bufferevent *bev _U_, short events, void *ptr) {
|
||||||
{
|
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
if (events & BEV_EVENT_CONNECTED) {
|
||||||
if(events & BEV_EVENT_CONNECTED) {
|
|
||||||
fprintf(stderr, "%s connected\n", session_data->client_addr);
|
fprintf(stderr, "%s connected\n", session_data->client_addr);
|
||||||
|
|
||||||
initialize_nghttp2_session(session_data);
|
initialize_nghttp2_session(session_data);
|
||||||
|
|
||||||
if(send_server_connection_header(session_data) != 0) {
|
if (send_server_connection_header(session_data) != 0) {
|
||||||
delete_http2_session_data(session_data);
|
delete_http2_session_data(session_data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(events & BEV_EVENT_EOF) {
|
if (events & BEV_EVENT_EOF) {
|
||||||
fprintf(stderr, "%s EOF\n", session_data->client_addr);
|
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);
|
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);
|
fprintf(stderr, "%s timeout\n", session_data->client_addr);
|
||||||
}
|
}
|
||||||
delete_http2_session_data(session_data);
|
delete_http2_session_data(session_data);
|
||||||
@@ -195,63 +193,46 @@ communication.
|
|||||||
We initialize a nghttp2 session object which is done in
|
We initialize a nghttp2 session object which is done in
|
||||||
``initialize_nghttp2_session()``::
|
``initialize_nghttp2_session()``::
|
||||||
|
|
||||||
static void initialize_nghttp2_session(http2_session_data *session_data)
|
static void initialize_nghttp2_session(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
nghttp2_option *option;
|
|
||||||
nghttp2_session_callbacks *callbacks;
|
nghttp2_session_callbacks *callbacks;
|
||||||
|
|
||||||
nghttp2_option_new(&option);
|
|
||||||
|
|
||||||
/* Tells nghttp2_session object that it handles client connection
|
|
||||||
preface */
|
|
||||||
nghttp2_option_set_recv_client_preface(option, 1);
|
|
||||||
|
|
||||||
nghttp2_session_callbacks_new(&callbacks);
|
nghttp2_session_callbacks_new(&callbacks);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_frame_recv_callback
|
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
|
||||||
(callbacks, on_frame_recv_callback);
|
on_frame_recv_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_stream_close_callback
|
nghttp2_session_callbacks_set_on_stream_close_callback(
|
||||||
(callbacks, on_stream_close_callback);
|
callbacks, on_stream_close_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_header_callback
|
nghttp2_session_callbacks_set_on_header_callback(callbacks,
|
||||||
(callbacks, on_header_callback);
|
on_header_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_begin_headers_callback
|
nghttp2_session_callbacks_set_on_begin_headers_callback(
|
||||||
(callbacks, on_begin_headers_callback);
|
callbacks, on_begin_headers_callback);
|
||||||
|
|
||||||
|
nghttp2_session_server_new(&session_data->session, callbacks, session_data);
|
||||||
|
|
||||||
nghttp2_session_server_new2(&session_data->session, callbacks, session_data,
|
|
||||||
option);
|
|
||||||
|
|
||||||
nghttp2_session_callbacks_del(callbacks);
|
nghttp2_session_callbacks_del(callbacks);
|
||||||
nghttp2_option_del(option);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Since we are creating a server and uses options, the nghttp2 session
|
Since we are creating a server and uses options, the nghttp2 session
|
||||||
object is created using `nghttp2_session_server_new2()` function. We
|
object is created using `nghttp2_session_server_new2()` function. We
|
||||||
registers five callbacks for nghttp2 session object. We'll talk about
|
registers five callbacks for nghttp2 session object. We'll talk about
|
||||||
these callbacks later. Our server only speaks HTTP/2. In this case,
|
these callbacks later.
|
||||||
we use `nghttp2_option_set_recv_client_preface()` to make
|
|
||||||
:type:`nghttp2_session` object handle client connection preface, which
|
|
||||||
saves some lines of application code.
|
|
||||||
|
|
||||||
After initialization of the nghttp2 session object, we are going to send
|
After initialization of the nghttp2 session object, we are going to send
|
||||||
a server connection header in ``send_server_connection_header()``::
|
a server connection header in ``send_server_connection_header()``::
|
||||||
|
|
||||||
static int send_server_connection_header(http2_session_data *session_data)
|
static int send_server_connection_header(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
nghttp2_settings_entry iv[1] = {
|
nghttp2_settings_entry iv[1] = {
|
||||||
{ NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 }
|
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
|
||||||
};
|
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE,
|
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
|
||||||
iv, ARRLEN(iv));
|
ARRLEN(iv));
|
||||||
if(rv != 0) {
|
if (rv != 0) {
|
||||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -272,23 +253,22 @@ have to process them here since libevent won't invoke callback functions for
|
|||||||
this pending data. To process the received data, we call the
|
this pending data. To process the received data, we call the
|
||||||
``session_recv()`` function::
|
``session_recv()`` function::
|
||||||
|
|
||||||
static int session_recv(http2_session_data *session_data)
|
static int session_recv(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
ssize_t readlen;
|
ssize_t readlen;
|
||||||
struct evbuffer *input = bufferevent_get_input(session_data->bev);
|
struct evbuffer *input = bufferevent_get_input(session_data->bev);
|
||||||
size_t datalen = evbuffer_get_length(input);
|
size_t datalen = evbuffer_get_length(input);
|
||||||
unsigned char *data = evbuffer_pullup(input, -1);
|
unsigned char *data = evbuffer_pullup(input, -1);
|
||||||
|
|
||||||
readlen = nghttp2_session_mem_recv(session_data->session, data, datalen);
|
readlen = nghttp2_session_mem_recv(session_data->session, data, datalen);
|
||||||
if(readlen < 0) {
|
if (readlen < 0) {
|
||||||
warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
|
warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if(evbuffer_drain(input, readlen) != 0) {
|
if (evbuffer_drain(input, readlen) != 0) {
|
||||||
warnx("Fatal error: evbuffer_drain failed");
|
warnx("Fatal error: evbuffer_drain failed");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if(session_send(session_data) != 0) {
|
if (session_send(session_data) != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@@ -301,11 +281,10 @@ nghttp2 callbacks and also queue outgoing frames. Since there may be pending
|
|||||||
outgoing frames, we call ``session_send()`` function to send off those
|
outgoing frames, we call ``session_send()`` function to send off those
|
||||||
frames. The ``session_send()`` function is defined as follows::
|
frames. The ``session_send()`` function is defined as follows::
|
||||||
|
|
||||||
static int session_send(http2_session_data *session_data)
|
static int session_send(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
rv = nghttp2_session_send(session_data->session);
|
rv = nghttp2_session_send(session_data->session);
|
||||||
if(rv != 0) {
|
if (rv != 0) {
|
||||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -317,15 +296,13 @@ format and calls ``send_callback()`` of type
|
|||||||
:type:`nghttp2_send_callback`. The ``send_callback()`` is defined as
|
:type:`nghttp2_send_callback`. The ``send_callback()`` is defined as
|
||||||
follows::
|
follows::
|
||||||
|
|
||||||
static ssize_t send_callback(nghttp2_session *session,
|
static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
|
||||||
const uint8_t *data, size_t length,
|
size_t length, int flags _U_, void *user_data) {
|
||||||
int flags, void *user_data)
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
|
||||||
struct bufferevent *bev = session_data->bev;
|
struct bufferevent *bev = session_data->bev;
|
||||||
/* Avoid excessive buffering in server side. */
|
/* Avoid excessive buffering in server side. */
|
||||||
if(evbuffer_get_length(bufferevent_get_output(session_data->bev)) >=
|
if (evbuffer_get_length(bufferevent_get_output(session_data->bev)) >=
|
||||||
OUTPUT_WOULDBLOCK_THRESHOLD) {
|
OUTPUT_WOULDBLOCK_THRESHOLD) {
|
||||||
return NGHTTP2_ERR_WOULDBLOCK;
|
return NGHTTP2_ERR_WOULDBLOCK;
|
||||||
}
|
}
|
||||||
bufferevent_write(bev, data, length);
|
bufferevent_write(bev, data, length);
|
||||||
@@ -350,10 +327,9 @@ calling send_callback.
|
|||||||
The next bufferevent callback is ``readcb()``, which is invoked when
|
The next bufferevent callback is ``readcb()``, which is invoked when
|
||||||
data is available to read in the bufferevent input buffer::
|
data is available to read in the bufferevent input buffer::
|
||||||
|
|
||||||
static void readcb(struct bufferevent *bev, void *ptr)
|
static void readcb(struct bufferevent *bev _U_, void *ptr) {
|
||||||
{
|
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
if (session_recv(session_data) != 0) {
|
||||||
if(session_recv(session_data) != 0) {
|
|
||||||
delete_http2_session_data(session_data);
|
delete_http2_session_data(session_data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -365,18 +341,17 @@ data.
|
|||||||
The third bufferevent callback is ``writecb()``, which is invoked when all
|
The third bufferevent callback is ``writecb()``, which is invoked when all
|
||||||
data in the bufferevent output buffer has been sent::
|
data in the bufferevent output buffer has been sent::
|
||||||
|
|
||||||
static void writecb(struct bufferevent *bev, void *ptr)
|
static void writecb(struct bufferevent *bev, void *ptr) {
|
||||||
{
|
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
if (evbuffer_get_length(bufferevent_get_output(bev)) > 0) {
|
||||||
if(evbuffer_get_length(bufferevent_get_output(bev)) > 0) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(nghttp2_session_want_read(session_data->session) == 0 &&
|
if (nghttp2_session_want_read(session_data->session) == 0 &&
|
||||||
nghttp2_session_want_write(session_data->session) == 0) {
|
nghttp2_session_want_write(session_data->session) == 0) {
|
||||||
delete_http2_session_data(session_data);
|
delete_http2_session_data(session_data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(session_send(session_data) != 0) {
|
if (session_send(session_data) != 0) {
|
||||||
delete_http2_session_data(session_data);
|
delete_http2_session_data(session_data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -407,13 +382,12 @@ a header block in HEADERS or PUSH_PROMISE frame is started::
|
|||||||
|
|
||||||
static int on_begin_headers_callback(nghttp2_session *session,
|
static int on_begin_headers_callback(nghttp2_session *session,
|
||||||
const nghttp2_frame *frame,
|
const nghttp2_frame *frame,
|
||||||
void *user_data)
|
void *user_data) {
|
||||||
{
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
|
||||||
http2_stream_data *stream_data;
|
http2_stream_data *stream_data;
|
||||||
|
|
||||||
if(frame->hd.type != NGHTTP2_HEADERS ||
|
if (frame->hd.type != NGHTTP2_HEADERS ||
|
||||||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
stream_data = create_http2_stream_data(session_data, frame->hd.stream_id);
|
stream_data = create_http2_stream_data(session_data, frame->hd.stream_id);
|
||||||
@@ -436,26 +410,26 @@ emitted via ``on_header_callback`` function, which is called after
|
|||||||
``on_begin_headers_callback()``::
|
``on_begin_headers_callback()``::
|
||||||
|
|
||||||
static int on_header_callback(nghttp2_session *session,
|
static int on_header_callback(nghttp2_session *session,
|
||||||
const nghttp2_frame *frame,
|
const nghttp2_frame *frame, const uint8_t *name,
|
||||||
const uint8_t *name, size_t namelen,
|
size_t namelen, const uint8_t *value,
|
||||||
const uint8_t *value, size_t valuelen,
|
size_t valuelen, uint8_t flags _U_,
|
||||||
void *user_data)
|
void *user_data _U_) {
|
||||||
{
|
|
||||||
http2_stream_data *stream_data;
|
http2_stream_data *stream_data;
|
||||||
const char PATH[] = ":path";
|
const char PATH[] = ":path";
|
||||||
switch(frame->hd.type) {
|
switch (frame->hd.type) {
|
||||||
case NGHTTP2_HEADERS:
|
case NGHTTP2_HEADERS:
|
||||||
if(frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
stream_data = nghttp2_session_get_stream_user_data(session,
|
stream_data =
|
||||||
frame->hd.stream_id);
|
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
|
||||||
if(!stream_data || stream_data->request_path) {
|
if (!stream_data || stream_data->request_path) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) {
|
if (namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) {
|
||||||
size_t j;
|
size_t j;
|
||||||
for(j = 0; j < valuelen && value[j] != '?'; ++j);
|
for (j = 0; j < valuelen && value[j] != '?'; ++j)
|
||||||
|
;
|
||||||
stream_data->request_path = percent_decode(value, j);
|
stream_data->request_path = percent_decode(value, j);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -472,20 +446,19 @@ The ``on_frame_recv_callback()`` function is invoked when a frame is
|
|||||||
fully received::
|
fully received::
|
||||||
|
|
||||||
static int on_frame_recv_callback(nghttp2_session *session,
|
static int on_frame_recv_callback(nghttp2_session *session,
|
||||||
const nghttp2_frame *frame, void *user_data)
|
const nghttp2_frame *frame, void *user_data) {
|
||||||
{
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
|
||||||
http2_stream_data *stream_data;
|
http2_stream_data *stream_data;
|
||||||
switch(frame->hd.type) {
|
switch (frame->hd.type) {
|
||||||
case NGHTTP2_DATA:
|
case NGHTTP2_DATA:
|
||||||
case NGHTTP2_HEADERS:
|
case NGHTTP2_HEADERS:
|
||||||
/* Check that the client request has finished */
|
/* Check that the client request has finished */
|
||||||
if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||||
stream_data = nghttp2_session_get_stream_user_data(session,
|
stream_data =
|
||||||
frame->hd.stream_id);
|
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
|
||||||
/* For DATA and HEADERS frame, this callback may be called after
|
/* For DATA and HEADERS frame, this callback may be called after
|
||||||
on_stream_close_callback. Check that stream still alive. */
|
on_stream_close_callback. Check that stream still alive. */
|
||||||
if(!stream_data) {
|
if (!stream_data) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return on_request_recv(session, session_data, stream_data);
|
return on_request_recv(session, session_data, stream_data);
|
||||||
@@ -508,15 +481,14 @@ header.
|
|||||||
Sending the content of the file is done in ``send_response()`` function::
|
Sending the content of the file is done in ``send_response()`` function::
|
||||||
|
|
||||||
static int send_response(nghttp2_session *session, int32_t stream_id,
|
static int send_response(nghttp2_session *session, int32_t stream_id,
|
||||||
nghttp2_nv *nva, size_t nvlen, int fd)
|
nghttp2_nv *nva, size_t nvlen, int fd) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
nghttp2_data_provider data_prd;
|
nghttp2_data_provider data_prd;
|
||||||
data_prd.source.fd = fd;
|
data_prd.source.fd = fd;
|
||||||
data_prd.read_callback = file_read_callback;
|
data_prd.read_callback = file_read_callback;
|
||||||
|
|
||||||
rv = nghttp2_submit_response(session, stream_id, nva, nvlen, &data_prd);
|
rv = nghttp2_submit_response(session, stream_id, nva, nvlen, &data_prd);
|
||||||
if(rv != 0) {
|
if (rv != 0) {
|
||||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -530,18 +502,19 @@ intended to be used as file descriptor. In this example server, we use
|
|||||||
the file descriptor. We also set the ``file_read_callback()`` callback
|
the file descriptor. We also set the ``file_read_callback()`` callback
|
||||||
function to read the contents of the file::
|
function to read the contents of the file::
|
||||||
|
|
||||||
static ssize_t file_read_callback
|
static ssize_t file_read_callback(nghttp2_session *session _U_,
|
||||||
(nghttp2_session *session, int32_t stream_id,
|
int32_t stream_id _U_, uint8_t *buf,
|
||||||
uint8_t *buf, size_t length, uint32_t *data_flags,
|
size_t length, uint32_t *data_flags,
|
||||||
nghttp2_data_source *source, void *user_data)
|
nghttp2_data_source *source,
|
||||||
{
|
void *user_data _U_) {
|
||||||
int fd = source->fd;
|
int fd = source->fd;
|
||||||
ssize_t r;
|
ssize_t r;
|
||||||
while((r = read(fd, buf, length)) == -1 && errno == EINTR);
|
while ((r = read(fd, buf, length)) == -1 && errno == EINTR)
|
||||||
if(r == -1) {
|
;
|
||||||
|
if (r == -1) {
|
||||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||||
}
|
}
|
||||||
if(r == 0) {
|
if (r == 0) {
|
||||||
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
@@ -559,16 +532,13 @@ remote peer.
|
|||||||
The ``on_stream_close_callback()`` function is invoked when the stream
|
The ``on_stream_close_callback()`` function is invoked when the stream
|
||||||
is about to close::
|
is about to close::
|
||||||
|
|
||||||
static int on_stream_close_callback(nghttp2_session *session,
|
static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||||
int32_t stream_id,
|
uint32_t error_code _U_, void *user_data) {
|
||||||
nghttp2_error_code error_code,
|
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;
|
http2_stream_data *stream_data;
|
||||||
|
|
||||||
stream_data = nghttp2_session_get_stream_user_data(session, stream_id);
|
stream_data = nghttp2_session_get_stream_user_data(session, stream_id);
|
||||||
if(!stream_data) {
|
if (!stream_data) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
remove_stream(session_data, stream_data);
|
remove_stream(session_data, stream_data);
|
||||||
|
|||||||
5
examples/.gitignore
vendored
5
examples/.gitignore
vendored
@@ -2,7 +2,8 @@ client
|
|||||||
libevent-client
|
libevent-client
|
||||||
libevent-server
|
libevent-server
|
||||||
deflate
|
deflate
|
||||||
asio-sv
|
|
||||||
tiny-nghttpd
|
tiny-nghttpd
|
||||||
|
asio-sv
|
||||||
asio-sv2
|
asio-sv2
|
||||||
asio-sv3
|
asio-cl
|
||||||
|
asio-cl2
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
|
|
||||||
if ENABLE_EXAMPLES
|
if ENABLE_EXAMPLES
|
||||||
|
|
||||||
|
AM_CFLAGS = $(WARNCFLAGS)
|
||||||
AM_CPPFLAGS = \
|
AM_CPPFLAGS = \
|
||||||
-Wall \
|
-Wall \
|
||||||
-I$(top_srcdir)/lib/includes \
|
-I$(top_srcdir)/lib/includes \
|
||||||
@@ -32,12 +33,10 @@ AM_CPPFLAGS = \
|
|||||||
@LIBEVENT_OPENSSL_CFLAGS@ \
|
@LIBEVENT_OPENSSL_CFLAGS@ \
|
||||||
@OPENSSL_CFLAGS@ \
|
@OPENSSL_CFLAGS@ \
|
||||||
@DEFS@
|
@DEFS@
|
||||||
AM_LDFLAGS = \
|
LDADD = $(top_builddir)/lib/libnghttp2.la \
|
||||||
|
$(top_builddir)/third-party/libhttp-parser.la \
|
||||||
@LIBEVENT_OPENSSL_LIBS@ \
|
@LIBEVENT_OPENSSL_LIBS@ \
|
||||||
@OPENSSL_LIBS@
|
@OPENSSL_LIBS@
|
||||||
LDADD = \
|
|
||||||
$(top_builddir)/lib/libnghttp2.la \
|
|
||||||
$(top_builddir)/third-party/libhttp-parser.la
|
|
||||||
|
|
||||||
noinst_PROGRAMS = client libevent-client libevent-server deflate
|
noinst_PROGRAMS = client libevent-client libevent-server deflate
|
||||||
|
|
||||||
@@ -59,26 +58,37 @@ endif # ENABLE_TINY_NGHTTPD
|
|||||||
|
|
||||||
if ENABLE_ASIO_LIB
|
if ENABLE_ASIO_LIB
|
||||||
|
|
||||||
noinst_PROGRAMS += asio-sv asio-sv2 asio-sv3
|
noinst_PROGRAMS += asio-sv asio-sv2 asio-cl asio-cl2
|
||||||
|
|
||||||
ASIOCPPFLAGS = ${BOOST_CPPFLAGS} ${AM_CPPFLAGS}
|
# AM_CPPFLAGS must be placed first, so that header file (e.g.,
|
||||||
ASIOLDFLAGS = @JEMALLOC_LIBS@
|
# nghttp2/nghttp2.h) in this package is used rather than installed
|
||||||
ASIOLDADD = $(top_builddir)/src/libnghttp2_asio.la
|
# one.
|
||||||
|
ASIOCPPFLAGS = ${AM_CPPFLAGS} ${BOOST_CPPFLAGS}
|
||||||
|
ASIOLDADD = $(top_builddir)/lib/libnghttp2.la \
|
||||||
|
$(top_builddir)/src/libnghttp2_asio.la @JEMALLOC_LIBS@ \
|
||||||
|
$(top_builddir)/third-party/libhttp-parser.la \
|
||||||
|
${BOOST_LDFLAGS} \
|
||||||
|
${BOOST_ASIO_LIB} \
|
||||||
|
${BOOST_THREAD_LIB} \
|
||||||
|
${BOOST_SYSTEM_LIB} \
|
||||||
|
@OPENSSL_LIBS@ \
|
||||||
|
@APPLDFLAGS@
|
||||||
|
|
||||||
asio_sv_SOURCES = asio-sv.cc
|
asio_sv_SOURCES = asio-sv.cc
|
||||||
asio_sv_CPPFLAGS = ${ASIOCPPFLAGS}
|
asio_sv_CPPFLAGS = ${ASIOCPPFLAGS}
|
||||||
asio_sv_LDFLAGS = ${ASIOLDFLAGS}
|
|
||||||
asio_sv_LDADD = ${ASIOLDADD}
|
asio_sv_LDADD = ${ASIOLDADD}
|
||||||
|
|
||||||
asio_sv2_SOURCES = asio-sv2.cc
|
asio_sv2_SOURCES = asio-sv2.cc
|
||||||
asio_sv2_CPPFLAGS = ${ASIOCPPFLAGS}
|
asio_sv2_CPPFLAGS = ${ASIOCPPFLAGS}
|
||||||
asio_sv2_LDFLAGS = ${ASIOLDFLAGS}
|
|
||||||
asio_sv2_LDADD = ${ASIOLDADD}
|
asio_sv2_LDADD = ${ASIOLDADD}
|
||||||
|
|
||||||
asio_sv3_SOURCES = asio-sv3.cc
|
asio_cl_SOURCES = asio-cl.cc
|
||||||
asio_sv3_CPPFLAGS = ${ASIOCPPFLAGS}
|
asio_cl_CPPFLAGS = ${ASIOCPPFLAGS}
|
||||||
asio_sv3_LDFLAGS = ${ASIOLDFLAGS}
|
asio_cl_LDADD = ${ASIOLDADD}
|
||||||
asio_sv3_LDADD = ${ASIOLDADD}
|
|
||||||
|
asio_cl2_SOURCES = asio-cl2.cc
|
||||||
|
asio_cl2_CPPFLAGS = ${ASIOCPPFLAGS}
|
||||||
|
asio_cl2_LDADD = ${ASIOLDADD}
|
||||||
|
|
||||||
endif # ENABLE_ASIO_LIB
|
endif # ENABLE_ASIO_LIB
|
||||||
|
|
||||||
|
|||||||
96
examples/asio-cl.cc
Normal file
96
examples/asio-cl.cc
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* nghttp2 - HTTP/2 C Library
|
||||||
|
*
|
||||||
|
* Copyright (c) 2015 Tatsuhiro Tsujikawa
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <nghttp2/asio_http2_client.h>
|
||||||
|
|
||||||
|
using boost::asio::ip::tcp;
|
||||||
|
|
||||||
|
using namespace nghttp2::asio_http2;
|
||||||
|
using namespace nghttp2::asio_http2::client;
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
try {
|
||||||
|
if (argc < 2) {
|
||||||
|
std::cerr << "Usage: asio-cl URI" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
boost::system::error_code ec;
|
||||||
|
boost::asio::io_service io_service;
|
||||||
|
|
||||||
|
std::string uri = argv[1];
|
||||||
|
std::string scheme, host, service;
|
||||||
|
|
||||||
|
if (host_service_from_uri(ec, scheme, host, service, uri)) {
|
||||||
|
std::cerr << "error: bad URI: " << ec.message() << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::asio::ssl::context tls_ctx(boost::asio::ssl::context::sslv23);
|
||||||
|
tls_ctx.set_default_verify_paths();
|
||||||
|
// disabled to make development easier...
|
||||||
|
// tls_ctx.set_verify_mode(boost::asio::ssl::verify_peer);
|
||||||
|
configure_tls_context(ec, tls_ctx);
|
||||||
|
|
||||||
|
auto sess = scheme == "https" ? session(io_service, tls_ctx, host, service)
|
||||||
|
: session(io_service, host, service);
|
||||||
|
|
||||||
|
sess.on_connect([&sess, &uri](tcp::resolver::iterator endpoint_it) {
|
||||||
|
boost::system::error_code ec;
|
||||||
|
auto req = sess.submit(ec, "GET", uri);
|
||||||
|
|
||||||
|
if (ec) {
|
||||||
|
std::cerr << "error: " << ec.message() << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
req->on_response([&sess](const response &res) {
|
||||||
|
std::cerr << "HTTP/2 " << res.status_code() << std::endl;
|
||||||
|
for (auto &kv : res.header()) {
|
||||||
|
std::cerr << kv.first << ": " << kv.second.value << "\n";
|
||||||
|
}
|
||||||
|
std::cerr << std::endl;
|
||||||
|
|
||||||
|
res.on_data([&sess](const uint8_t *data, std::size_t len) {
|
||||||
|
std::cerr.write(reinterpret_cast<const char *>(data), len);
|
||||||
|
std::cerr << std::endl;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
req->on_close([&sess](uint32_t error_code) { sess.shutdown(); });
|
||||||
|
});
|
||||||
|
|
||||||
|
sess.on_error([](const boost::system::error_code &ec) {
|
||||||
|
std::cerr << "error: " << ec.message() << std::endl;
|
||||||
|
});
|
||||||
|
|
||||||
|
io_service.run();
|
||||||
|
} catch (std::exception &e) {
|
||||||
|
std::cerr << "exception: " << e.what() << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
134
examples/asio-cl2.cc
Normal file
134
examples/asio-cl2.cc
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
/*
|
||||||
|
* nghttp2 - HTTP/2 C Library
|
||||||
|
*
|
||||||
|
* Copyright (c) 2015 Tatsuhiro Tsujikawa
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <nghttp2/asio_http2_client.h>
|
||||||
|
|
||||||
|
using boost::asio::ip::tcp;
|
||||||
|
|
||||||
|
using namespace nghttp2::asio_http2;
|
||||||
|
using namespace nghttp2::asio_http2::client;
|
||||||
|
|
||||||
|
void print_header(const header_map &h) {
|
||||||
|
for (auto &kv : h) {
|
||||||
|
std::cerr << kv.first << ": " << kv.second.value << "\n";
|
||||||
|
}
|
||||||
|
std::cerr << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_header(const response &res) {
|
||||||
|
std::cerr << "HTTP/2 " << res.status_code() << "\n";
|
||||||
|
print_header(res.header());
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_header(const request &req) {
|
||||||
|
auto &uri = req.uri();
|
||||||
|
std::cerr << req.method() << " " << uri.scheme << "://" << uri.host
|
||||||
|
<< uri.path;
|
||||||
|
if (!uri.raw_query.empty()) {
|
||||||
|
std::cerr << "?" << uri.raw_query;
|
||||||
|
}
|
||||||
|
std::cerr << " HTTP/2\n";
|
||||||
|
print_header(req.header());
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
try {
|
||||||
|
if (argc < 2) {
|
||||||
|
std::cerr << "Usage: asio-cl URI" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
boost::system::error_code ec;
|
||||||
|
boost::asio::io_service io_service;
|
||||||
|
|
||||||
|
std::string uri = argv[1];
|
||||||
|
std::string scheme, host, service;
|
||||||
|
|
||||||
|
if (host_service_from_uri(ec, scheme, host, service, uri)) {
|
||||||
|
std::cerr << "error: bad URI: " << ec.message() << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::asio::ssl::context tls_ctx(boost::asio::ssl::context::sslv23);
|
||||||
|
tls_ctx.set_default_verify_paths();
|
||||||
|
// disabled to make development easier...
|
||||||
|
// tls_ctx.set_verify_mode(boost::asio::ssl::verify_peer);
|
||||||
|
configure_tls_context(ec, tls_ctx);
|
||||||
|
|
||||||
|
auto sess = scheme == "https" ? session(io_service, tls_ctx, host, service)
|
||||||
|
: session(io_service, host, service);
|
||||||
|
|
||||||
|
sess.on_connect([&sess, &uri](tcp::resolver::iterator endpoint_it) {
|
||||||
|
std::cerr << "connected to " << (*endpoint_it).endpoint() << std::endl;
|
||||||
|
boost::system::error_code ec;
|
||||||
|
auto req = sess.submit(ec, "GET", uri, {{"cookie", {"foo=bar", true}}});
|
||||||
|
if (ec) {
|
||||||
|
std::cerr << "error: " << ec.message() << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
req->on_response([&sess, req](const response &res) {
|
||||||
|
std::cerr << "response header was received" << std::endl;
|
||||||
|
print_header(res);
|
||||||
|
|
||||||
|
res.on_data([&sess](const uint8_t *data, std::size_t len) {
|
||||||
|
std::cerr.write(reinterpret_cast<const char *>(data), len);
|
||||||
|
std::cerr << std::endl;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
req->on_close([&sess](uint32_t error_code) {
|
||||||
|
std::cerr << "request done with error_code=" << error_code << std::endl;
|
||||||
|
});
|
||||||
|
|
||||||
|
req->on_push([](const request &push_req) {
|
||||||
|
std::cerr << "push request was received" << std::endl;
|
||||||
|
|
||||||
|
print_header(push_req);
|
||||||
|
|
||||||
|
push_req.on_response([](const response &res) {
|
||||||
|
std::cerr << "push response header was received" << std::endl;
|
||||||
|
|
||||||
|
res.on_data([](const uint8_t *data, std::size_t len) {
|
||||||
|
std::cerr.write(reinterpret_cast<const char *>(data), len);
|
||||||
|
std::cerr << std::endl;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
sess.on_error([](const boost::system::error_code &ec) {
|
||||||
|
std::cerr << "error: " << ec.message() << std::endl;
|
||||||
|
});
|
||||||
|
|
||||||
|
io_service.run();
|
||||||
|
} catch (std::exception &e) {
|
||||||
|
std::cerr << "exception: " << e.what() << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -37,41 +37,111 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <nghttp2/asio_http2.h>
|
#include <nghttp2/asio_http2_server.h>
|
||||||
|
|
||||||
using namespace nghttp2::asio_http2;
|
using namespace nghttp2::asio_http2;
|
||||||
using namespace nghttp2::asio_http2::server;
|
using namespace nghttp2::asio_http2::server;
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char *argv[]) {
|
||||||
{
|
|
||||||
try {
|
try {
|
||||||
// Check command line arguments.
|
// Check command line arguments.
|
||||||
if (argc < 3) {
|
if (argc < 4) {
|
||||||
std::cerr << "Usage: asio-sv <port> <threads> <private-key-file> "
|
std::cerr
|
||||||
<< "<cert-file>\n";
|
<< "Usage: asio-sv <address> <port> <threads> [<private-key-file> "
|
||||||
|
<< "<cert-file>]\n";
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t port = std::stoi(argv[1]);
|
boost::system::error_code ec;
|
||||||
std::size_t num_threads = std::stoi(argv[2]);
|
|
||||||
|
std::string addr = argv[1];
|
||||||
|
std::string port = argv[2];
|
||||||
|
std::size_t num_threads = std::stoi(argv[3]);
|
||||||
|
|
||||||
http2 server;
|
http2 server;
|
||||||
|
|
||||||
server.num_threads(num_threads);
|
server.num_threads(num_threads);
|
||||||
|
|
||||||
if(argc >= 5) {
|
server.handle("/", [](const request &req, const response &res) {
|
||||||
server.tls(argv[3], argv[4]);
|
res.write_head(200, {{"foo", {"bar"}}});
|
||||||
}
|
res.end("hello, world\n");
|
||||||
|
});
|
||||||
|
server.handle("/secret/", [](const request &req, const response &res) {
|
||||||
|
res.write_head(200);
|
||||||
|
res.end("under construction!\n");
|
||||||
|
});
|
||||||
|
server.handle("/push", [](const request &req, const response &res) {
|
||||||
|
boost::system::error_code ec;
|
||||||
|
auto push = res.push(ec, "GET", "/push/1");
|
||||||
|
if (!ec) {
|
||||||
|
push->write_head(200);
|
||||||
|
push->end("server push FTW!\n");
|
||||||
|
}
|
||||||
|
|
||||||
server.listen
|
res.write_head(200);
|
||||||
("*", port,
|
res.end("you'll receive server push!\n");
|
||||||
[](const std::shared_ptr<request>& req,
|
});
|
||||||
const std::shared_ptr<response>& res)
|
server.handle("/delay", [](const request &req, const response &res) {
|
||||||
{
|
res.write_head(200);
|
||||||
res->write_head(200, { header{ "foo", "bar" } });
|
|
||||||
res->end("hello, world");
|
auto timer = std::make_shared<boost::asio::deadline_timer>(
|
||||||
});
|
res.io_service(), boost::posix_time::seconds(3));
|
||||||
} catch (std::exception& e) {
|
auto closed = std::make_shared<bool>();
|
||||||
|
|
||||||
|
res.on_close([timer, closed](uint32_t error_code) {
|
||||||
|
timer->cancel();
|
||||||
|
*closed = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
timer->async_wait([&res, closed](const boost::system::error_code &ec) {
|
||||||
|
if (ec || *closed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.end("finally!\n");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
server.handle("/trailer", [](const request &req, const response &res) {
|
||||||
|
// send trailer part.
|
||||||
|
res.write_head(200, {{"trailers", {"digest"}}});
|
||||||
|
|
||||||
|
std::string body = "nghttp2 FTW!\n";
|
||||||
|
auto left = std::make_shared<size_t>(body.size());
|
||||||
|
|
||||||
|
res.end([&res, body, left](uint8_t *dst, std::size_t len,
|
||||||
|
uint32_t *data_flags) {
|
||||||
|
auto n = std::min(len, *left);
|
||||||
|
std::copy_n(body.c_str() + (body.size() - *left), n, dst);
|
||||||
|
*left -= n;
|
||||||
|
if (*left == 0) {
|
||||||
|
*data_flags |=
|
||||||
|
NGHTTP2_DATA_FLAG_EOF | NGHTTP2_DATA_FLAG_NO_END_STREAM;
|
||||||
|
// RFC 3230 Instance Digests in HTTP. The digest value is
|
||||||
|
// SHA-256 message digest of body.
|
||||||
|
res.write_trailer(
|
||||||
|
{{"digest",
|
||||||
|
{"SHA-256=qqXqskW7F3ueBSvmZRCiSwl2ym4HRO0M/pvQCBlSDis="}}});
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (argc >= 6) {
|
||||||
|
boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23);
|
||||||
|
tls.use_private_key_file(argv[4], boost::asio::ssl::context::pem);
|
||||||
|
tls.use_certificate_chain_file(argv[5]);
|
||||||
|
|
||||||
|
configure_tls_context_easy(ec, tls);
|
||||||
|
|
||||||
|
if (server.listen_and_serve(ec, tls, addr, port)) {
|
||||||
|
std::cerr << "error: " << ec.message() << std::endl;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (server.listen_and_serve(ec, addr, port)) {
|
||||||
|
std::cerr << "error: " << ec.message() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (std::exception &e) {
|
||||||
std::cerr << "exception: " << e.what() << "\n";
|
std::cerr << "exception: " << e.what() << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,75 +35,89 @@
|
|||||||
//
|
//
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#ifdef HAVE_UNISTD_H
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#endif // HAVE_UNISTD_H
|
||||||
|
#ifdef HAVE_FCNTL_H
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#endif // HAVE_FCNTL_H
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <nghttp2/asio_http2.h>
|
#include <nghttp2/asio_http2_server.h>
|
||||||
|
|
||||||
using namespace nghttp2::asio_http2;
|
using namespace nghttp2::asio_http2;
|
||||||
using namespace nghttp2::asio_http2::server;
|
using namespace nghttp2::asio_http2::server;
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char *argv[]) {
|
||||||
{
|
|
||||||
try {
|
try {
|
||||||
// Check command line arguments.
|
// Check command line arguments.
|
||||||
if (argc < 4) {
|
if (argc < 5) {
|
||||||
std::cerr << "Usage: asio-sv2 <port> <threads> <doc-root> "
|
std::cerr << "Usage: asio-sv2 <address> <port> <threads> <doc-root> "
|
||||||
<< "<private-key-file> <cert-file>\n";
|
<< "[<private-key-file> <cert-file>]\n";
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t port = std::stoi(argv[1]);
|
boost::system::error_code ec;
|
||||||
std::size_t num_threads = std::stoi(argv[2]);
|
|
||||||
std::string docroot = argv[3];
|
std::string addr = argv[1];
|
||||||
|
std::string port = argv[2];
|
||||||
|
std::size_t num_threads = std::stoi(argv[3]);
|
||||||
|
std::string docroot = argv[4];
|
||||||
|
|
||||||
http2 server;
|
http2 server;
|
||||||
|
|
||||||
server.num_threads(num_threads);
|
server.num_threads(num_threads);
|
||||||
|
|
||||||
if(argc >= 6) {
|
server.handle("/", [&docroot](const request &req, const response &res) {
|
||||||
server.tls(argv[4], argv[5]);
|
auto path = percent_decode(req.uri().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 header = header_map();
|
||||||
|
|
||||||
|
struct stat stbuf;
|
||||||
|
if (stat(path.c_str(), &stbuf) == 0) {
|
||||||
|
header.emplace("content-length",
|
||||||
|
header_value{std::to_string(stbuf.st_size)});
|
||||||
|
header.emplace("last-modified",
|
||||||
|
header_value{http_date(stbuf.st_mtime)});
|
||||||
|
}
|
||||||
|
res.write_head(200, std::move(header));
|
||||||
|
res.end(file_generator_from_fd(fd));
|
||||||
|
});
|
||||||
|
|
||||||
|
if (argc >= 7) {
|
||||||
|
boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23);
|
||||||
|
tls.use_private_key_file(argv[5], boost::asio::ssl::context::pem);
|
||||||
|
tls.use_certificate_chain_file(argv[6]);
|
||||||
|
|
||||||
|
configure_tls_context_easy(ec, tls);
|
||||||
|
|
||||||
|
if (server.listen_and_serve(ec, tls, addr, port)) {
|
||||||
|
std::cerr << "error: " << ec.message() << std::endl;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (server.listen_and_serve(ec, addr, port)) {
|
||||||
|
std::cerr << "error: " << ec.message() << std::endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} catch (std::exception &e) {
|
||||||
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";
|
std::cerr << "exception: " << e.what() << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,151 +0,0 @@
|
|||||||
/*
|
|
||||||
* nghttp2 - HTTP/2 C Library
|
|
||||||
*
|
|
||||||
* Copyright (c) 2014 Tatsuhiro Tsujikawa
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
* a copy of this software and associated documentation files (the
|
|
||||||
* "Software"), to deal in the Software without restriction, including
|
|
||||||
* without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
* permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
* the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be
|
|
||||||
* included in all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
||||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
// We wrote this code based on the original code which has the
|
|
||||||
// following license:
|
|
||||||
//
|
|
||||||
// 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,14 +26,28 @@
|
|||||||
* This program is written to show how to use nghttp2 API in C and
|
* This program is written to show how to use nghttp2 API in C and
|
||||||
* intentionally made simple.
|
* intentionally made simple.
|
||||||
*/
|
*/
|
||||||
#include <stdint.h>
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif /* HAVE_CONFIG_H */
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#ifdef HAVE_UNISTD_H
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#endif /* HAVE_UNISTD_H */
|
||||||
|
#ifdef HAVE_FCNTL_H
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#endif /* HAVE_FCNTL_H */
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#ifdef HAVE_SYS_SOCKET_H
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
#endif /* HAVE_SYS_SOCKET_H */
|
||||||
|
#ifdef HAVE_NETDB_H
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
|
#endif /* HAVE_NETDB_H */
|
||||||
|
#ifdef HAVE_NETINET_IN_H
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
#endif /* HAVE_NETINET_IN_H */
|
||||||
#include <netinet/tcp.h>
|
#include <netinet/tcp.h>
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
@@ -46,19 +60,19 @@
|
|||||||
#include <openssl/err.h>
|
#include <openssl/err.h>
|
||||||
#include <openssl/conf.h>
|
#include <openssl/conf.h>
|
||||||
|
|
||||||
enum {
|
enum { IO_NONE, WANT_READ, WANT_WRITE };
|
||||||
IO_NONE,
|
|
||||||
WANT_READ,
|
|
||||||
WANT_WRITE
|
|
||||||
};
|
|
||||||
|
|
||||||
#define MAKE_NV(NAME, VALUE) \
|
#define MAKE_NV(NAME, VALUE) \
|
||||||
{(uint8_t*)NAME, (uint8_t*)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \
|
{ \
|
||||||
NGHTTP2_NV_FLAG_NONE}
|
(uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \
|
||||||
|
NGHTTP2_NV_FLAG_NONE \
|
||||||
|
}
|
||||||
|
|
||||||
#define MAKE_NV_CS(NAME, VALUE) \
|
#define MAKE_NV_CS(NAME, VALUE) \
|
||||||
{(uint8_t*)NAME, (uint8_t*)VALUE, sizeof(NAME) - 1, strlen(VALUE), \
|
{ \
|
||||||
NGHTTP2_NV_FLAG_NONE}
|
(uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, strlen(VALUE), \
|
||||||
|
NGHTTP2_NV_FLAG_NONE \
|
||||||
|
}
|
||||||
|
|
||||||
struct Connection {
|
struct Connection {
|
||||||
SSL *ssl;
|
SSL *ssl;
|
||||||
@@ -99,10 +113,9 @@ struct URI {
|
|||||||
* Returns copy of string |s| with the length |len|. The returned
|
* Returns copy of string |s| with the length |len|. The returned
|
||||||
* string is NULL-terminated.
|
* string is NULL-terminated.
|
||||||
*/
|
*/
|
||||||
static char* strcopy(const char *s, size_t len)
|
static char *strcopy(const char *s, size_t len) {
|
||||||
{
|
|
||||||
char *dst;
|
char *dst;
|
||||||
dst = malloc(len+1);
|
dst = malloc(len + 1);
|
||||||
memcpy(dst, s, len);
|
memcpy(dst, s, len);
|
||||||
dst[len] = '\0';
|
dst[len] = '\0';
|
||||||
return dst;
|
return dst;
|
||||||
@@ -111,8 +124,7 @@ static char* strcopy(const char *s, size_t len)
|
|||||||
/*
|
/*
|
||||||
* Prints error message |msg| and exit.
|
* Prints error message |msg| and exit.
|
||||||
*/
|
*/
|
||||||
static void die(const char *msg)
|
static void die(const char *msg) {
|
||||||
{
|
|
||||||
fprintf(stderr, "FATAL: %s\n", msg);
|
fprintf(stderr, "FATAL: %s\n", msg);
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
@@ -121,8 +133,7 @@ static void die(const char *msg)
|
|||||||
* Prints error containing the function name |func| and message |msg|
|
* Prints error containing the function name |func| and message |msg|
|
||||||
* and exit.
|
* and exit.
|
||||||
*/
|
*/
|
||||||
static void dief(const char *func, const char *msg)
|
static void dief(const char *func, const char *msg) {
|
||||||
{
|
|
||||||
fprintf(stderr, "FATAL: %s: %s\n", func, msg);
|
fprintf(stderr, "FATAL: %s: %s\n", func, msg);
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
@@ -131,8 +142,7 @@ static void dief(const char *func, const char *msg)
|
|||||||
* Prints error containing the function name |func| and error code
|
* Prints error containing the function name |func| and error code
|
||||||
* |error_code| and exit.
|
* |error_code| and exit.
|
||||||
*/
|
*/
|
||||||
static void diec(const char *func, int error_code)
|
static void diec(const char *func, int error_code) {
|
||||||
{
|
|
||||||
fprintf(stderr, "FATAL: %s: error_code=%d, msg=%s\n", func, error_code,
|
fprintf(stderr, "FATAL: %s: error_code=%d, msg=%s\n", func, error_code,
|
||||||
nghttp2_strerror(error_code));
|
nghttp2_strerror(error_code));
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
@@ -144,21 +154,19 @@ static void diec(const char *func, int error_code)
|
|||||||
* bytes actually written. See the documentation of
|
* bytes actually written. See the documentation of
|
||||||
* nghttp2_send_callback for the details.
|
* nghttp2_send_callback for the details.
|
||||||
*/
|
*/
|
||||||
static ssize_t send_callback(nghttp2_session *session,
|
static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
|
||||||
const uint8_t *data, size_t length, int flags,
|
size_t length, int flags _U_, void *user_data) {
|
||||||
void *user_data)
|
|
||||||
{
|
|
||||||
struct Connection *connection;
|
struct Connection *connection;
|
||||||
int rv;
|
int rv;
|
||||||
connection = (struct Connection*)user_data;
|
connection = (struct Connection *)user_data;
|
||||||
connection->want_io = IO_NONE;
|
connection->want_io = IO_NONE;
|
||||||
ERR_clear_error();
|
ERR_clear_error();
|
||||||
rv = SSL_write(connection->ssl, data, (int)length);
|
rv = SSL_write(connection->ssl, data, (int)length);
|
||||||
if(rv < 0) {
|
if (rv <= 0) {
|
||||||
int err = SSL_get_error(connection->ssl, rv);
|
int err = SSL_get_error(connection->ssl, rv);
|
||||||
if(err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
|
if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
|
||||||
connection->want_io = (err == SSL_ERROR_WANT_READ ?
|
connection->want_io =
|
||||||
WANT_READ : WANT_WRITE);
|
(err == SSL_ERROR_WANT_READ ? WANT_READ : WANT_WRITE);
|
||||||
rv = NGHTTP2_ERR_WOULDBLOCK;
|
rv = NGHTTP2_ERR_WOULDBLOCK;
|
||||||
} else {
|
} else {
|
||||||
rv = NGHTTP2_ERR_CALLBACK_FAILURE;
|
rv = NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
@@ -173,41 +181,39 @@ static ssize_t send_callback(nghttp2_session *session,
|
|||||||
* |length| bytes. Returns the number of bytes stored in |buf|. See
|
* |length| bytes. Returns the number of bytes stored in |buf|. See
|
||||||
* the documentation of nghttp2_recv_callback for the details.
|
* the documentation of nghttp2_recv_callback for the details.
|
||||||
*/
|
*/
|
||||||
static ssize_t recv_callback(nghttp2_session *session,
|
static ssize_t recv_callback(nghttp2_session *session _U_, uint8_t *buf,
|
||||||
uint8_t *buf, size_t length, int flags,
|
size_t length, int flags _U_, void *user_data) {
|
||||||
void *user_data)
|
|
||||||
{
|
|
||||||
struct Connection *connection;
|
struct Connection *connection;
|
||||||
int rv;
|
int rv;
|
||||||
connection = (struct Connection*)user_data;
|
connection = (struct Connection *)user_data;
|
||||||
connection->want_io = IO_NONE;
|
connection->want_io = IO_NONE;
|
||||||
ERR_clear_error();
|
ERR_clear_error();
|
||||||
rv = SSL_read(connection->ssl, buf, (int)length);
|
rv = SSL_read(connection->ssl, buf, (int)length);
|
||||||
if(rv < 0) {
|
if (rv < 0) {
|
||||||
int err = SSL_get_error(connection->ssl, rv);
|
int err = SSL_get_error(connection->ssl, rv);
|
||||||
if(err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
|
if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
|
||||||
connection->want_io = (err == SSL_ERROR_WANT_READ ?
|
connection->want_io =
|
||||||
WANT_READ : WANT_WRITE);
|
(err == SSL_ERROR_WANT_READ ? WANT_READ : WANT_WRITE);
|
||||||
rv = NGHTTP2_ERR_WOULDBLOCK;
|
rv = NGHTTP2_ERR_WOULDBLOCK;
|
||||||
} else {
|
} else {
|
||||||
rv = NGHTTP2_ERR_CALLBACK_FAILURE;
|
rv = NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
}
|
}
|
||||||
} else if(rv == 0) {
|
} else if (rv == 0) {
|
||||||
rv = NGHTTP2_ERR_EOF;
|
rv = NGHTTP2_ERR_EOF;
|
||||||
}
|
}
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int on_frame_send_callback(nghttp2_session *session,
|
static int on_frame_send_callback(nghttp2_session *session,
|
||||||
const nghttp2_frame *frame, void *user_data)
|
const nghttp2_frame *frame,
|
||||||
{
|
void *user_data _U_) {
|
||||||
size_t i;
|
size_t i;
|
||||||
switch(frame->hd.type) {
|
switch (frame->hd.type) {
|
||||||
case NGHTTP2_HEADERS:
|
case NGHTTP2_HEADERS:
|
||||||
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;
|
const nghttp2_nv *nva = frame->headers.nva;
|
||||||
printf("[INFO] C ----------------------------> S (HEADERS)\n");
|
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);
|
fwrite(nva[i].name, nva[i].namelen, 1, stdout);
|
||||||
printf(": ");
|
printf(": ");
|
||||||
fwrite(nva[i].value, nva[i].valuelen, 1, stdout);
|
fwrite(nva[i].value, nva[i].valuelen, 1, stdout);
|
||||||
@@ -226,18 +232,18 @@ static int on_frame_send_callback(nghttp2_session *session,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int on_frame_recv_callback(nghttp2_session *session,
|
static int on_frame_recv_callback(nghttp2_session *session,
|
||||||
const nghttp2_frame *frame, void *user_data)
|
const nghttp2_frame *frame,
|
||||||
{
|
void *user_data _U_) {
|
||||||
size_t i;
|
size_t i;
|
||||||
switch(frame->hd.type) {
|
switch (frame->hd.type) {
|
||||||
case NGHTTP2_HEADERS:
|
case NGHTTP2_HEADERS:
|
||||||
if(frame->headers.cat == NGHTTP2_HCAT_RESPONSE) {
|
if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE) {
|
||||||
const nghttp2_nv *nva = frame->headers.nva;
|
const nghttp2_nv *nva = frame->headers.nva;
|
||||||
struct Request *req;
|
struct Request *req;
|
||||||
req = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
|
req = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
|
||||||
if(req) {
|
if (req) {
|
||||||
printf("[INFO] C <---------------------------- S (HEADERS)\n");
|
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);
|
fwrite(nva[i].name, nva[i].namelen, 1, stdout);
|
||||||
printf(": ");
|
printf(": ");
|
||||||
fwrite(nva[i].value, nva[i].valuelen, 1, stdout);
|
fwrite(nva[i].value, nva[i].valuelen, 1, stdout);
|
||||||
@@ -262,18 +268,16 @@ static int on_frame_recv_callback(nghttp2_session *session,
|
|||||||
* fetch 1 resource in this program, after reception of the response,
|
* fetch 1 resource in this program, after reception of the response,
|
||||||
* we submit GOAWAY and close the session.
|
* we submit GOAWAY and close the session.
|
||||||
*/
|
*/
|
||||||
static int on_stream_close_callback(nghttp2_session *session,
|
static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||||
int32_t stream_id,
|
uint32_t error_code _U_,
|
||||||
nghttp2_error_code error_code,
|
void *user_data _U_) {
|
||||||
void *user_data)
|
|
||||||
{
|
|
||||||
struct Request *req;
|
struct Request *req;
|
||||||
req = nghttp2_session_get_stream_user_data(session, stream_id);
|
req = nghttp2_session_get_stream_user_data(session, stream_id);
|
||||||
if(req) {
|
if (req) {
|
||||||
int rv;
|
int rv;
|
||||||
rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
|
rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
|
||||||
|
|
||||||
if(rv != 0) {
|
if (rv != 0) {
|
||||||
diec("nghttp2_session_terminate_session", rv);
|
diec("nghttp2_session_terminate_session", rv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -286,16 +290,16 @@ static int on_stream_close_callback(nghttp2_session *session,
|
|||||||
* The implementation of nghttp2_on_data_chunk_recv_callback type. We
|
* The implementation of nghttp2_on_data_chunk_recv_callback type. We
|
||||||
* use this function to print the received response body.
|
* use this function to print the received response body.
|
||||||
*/
|
*/
|
||||||
static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
static int on_data_chunk_recv_callback(nghttp2_session *session,
|
||||||
int32_t stream_id,
|
uint8_t flags _U_, int32_t stream_id,
|
||||||
const uint8_t *data, size_t len,
|
const uint8_t *data, size_t len,
|
||||||
void *user_data)
|
void *user_data _U_) {
|
||||||
{
|
|
||||||
struct Request *req;
|
struct Request *req;
|
||||||
req = nghttp2_session_get_stream_user_data(session, stream_id);
|
req = nghttp2_session_get_stream_user_data(session, stream_id);
|
||||||
if(req) {
|
if (req) {
|
||||||
printf("[INFO] C <---------------------------- S (DATA chunk)\n"
|
printf("[INFO] C <---------------------------- S (DATA chunk)\n"
|
||||||
"%lu bytes\n", (unsigned long int)len);
|
"%lu bytes\n",
|
||||||
|
(unsigned long int)len);
|
||||||
fwrite(data, 1, len, stdout);
|
fwrite(data, 1, len, stdout);
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
@@ -308,23 +312,22 @@ static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
|||||||
* always required. Since we use nghttp2_session_recv(), the
|
* always required. Since we use nghttp2_session_recv(), the
|
||||||
* recv_callback is also required.
|
* recv_callback is also required.
|
||||||
*/
|
*/
|
||||||
static void setup_nghttp2_callbacks(nghttp2_session_callbacks *callbacks)
|
static void setup_nghttp2_callbacks(nghttp2_session_callbacks *callbacks) {
|
||||||
{
|
|
||||||
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_recv_callback(callbacks, recv_callback);
|
nghttp2_session_callbacks_set_recv_callback(callbacks, recv_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_frame_send_callback
|
nghttp2_session_callbacks_set_on_frame_send_callback(callbacks,
|
||||||
(callbacks, on_frame_send_callback);
|
on_frame_send_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_frame_recv_callback
|
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
|
||||||
(callbacks, on_frame_recv_callback);
|
on_frame_recv_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_stream_close_callback
|
nghttp2_session_callbacks_set_on_stream_close_callback(
|
||||||
(callbacks, on_stream_close_callback);
|
callbacks, on_stream_close_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_data_chunk_recv_callback
|
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
|
||||||
(callbacks, on_data_chunk_recv_callback);
|
callbacks, on_data_chunk_recv_callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -332,16 +335,14 @@ static void setup_nghttp2_callbacks(nghttp2_session_callbacks *callbacks)
|
|||||||
* HTTP/2 protocol, if server does not offer HTTP/2 the nghttp2
|
* HTTP/2 protocol, if server does not offer HTTP/2 the nghttp2
|
||||||
* library supports, we terminate program.
|
* library supports, we terminate program.
|
||||||
*/
|
*/
|
||||||
static int select_next_proto_cb(SSL* ssl,
|
static int select_next_proto_cb(SSL *ssl _U_, unsigned char **out,
|
||||||
unsigned char **out, unsigned char *outlen,
|
unsigned char *outlen, const unsigned char *in,
|
||||||
const unsigned char *in, unsigned int inlen,
|
unsigned int inlen, void *arg _U_) {
|
||||||
void *arg)
|
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
/* nghttp2_select_next_protocol() selects HTTP/2 protocol the
|
/* nghttp2_select_next_protocol() selects HTTP/2 protocol the
|
||||||
nghttp2 library supports. */
|
nghttp2 library supports. */
|
||||||
rv = nghttp2_select_next_protocol(out, outlen, in, inlen);
|
rv = nghttp2_select_next_protocol(out, outlen, in, inlen);
|
||||||
if(rv <= 0) {
|
if (rv <= 0) {
|
||||||
die("Server did not advertise HTTP/2 protocol");
|
die("Server did not advertise HTTP/2 protocol");
|
||||||
}
|
}
|
||||||
return SSL_TLSEXT_ERR_OK;
|
return SSL_TLSEXT_ERR_OK;
|
||||||
@@ -350,25 +351,23 @@ static int select_next_proto_cb(SSL* ssl,
|
|||||||
/*
|
/*
|
||||||
* Setup SSL/TLS context.
|
* Setup SSL/TLS context.
|
||||||
*/
|
*/
|
||||||
static void init_ssl_ctx(SSL_CTX *ssl_ctx)
|
static void init_ssl_ctx(SSL_CTX *ssl_ctx) {
|
||||||
{
|
|
||||||
/* Disable SSLv2 and enable all workarounds for buggy servers */
|
/* Disable SSLv2 and enable all workarounds for buggy servers */
|
||||||
SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2);
|
SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2);
|
||||||
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
|
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
|
||||||
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
|
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
|
||||||
/* Set NPN callback */
|
/* Set NPN callback */
|
||||||
SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL);
|
SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ssl_handshake(SSL *ssl, int fd)
|
static void ssl_handshake(SSL *ssl, int fd) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
if(SSL_set_fd(ssl, fd) == 0) {
|
if (SSL_set_fd(ssl, fd) == 0) {
|
||||||
dief("SSL_set_fd", ERR_error_string(ERR_get_error(), NULL));
|
dief("SSL_set_fd", ERR_error_string(ERR_get_error(), NULL));
|
||||||
}
|
}
|
||||||
ERR_clear_error();
|
ERR_clear_error();
|
||||||
rv = SSL_connect(ssl);
|
rv = SSL_connect(ssl);
|
||||||
if(rv <= 0) {
|
if (rv <= 0) {
|
||||||
dief("SSL_connect", ERR_error_string(ERR_get_error(), NULL));
|
dief("SSL_connect", ERR_error_string(ERR_get_error(), NULL));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -377,8 +376,7 @@ static void ssl_handshake(SSL *ssl, int fd)
|
|||||||
* Connects to the host |host| and port |port|. This function returns
|
* Connects to the host |host| and port |port|. This function returns
|
||||||
* the file descriptor of the client socket.
|
* the file descriptor of the client socket.
|
||||||
*/
|
*/
|
||||||
static int connect_to(const char *host, uint16_t port)
|
static int connect_to(const char *host, uint16_t port) {
|
||||||
{
|
|
||||||
struct addrinfo hints;
|
struct addrinfo hints;
|
||||||
int fd = -1;
|
int fd = -1;
|
||||||
int rv;
|
int rv;
|
||||||
@@ -389,17 +387,18 @@ static int connect_to(const char *host, uint16_t port)
|
|||||||
hints.ai_family = AF_UNSPEC;
|
hints.ai_family = AF_UNSPEC;
|
||||||
hints.ai_socktype = SOCK_STREAM;
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
rv = getaddrinfo(host, service, &hints, &res);
|
rv = getaddrinfo(host, service, &hints, &res);
|
||||||
if(rv != 0) {
|
if (rv != 0) {
|
||||||
dief("getaddrinfo", gai_strerror(rv));
|
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);
|
fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
||||||
if(fd == -1) {
|
if (fd == -1) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
while((rv = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 &&
|
while ((rv = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 &&
|
||||||
errno == EINTR);
|
errno == EINTR)
|
||||||
if(rv == 0) {
|
;
|
||||||
|
if (rv == 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
close(fd);
|
close(fd);
|
||||||
@@ -409,25 +408,25 @@ static int connect_to(const char *host, uint16_t port)
|
|||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void make_non_block(int fd)
|
static void make_non_block(int fd) {
|
||||||
{
|
|
||||||
int flags, rv;
|
int flags, rv;
|
||||||
while((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR);
|
while ((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR)
|
||||||
if(flags == -1) {
|
;
|
||||||
|
if (flags == -1) {
|
||||||
dief("fcntl", strerror(errno));
|
dief("fcntl", strerror(errno));
|
||||||
}
|
}
|
||||||
while((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR);
|
while ((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR)
|
||||||
if(rv == -1) {
|
;
|
||||||
|
if (rv == -1) {
|
||||||
dief("fcntl", strerror(errno));
|
dief("fcntl", strerror(errno));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void set_tcp_nodelay(int fd)
|
static void set_tcp_nodelay(int fd) {
|
||||||
{
|
|
||||||
int val = 1;
|
int val = 1;
|
||||||
int rv;
|
int rv;
|
||||||
rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val));
|
rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val));
|
||||||
if(rv == -1) {
|
if (rv == -1) {
|
||||||
dief("setsockopt", strerror(errno));
|
dief("setsockopt", strerror(errno));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -435,15 +434,14 @@ static void set_tcp_nodelay(int fd)
|
|||||||
/*
|
/*
|
||||||
* Update |pollfd| based on the state of |connection|.
|
* Update |pollfd| based on the state of |connection|.
|
||||||
*/
|
*/
|
||||||
static void ctl_poll(struct pollfd *pollfd, struct Connection *connection)
|
static void ctl_poll(struct pollfd *pollfd, struct Connection *connection) {
|
||||||
{
|
|
||||||
pollfd->events = 0;
|
pollfd->events = 0;
|
||||||
if(nghttp2_session_want_read(connection->session) ||
|
if (nghttp2_session_want_read(connection->session) ||
|
||||||
connection->want_io == WANT_READ) {
|
connection->want_io == WANT_READ) {
|
||||||
pollfd->events |= POLLIN;
|
pollfd->events |= POLLIN;
|
||||||
}
|
}
|
||||||
if(nghttp2_session_want_write(connection->session) ||
|
if (nghttp2_session_want_write(connection->session) ||
|
||||||
connection->want_io == WANT_WRITE) {
|
connection->want_io == WANT_WRITE) {
|
||||||
pollfd->events |= POLLOUT;
|
pollfd->events |= POLLOUT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -453,24 +451,20 @@ static void ctl_poll(struct pollfd *pollfd, struct Connection *connection)
|
|||||||
* function does not send packets; just append the request to the
|
* function does not send packets; just append the request to the
|
||||||
* internal queue in |connection->session|.
|
* internal queue in |connection->session|.
|
||||||
*/
|
*/
|
||||||
static void submit_request(struct Connection *connection, struct Request *req)
|
static void submit_request(struct Connection *connection, struct Request *req) {
|
||||||
{
|
|
||||||
int32_t stream_id;
|
int32_t stream_id;
|
||||||
const nghttp2_nv nva[] = {
|
/* Make sure that the last item is NULL */
|
||||||
/* Make sure that the last item is NULL */
|
const nghttp2_nv nva[] = {MAKE_NV(":method", "GET"),
|
||||||
MAKE_NV(":method", "GET"),
|
MAKE_NV_CS(":path", req->path),
|
||||||
MAKE_NV_CS(":path", req->path),
|
MAKE_NV(":scheme", "https"),
|
||||||
MAKE_NV(":scheme", "https"),
|
MAKE_NV_CS(":authority", req->hostport),
|
||||||
MAKE_NV_CS(":authority", req->hostport),
|
MAKE_NV("accept", "*/*"),
|
||||||
MAKE_NV("accept", "*/*"),
|
MAKE_NV("user-agent", "nghttp2/" NGHTTP2_VERSION)};
|
||||||
MAKE_NV("user-agent", "nghttp2/"NGHTTP2_VERSION)
|
|
||||||
};
|
|
||||||
|
|
||||||
stream_id = nghttp2_submit_request(connection->session, NULL,
|
stream_id = nghttp2_submit_request(connection->session, NULL, nva,
|
||||||
nva, sizeof(nva)/sizeof(nva[0]),
|
sizeof(nva) / sizeof(nva[0]), NULL, req);
|
||||||
NULL, req);
|
|
||||||
|
|
||||||
if(stream_id < 0) {
|
if (stream_id < 0) {
|
||||||
diec("nghttp2_submit_request", stream_id);
|
diec("nghttp2_submit_request", stream_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -481,21 +475,19 @@ static void submit_request(struct Connection *connection, struct Request *req)
|
|||||||
/*
|
/*
|
||||||
* Performs the network I/O.
|
* Performs the network I/O.
|
||||||
*/
|
*/
|
||||||
static void exec_io(struct Connection *connection)
|
static void exec_io(struct Connection *connection) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
rv = nghttp2_session_recv(connection->session);
|
rv = nghttp2_session_recv(connection->session);
|
||||||
if(rv != 0) {
|
if (rv != 0) {
|
||||||
diec("nghttp2_session_recv", rv);
|
diec("nghttp2_session_recv", rv);
|
||||||
}
|
}
|
||||||
rv = nghttp2_session_send(connection->session);
|
rv = nghttp2_session_send(connection->session);
|
||||||
if(rv != 0) {
|
if (rv != 0) {
|
||||||
diec("nghttp2_session_send", rv);
|
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->host = strcopy(uri->host, uri->hostlen);
|
||||||
req->port = uri->port;
|
req->port = uri->port;
|
||||||
req->path = strcopy(uri->path, uri->pathlen);
|
req->path = strcopy(uri->path, uri->pathlen);
|
||||||
@@ -503,8 +495,7 @@ static void request_init(struct Request *req, const struct URI *uri)
|
|||||||
req->stream_id = -1;
|
req->stream_id = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void request_free(struct Request *req)
|
static void request_free(struct Request *req) {
|
||||||
{
|
|
||||||
free(req->host);
|
free(req->host);
|
||||||
free(req->path);
|
free(req->path);
|
||||||
free(req->hostport);
|
free(req->hostport);
|
||||||
@@ -513,8 +504,7 @@ static void request_free(struct Request *req)
|
|||||||
/*
|
/*
|
||||||
* Fetches the resource denoted by |uri|.
|
* Fetches the resource denoted by |uri|.
|
||||||
*/
|
*/
|
||||||
static void fetch_uri(const struct URI *uri)
|
static void fetch_uri(const struct URI *uri) {
|
||||||
{
|
|
||||||
nghttp2_session_callbacks *callbacks;
|
nghttp2_session_callbacks *callbacks;
|
||||||
int fd;
|
int fd;
|
||||||
SSL_CTX *ssl_ctx;
|
SSL_CTX *ssl_ctx;
|
||||||
@@ -529,16 +519,16 @@ static void fetch_uri(const struct URI *uri)
|
|||||||
|
|
||||||
/* Establish connection and setup SSL */
|
/* Establish connection and setup SSL */
|
||||||
fd = connect_to(req.host, req.port);
|
fd = connect_to(req.host, req.port);
|
||||||
if(fd == -1) {
|
if (fd == -1) {
|
||||||
die("Could not open file descriptor");
|
die("Could not open file descriptor");
|
||||||
}
|
}
|
||||||
ssl_ctx = SSL_CTX_new(SSLv23_client_method());
|
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));
|
dief("SSL_CTX_new", ERR_error_string(ERR_get_error(), NULL));
|
||||||
}
|
}
|
||||||
init_ssl_ctx(ssl_ctx);
|
init_ssl_ctx(ssl_ctx);
|
||||||
ssl = SSL_new(ssl_ctx);
|
ssl = SSL_new(ssl_ctx);
|
||||||
if(ssl == NULL) {
|
if (ssl == NULL) {
|
||||||
dief("SSL_new", ERR_error_string(ERR_get_error(), NULL));
|
dief("SSL_new", ERR_error_string(ERR_get_error(), NULL));
|
||||||
}
|
}
|
||||||
/* To simplify the program, we perform SSL/TLS handshake in blocking
|
/* To simplify the program, we perform SSL/TLS handshake in blocking
|
||||||
@@ -548,10 +538,6 @@ static void fetch_uri(const struct URI *uri)
|
|||||||
connection.ssl = ssl;
|
connection.ssl = ssl;
|
||||||
connection.want_io = IO_NONE;
|
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);
|
|
||||||
|
|
||||||
/* Here make file descriptor non-block */
|
/* Here make file descriptor non-block */
|
||||||
make_non_block(fd);
|
make_non_block(fd);
|
||||||
set_tcp_nodelay(fd);
|
set_tcp_nodelay(fd);
|
||||||
@@ -560,21 +546,22 @@ static void fetch_uri(const struct URI *uri)
|
|||||||
|
|
||||||
rv = nghttp2_session_callbacks_new(&callbacks);
|
rv = nghttp2_session_callbacks_new(&callbacks);
|
||||||
|
|
||||||
if(rv != 0) {
|
if (rv != 0) {
|
||||||
diec("nghttp2_session_callbacks_new", rv);
|
diec("nghttp2_session_callbacks_new", rv);
|
||||||
}
|
}
|
||||||
|
|
||||||
setup_nghttp2_callbacks(callbacks);
|
setup_nghttp2_callbacks(callbacks);
|
||||||
|
|
||||||
rv = nghttp2_session_client_new(&connection.session, callbacks,
|
rv = nghttp2_session_client_new(&connection.session, callbacks, &connection);
|
||||||
&connection);
|
|
||||||
|
|
||||||
nghttp2_session_callbacks_del(callbacks);
|
nghttp2_session_callbacks_del(callbacks);
|
||||||
|
|
||||||
if(rv != 0) {
|
if (rv != 0) {
|
||||||
diec("nghttp2_session_client_new", rv);
|
diec("nghttp2_session_client_new", rv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nghttp2_submit_settings(connection.session, NGHTTP2_FLAG_NONE, NULL, 0);
|
||||||
|
|
||||||
/* Submit the HTTP request to the outbound queue. */
|
/* Submit the HTTP request to the outbound queue. */
|
||||||
submit_request(&connection, &req);
|
submit_request(&connection, &req);
|
||||||
|
|
||||||
@@ -582,16 +569,16 @@ static void fetch_uri(const struct URI *uri)
|
|||||||
ctl_poll(pollfds, &connection);
|
ctl_poll(pollfds, &connection);
|
||||||
|
|
||||||
/* Event loop */
|
/* Event loop */
|
||||||
while(nghttp2_session_want_read(connection.session) ||
|
while (nghttp2_session_want_read(connection.session) ||
|
||||||
nghttp2_session_want_write(connection.session)) {
|
nghttp2_session_want_write(connection.session)) {
|
||||||
int nfds = poll(pollfds, npollfds, -1);
|
int nfds = poll(pollfds, npollfds, -1);
|
||||||
if(nfds == -1) {
|
if (nfds == -1) {
|
||||||
dief("poll", strerror(errno));
|
dief("poll", strerror(errno));
|
||||||
}
|
}
|
||||||
if(pollfds[0].revents & (POLLIN | POLLOUT)) {
|
if (pollfds[0].revents & (POLLIN | POLLOUT)) {
|
||||||
exec_io(&connection);
|
exec_io(&connection);
|
||||||
}
|
}
|
||||||
if((pollfds[0].revents & POLLHUP) || (pollfds[0].revents & POLLERR)) {
|
if ((pollfds[0].revents & POLLHUP) || (pollfds[0].revents & POLLERR)) {
|
||||||
die("Connection error");
|
die("Connection error");
|
||||||
}
|
}
|
||||||
ctl_poll(pollfds, &connection);
|
ctl_poll(pollfds, &connection);
|
||||||
@@ -607,96 +594,94 @@ static void fetch_uri(const struct URI *uri)
|
|||||||
request_free(&req);
|
request_free(&req);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_uri(struct URI *res, const char *uri)
|
static int parse_uri(struct URI *res, const char *uri) {
|
||||||
{
|
|
||||||
/* We only interested in https */
|
/* We only interested in https */
|
||||||
size_t len, i, offset;
|
size_t len, i, offset;
|
||||||
int ipv6addr = 0;
|
int ipv6addr = 0;
|
||||||
memset(res, 0, sizeof(struct URI));
|
memset(res, 0, sizeof(struct URI));
|
||||||
len = strlen(uri);
|
len = strlen(uri);
|
||||||
if(len < 9 || memcmp("https://", uri, 8) != 0) {
|
if (len < 9 || memcmp("https://", uri, 8) != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
offset = 8;
|
offset = 8;
|
||||||
res->host = res->hostport = &uri[offset];
|
res->host = res->hostport = &uri[offset];
|
||||||
res->hostlen = 0;
|
res->hostlen = 0;
|
||||||
if(uri[offset] == '[') {
|
if (uri[offset] == '[') {
|
||||||
/* IPv6 literal address */
|
/* IPv6 literal address */
|
||||||
++offset;
|
++offset;
|
||||||
++res->host;
|
++res->host;
|
||||||
ipv6addr = 1;
|
ipv6addr = 1;
|
||||||
for(i = offset; i < len; ++i) {
|
for (i = offset; i < len; ++i) {
|
||||||
if(uri[i] == ']') {
|
if (uri[i] == ']') {
|
||||||
res->hostlen = i-offset;
|
res->hostlen = i - offset;
|
||||||
offset = i+1;
|
offset = i + 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const char delims[] = ":/?#";
|
const char delims[] = ":/?#";
|
||||||
for(i = offset; i < len; ++i) {
|
for (i = offset; i < len; ++i) {
|
||||||
if(strchr(delims, uri[i]) != NULL) {
|
if (strchr(delims, uri[i]) != NULL) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
res->hostlen = i-offset;
|
res->hostlen = i - offset;
|
||||||
offset = i;
|
offset = i;
|
||||||
}
|
}
|
||||||
if(res->hostlen == 0) {
|
if (res->hostlen == 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
/* Assuming https */
|
/* Assuming https */
|
||||||
res->port = 443;
|
res->port = 443;
|
||||||
if(offset < len) {
|
if (offset < len) {
|
||||||
if(uri[offset] == ':') {
|
if (uri[offset] == ':') {
|
||||||
/* port */
|
/* port */
|
||||||
const char delims[] = "/?#";
|
const char delims[] = "/?#";
|
||||||
int port = 0;
|
int port = 0;
|
||||||
++offset;
|
++offset;
|
||||||
for(i = offset; i < len; ++i) {
|
for (i = offset; i < len; ++i) {
|
||||||
if(strchr(delims, uri[i]) != NULL) {
|
if (strchr(delims, uri[i]) != NULL) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if('0' <= uri[i] && uri[i] <= '9') {
|
if ('0' <= uri[i] && uri[i] <= '9') {
|
||||||
port *= 10;
|
port *= 10;
|
||||||
port += uri[i]-'0';
|
port += uri[i] - '0';
|
||||||
if(port > 65535) {
|
if (port > 65535) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(port == 0) {
|
if (port == 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
offset = i;
|
offset = i;
|
||||||
res->port = port;
|
res->port = port;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
res->hostportlen = uri+offset+ipv6addr-res->host;
|
res->hostportlen = uri + offset + ipv6addr - res->host;
|
||||||
for(i = offset; i < len; ++i) {
|
for (i = offset; i < len; ++i) {
|
||||||
if(uri[i] == '#') {
|
if (uri[i] == '#') {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(i-offset == 0) {
|
if (i - offset == 0) {
|
||||||
res->path = "/";
|
res->path = "/";
|
||||||
res->pathlen = 1;
|
res->pathlen = 1;
|
||||||
} else {
|
} else {
|
||||||
res->path = &uri[offset];
|
res->path = &uri[offset];
|
||||||
res->pathlen = i-offset;
|
res->pathlen = i - offset;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv) {
|
||||||
{
|
|
||||||
struct URI uri;
|
struct URI uri;
|
||||||
struct sigaction act;
|
struct sigaction act;
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
if(argc < 2) {
|
if (argc < 2) {
|
||||||
die("Specify a https URI");
|
die("Specify a https URI");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -704,13 +689,13 @@ int main(int argc, char **argv)
|
|||||||
act.sa_handler = SIG_IGN;
|
act.sa_handler = SIG_IGN;
|
||||||
sigaction(SIGPIPE, &act, 0);
|
sigaction(SIGPIPE, &act, 0);
|
||||||
|
|
||||||
OPENSSL_config(NULL);
|
|
||||||
OpenSSL_add_all_algorithms();
|
|
||||||
SSL_load_error_strings();
|
SSL_load_error_strings();
|
||||||
SSL_library_init();
|
SSL_library_init();
|
||||||
|
OpenSSL_add_all_algorithms();
|
||||||
|
OPENSSL_config(NULL);
|
||||||
|
|
||||||
rv = parse_uri(&uri, argv[1]);
|
rv = parse_uri(&uri, argv[1]);
|
||||||
if(rv != 0) {
|
if (rv != 0) {
|
||||||
die("parse_uri failed");
|
die("parse_uri failed");
|
||||||
}
|
}
|
||||||
fetch_uri(&uri);
|
fetch_uri(&uri);
|
||||||
|
|||||||
@@ -22,48 +22,48 @@
|
|||||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif /* !HAVE_CONFIG_H */
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <nghttp2/nghttp2.h>
|
#include <nghttp2/nghttp2.h>
|
||||||
|
|
||||||
#define MAKE_NV(K, V) \
|
#define MAKE_NV(K, V) \
|
||||||
{ (uint8_t*)K, (uint8_t*)V, sizeof(K) - 1, sizeof(V) - 1, \
|
{ \
|
||||||
NGHTTP2_NV_FLAG_NONE }
|
(uint8_t *) K, (uint8_t *)V, sizeof(K) - 1, sizeof(V) - 1, \
|
||||||
|
NGHTTP2_NV_FLAG_NONE \
|
||||||
|
}
|
||||||
|
|
||||||
static void deflate(nghttp2_hd_deflater *deflater,
|
static void deflate(nghttp2_hd_deflater *deflater,
|
||||||
nghttp2_hd_inflater *inflater,
|
nghttp2_hd_inflater *inflater, const nghttp2_nv *const nva,
|
||||||
const nghttp2_nv * const nva, size_t nvlen);
|
size_t nvlen);
|
||||||
|
|
||||||
static int inflate_header_block(nghttp2_hd_inflater *inflater,
|
static int inflate_header_block(nghttp2_hd_inflater *inflater, uint8_t *in,
|
||||||
uint8_t *in, size_t inlen, int final);
|
size_t inlen, int final);
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc _U_, char **argv _U_) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
nghttp2_hd_deflater *deflater;
|
nghttp2_hd_deflater *deflater;
|
||||||
nghttp2_hd_inflater *inflater;
|
nghttp2_hd_inflater *inflater;
|
||||||
/* Define 1st header set. This is looks like a HTTP request. */
|
/* Define 1st header set. This is looks like a HTTP request. */
|
||||||
nghttp2_nv nva1[] = {
|
nghttp2_nv nva1[] = {
|
||||||
MAKE_NV(":scheme", "https"),
|
MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "example.org"),
|
||||||
MAKE_NV(":authority", "example.org"),
|
MAKE_NV(":path", "/"), MAKE_NV("user-agent", "libnghttp2"),
|
||||||
MAKE_NV(":path", "/"),
|
MAKE_NV("accept-encoding", "gzip, deflate")};
|
||||||
MAKE_NV("user-agent", "libnghttp2"),
|
|
||||||
MAKE_NV("accept-encoding", "gzip, deflate")
|
|
||||||
};
|
|
||||||
/* Define 2nd header set */
|
/* Define 2nd header set */
|
||||||
nghttp2_nv nva2[] = {
|
nghttp2_nv nva2[] = {MAKE_NV(":scheme", "https"),
|
||||||
MAKE_NV(":scheme", "https"),
|
MAKE_NV(":authority", "example.org"),
|
||||||
MAKE_NV(":authority", "example.org"),
|
MAKE_NV(":path", "/stylesheet/style.css"),
|
||||||
MAKE_NV(":path", "/stylesheet/style.css"),
|
MAKE_NV("user-agent", "libnghttp2"),
|
||||||
MAKE_NV("user-agent", "libnghttp2"),
|
MAKE_NV("accept-encoding", "gzip, deflate"),
|
||||||
MAKE_NV("accept-encoding", "gzip, deflate"),
|
MAKE_NV("referer", "https://example.org")};
|
||||||
MAKE_NV("referer", "https://example.org")
|
|
||||||
};
|
|
||||||
|
|
||||||
rv = nghttp2_hd_deflate_new(&deflater, 4096);
|
rv = nghttp2_hd_deflate_new(&deflater, 4096);
|
||||||
|
|
||||||
if(rv != 0) {
|
if (rv != 0) {
|
||||||
fprintf(stderr, "nghttp2_hd_deflate_init failed with error: %s\n",
|
fprintf(stderr, "nghttp2_hd_deflate_init failed with error: %s\n",
|
||||||
nghttp2_strerror(rv));
|
nghttp2_strerror(rv));
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
@@ -71,7 +71,7 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
rv = nghttp2_hd_inflate_new(&inflater);
|
rv = nghttp2_hd_inflate_new(&inflater);
|
||||||
|
|
||||||
if(rv != 0) {
|
if (rv != 0) {
|
||||||
fprintf(stderr, "nghttp2_hd_inflate_init failed with error: %s\n",
|
fprintf(stderr, "nghttp2_hd_inflate_init failed with error: %s\n",
|
||||||
nghttp2_strerror(rv));
|
nghttp2_strerror(rv));
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
@@ -91,9 +91,8 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void deflate(nghttp2_hd_deflater *deflater,
|
static void deflate(nghttp2_hd_deflater *deflater,
|
||||||
nghttp2_hd_inflater *inflater,
|
nghttp2_hd_inflater *inflater, const nghttp2_nv *const nva,
|
||||||
const nghttp2_nv * const nva, size_t nvlen)
|
size_t nvlen) {
|
||||||
{
|
|
||||||
ssize_t rv;
|
ssize_t rv;
|
||||||
uint8_t *buf;
|
uint8_t *buf;
|
||||||
size_t buflen;
|
size_t buflen;
|
||||||
@@ -103,13 +102,13 @@ static void deflate(nghttp2_hd_deflater *deflater,
|
|||||||
|
|
||||||
sum = 0;
|
sum = 0;
|
||||||
|
|
||||||
for(i = 0; i < nvlen; ++i) {
|
for (i = 0; i < nvlen; ++i) {
|
||||||
sum += nva[i].namelen + nva[i].valuelen;
|
sum += nva[i].namelen + nva[i].valuelen;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Input (%zu byte(s)):\n\n", sum);
|
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);
|
fwrite(nva[i].name, nva[i].namelen, 1, stdout);
|
||||||
printf(": ");
|
printf(": ");
|
||||||
fwrite(nva[i].value, nva[i].valuelen, 1, stdout);
|
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);
|
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",
|
fprintf(stderr, "nghttp2_hd_deflate_hd() failed with error: %s\n",
|
||||||
nghttp2_strerror((int)rv));
|
nghttp2_strerror((int)rv));
|
||||||
|
|
||||||
@@ -132,17 +131,17 @@ static void deflate(nghttp2_hd_deflater *deflater,
|
|||||||
|
|
||||||
outlen = rv;
|
outlen = rv;
|
||||||
|
|
||||||
printf("\nDeflate (%zu byte(s), ratio %.02f):\n\n",
|
printf("\nDeflate (%zu byte(s), ratio %.02f):\n\n", outlen,
|
||||||
outlen, sum == 0 ? 0 : (double)outlen / sum);
|
sum == 0 ? 0 : (double)outlen / sum);
|
||||||
|
|
||||||
for(i = 0; i < outlen; ++i) {
|
for (i = 0; i < outlen; ++i) {
|
||||||
if((i & 0x0fu) == 0) {
|
if ((i & 0x0fu) == 0) {
|
||||||
printf("%08zX: ", i);
|
printf("%08zX: ", i);
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("%02X ", buf[i]);
|
printf("%02X ", buf[i]);
|
||||||
|
|
||||||
if(((i + 1) & 0x0fu) == 0) {
|
if (((i + 1) & 0x0fu) == 0) {
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -153,7 +152,7 @@ static void deflate(nghttp2_hd_deflater *deflater,
|
|||||||
header data. */
|
header data. */
|
||||||
rv = inflate_header_block(inflater, buf, outlen, 1);
|
rv = inflate_header_block(inflater, buf, outlen, 1);
|
||||||
|
|
||||||
if(rv != 0) {
|
if (rv != 0) {
|
||||||
free(buf);
|
free(buf);
|
||||||
|
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
@@ -165,20 +164,18 @@ static void deflate(nghttp2_hd_deflater *deflater,
|
|||||||
free(buf);
|
free(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
int inflate_header_block(nghttp2_hd_inflater *inflater,
|
int inflate_header_block(nghttp2_hd_inflater *inflater, uint8_t *in,
|
||||||
uint8_t *in, size_t inlen, int final)
|
size_t inlen, int final) {
|
||||||
{
|
|
||||||
ssize_t rv;
|
ssize_t rv;
|
||||||
|
|
||||||
for(;;) {
|
for (;;) {
|
||||||
nghttp2_nv nv;
|
nghttp2_nv nv;
|
||||||
int inflate_flags = 0;
|
int inflate_flags = 0;
|
||||||
size_t proclen;
|
size_t proclen;
|
||||||
|
|
||||||
rv = nghttp2_hd_inflate_hd(inflater, &nv, &inflate_flags,
|
rv = nghttp2_hd_inflate_hd(inflater, &nv, &inflate_flags, in, inlen, final);
|
||||||
in, inlen, final);
|
|
||||||
|
|
||||||
if(rv < 0) {
|
if (rv < 0) {
|
||||||
fprintf(stderr, "inflate failed with error code %zd", rv);
|
fprintf(stderr, "inflate failed with error code %zd", rv);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -188,20 +185,19 @@ int inflate_header_block(nghttp2_hd_inflater *inflater,
|
|||||||
in += proclen;
|
in += proclen;
|
||||||
inlen -= proclen;
|
inlen -= proclen;
|
||||||
|
|
||||||
if(inflate_flags & NGHTTP2_HD_INFLATE_EMIT) {
|
if (inflate_flags & NGHTTP2_HD_INFLATE_EMIT) {
|
||||||
fwrite(nv.name, nv.namelen, 1, stderr);
|
fwrite(nv.name, nv.namelen, 1, stderr);
|
||||||
fprintf(stderr, ": ");
|
fprintf(stderr, ": ");
|
||||||
fwrite(nv.value, nv.valuelen, 1, stderr);
|
fwrite(nv.value, nv.valuelen, 1, stderr);
|
||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(inflate_flags & NGHTTP2_HD_INFLATE_FINAL) {
|
if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) {
|
||||||
nghttp2_hd_inflate_end_headers(inflater);
|
nghttp2_hd_inflate_end_headers(inflater);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 &&
|
if ((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && inlen == 0) {
|
||||||
inlen == 0) {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,10 +22,20 @@
|
|||||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif /* HAVE_CONFIG_H */
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#ifdef HAVE_UNISTD_H
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#endif /* HAVE_UNISTD_H */
|
||||||
|
#ifdef HAVE_SYS_SOCKET_H
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
#endif /* HAVE_SYS_SOCKET_H */
|
||||||
|
#ifdef HAVE_NETINET_IN_H
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
#endif /* HAVE_NETINET_IN_H */
|
||||||
#include <netinet/tcp.h>
|
#include <netinet/tcp.h>
|
||||||
#include <err.h>
|
#include <err.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
@@ -43,14 +53,14 @@
|
|||||||
|
|
||||||
#include "http-parser/http_parser.h"
|
#include "http-parser/http_parser.h"
|
||||||
|
|
||||||
#define ARRLEN(x) (sizeof(x)/sizeof(x[0]))
|
#define ARRLEN(x) (sizeof(x) / sizeof(x[0]))
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
/* The NULL-terminated URI string to retreive. */
|
/* The NULL-terminated URI string to retrieve. */
|
||||||
const char *uri;
|
const char *uri;
|
||||||
/* Parsed result of the |uri| */
|
/* Parsed result of the |uri| */
|
||||||
struct http_parser_url *u;
|
struct http_parser_url *u;
|
||||||
/* The authroity portion of the |uri|, not NULL-terminated */
|
/* The authority portion of the |uri|, not NULL-terminated */
|
||||||
char *authority;
|
char *authority;
|
||||||
/* The path portion of the |uri|, including query, not
|
/* The path portion of the |uri|, including query, not
|
||||||
NULL-terminated */
|
NULL-terminated */
|
||||||
@@ -70,9 +80,8 @@ typedef struct {
|
|||||||
http2_stream_data *stream_data;
|
http2_stream_data *stream_data;
|
||||||
} http2_session_data;
|
} http2_session_data;
|
||||||
|
|
||||||
static http2_stream_data* create_http2_stream_data(const char *uri,
|
static http2_stream_data *create_http2_stream_data(const char *uri,
|
||||||
struct http_parser_url *u)
|
struct http_parser_url *u) {
|
||||||
{
|
|
||||||
/* MAX 5 digits (max 65535) + 1 ':' + 1 NULL (because of snprintf) */
|
/* MAX 5 digits (max 65535) + 1 ':' + 1 NULL (because of snprintf) */
|
||||||
size_t extra = 7;
|
size_t extra = 7;
|
||||||
http2_stream_data *stream_data = malloc(sizeof(http2_stream_data));
|
http2_stream_data *stream_data = malloc(sizeof(http2_stream_data));
|
||||||
@@ -83,48 +92,51 @@ static http2_stream_data* create_http2_stream_data(const char *uri,
|
|||||||
|
|
||||||
stream_data->authoritylen = u->field_data[UF_HOST].len;
|
stream_data->authoritylen = u->field_data[UF_HOST].len;
|
||||||
stream_data->authority = malloc(stream_data->authoritylen + extra);
|
stream_data->authority = malloc(stream_data->authoritylen + extra);
|
||||||
memcpy(stream_data->authority,
|
memcpy(stream_data->authority, &uri[u->field_data[UF_HOST].off],
|
||||||
&uri[u->field_data[UF_HOST].off], u->field_data[UF_HOST].len);
|
u->field_data[UF_HOST].len);
|
||||||
if(u->field_set & (1 << UF_PORT)) {
|
if (u->field_set & (1 << UF_PORT)) {
|
||||||
stream_data->authoritylen +=
|
stream_data->authoritylen +=
|
||||||
snprintf(stream_data->authority + u->field_data[UF_HOST].len, extra,
|
snprintf(stream_data->authority + u->field_data[UF_HOST].len, extra,
|
||||||
":%u", u->port);
|
":%u", u->port);
|
||||||
}
|
}
|
||||||
|
|
||||||
stream_data->pathlen = 0;
|
/* If we don't have path in URI, we use "/" as path. */
|
||||||
if(u->field_set & (1 << UF_PATH)) {
|
stream_data->pathlen = 1;
|
||||||
|
if (u->field_set & (1 << UF_PATH)) {
|
||||||
stream_data->pathlen = u->field_data[UF_PATH].len;
|
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 */
|
/* +1 for '?' character */
|
||||||
stream_data->pathlen += u->field_data[UF_QUERY].len + 1;
|
stream_data->pathlen += u->field_data[UF_QUERY].len + 1;
|
||||||
}
|
}
|
||||||
if(stream_data->pathlen > 0) {
|
|
||||||
stream_data->path = malloc(stream_data->pathlen);
|
stream_data->path = malloc(stream_data->pathlen);
|
||||||
if(u->field_set & (1 << UF_PATH)) {
|
if (u->field_set & (1 << UF_PATH)) {
|
||||||
memcpy(stream_data->path,
|
memcpy(stream_data->path, &uri[u->field_data[UF_PATH].off],
|
||||||
&uri[u->field_data[UF_PATH].off], u->field_data[UF_PATH].len);
|
u->field_data[UF_PATH].len);
|
||||||
}
|
|
||||||
if(u->field_set & (1 << UF_QUERY)) {
|
|
||||||
memcpy(stream_data->path + u->field_data[UF_PATH].len + 1,
|
|
||||||
&uri[u->field_data[UF_QUERY].off], u->field_data[UF_QUERY].len);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
stream_data->path = NULL;
|
stream_data->path[0] = '/';
|
||||||
}
|
}
|
||||||
|
if (u->field_set & (1 << UF_QUERY)) {
|
||||||
|
stream_data->path[stream_data->pathlen - u->field_data[UF_QUERY].len - 1] =
|
||||||
|
'?';
|
||||||
|
memcpy(stream_data->path + stream_data->pathlen -
|
||||||
|
u->field_data[UF_QUERY].len,
|
||||||
|
&uri[u->field_data[UF_QUERY].off], u->field_data[UF_QUERY].len);
|
||||||
|
}
|
||||||
|
|
||||||
return stream_data;
|
return stream_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void delete_http2_stream_data(http2_stream_data *stream_data)
|
static void delete_http2_stream_data(http2_stream_data *stream_data) {
|
||||||
{
|
|
||||||
free(stream_data->path);
|
free(stream_data->path);
|
||||||
free(stream_data->authority);
|
free(stream_data->authority);
|
||||||
free(stream_data);
|
free(stream_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initializes |session_data| */
|
/* Initializes |session_data| */
|
||||||
static http2_session_data *create_http2_session_data(struct event_base *evbase)
|
static http2_session_data *
|
||||||
{
|
create_http2_session_data(struct event_base *evbase) {
|
||||||
http2_session_data *session_data = malloc(sizeof(http2_session_data));
|
http2_session_data *session_data = malloc(sizeof(http2_session_data));
|
||||||
|
|
||||||
memset(session_data, 0, sizeof(http2_session_data));
|
memset(session_data, 0, sizeof(http2_session_data));
|
||||||
@@ -132,11 +144,10 @@ static http2_session_data *create_http2_session_data(struct event_base *evbase)
|
|||||||
return session_data;
|
return session_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void delete_http2_session_data(http2_session_data *session_data)
|
static void delete_http2_session_data(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
SSL *ssl = bufferevent_openssl_get_ssl(session_data->bev);
|
SSL *ssl = bufferevent_openssl_get_ssl(session_data->bev);
|
||||||
|
|
||||||
if(ssl) {
|
if (ssl) {
|
||||||
SSL_shutdown(ssl);
|
SSL_shutdown(ssl);
|
||||||
}
|
}
|
||||||
bufferevent_free(session_data->bev);
|
bufferevent_free(session_data->bev);
|
||||||
@@ -145,17 +156,15 @@ static void delete_http2_session_data(http2_session_data *session_data)
|
|||||||
session_data->dnsbase = NULL;
|
session_data->dnsbase = NULL;
|
||||||
nghttp2_session_del(session_data->session);
|
nghttp2_session_del(session_data->session);
|
||||||
session_data->session = NULL;
|
session_data->session = NULL;
|
||||||
if(session_data->stream_data) {
|
if (session_data->stream_data) {
|
||||||
delete_http2_stream_data(session_data->stream_data);
|
delete_http2_stream_data(session_data->stream_data);
|
||||||
session_data->stream_data = NULL;
|
session_data->stream_data = NULL;
|
||||||
}
|
}
|
||||||
free(session_data);
|
free(session_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_header(FILE *f,
|
static void print_header(FILE *f, const uint8_t *name, size_t namelen,
|
||||||
const uint8_t *name, size_t namelen,
|
const uint8_t *value, size_t valuelen) {
|
||||||
const uint8_t *value, size_t valuelen)
|
|
||||||
{
|
|
||||||
fwrite(name, namelen, 1, f);
|
fwrite(name, namelen, 1, f);
|
||||||
fprintf(f, ": ");
|
fprintf(f, ": ");
|
||||||
fwrite(value, valuelen, 1, f);
|
fwrite(value, valuelen, 1, f);
|
||||||
@@ -165,13 +174,10 @@ static void print_header(FILE *f,
|
|||||||
/* Print HTTP headers to |f|. Please note that this function does not
|
/* Print HTTP headers to |f|. Please note that this function does not
|
||||||
take into account that header name and value are sequence of
|
take into account that header name and value are sequence of
|
||||||
octets, therefore they may contain non-printable characters. */
|
octets, therefore they may contain non-printable characters. */
|
||||||
static void print_headers(FILE *f, nghttp2_nv *nva, size_t nvlen)
|
static void print_headers(FILE *f, nghttp2_nv *nva, size_t nvlen) {
|
||||||
{
|
|
||||||
size_t i;
|
size_t i;
|
||||||
for(i = 0; i < nvlen; ++i) {
|
for (i = 0; i < nvlen; ++i) {
|
||||||
print_header(f,
|
print_header(f, nva[i].name, nva[i].namelen, nva[i].value, nva[i].valuelen);
|
||||||
nva[i].name, nva[i].namelen,
|
|
||||||
nva[i].value, nva[i].valuelen);
|
|
||||||
}
|
}
|
||||||
fprintf(f, "\n");
|
fprintf(f, "\n");
|
||||||
}
|
}
|
||||||
@@ -179,11 +185,9 @@ static void print_headers(FILE *f, nghttp2_nv *nva, size_t nvlen)
|
|||||||
/* nghttp2_send_callback. Here we transmit the |data|, |length| bytes,
|
/* nghttp2_send_callback. Here we transmit the |data|, |length| bytes,
|
||||||
to the network. Because we are using libevent bufferevent, we just
|
to the network. Because we are using libevent bufferevent, we just
|
||||||
write those bytes into bufferevent buffer. */
|
write those bytes into bufferevent buffer. */
|
||||||
static ssize_t send_callback(nghttp2_session *session,
|
static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
|
||||||
const uint8_t *data, size_t length,
|
size_t length, int flags _U_, void *user_data) {
|
||||||
int flags, void *user_data)
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
|
||||||
struct bufferevent *bev = session_data->bev;
|
struct bufferevent *bev = session_data->bev;
|
||||||
bufferevent_write(bev, data, length);
|
bufferevent_write(bev, data, length);
|
||||||
return length;
|
return length;
|
||||||
@@ -191,18 +195,16 @@ static ssize_t send_callback(nghttp2_session *session,
|
|||||||
|
|
||||||
/* nghttp2_on_header_callback: Called when nghttp2 library emits
|
/* nghttp2_on_header_callback: Called when nghttp2 library emits
|
||||||
single header name/value pair. */
|
single header name/value pair. */
|
||||||
static int on_header_callback(nghttp2_session *session,
|
static int on_header_callback(nghttp2_session *session _U_,
|
||||||
const nghttp2_frame *frame,
|
const nghttp2_frame *frame, const uint8_t *name,
|
||||||
const uint8_t *name, size_t namelen,
|
size_t namelen, const uint8_t *value,
|
||||||
const uint8_t *value, size_t valuelen,
|
size_t valuelen, uint8_t flags _U_,
|
||||||
uint8_t flags,
|
void *user_data) {
|
||||||
void *user_data)
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
{
|
switch (frame->hd.type) {
|
||||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
|
||||||
switch(frame->hd.type) {
|
|
||||||
case NGHTTP2_HEADERS:
|
case NGHTTP2_HEADERS:
|
||||||
if(frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
|
if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
|
||||||
session_data->stream_data->stream_id == frame->hd.stream_id) {
|
session_data->stream_data->stream_id == frame->hd.stream_id) {
|
||||||
/* Print response headers for the initiated request. */
|
/* Print response headers for the initiated request. */
|
||||||
print_header(stderr, name, namelen, value, valuelen);
|
print_header(stderr, name, namelen, value, valuelen);
|
||||||
break;
|
break;
|
||||||
@@ -213,15 +215,14 @@ static int on_header_callback(nghttp2_session *session,
|
|||||||
|
|
||||||
/* nghttp2_on_begin_headers_callback: Called when nghttp2 library gets
|
/* nghttp2_on_begin_headers_callback: Called when nghttp2 library gets
|
||||||
started to receive header block. */
|
started to receive header block. */
|
||||||
static int on_begin_headers_callback(nghttp2_session *session,
|
static int on_begin_headers_callback(nghttp2_session *session _U_,
|
||||||
const nghttp2_frame *frame,
|
const nghttp2_frame *frame,
|
||||||
void *user_data)
|
void *user_data) {
|
||||||
{
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
switch (frame->hd.type) {
|
||||||
switch(frame->hd.type) {
|
|
||||||
case NGHTTP2_HEADERS:
|
case NGHTTP2_HEADERS:
|
||||||
if(frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
|
if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
|
||||||
session_data->stream_data->stream_id == frame->hd.stream_id) {
|
session_data->stream_data->stream_id == frame->hd.stream_id) {
|
||||||
fprintf(stderr, "Response headers for stream ID=%d:\n",
|
fprintf(stderr, "Response headers for stream ID=%d:\n",
|
||||||
frame->hd.stream_id);
|
frame->hd.stream_id);
|
||||||
}
|
}
|
||||||
@@ -232,14 +233,13 @@ static int on_begin_headers_callback(nghttp2_session *session,
|
|||||||
|
|
||||||
/* nghttp2_on_frame_recv_callback: Called when nghttp2 library
|
/* nghttp2_on_frame_recv_callback: Called when nghttp2 library
|
||||||
received a complete frame from the remote peer. */
|
received a complete frame from the remote peer. */
|
||||||
static int on_frame_recv_callback(nghttp2_session *session,
|
static int on_frame_recv_callback(nghttp2_session *session _U_,
|
||||||
const nghttp2_frame *frame, void *user_data)
|
const nghttp2_frame *frame, void *user_data) {
|
||||||
{
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
switch (frame->hd.type) {
|
||||||
switch(frame->hd.type) {
|
|
||||||
case NGHTTP2_HEADERS:
|
case NGHTTP2_HEADERS:
|
||||||
if(frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
|
if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
|
||||||
session_data->stream_data->stream_id == frame->hd.stream_id) {
|
session_data->stream_data->stream_id == frame->hd.stream_id) {
|
||||||
fprintf(stderr, "All headers received\n");
|
fprintf(stderr, "All headers received\n");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -252,13 +252,12 @@ static int on_frame_recv_callback(nghttp2_session *session,
|
|||||||
is meant to the stream we initiated, print the received data in
|
is meant to the stream we initiated, print the received data in
|
||||||
stdout, so that the user can redirect its output to the file
|
stdout, so that the user can redirect its output to the file
|
||||||
easily. */
|
easily. */
|
||||||
static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
static int on_data_chunk_recv_callback(nghttp2_session *session _U_,
|
||||||
int32_t stream_id,
|
uint8_t flags _U_, int32_t stream_id,
|
||||||
const uint8_t *data, size_t len,
|
const uint8_t *data, size_t len,
|
||||||
void *user_data)
|
void *user_data) {
|
||||||
{
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
if (session_data->stream_data->stream_id == stream_id) {
|
||||||
if(session_data->stream_data->stream_id == stream_id) {
|
|
||||||
fwrite(data, len, 1, stdout);
|
fwrite(data, len, 1, stdout);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@@ -268,19 +267,16 @@ static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
|||||||
closed. This example program only deals with 1 HTTP request (1
|
closed. This example program only deals with 1 HTTP request (1
|
||||||
stream), if it is closed, we send GOAWAY and tear down the
|
stream), if it is closed, we send GOAWAY and tear down the
|
||||||
session */
|
session */
|
||||||
static int on_stream_close_callback(nghttp2_session *session,
|
static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||||
int32_t stream_id,
|
uint32_t error_code, void *user_data) {
|
||||||
nghttp2_error_code error_code,
|
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;
|
int rv;
|
||||||
|
|
||||||
if(session_data->stream_data->stream_id == stream_id) {
|
if (session_data->stream_data->stream_id == stream_id) {
|
||||||
fprintf(stderr, "Stream %d closed with error_code=%d\n",
|
fprintf(stderr, "Stream %d closed with error_code=%d\n", stream_id,
|
||||||
stream_id, error_code);
|
error_code);
|
||||||
rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
|
rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
|
||||||
if(rv != 0) {
|
if (rv != 0) {
|
||||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -290,117 +286,111 @@ static int on_stream_close_callback(nghttp2_session *session,
|
|||||||
/* NPN TLS extension client callback. We check that server advertised
|
/* NPN TLS extension client callback. We check that server advertised
|
||||||
the HTTP/2 protocol the nghttp2 library supports. If not, exit
|
the HTTP/2 protocol the nghttp2 library supports. If not, exit
|
||||||
the program. */
|
the program. */
|
||||||
static int select_next_proto_cb(SSL* ssl,
|
static int select_next_proto_cb(SSL *ssl _U_, unsigned char **out,
|
||||||
unsigned char **out, unsigned char *outlen,
|
unsigned char *outlen, const unsigned char *in,
|
||||||
const unsigned char *in, unsigned int inlen,
|
unsigned int inlen, void *arg _U_) {
|
||||||
void *arg)
|
if (nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) {
|
||||||
{
|
|
||||||
if(nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) {
|
|
||||||
errx(1, "Server did not advertise " NGHTTP2_PROTO_VERSION_ID);
|
errx(1, "Server did not advertise " NGHTTP2_PROTO_VERSION_ID);
|
||||||
}
|
}
|
||||||
return SSL_TLSEXT_ERR_OK;
|
return SSL_TLSEXT_ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create SSL_CTX. */
|
/* Create SSL_CTX. */
|
||||||
static SSL_CTX* create_ssl_ctx(void)
|
static SSL_CTX *create_ssl_ctx(void) {
|
||||||
{
|
|
||||||
SSL_CTX *ssl_ctx;
|
SSL_CTX *ssl_ctx;
|
||||||
ssl_ctx = SSL_CTX_new(SSLv23_client_method());
|
ssl_ctx = SSL_CTX_new(SSLv23_client_method());
|
||||||
if(!ssl_ctx) {
|
if (!ssl_ctx) {
|
||||||
errx(1, "Could not create SSL/TLS context: %s",
|
errx(1, "Could not create SSL/TLS context: %s",
|
||||||
ERR_error_string(ERR_get_error(), NULL));
|
ERR_error_string(ERR_get_error(), NULL));
|
||||||
}
|
}
|
||||||
SSL_CTX_set_options(ssl_ctx,
|
SSL_CTX_set_options(ssl_ctx,
|
||||||
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_COMPRESSION |
|
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
|
||||||
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
|
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);
|
SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL);
|
||||||
return ssl_ctx;
|
return ssl_ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create SSL object */
|
/* Create SSL object */
|
||||||
static SSL* create_ssl(SSL_CTX *ssl_ctx)
|
static SSL *create_ssl(SSL_CTX *ssl_ctx) {
|
||||||
{
|
|
||||||
SSL *ssl;
|
SSL *ssl;
|
||||||
ssl = SSL_new(ssl_ctx);
|
ssl = SSL_new(ssl_ctx);
|
||||||
if(!ssl) {
|
if (!ssl) {
|
||||||
errx(1, "Could not create SSL/TLS session object: %s",
|
errx(1, "Could not create SSL/TLS session object: %s",
|
||||||
ERR_error_string(ERR_get_error(), NULL));
|
ERR_error_string(ERR_get_error(), NULL));
|
||||||
}
|
}
|
||||||
return ssl;
|
return ssl;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void initialize_nghttp2_session(http2_session_data *session_data)
|
static void initialize_nghttp2_session(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
nghttp2_session_callbacks *callbacks;
|
nghttp2_session_callbacks *callbacks;
|
||||||
|
|
||||||
nghttp2_session_callbacks_new(&callbacks);
|
nghttp2_session_callbacks_new(&callbacks);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_frame_recv_callback
|
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
|
||||||
(callbacks, on_frame_recv_callback);
|
on_frame_recv_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_data_chunk_recv_callback
|
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
|
||||||
(callbacks, on_data_chunk_recv_callback);
|
callbacks, on_data_chunk_recv_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_stream_close_callback
|
nghttp2_session_callbacks_set_on_stream_close_callback(
|
||||||
(callbacks, on_stream_close_callback);
|
callbacks, on_stream_close_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_header_callback
|
nghttp2_session_callbacks_set_on_header_callback(callbacks,
|
||||||
(callbacks, on_header_callback);
|
on_header_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_begin_headers_callback
|
nghttp2_session_callbacks_set_on_begin_headers_callback(
|
||||||
(callbacks, on_begin_headers_callback);
|
callbacks, on_begin_headers_callback);
|
||||||
|
|
||||||
nghttp2_session_client_new(&session_data->session, callbacks, session_data);
|
nghttp2_session_client_new(&session_data->session, callbacks, session_data);
|
||||||
|
|
||||||
nghttp2_session_callbacks_del(callbacks);
|
nghttp2_session_callbacks_del(callbacks);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void send_client_connection_header(http2_session_data *session_data)
|
static void send_client_connection_header(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
nghttp2_settings_entry iv[1] = {
|
nghttp2_settings_entry iv[1] = {
|
||||||
{ NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 }
|
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
|
||||||
};
|
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
bufferevent_write(session_data->bev,
|
/* client 24 bytes magic string will be sent by nghttp2 library */
|
||||||
NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
|
||||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
|
ARRLEN(iv));
|
||||||
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE,
|
if (rv != 0) {
|
||||||
iv, ARRLEN(iv));
|
|
||||||
if(rv != 0) {
|
|
||||||
errx(1, "Could not submit SETTINGS: %s", nghttp2_strerror(rv));
|
errx(1, "Could not submit SETTINGS: %s", nghttp2_strerror(rv));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#define MAKE_NV(NAME, VALUE, VALUELEN) \
|
#define MAKE_NV(NAME, VALUE, VALUELEN) \
|
||||||
{ (uint8_t*)NAME, (uint8_t*)VALUE, sizeof(NAME) - 1, VALUELEN, \
|
{ \
|
||||||
NGHTTP2_NV_FLAG_NONE }
|
(uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, VALUELEN, \
|
||||||
|
NGHTTP2_NV_FLAG_NONE \
|
||||||
|
}
|
||||||
|
|
||||||
#define MAKE_NV2(NAME, VALUE) \
|
#define MAKE_NV2(NAME, VALUE) \
|
||||||
{ (uint8_t*)NAME, (uint8_t*)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \
|
{ \
|
||||||
NGHTTP2_NV_FLAG_NONE }
|
(uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \
|
||||||
|
NGHTTP2_NV_FLAG_NONE \
|
||||||
|
}
|
||||||
|
|
||||||
/* Send HTTP request to the remote peer */
|
/* Send HTTP request to the remote peer */
|
||||||
static void submit_request(http2_session_data *session_data)
|
static void submit_request(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
int32_t stream_id;
|
int32_t stream_id;
|
||||||
http2_stream_data *stream_data = session_data->stream_data;
|
http2_stream_data *stream_data = session_data->stream_data;
|
||||||
const char *uri = stream_data->uri;
|
const char *uri = stream_data->uri;
|
||||||
const struct http_parser_url *u = stream_data->u;
|
const struct http_parser_url *u = stream_data->u;
|
||||||
nghttp2_nv hdrs[] = {
|
nghttp2_nv hdrs[] = {
|
||||||
MAKE_NV2(":method", "GET"),
|
MAKE_NV2(":method", "GET"),
|
||||||
MAKE_NV(":scheme",
|
MAKE_NV(":scheme", &uri[u->field_data[UF_SCHEMA].off],
|
||||||
&uri[u->field_data[UF_SCHEMA].off], u->field_data[UF_SCHEMA].len),
|
u->field_data[UF_SCHEMA].len),
|
||||||
MAKE_NV(":authority", stream_data->authority, stream_data->authoritylen),
|
MAKE_NV(":authority", stream_data->authority, stream_data->authoritylen),
|
||||||
MAKE_NV(":path", stream_data->path, stream_data->pathlen)
|
MAKE_NV(":path", stream_data->path, stream_data->pathlen)};
|
||||||
};
|
|
||||||
fprintf(stderr, "Request headers:\n");
|
fprintf(stderr, "Request headers:\n");
|
||||||
print_headers(stderr, hdrs, ARRLEN(hdrs));
|
print_headers(stderr, hdrs, ARRLEN(hdrs));
|
||||||
stream_id = nghttp2_submit_request(session_data->session, NULL,
|
stream_id = nghttp2_submit_request(session_data->session, NULL, hdrs,
|
||||||
hdrs, ARRLEN(hdrs), NULL, stream_data);
|
ARRLEN(hdrs), NULL, stream_data);
|
||||||
if(stream_id < 0) {
|
if (stream_id < 0) {
|
||||||
errx(1, "Could not submit HTTP request: %s", nghttp2_strerror(stream_id));
|
errx(1, "Could not submit HTTP request: %s", nghttp2_strerror(stream_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -409,12 +399,11 @@ static void submit_request(http2_session_data *session_data)
|
|||||||
|
|
||||||
/* Serialize the frame and send (or buffer) the data to
|
/* Serialize the frame and send (or buffer) the data to
|
||||||
bufferevent. */
|
bufferevent. */
|
||||||
static int session_send(http2_session_data *session_data)
|
static int session_send(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
rv = nghttp2_session_send(session_data->session);
|
rv = nghttp2_session_send(session_data->session);
|
||||||
if(rv != 0) {
|
if (rv != 0) {
|
||||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -425,26 +414,25 @@ static int session_send(http2_session_data *session_data)
|
|||||||
of bufferevent and feed them to nghttp2 library. This may invoke
|
of bufferevent and feed them to nghttp2 library. This may invoke
|
||||||
nghttp2 callbacks. It may also queues the frame in nghttp2 session
|
nghttp2 callbacks. It may also queues the frame in nghttp2 session
|
||||||
context. To send them, we call session_send() in the end. */
|
context. To send them, we call session_send() in the end. */
|
||||||
static void readcb(struct bufferevent *bev, void *ptr)
|
static void readcb(struct bufferevent *bev, void *ptr) {
|
||||||
{
|
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
|
||||||
ssize_t readlen;
|
ssize_t readlen;
|
||||||
struct evbuffer *input = bufferevent_get_input(bev);
|
struct evbuffer *input = bufferevent_get_input(bev);
|
||||||
size_t datalen = evbuffer_get_length(input);
|
size_t datalen = evbuffer_get_length(input);
|
||||||
unsigned char *data = evbuffer_pullup(input, -1);
|
unsigned char *data = evbuffer_pullup(input, -1);
|
||||||
|
|
||||||
readlen = nghttp2_session_mem_recv(session_data->session, data, datalen);
|
readlen = nghttp2_session_mem_recv(session_data->session, data, datalen);
|
||||||
if(readlen < 0) {
|
if (readlen < 0) {
|
||||||
warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
|
warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
|
||||||
delete_http2_session_data(session_data);
|
delete_http2_session_data(session_data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(evbuffer_drain(input, readlen) != 0) {
|
if (evbuffer_drain(input, readlen) != 0) {
|
||||||
warnx("Fatal error: evbuffer_drain failed");
|
warnx("Fatal error: evbuffer_drain failed");
|
||||||
delete_http2_session_data(session_data);
|
delete_http2_session_data(session_data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(session_send(session_data) != 0) {
|
if (session_send(session_data) != 0) {
|
||||||
delete_http2_session_data(session_data);
|
delete_http2_session_data(session_data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -454,12 +442,11 @@ static void readcb(struct bufferevent *bev, void *ptr)
|
|||||||
receiving GOAWAY, we check the some conditions on the nghttp2
|
receiving GOAWAY, we check the some conditions on the nghttp2
|
||||||
library and output buffer of bufferevent. If it indicates we have
|
library and output buffer of bufferevent. If it indicates we have
|
||||||
no business to this session, tear down the connection. */
|
no business to this session, tear down the connection. */
|
||||||
static void writecb(struct bufferevent *bev, void *ptr)
|
static void writecb(struct bufferevent *bev _U_, void *ptr) {
|
||||||
{
|
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
if (nghttp2_session_want_read(session_data->session) == 0 &&
|
||||||
if(nghttp2_session_want_read(session_data->session) == 0 &&
|
nghttp2_session_want_write(session_data->session) == 0 &&
|
||||||
nghttp2_session_want_write(session_data->session) == 0 &&
|
evbuffer_get_length(bufferevent_get_output(session_data->bev)) == 0) {
|
||||||
evbuffer_get_length(bufferevent_get_output(session_data->bev)) == 0) {
|
|
||||||
delete_http2_session_data(session_data);
|
delete_http2_session_data(session_data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -469,10 +456,9 @@ static void writecb(struct bufferevent *bev, void *ptr)
|
|||||||
peer verification. After SSL/TLS handshake is over, initialize
|
peer verification. After SSL/TLS handshake is over, initialize
|
||||||
nghttp2 library session, and send client connection header. Then
|
nghttp2 library session, and send client connection header. Then
|
||||||
send HTTP request. */
|
send HTTP request. */
|
||||||
static void eventcb(struct bufferevent *bev, short events, void *ptr)
|
static void eventcb(struct bufferevent *bev, short events, void *ptr) {
|
||||||
{
|
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
if (events & BEV_EVENT_CONNECTED) {
|
||||||
if(events & BEV_EVENT_CONNECTED) {
|
|
||||||
int fd = bufferevent_getfd(bev);
|
int fd = bufferevent_getfd(bev);
|
||||||
int val = 1;
|
int val = 1;
|
||||||
fprintf(stderr, "Connected\n");
|
fprintf(stderr, "Connected\n");
|
||||||
@@ -480,41 +466,38 @@ static void eventcb(struct bufferevent *bev, short events, void *ptr)
|
|||||||
initialize_nghttp2_session(session_data);
|
initialize_nghttp2_session(session_data);
|
||||||
send_client_connection_header(session_data);
|
send_client_connection_header(session_data);
|
||||||
submit_request(session_data);
|
submit_request(session_data);
|
||||||
if(session_send(session_data) != 0) {
|
if (session_send(session_data) != 0) {
|
||||||
delete_http2_session_data(session_data);
|
delete_http2_session_data(session_data);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(events & BEV_EVENT_EOF) {
|
if (events & BEV_EVENT_EOF) {
|
||||||
warnx("Disconnected from the remote host");
|
warnx("Disconnected from the remote host");
|
||||||
} else if(events & BEV_EVENT_ERROR) {
|
} else if (events & BEV_EVENT_ERROR) {
|
||||||
warnx("Network error");
|
warnx("Network error");
|
||||||
} else if(events & BEV_EVENT_TIMEOUT) {
|
} else if (events & BEV_EVENT_TIMEOUT) {
|
||||||
warnx("Timeout");
|
warnx("Timeout");
|
||||||
}
|
}
|
||||||
delete_http2_session_data(session_data);
|
delete_http2_session_data(session_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Start connecting to the remote peer |host:port| */
|
/* Start connecting to the remote peer |host:port| */
|
||||||
static void initiate_connection(struct event_base *evbase,
|
static void initiate_connection(struct event_base *evbase, SSL_CTX *ssl_ctx,
|
||||||
SSL_CTX *ssl_ctx,
|
|
||||||
const char *host, uint16_t port,
|
const char *host, uint16_t port,
|
||||||
http2_session_data *session_data)
|
http2_session_data *session_data) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
struct bufferevent *bev;
|
struct bufferevent *bev;
|
||||||
SSL *ssl;
|
SSL *ssl;
|
||||||
|
|
||||||
ssl = create_ssl(ssl_ctx);
|
ssl = create_ssl(ssl_ctx);
|
||||||
bev = bufferevent_openssl_socket_new(evbase, -1, ssl,
|
bev = bufferevent_openssl_socket_new(
|
||||||
BUFFEREVENT_SSL_CONNECTING,
|
evbase, -1, ssl, BUFFEREVENT_SSL_CONNECTING,
|
||||||
BEV_OPT_DEFER_CALLBACKS |
|
BEV_OPT_DEFER_CALLBACKS | BEV_OPT_CLOSE_ON_FREE);
|
||||||
BEV_OPT_CLOSE_ON_FREE);
|
|
||||||
bufferevent_setcb(bev, readcb, writecb, eventcb, session_data);
|
bufferevent_setcb(bev, readcb, writecb, eventcb, session_data);
|
||||||
rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase,
|
rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase,
|
||||||
AF_UNSPEC, host, port);
|
AF_UNSPEC, host, port);
|
||||||
|
|
||||||
if(rv != 0) {
|
if (rv != 0) {
|
||||||
errx(1, "Could not connect to the remote host %s", host);
|
errx(1, "Could not connect to the remote host %s", host);
|
||||||
}
|
}
|
||||||
session_data->bev = bev;
|
session_data->bev = bev;
|
||||||
@@ -522,8 +505,7 @@ static void initiate_connection(struct event_base *evbase,
|
|||||||
|
|
||||||
/* Get resource denoted by the |uri|. The debug and error messages are
|
/* Get resource denoted by the |uri|. The debug and error messages are
|
||||||
printed in stderr, while the response body is printed in stdout. */
|
printed in stderr, while the response body is printed in stdout. */
|
||||||
static void run(const char *uri)
|
static void run(const char *uri) {
|
||||||
{
|
|
||||||
struct http_parser_url u;
|
struct http_parser_url u;
|
||||||
char *host;
|
char *host;
|
||||||
uint16_t port;
|
uint16_t port;
|
||||||
@@ -534,11 +516,11 @@ static void run(const char *uri)
|
|||||||
|
|
||||||
/* Parse the |uri| and stores its components in |u| */
|
/* Parse the |uri| and stores its components in |u| */
|
||||||
rv = http_parser_parse_url(uri, strlen(uri), 0, &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);
|
errx(1, "Could not parse URI %s", uri);
|
||||||
}
|
}
|
||||||
host = strndup(&uri[u.field_data[UF_HOST].off], u.field_data[UF_HOST].len);
|
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;
|
port = 443;
|
||||||
} else {
|
} else {
|
||||||
port = u.port;
|
port = u.port;
|
||||||
@@ -561,11 +543,10 @@ static void run(const char *uri)
|
|||||||
SSL_CTX_free(ssl_ctx);
|
SSL_CTX_free(ssl_ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv) {
|
||||||
{
|
|
||||||
struct sigaction act;
|
struct sigaction act;
|
||||||
|
|
||||||
if(argc < 2) {
|
if (argc < 2) {
|
||||||
fprintf(stderr, "Usage: libevent-client HTTPS_URI\n");
|
fprintf(stderr, "Usage: libevent-client HTTPS_URI\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
@@ -574,10 +555,10 @@ int main(int argc, char **argv)
|
|||||||
act.sa_handler = SIG_IGN;
|
act.sa_handler = SIG_IGN;
|
||||||
sigaction(SIGPIPE, &act, NULL);
|
sigaction(SIGPIPE, &act, NULL);
|
||||||
|
|
||||||
OPENSSL_config(NULL);
|
|
||||||
OpenSSL_add_all_algorithms();
|
|
||||||
SSL_load_error_strings();
|
SSL_load_error_strings();
|
||||||
SSL_library_init();
|
SSL_library_init();
|
||||||
|
OpenSSL_add_all_algorithms();
|
||||||
|
OPENSSL_config(NULL);
|
||||||
|
|
||||||
run(argv[1]);
|
run(argv[1]);
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -22,15 +22,29 @@
|
|||||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif /* HAVE_CONFIG_H */
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#ifdef HAVE_SYS_SOCKET_H
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
#endif /* HAVE_SYS_SOCKET_H */
|
||||||
|
#ifdef HAVE_NETDB_H
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
|
#endif /* HAVE_NETDB_H */
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#ifdef HAVE_UNISTD_H
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#endif /* HAVE_UNISTD_H */
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#ifdef HAVE_FCNTL_H
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#endif /* HAVE_FCNTL_H */
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
#ifdef HAVE_NETINET_IN_H
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
#endif /* HAVE_NETINET_IN_H */
|
||||||
#include <netinet/tcp.h>
|
#include <netinet/tcp.h>
|
||||||
#include <err.h>
|
#include <err.h>
|
||||||
|
|
||||||
@@ -47,11 +61,13 @@
|
|||||||
|
|
||||||
#define OUTPUT_WOULDBLOCK_THRESHOLD (1 << 16)
|
#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) \
|
#define MAKE_NV(NAME, VALUE) \
|
||||||
{ (uint8_t*)NAME, (uint8_t*)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \
|
{ \
|
||||||
NGHTTP2_NV_FLAG_NONE }
|
(uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \
|
||||||
|
NGHTTP2_NV_FLAG_NONE \
|
||||||
|
}
|
||||||
|
|
||||||
struct app_context;
|
struct app_context;
|
||||||
typedef struct app_context app_context;
|
typedef struct app_context app_context;
|
||||||
@@ -79,32 +95,40 @@ struct app_context {
|
|||||||
static unsigned char next_proto_list[256];
|
static unsigned char next_proto_list[256];
|
||||||
static size_t next_proto_list_len;
|
static size_t next_proto_list_len;
|
||||||
|
|
||||||
static int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len,
|
static int next_proto_cb(SSL *s _U_, const unsigned char **data,
|
||||||
void *arg)
|
unsigned int *len, void *arg _U_) {
|
||||||
{
|
|
||||||
*data = next_proto_list;
|
*data = next_proto_list;
|
||||||
*len = (unsigned int)next_proto_list_len;
|
*len = (unsigned int)next_proto_list_len;
|
||||||
return SSL_TLSEXT_ERR_OK;
|
return SSL_TLSEXT_ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create SSL_CTX. */
|
/* Create SSL_CTX. */
|
||||||
static SSL_CTX* create_ssl_ctx(const char *key_file, const char *cert_file)
|
static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) {
|
||||||
{
|
|
||||||
SSL_CTX *ssl_ctx;
|
SSL_CTX *ssl_ctx;
|
||||||
|
EC_KEY *ecdh;
|
||||||
|
|
||||||
ssl_ctx = SSL_CTX_new(SSLv23_server_method());
|
ssl_ctx = SSL_CTX_new(SSLv23_server_method());
|
||||||
if(!ssl_ctx) {
|
if (!ssl_ctx) {
|
||||||
errx(1, "Could not create SSL/TLS context: %s",
|
errx(1, "Could not create SSL/TLS context: %s",
|
||||||
ERR_error_string(ERR_get_error(), NULL));
|
ERR_error_string(ERR_get_error(), NULL));
|
||||||
}
|
}
|
||||||
SSL_CTX_set_options(ssl_ctx,
|
SSL_CTX_set_options(ssl_ctx,
|
||||||
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_COMPRESSION |
|
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
|
||||||
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
|
SSL_OP_NO_COMPRESSION |
|
||||||
|
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
|
||||||
|
|
||||||
if(SSL_CTX_use_PrivateKey_file(ssl_ctx, key_file,
|
ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
|
||||||
SSL_FILETYPE_PEM) != 1) {
|
if (!ecdh) {
|
||||||
|
errx(1, "EC_KEY_new_by_curv_name failed: %s",
|
||||||
|
ERR_error_string(ERR_get_error(), NULL));
|
||||||
|
}
|
||||||
|
SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh);
|
||||||
|
EC_KEY_free(ecdh);
|
||||||
|
|
||||||
|
if (SSL_CTX_use_PrivateKey_file(ssl_ctx, key_file, SSL_FILETYPE_PEM) != 1) {
|
||||||
errx(1, "Could not read private key file %s", key_file);
|
errx(1, "Could not read private key file %s", key_file);
|
||||||
}
|
}
|
||||||
if(SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file) != 1) {
|
if (SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file) != 1) {
|
||||||
errx(1, "Could not read certificate file %s", cert_file);
|
errx(1, "Could not read certificate file %s", cert_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,11 +142,10 @@ static SSL_CTX* create_ssl_ctx(const char *key_file, const char *cert_file)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Create SSL object */
|
/* Create SSL object */
|
||||||
static SSL* create_ssl(SSL_CTX *ssl_ctx)
|
static SSL *create_ssl(SSL_CTX *ssl_ctx) {
|
||||||
{
|
|
||||||
SSL *ssl;
|
SSL *ssl;
|
||||||
ssl = SSL_new(ssl_ctx);
|
ssl = SSL_new(ssl_ctx);
|
||||||
if(!ssl) {
|
if (!ssl) {
|
||||||
errx(1, "Could not create SSL/TLS session object: %s",
|
errx(1, "Could not create SSL/TLS session object: %s",
|
||||||
ERR_error_string(ERR_get_error(), NULL));
|
ERR_error_string(ERR_get_error(), NULL));
|
||||||
}
|
}
|
||||||
@@ -130,28 +153,25 @@ static SSL* create_ssl(SSL_CTX *ssl_ctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void add_stream(http2_session_data *session_data,
|
static void add_stream(http2_session_data *session_data,
|
||||||
http2_stream_data *stream_data)
|
http2_stream_data *stream_data) {
|
||||||
{
|
|
||||||
stream_data->next = session_data->root.next;
|
stream_data->next = session_data->root.next;
|
||||||
session_data->root.next = stream_data;
|
session_data->root.next = stream_data;
|
||||||
stream_data->prev = &session_data->root;
|
stream_data->prev = &session_data->root;
|
||||||
if(stream_data->next) {
|
if (stream_data->next) {
|
||||||
stream_data->next->prev = stream_data;
|
stream_data->next->prev = stream_data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void remove_stream(http2_session_data *session_data,
|
static void remove_stream(http2_session_data *session_data _U_,
|
||||||
http2_stream_data *stream_data)
|
http2_stream_data *stream_data) {
|
||||||
{
|
|
||||||
stream_data->prev->next = stream_data->next;
|
stream_data->prev->next = stream_data->next;
|
||||||
if(stream_data->next) {
|
if (stream_data->next) {
|
||||||
stream_data->next->prev = stream_data->prev;
|
stream_data->next->prev = stream_data->prev;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static http2_stream_data* create_http2_stream_data
|
static http2_stream_data *
|
||||||
(http2_session_data *session_data, int32_t stream_id)
|
create_http2_stream_data(http2_session_data *session_data, int32_t stream_id) {
|
||||||
{
|
|
||||||
http2_stream_data *stream_data;
|
http2_stream_data *stream_data;
|
||||||
stream_data = malloc(sizeof(http2_stream_data));
|
stream_data = malloc(sizeof(http2_stream_data));
|
||||||
memset(stream_data, 0, sizeof(http2_stream_data));
|
memset(stream_data, 0, sizeof(http2_stream_data));
|
||||||
@@ -162,20 +182,18 @@ static http2_stream_data* create_http2_stream_data
|
|||||||
return stream_data;
|
return stream_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void delete_http2_stream_data(http2_stream_data *stream_data)
|
static void delete_http2_stream_data(http2_stream_data *stream_data) {
|
||||||
{
|
if (stream_data->fd != -1) {
|
||||||
if(stream_data->fd != -1) {
|
|
||||||
close(stream_data->fd);
|
close(stream_data->fd);
|
||||||
}
|
}
|
||||||
free(stream_data->request_path);
|
free(stream_data->request_path);
|
||||||
free(stream_data);
|
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,
|
int fd,
|
||||||
struct sockaddr *addr,
|
struct sockaddr *addr,
|
||||||
int addrlen)
|
int addrlen) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
http2_session_data *session_data;
|
http2_session_data *session_data;
|
||||||
SSL *ssl;
|
SSL *ssl;
|
||||||
@@ -187,12 +205,11 @@ static http2_session_data* create_http2_session_data(app_context *app_ctx,
|
|||||||
memset(session_data, 0, sizeof(http2_session_data));
|
memset(session_data, 0, sizeof(http2_session_data));
|
||||||
session_data->app_ctx = app_ctx;
|
session_data->app_ctx = app_ctx;
|
||||||
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val));
|
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val));
|
||||||
session_data->bev = bufferevent_openssl_socket_new
|
session_data->bev = bufferevent_openssl_socket_new(
|
||||||
(app_ctx->evbase, fd, ssl,
|
app_ctx->evbase, fd, ssl, BUFFEREVENT_SSL_ACCEPTING,
|
||||||
BUFFEREVENT_SSL_ACCEPTING,
|
BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
|
||||||
BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
|
|
||||||
rv = getnameinfo(addr, addrlen, host, sizeof(host), NULL, 0, NI_NUMERICHOST);
|
rv = getnameinfo(addr, addrlen, host, sizeof(host), NULL, 0, NI_NUMERICHOST);
|
||||||
if(rv != 0) {
|
if (rv != 0) {
|
||||||
session_data->client_addr = strdup("(unknown)");
|
session_data->client_addr = strdup("(unknown)");
|
||||||
} else {
|
} else {
|
||||||
session_data->client_addr = strdup(host);
|
session_data->client_addr = strdup(host);
|
||||||
@@ -201,17 +218,16 @@ static http2_session_data* create_http2_session_data(app_context *app_ctx,
|
|||||||
return session_data;
|
return session_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void delete_http2_session_data(http2_session_data *session_data)
|
static void delete_http2_session_data(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
http2_stream_data *stream_data;
|
http2_stream_data *stream_data;
|
||||||
SSL *ssl = bufferevent_openssl_get_ssl(session_data->bev);
|
SSL *ssl = bufferevent_openssl_get_ssl(session_data->bev);
|
||||||
fprintf(stderr, "%s disconnected\n", session_data->client_addr);
|
fprintf(stderr, "%s disconnected\n", session_data->client_addr);
|
||||||
if(ssl) {
|
if (ssl) {
|
||||||
SSL_shutdown(ssl);
|
SSL_shutdown(ssl);
|
||||||
}
|
}
|
||||||
bufferevent_free(session_data->bev);
|
bufferevent_free(session_data->bev);
|
||||||
nghttp2_session_del(session_data->session);
|
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;
|
http2_stream_data *next = stream_data->next;
|
||||||
delete_http2_stream_data(stream_data);
|
delete_http2_stream_data(stream_data);
|
||||||
stream_data = next;
|
stream_data = next;
|
||||||
@@ -222,11 +238,10 @@ static void delete_http2_session_data(http2_session_data *session_data)
|
|||||||
|
|
||||||
/* Serialize the frame and send (or buffer) the data to
|
/* Serialize the frame and send (or buffer) the data to
|
||||||
bufferevent. */
|
bufferevent. */
|
||||||
static int session_send(http2_session_data *session_data)
|
static int session_send(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
rv = nghttp2_session_send(session_data->session);
|
rv = nghttp2_session_send(session_data->session);
|
||||||
if(rv != 0) {
|
if (rv != 0) {
|
||||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -237,37 +252,34 @@ static int session_send(http2_session_data *session_data)
|
|||||||
function. Invocation of nghttp2_session_mem_recv() may make
|
function. Invocation of nghttp2_session_mem_recv() may make
|
||||||
additional pending frames, so call session_send() at the end of the
|
additional pending frames, so call session_send() at the end of the
|
||||||
function. */
|
function. */
|
||||||
static int session_recv(http2_session_data *session_data)
|
static int session_recv(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
ssize_t readlen;
|
ssize_t readlen;
|
||||||
struct evbuffer *input = bufferevent_get_input(session_data->bev);
|
struct evbuffer *input = bufferevent_get_input(session_data->bev);
|
||||||
size_t datalen = evbuffer_get_length(input);
|
size_t datalen = evbuffer_get_length(input);
|
||||||
unsigned char *data = evbuffer_pullup(input, -1);
|
unsigned char *data = evbuffer_pullup(input, -1);
|
||||||
|
|
||||||
readlen = nghttp2_session_mem_recv(session_data->session, data, datalen);
|
readlen = nghttp2_session_mem_recv(session_data->session, data, datalen);
|
||||||
if(readlen < 0) {
|
if (readlen < 0) {
|
||||||
warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
|
warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if(evbuffer_drain(input, readlen) != 0) {
|
if (evbuffer_drain(input, readlen) != 0) {
|
||||||
warnx("Fatal error: evbuffer_drain failed");
|
warnx("Fatal error: evbuffer_drain failed");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if(session_send(session_data) != 0) {
|
if (session_send(session_data) != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t send_callback(nghttp2_session *session,
|
static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
|
||||||
const uint8_t *data, size_t length,
|
size_t length, int flags _U_, void *user_data) {
|
||||||
int flags, void *user_data)
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
|
||||||
struct bufferevent *bev = session_data->bev;
|
struct bufferevent *bev = session_data->bev;
|
||||||
/* Avoid excessive buffering in server side. */
|
/* Avoid excessive buffering in server side. */
|
||||||
if(evbuffer_get_length(bufferevent_get_output(session_data->bev)) >=
|
if (evbuffer_get_length(bufferevent_get_output(session_data->bev)) >=
|
||||||
OUTPUT_WOULDBLOCK_THRESHOLD) {
|
OUTPUT_WOULDBLOCK_THRESHOLD) {
|
||||||
return NGHTTP2_ERR_WOULDBLOCK;
|
return NGHTTP2_ERR_WOULDBLOCK;
|
||||||
}
|
}
|
||||||
bufferevent_write(bev, data, length);
|
bufferevent_write(bev, data, length);
|
||||||
@@ -275,26 +287,24 @@ static ssize_t send_callback(nghttp2_session *session,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Returns nonzero if the string |s| ends with the substring |sub| */
|
/* Returns nonzero if the string |s| ends with the substring |sub| */
|
||||||
static int ends_with(const char *s, const char *sub)
|
static int ends_with(const char *s, const char *sub) {
|
||||||
{
|
|
||||||
size_t slen = strlen(s);
|
size_t slen = strlen(s);
|
||||||
size_t sublen = strlen(sub);
|
size_t sublen = strlen(sub);
|
||||||
if(slen < sublen) {
|
if (slen < sublen) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return memcmp(s + slen - sublen, sub, sublen) == 0;
|
return memcmp(s + slen - sublen, sub, sublen) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns int value of hex string character |c| */
|
/* Returns int value of hex string character |c| */
|
||||||
static uint8_t hex_to_uint(uint8_t c)
|
static uint8_t hex_to_uint(uint8_t c) {
|
||||||
{
|
if ('0' <= c && c <= '9') {
|
||||||
if('0' <= c && c <= '9') {
|
|
||||||
return c - '0';
|
return c - '0';
|
||||||
}
|
}
|
||||||
if('A' <= c && c <= 'F') {
|
if ('A' <= c && c <= 'F') {
|
||||||
return c - 'A' + 10;
|
return c - 'A' + 10;
|
||||||
}
|
}
|
||||||
if('a' <= c && c <= 'f') {
|
if ('a' <= c && c <= 'f') {
|
||||||
return c - 'a' + 10;
|
return c - 'a' + 10;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@@ -304,16 +314,15 @@ static uint8_t hex_to_uint(uint8_t c)
|
|||||||
and returns the decoded byte string in allocated buffer. The return
|
and returns the decoded byte string in allocated buffer. The return
|
||||||
value is NULL terminated. The caller must free the returned
|
value is NULL terminated. The caller must free the returned
|
||||||
string. */
|
string. */
|
||||||
static char* percent_decode(const uint8_t *value, size_t valuelen)
|
static char *percent_decode(const uint8_t *value, size_t valuelen) {
|
||||||
{
|
|
||||||
char *res;
|
char *res;
|
||||||
|
|
||||||
res = malloc(valuelen + 1);
|
res = malloc(valuelen + 1);
|
||||||
if(valuelen > 3) {
|
if (valuelen > 3) {
|
||||||
size_t i, j;
|
size_t i, j;
|
||||||
for(i = 0, j = 0; i < valuelen - 2;) {
|
for (i = 0, j = 0; i < valuelen - 2;) {
|
||||||
if(value[i] != '%' ||
|
if (value[i] != '%' || !isxdigit(value[i + 1]) ||
|
||||||
!isxdigit(value[i + 1]) || !isxdigit(value[i + 2])) {
|
!isxdigit(value[i + 2])) {
|
||||||
res[j++] = value[i++];
|
res[j++] = value[i++];
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -329,33 +338,33 @@ static char* percent_decode(const uint8_t *value, size_t valuelen)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t file_read_callback
|
static ssize_t file_read_callback(nghttp2_session *session _U_,
|
||||||
(nghttp2_session *session, int32_t stream_id,
|
int32_t stream_id _U_, uint8_t *buf,
|
||||||
uint8_t *buf, size_t length, uint32_t *data_flags,
|
size_t length, uint32_t *data_flags,
|
||||||
nghttp2_data_source *source, void *user_data)
|
nghttp2_data_source *source,
|
||||||
{
|
void *user_data _U_) {
|
||||||
int fd = source->fd;
|
int fd = source->fd;
|
||||||
ssize_t r;
|
ssize_t r;
|
||||||
while((r = read(fd, buf, length)) == -1 && errno == EINTR);
|
while ((r = read(fd, buf, length)) == -1 && errno == EINTR)
|
||||||
if(r == -1) {
|
;
|
||||||
|
if (r == -1) {
|
||||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||||
}
|
}
|
||||||
if(r == 0) {
|
if (r == 0) {
|
||||||
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int send_response(nghttp2_session *session, int32_t stream_id,
|
static int send_response(nghttp2_session *session, int32_t stream_id,
|
||||||
nghttp2_nv *nva, size_t nvlen, int fd)
|
nghttp2_nv *nva, size_t nvlen, int fd) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
nghttp2_data_provider data_prd;
|
nghttp2_data_provider data_prd;
|
||||||
data_prd.source.fd = fd;
|
data_prd.source.fd = fd;
|
||||||
data_prd.read_callback = file_read_callback;
|
data_prd.read_callback = file_read_callback;
|
||||||
|
|
||||||
rv = nghttp2_submit_response(session, stream_id, nva, nvlen, &data_prd);
|
rv = nghttp2_submit_response(session, stream_id, nva, nvlen, &data_prd);
|
||||||
if(rv != 0) {
|
if (rv != 0) {
|
||||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -363,25 +372,22 @@ static int send_response(nghttp2_session *session, int32_t stream_id,
|
|||||||
}
|
}
|
||||||
|
|
||||||
const char ERROR_HTML[] = "<html><head><title>404</title></head>"
|
const char ERROR_HTML[] = "<html><head><title>404</title></head>"
|
||||||
"<body><h1>404 Not Found</h1></body></html>";
|
"<body><h1>404 Not Found</h1></body></html>";
|
||||||
|
|
||||||
static int error_reply(nghttp2_session *session,
|
static int error_reply(nghttp2_session *session,
|
||||||
http2_stream_data *stream_data)
|
http2_stream_data *stream_data) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
ssize_t writelen;
|
ssize_t writelen;
|
||||||
int pipefd[2];
|
int pipefd[2];
|
||||||
nghttp2_nv hdrs[] = {
|
nghttp2_nv hdrs[] = {MAKE_NV(":status", "404")};
|
||||||
MAKE_NV(":status", "404")
|
|
||||||
};
|
|
||||||
|
|
||||||
rv = pipe(pipefd);
|
rv = pipe(pipefd);
|
||||||
if(rv != 0) {
|
if (rv != 0) {
|
||||||
warn("Could not create pipe");
|
warn("Could not create pipe");
|
||||||
rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
|
rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
|
||||||
stream_data->stream_id,
|
stream_data->stream_id,
|
||||||
NGHTTP2_INTERNAL_ERROR);
|
NGHTTP2_INTERNAL_ERROR);
|
||||||
if(rv != 0) {
|
if (rv != 0) {
|
||||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -391,15 +397,15 @@ static int error_reply(nghttp2_session *session,
|
|||||||
writelen = write(pipefd[1], ERROR_HTML, sizeof(ERROR_HTML) - 1);
|
writelen = write(pipefd[1], ERROR_HTML, sizeof(ERROR_HTML) - 1);
|
||||||
close(pipefd[1]);
|
close(pipefd[1]);
|
||||||
|
|
||||||
if(writelen != sizeof(ERROR_HTML) - 1) {
|
if (writelen != sizeof(ERROR_HTML) - 1) {
|
||||||
close(pipefd[0]);
|
close(pipefd[0]);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
stream_data->fd = pipefd[0];
|
stream_data->fd = pipefd[0];
|
||||||
|
|
||||||
if(send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs),
|
if (send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs),
|
||||||
pipefd[0]) != 0) {
|
pipefd[0]) != 0) {
|
||||||
close(pipefd[0]);
|
close(pipefd[0]);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -409,27 +415,26 @@ static int error_reply(nghttp2_session *session,
|
|||||||
/* nghttp2_on_header_callback: Called when nghttp2 library emits
|
/* nghttp2_on_header_callback: Called when nghttp2 library emits
|
||||||
single header name/value pair. */
|
single header name/value pair. */
|
||||||
static int on_header_callback(nghttp2_session *session,
|
static int on_header_callback(nghttp2_session *session,
|
||||||
const nghttp2_frame *frame,
|
const nghttp2_frame *frame, const uint8_t *name,
|
||||||
const uint8_t *name, size_t namelen,
|
size_t namelen, const uint8_t *value,
|
||||||
const uint8_t *value, size_t valuelen,
|
size_t valuelen, uint8_t flags _U_,
|
||||||
uint8_t flags,
|
void *user_data _U_) {
|
||||||
void *user_data)
|
|
||||||
{
|
|
||||||
http2_stream_data *stream_data;
|
http2_stream_data *stream_data;
|
||||||
const char PATH[] = ":path";
|
const char PATH[] = ":path";
|
||||||
switch(frame->hd.type) {
|
switch (frame->hd.type) {
|
||||||
case NGHTTP2_HEADERS:
|
case NGHTTP2_HEADERS:
|
||||||
if(frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
stream_data = nghttp2_session_get_stream_user_data(session,
|
stream_data =
|
||||||
frame->hd.stream_id);
|
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
|
||||||
if(!stream_data || stream_data->request_path) {
|
if (!stream_data || stream_data->request_path) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) {
|
if (namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) {
|
||||||
size_t j;
|
size_t j;
|
||||||
for(j = 0; j < valuelen && value[j] != '?'; ++j);
|
for (j = 0; j < valuelen && value[j] != '?'; ++j)
|
||||||
|
;
|
||||||
stream_data->request_path = percent_decode(value, j);
|
stream_data->request_path = percent_decode(value, j);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -439,13 +444,12 @@ static int on_header_callback(nghttp2_session *session,
|
|||||||
|
|
||||||
static int on_begin_headers_callback(nghttp2_session *session,
|
static int on_begin_headers_callback(nghttp2_session *session,
|
||||||
const nghttp2_frame *frame,
|
const nghttp2_frame *frame,
|
||||||
void *user_data)
|
void *user_data) {
|
||||||
{
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
|
||||||
http2_stream_data *stream_data;
|
http2_stream_data *stream_data;
|
||||||
|
|
||||||
if(frame->hd.type != NGHTTP2_HEADERS ||
|
if (frame->hd.type != NGHTTP2_HEADERS ||
|
||||||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
stream_data = create_http2_stream_data(session_data, frame->hd.stream_id);
|
stream_data = create_http2_stream_data(session_data, frame->hd.stream_id);
|
||||||
@@ -456,52 +460,47 @@ static int on_begin_headers_callback(nghttp2_session *session,
|
|||||||
|
|
||||||
/* Minimum check for directory traversal. Returns nonzero if it is
|
/* Minimum check for directory traversal. Returns nonzero if it is
|
||||||
safe. */
|
safe. */
|
||||||
static int check_path(const char *path)
|
static int check_path(const char *path) {
|
||||||
{
|
|
||||||
/* We don't like '\' in url. */
|
/* We don't like '\' in url. */
|
||||||
return path[0] && path[0] == '/' &&
|
return path[0] && path[0] == '/' && strchr(path, '\\') == NULL &&
|
||||||
strchr(path, '\\') == NULL &&
|
strstr(path, "/../") == NULL && strstr(path, "/./") == NULL &&
|
||||||
strstr(path, "/../") == NULL &&
|
!ends_with(path, "/..") && !ends_with(path, "/.");
|
||||||
strstr(path, "/./") == NULL &&
|
|
||||||
!ends_with(path, "/..") && !ends_with(path, "/.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int on_request_recv(nghttp2_session *session,
|
static int on_request_recv(nghttp2_session *session,
|
||||||
http2_session_data *session_data,
|
http2_session_data *session_data,
|
||||||
http2_stream_data *stream_data)
|
http2_stream_data *stream_data) {
|
||||||
{
|
|
||||||
int fd;
|
int fd;
|
||||||
nghttp2_nv hdrs[] = {
|
nghttp2_nv hdrs[] = {MAKE_NV(":status", "200")};
|
||||||
MAKE_NV(":status", "200")
|
|
||||||
};
|
|
||||||
char *rel_path;
|
char *rel_path;
|
||||||
|
|
||||||
if(!stream_data->request_path) {
|
if (!stream_data->request_path) {
|
||||||
if(error_reply(session, stream_data) != 0) {
|
if (error_reply(session, stream_data) != 0) {
|
||||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
fprintf(stderr, "%s GET %s\n", session_data->client_addr,
|
fprintf(stderr, "%s GET %s\n", session_data->client_addr,
|
||||||
stream_data->request_path);
|
stream_data->request_path);
|
||||||
if(!check_path(stream_data->request_path)) {
|
if (!check_path(stream_data->request_path)) {
|
||||||
if(error_reply(session, stream_data) != 0) {
|
if (error_reply(session, stream_data) != 0) {
|
||||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
for(rel_path = stream_data->request_path; *rel_path == '/'; ++rel_path);
|
for (rel_path = stream_data->request_path; *rel_path == '/'; ++rel_path)
|
||||||
|
;
|
||||||
fd = open(rel_path, O_RDONLY);
|
fd = open(rel_path, O_RDONLY);
|
||||||
if(fd == -1) {
|
if (fd == -1) {
|
||||||
if(error_reply(session, stream_data) != 0) {
|
if (error_reply(session, stream_data) != 0) {
|
||||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
stream_data->fd = fd;
|
stream_data->fd = fd;
|
||||||
|
|
||||||
if(send_response(session, stream_data->stream_id, hdrs,
|
if (send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs), fd) !=
|
||||||
ARRLEN(hdrs), fd) != 0) {
|
0) {
|
||||||
close(fd);
|
close(fd);
|
||||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
}
|
}
|
||||||
@@ -509,20 +508,19 @@ static int on_request_recv(nghttp2_session *session,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int on_frame_recv_callback(nghttp2_session *session,
|
static int on_frame_recv_callback(nghttp2_session *session,
|
||||||
const nghttp2_frame *frame, void *user_data)
|
const nghttp2_frame *frame, void *user_data) {
|
||||||
{
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
|
||||||
http2_stream_data *stream_data;
|
http2_stream_data *stream_data;
|
||||||
switch(frame->hd.type) {
|
switch (frame->hd.type) {
|
||||||
case NGHTTP2_DATA:
|
case NGHTTP2_DATA:
|
||||||
case NGHTTP2_HEADERS:
|
case NGHTTP2_HEADERS:
|
||||||
/* Check that the client request has finished */
|
/* Check that the client request has finished */
|
||||||
if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||||
stream_data = nghttp2_session_get_stream_user_data(session,
|
stream_data =
|
||||||
frame->hd.stream_id);
|
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
|
||||||
/* For DATA and HEADERS frame, this callback may be called after
|
/* For DATA and HEADERS frame, this callback may be called after
|
||||||
on_stream_close_callback. Check that stream still alive. */
|
on_stream_close_callback. Check that stream still alive. */
|
||||||
if(!stream_data) {
|
if (!stream_data) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return on_request_recv(session, session_data, stream_data);
|
return on_request_recv(session, session_data, stream_data);
|
||||||
@@ -534,16 +532,13 @@ static int on_frame_recv_callback(nghttp2_session *session,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int on_stream_close_callback(nghttp2_session *session,
|
static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||||
int32_t stream_id,
|
uint32_t error_code _U_, void *user_data) {
|
||||||
nghttp2_error_code error_code,
|
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;
|
http2_stream_data *stream_data;
|
||||||
|
|
||||||
stream_data = nghttp2_session_get_stream_user_data(session, stream_id);
|
stream_data = nghttp2_session_get_stream_user_data(session, stream_id);
|
||||||
if(!stream_data) {
|
if (!stream_data) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
remove_stream(session_data, stream_data);
|
remove_stream(session_data, stream_data);
|
||||||
@@ -551,54 +546,40 @@ static int on_stream_close_callback(nghttp2_session *session,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void initialize_nghttp2_session(http2_session_data *session_data)
|
static void initialize_nghttp2_session(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
nghttp2_option *option;
|
|
||||||
nghttp2_session_callbacks *callbacks;
|
nghttp2_session_callbacks *callbacks;
|
||||||
|
|
||||||
nghttp2_option_new(&option);
|
|
||||||
|
|
||||||
/* Tells nghttp2_session object that it handles client connection
|
|
||||||
preface */
|
|
||||||
nghttp2_option_set_recv_client_preface(option, 1);
|
|
||||||
|
|
||||||
nghttp2_session_callbacks_new(&callbacks);
|
nghttp2_session_callbacks_new(&callbacks);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_frame_recv_callback
|
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
|
||||||
(callbacks, on_frame_recv_callback);
|
on_frame_recv_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_stream_close_callback
|
nghttp2_session_callbacks_set_on_stream_close_callback(
|
||||||
(callbacks, on_stream_close_callback);
|
callbacks, on_stream_close_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_header_callback
|
nghttp2_session_callbacks_set_on_header_callback(callbacks,
|
||||||
(callbacks, on_header_callback);
|
on_header_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_begin_headers_callback
|
nghttp2_session_callbacks_set_on_begin_headers_callback(
|
||||||
(callbacks, on_begin_headers_callback);
|
callbacks, on_begin_headers_callback);
|
||||||
|
|
||||||
|
nghttp2_session_server_new(&session_data->session, callbacks, session_data);
|
||||||
|
|
||||||
nghttp2_session_server_new2(&session_data->session, callbacks, session_data,
|
|
||||||
option);
|
|
||||||
|
|
||||||
nghttp2_session_callbacks_del(callbacks);
|
nghttp2_session_callbacks_del(callbacks);
|
||||||
nghttp2_option_del(option);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Send HTTP/2 client connection header, which includes 24 bytes
|
/* Send HTTP/2 client connection header, which includes 24 bytes
|
||||||
magic octets and SETTINGS frame */
|
magic octets and SETTINGS frame */
|
||||||
static int send_server_connection_header(http2_session_data *session_data)
|
static int send_server_connection_header(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
nghttp2_settings_entry iv[1] = {
|
nghttp2_settings_entry iv[1] = {
|
||||||
{ NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 }
|
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
|
||||||
};
|
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE,
|
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
|
||||||
iv, ARRLEN(iv));
|
ARRLEN(iv));
|
||||||
if(rv != 0) {
|
if (rv != 0) {
|
||||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -607,10 +588,9 @@ static int send_server_connection_header(http2_session_data *session_data)
|
|||||||
|
|
||||||
/* readcb for bufferevent after client connection header was
|
/* readcb for bufferevent after client connection header was
|
||||||
checked. */
|
checked. */
|
||||||
static void readcb(struct bufferevent *bev, void *ptr)
|
static void readcb(struct bufferevent *bev _U_, void *ptr) {
|
||||||
{
|
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
if (session_recv(session_data) != 0) {
|
||||||
if(session_recv(session_data) != 0) {
|
|
||||||
delete_http2_session_data(session_data);
|
delete_http2_session_data(session_data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -624,54 +604,51 @@ static void readcb(struct bufferevent *bev, void *ptr)
|
|||||||
process pending data in the output buffer. This is necessary
|
process pending data in the output buffer. This is necessary
|
||||||
because we have a threshold on the buffer size to avoid too much
|
because we have a threshold on the buffer size to avoid too much
|
||||||
buffering. See send_callback(). */
|
buffering. See send_callback(). */
|
||||||
static void writecb(struct bufferevent *bev, void *ptr)
|
static void writecb(struct bufferevent *bev, void *ptr) {
|
||||||
{
|
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
if (evbuffer_get_length(bufferevent_get_output(bev)) > 0) {
|
||||||
if(evbuffer_get_length(bufferevent_get_output(bev)) > 0) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(nghttp2_session_want_read(session_data->session) == 0 &&
|
if (nghttp2_session_want_read(session_data->session) == 0 &&
|
||||||
nghttp2_session_want_write(session_data->session) == 0) {
|
nghttp2_session_want_write(session_data->session) == 0) {
|
||||||
delete_http2_session_data(session_data);
|
delete_http2_session_data(session_data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(session_send(session_data) != 0) {
|
if (session_send(session_data) != 0) {
|
||||||
delete_http2_session_data(session_data);
|
delete_http2_session_data(session_data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* eventcb for bufferevent */
|
/* eventcb for bufferevent */
|
||||||
static void eventcb(struct bufferevent *bev, short events, void *ptr)
|
static void eventcb(struct bufferevent *bev _U_, short events, void *ptr) {
|
||||||
{
|
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
if (events & BEV_EVENT_CONNECTED) {
|
||||||
if(events & BEV_EVENT_CONNECTED) {
|
|
||||||
fprintf(stderr, "%s connected\n", session_data->client_addr);
|
fprintf(stderr, "%s connected\n", session_data->client_addr);
|
||||||
|
|
||||||
initialize_nghttp2_session(session_data);
|
initialize_nghttp2_session(session_data);
|
||||||
|
|
||||||
if(send_server_connection_header(session_data) != 0) {
|
if (send_server_connection_header(session_data) != 0) {
|
||||||
delete_http2_session_data(session_data);
|
delete_http2_session_data(session_data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(events & BEV_EVENT_EOF) {
|
if (events & BEV_EVENT_EOF) {
|
||||||
fprintf(stderr, "%s EOF\n", session_data->client_addr);
|
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);
|
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);
|
fprintf(stderr, "%s timeout\n", session_data->client_addr);
|
||||||
}
|
}
|
||||||
delete_http2_session_data(session_data);
|
delete_http2_session_data(session_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* callback for evconnlistener */
|
/* callback for evconnlistener */
|
||||||
static void acceptcb(struct evconnlistener *listener, int fd,
|
static void acceptcb(struct evconnlistener *listener _U_, int fd,
|
||||||
struct sockaddr *addr, int addrlen, void *arg)
|
struct sockaddr *addr, int addrlen, void *arg) {
|
||||||
{
|
app_context *app_ctx = (app_context *)arg;
|
||||||
app_context *app_ctx = (app_context*)arg;
|
|
||||||
http2_session_data *session_data;
|
http2_session_data *session_data;
|
||||||
|
|
||||||
session_data = create_http2_session_data(app_ctx, fd, addr, addrlen);
|
session_data = create_http2_session_data(app_ctx, fd, addr, addrlen);
|
||||||
@@ -680,8 +657,7 @@ static void acceptcb(struct evconnlistener *listener, int fd,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void start_listen(struct event_base *evbase, const char *service,
|
static void start_listen(struct event_base *evbase, const char *service,
|
||||||
app_context *app_ctx)
|
app_context *app_ctx) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
struct addrinfo hints;
|
struct addrinfo hints;
|
||||||
struct addrinfo *res, *rp;
|
struct addrinfo *res, *rp;
|
||||||
@@ -695,16 +671,15 @@ static void start_listen(struct event_base *evbase, const char *service,
|
|||||||
#endif /* AI_ADDRCONFIG */
|
#endif /* AI_ADDRCONFIG */
|
||||||
|
|
||||||
rv = getaddrinfo(NULL, service, &hints, &res);
|
rv = getaddrinfo(NULL, service, &hints, &res);
|
||||||
if(rv != 0) {
|
if (rv != 0) {
|
||||||
errx(1, NULL);
|
errx(1, NULL);
|
||||||
}
|
}
|
||||||
for(rp = res; rp; rp = rp->ai_next) {
|
for (rp = res; rp; rp = rp->ai_next) {
|
||||||
struct evconnlistener *listener;
|
struct evconnlistener *listener;
|
||||||
listener = evconnlistener_new_bind(evbase, acceptcb, app_ctx,
|
listener = evconnlistener_new_bind(
|
||||||
LEV_OPT_CLOSE_ON_FREE |
|
evbase, acceptcb, app_ctx, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
|
||||||
LEV_OPT_REUSEABLE, 16,
|
16, rp->ai_addr, rp->ai_addrlen);
|
||||||
rp->ai_addr, rp->ai_addrlen);
|
if (listener) {
|
||||||
if(listener) {
|
|
||||||
freeaddrinfo(res);
|
freeaddrinfo(res);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -714,16 +689,14 @@ static void start_listen(struct event_base *evbase, const char *service,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void initialize_app_context(app_context *app_ctx, SSL_CTX *ssl_ctx,
|
static void initialize_app_context(app_context *app_ctx, SSL_CTX *ssl_ctx,
|
||||||
struct event_base *evbase)
|
struct event_base *evbase) {
|
||||||
{
|
|
||||||
memset(app_ctx, 0, sizeof(app_context));
|
memset(app_ctx, 0, sizeof(app_context));
|
||||||
app_ctx->ssl_ctx = ssl_ctx;
|
app_ctx->ssl_ctx = ssl_ctx;
|
||||||
app_ctx->evbase = evbase;
|
app_ctx->evbase = evbase;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void run(const char *service,
|
static void run(const char *service, const char *key_file,
|
||||||
const char *key_file, const char *cert_file)
|
const char *cert_file) {
|
||||||
{
|
|
||||||
SSL_CTX *ssl_ctx;
|
SSL_CTX *ssl_ctx;
|
||||||
app_context app_ctx;
|
app_context app_ctx;
|
||||||
struct event_base *evbase;
|
struct event_base *evbase;
|
||||||
@@ -739,11 +712,10 @@ static void run(const char *service,
|
|||||||
SSL_CTX_free(ssl_ctx);
|
SSL_CTX_free(ssl_ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv) {
|
||||||
{
|
|
||||||
struct sigaction act;
|
struct sigaction act;
|
||||||
|
|
||||||
if(argc < 4) {
|
if (argc < 4) {
|
||||||
fprintf(stderr, "Usage: libevent-server PORT KEY_FILE CERT_FILE\n");
|
fprintf(stderr, "Usage: libevent-server PORT KEY_FILE CERT_FILE\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
@@ -752,10 +724,10 @@ int main(int argc, char **argv)
|
|||||||
act.sa_handler = SIG_IGN;
|
act.sa_handler = SIG_IGN;
|
||||||
sigaction(SIGPIPE, &act, NULL);
|
sigaction(SIGPIPE, &act, NULL);
|
||||||
|
|
||||||
OPENSSL_config(NULL);
|
|
||||||
OpenSSL_add_all_algorithms();
|
|
||||||
SSL_load_error_strings();
|
SSL_load_error_strings();
|
||||||
SSL_library_init();
|
SSL_library_init();
|
||||||
|
OpenSSL_add_all_algorithms();
|
||||||
|
OPENSSL_config(NULL);
|
||||||
|
|
||||||
run(argv[1], argv[2], argv[3]);
|
run(argv[1], argv[2], argv[3]);
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
40
genheaderfunc.py
Executable file
40
genheaderfunc.py
Executable file
@@ -0,0 +1,40 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
from gentokenlookup import gentokenlookup
|
||||||
|
|
||||||
|
HEADERS = [
|
||||||
|
':authority',
|
||||||
|
':method',
|
||||||
|
':path',
|
||||||
|
':scheme',
|
||||||
|
':status',
|
||||||
|
':host', # for spdy
|
||||||
|
'expect',
|
||||||
|
'host',
|
||||||
|
'if-modified-since',
|
||||||
|
"te",
|
||||||
|
"cookie",
|
||||||
|
"http2-settings",
|
||||||
|
"server",
|
||||||
|
"via",
|
||||||
|
"x-forwarded-for",
|
||||||
|
"x-forwarded-proto",
|
||||||
|
"alt-svc",
|
||||||
|
"content-length",
|
||||||
|
"location",
|
||||||
|
"trailer",
|
||||||
|
"link",
|
||||||
|
"accept-encoding",
|
||||||
|
"accept-language",
|
||||||
|
"cache-control",
|
||||||
|
"user-agent",
|
||||||
|
# disallowed h1 headers
|
||||||
|
'connection',
|
||||||
|
'keep-alive',
|
||||||
|
'proxy-connection',
|
||||||
|
'transfer-encoding',
|
||||||
|
'upgrade'
|
||||||
|
]
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
gentokenlookup(HEADERS, 'HD')
|
||||||
140
genlibtokenlookup.py
Executable file
140
genlibtokenlookup.py
Executable file
@@ -0,0 +1,140 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
HEADERS = [
|
||||||
|
(':authority', 0),
|
||||||
|
(':method', 1),
|
||||||
|
(':method', 2),
|
||||||
|
(':path', 3),
|
||||||
|
(':path', 4),
|
||||||
|
(':scheme', 5),
|
||||||
|
(':scheme', 6),
|
||||||
|
(':status', 7),
|
||||||
|
(':status', 8),
|
||||||
|
(':status', 9),
|
||||||
|
(':status', 10),
|
||||||
|
(':status', 11),
|
||||||
|
(':status', 12),
|
||||||
|
(':status', 13),
|
||||||
|
('accept-charset', 14),
|
||||||
|
('accept-encoding', 15),
|
||||||
|
('accept-language', 16),
|
||||||
|
('accept-ranges', 17),
|
||||||
|
('accept', 18),
|
||||||
|
('access-control-allow-origin', 19),
|
||||||
|
('age', 20),
|
||||||
|
('allow', 21),
|
||||||
|
('authorization', 22),
|
||||||
|
('cache-control', 23),
|
||||||
|
('content-disposition', 24),
|
||||||
|
('content-encoding', 25),
|
||||||
|
('content-language', 26),
|
||||||
|
('content-length', 27),
|
||||||
|
('content-location', 28),
|
||||||
|
('content-range', 29),
|
||||||
|
('content-type', 30),
|
||||||
|
('cookie', 31),
|
||||||
|
('date', 32),
|
||||||
|
('etag', 33),
|
||||||
|
('expect', 34),
|
||||||
|
('expires', 35),
|
||||||
|
('from', 36),
|
||||||
|
('host', 37),
|
||||||
|
('if-match', 38),
|
||||||
|
('if-modified-since', 39),
|
||||||
|
('if-none-match', 40),
|
||||||
|
('if-range', 41),
|
||||||
|
('if-unmodified-since', 42),
|
||||||
|
('last-modified', 43),
|
||||||
|
('link', 44),
|
||||||
|
('location', 45),
|
||||||
|
('max-forwards', 46),
|
||||||
|
('proxy-authenticate', 47),
|
||||||
|
('proxy-authorization', 48),
|
||||||
|
('range', 49),
|
||||||
|
('referer', 50),
|
||||||
|
('refresh', 51),
|
||||||
|
('retry-after', 52),
|
||||||
|
('server', 53),
|
||||||
|
('set-cookie', 54),
|
||||||
|
('strict-transport-security', 55),
|
||||||
|
('transfer-encoding', 56),
|
||||||
|
('user-agent', 57),
|
||||||
|
('vary', 58),
|
||||||
|
('via', 59),
|
||||||
|
('www-authenticate', 60),
|
||||||
|
('te', None),
|
||||||
|
('connection', None),
|
||||||
|
('keep-alive',None),
|
||||||
|
('proxy-connection', None),
|
||||||
|
('upgrade', None),
|
||||||
|
]
|
||||||
|
|
||||||
|
def to_enum_hd(k):
|
||||||
|
res = 'NGHTTP2_TOKEN_'
|
||||||
|
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():
|
||||||
|
name = ''
|
||||||
|
print 'typedef enum {'
|
||||||
|
for k, token in HEADERS:
|
||||||
|
if token is None:
|
||||||
|
print ' {},'.format(to_enum_hd(k))
|
||||||
|
else:
|
||||||
|
if name != k:
|
||||||
|
name = k
|
||||||
|
print ' {} = {},'.format(to_enum_hd(k), token)
|
||||||
|
print '} nghttp2_token;'
|
||||||
|
|
||||||
|
def gen_index_header():
|
||||||
|
print '''\
|
||||||
|
static inline 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[{}]) {{'''.format(size - 1)
|
||||||
|
for c in sorted(ents.keys()):
|
||||||
|
headers = sorted(ents[c])
|
||||||
|
print '''\
|
||||||
|
case '{}':'''.format(c)
|
||||||
|
for k in headers:
|
||||||
|
print '''\
|
||||||
|
if (lstreq("{}", 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()
|
||||||
53
genmethodfunc.py
Executable file
53
genmethodfunc.py
Executable file
@@ -0,0 +1,53 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
from io import StringIO
|
||||||
|
|
||||||
|
from gentokenlookup import gentokenlookup
|
||||||
|
|
||||||
|
# copied from http-parser/http_parser.h, and stripped trailing spaces
|
||||||
|
# and backslashes.
|
||||||
|
SRC = '''
|
||||||
|
XX(0, DELETE, DELETE)
|
||||||
|
XX(1, GET, GET)
|
||||||
|
XX(2, HEAD, HEAD)
|
||||||
|
XX(3, POST, POST)
|
||||||
|
XX(4, PUT, PUT)
|
||||||
|
/* pathological */
|
||||||
|
XX(5, CONNECT, CONNECT)
|
||||||
|
XX(6, OPTIONS, OPTIONS)
|
||||||
|
XX(7, TRACE, TRACE)
|
||||||
|
/* webdav */
|
||||||
|
XX(8, COPY, COPY)
|
||||||
|
XX(9, LOCK, LOCK)
|
||||||
|
XX(10, MKCOL, MKCOL)
|
||||||
|
XX(11, MOVE, MOVE)
|
||||||
|
XX(12, PROPFIND, PROPFIND)
|
||||||
|
XX(13, PROPPATCH, PROPPATCH)
|
||||||
|
XX(14, SEARCH, SEARCH)
|
||||||
|
XX(15, UNLOCK, UNLOCK)
|
||||||
|
/* subversion */
|
||||||
|
XX(16, REPORT, REPORT)
|
||||||
|
XX(17, MKACTIVITY, MKACTIVITY)
|
||||||
|
XX(18, CHECKOUT, CHECKOUT)
|
||||||
|
XX(19, MERGE, MERGE)
|
||||||
|
/* upnp */
|
||||||
|
XX(20, MSEARCH, M-SEARCH)
|
||||||
|
XX(21, NOTIFY, NOTIFY)
|
||||||
|
XX(22, SUBSCRIBE, SUBSCRIBE)
|
||||||
|
XX(23, UNSUBSCRIBE, UNSUBSCRIBE)
|
||||||
|
/* RFC-5789 */
|
||||||
|
XX(24, PATCH, PATCH)
|
||||||
|
XX(25, PURGE, PURGE)
|
||||||
|
/* CalDAV */
|
||||||
|
XX(26, MKCALENDAR, MKCALENDAR)
|
||||||
|
'''
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
methods = []
|
||||||
|
for line in StringIO(SRC):
|
||||||
|
line = line.strip()
|
||||||
|
if not line.startswith('XX'):
|
||||||
|
continue
|
||||||
|
_, m, _ = line.split(',', 2)
|
||||||
|
methods.append(m.strip())
|
||||||
|
gentokenlookup(methods, 'HTTP')
|
||||||
69
gentokenlookup.py
Normal file
69
gentokenlookup.py
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
def to_enum_hd(k, prefix):
|
||||||
|
res = prefix + '_'
|
||||||
|
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(tokens, prefix):
|
||||||
|
print '''\
|
||||||
|
enum {'''
|
||||||
|
for k in sorted(tokens):
|
||||||
|
print '''\
|
||||||
|
{},'''.format(to_enum_hd(k, prefix))
|
||||||
|
print '''\
|
||||||
|
{}_MAXIDX,
|
||||||
|
}};'''.format(prefix)
|
||||||
|
|
||||||
|
def gen_index_header(tokens, prefix):
|
||||||
|
print '''\
|
||||||
|
int lookup_token(const uint8_t *name, size_t namelen) {
|
||||||
|
switch (namelen) {'''
|
||||||
|
b = build_header(tokens)
|
||||||
|
for size in sorted(b.keys()):
|
||||||
|
ents = b[size]
|
||||||
|
print '''\
|
||||||
|
case {}:'''.format(size)
|
||||||
|
print '''\
|
||||||
|
switch (name[{}]) {{'''.format(size - 1)
|
||||||
|
for c in sorted(ents.keys()):
|
||||||
|
headers = sorted(ents[c])
|
||||||
|
print '''\
|
||||||
|
case '{}':'''.format(c)
|
||||||
|
for k in headers:
|
||||||
|
print '''\
|
||||||
|
if (util::streq_l("{}", name, {})) {{
|
||||||
|
return {};
|
||||||
|
}}'''.format(k[:-1], size - 1, to_enum_hd(k, prefix))
|
||||||
|
print '''\
|
||||||
|
break;'''
|
||||||
|
print '''\
|
||||||
|
}
|
||||||
|
break;'''
|
||||||
|
print '''\
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}'''
|
||||||
|
|
||||||
|
def gentokenlookup(tokens, prefix):
|
||||||
|
gen_enum(tokens, prefix)
|
||||||
|
print ''
|
||||||
|
gen_index_header(tokens, prefix)
|
||||||
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()
|
||||||
192
help2rst.py
Executable file
192
help2rst.py
Executable file
@@ -0,0 +1,192 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# script to produce rst file from program's help output.
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
from __future__ import print_function
|
||||||
|
import sys
|
||||||
|
import re
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
arg_indent = ' ' * 14
|
||||||
|
|
||||||
|
def help2man(infile):
|
||||||
|
# We assume that first line is usage line like this:
|
||||||
|
#
|
||||||
|
# Usage: nghttp [OPTIONS]... URI...
|
||||||
|
#
|
||||||
|
# The second line is description of the command. Multiple lines
|
||||||
|
# are permitted. The blank line signals the end of this section.
|
||||||
|
# After that, we parses positional and optional arguments.
|
||||||
|
#
|
||||||
|
# The positional argument is enclosed with < and >:
|
||||||
|
#
|
||||||
|
# <PRIVATE_KEY>
|
||||||
|
#
|
||||||
|
# We may describe default behavior without any options by encoding
|
||||||
|
# ( and ):
|
||||||
|
#
|
||||||
|
# (default mode)
|
||||||
|
#
|
||||||
|
# "Options:" is treated specially and produces "OPTIONS" section.
|
||||||
|
# We allow subsection under OPTIONS. Lines not starting with (, <
|
||||||
|
# and Options: are treated as subsection name and produces section
|
||||||
|
# one level down:
|
||||||
|
#
|
||||||
|
# TLS/SSL:
|
||||||
|
#
|
||||||
|
# The above is an example of subsection.
|
||||||
|
#
|
||||||
|
# The description of arguments must be indented by len(arg_indent)
|
||||||
|
# characters. The default value should be placed in separate line
|
||||||
|
# and should be start with "Default: " after indentation.
|
||||||
|
|
||||||
|
line = infile.readline().strip()
|
||||||
|
m = re.match(r'^Usage: (.*)', line)
|
||||||
|
if not m:
|
||||||
|
print('usage line is invalid. Expected following lines:')
|
||||||
|
print('Usage: cmdname ...')
|
||||||
|
sys.exit(1)
|
||||||
|
synopsis = m.group(1).split(' ', 1)
|
||||||
|
if len(synopsis) == 2:
|
||||||
|
cmdname, args = synopsis
|
||||||
|
else:
|
||||||
|
cmdname, args = synopsis[0], ''
|
||||||
|
|
||||||
|
description = []
|
||||||
|
for line in infile:
|
||||||
|
line = line.strip()
|
||||||
|
if not line:
|
||||||
|
break
|
||||||
|
description.append(line)
|
||||||
|
|
||||||
|
print('''
|
||||||
|
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
|
||||||
|
|
||||||
|
.. program:: {cmdname}
|
||||||
|
|
||||||
|
{cmdname}(1)
|
||||||
|
{cmdnameunderline}
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
--------
|
||||||
|
|
||||||
|
**{cmdname}** {args}
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
-----------
|
||||||
|
|
||||||
|
{description}
|
||||||
|
'''.format(cmdname=cmdname, args=args,
|
||||||
|
cmdnameunderline='=' * (len(cmdname) + 3),
|
||||||
|
synopsis=synopsis, description=format_text('\n'.join(description))))
|
||||||
|
|
||||||
|
in_arg = False
|
||||||
|
in_footer = False
|
||||||
|
|
||||||
|
for line in infile:
|
||||||
|
line = line.rstrip()
|
||||||
|
|
||||||
|
if not line.strip() and in_arg:
|
||||||
|
print()
|
||||||
|
continue
|
||||||
|
if line.startswith(' ') and in_arg:
|
||||||
|
if not line.startswith(arg_indent):
|
||||||
|
sys.stderr.write('warning: argument description is not indented correctly. We need {} spaces as indentation.\n'.format(len(arg_indent)))
|
||||||
|
print('{}'.format(format_arg_text(line[len(arg_indent):])))
|
||||||
|
continue
|
||||||
|
|
||||||
|
if in_arg:
|
||||||
|
print()
|
||||||
|
in_arg = False
|
||||||
|
|
||||||
|
if line == '--':
|
||||||
|
in_footer = True
|
||||||
|
continue
|
||||||
|
|
||||||
|
if in_footer:
|
||||||
|
print(line.strip())
|
||||||
|
continue
|
||||||
|
|
||||||
|
if line == 'Options:':
|
||||||
|
print('OPTIONS')
|
||||||
|
print('-------')
|
||||||
|
print()
|
||||||
|
continue
|
||||||
|
|
||||||
|
if line.startswith(' <'):
|
||||||
|
# positional argument
|
||||||
|
m = re.match(r'^(?:\s+)([a-zA-Z0-9-_<>]+)(.*)', line)
|
||||||
|
argname, rest = m.group(1), m.group(2)
|
||||||
|
print('.. describe:: {}'.format(argname))
|
||||||
|
print()
|
||||||
|
print('{}'.format(format_arg_text(rest.strip())))
|
||||||
|
in_arg = True
|
||||||
|
continue
|
||||||
|
|
||||||
|
if line.startswith(' ('):
|
||||||
|
# positional argument
|
||||||
|
m = re.match(r'^(?:\s+)(\([a-zA-Z0-9-_<> ]+\))(.*)', line)
|
||||||
|
argname, rest = m.group(1), m.group(2)
|
||||||
|
print('.. describe:: {}'.format(argname))
|
||||||
|
print()
|
||||||
|
print('{}'.format(format_arg_text(rest.strip())))
|
||||||
|
in_arg = True
|
||||||
|
continue
|
||||||
|
|
||||||
|
if line.startswith(' -'):
|
||||||
|
# optional argument
|
||||||
|
m = re.match(
|
||||||
|
r'^(?:\s+)(-\S+?(?:, -\S+?)*)($| .*)',
|
||||||
|
line)
|
||||||
|
argname, rest = m.group(1), m.group(2)
|
||||||
|
print('.. option:: {}'.format(argname))
|
||||||
|
print()
|
||||||
|
rest = rest.strip()
|
||||||
|
if len(rest):
|
||||||
|
print('{}'.format(format_arg_text(rest)))
|
||||||
|
in_arg = True
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not line.startswith(' ') and line.endswith(':'):
|
||||||
|
# subsection
|
||||||
|
subsec = line.strip()[:-1]
|
||||||
|
print('{}'.format(subsec))
|
||||||
|
print('{}'.format('~' * len(subsec)))
|
||||||
|
print()
|
||||||
|
continue
|
||||||
|
|
||||||
|
print(line.strip())
|
||||||
|
|
||||||
|
def format_text(text):
|
||||||
|
# escape *
|
||||||
|
if len(text) > len(arg_indent):
|
||||||
|
text = text[:len(arg_indent) + 1] + re.sub(r'\*', r'\*', text[len(arg_indent) + 1:])
|
||||||
|
else:
|
||||||
|
text = re.sub(r'\*', r'\*', text)
|
||||||
|
# markup option reference
|
||||||
|
text = re.sub(r'(^|\s)(-[a-zA-Z0-9-]+)', r'\1:option:`\2`', text)
|
||||||
|
# sphinx does not like markup like ':option:`-f`='. We need
|
||||||
|
# backslash between ` and =.
|
||||||
|
text = re.sub(r'(:option:`.*?`)(\S)', r'\1\\\2', text)
|
||||||
|
# file path should be italic
|
||||||
|
text = re.sub(r'(^|\s|\'|")(/[^\s\'"]*)', r'\1*\2*', text)
|
||||||
|
return text
|
||||||
|
|
||||||
|
def format_arg_text(text):
|
||||||
|
if text.strip().startswith('Default: '):
|
||||||
|
return '\n ' + re.sub(r'^(\s*Default: )(.*)$', r'\1``\2``', text)
|
||||||
|
return ' {}'.format(format_text(text))
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description='Produces rst document from help output.')
|
||||||
|
parser.add_argument('-i', '--include', metavar='FILE',
|
||||||
|
help='include content of <FILE> as verbatim. It should be ReST formatted text.')
|
||||||
|
args = parser.parse_args()
|
||||||
|
help2man(sys.stdin)
|
||||||
|
if args.include:
|
||||||
|
print()
|
||||||
|
with open(args.include) as f:
|
||||||
|
sys.stdout.write(f.read())
|
||||||
2
integration-tests/.gitignore
vendored
Normal file
2
integration-tests/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# generated files
|
||||||
|
config.go
|
||||||
44
integration-tests/Makefile.am
Normal file
44
integration-tests/Makefile.am
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
# nghttp2 - HTTP/2 C Library
|
||||||
|
|
||||||
|
# Copyright (c) 2015 Tatsuhiro Tsujikawa
|
||||||
|
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
# a copy of this software and associated documentation files (the
|
||||||
|
# "Software"), to deal in the Software without restriction, including
|
||||||
|
# without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
# permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
# the following conditions:
|
||||||
|
|
||||||
|
# The above copyright notice and this permission notice shall be
|
||||||
|
# included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
EXTRA_DIST = \
|
||||||
|
nghttpx_http1_test.go \
|
||||||
|
nghttpx_http2_test.go \
|
||||||
|
nghttpx_spdy_test.go \
|
||||||
|
server_tester.go \
|
||||||
|
server.key \
|
||||||
|
server.crt \
|
||||||
|
alt-server.key \
|
||||||
|
alt-server.crt \
|
||||||
|
setenv
|
||||||
|
|
||||||
|
.PHONY: itprep it
|
||||||
|
|
||||||
|
itprep:
|
||||||
|
go get -d -v github.com/bradfitz/http2
|
||||||
|
go get -d -v github.com/tatsuhiro-t/go-nghttp2
|
||||||
|
go get -d -v github.com/tatsuhiro-t/spdy
|
||||||
|
go get -d -v golang.org/x/net/websocket
|
||||||
|
|
||||||
|
it:
|
||||||
|
sh setenv go test -v
|
||||||
21
integration-tests/alt-server.crt
Normal file
21
integration-tests/alt-server.crt
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDhzCCAm+gAwIBAgIJANfuEldiquMNMA0GCSqGSIb3DQEBCwUAMFoxCzAJBgNV
|
||||||
|
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
|
||||||
|
aWRnaXRzIFB0eSBMdGQxEzARBgNVBAMMCmFsdC1kb21haW4wHhcNMTUwMTI1MDYy
|
||||||
|
NTQxWhcNMjUwMTIyMDYyNTQxWjBaMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29t
|
||||||
|
ZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRMwEQYD
|
||||||
|
VQQDDAphbHQtZG9tYWluMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
|
||||||
|
0IwhDOGDipGrJQ9IoRSzPdkU/Ii4aJgGKHlXminym42X0VI3IW61RLvOHRlHVmVH
|
||||||
|
JQjFuDo2x+y81t9NlDg3HGUbSpzOzpm6StiutB7c4hreT5G4r0YKya1ugiemN0+p
|
||||||
|
qjIPJWm2jVnf448eZvUKRKEQ9W0MLZjiNjVGKrKlwo7fIlXg4N3+YixLYffAT1NV
|
||||||
|
d1T6V5jzlbruj15gK2nGjMQ9D1h1t9vTbTxY+mtk72aX0Y64IE6pPBWLFSSH8ozU
|
||||||
|
idDoL3AZwz2Jker+ALKK8CM4uho/RPpyW1C06HH+HLdH2MqEjDOROde/Nzxm668O
|
||||||
|
gK/JWGIEyUqYiUXx0yhFxwIDAQABo1AwTjAdBgNVHQ4EFgQU/Y0GDN2uPjbyePcu
|
||||||
|
95ZvYEK/gHIwHwYDVR0jBBgwFoAU/Y0GDN2uPjbyePcu95ZvYEK/gHIwDAYDVR0T
|
||||||
|
BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAodD6LVCzL3wfsZ6TxTzf9TfgIdbj
|
||||||
|
ilL3SEMT/xnfTXT3SLYScTRqQIAI29Y7dOLMq89p4hY2wmeUEhBUAz+y9G2JVr8o
|
||||||
|
6EbxXrQpWgNJogELqoNnMdrDxB5RsmDDKEJ/rLjDfSkjWbK7B2PZsqVTDgjekCFw
|
||||||
|
u6FqTIjn/O1O/L5tjwxwxjHmQod/maFCvXoDOVBuwdHnkp298tqlvsHfHO8m++Wj
|
||||||
|
+XYB8plMIjpeTh9v4w9Jc4QZ59lK/3Tt4qaENeQrMEubKSY/Zen7L2bzhk+cChWT
|
||||||
|
GSGz9uNXieoZaH79D0wnyZaSZ5Ds4ActMevnGg3iYXuzuFqx8Pungn74Vg==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
28
integration-tests/alt-server.key
Normal file
28
integration-tests/alt-server.key
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDQjCEM4YOKkasl
|
||||||
|
D0ihFLM92RT8iLhomAYoeVeaKfKbjZfRUjchbrVEu84dGUdWZUclCMW4OjbH7LzW
|
||||||
|
302UODccZRtKnM7OmbpK2K60HtziGt5PkbivRgrJrW6CJ6Y3T6mqMg8labaNWd/j
|
||||||
|
jx5m9QpEoRD1bQwtmOI2NUYqsqXCjt8iVeDg3f5iLEth98BPU1V3VPpXmPOVuu6P
|
||||||
|
XmAracaMxD0PWHW329NtPFj6a2TvZpfRjrggTqk8FYsVJIfyjNSJ0OgvcBnDPYmR
|
||||||
|
6v4AsorwIzi6Gj9E+nJbULTocf4ct0fYyoSMM5E51783PGbrrw6Ar8lYYgTJSpiJ
|
||||||
|
RfHTKEXHAgMBAAECggEBALTrjFSXY72YB+h7rN+JjMIwDIPUvF6I3HbKZhQpJf6K
|
||||||
|
xNVkRM2tNHavku0tm/S4ohLf3F+pqRKiL2Udjjjy1+S7VgTRqpwTQ0lhV5aNW8SP
|
||||||
|
2KMg4R61XfB+k+s4KHu9kYxEJ12mqydPe+r3o0FgfYryTDsOYk1AX6b1aqzqFOGF
|
||||||
|
7GaqLALSbKU59tcJJ1SZNBbpIKFUrAT9nZt9dW02/foqP5bzUk43Yjw48xmLwegc
|
||||||
|
bMXXcpZhNZSktltvwRw7Q4Foc9kuRlMdTAnAD9PnMCcZwicS/YeVVF6Rz4fGviKv
|
||||||
|
7/kPHQ7g4YpFktVDzuZ5xw6GDVFeJ6uGMVUX8+EePvkCgYEA+/nrcn82nFHCxm8Q
|
||||||
|
0iiUhi/AoXjZg+O5Ytaje9O/YNoX+c4ywe13h0+TXKH79O0KfTwXeJyDgPZbAIFV
|
||||||
|
9oURellRYUzKDafnBHis2f+Ywn6GqHL5e2X30ZxIp1GK46pcvne1YuvJhgGmiVay
|
||||||
|
vd7sRx09OKU124dG22rIFCis6asCgYEA0+CsA6LrEwQ/aPJYASY3VHNO/WoAOnPg
|
||||||
|
Cwsg+02XWsPEwP//lNmpanz8TUm2URS063ZK8bx7t3ejvDgBdsRwwjiMlDp7XTUU
|
||||||
|
3Zk+mhCV2qkMi02aKemvz29bDhmh5JoH7W3IwsXtJYO0yZDYrDR3ioiKRccioPoE
|
||||||
|
b/Nq781sEFUCgYEA4xqx9xRpaCLY5nicNI6WrwrDF8YQZisNn+PMnYKP7v8itOgA
|
||||||
|
H4GkRbSXINpueKZc2dsbXH3UmJtyEdaAYBw3UIrIKmZHhl9afFE3mZQhXssjGxfl
|
||||||
|
fC6/WZD+eq+n+uJFjPXf6jSSAdHjA828dB1D4CSeVTuyexZF6uUnR+QRVNkCgYEA
|
||||||
|
i+pb7XLSpZYygY03zFp+Q0h6KyKqz+7hTqmkuA8/GfMZpRHop1UtaWLsAeXhfZ2c
|
||||||
|
87kEOKptUHSzLYIWhWWnyLorK1+LQ7vf8Y5XJso5C1KDNCKk4XSuYt94U9FddWa6
|
||||||
|
QXI0F1s5BYL6Cfma++0R2+va08Vy+rbf40XtojoXWJkCgYEA0hMQSCvok7is27nQ
|
||||||
|
G80KXfmghU2eEB7zif3T00/fwJycxEbmnNeof+SKmhdY4ZgqTscfOxlQPflV/eqB
|
||||||
|
xs4GnFDDeM0F8KH0BimOXxr7sJPFCg22PCCQQcRtM/KoU+ip/kNmTfwrsC0xMFPU
|
||||||
|
HD8M1JCZF2eLMekXXP3cB0U4sUs=
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
5
integration-tests/config.go.in
Normal file
5
integration-tests/config.go.in
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package nghttp2
|
||||||
|
|
||||||
|
const (
|
||||||
|
buildDir = "@top_builddir@"
|
||||||
|
)
|
||||||
549
integration-tests/nghttpx_http1_test.go
Normal file
549
integration-tests/nghttpx_http1_test.go
Normal file
@@ -0,0 +1,549 @@
|
|||||||
|
package nghttp2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"github.com/bradfitz/http2/hpack"
|
||||||
|
"golang.org/x/net/websocket"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"syscall"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestH1H1PlainGET tests whether simple HTTP/1 GET request works.
|
||||||
|
func TestH1H1PlainGET(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http1(requestParam{
|
||||||
|
name: "TestH1H1PlainGET",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http1() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
want := 200
|
||||||
|
if got := res.status; got != want {
|
||||||
|
t.Errorf("status = %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH1H1PlainGETClose tests whether simple HTTP/1 GET request with
|
||||||
|
// Connetion: close request header field works.
|
||||||
|
func TestH1H1PlainGETClose(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http1(requestParam{
|
||||||
|
name: "TestH1H1PlainGETClose",
|
||||||
|
header: []hpack.HeaderField{
|
||||||
|
pair("Connection", "close"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http1() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
want := 200
|
||||||
|
if got := res.status; got != want {
|
||||||
|
t.Errorf("status = %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH1H1InvalidMethod tests that server rejects invalid method with
|
||||||
|
// 501 status code
|
||||||
|
func TestH1H1InvalidMethod(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
t.Errorf("server should not forward this request")
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http1(requestParam{
|
||||||
|
name: "TestH1H1InvalidMethod",
|
||||||
|
method: "get",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http1() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if got, want := res.status, 501; got != want {
|
||||||
|
t.Errorf("status = %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH1H1MultipleRequestCL tests that server rejects request which
|
||||||
|
// contains multiple Content-Length header fields.
|
||||||
|
func TestH1H1MultipleRequestCL(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
t.Errorf("server should not forward bad request")
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
if _, err := io.WriteString(st.conn, fmt.Sprintf(`GET / HTTP/1.1
|
||||||
|
Host: %v
|
||||||
|
Test-Case: TestH1H1MultipleRequestCL
|
||||||
|
Content-Length: 0
|
||||||
|
Content-Length: 0
|
||||||
|
|
||||||
|
`, st.authority)); err != nil {
|
||||||
|
t.Fatalf("Error io.WriteString() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error http.ReadResponse() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
want := 400
|
||||||
|
if got := resp.StatusCode; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH1H1ConnectFailure tests that server handles the situation that
|
||||||
|
// connection attempt to HTTP/1 backend failed.
|
||||||
|
func TestH1H1ConnectFailure(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
// shutdown backend server to simulate backend connect failure
|
||||||
|
st.ts.Close()
|
||||||
|
|
||||||
|
res, err := st.http1(requestParam{
|
||||||
|
name: "TestH1H1ConnectFailure",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http1() = %v", err)
|
||||||
|
}
|
||||||
|
want := 503
|
||||||
|
if got := res.status; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH1H1GracefulShutdown tests graceful shutdown.
|
||||||
|
func TestH1H1GracefulShutdown(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http1(requestParam{
|
||||||
|
name: "TestH1H1GracefulShutdown-1",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http1() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if got, want := res.status, 200; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
st.cmd.Process.Signal(syscall.SIGQUIT)
|
||||||
|
|
||||||
|
res, err = st.http1(requestParam{
|
||||||
|
name: "TestH1H1GracefulShutdown-2",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http1() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if got, want := res.status, 200; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
if got, want := res.connClose, true; got != want {
|
||||||
|
t.Errorf("res.connClose: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
want := io.EOF
|
||||||
|
if _, err := st.conn.Read(nil); err == nil || err != want {
|
||||||
|
t.Errorf("st.conn.Read(): %v; want %v", err, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH1H1HostRewrite tests that server rewrites Host header field
|
||||||
|
func TestH1H1HostRewrite(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Add("request-host", r.Host)
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http1(requestParam{
|
||||||
|
name: "TestH1H1HostRewrite",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http1() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.status, 200; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
if got, want := res.header.Get("request-host"), st.backendHost; got != want {
|
||||||
|
t.Errorf("request-host: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH1H1HTTP10 tests that server can accept HTTP/1.0 request
|
||||||
|
// without Host header field
|
||||||
|
func TestH1H1HTTP10(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Add("request-host", r.Host)
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H1HTTP10\r\n\r\n"); err != nil {
|
||||||
|
t.Fatalf("Error io.WriteString() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error http.ReadResponse() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if got, want := resp.StatusCode, 200; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
if got, want := resp.Header.Get("request-host"), st.backendHost; got != want {
|
||||||
|
t.Errorf("request-host: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH1H1HTTP10NoHostRewrite tests that server generates host header
|
||||||
|
// field using actual backend server even if --no-http-rewrite is
|
||||||
|
// used.
|
||||||
|
func TestH1H1HTTP10NoHostRewrite(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--no-host-rewrite"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Add("request-host", r.Host)
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H1HTTP10NoHostRewrite\r\n\r\n"); err != nil {
|
||||||
|
t.Fatalf("Error io.WriteString() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error http.ReadResponse() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if got, want := resp.StatusCode, 200; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
if got, want := resp.Header.Get("request-host"), st.backendHost; got != want {
|
||||||
|
t.Errorf("request-host: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH1H1RequestTrailer tests request trailer part is forwarded to
|
||||||
|
// backend.
|
||||||
|
func TestH1H1RequestTrailer(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
buf := make([]byte, 4096)
|
||||||
|
for {
|
||||||
|
_, err := r.Body.Read(buf)
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("r.Body.Read() = %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if got, want := r.Trailer.Get("foo"), "bar"; got != want {
|
||||||
|
t.Errorf("r.Trailer.Get(foo): %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http1(requestParam{
|
||||||
|
name: "TestH1H1RequestTrailer",
|
||||||
|
body: []byte("1"),
|
||||||
|
trailer: []hpack.HeaderField{
|
||||||
|
pair("foo", "bar"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http1() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.status, 200; got != want {
|
||||||
|
t.Errorf("res.status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH1H1HeaderFieldBufferPath tests that request with request path
|
||||||
|
// larger than configured buffer size is rejected.
|
||||||
|
func TestH1H1HeaderFieldBufferPath(t *testing.T) {
|
||||||
|
// The value 100 is chosen so that sum of header fields bytes
|
||||||
|
// does not exceed it. We use > 100 bytes URI to exceed this
|
||||||
|
// limit.
|
||||||
|
st := newServerTester([]string{"--header-field-buffer=100"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
t.Fatal("execution path should not be here")
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http1(requestParam{
|
||||||
|
name: "TestH1H1HeaderFieldBufferPath",
|
||||||
|
path: "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http1() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.status, 431; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH1H1HeaderFieldBuffer tests that request with header fields
|
||||||
|
// larger than configured buffer size is rejected.
|
||||||
|
func TestH1H1HeaderFieldBuffer(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--header-field-buffer=10"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
t.Fatal("execution path should not be here")
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http1(requestParam{
|
||||||
|
name: "TestH1H1HeaderFieldBuffer",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http1() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.status, 431; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH1H1HeaderFields tests that request with header fields more
|
||||||
|
// than configured number is rejected.
|
||||||
|
func TestH1H1HeaderFields(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--max-header-fields=1"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
t.Fatal("execution path should not be here")
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http1(requestParam{
|
||||||
|
name: "TestH1H1HeaderFields",
|
||||||
|
header: []hpack.HeaderField{
|
||||||
|
// Add extra header field to ensure that
|
||||||
|
// header field limit exceeds
|
||||||
|
pair("Connection", "close"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http1() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.status, 431; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH1H1Websocket tests that HTTP Upgrade to WebSocket works.
|
||||||
|
func TestH1H1Websocket(t *testing.T) {
|
||||||
|
st := newServerTesterHandler(nil, t, websocket.Handler(func(ws *websocket.Conn) {
|
||||||
|
io.Copy(ws, ws)
|
||||||
|
}))
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
content := []byte("hello world")
|
||||||
|
res, err := st.websocket(requestParam{
|
||||||
|
name: "TestH1H1Websocket",
|
||||||
|
body: content,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.websocket() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.body, content; !bytes.Equal(got, want) {
|
||||||
|
t.Errorf("echo: %q; want %q", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH1H2ConnectFailure tests that server handles the situation that
|
||||||
|
// connection attempt to HTTP/2 backend failed.
|
||||||
|
func TestH1H2ConnectFailure(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--http2-bridge"}, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
// simulate backend connect attempt failure
|
||||||
|
st.ts.Close()
|
||||||
|
|
||||||
|
res, err := st.http1(requestParam{
|
||||||
|
name: "TestH1H2ConnectFailure",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http1() = %v", err)
|
||||||
|
}
|
||||||
|
want := 503
|
||||||
|
if got := res.status; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH1H2NoHost tests that server rejects request without Host
|
||||||
|
// header field for HTTP/2 backend.
|
||||||
|
func TestH1H2NoHost(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
t.Errorf("server should not forward bad request")
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
// without Host header field, we expect 400 response
|
||||||
|
if _, err := io.WriteString(st.conn, "GET / HTTP/1.1\r\nTest-Case: TestH1H2NoHost\r\n\r\n"); err != nil {
|
||||||
|
t.Fatalf("Error io.WriteString() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error http.ReadResponse() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
want := 400
|
||||||
|
if got := resp.StatusCode; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH1H2HTTP10 tests that server can accept HTTP/1.0 request
|
||||||
|
// without Host header field
|
||||||
|
func TestH1H2HTTP10(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Add("request-host", r.Host)
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H2HTTP10\r\n\r\n"); err != nil {
|
||||||
|
t.Fatalf("Error io.WriteString() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error http.ReadResponse() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if got, want := resp.StatusCode, 200; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
if got, want := resp.Header.Get("request-host"), st.backendHost; got != want {
|
||||||
|
t.Errorf("request-host: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH1H2HTTP10NoHostRewrite tests that server generates host header
|
||||||
|
// field using actual backend server even if --no-http-rewrite is
|
||||||
|
// used.
|
||||||
|
func TestH1H2HTTP10NoHostRewrite(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--http2-bridge", "--no-host-rewrite"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Add("request-host", r.Host)
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H2HTTP10NoHostRewrite\r\n\r\n"); err != nil {
|
||||||
|
t.Fatalf("Error io.WriteString() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error http.ReadResponse() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if got, want := resp.StatusCode, 200; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
if got, want := resp.Header.Get("request-host"), st.backendHost; got != want {
|
||||||
|
t.Errorf("request-host: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH1H2CrumbleCookie tests that Cookies are crumbled and assembled
|
||||||
|
// when forwarding to HTTP/2 backend link. go-nghttp2 server
|
||||||
|
// concatenates crumbled Cookies automatically, so this test is not
|
||||||
|
// much effective now.
|
||||||
|
func TestH1H2CrumbleCookie(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if got, want := r.Header.Get("Cookie"), "alpha; bravo; charlie"; got != want {
|
||||||
|
t.Errorf("Cookie: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http1(requestParam{
|
||||||
|
name: "TestH1H2CrumbleCookie",
|
||||||
|
header: []hpack.HeaderField{
|
||||||
|
pair("Cookie", "alpha; bravo; charlie"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http1() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.status, 200; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH1H2GenerateVia tests that server generates Via header field to and
|
||||||
|
// from backend server.
|
||||||
|
func TestH1H2GenerateVia(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if got, want := r.Header.Get("Via"), "1.1 nghttpx"; got != want {
|
||||||
|
t.Errorf("Via: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http1(requestParam{
|
||||||
|
name: "TestH1H2GenerateVia",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http1() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.header.Get("Via"), "2 nghttpx"; got != want {
|
||||||
|
t.Errorf("Via: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH1H2AppendVia tests that server adds value to existing Via
|
||||||
|
// header field to and from backend server.
|
||||||
|
func TestH1H2AppendVia(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if got, want := r.Header.Get("Via"), "foo, 1.1 nghttpx"; got != want {
|
||||||
|
t.Errorf("Via: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
w.Header().Add("Via", "bar")
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http1(requestParam{
|
||||||
|
name: "TestH1H2AppendVia",
|
||||||
|
header: []hpack.HeaderField{
|
||||||
|
pair("via", "foo"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http1() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.header.Get("Via"), "bar, 2 nghttpx"; got != want {
|
||||||
|
t.Errorf("Via: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH1H2NoVia tests that server does not add value to existing Via
|
||||||
|
// header field to and from backend server.
|
||||||
|
func TestH1H2NoVia(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--http2-bridge", "--no-via"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if got, want := r.Header.Get("Via"), "foo"; got != want {
|
||||||
|
t.Errorf("Via: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
w.Header().Add("Via", "bar")
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http1(requestParam{
|
||||||
|
name: "TestH1H2NoVia",
|
||||||
|
header: []hpack.HeaderField{
|
||||||
|
pair("via", "foo"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http1() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.header.Get("Via"), "bar"; got != want {
|
||||||
|
t.Errorf("Via: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
877
integration-tests/nghttpx_http2_test.go
Normal file
877
integration-tests/nghttpx_http2_test.go
Normal file
@@ -0,0 +1,877 @@
|
|||||||
|
package nghttp2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
|
"github.com/bradfitz/http2"
|
||||||
|
"github.com/bradfitz/http2/hpack"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestH2H1PlainGET tests whether simple HTTP/2 GET request works.
|
||||||
|
func TestH2H1PlainGET(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1PlainGET",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
want := 200
|
||||||
|
if res.status != want {
|
||||||
|
t.Errorf("status = %v; want %v", res.status, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1AddXff tests that server generates X-Forwarded-For header
|
||||||
|
// field when forwarding request to backend.
|
||||||
|
func TestH2H1AddXff(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--add-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
xff := r.Header.Get("X-Forwarded-For")
|
||||||
|
want := "127.0.0.1"
|
||||||
|
if xff != want {
|
||||||
|
t.Errorf("X-Forwarded-For = %v; want %v", xff, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
_, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1AddXff",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1AddXff2 tests that server appends X-Forwarded-For header
|
||||||
|
// field to existing one when forwarding request to backend.
|
||||||
|
func TestH2H1AddXff2(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--add-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
xff := r.Header.Get("X-Forwarded-For")
|
||||||
|
want := "host, 127.0.0.1"
|
||||||
|
if xff != want {
|
||||||
|
t.Errorf("X-Forwarded-For = %v; want %v", xff, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
_, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1AddXff2",
|
||||||
|
header: []hpack.HeaderField{
|
||||||
|
pair("x-forwarded-for", "host"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1StripXff tests that --strip-incoming-x-forwarded-for
|
||||||
|
// option.
|
||||||
|
func TestH2H1StripXff(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--strip-incoming-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if xff, found := r.Header["X-Forwarded-For"]; found {
|
||||||
|
t.Errorf("X-Forwarded-For = %v; want nothing", xff)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
_, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1StripXff1",
|
||||||
|
header: []hpack.HeaderField{
|
||||||
|
pair("x-forwarded-for", "host"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1StripAddXff tests that --strip-incoming-x-forwarded-for and
|
||||||
|
// --add-x-forwarded-for options.
|
||||||
|
func TestH2H1StripAddXff(t *testing.T) {
|
||||||
|
args := []string{
|
||||||
|
"--strip-incoming-x-forwarded-for",
|
||||||
|
"--add-x-forwarded-for",
|
||||||
|
}
|
||||||
|
st := newServerTester(args, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
xff := r.Header.Get("X-Forwarded-For")
|
||||||
|
want := "127.0.0.1"
|
||||||
|
if xff != want {
|
||||||
|
t.Errorf("X-Forwarded-For = %v; want %v", xff, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
_, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1StripAddXff",
|
||||||
|
header: []hpack.HeaderField{
|
||||||
|
pair("x-forwarded-for", "host"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1GenerateVia tests that server generates Via header field to and
|
||||||
|
// from backend server.
|
||||||
|
func TestH2H1GenerateVia(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if got, want := r.Header.Get("Via"), "2 nghttpx"; got != want {
|
||||||
|
t.Errorf("Via: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1GenerateVia",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.header.Get("Via"), "1.1 nghttpx"; got != want {
|
||||||
|
t.Errorf("Via: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1AppendVia tests that server adds value to existing Via
|
||||||
|
// header field to and from backend server.
|
||||||
|
func TestH2H1AppendVia(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if got, want := r.Header.Get("Via"), "foo, 2 nghttpx"; got != want {
|
||||||
|
t.Errorf("Via: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
w.Header().Add("Via", "bar")
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1AppendVia",
|
||||||
|
header: []hpack.HeaderField{
|
||||||
|
pair("via", "foo"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.header.Get("Via"), "bar, 1.1 nghttpx"; got != want {
|
||||||
|
t.Errorf("Via: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1NoVia tests that server does not add value to existing Via
|
||||||
|
// header field to and from backend server.
|
||||||
|
func TestH2H1NoVia(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--no-via"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if got, want := r.Header.Get("Via"), "foo"; got != want {
|
||||||
|
t.Errorf("Via: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
w.Header().Add("Via", "bar")
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1NoVia",
|
||||||
|
header: []hpack.HeaderField{
|
||||||
|
pair("via", "foo"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.header.Get("Via"), "bar"; got != want {
|
||||||
|
t.Errorf("Via: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1HostRewrite tests that server rewrites host header field
|
||||||
|
func TestH2H1HostRewrite(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Add("request-host", r.Host)
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1HostRewrite",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.status, 200; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
if got, want := res.header.Get("request-host"), st.backendHost; got != want {
|
||||||
|
t.Errorf("request-host: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1NoHostRewrite tests that server does not rewrite host
|
||||||
|
// header field
|
||||||
|
func TestH2H1NoHostRewrite(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--no-host-rewrite"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Add("request-host", r.Host)
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1NoHostRewrite",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.status, 200; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
if got, want := res.header.Get("request-host"), st.frontendHost; got != want {
|
||||||
|
t.Errorf("request-host: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1BadRequestCL tests that server rejects request whose
|
||||||
|
// content-length header field value does not match its request body
|
||||||
|
// size.
|
||||||
|
func TestH2H1BadRequestCL(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
// we set content-length: 1024, but the actual request body is
|
||||||
|
// 3 bytes.
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1BadRequestCL",
|
||||||
|
method: "POST",
|
||||||
|
header: []hpack.HeaderField{
|
||||||
|
pair("content-length", "1024"),
|
||||||
|
},
|
||||||
|
body: []byte("foo"),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
want := http2.ErrCodeProtocol
|
||||||
|
if res.errCode != want {
|
||||||
|
t.Errorf("res.errCode = %v; want %v", res.errCode, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1BadResponseCL tests that server returns error when
|
||||||
|
// content-length response header field value does not match its
|
||||||
|
// response body size.
|
||||||
|
func TestH2H1BadResponseCL(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// we set content-length: 1024, but only send 3 bytes.
|
||||||
|
w.Header().Add("Content-Length", "1024")
|
||||||
|
w.Write([]byte("foo"))
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1BadResponseCL",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
want := http2.ErrCodeProtocol
|
||||||
|
if res.errCode != want {
|
||||||
|
t.Errorf("res.errCode = %v; want %v", res.errCode, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1LocationRewrite tests location header field rewriting
|
||||||
|
// works.
|
||||||
|
func TestH2H1LocationRewrite(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// TODO we cannot get st.ts's port number here.. 8443
|
||||||
|
// is just a place holder. We ignore it on rewrite.
|
||||||
|
w.Header().Add("Location", "http://127.0.0.1:8443/p/q?a=b#fragment")
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1LocationRewrite",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
want := fmt.Sprintf("http://127.0.0.1:%v/p/q?a=b#fragment", serverPort)
|
||||||
|
if got := res.header.Get("Location"); got != want {
|
||||||
|
t.Errorf("Location: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1ChunkedRequestBody tests that chunked request body works.
|
||||||
|
func TestH2H1ChunkedRequestBody(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
want := "[chunked]"
|
||||||
|
if got := fmt.Sprint(r.TransferEncoding); got != want {
|
||||||
|
t.Errorf("Transfer-Encoding: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error reading r.body: %v", err)
|
||||||
|
}
|
||||||
|
want = "foo"
|
||||||
|
if got := string(body); got != want {
|
||||||
|
t.Errorf("body: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
_, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1ChunkedRequestBody",
|
||||||
|
method: "POST",
|
||||||
|
body: []byte("foo"),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1MultipleRequestCL tests that server rejects request with
|
||||||
|
// multiple Content-Length request header fields.
|
||||||
|
func TestH2H1MultipleRequestCL(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
t.Errorf("server should not forward bad request")
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1MultipleRequestCL",
|
||||||
|
header: []hpack.HeaderField{
|
||||||
|
pair("content-length", "1"),
|
||||||
|
pair("content-length", "1"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.errCode, http2.ErrCodeProtocol; got != want {
|
||||||
|
t.Errorf("res.errCode: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1InvalidRequestCL tests that server rejects request with
|
||||||
|
// Content-Length which cannot be parsed as a number.
|
||||||
|
func TestH2H1InvalidRequestCL(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
t.Errorf("server should not forward bad request")
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1InvalidRequestCL",
|
||||||
|
header: []hpack.HeaderField{
|
||||||
|
pair("content-length", ""),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.errCode, http2.ErrCodeProtocol; got != want {
|
||||||
|
t.Errorf("res.errCode: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1ConnectFailure tests that server handles the situation that
|
||||||
|
// connection attempt to HTTP/1 backend failed.
|
||||||
|
func TestH2H1ConnectFailure(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
// shutdown backend server to simulate backend connect failure
|
||||||
|
st.ts.Close()
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1ConnectFailure",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
want := 503
|
||||||
|
if got := res.status; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1InvalidMethod tests that server rejects invalid method with
|
||||||
|
// 501.
|
||||||
|
func TestH2H1InvalidMethod(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
t.Errorf("server should not forward this request")
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1InvalidMethod",
|
||||||
|
method: "get",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.status, 501; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1AssembleCookies tests that crumbled cookies in HTTP/2
|
||||||
|
// request is assembled into 1 when forwarding to HTTP/1 backend link.
|
||||||
|
func TestH2H1AssembleCookies(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if got, want := r.Header.Get("Cookie"), "alpha; bravo; charlie"; got != want {
|
||||||
|
t.Errorf("Cookie: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1AssembleCookies",
|
||||||
|
header: []hpack.HeaderField{
|
||||||
|
pair("cookie", "alpha"),
|
||||||
|
pair("cookie", "bravo"),
|
||||||
|
pair("cookie", "charlie"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.status, 200; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1TETrailers tests that server accepts TE request header
|
||||||
|
// field if it has trailers only.
|
||||||
|
func TestH2H1TETrailers(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1TETrailers",
|
||||||
|
header: []hpack.HeaderField{
|
||||||
|
pair("te", "trailers"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.status, 200; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1TEGzip tests that server resets stream if TE request header
|
||||||
|
// field contains gzip.
|
||||||
|
func TestH2H1TEGzip(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
t.Error("server should not forward bad request")
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1TEGzip",
|
||||||
|
header: []hpack.HeaderField{
|
||||||
|
pair("te", "gzip"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.errCode, http2.ErrCodeProtocol; got != want {
|
||||||
|
t.Errorf("res.errCode = %v; want %v", res.errCode, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1SNI tests server's TLS SNI extension feature. It must
|
||||||
|
// choose appropriate certificate depending on the indicated
|
||||||
|
// server_name from client.
|
||||||
|
func TestH2H1SNI(t *testing.T) {
|
||||||
|
st := newServerTesterTLSConfig([]string{"--subcert=" + testDir + "/alt-server.key:" + testDir + "/alt-server.crt"}, t, noopHandler, &tls.Config{
|
||||||
|
ServerName: "alt-domain",
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
tlsConn := st.conn.(*tls.Conn)
|
||||||
|
connState := tlsConn.ConnectionState()
|
||||||
|
cert := connState.PeerCertificates[0]
|
||||||
|
|
||||||
|
if got, want := cert.Subject.CommonName, "alt-domain"; got != want {
|
||||||
|
t.Errorf("CommonName: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1TLSXfp tests nghttpx sends x-forwarded-proto header field
|
||||||
|
// with http value since :scheme is http, even if the frontend
|
||||||
|
// connection is encrypted.
|
||||||
|
func TestH2H1TLSXfp(t *testing.T) {
|
||||||
|
st := newServerTesterTLS(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if got, want := r.Header.Get("x-forwarded-proto"), "http"; got != want {
|
||||||
|
t.Errorf("x-forwarded-proto: want %v; got %v", want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1TLSXfp",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.status, 200; got != want {
|
||||||
|
t.Errorf("res.status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1ServerPush tests server push using Link header field from
|
||||||
|
// backend server.
|
||||||
|
func TestH2H1ServerPush(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// only resources marked as rel=preload are pushed
|
||||||
|
if !strings.HasPrefix(r.URL.Path, "/css/") {
|
||||||
|
w.Header().Add("Link", "</css/main.css>; rel=preload, </foo>, </css/theme.css>; rel=preload")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1ServerPush",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.status, 200; got != want {
|
||||||
|
t.Errorf("res.status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
if got, want := len(res.pushResponse), 2; got != want {
|
||||||
|
t.Fatalf("len(res.pushResponse): %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
mainCSS := res.pushResponse[0]
|
||||||
|
if got, want := mainCSS.status, 200; got != want {
|
||||||
|
t.Errorf("mainCSS.status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
themeCSS := res.pushResponse[1]
|
||||||
|
if got, want := themeCSS.status, 200; got != want {
|
||||||
|
t.Errorf("themeCSS.status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1RequestTrailer tests request trailer part is forwarded to
|
||||||
|
// backend.
|
||||||
|
func TestH2H1RequestTrailer(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
buf := make([]byte, 4096)
|
||||||
|
for {
|
||||||
|
_, err := r.Body.Read(buf)
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("r.Body.Read() = %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if got, want := r.Trailer.Get("foo"), "bar"; got != want {
|
||||||
|
t.Errorf("r.Trailer.Get(foo): %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1RequestTrailer",
|
||||||
|
body: []byte("1"),
|
||||||
|
trailer: []hpack.HeaderField{
|
||||||
|
pair("foo", "bar"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.status, 200; got != want {
|
||||||
|
t.Errorf("res.status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1HeaderFieldBuffer tests that request with header fields
|
||||||
|
// larger than configured buffer size is rejected.
|
||||||
|
func TestH2H1HeaderFieldBuffer(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--header-field-buffer=10"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
t.Fatal("execution path should not be here")
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1HeaderFieldBuffer",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.status, 431; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1HeaderFields tests that request with header fields more
|
||||||
|
// than configured number is rejected.
|
||||||
|
func TestH2H1HeaderFields(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--max-header-fields=1"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
t.Fatal("execution path should not be here")
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1HeaderFields",
|
||||||
|
// we have at least 4 pseudo-header fields sent, and
|
||||||
|
// that ensures that buffer limit exceeds.
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.status, 431; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1Upgrade tests HTTP Upgrade to HTTP/2
|
||||||
|
func TestH2H1Upgrade(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http1(requestParam{
|
||||||
|
name: "TestH2H1Upgrade",
|
||||||
|
header: []hpack.HeaderField{
|
||||||
|
pair("Connection", "Upgrade, HTTP2-Settings"),
|
||||||
|
pair("Upgrade", "h2c"),
|
||||||
|
pair("HTTP2-Settings", "AAMAAABkAAQAAP__"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http1() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if got, want := res.status, 101; got != want {
|
||||||
|
t.Errorf("res.status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err = st.http2(requestParam{
|
||||||
|
httpUpgrade: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.status, 200; got != want {
|
||||||
|
t.Errorf("res.status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1GracefulShutdown tests graceful shutdown.
|
||||||
|
func TestH2H1GracefulShutdown(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
fmt.Fprint(st.conn, "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")
|
||||||
|
if err := st.fr.WriteSettings(); err != nil {
|
||||||
|
t.Fatalf("st.fr.WriteSettings(): %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
header := []hpack.HeaderField{
|
||||||
|
pair(":method", "GET"),
|
||||||
|
pair(":scheme", "http"),
|
||||||
|
pair(":authority", st.authority),
|
||||||
|
pair(":path", "/"),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, h := range header {
|
||||||
|
_ = st.enc.WriteField(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := st.fr.WriteHeaders(http2.HeadersFrameParam{
|
||||||
|
StreamID: 1,
|
||||||
|
EndStream: false,
|
||||||
|
EndHeaders: true,
|
||||||
|
BlockFragment: st.headerBlkBuf.Bytes(),
|
||||||
|
}); err != nil {
|
||||||
|
t.Fatalf("st.fr.WriteHeaders(): %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// send SIGQUIT signal to nghttpx to perform graceful shutdown
|
||||||
|
st.cmd.Process.Signal(syscall.SIGQUIT)
|
||||||
|
|
||||||
|
// after signal, finish request body
|
||||||
|
if err := st.fr.WriteData(1, true, nil); err != nil {
|
||||||
|
t.Fatalf("st.fr.WriteData(): %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
numGoAway := 0
|
||||||
|
|
||||||
|
for {
|
||||||
|
fr, err := st.readFrame()
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
want := 2
|
||||||
|
if got := numGoAway; got != want {
|
||||||
|
t.Fatalf("numGoAway: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Fatalf("st.readFrame(): %v", err)
|
||||||
|
}
|
||||||
|
switch f := fr.(type) {
|
||||||
|
case *http2.GoAwayFrame:
|
||||||
|
numGoAway += 1
|
||||||
|
want := http2.ErrCodeNo
|
||||||
|
if got := f.ErrCode; got != want {
|
||||||
|
t.Fatalf("f.ErrCode(%v): %v; want %v", numGoAway, got, want)
|
||||||
|
}
|
||||||
|
switch numGoAway {
|
||||||
|
case 1:
|
||||||
|
want := (uint32(1) << 31) - 1
|
||||||
|
if got := f.LastStreamID; got != want {
|
||||||
|
t.Fatalf("f.LastStreamID(%v): %v; want %v", numGoAway, got, want)
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
want := uint32(1)
|
||||||
|
if got := f.LastStreamID; got != want {
|
||||||
|
t.Fatalf("f.LastStreamID(%v): %v; want %v", numGoAway, got, want)
|
||||||
|
}
|
||||||
|
case 3:
|
||||||
|
t.Fatalf("too many GOAWAYs received")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H2MultipleResponseCL tests that server returns error if
|
||||||
|
// multiple Content-Length response header fields are received.
|
||||||
|
func TestH2H2MultipleResponseCL(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Add("content-length", "1")
|
||||||
|
w.Header().Add("content-length", "1")
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H2MultipleResponseCL",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.errCode, http2.ErrCodeInternal; got != want {
|
||||||
|
t.Errorf("res.errCode: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H2InvalidResponseCL tests that server returns error if
|
||||||
|
// Content-Length response header field value cannot be parsed as a
|
||||||
|
// number.
|
||||||
|
func TestH2H2InvalidResponseCL(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Add("content-length", "")
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H2InvalidResponseCL",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.errCode, http2.ErrCodeInternal; got != want {
|
||||||
|
t.Errorf("res.errCode: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H2ConnectFailure tests that server handles the situation that
|
||||||
|
// connection attempt to HTTP/2 backend failed.
|
||||||
|
func TestH2H2ConnectFailure(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--http2-bridge"}, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
// simulate backend connect attempt failure
|
||||||
|
st.ts.Close()
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H2ConnectFailure",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
want := 503
|
||||||
|
if got := res.status; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H2HostRewrite tests that server rewrites host header field
|
||||||
|
func TestH2H2HostRewrite(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Add("request-host", r.Host)
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H2HostRewrite",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.status, 200; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
if got, want := res.header.Get("request-host"), st.backendHost; got != want {
|
||||||
|
t.Errorf("request-host: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H2NoHostRewrite tests that server does not rewrite host
|
||||||
|
// header field
|
||||||
|
func TestH2H2NoHostRewrite(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--http2-bridge", "--no-host-rewrite"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Add("request-host", r.Host)
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H2NoHostRewrite",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.status, 200; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
if got, want := res.header.Get("request-host"), st.frontendHost; got != want {
|
||||||
|
t.Errorf("request-host: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H2TLSXfp tests nghttpx sends x-forwarded-proto header field
|
||||||
|
// with http value since :scheme is http, even if the frontend
|
||||||
|
// connection is encrypted.
|
||||||
|
func TestH2H2TLSXfp(t *testing.T) {
|
||||||
|
st := newServerTesterTLS([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if got, want := r.Header.Get("x-forwarded-proto"), "http"; got != want {
|
||||||
|
t.Errorf("x-forwarded-proto: want %v; got %v", want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H2TLSXfp",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.status, 200; got != want {
|
||||||
|
t.Errorf("res.status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
252
integration-tests/nghttpx_spdy_test.go
Normal file
252
integration-tests/nghttpx_spdy_test.go
Normal file
@@ -0,0 +1,252 @@
|
|||||||
|
package nghttp2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/bradfitz/http2/hpack"
|
||||||
|
"github.com/tatsuhiro-t/spdy"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestS3H1PlainGET tests whether simple SPDY GET request works.
|
||||||
|
func TestS3H1PlainGET(t *testing.T) {
|
||||||
|
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.spdy(requestParam{
|
||||||
|
name: "TestS3H1PlainGET",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.spdy() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
want := 200
|
||||||
|
if got := res.status; got != want {
|
||||||
|
t.Errorf("status = %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestS3H1BadRequestCL tests that server rejects request whose
|
||||||
|
// content-length header field value does not match its request body
|
||||||
|
// size.
|
||||||
|
func TestS3H1BadRequestCL(t *testing.T) {
|
||||||
|
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
// we set content-length: 1024, but the actual request body is
|
||||||
|
// 3 bytes.
|
||||||
|
res, err := st.spdy(requestParam{
|
||||||
|
name: "TestS3H1BadRequestCL",
|
||||||
|
method: "POST",
|
||||||
|
header: []hpack.HeaderField{
|
||||||
|
pair("content-length", "1024"),
|
||||||
|
},
|
||||||
|
body: []byte("foo"),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.spdy() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
want := spdy.ProtocolError
|
||||||
|
if got := res.spdyRstErrCode; got != want {
|
||||||
|
t.Errorf("res.spdyRstErrCode = %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestS3H1MultipleRequestCL tests that server rejects request with
|
||||||
|
// multiple Content-Length request header fields.
|
||||||
|
func TestS3H1MultipleRequestCL(t *testing.T) {
|
||||||
|
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
t.Errorf("server should not forward bad request")
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.spdy(requestParam{
|
||||||
|
name: "TestS3H1MultipleRequestCL",
|
||||||
|
header: []hpack.HeaderField{
|
||||||
|
pair("content-length", "1"),
|
||||||
|
pair("content-length", "1"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.spdy() = %v", err)
|
||||||
|
}
|
||||||
|
want := 400
|
||||||
|
if got := res.status; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestS3H1InvalidRequestCL tests that server rejects request with
|
||||||
|
// Content-Length which cannot be parsed as a number.
|
||||||
|
func TestS3H1InvalidRequestCL(t *testing.T) {
|
||||||
|
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
t.Errorf("server should not forward bad request")
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.spdy(requestParam{
|
||||||
|
name: "TestS3H1InvalidRequestCL",
|
||||||
|
header: []hpack.HeaderField{
|
||||||
|
pair("content-length", ""),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.spdy() = %v", err)
|
||||||
|
}
|
||||||
|
want := 400
|
||||||
|
if got := res.status; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestS3H1GenerateVia tests that server generates Via header field to and
|
||||||
|
// from backend server.
|
||||||
|
func TestS3H1GenerateVia(t *testing.T) {
|
||||||
|
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if got, want := r.Header.Get("Via"), "1.1 nghttpx"; got != want {
|
||||||
|
t.Errorf("Via: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.spdy(requestParam{
|
||||||
|
name: "TestS3H1GenerateVia",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.spdy() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.header.Get("Via"), "1.1 nghttpx"; got != want {
|
||||||
|
t.Errorf("Via: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestS3H1AppendVia tests that server adds value to existing Via
|
||||||
|
// header field to and from backend server.
|
||||||
|
func TestS3H1AppendVia(t *testing.T) {
|
||||||
|
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if got, want := r.Header.Get("Via"), "foo, 1.1 nghttpx"; got != want {
|
||||||
|
t.Errorf("Via: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
w.Header().Add("Via", "bar")
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.spdy(requestParam{
|
||||||
|
name: "TestS3H1AppendVia",
|
||||||
|
header: []hpack.HeaderField{
|
||||||
|
pair("via", "foo"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.spdy() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.header.Get("Via"), "bar, 1.1 nghttpx"; got != want {
|
||||||
|
t.Errorf("Via: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestS3H1NoVia tests that server does not add value to existing Via
|
||||||
|
// header field to and from backend server.
|
||||||
|
func TestS3H1NoVia(t *testing.T) {
|
||||||
|
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--no-via"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if got, want := r.Header.Get("Via"), "foo"; got != want {
|
||||||
|
t.Errorf("Via: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
w.Header().Add("Via", "bar")
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.spdy(requestParam{
|
||||||
|
name: "TestS3H1NoVia",
|
||||||
|
header: []hpack.HeaderField{
|
||||||
|
pair("via", "foo"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.spdy() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.header.Get("Via"), "bar"; got != want {
|
||||||
|
t.Errorf("Via: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestS3H1HeaderFieldBuffer tests that request with header fields
|
||||||
|
// larger than configured buffer size is rejected.
|
||||||
|
func TestS3H1HeaderFieldBuffer(t *testing.T) {
|
||||||
|
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--header-field-buffer=10"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
t.Fatal("execution path should not be here")
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.spdy(requestParam{
|
||||||
|
name: "TestS3H1HeaderFieldBuffer",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.spdy() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.spdyRstErrCode, spdy.InternalError; got != want {
|
||||||
|
t.Errorf("res.spdyRstErrCode: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestS3H1HeaderFields tests that request with header fields more
|
||||||
|
// than configured number is rejected.
|
||||||
|
func TestS3H1HeaderFields(t *testing.T) {
|
||||||
|
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--max-header-fields=1"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
t.Fatal("execution path should not be here")
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.spdy(requestParam{
|
||||||
|
name: "TestS3H1HeaderFields",
|
||||||
|
// we have at least 5 pseudo-header fields sent, and
|
||||||
|
// that ensures that buffer limit exceeds.
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.spdy() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.spdyRstErrCode, spdy.InternalError; got != want {
|
||||||
|
t.Errorf("res.spdyRstErrCode: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestS3H1InvalidMethod tests that server rejects invalid method with
|
||||||
|
// 501.
|
||||||
|
func TestS3H1InvalidMethod(t *testing.T) {
|
||||||
|
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
t.Errorf("server should not forward this request")
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.spdy(requestParam{
|
||||||
|
name: "TestS3H1InvalidMethod",
|
||||||
|
method: "get",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.spdy() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.status, 501; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestS3H2ConnectFailure tests that server handles the situation that
|
||||||
|
// connection attempt to HTTP/2 backend failed.
|
||||||
|
func TestS3H2ConnectFailure(t *testing.T) {
|
||||||
|
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--http2-bridge"}, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
// simulate backend connect attempt failure
|
||||||
|
st.ts.Close()
|
||||||
|
|
||||||
|
res, err := st.spdy(requestParam{
|
||||||
|
name: "TestS3H2ConnectFailure",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.spdy() = %v", err)
|
||||||
|
}
|
||||||
|
want := 503
|
||||||
|
if got := res.status; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
21
integration-tests/server.crt
Normal file
21
integration-tests/server.crt
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDhTCCAm2gAwIBAgIJAOvIx8xIxgyOMA0GCSqGSIb3DQEBCwUAMFkxCzAJBgNV
|
||||||
|
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
|
||||||
|
aWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMMCTEyNy4wLjAuMTAeFw0xNTAxMjMxMjI0
|
||||||
|
MjdaFw0yNTAxMjAxMjI0MjdaMFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21l
|
||||||
|
LVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNV
|
||||||
|
BAMMCTEyNy4wLjAuMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMuI
|
||||||
|
QZRI/iBaxPTjTWGemt8tCEfzZWxuIW3hY/gIhwJDfH2SbourBh1s9vqcqhBq5vmo
|
||||||
|
kdfVQXAnNLjIG1uhWmcHuNnKrE5hU82N6i9RsmuM5TQRvhsamHri4G+EXJMu9GqF
|
||||||
|
Mso8g7MWpRSGKf+8gfjAVNwfCHFiu8oBcMmy3l54MFHgRLSveAMhiPB0e3Xlnpr5
|
||||||
|
2bS/oGTx5ynwPgBpEn2FrpT4Z/aLCLzJ/ysgNH8BXEh7n/v7xM3vd5grqB039rd5
|
||||||
|
JoxlWvp+4XpzKp5upaqmOcVUq4pDSFUQ3w6C+v33Z3OK6Qaon7GMxLv3Us3b7PZ3
|
||||||
|
1CLoWJR2o3OSnUfO/gUCAwEAAaNQME4wHQYDVR0OBBYEFLc5JWPUUVx4GJesogMV
|
||||||
|
w2Rz0L3yMB8GA1UdIwQYMBaAFLc5JWPUUVx4GJesogMVw2Rz0L3yMAwGA1UdEwQF
|
||||||
|
MAMBAf8wDQYJKoZIhvcNAQELBQADggEBAAP/cJWpM+GEjmVYHFacKTdbXBMox2Xn
|
||||||
|
QY2NLm00WPOGvKnO7czMFfX/pEmiq71kD45rLLfbaJP205QpxqiAIvhFhuq50Co7
|
||||||
|
sTDtwcDTPLX9H7Ugjt4sTMPiwC14uVXFfoT/J46zMjXwP00qKyfszc2tkIgHfrTl
|
||||||
|
h4M1hkdfmMximir/Ii7TdYYJ3oGS8tdcYb6D4DZwAljKmxF6iUOwFCUgpTmqDBT5
|
||||||
|
irXY8D27DzuNN5Pg07rwAlwXLCzrJE10UtO4MmRVXwpzmoaRQD4/tna6bZzdetvs
|
||||||
|
gPdGP6W1o0q85gullieMJWeKyQA/wasoE7fypn4pHAdTZm/vH+v7GHg=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user