mirror of
https://github.com/nghttp2/nghttp2.git
synced 2025-12-06 18:18:52 +08:00
Compare commits
1655 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 | ||
|
|
14e56fcd81 | ||
|
|
db67412511 | ||
|
|
76800dc8e7 | ||
|
|
7d282cd0bd | ||
|
|
49b8d1d88c | ||
|
|
4d93dd9d91 | ||
|
|
be1a513c59 | ||
|
|
20900b133e | ||
|
|
279fc2ad37 | ||
|
|
0ef99b90d9 | ||
|
|
1bd8f6a0e2 | ||
|
|
f70c142e10 | ||
|
|
b3fbf047b2 | ||
|
|
0cd8da2cd9 | ||
|
|
7fa0f2763e | ||
|
|
f381b13c91 | ||
|
|
6b9382d865 | ||
|
|
2c335dbc7a | ||
|
|
b3463b20a3 | ||
|
|
15bdf048cc | ||
|
|
992ca93533 | ||
|
|
b4ed3324c0 | ||
|
|
98fd6019cf | ||
|
|
6ccae48f7c | ||
|
|
6933e0ef54 | ||
|
|
a9ecdca08a | ||
|
|
af5bedd45f | ||
|
|
7097a31968 | ||
|
|
4122920dc6 | ||
|
|
9aed11e3dc | ||
|
|
9ea4905f68 | ||
|
|
c6cfcc3c30 | ||
|
|
4bc5e55113 | ||
|
|
566a252577 | ||
|
|
27c766cb04 | ||
|
|
41dd6d0205 | ||
|
|
db071ca35c | ||
|
|
e3af9d8bd3 | ||
|
|
73955f0519 | ||
|
|
5df21e3683 | ||
|
|
a6e1a40c05 | ||
|
|
e330520341 | ||
|
|
d13ed04b17 | ||
|
|
75a23c6c7e | ||
|
|
eaca5d83b0 | ||
|
|
9c1b5e8fb1 | ||
|
|
0c31dbb5d9 | ||
|
|
8a0b11e9e1 | ||
|
|
7ca2787cc8 | ||
|
|
cfbf907418 | ||
|
|
dd02c4cd9b | ||
|
|
93ee5bdba6 | ||
|
|
f2aa6f4e2b | ||
|
|
bc8a583184 | ||
|
|
502ff24568 | ||
|
|
47692d113c | ||
|
|
19c2805d0d | ||
|
|
df078dc004 | ||
|
|
e570225e97 | ||
|
|
8fffa05513 | ||
|
|
0d4120ce2c | ||
|
|
20de432725 | ||
|
|
325bb0115e | ||
|
|
cc3b41ec96 | ||
|
|
225b90eefd | ||
|
|
3931a0b04d | ||
|
|
70c0558443 | ||
|
|
f2bfe623fc | ||
|
|
80dcb565eb | ||
|
|
6d42b6697b | ||
|
|
bcbb2e8649 | ||
|
|
ba92935f64 | ||
|
|
402c262de5 | ||
|
|
03c4092862 | ||
|
|
a225bb29df | ||
|
|
f3a76d84f1 | ||
|
|
7a09feebc3 | ||
|
|
32ddca532a | ||
|
|
df875db989 | ||
|
|
1d138accb9 | ||
|
|
7e6019aef1 | ||
|
|
e20b417b84 | ||
|
|
df56b69060 | ||
|
|
b6d0a32d0e | ||
|
|
a82956d1d6 | ||
|
|
9c0760e3c1 | ||
|
|
4e71e9e2e8 | ||
|
|
521450c7ad | ||
|
|
aa57e91e85 | ||
|
|
4023c26cf1 | ||
|
|
a0f558ee3c | ||
|
|
4f0d03b4b9 | ||
|
|
88d7abcc23 | ||
|
|
409316018d | ||
|
|
d25b9da9f6 | ||
|
|
b48ceac56c | ||
|
|
34413d8d7c | ||
|
|
b7ccca4c47 | ||
|
|
e887b2516f | ||
|
|
be0f6dcaaf | ||
|
|
937bb9f768 | ||
|
|
a11fbf6e2f | ||
|
|
9aa914c756 | ||
|
|
e20d2ba9c1 | ||
|
|
c1be28684a | ||
|
|
fd07f5e142 | ||
|
|
99ca15cae0 | ||
|
|
3651467c71 | ||
|
|
c27ec6f57b | ||
|
|
485d04851c | ||
|
|
83728219db | ||
|
|
5d0bf4cc84 | ||
|
|
446f8f13aa | ||
|
|
96bb9c2018 | ||
|
|
a9b74261b6 | ||
|
|
89c3c08590 | ||
|
|
83309b6391 | ||
|
|
23dd428d65 | ||
|
|
b305495a75 | ||
|
|
727662257c | ||
|
|
b2f88f8fe3 | ||
|
|
44ac571037 | ||
|
|
5bff48a15a | ||
|
|
e4751a798a | ||
|
|
6fc12caa6d | ||
|
|
d00d4d647d | ||
|
|
5ff73de195 | ||
|
|
901de5fbce | ||
|
|
5847a56c40 | ||
|
|
0da0140026 | ||
|
|
207b1db6af | ||
|
|
85f605f20d | ||
|
|
5cf07f5c21 | ||
|
|
340b52da84 | ||
|
|
3de678e164 | ||
|
|
0af71ee6be | ||
|
|
d8c0d87c90 | ||
|
|
9b2c24ad68 | ||
|
|
b4bb6a6101 | ||
|
|
8890e593e6 | ||
|
|
ec0a2e7cca | ||
|
|
f8471a5f45 | ||
|
|
f34cbf9b45 | ||
|
|
a23a705121 | ||
|
|
822ec75814 | ||
|
|
0209b7c083 | ||
|
|
70a8fd59b1 | ||
|
|
223242b512 | ||
|
|
42ac80d3da | ||
|
|
4c11cd0671 | ||
|
|
4c5c6749a0 | ||
|
|
dd038bf753 | ||
|
|
577512f2ca | ||
|
|
565c635e9b | ||
|
|
0b1ab90fb8 | ||
|
|
93b4d9efc3 | ||
|
|
82bc7198e6 | ||
|
|
03ed29953e | ||
|
|
a36c4c6f5f | ||
|
|
3daa6f2c30 | ||
|
|
53ee21caa9 | ||
|
|
31528b6267 | ||
|
|
ab5b81bee1 | ||
|
|
9f6bb989e3 | ||
|
|
3655090997 | ||
|
|
3cd08251ca | ||
|
|
1673ae2c99 | ||
|
|
bf48ef9bab | ||
|
|
1093b3eeab | ||
|
|
d5da7611fa | ||
|
|
9893ae81af | ||
|
|
7bfa276e96 | ||
|
|
02c5621c61 | ||
|
|
9ad2c0887e | ||
|
|
0c7e2fbec6 | ||
|
|
cfee9cab36 | ||
|
|
679a0a0fa1 | ||
|
|
d5dcbf6f3b | ||
|
|
69b9ce6b68 | ||
|
|
c81e87bf37 | ||
|
|
97533c966d | ||
|
|
d6b495f2a7 | ||
|
|
c7e9fe8154 | ||
|
|
fb62a5ed4f | ||
|
|
345121975a | ||
|
|
a5dfe24e49 | ||
|
|
273d9f4f7d | ||
|
|
500c5eea56 | ||
|
|
83a39f5b49 | ||
|
|
2fb675f13c | ||
|
|
eebd1f5492 | ||
|
|
00ead22395 | ||
|
|
2b4bd57c7f | ||
|
|
86dd1519b4 | ||
|
|
a507fc80b6 | ||
|
|
69c3920a1a | ||
|
|
bbe4f5a3d1 | ||
|
|
49a9ec2cb3 | ||
|
|
5d2390deba | ||
|
|
02c347fe6b | ||
|
|
3c056973a1 | ||
|
|
d17f35a488 | ||
|
|
1b8ad61779 | ||
|
|
ede801d099 | ||
|
|
9649b2d346 | ||
|
|
53e52194b5 | ||
|
|
0e8419ac37 | ||
|
|
da08ba5d50 | ||
|
|
30fa6d24d0 | ||
|
|
f776c50d43 | ||
|
|
7b85f6c50d | ||
|
|
21cbf417c8 | ||
|
|
ca680c16e3 | ||
|
|
bf13d91264 | ||
|
|
8aa6580d89 | ||
|
|
11fa71ba6c | ||
|
|
58afce2382 | ||
|
|
24cfb52b5a | ||
|
|
d499803221 | ||
|
|
894783f572 | ||
|
|
93ed89df5f | ||
|
|
1a2e50ca08 | ||
|
|
76703f79fa | ||
|
|
4679188069 | ||
|
|
4c3aa081a0 | ||
|
|
ab9b0538bc | ||
|
|
24edd2972d | ||
|
|
a8b7fa524f | ||
|
|
b827b99b2f | ||
|
|
88add854ff | ||
|
|
704bbbfcaa | ||
|
|
e217e789de | ||
|
|
d4d56e1846 | ||
|
|
8883df2fd6 | ||
|
|
d496c42dc9 | ||
|
|
8433a75a6b | ||
|
|
76b3ba2832 | ||
|
|
05f982dcfb | ||
|
|
cc24b9aaf0 | ||
|
|
e6695d9ba7 | ||
|
|
9fb2bc8468 | ||
|
|
6ccf06c6da | ||
|
|
49e3fd6862 | ||
|
|
9c1a956e47 | ||
|
|
86b089f957 | ||
|
|
3f212a60a5 | ||
|
|
5c61917007 | ||
|
|
8736f61fbd | ||
|
|
04e94824a0 | ||
|
|
ec93c9f55f | ||
|
|
6c71889552 | ||
|
|
4bbb4172aa | ||
|
|
9ccf4c037d | ||
|
|
1f356391f1 | ||
|
|
3c603ec4ae | ||
|
|
d36bea8554 | ||
|
|
16101b8b3f | ||
|
|
455d911f61 | ||
|
|
2fb750f2e3 | ||
|
|
7c781bcd1a | ||
|
|
a234166fc4 | ||
|
|
ecb7e7db74 | ||
|
|
99ceb7df33 | ||
|
|
2e0775d506 | ||
|
|
ac28cd7efa | ||
|
|
a5ec5c1a1d | ||
|
|
7952029752 | ||
|
|
2e083352d5 | ||
|
|
d848b9815a | ||
|
|
48734b6d05 | ||
|
|
8838f666cb | ||
|
|
d8d14a3fc9 | ||
|
|
6e027ad830 | ||
|
|
9d78167297 | ||
|
|
c13329b328 | ||
|
|
b8a2bf2675 | ||
|
|
98be65a1eb | ||
|
|
742b28833a | ||
|
|
77374ac6e2 | ||
|
|
c4be7d48a0 | ||
|
|
0752ce6701 | ||
|
|
8d5422c9bb | ||
|
|
04b5d1679f | ||
|
|
15055c11f9 | ||
|
|
c859fb8f7c | ||
|
|
dd1850aed0 | ||
|
|
079db14d45 | ||
|
|
9f17bee51d | ||
|
|
e904842504 | ||
|
|
4f815521ae | ||
|
|
78df530b90 | ||
|
|
e147c14186 | ||
|
|
06453fb15e | ||
|
|
af5fd2019d | ||
|
|
744ec4dba1 | ||
|
|
38bfbffb1b | ||
|
|
63398f30dd | ||
|
|
961dcf614a | ||
|
|
5b572d8d59 | ||
|
|
8d29710393 | ||
|
|
e839ea8596 | ||
|
|
57e9b94aaa | ||
|
|
9de9b6ebd6 | ||
|
|
44310c6de5 | ||
|
|
139c3b508a | ||
|
|
8207dd33b7 | ||
|
|
d99e1135c8 | ||
|
|
db3b07735b | ||
|
|
0a2149f322 | ||
|
|
da85d2eecc | ||
|
|
cc5802ec05 | ||
|
|
67b13ad9ff | ||
|
|
8dc47c6750 | ||
|
|
39498beffd | ||
|
|
5c305acb97 | ||
|
|
61053653df | ||
|
|
62423f5949 | ||
|
|
55c697e9f4 | ||
|
|
bb47484667 | ||
|
|
c4d2639ed8 | ||
|
|
8f1249ab67 | ||
|
|
35ffeb5ff4 | ||
|
|
25326b40db | ||
|
|
25e18174dd | ||
|
|
a072d719b3 | ||
|
|
d0d0488301 | ||
|
|
0d1ebd73de | ||
|
|
d3ca003346 | ||
|
|
9cbedb16d0 | ||
|
|
9f1c819242 | ||
|
|
a02624c9ed | ||
|
|
933e24d412 | ||
|
|
fe2843f9d6 | ||
|
|
afdc61a253 | ||
|
|
367e764ca3 | ||
|
|
9841f778a9 | ||
|
|
bf4042ce98 | ||
|
|
2e6d5e9bd0 | ||
|
|
0ce848a611 | ||
|
|
57230b4029 | ||
|
|
3111138ca3 | ||
|
|
0f4d01c25c | ||
|
|
f8b872096e | ||
|
|
e18c439a73 | ||
|
|
545732fed8 | ||
|
|
4b6f124b7e | ||
|
|
119fb05cc2 | ||
|
|
e08df2840c | ||
|
|
1ce00f455c | ||
|
|
9cddb05f54 | ||
|
|
93b3a44fb5 | ||
|
|
acb3d4dcfc | ||
|
|
797edae4d4 | ||
|
|
e5abc475f1 | ||
|
|
593485c652 | ||
|
|
6da044cbb5 | ||
|
|
ed38dbf67a | ||
|
|
499af94840 | ||
|
|
2f91a7f603 | ||
|
|
e517ff6534 | ||
|
|
1039dc59ea | ||
|
|
a7501609e8 | ||
|
|
8235bb136b | ||
|
|
bd06f2cec3 | ||
|
|
48c8c89a3f | ||
|
|
8167968efb | ||
|
|
4e81a34146 | ||
|
|
9fec34968b | ||
|
|
a3334bb21c | ||
|
|
d318e6a62e | ||
|
|
ff3a4a770f | ||
|
|
305efe4181 | ||
|
|
c1be7c734f | ||
|
|
303f0f3fcd | ||
|
|
ca87b45fe4 | ||
|
|
31d99b3664 | ||
|
|
3312df42d2 | ||
|
|
68cd12ac29 | ||
|
|
451c7d957d | ||
|
|
15a7b0414c | ||
|
|
52ff158f34 | ||
|
|
479e15469c | ||
|
|
16fef227e8 | ||
|
|
55c338d7af | ||
|
|
4cbfe5d3d1 | ||
|
|
ad60a18fb9 | ||
|
|
5aba6e6d1b | ||
|
|
31de732e3b | ||
|
|
1caec7cb16 | ||
|
|
0cd26d6f3b | ||
|
|
2bfa772472 | ||
|
|
133c3d66be | ||
|
|
266a15ccd3 | ||
|
|
4ff522764c | ||
|
|
4ab594b144 | ||
|
|
3f5e359ef4 | ||
|
|
a344a8f566 | ||
|
|
8acd67b3fa | ||
|
|
323c3b4225 | ||
|
|
ae74a18624 | ||
|
|
85a43cfa66 | ||
|
|
4fdc274a42 | ||
|
|
2d18a07364 | ||
|
|
6cb971ee7b | ||
|
|
cb132338df | ||
|
|
87360b4f7d | ||
|
|
8e0fcd3922 | ||
|
|
9294622519 | ||
|
|
b78a51da0e | ||
|
|
975524a125 | ||
|
|
817e1ce2a7 | ||
|
|
62900cd85b | ||
|
|
174e410b93 | ||
|
|
a5af621947 | ||
|
|
b53ef8de8f | ||
|
|
9a3cdeb7e6 | ||
|
|
c9b6371977 | ||
|
|
7f6ddd0f2e | ||
|
|
94b9c3771d | ||
|
|
86956db27f | ||
|
|
5383db467e | ||
|
|
1cbb5a3d8f | ||
|
|
6513711943 | ||
|
|
f85a213fb1 | ||
|
|
041cec2d97 | ||
|
|
25b58d5e22 | ||
|
|
dd006f8a79 | ||
|
|
1ee847c21a | ||
|
|
1af9a9cee0 | ||
|
|
6a2950aef0 | ||
|
|
a15d4fc306 | ||
|
|
9b174bf5c9 | ||
|
|
6e316e269f | ||
|
|
f0f5342cd4 | ||
|
|
21c4931197 | ||
|
|
24762db8f5 | ||
|
|
492b42e6e9 | ||
|
|
d6b5824c9c | ||
|
|
c204861dec | ||
|
|
7b0ed5d9bd | ||
|
|
5f5b5378c9 | ||
|
|
cfcecca751 | ||
|
|
2878e1e258 | ||
|
|
4596f73ee0 | ||
|
|
caf3e1c035 | ||
|
|
dacc9b2f1c | ||
|
|
de14c02227 | ||
|
|
e665123ebe | ||
|
|
db8af31e2b | ||
|
|
0fd5b2aa32 | ||
|
|
1f58be423d | ||
|
|
14b818efc8 | ||
|
|
19ed13c753 | ||
|
|
317b8baa4f | ||
|
|
d4602a0991 | ||
|
|
3daeadcb07 | ||
|
|
19729962a3 | ||
|
|
bfaab30733 | ||
|
|
c46d3dafc6 | ||
|
|
70c86979e8 | ||
|
|
d402ba6fa2 | ||
|
|
458ccb3681 | ||
|
|
3db8935e20 | ||
|
|
b8ed74c1ec | ||
|
|
fcec996925 | ||
|
|
7cb28e88e2 | ||
|
|
aea4001d73 | ||
|
|
eb0a894ede | ||
|
|
6b08534ffc | ||
|
|
1390378341 | ||
|
|
7146954de2 | ||
|
|
75b9be2d5a | ||
|
|
99aaaccf03 | ||
|
|
7e217511bf | ||
|
|
8c67bbe3a8 | ||
|
|
cc250386df | ||
|
|
3b7c733246 | ||
|
|
7e5567341f | ||
|
|
4f7223e89f | ||
|
|
88b69bb669 | ||
|
|
7a797b2c11 | ||
|
|
832f2fc00f | ||
|
|
d113055899 | ||
|
|
53e75ff0d0 | ||
|
|
3921af434a | ||
|
|
5e6a2fa256 | ||
|
|
86ab9f33de | ||
|
|
d844b0acd0 | ||
|
|
d733c87567 | ||
|
|
589d3e71a3 | ||
|
|
db354b228a | ||
|
|
1fa5852f8f | ||
|
|
ebf0e4d787 | ||
|
|
9677788317 | ||
|
|
78a55935ac | ||
|
|
2aa84019c7 | ||
|
|
6d942dc0a6 | ||
|
|
d408ee1783 | ||
|
|
672ad82849 | ||
|
|
896717f5d4 | ||
|
|
eba2825286 | ||
|
|
a4a06947a5 | ||
|
|
e54e86375b |
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
|
||||
...
|
||||
|
||||
37
.gitignore
vendored
37
.gitignore
vendored
@@ -1,14 +1,18 @@
|
||||
# emacs backup file
|
||||
*~
|
||||
*.o
|
||||
*.lo
|
||||
|
||||
# autotools
|
||||
*.la
|
||||
depcomp
|
||||
*.lo
|
||||
*.m4
|
||||
*.o
|
||||
.deps/
|
||||
.libs/
|
||||
INSTALL
|
||||
Makefile
|
||||
Makefile.in
|
||||
libtool
|
||||
missing
|
||||
autom4te.cache/
|
||||
compile
|
||||
config.guess
|
||||
config.h
|
||||
config.h.in
|
||||
@@ -16,23 +20,14 @@ config.log
|
||||
config.status
|
||||
config.sub
|
||||
configure
|
||||
depcomp
|
||||
install-sh
|
||||
.deps/
|
||||
.libs
|
||||
lib/includes/nghttp2/nghttp2ver.h
|
||||
lib/libnghttp2.pc
|
||||
libtool
|
||||
ltmain.sh
|
||||
missing
|
||||
stamp-h1
|
||||
.deps/
|
||||
INSTALL
|
||||
.DS_STORE
|
||||
compile
|
||||
test-driver
|
||||
.dirstamp
|
||||
doc/index.rst
|
||||
doc/nghttp2.h.rst
|
||||
doc/nghttp2ver.h.rst
|
||||
doc/package_README.rst
|
||||
doc/tutorial-client.rst
|
||||
doc/tutorial-server.rst
|
||||
doc/nghttpx-howto.rst
|
||||
|
||||
# test logs generated by `make check`
|
||||
*.log
|
||||
*.trs
|
||||
|
||||
37
.travis.yml
Normal file
37
.travis.yml
Normal file
@@ -0,0 +1,37 @@
|
||||
language: cpp
|
||||
compiler:
|
||||
- clang
|
||||
- gcc
|
||||
sudo: false
|
||||
addons:
|
||||
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:
|
||||
- $CC --version
|
||||
- if [ "$CXX" = "g++" ]; then export CXX="g++-4.9" CC="gcc-4.9"; fi
|
||||
- $CC --version
|
||||
before_script:
|
||||
- autoreconf -i
|
||||
- automake
|
||||
- autoconf
|
||||
- ./configure --enable-werror
|
||||
script:
|
||||
- make
|
||||
- make check
|
||||
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!
|
||||
2
COPYING
2
COPYING
@@ -1,6 +1,6 @@
|
||||
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
|
||||
a copy of this software and associated documentation files (the
|
||||
|
||||
128
Dockerfile.android
Normal file
128
Dockerfile.android
Normal file
@@ -0,0 +1,128 @@
|
||||
# vim: ft=dockerfile:
|
||||
# Dockerfile to build nghttp2 android binary
|
||||
#
|
||||
# $ sudo docker build -t nghttp2-android - < Dockerfile.android
|
||||
#
|
||||
# After successful build, android binaries are located under
|
||||
# /root/build/nghttp2. You can copy the binary using docker cp. For
|
||||
# example, to copy nghttpx binary to host file system location
|
||||
# /path/to/dest, do this:
|
||||
#
|
||||
# $ sudo docker run -v /path/to/dest:/out nghttp2-android cp /root/build/nghttp2/src/nghttpx /out
|
||||
|
||||
FROM ubuntu:vivid
|
||||
|
||||
MAINTAINER Tatsuhiro Tsujikawa
|
||||
|
||||
ENV ANDROID_HOME /root/android
|
||||
ENV PREFIX $ANDROID_HOME/usr/local
|
||||
ENV TOOLCHAIN $ANDROID_HOME/toolchain
|
||||
ENV PATH $TOOLCHAIN/bin:$PATH
|
||||
|
||||
# It would be better to use nearest ubuntu archive mirror for faster
|
||||
# downloads.
|
||||
# RUN sed -ie 's/archive\.ubuntu/jp.archive.ubuntu/g' /etc/apt/sources.list
|
||||
|
||||
RUN apt-get update
|
||||
# genisoimage, libc6-i386 and lib32stdc++6 are required to decompress ndk.
|
||||
RUN apt-get install -y make binutils autoconf automake autotools-dev libtool \
|
||||
pkg-config git curl dpkg-dev libxml2-dev \
|
||||
genisoimage libc6-i386 lib32stdc++6
|
||||
|
||||
WORKDIR /root/build
|
||||
RUN curl -L -O http://dl.google.com/android/ndk/android-ndk-r10d-linux-x86_64.bin && \
|
||||
chmod a+x android-ndk-r10d-linux-x86_64.bin && \
|
||||
./android-ndk-r10d-linux-x86_64.bin && \
|
||||
rm android-ndk-r10d-linux-x86_64.bin
|
||||
|
||||
WORKDIR /root/build/android-ndk-r10d
|
||||
RUN /bin/bash build/tools/make-standalone-toolchain.sh \
|
||||
--install-dir=$ANDROID_HOME/toolchain \
|
||||
--toolchain=arm-linux-androideabi-4.9 --llvm-version=3.5 \
|
||||
--system=linux-x86_64
|
||||
|
||||
WORKDIR /root/build
|
||||
RUN git clone https://github.com/tatsuhiro-t/spdylay
|
||||
WORKDIR /root/build/spdylay
|
||||
RUN autoreconf -i && \
|
||||
./configure \
|
||||
--disable-shared \
|
||||
--host=arm-linux-androideabi \
|
||||
--build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \
|
||||
--prefix=$PREFIX \
|
||||
--without-libxml2 \
|
||||
--disable-src \
|
||||
--disable-examples \
|
||||
CPPFLAGS="-I$PREFIX/include" \
|
||||
PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \
|
||||
LDFLAGS="-L$PREFIX/lib" && \
|
||||
make install
|
||||
|
||||
WORKDIR /root/build
|
||||
RUN curl -L -O https://www.openssl.org/source/openssl-1.0.2a.tar.gz && \
|
||||
tar xf openssl-1.0.2a.tar.gz && \
|
||||
rm openssl-1.0.2a.tar.gz
|
||||
|
||||
WORKDIR /root/build/openssl-1.0.2a
|
||||
RUN export CROSS_COMPILE=$TOOLCHAIN/bin/arm-linux-androideabi- && \
|
||||
./Configure --prefix=$PREFIX android && \
|
||||
make && make install_sw
|
||||
|
||||
WORKDIR /root/build
|
||||
RUN curl -L -O http://dist.schmorp.de/libev/libev-4.19.tar.gz && \
|
||||
curl -L -O https://gist.github.com/tatsuhiro-t/48c45f08950f587180ed/raw/80a8f003b5d1091eae497c5995bbaa68096e739b/libev-4.19-android.patch && \
|
||||
tar xf libev-4.19.tar.gz && \
|
||||
rm libev-4.19.tar.gz
|
||||
|
||||
WORKDIR /root/build/libev-4.19
|
||||
RUN patch -p1 < ../libev-4.19-android.patch && \
|
||||
./configure \
|
||||
--host=arm-linux-androideabi \
|
||||
--build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \
|
||||
--prefix=$PREFIX \
|
||||
--disable-shared \
|
||||
--enable-static \
|
||||
CPPFLAGS=-I$PREFIX/include \
|
||||
LDFLAGS=-L$PREFIX/lib && \
|
||||
make install
|
||||
|
||||
WORKDIR /root/build
|
||||
RUN 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
|
||||
RUN git clone https://github.com/tatsuhiro-t/nghttp2
|
||||
WORKDIR /root/build/nghttp2
|
||||
RUN autoreconf -i && \
|
||||
./configure \
|
||||
--disable-shared \
|
||||
--host=arm-linux-androideabi \
|
||||
--build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \
|
||||
--with-xml-prefix="$PREFIX" \
|
||||
--without-libxml2 \
|
||||
--disable-python-bindings \
|
||||
--disable-examples \
|
||||
--disable-threads \
|
||||
LIBSPDYLAY_CFLAGS=-I$PREFIX/usr/local/include \
|
||||
LIBSPDYLAY_LIBS="-L$PREFIX/usr/local/lib -lspdylay" \
|
||||
CPPFLAGS="-fPIE -I$PREFIX/include" \
|
||||
CXXFLAGS="-fno-strict-aliasing" \
|
||||
PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \
|
||||
LDFLAGS="-fPIE -pie -L$PREFIX/lib" && \
|
||||
make && \
|
||||
arm-linux-androideabi-strip src/nghttpx src/nghttpd src/nghttp
|
||||
18
Makefile.am
18
Makefile.am
@@ -20,10 +20,24 @@
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
SUBDIRS = lib third-party src examples python tests doc
|
||||
SUBDIRS = lib third-party src examples python tests integration-tests \
|
||||
doc contrib script
|
||||
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
dist_doc_DATA = README.rst
|
||||
|
||||
EXTRA_DIST = nghttpx.conf.sample proxy.pac.sample android-config android-make
|
||||
EXTRA_DIST = nghttpx.conf.sample proxy.pac.sample android-config android-make \
|
||||
Dockerfile.android
|
||||
|
||||
.PHONY: clang-format
|
||||
|
||||
# Format source files using clang-format. Don't format source files
|
||||
# under third-party directory since we are not responsible for thier
|
||||
# coding style.
|
||||
clang-format:
|
||||
CLANGFORMAT=`git config --get clangformat.binary`; \
|
||||
test -z $${CLANGFORMAT} && CLANGFORMAT="clang-format"; \
|
||||
$${CLANGFORMAT} -i lib/*.{c,h} lib/includes/nghttp2/*.h \
|
||||
src/*.{c,cc,h} src/includes/nghttp2/*.h examples/*.{c,cc} \
|
||||
tests/*.{c,h}
|
||||
|
||||
982
README.rst
982
README.rst
File diff suppressed because it is too large
Load Diff
@@ -36,6 +36,12 @@ PATH=$TOOLCHAIN/bin:$PATH
|
||||
--host=arm-linux-androideabi \
|
||||
--build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \
|
||||
--with-xml-prefix="$PREFIX" \
|
||||
CPPFLAGS="-I$PREFIX/include" \
|
||||
--without-libxml2 \
|
||||
--disable-python-bindings \
|
||||
--disable-examples \
|
||||
--enable-werror \
|
||||
CC=clang \
|
||||
CXX=clang++ \
|
||||
CPPFLAGS="-fPIE -I$PREFIX/include" \
|
||||
PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \
|
||||
LDFLAGS="-L$PREFIX/lib"
|
||||
LDFLAGS="-fPIE -pie -L$PREFIX/lib"
|
||||
|
||||
408
configure.ac
408
configure.ac
@@ -1,6 +1,6 @@
|
||||
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 a copy of this software and associated documentation files (the
|
||||
@@ -20,14 +20,35 @@ dnl NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
dnl LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
dnl OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
dnl WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
dnl Do not change user variables!
|
||||
dnl http://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html
|
||||
|
||||
AC_PREREQ(2.61)
|
||||
AC_INIT([nghttp2], [0.4.0], [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_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 http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
||||
AC_SUBST(LT_CURRENT, 3)
|
||||
AC_SUBST(LT_REVISION, 0)
|
||||
AC_SUBST(LT_CURRENT, 14)
|
||||
AC_SUBST(LT_REVISION, 4)
|
||||
AC_SUBST(LT_AGE, 0)
|
||||
|
||||
major=`echo $PACKAGE_VERSION |cut -d. -f1 | 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_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
|
||||
AC_ARG_ENABLE([werror],
|
||||
[AS_HELP_STRING([--enable-werror],
|
||||
@@ -76,6 +85,11 @@ AC_ARG_ENABLE([hpack-tools],
|
||||
[Build HPACK tools [default=check]])],
|
||||
[request_hpack_tools=$enableval], [request_hpack_tools=check])
|
||||
|
||||
AC_ARG_ENABLE([asio-lib],
|
||||
[AS_HELP_STRING([--enable-asio-lib],
|
||||
[Build C++ libnghttp2_asio library [default=no]])],
|
||||
[request_asio_lib=$enableval], [request_asio_lib=no])
|
||||
|
||||
AC_ARG_ENABLE([examples],
|
||||
[AS_HELP_STRING([--enable-examples],
|
||||
[Build examples [default=check]])],
|
||||
@@ -87,9 +101,9 @@ AC_ARG_ENABLE([python-bindings],
|
||||
[request_python_bindings=$enableval], [request_python_bindings=check])
|
||||
|
||||
AC_ARG_ENABLE([failmalloc],
|
||||
[AS_HELP_STRING([--enable-failmalloc],
|
||||
[Build failmalloc test program [default=no]])],
|
||||
[request_failmalloc=$enableval], [request_failmalloc=no])
|
||||
[AS_HELP_STRING([--disable-failmalloc],
|
||||
[Do not build failmalloc test program])],
|
||||
[request_failmalloc=$enableval], [request_failmalloc=yes])
|
||||
|
||||
AC_ARG_WITH([libxml2],
|
||||
[AS_HELP_STRING([--with-libxml2],
|
||||
@@ -117,14 +131,17 @@ AC_ARG_VAR([CYTHON], [the Cython executable])
|
||||
dnl Checks for programs
|
||||
AC_PROG_CC
|
||||
AC_PROG_CXX
|
||||
AC_PROG_CPP
|
||||
AC_PROG_INSTALL
|
||||
AC_PROG_LN_S
|
||||
AC_PROG_MAKE_SET
|
||||
AM_PROG_CC_C_O
|
||||
AC_PROG_MKDIR_P
|
||||
|
||||
PKG_PROG_PKG_CONFIG([0.20])
|
||||
|
||||
AM_PATH_PYTHON([2.7],, [:])
|
||||
|
||||
if [test "x$request_python_bindings" != "xno"]; then
|
||||
AM_PATH_PYTHON([2.7],, [:])
|
||||
AX_PYTHON_DEVEL([>= '2.7'])
|
||||
fi
|
||||
|
||||
@@ -135,14 +152,29 @@ else
|
||||
AC_SUBST([CYTHON])
|
||||
fi
|
||||
|
||||
#
|
||||
# If we're running GCC or clang define _U_ to be "__attribute__((unused))"
|
||||
# so we can use _U_ to flag unused function parameters and not get warnings
|
||||
# about them. Otherwise, define _U_ to be an empty string so that _U_ used
|
||||
# to flag an unused function parameters will compile with other compilers.
|
||||
#
|
||||
# XXX - similar hints for other compilers?
|
||||
#
|
||||
if test "x$GCC" = "xyes" -o "x$CC" = "xclang" ; then
|
||||
AC_DEFINE([_U_], [__attribute__((unused))], [Hint to the compiler that a function parameters is not used])
|
||||
else
|
||||
AC_DEFINE([_U_], , [Hint to the compiler that a function parameters is not used])
|
||||
fi
|
||||
|
||||
AX_CXX_COMPILE_STDCXX_11([noext], [optional])
|
||||
|
||||
AC_LANG_PUSH(C++)
|
||||
|
||||
# Check that std::chrono::steady_clock is available. In particular,
|
||||
# gcc 4.6 does not have one, but has monotonic_clock which is the old
|
||||
# name existed in the pre-standard draft. If steady_clock is not
|
||||
# available, don't define HAVE_STEADY_CLOCK and replace steady_clock
|
||||
# with monotonic_clock.
|
||||
AC_LANG_PUSH(C++)
|
||||
AC_MSG_CHECKING([whether std::chrono::steady_clock is available])
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
|
||||
[[
|
||||
@@ -155,51 +187,67 @@ auto tp = std::chrono::steady_clock::now();
|
||||
[Define to 1 if you have the `std::chrono::steady_clock`.])
|
||||
AC_MSG_RESULT([yes])],
|
||||
[AC_MSG_RESULT([no])])
|
||||
|
||||
# Check that std::future is available.
|
||||
AC_MSG_CHECKING([whether std::future is available])
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
|
||||
[[
|
||||
#include <vector>
|
||||
#include <future>
|
||||
]],
|
||||
[[
|
||||
std::vector<std::future<int>> v;
|
||||
]])],
|
||||
[AC_DEFINE([HAVE_STD_FUTURE], [1],
|
||||
[Define to 1 if you have the `std::future`.])
|
||||
have_std_future=yes
|
||||
AC_MSG_RESULT([yes])],
|
||||
[have_std_future=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()
|
||||
|
||||
# Checks for libraries.
|
||||
|
||||
# Additional libraries required for tests.
|
||||
TESTS_LIBS=
|
||||
TESTLDADD=
|
||||
|
||||
# Additional libraries required for programs under src directory.
|
||||
SRC_LIBS=
|
||||
|
||||
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
|
||||
APPLDFLAGS=
|
||||
|
||||
case "$host" in
|
||||
*android*)
|
||||
android_build=yes
|
||||
# android does not need -pthread, but needs followng 2 libs for C++
|
||||
SRC_LIBS="$SRC_LIBS -lstdc++ -lsupc++"
|
||||
# android does not need -pthread, but needs followng 3 libs for C++
|
||||
APPLDFLAGS="$APPLDFLAGS -lstdc++ -latomic -lsupc++"
|
||||
;;
|
||||
*)
|
||||
SRC_LIBS="$SRC_LIBS -pthread"
|
||||
PTHREAD_LDFLAGS="-pthread"
|
||||
APPLDFLAGS="$APPLDFLAGS $PTHREAD_LDFLAGS"
|
||||
;;
|
||||
esac
|
||||
|
||||
# zlib
|
||||
if test "x$android_build" = "xyes"; then
|
||||
# Use zlib provided by NDK
|
||||
SRC_LIBS="-lz $SRC_LIBS"
|
||||
else
|
||||
PKG_CHECK_MODULES([ZLIB], [zlib >= 1.2.3], [have_zlib=yes], [have_zlib=no])
|
||||
PKG_CHECK_MODULES([ZLIB], [zlib >= 1.2.3], [have_zlib=yes], [have_zlib=no])
|
||||
|
||||
if test "x${have_zlib}" = "xno"; then
|
||||
AC_MSG_NOTICE($ZLIB_PKG_ERRORS)
|
||||
fi
|
||||
if test "x${have_zlib}" = "xno"; then
|
||||
AC_MSG_NOTICE($ZLIB_PKG_ERRORS)
|
||||
fi
|
||||
|
||||
# cunit
|
||||
@@ -231,6 +279,22 @@ fi
|
||||
|
||||
AM_CONDITIONAL([HAVE_CUNIT], [ test "x${have_cunit}" = "xyes" ])
|
||||
|
||||
# libev (for src)
|
||||
# libev does not have pkg-config file. Check it in an old way.
|
||||
LIBS_OLD=$LIBS
|
||||
# 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)
|
||||
PKG_CHECK_MODULES([OPENSSL], [openssl >= 1.0.1],
|
||||
[have_openssl=yes], [have_openssl=no])
|
||||
@@ -238,7 +302,7 @@ if test "x${have_openssl}" = "xno"; then
|
||||
AC_MSG_NOTICE($OPENSSL_PKG_ERRORS)
|
||||
fi
|
||||
|
||||
# libevent_openssl (for src)
|
||||
# libevent_openssl (for examples)
|
||||
# 2.0.8 is required because we use evconnlistener_set_error_cb()
|
||||
PKG_CHECK_MODULES([LIBEVENT_OPENSSL], [libevent_openssl >= 2.0.8],
|
||||
[have_libevent_openssl=yes], [have_libevent_openssl=no])
|
||||
@@ -246,10 +310,13 @@ if test "x${have_libevent_openssl}" = "xno"; then
|
||||
AC_MSG_NOTICE($LIBEVENT_OPENSSL_PKG_ERRORS)
|
||||
fi
|
||||
|
||||
# jansson (for hdtest/deflatehd and hdtest/inflatehd)
|
||||
# jansson (for src/nghttp, src/deflatehd and src/inflatehd)
|
||||
PKG_CHECK_MODULES([JANSSON], [jansson >= 2.5],
|
||||
[have_jansson=yes], [have_jansson=no])
|
||||
if test "x${have_jansson}" == "xno"; then
|
||||
if test "x${have_jansson}" = "xyes"; then
|
||||
AC_DEFINE([HAVE_JANSSON], [1],
|
||||
[Define to 1 if you have `libjansson` library.])
|
||||
else
|
||||
AC_MSG_NOTICE($JANSSON_PKG_ERRORS)
|
||||
fi
|
||||
|
||||
@@ -273,10 +340,26 @@ AM_CONDITIONAL([HAVE_LIBXML2], [ test "x${have_libxml2}" = "xyes" ])
|
||||
have_jemalloc=no
|
||||
if test "x${request_jemalloc}" != "xno"; then
|
||||
LIBS_OLD=$LIBS
|
||||
AC_SEARCH_LIBS([malloc_stats_print], [jemalloc], [have_jemalloc=yes])
|
||||
AC_SEARCH_LIBS([malloc_stats_print], [jemalloc], [have_jemalloc=yes], [],
|
||||
[$PTHREAD_LDFLAGS])
|
||||
LIBS=$LIBS_OLD
|
||||
|
||||
if test "x${have_jemalloc}" = "xyes"; then
|
||||
JEMALLOC_LIBS="-ljemalloc"
|
||||
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" &&
|
||||
test "x${jemalloc_libs}" != "xnone required"; then
|
||||
JEMALLOC_LIBS=${jemalloc_libs}
|
||||
AC_SUBST([JEMALLOC_LIBS])
|
||||
fi
|
||||
fi
|
||||
@@ -289,7 +372,7 @@ fi
|
||||
# spdylay (for src/nghttpx and src/h2load)
|
||||
have_spdylay=no
|
||||
if test "x${request_spdylay}" != "xno"; then
|
||||
PKG_CHECK_MODULES([LIBSPDYLAY], [libspdylay >= 1.2.3],
|
||||
PKG_CHECK_MODULES([LIBSPDYLAY], [libspdylay >= 1.3.2],
|
||||
[have_spdylay=yes], [have_spdylay=no])
|
||||
if test "x${have_spdylay}" = "xyes"; then
|
||||
AC_DEFINE([HAVE_SPDYLAY], [1], [Define to 1 if you have `spdylay` library.])
|
||||
@@ -306,13 +389,32 @@ fi
|
||||
|
||||
AM_CONDITIONAL([HAVE_SPDYLAY], [ test "x${have_spdylay}" = "xyes" ])
|
||||
|
||||
# Check Boost Asio library
|
||||
have_asio_lib=no
|
||||
|
||||
if test "x${request_asio_lib}" = "xyes"; then
|
||||
AX_BOOST_BASE([1.54.0], [have_boost_base=yes], [have_boost_base=no])
|
||||
|
||||
if test "x${have_boost_base}" = "xyes"; then
|
||||
AX_BOOST_ASIO()
|
||||
AX_BOOST_SYSTEM()
|
||||
AX_BOOST_THREAD()
|
||||
|
||||
if test "x${ax_cv_boost_asio}" = "xyes" &&
|
||||
test "x${ax_cv_boost_system}" = "xyes" &&
|
||||
test "x${ax_cv_boost_thread}" = "xyes"; then
|
||||
have_asio_lib=yes
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# The nghttp, nghttpd and nghttpx under src depend on zlib, OpenSSL
|
||||
# and libevent_openssl
|
||||
# and libev
|
||||
enable_app=no
|
||||
if test "x${request_app}" != "xno" &&
|
||||
test "x${have_zlib}" = "xyes" &&
|
||||
test "x${have_openssl}" = "xyes" &&
|
||||
test "x${have_libevent_openssl}" = "xyes"; then
|
||||
test "x${have_libev}" = "xyes"; then
|
||||
enable_app=yes
|
||||
fi
|
||||
|
||||
@@ -337,6 +439,16 @@ fi
|
||||
|
||||
AM_CONDITIONAL([ENABLE_HPACK_TOOLS], [ test "x${enable_hpack_tools}" = "xyes" ])
|
||||
|
||||
# C++ library libnghttp2_asio
|
||||
|
||||
enable_asio_lib=no
|
||||
if test "x${request_asio_lib}" != "xno" &&
|
||||
test "x${have_asio_lib}" = "xyes"; then
|
||||
enable_asio_lib=yes
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL([ENABLE_ASIO_LIB], [ test "x${enable_asio_lib}" = "xyes" ])
|
||||
|
||||
# The example programs depend on OpenSSL and libevent_openssl
|
||||
enable_examples=no
|
||||
if test "x${request_examples}" != "xno" &&
|
||||
@@ -352,6 +464,17 @@ fi
|
||||
|
||||
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
|
||||
enable_python_bindings=no
|
||||
if test "x${request_python_bindings}" != "xno" &&
|
||||
@@ -374,29 +497,34 @@ AM_CONDITIONAL([ENABLE_PYTHON_BINDINGS],
|
||||
AM_CONDITIONAL([HAVE_CYTHON], [test "x${CYTHON}" != "x"])
|
||||
|
||||
# failmalloc tests
|
||||
enable_failmalloc=no
|
||||
if test "x${request_failmalloc}" = "xyes"; then
|
||||
enable_failmalloc=yes
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL([ENABLE_FAILMALLOC], [ test "x${enable_failmalloc}" = "xyes" ])
|
||||
|
||||
# Checks for header files.
|
||||
AC_HEADER_ASSERT
|
||||
AC_CHECK_HEADERS([ \
|
||||
arpa/inet.h \
|
||||
fcntl.h \
|
||||
inttypes.h \
|
||||
limits.h \
|
||||
netdb.h \
|
||||
netinet/in.h \
|
||||
pwd.h \
|
||||
stddef.h \
|
||||
stdint.h \
|
||||
stdlib.h \
|
||||
string.h \
|
||||
sys/socket.h \
|
||||
sys/time.h \
|
||||
syslog.h \
|
||||
time.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.
|
||||
AC_TYPE_SIZE_T
|
||||
AC_TYPE_SSIZE_T
|
||||
@@ -404,27 +532,80 @@ AC_TYPE_UINT8_T
|
||||
AC_TYPE_UINT16_T
|
||||
AC_TYPE_UINT32_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_C_BIGENDIAN
|
||||
AC_C_INLINE
|
||||
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.
|
||||
if test "x$cross_compiling" != "xyes"; then
|
||||
AC_FUNC_MALLOC
|
||||
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([ \
|
||||
_Exit \
|
||||
accept4 \
|
||||
dup2 \
|
||||
getcwd \
|
||||
getpwnam \
|
||||
localtime_r \
|
||||
memchr \
|
||||
memmove \
|
||||
memset \
|
||||
socket \
|
||||
sqrt \
|
||||
strchr \
|
||||
strdup \
|
||||
strerror \
|
||||
strndup \
|
||||
strstr \
|
||||
strtol \
|
||||
strtoul \
|
||||
timegm \
|
||||
])
|
||||
|
||||
dnl Windows library for winsock2
|
||||
case "${host}" in
|
||||
*mingw*)
|
||||
LIBS="$LIBS -lws2_32"
|
||||
;;
|
||||
esac
|
||||
# timerfd_create was added in linux kernel 2.6.25
|
||||
|
||||
AC_CHECK_FUNC([timerfd_create],
|
||||
[have_timerfd_create=yes], [have_timerfd_create=no])
|
||||
|
||||
|
||||
# Checks for epoll availability, primarily for examples/tiny-nghttpd
|
||||
AX_HAVE_EPOLL([have_epoll=yes], [have_epoll=no])
|
||||
|
||||
AM_CONDITIONAL([ENABLE_TINY_NGHTTPD],
|
||||
[ test "x${have_epoll}" = "xyes" &&
|
||||
test "x${have_timerfd_create}" = "xyes"])
|
||||
|
||||
ac_save_CFLAGS=$CFLAGS
|
||||
CFLAGS=
|
||||
|
||||
if test "x$werror" != "xno"; then
|
||||
AX_CHECK_COMPILE_FLAG([-Wall], [CFLAGS="$CFLAGS -Wall"])
|
||||
@@ -447,27 +628,37 @@ if test "x$werror" != "xno"; then
|
||||
AX_CHECK_COMPILE_FLAG([-Wcast-align], [CFLAGS="$CFLAGS -Wcast-align"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wclobbered], [CFLAGS="$CFLAGS -Wclobbered"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wvla], [CFLAGS="$CFLAGS -Wvla"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wno-unused-parameter], [CFLAGS="$CFLAGS -Wno-unused-parameter"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wpragmas], [CFLAGS="$CFLAGS -Wpragmas"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wunreachable-code], [CFLAGS="$CFLAGS -Wunreachable-code"])
|
||||
AX_CHECK_COMPILE_FLAG([-Waddress], [CFLAGS="$CFLAGS -Waddress"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wattributes], [CFLAGS="$CFLAGS -Wattributes"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wdiv-by-zero], [CFLAGS="$CFLAGS -Wdiv-by-zero"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wshorten-64-to-32], [CFLAGS="$CFLAGS -Wshorten-64-to-32"])
|
||||
|
||||
# Only work with Clang for the moment
|
||||
AX_CHECK_COMPILE_FLAG([-Wheader-guard], [CFLAGS="$CFLAGS -Wheader-guard"])
|
||||
fi
|
||||
|
||||
WARNCFLAGS=$CFLAGS
|
||||
CFLAGS=$ac_save_CFLAGS
|
||||
|
||||
AC_SUBST([WARNCFLAGS])
|
||||
|
||||
if test "x$debug" != "xno"; then
|
||||
AC_DEFINE([DEBUGBUILD], [1], [Define to 1 to enable debug output.])
|
||||
fi
|
||||
|
||||
if test "x$threads" != "xyes"; then
|
||||
enable_threads=yes
|
||||
# Some platform does not have working std::future. We disable
|
||||
# threading for those platforms.
|
||||
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.])
|
||||
fi
|
||||
|
||||
AC_SUBST([TESTS_LIBS])
|
||||
AC_SUBST([SRC_LIBS])
|
||||
AC_SUBST([TESTLDADD])
|
||||
AC_SUBST([APPLDFLAGS])
|
||||
|
||||
AC_CONFIG_FILES([
|
||||
Makefile
|
||||
@@ -479,18 +670,34 @@ AC_CONFIG_FILES([
|
||||
tests/testdata/Makefile
|
||||
third-party/Makefile
|
||||
src/Makefile
|
||||
src/includes/Makefile
|
||||
src/libnghttp2_asio.pc
|
||||
examples/Makefile
|
||||
python/Makefile
|
||||
python/setup.py
|
||||
integration-tests/Makefile
|
||||
integration-tests/config.go
|
||||
integration-tests/setenv
|
||||
doc/Makefile
|
||||
doc/conf.py
|
||||
doc/index.rst
|
||||
doc/package_README.rst
|
||||
doc/tutorial-client.rst
|
||||
doc/tutorial-server.rst
|
||||
doc/tutorial-hpack.rst
|
||||
doc/nghttpx-howto.rst
|
||||
doc/h2load-howto.rst
|
||||
doc/libnghttp2_asio.rst
|
||||
doc/python-apiref.rst
|
||||
doc/building-android-binary.rst
|
||||
doc/nghttp2.h.rst
|
||||
doc/nghttp2ver.h.rst
|
||||
doc/asio_http2.h.rst
|
||||
doc/asio_http2_server.h.rst
|
||||
doc/asio_http2_client.h.rst
|
||||
doc/contribute.rst
|
||||
contrib/Makefile
|
||||
script/Makefile
|
||||
])
|
||||
AC_OUTPUT
|
||||
|
||||
@@ -501,6 +708,7 @@ AC_MSG_NOTICE([summary of build options:
|
||||
Install prefix: ${prefix}
|
||||
C compiler: ${CC}
|
||||
CFLAGS: ${CFLAGS}
|
||||
WARNCFLAGS: ${WARNCFLAGS}
|
||||
LDFLAGS: ${LDFLAGS}
|
||||
LIBS: ${LIBS}
|
||||
CPPFLAGS: ${CPPFLAGS}
|
||||
@@ -509,23 +717,37 @@ AC_MSG_NOTICE([summary of build options:
|
||||
CXXFLAGS: ${CXXFLAGS}
|
||||
CXXCPP: ${CXXCPP}
|
||||
Library types: Shared=${enable_shared}, Static=${enable_static}
|
||||
Python: ${PYTHON}
|
||||
PYTHON_VERSION: ${PYTHON_VERSION}
|
||||
pyexecdir: ${pyexecdir}
|
||||
Python-dev: ${have_python_dev}
|
||||
PYTHON_CPPFLAGS:${PYTHON_CPPFLAGS}
|
||||
PYTHON_LDFLAGS: ${PYTHON_LDFLAGS}
|
||||
Cython: ${CYTHON}
|
||||
CUnit: ${have_cunit}
|
||||
OpenSSL: ${have_openssl}
|
||||
Libxml2: ${have_libxml2}
|
||||
Libevent(SSL): ${have_libevent_openssl}
|
||||
Spdylay: ${have_spdylay}
|
||||
Jansson: ${have_jansson}
|
||||
Jemalloc: ${have_jemalloc}
|
||||
Applications: ${enable_app}
|
||||
HPACK tools: ${enable_hpack_tools}
|
||||
Examples: ${enable_examples}
|
||||
Python bindings:${enable_python_bindings}
|
||||
Failmalloc: ${request_failmalloc}
|
||||
Python:
|
||||
Python: ${PYTHON}
|
||||
PYTHON_VERSION: ${PYTHON_VERSION}
|
||||
pyexecdir: ${pyexecdir}
|
||||
Python-dev: ${have_python_dev}
|
||||
PYTHON_CPPFLAGS:${PYTHON_CPPFLAGS}
|
||||
PYTHON_LDFLAGS: ${PYTHON_LDFLAGS}
|
||||
Cython: ${CYTHON}
|
||||
Test:
|
||||
CUnit: ${have_cunit}
|
||||
Failmalloc: ${enable_failmalloc}
|
||||
Libs:
|
||||
OpenSSL: ${have_openssl}
|
||||
Libxml2: ${have_libxml2}
|
||||
Libev: ${have_libev}
|
||||
Libevent(SSL): ${have_libevent_openssl}
|
||||
Spdylay: ${have_spdylay}
|
||||
Jansson: ${have_jansson}
|
||||
Jemalloc: ${have_jemalloc}
|
||||
Zlib: ${have_zlib}
|
||||
Boost CPPFLAGS: ${BOOST_CPPFLAGS}
|
||||
Boost LDFLAGS: ${BOOST_LDFLAGS}
|
||||
Boost::ASIO: ${BOOST_ASIO_LIB}
|
||||
Boost::System: ${BOOST_SYSTEM_LIB}
|
||||
Boost::Thread: ${BOOST_THREAD_LIB}
|
||||
Features:
|
||||
Applications: ${enable_app}
|
||||
HPACK tools: ${enable_hpack_tools}
|
||||
Libnghttp2_asio:${enable_asio_lib}
|
||||
Examples: ${enable_examples}
|
||||
Python bindings:${enable_python_bindings}
|
||||
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
|
||||
44
contrib/Makefile.am
Normal file
44
contrib/Makefile.am
Normal file
@@ -0,0 +1,44 @@
|
||||
# nghttp2 - HTTP/2 C Library
|
||||
|
||||
# Copyright (c) 2014 Tatsuhiro Tsujikawa
|
||||
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
configfiles = nghttpx-init nghttpx.service nghttpx-upstart.conf
|
||||
|
||||
EXTRA_DIST = $(configfiles:%=%.in) nghttpx-logrotate
|
||||
|
||||
edit = sed -e 's|@bindir[@]|$(bindir)|g'
|
||||
|
||||
nghttpx-init: %: $(srcdir)/%.in
|
||||
rm -f $@ $@.tmp
|
||||
$(edit) $< > $@.tmp
|
||||
chmod +x $@.tmp
|
||||
mv $@.tmp $@
|
||||
|
||||
nghttpx.service nghttpx-upstart.conf: %: $(srcdir)/%.in
|
||||
$(edit) $< > $@
|
||||
|
||||
$(configfiles): Makefile
|
||||
|
||||
all-local: $(configfiles)
|
||||
|
||||
clean-local:
|
||||
-rm -f nghttpx-init.tmp $(configfiles)
|
||||
173
contrib/nghttpx-init.in
Normal file
173
contrib/nghttpx-init.in
Normal file
@@ -0,0 +1,173 @@
|
||||
#! /bin/sh
|
||||
### BEGIN INIT INFO
|
||||
# Provides: nghttpx
|
||||
# Required-Start: $remote_fs $syslog
|
||||
# Required-Stop: $remote_fs $syslog
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: nghttpx initscript
|
||||
# Description: nghttpx initscript
|
||||
### END INIT INFO
|
||||
|
||||
# Author: Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
|
||||
#
|
||||
# Do NOT "set -e"
|
||||
|
||||
# PATH should only include /usr/* if it runs after the mountnfs.sh script
|
||||
PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin
|
||||
DESC="HTTP/2 reverse proxy"
|
||||
NAME=nghttpx
|
||||
# Depending on the configuration, binary may be located under @sbindir@
|
||||
DAEMON=@bindir@/$NAME
|
||||
PIDFILE=/var/run/$NAME.pid
|
||||
DAEMON_ARGS="--conf /etc/nghttpx/nghttpx.conf --pid-file=$PIDFILE"
|
||||
SCRIPTNAME=/etc/init.d/$NAME
|
||||
|
||||
# Exit if the package is not installed
|
||||
[ -x "$DAEMON" ] || exit 0
|
||||
|
||||
# Read configuration variable file if it is present
|
||||
[ -r /etc/default/$NAME ] && . /etc/default/$NAME
|
||||
|
||||
# Load the VERBOSE setting and other rcS variables
|
||||
. /lib/init/vars.sh
|
||||
|
||||
# Define LSB log_* functions.
|
||||
# Depend on lsb-base (>= 3.2-14) to ensure that this file is present
|
||||
# and status_of_proc is working.
|
||||
. /lib/lsb/init-functions
|
||||
|
||||
#
|
||||
# Function that starts the daemon/service
|
||||
#
|
||||
do_start()
|
||||
{
|
||||
# Return
|
||||
# 0 if daemon has been started
|
||||
# 1 if daemon was already running
|
||||
# 2 if daemon could not be started
|
||||
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
|
||||
|| return 1
|
||||
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \
|
||||
$DAEMON_ARGS \
|
||||
|| return 2
|
||||
# Add code here, if necessary, that waits for the process to be ready
|
||||
# to handle requests from services started subsequently which depend
|
||||
# on this one. As a last resort, sleep for some time.
|
||||
}
|
||||
|
||||
#
|
||||
# Function that stops the daemon/service
|
||||
#
|
||||
do_stop()
|
||||
{
|
||||
# Return
|
||||
# 0 if daemon has been stopped
|
||||
# 1 if daemon was already stopped
|
||||
# 2 if daemon could not be stopped
|
||||
# other if a failure occurred
|
||||
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE
|
||||
RETVAL="$?"
|
||||
[ "$RETVAL" = 2 ] && return 2
|
||||
|
||||
# Wait for children to finish too if this is a daemon that forks
|
||||
# and if the daemon is only ever run from this initscript.
|
||||
# If the above conditions are not satisfied then add some other code
|
||||
# that waits for the process to drop all resources that could be
|
||||
# needed by services started subsequently. A last resort is to
|
||||
# sleep for some time.
|
||||
#start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
|
||||
#[ "$?" = 2 ] && return 2
|
||||
# Many daemons don't delete their pidfiles when they exit.
|
||||
rm -f $PIDFILE
|
||||
return "$RETVAL"
|
||||
}
|
||||
|
||||
#
|
||||
# Function that sends a SIGHUP to the daemon/service
|
||||
#
|
||||
do_reload() {
|
||||
#
|
||||
# If the daemon can reload its configuration without
|
||||
# restarting (for example, when it is sent a SIGHUP),
|
||||
# then implement that here.
|
||||
#
|
||||
start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME
|
||||
return 0
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
|
||||
do_start
|
||||
case "$?" in
|
||||
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
|
||||
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
|
||||
esac
|
||||
;;
|
||||
stop)
|
||||
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
|
||||
do_stop
|
||||
case "$?" in
|
||||
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
|
||||
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
|
||||
esac
|
||||
;;
|
||||
status)
|
||||
status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
|
||||
;;
|
||||
upgrade)
|
||||
log_daemon_msg "Upgrade $DESC" "$NAME"
|
||||
pid=`pidofproc -p $PIDFILE $NAME`
|
||||
case "$?" in
|
||||
0) echo "Sending USR2 signal to $pid"
|
||||
kill -USR2 $pid
|
||||
echo "Waiting for new binary..."
|
||||
sleep 5
|
||||
echo "Sending QUIT signal to $pid"
|
||||
kill -QUIT $pid
|
||||
log_end_msg 0
|
||||
;;
|
||||
*) echo "pidofproc() failed"
|
||||
log_end_msg 1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
#reload|force-reload)
|
||||
#
|
||||
# If do_reload() is not implemented then leave this commented out
|
||||
# and leave 'force-reload' as an alias for 'restart'.
|
||||
#
|
||||
#log_daemon_msg "Reloading $DESC" "$NAME"
|
||||
#do_reload
|
||||
#log_end_msg $?
|
||||
#;;
|
||||
restart|force-reload)
|
||||
#
|
||||
# If the "reload" option is implemented then remove the
|
||||
# 'force-reload' alias
|
||||
#
|
||||
log_daemon_msg "Restarting $DESC" "$NAME"
|
||||
do_stop
|
||||
case "$?" in
|
||||
0|1)
|
||||
do_start
|
||||
case "$?" in
|
||||
0) log_end_msg 0 ;;
|
||||
1) log_end_msg 1 ;; # Old process is still running
|
||||
*) log_end_msg 1 ;; # Failed to start
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
# Failed to stop
|
||||
log_end_msg 1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload|upgrade}" >&2
|
||||
exit 3
|
||||
;;
|
||||
esac
|
||||
|
||||
:
|
||||
11
contrib/nghttpx-logrotate
Normal file
11
contrib/nghttpx-logrotate
Normal file
@@ -0,0 +1,11 @@
|
||||
/var/log/nghttpx/*.log {
|
||||
weekly
|
||||
rotate 52
|
||||
missingok
|
||||
compress
|
||||
delaycompress
|
||||
notifempty
|
||||
postrotate
|
||||
killall -USR1 nghttpx 2> /dev/null || true
|
||||
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
|
||||
asio_http2.h.rst
|
||||
asio_http2_client.h.rst
|
||||
asio_http2_server.h.rst
|
||||
building-android-binary.rst
|
||||
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
|
||||
|
||||
172
doc/Makefile.am
172
doc/Makefile.am
@@ -23,32 +23,147 @@
|
||||
|
||||
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 = \
|
||||
mkapiref.py \
|
||||
README.rst \
|
||||
apiref-header.rst \
|
||||
programmers-guide.rst \
|
||||
$(APIDOCS) \
|
||||
nghttp.1.rst \
|
||||
nghttpd.1.rst \
|
||||
nghttpx.1.rst \
|
||||
h2load.1.rst \
|
||||
sources/index.rst \
|
||||
sources/tutorial-client.rst \
|
||||
sources/tutorial-server.rst \
|
||||
sources/tutorial-hpack.rst \
|
||||
sources/nghttpx-howto.rst \
|
||||
_themes/sphinx_rtd_theme/footer.html \
|
||||
_themes/sphinx_rtd_theme/theme.conf \
|
||||
_themes/sphinx_rtd_theme/layout_old.html \
|
||||
sources/h2load-howto.rst \
|
||||
sources/libnghttp2_asio.rst \
|
||||
sources/python-apiref.rst \
|
||||
sources/building-android-binary.rst \
|
||||
sources/contribute.rst \
|
||||
_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/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/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 \
|
||||
$(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
|
||||
#
|
||||
@@ -85,16 +200,33 @@ help:
|
||||
@echo " linkcheck to check all external links for integrity"
|
||||
@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
|
||||
$(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:
|
||||
-rm apiref.rst
|
||||
-rm $(APIDOCS)
|
||||
-rm -rf $(BUILDDIR)/*
|
||||
|
||||
html: apiref.rst
|
||||
html-local: apiref.rst
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||
@@ -166,7 +298,7 @@ text:
|
||||
@echo
|
||||
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
||||
|
||||
man:
|
||||
man: apiref.rst
|
||||
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
||||
@echo
|
||||
@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
|
||||
|
||||
VERSION = (0, 1, 5)
|
||||
VERSION = (0, 1, 8)
|
||||
|
||||
__version__ = ".".join(str(v) for v in 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 %}
|
||||
<li>{{ title }}</li>
|
||||
<li class="wy-breadcrumbs-aside">
|
||||
{% if display_github %}
|
||||
<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>
|
||||
{% elif display_bitbucket %}
|
||||
<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 show_source and has_source and sourcename %}
|
||||
<a href="{{ pathto('_sources/' + sourcename, true)|e }}" rel="nofollow"> View page source</a>
|
||||
{% if pagename != "search" %}
|
||||
{% if display_github %}
|
||||
<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>
|
||||
{% elif display_bitbucket %}
|
||||
<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 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 %}
|
||||
</li>
|
||||
</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 %}
|
||||
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
|
||||
{% 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 %}
|
||||
{% 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 %}
|
||||
</div>
|
||||
{% endif %}
|
||||
@@ -28,5 +28,9 @@
|
||||
</p>
|
||||
</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>
|
||||
|
||||
|
||||
43
doc/_themes/sphinx_rtd_theme/layout.html
vendored
43
doc/_themes/sphinx_rtd_theme/layout.html
vendored
@@ -12,6 +12,7 @@
|
||||
<!--[if gt IE 8]><!--> <html class="no-js" lang="en" > <!--<![endif]-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
{{ metatags }}
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
{% block htmltitle %}
|
||||
<title>{{ title|striptags|e }}{{ titlesuffix }}</title>
|
||||
@@ -23,7 +24,6 @@
|
||||
{% endif %}
|
||||
|
||||
{# CSS #}
|
||||
<link href='https://fonts.googleapis.com/css?family=Lato:400,700|Roboto+Slab:400,700|Inconsolata:400,700' rel='stylesheet' type='text/css'>
|
||||
|
||||
{# OPENSEARCH #}
|
||||
{% if not embedded %}
|
||||
@@ -42,6 +42,10 @@
|
||||
<link rel="stylesheet" href="{{ pathto(cssfile, 1) }}" type="text/css" />
|
||||
{% endfor %}
|
||||
|
||||
{% for cssfile in extra_css_files %}
|
||||
<link rel="stylesheet" href="{{ pathto(cssfile, 1) }}" type="text/css" />
|
||||
{% endfor %}
|
||||
|
||||
{%- block linktags %}
|
||||
{%- if hasdoc('about') %}
|
||||
<link rel="author" title="{{ _('About these documents') }}"
|
||||
@@ -71,7 +75,7 @@
|
||||
{%- block extrahead %} {% endblock %}
|
||||
|
||||
{# 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>
|
||||
|
||||
@@ -82,18 +86,35 @@
|
||||
{# SIDE NAV, TOGGLES ON MOBILE #}
|
||||
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
|
||||
<div class="wy-side-nav-search">
|
||||
<a href="{{ pathto(master_doc) }}" class="fa fa-home"> {{ project }}</a>
|
||||
{% block sidebartitle %}
|
||||
|
||||
{% 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" %}
|
||||
|
||||
{% endblock %}
|
||||
</div>
|
||||
|
||||
<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) %}
|
||||
{% if toctree %}
|
||||
{{ toctree }}
|
||||
{% else %}
|
||||
<!-- Local TOC -->
|
||||
<div class="local-toc">{{ toc }}</div>
|
||||
{% endif %}
|
||||
{% block menu %}
|
||||
{% set toctree = toctree(maxdepth=4, collapse=False, includehidden=True) %}
|
||||
{% if toctree %}
|
||||
{{ toctree }}
|
||||
{% else %}
|
||||
<!-- Local TOC -->
|
||||
<div class="local-toc">{{ toc }}</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
@@ -111,7 +132,7 @@
|
||||
<div class="wy-nav-content">
|
||||
<div class="rst-content">
|
||||
{% include "breadcrumbs.html" %}
|
||||
<div role="main">
|
||||
<div role="main" class="document">
|
||||
{% block body %}{% endblock %}
|
||||
</div>
|
||||
{% include "footer.html" %}
|
||||
|
||||
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">
|
||||
<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="hidden" name="check_keywords" value="yes" />
|
||||
<input type="hidden" name="area" value="default" />
|
||||
</form>
|
||||
</div>
|
||||
{%- endif %}
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
.fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-weight:normal;font-style:normal;src:url("../font/fontawesome_webfont.eot");src:url("../font/fontawesome_webfont.eot?#iefix") format("embedded-opentype"),url("../font/fontawesome_webfont.woff") format("woff"),url("../font/fontawesome_webfont.ttf") format("truetype"),url("../font/fontawesome_webfont.svg#FontAwesome") format("svg")}.fa:before{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa{display:inline-block;text-decoration:inherit}li .fa{display:inline-block}li .fa-large:before,li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-0.8em}ul.fas li .fa{width:0.8em}ul.fas li .fa-large:before,ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before{content:"\f02d"}.icon-book:before{content:"\f02d"}.fa-caret-down:before{content:"\f0d7"}.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{width:100%;height:auto}}
|
||||
.fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-weight:normal;font-style:normal;src:url("../font/fontawesome_webfont.eot");src:url("../font/fontawesome_webfont.eot?#iefix") format("embedded-opentype"),url("../font/fontawesome_webfont.woff") format("woff"),url("../font/fontawesome_webfont.ttf") format("truetype"),url("../font/fontawesome_webfont.svg#FontAwesome") format("svg")}.fa:before{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa{display:inline-block;text-decoration:inherit}li .fa{display:inline-block}li .fa-large:before,li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-0.8em}ul.fas li .fa{width:0.8em}ul.fas li .fa-large:before,ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before{content:""}.icon-book:before{content:""}.fa-caret-down:before{content:""}.icon-caret-down:before{content:""}.fa-caret-up:before{content:""}.icon-caret-up:before{content:""}.fa-caret-left:before{content:""}.icon-caret-left:before{content:""}.fa-caret-right:before{content:""}.icon-caret-right:before{content:""}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980B9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27AE60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#E74C3C;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#F1C40F;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{width:100%;height:auto}}
|
||||
/*# sourceMappingURL=badge_only.css.map */
|
||||
|
||||
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.
|
||||
$(document).on('click', "[data-toggle='wy-nav-top']", function() {
|
||||
$("[data-toggle='wy-nav-shift']").toggleClass("shift");
|
||||
$("[data-toggle='rst-versions']").toggleClass("shift");
|
||||
$("[data-toggle='wy-nav-shift']").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() {
|
||||
$("[data-toggle='wy-nav-shift']").removeClass("shift");
|
||||
$("[data-toggle='rst-versions']").toggleClass("shift");
|
||||
var target = $(this);
|
||||
// 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() {
|
||||
$("[data-toggle='rst-versions']").toggleClass("shift-up");
|
||||
});
|
||||
$("[data-toggle='rst-versions']").toggleClass("shift-up");
|
||||
});
|
||||
// Make tables responsive
|
||||
$("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) {
|
||||
var stickyNav = (function () {
|
||||
var navBar,
|
||||
win,
|
||||
stickyNavCssClass = 'stickynav',
|
||||
applyStickNav = function () {
|
||||
if (navBar.height() <= win.height()) {
|
||||
navBar.addClass(stickyNavCssClass);
|
||||
} else {
|
||||
navBar.removeClass(stickyNavCssClass);
|
||||
}
|
||||
},
|
||||
winScroll = false,
|
||||
linkScroll = false,
|
||||
winPosition = 0,
|
||||
enable = function () {
|
||||
applyStickNav();
|
||||
win.on('resize', applyStickNav);
|
||||
init();
|
||||
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 () {
|
||||
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);
|
||||
return {
|
||||
enable : enable
|
||||
enable: enable,
|
||||
hashChange: hashChange
|
||||
};
|
||||
}());
|
||||
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
|
||||
analytics_id =
|
||||
sticky_navigation = False
|
||||
logo_only =
|
||||
|
||||
@@ -1,31 +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. The applications are
|
||||
responsible to receive it before calling these functions if
|
||||
:type:`nghttp2_session` is configured as server.
|
||||
5
doc/asio_http2.h.rst.in
Normal file
5
doc/asio_http2.h.rst.in
Normal file
@@ -0,0 +1,5 @@
|
||||
asio_http2.h
|
||||
============
|
||||
|
||||
.. literalinclude:: @top_srcdir@/src/includes/nghttp2/asio_http2.h
|
||||
:language: cpp
|
||||
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
|
||||
1
doc/building-android-binary.rst.in
Normal file
1
doc/building-android-binary.rst.in
Normal file
@@ -0,0 +1 @@
|
||||
.. include:: @top_srcdir@/doc/sources/building-android-binary.rst
|
||||
@@ -64,7 +64,7 @@ master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
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
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
@@ -155,7 +155,7 @@ html_theme_path = ['@top_srcdir@/doc/_themes']
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#html_use_smartypants = True
|
||||
html_use_smartypants = False
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
html_sidebars = {
|
||||
@@ -242,6 +242,12 @@ latex_documents = [
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
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)
|
||||
]
|
||||
|
||||
1
doc/contribute.rst.in
Normal file
1
doc/contribute.rst.in
Normal file
@@ -0,0 +1 @@
|
||||
.. include:: @top_srcdir@/doc/sources/contribute.rst
|
||||
1
doc/h2load-howto.rst.in
Normal file
1
doc/h2load-howto.rst.in
Normal file
@@ -0,0 +1 @@
|
||||
.. include:: @top_srcdir@/doc/sources/h2load-howto.rst
|
||||
296
doc/h2load.1
296
doc/h2load.1
@@ -1,61 +1,273 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.45.1.
|
||||
.TH H2LOAD "1" "May 2014" "h2load nghttp2/0.4.0" "User Commands"
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "H2LOAD" "1" "June 23, 2015" "1.0.4" "nghttp2"
|
||||
.SH NAME
|
||||
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
|
||||
.B h2load
|
||||
[\fI\,OPTIONS\/\fR]... \fI\,<URI>\/\fR...
|
||||
.sp
|
||||
\fBh2load\fP [OPTIONS]... [URI]...
|
||||
.SH DESCRIPTION
|
||||
.sp
|
||||
benchmarking tool for HTTP/2 and SPDY server
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
<URI>
|
||||
Specify URI to access. Multiple URIs can be
|
||||
specified. 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.
|
||||
.B <URI>
|
||||
Specify URI to access. Multiple URIs can be specified.
|
||||
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
|
||||
.SH OPTIONS
|
||||
.HP
|
||||
\fB\-n\fR, \fB\-\-requests=\fR<N> Number of requests. Default: 1
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-c\fR, \fB\-\-clients=\fR<N>
|
||||
Number of concurrent clients. Default: 1
|
||||
.B \-n, \-\-requests=<N>
|
||||
Number of requests.
|
||||
.sp
|
||||
Default: \fB1\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-t\fR, \fB\-\-threads=\fR<N>
|
||||
Number of native threads. Default: 1
|
||||
.B \-c, \-\-clients=<N>
|
||||
Number of concurrent clients.
|
||||
.sp
|
||||
Default: \fB1\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-m\fR, \fB\-\-max\-concurrent\-streams=\fR(auto|<N>)
|
||||
Max concurrent streams to issue per session. If
|
||||
"auto" is given, the number of given URIs is
|
||||
used. Default: auto
|
||||
.B \-t, \-\-threads=<N>
|
||||
Number of native threads.
|
||||
.sp
|
||||
Default: \fB1\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-w\fR, \fB\-\-window\-bits=\fR<N>
|
||||
Sets the stream level initial window size to
|
||||
(2**<N>)\-1. For SPDY, 2**<N> is used instead.
|
||||
.B \-i, \-\-input\-file=<FILE>
|
||||
Path of a file with multiple URIs are separated by EOLs.
|
||||
This option will disable URIs getting from command\-line.
|
||||
If \(aq\-\(aq is given as <FILE>, URIs will be read from stdin.
|
||||
URIs are used in this order for each client. All URIs
|
||||
are used, then first URI is used and then 2nd URI, and
|
||||
so on. The scheme, host and port in the subsequent
|
||||
URIs, if present, are ignored. Those in the first URI
|
||||
are used solely.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-W\fR, \fB\-\-connection\-window\-bits=\fR<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.
|
||||
.B \-m, \-\-max\-concurrent\-streams=(auto|<N>)
|
||||
Max concurrent streams to issue per session. If "auto"
|
||||
is given, the number of given URIs is used.
|
||||
.sp
|
||||
Default: \fBauto\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-p\fR, \fB\-\-no\-tls\-proto=\fR<PROTOID>
|
||||
Specify ALPN identifier of the protocol to be
|
||||
used when accessing http URI without SSL/TLS.
|
||||
Available protocols: spdy/2, spdy/3, spdy/3.1 and
|
||||
h2c\-12
|
||||
Default: h2c\-12
|
||||
.B \-w, \-\-window\-bits=<N>
|
||||
Sets the stream level initial window size to (2**<N>)\-1.
|
||||
For SPDY, 2**<N> is used instead.
|
||||
.sp
|
||||
Default: \fB30\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-v\fR, \fB\-\-verbose\fR
|
||||
.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.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-p, \-\-no\-tls\-proto=<PROTOID>
|
||||
Specify ALPN identifier of the protocol to be used when
|
||||
accessing http URI without SSL/TLS.
|
||||
Available protocols: spdy/2, spdy/3, spdy/3.1 and h2c
|
||||
.sp
|
||||
Default: \fBh2c\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.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.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-\-version\fR
|
||||
.B \-\-version
|
||||
Display version information and exit.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-h\fR, \fB\-\-help\fR
|
||||
.B \-h, \-\-help
|
||||
Display this help and exit.
|
||||
.SH "SEE ALSO"
|
||||
|
||||
nghttp(1), nghttpd(1), nghttpx(1)
|
||||
.UNINDENT
|
||||
.SH OUTPUT
|
||||
.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.
|
||||
.
|
||||
|
||||
204
doc/h2load.1.rst
Normal file
204
doc/h2load.1.rst
Normal file
@@ -0,0 +1,204 @@
|
||||
|
||||
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
|
||||
|
||||
.. program:: h2load
|
||||
|
||||
h2load(1)
|
||||
=========
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
|
||||
**h2load** [OPTIONS]... [URI]...
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
|
||||
benchmarking tool for HTTP/2 and SPDY server
|
||||
|
||||
.. describe:: <URI>
|
||||
|
||||
Specify URI to access. Multiple URIs can be specified.
|
||||
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.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
|
||||
.. option:: -n, --requests=<N>
|
||||
|
||||
Number of requests.
|
||||
|
||||
Default: ``1``
|
||||
|
||||
.. option:: -c, --clients=<N>
|
||||
|
||||
Number of concurrent clients.
|
||||
|
||||
Default: ``1``
|
||||
|
||||
.. option:: -t, --threads=<N>
|
||||
|
||||
Number of native threads.
|
||||
|
||||
Default: ``1``
|
||||
|
||||
.. option:: -i, --input-file=<FILE>
|
||||
|
||||
Path of a file with multiple URIs are separated by EOLs.
|
||||
This option will disable URIs getting from command-line.
|
||||
If '-' is given as <FILE>, URIs will be read from stdin.
|
||||
URIs are used in this order for each client. All URIs
|
||||
are used, then first URI is used and then 2nd URI, and
|
||||
so on. The scheme, host and port in the subsequent
|
||||
URIs, if present, are ignored. Those in the first URI
|
||||
are used solely.
|
||||
|
||||
.. option:: -m, --max-concurrent-streams=(auto|<N>)
|
||||
|
||||
Max concurrent streams to issue per session. If "auto"
|
||||
is given, the number of given URIs is used.
|
||||
|
||||
Default: ``auto``
|
||||
|
||||
.. option:: -w, --window-bits=<N>
|
||||
|
||||
Sets the stream level initial window size to (2\*\*<N>)-1.
|
||||
For SPDY, 2**<N> is used instead.
|
||||
|
||||
Default: ``30``
|
||||
|
||||
.. option:: -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.
|
||||
|
||||
Default: ``30``
|
||||
|
||||
.. option:: -H, --header=<HEADER>
|
||||
|
||||
Add/Override a header to the requests.
|
||||
|
||||
.. option:: -p, --no-tls-proto=<PROTOID>
|
||||
|
||||
Specify ALPN identifier of the protocol to be used when
|
||||
accessing http URI without SSL/TLS.
|
||||
Available protocols: spdy/2, spdy/3, spdy/3.1 and h2c
|
||||
|
||||
Default: ``h2c``
|
||||
|
||||
.. option:: -d, --data=<FILE>
|
||||
|
||||
Post FILE to server. The request method is changed to
|
||||
POST.
|
||||
|
||||
.. option:: -v, --verbose
|
||||
|
||||
Output debug information.
|
||||
|
||||
.. option:: --version
|
||||
|
||||
Display version information and exit.
|
||||
|
||||
.. option:: -h, --help
|
||||
|
||||
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
|
||||
--------
|
||||
|
||||
: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)`
|
||||
1
doc/libnghttp2_asio.rst.in
Normal file
1
doc/libnghttp2_asio.rst.in
Normal file
@@ -0,0 +1 @@
|
||||
.. include:: @top_srcdir@/doc/sources/libnghttp2_asio.rst
|
||||
128
doc/mkapiref.py
128
doc/mkapiref.py
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# nghttp2 - HTTP/2 C Library
|
||||
|
||||
# Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
@@ -23,20 +24,24 @@
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
# Generates API reference from C source code.
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from __future__ import print_function # At least python 2.6 is required
|
||||
import re, sys, argparse
|
||||
import re, sys, argparse, os.path
|
||||
|
||||
class FunctionDoc:
|
||||
def __init__(self, name, content, domain):
|
||||
self.name = name
|
||||
self.content = content
|
||||
self.domain = domain
|
||||
if self.domain == 'function':
|
||||
self.funcname = re.search(r'(nghttp2_[^ )]+)\(', self.name).group(1)
|
||||
|
||||
def write(self, out):
|
||||
print('''.. {}:: {}'''.format(self.domain, self.name))
|
||||
print('')
|
||||
out.write('.. {}:: {}\n'.format(self.domain, self.name))
|
||||
out.write('\n')
|
||||
for line in self.content:
|
||||
print(' {}'.format(line))
|
||||
out.write(' {}\n'.format(line))
|
||||
|
||||
class StructDoc:
|
||||
def __init__(self, name, content, members, member_domain):
|
||||
@@ -47,17 +52,17 @@ class StructDoc:
|
||||
|
||||
def write(self, out):
|
||||
if self.name:
|
||||
print('''.. type:: {}'''.format(self.name))
|
||||
print('')
|
||||
out.write('.. type:: {}\n'.format(self.name))
|
||||
out.write('\n')
|
||||
for line in self.content:
|
||||
print(' {}'.format(line))
|
||||
print('')
|
||||
out.write(' {}\n'.format(line))
|
||||
out.write('\n')
|
||||
for name, content in self.members:
|
||||
print(''' .. {}:: {}'''.format(self.member_domain, name))
|
||||
print('')
|
||||
out.write(' .. {}:: {}\n'.format(self.member_domain, name))
|
||||
out.write('\n')
|
||||
for line in content:
|
||||
print(''' {}'''.format(line))
|
||||
print('')
|
||||
out.write(' {}\n'.format(line))
|
||||
out.write('\n')
|
||||
|
||||
class MacroDoc:
|
||||
def __init__(self, name, content):
|
||||
@@ -65,10 +70,10 @@ class MacroDoc:
|
||||
self.content = content
|
||||
|
||||
def write(self, out):
|
||||
print('''.. macro:: {}'''.format(self.name))
|
||||
print('')
|
||||
out.write('''.. macro:: {}\n'''.format(self.name))
|
||||
out.write('\n')
|
||||
for line in self.content:
|
||||
print(' {}'.format(line))
|
||||
out.write(' {}\n'.format(line))
|
||||
|
||||
def make_api_ref(infiles):
|
||||
macros = []
|
||||
@@ -93,19 +98,65 @@ def make_api_ref(infiles):
|
||||
enums.append(process_enum(infile))
|
||||
elif doctype == '@macro':
|
||||
macros.append(process_macro(infile))
|
||||
return macros, enums, types, functions
|
||||
|
||||
alldocs = [('Macros', macros),
|
||||
('Enums', enums),
|
||||
('Types (structs, unions and typedefs)', types),
|
||||
('Functions', functions)]
|
||||
for title, docs in alldocs:
|
||||
if not docs:
|
||||
continue
|
||||
print(title)
|
||||
print('-'*len(title))
|
||||
for doc in docs:
|
||||
doc.write(sys.stdout)
|
||||
print('')
|
||||
print('')
|
||||
|
||||
def output(
|
||||
indexfile, macrosfile, enumsfile, typesfile, funcsdir,
|
||||
macros, enums, types, functions):
|
||||
indexfile.write('''
|
||||
API Reference
|
||||
=============
|
||||
|
||||
.. 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):
|
||||
content = read_content(infile)
|
||||
@@ -174,6 +225,7 @@ def process_function(domain, infile):
|
||||
func_proto = ''.join(func_proto)
|
||||
func_proto = re.sub(r';\n$', '', func_proto)
|
||||
func_proto = re.sub(r'\s+', ' ', func_proto)
|
||||
func_proto = re.sub(r'NGHTTP2_EXTERN ', '', func_proto)
|
||||
return FunctionDoc(func_proto, content, domain)
|
||||
|
||||
def read_content(infile):
|
||||
@@ -199,12 +251,30 @@ def transform_content(content):
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description="Generate API reference")
|
||||
parser.add_argument('--header', type=argparse.FileType('r'),
|
||||
help='header inserted at the top of the page')
|
||||
parser.add_argument('index', type=argparse.FileType('w'),
|
||||
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'),
|
||||
help='source file')
|
||||
args = parser.parse_args()
|
||||
if args.header:
|
||||
print(args.header.read())
|
||||
macros = []
|
||||
enums = []
|
||||
types = []
|
||||
funcs = []
|
||||
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)
|
||||
|
||||
322
doc/nghttp.1
322
doc/nghttp.1
@@ -1,109 +1,289 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.45.1.
|
||||
.TH NGHTTP "1" "May 2014" "nghttp nghttp2/0.4.0" "User Commands"
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTP" "1" "June 23, 2015" "1.0.4" "nghttp2"
|
||||
.SH NAME
|
||||
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
|
||||
.B nghttp
|
||||
[\fI\,OPTIONS\/\fR]... \fI\,<URI>\/\fR...
|
||||
.sp
|
||||
\fBnghttp\fP [OPTIONS]... <URI>...
|
||||
.SH DESCRIPTION
|
||||
.sp
|
||||
HTTP/2 experimental client
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
<URI>
|
||||
.B <URI>
|
||||
Specify URI to access.
|
||||
.UNINDENT
|
||||
.SH OPTIONS
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-v\fR, \fB\-\-verbose\fR
|
||||
Print debug information such as reception and
|
||||
transmission of frames and name/value pairs.
|
||||
.B \-v, \-\-verbose
|
||||
Print debug information such as reception and
|
||||
transmission of frames and name/value pairs. Specifying
|
||||
this option multiple times increases verbosity.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-n\fR, \fB\-\-null\-out\fR
|
||||
.B \-n, \-\-null\-out
|
||||
Discard downloaded data.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-O\fR, \fB\-\-remote\-name\fR
|
||||
Save download data in the current directory. The
|
||||
filename is dereived from URI. If URI ends with
|
||||
\&'/', 'index.html' is used as a filename. Not
|
||||
implemented yet.
|
||||
.B \-O, \-\-remote\-name
|
||||
Save download data in the current directory. The
|
||||
filename is dereived from URI. If URI ends with \(aq\fI/\fP\(aq,
|
||||
\(aqindex.html\(aq is used as a filename. Not implemented
|
||||
yet.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-t\fR, \fB\-\-timeout=\fR<N>
|
||||
Timeout each request after <N> seconds.
|
||||
.B \-t, \-\-timeout=<DURATION>
|
||||
Timeout each request after <DURATION>. Set 0 to disable
|
||||
timeout.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-w\fR, \fB\-\-window\-bits=\fR<N>
|
||||
Sets the stream level initial window size to
|
||||
.B \-w, \-\-window\-bits=<N>
|
||||
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.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-W\fR, \fB\-\-connection\-window\-bits=\fR<N>
|
||||
Sets the connection level initial window size to
|
||||
2**<N>\-1.
|
||||
.B \-a, \-\-get\-assets
|
||||
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.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-a\fR, \fB\-\-get\-assets\fR
|
||||
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.
|
||||
.TP
|
||||
\fB\-s\fR, \fB\-\-stat\fR
|
||||
.B \-s, \-\-stat
|
||||
Print statistics.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-H\fR, \fB\-\-header\fR
|
||||
Add a header to the requests.
|
||||
.B \-H, \-\-header=<HEADER>
|
||||
Add a header to the requests. Example: \fI\%\-H\fP\(aq:method: PUT\(aq
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-\-cert=\fR<CERT>
|
||||
Use the specified client certificate file. The
|
||||
file must be in PEM format.
|
||||
.B \-\-trailer=<HEADER>
|
||||
Add a trailer header to the requests. <HEADER> must not
|
||||
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
|
||||
\fB\-\-key=\fR<KEY>
|
||||
Use the client private key file. The file must
|
||||
be in PEM format.
|
||||
.B \-\-cert=<CERT>
|
||||
Use the specified client certificate file. The file
|
||||
must be in PEM format.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-d\fR, \fB\-\-data=\fR<FILE>
|
||||
Post FILE to server. If '\-' is given, data will
|
||||
be read from stdin.
|
||||
.B \-\-key=<KEY>
|
||||
Use the client private key file. The file must be in
|
||||
PEM format.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-g\fR, \fB\-\-compress\-data\fR
|
||||
When used with \fB\-d\fR option, compress request body
|
||||
on the fly using per\-frame compression.
|
||||
.B \-d, \-\-data=<FILE>
|
||||
Post FILE to server. If \(aq\-\(aq is given, data will be read
|
||||
from stdin.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-m\fR, \fB\-\-multiply=\fR<N> Request each URI <N> times.
|
||||
By default, same URI
|
||||
is not requested twice. This option disables it
|
||||
too.
|
||||
.B \-m, \-\-multiply=<N>
|
||||
Request each URI <N> times. By default, same URI is not
|
||||
requested twice. This option disables it too.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-u\fR, \fB\-\-upgrade\fR
|
||||
Perform HTTP Upgrade for HTTP/2. This option is
|
||||
ignored if the request URI has https scheme. If
|
||||
\fB\-d\fR is used, the HTTP upgrade request is performed
|
||||
with OPTIONS method.
|
||||
.B \-u, \-\-upgrade
|
||||
Perform HTTP Upgrade for HTTP/2. This option is ignored
|
||||
if the request URI has https scheme. If \fI\%\-d\fP is used, the
|
||||
HTTP upgrade request is performed with OPTIONS method.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-p\fR, \fB\-\-weight=\fR<WEIGHT>
|
||||
Sets priority group weight. The valid value
|
||||
range is [1, 256], inclusive.
|
||||
Default: 16
|
||||
.B \-p, \-\-weight=<WEIGHT>
|
||||
Sets priority group weight. The valid value range is
|
||||
[1, 256], inclusive.
|
||||
.sp
|
||||
Default: \fB16\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-M\fR, \fB\-\-peer\-max\-concurrent\-streams=\fR<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.
|
||||
.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
|
||||
\fB\-c\fR, \fB\-\-header\-table\-size=\fR<N>
|
||||
.B \-c, \-\-header\-table\-size=<SIZE>
|
||||
Specify decoder header table size.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-b\fR, \fB\-\-padding=\fR<N>
|
||||
Add at most <N> bytes to a frame payload as
|
||||
padding. Specify 0 to disable padding.
|
||||
.B \-b, \-\-padding=<N>
|
||||
Add at most <N> bytes to a frame payload as padding.
|
||||
Specify 0 to disable padding.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.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.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-\-continuation\fR
|
||||
.B \-\-continuation
|
||||
Send large header to test CONTINUATION.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-\-version\fR
|
||||
.B \-\-no\-content\-length
|
||||
Don\(aqt send content\-length header field.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.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.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-h\fR, \fB\-\-help\fR
|
||||
.B \-h, \-\-help
|
||||
Display this help and exit.
|
||||
.SH "SEE ALSO"
|
||||
|
||||
nghttpd(1), nghttpx(1), h2load(1)
|
||||
.UNINDENT
|
||||
.sp
|
||||
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.
|
||||
.
|
||||
|
||||
233
doc/nghttp.1.rst
Normal file
233
doc/nghttp.1.rst
Normal file
@@ -0,0 +1,233 @@
|
||||
|
||||
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
|
||||
|
||||
.. program:: nghttp
|
||||
|
||||
nghttp(1)
|
||||
=========
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
|
||||
**nghttp** [OPTIONS]... <URI>...
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
|
||||
HTTP/2 experimental client
|
||||
|
||||
.. describe:: <URI>
|
||||
|
||||
Specify URI to access.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
|
||||
.. option:: -v, --verbose
|
||||
|
||||
Print debug information such as reception and
|
||||
transmission of frames and name/value pairs. Specifying
|
||||
this option multiple times increases verbosity.
|
||||
|
||||
.. option:: -n, --null-out
|
||||
|
||||
Discard downloaded data.
|
||||
|
||||
.. option:: -O, --remote-name
|
||||
|
||||
Save download data in the current directory. The
|
||||
filename is dereived from URI. If URI ends with '*/*',
|
||||
'index.html' is used as a filename. Not implemented
|
||||
yet.
|
||||
|
||||
.. option:: -t, --timeout=<DURATION>
|
||||
|
||||
Timeout each request after <DURATION>. Set 0 to disable
|
||||
timeout.
|
||||
|
||||
.. option:: -w, --window-bits=<N>
|
||||
|
||||
Sets the stream level initial window size to 2\*\*<N>-1.
|
||||
|
||||
.. option:: -W, --connection-window-bits=<N>
|
||||
|
||||
Sets the connection level initial window size to
|
||||
2\*\*<N>-1.
|
||||
|
||||
.. option:: -a, --get-assets
|
||||
|
||||
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.
|
||||
|
||||
.. option:: -s, --stat
|
||||
|
||||
Print statistics.
|
||||
|
||||
.. option:: -H, --header=<HEADER>
|
||||
|
||||
Add a header to the requests. Example: :option:`-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>
|
||||
|
||||
Use the specified client certificate file. The file
|
||||
must be in PEM format.
|
||||
|
||||
.. option:: --key=<KEY>
|
||||
|
||||
Use the client private key file. The file must be in
|
||||
PEM format.
|
||||
|
||||
.. option:: -d, --data=<FILE>
|
||||
|
||||
Post FILE to server. If '-' is given, data will be read
|
||||
from stdin.
|
||||
|
||||
.. option:: -m, --multiply=<N>
|
||||
|
||||
Request each URI <N> times. By default, same URI is not
|
||||
requested twice. This option disables it too.
|
||||
|
||||
.. option:: -u, --upgrade
|
||||
|
||||
Perform HTTP Upgrade for HTTP/2. This option is ignored
|
||||
if the request URI has https scheme. If :option:`-d` is used, the
|
||||
HTTP upgrade request is performed with OPTIONS method.
|
||||
|
||||
.. option:: -p, --weight=<WEIGHT>
|
||||
|
||||
Sets priority group weight. The valid value range is
|
||||
[1, 256], inclusive.
|
||||
|
||||
Default: ``16``
|
||||
|
||||
.. option:: -M, --peer-max-concurrent-streams=<N>
|
||||
|
||||
Use <N> as SETTINGS_MAX_CONCURRENT_STREAMS value of
|
||||
remote endpoint as if it is received in SETTINGS frame.
|
||||
The default is large enough as it is seen as unlimited.
|
||||
|
||||
.. option:: -c, --header-table-size=<SIZE>
|
||||
|
||||
Specify decoder header table size.
|
||||
|
||||
.. option:: -b, --padding=<N>
|
||||
|
||||
Add at most <N> bytes to a frame payload as padding.
|
||||
Specify 0 to disable padding.
|
||||
|
||||
.. option:: -r, --har=<FILE>
|
||||
|
||||
Output HTTP transactions <FILE> in HAR format. If '-'
|
||||
is given, data is written to stdout.
|
||||
|
||||
.. option:: --color
|
||||
|
||||
Force colored log output.
|
||||
|
||||
.. option:: --continuation
|
||||
|
||||
Send large header to test CONTINUATION.
|
||||
|
||||
.. option:: --no-content-length
|
||||
|
||||
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
|
||||
|
||||
Display version information and exit.
|
||||
|
||||
.. option:: -h, --help
|
||||
|
||||
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
|
||||
--------
|
||||
|
||||
: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)`
|
||||
217
doc/nghttpd.1
217
doc/nghttpd.1
@@ -1,83 +1,194 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.45.1.
|
||||
.TH NGHTTPD "1" "May 2014" "nghttpd nghttp2/0.4.0" "User Commands"
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTPD" "1" "June 23, 2015" "1.0.4" "nghttp2"
|
||||
.SH NAME
|
||||
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
|
||||
.B nghttpd
|
||||
[\fI\,OPTION\/\fR]... \fI\,<PORT> <PRIVATE_KEY> <CERT>\/\fR
|
||||
.br
|
||||
.B nghttpd
|
||||
\fI\,--no-tls \/\fR[\fI\,OPTION\/\fR]... \fI\,<PORT>\/\fR
|
||||
.sp
|
||||
\fBnghttpd\fP [OPTION]... <PORT> [<PRIVATE_KEY> <CERT>]
|
||||
.SH DESCRIPTION
|
||||
.sp
|
||||
HTTP/2 experimental server
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
<PORT>
|
||||
.B <PORT>
|
||||
Specify listening port number.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
<PRIVATE_KEY>
|
||||
Set path to server's private key. Required
|
||||
unless \fB\-\-no\-tls\fR is specified.
|
||||
.B <PRIVATE_KEY>
|
||||
Set path to server\(aqs private key. Required unless
|
||||
\fI\%\-\-no\-tls\fP is specified.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
<CERT>
|
||||
Set path to server's certificate. Required
|
||||
unless \fB\-\-no\-tls\fR is specified.
|
||||
.B <CERT>
|
||||
Set path to server\(aqs certificate. Required unless
|
||||
\fI\%\-\-no\-tls\fP is specified.
|
||||
.UNINDENT
|
||||
.SH OPTIONS
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-D\fR, \fB\-\-daemon\fR
|
||||
Run in a background. If \fB\-D\fR is used, the current
|
||||
working directory is changed to '/'. Therefore
|
||||
if this option is used, \fB\-d\fR option must be
|
||||
specified.
|
||||
.B \-a, \-\-address=<ADDR>
|
||||
The address to bind to. If not specified the default IP
|
||||
address determined by getaddrinfo is used.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-V\fR, \fB\-\-verify\-client\fR
|
||||
The server sends a client certificate request.
|
||||
If the client did not return a certificate, the
|
||||
handshake is terminated. Currently, this option
|
||||
just requests a client certificate and does not
|
||||
verify it.
|
||||
.B \-D, \-\-daemon
|
||||
Run in a background. If \fI\%\-D\fP is used, the current working
|
||||
directory is changed to \(aq\fI/\fP\(aq. Therefore if this option
|
||||
is used, \fI\%\-d\fP option must be specified.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-d\fR, \fB\-\-htdocs=\fR<PATH>
|
||||
Specify document root. If this option is not
|
||||
specified, the document root is the current
|
||||
working directory.
|
||||
.B \-V, \-\-verify\-client
|
||||
The server sends a client certificate request. If the
|
||||
client did not return a certificate, the handshake is
|
||||
terminated. Currently, this option just requests a
|
||||
client certificate and does not verify it.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-v\fR, \fB\-\-verbose\fR
|
||||
Print debug information such as reception/
|
||||
transmission of frames and name/value pairs.
|
||||
.B \-d, \-\-htdocs=<PATH>
|
||||
Specify document root. If this option is not specified,
|
||||
the document root is the current working directory.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.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.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-c\fR, \fB\-\-header\-table\-size=\fR<N>
|
||||
.B \-c, \-\-header\-table\-size=<SIZE>
|
||||
Specify decoder header table size.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-\-color\fR
|
||||
.B \-\-color
|
||||
Force colored log output.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-p\fR, \fB\-\-push=\fR<PATH>=<PUSH_PATH,...>
|
||||
Push resources <PUSH_PATH>s when <PATH> is
|
||||
requested. This option can be used repeatedly to
|
||||
specify multiple push configurations. <PATH> and
|
||||
<PUSH_PATH>s are relative to document root. See
|
||||
\fB\-\-htdocs\fR option. Example: \fB\-p\fR/=/foo.png
|
||||
\fB\-p\fR/doc=/bar.css
|
||||
.B \-p, \-\-push=<PATH>=<PUSH_PATH,...>
|
||||
Push resources <PUSH_PATH>s when <PATH> is requested.
|
||||
This option can be used repeatedly to specify multiple
|
||||
push configurations. <PATH> and <PUSH_PATH>s are
|
||||
relative to document root. See \fI\%\-\-htdocs\fP option.
|
||||
Example: \fI\%\-p\fP/=/foo.png \fI\%\-p\fP/doc=/bar.css
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-b\fR, \fB\-\-padding=\fR<N>
|
||||
Add at most <N> bytes to a frame payload as
|
||||
padding. Specify 0 to disable padding.
|
||||
.B \-b, \-\-padding=<N>
|
||||
Add at most <N> bytes to a frame payload as padding.
|
||||
Specify 0 to disable padding.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.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.
|
||||
Default: 1
|
||||
.sp
|
||||
Default: \fB1\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-e\fR, \fB\-\-error\-gzip\fR
|
||||
.B \-e, \-\-error\-gzip
|
||||
Make error response gzipped.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-\-version\fR
|
||||
.B \-\-dh\-param\-file=<PATH>
|
||||
Path to file that contains DH parameters in PEM format.
|
||||
Without this option, DHE cipher suites are not
|
||||
available.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-early\-response
|
||||
Start sending response when request HEADERS is received,
|
||||
rather than complete request is received.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.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.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-h\fR, \fB\-\-help\fR
|
||||
.B \-h, \-\-help
|
||||
Display this help and exit.
|
||||
.SH "SEE ALSO"
|
||||
|
||||
nghttp(1), nghttpx(1), h2load(1)
|
||||
.UNINDENT
|
||||
.sp
|
||||
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.
|
||||
.
|
||||
|
||||
151
doc/nghttpd.1.rst
Normal file
151
doc/nghttpd.1.rst
Normal file
@@ -0,0 +1,151 @@
|
||||
|
||||
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
|
||||
|
||||
.. program:: nghttpd
|
||||
|
||||
nghttpd(1)
|
||||
==========
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
|
||||
**nghttpd** [OPTION]... <PORT> [<PRIVATE_KEY> <CERT>]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
|
||||
HTTP/2 experimental server
|
||||
|
||||
.. describe:: <PORT>
|
||||
|
||||
Specify listening port number.
|
||||
|
||||
.. describe:: <PRIVATE_KEY>
|
||||
|
||||
|
||||
Set path to server's private key. Required unless
|
||||
:option:`--no-tls` is specified.
|
||||
|
||||
.. describe:: <CERT>
|
||||
|
||||
Set path to server's certificate. Required unless
|
||||
:option:`--no-tls` is specified.
|
||||
|
||||
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
|
||||
|
||||
Run in a background. If :option:`-D` is used, the current working
|
||||
directory is changed to '*/*'. Therefore if this option
|
||||
is used, :option:`-d` option must be specified.
|
||||
|
||||
.. option:: -V, --verify-client
|
||||
|
||||
The server sends a client certificate request. If the
|
||||
client did not return a certificate, the handshake is
|
||||
terminated. Currently, this option just requests a
|
||||
client certificate and does not verify it.
|
||||
|
||||
.. option:: -d, --htdocs=<PATH>
|
||||
|
||||
Specify document root. If this option is not specified,
|
||||
the document root is the current working directory.
|
||||
|
||||
.. option:: -v, --verbose
|
||||
|
||||
Print debug information such as reception/ transmission
|
||||
of frames and name/value pairs.
|
||||
|
||||
.. option:: --no-tls
|
||||
|
||||
Disable SSL/TLS.
|
||||
|
||||
.. option:: -c, --header-table-size=<SIZE>
|
||||
|
||||
Specify decoder header table size.
|
||||
|
||||
.. option:: --color
|
||||
|
||||
Force colored log output.
|
||||
|
||||
.. option:: -p, --push=<PATH>=<PUSH_PATH,...>
|
||||
|
||||
Push resources <PUSH_PATH>s when <PATH> is requested.
|
||||
This option can be used repeatedly to specify multiple
|
||||
push configurations. <PATH> and <PUSH_PATH>s are
|
||||
relative to document root. See :option:`--htdocs` option.
|
||||
Example: :option:`-p`\/=/foo.png :option:`-p`\/doc=/bar.css
|
||||
|
||||
.. option:: -b, --padding=<N>
|
||||
|
||||
Add at most <N> bytes to a frame payload as padding.
|
||||
Specify 0 to disable padding.
|
||||
|
||||
.. 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.
|
||||
|
||||
Default: ``1``
|
||||
|
||||
.. option:: -e, --error-gzip
|
||||
|
||||
Make error response gzipped.
|
||||
|
||||
.. option:: --dh-param-file=<PATH>
|
||||
|
||||
Path to file that contains DH parameters in PEM format.
|
||||
Without this option, DHE cipher suites are not
|
||||
available.
|
||||
|
||||
.. option:: --early-response
|
||||
|
||||
Start sending response when request HEADERS is 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
|
||||
|
||||
Display version information and exit.
|
||||
|
||||
.. option:: -h, --help
|
||||
|
||||
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
|
||||
--------
|
||||
|
||||
: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)`
|
||||
1115
doc/nghttpx.1
1115
doc/nghttpx.1
File diff suppressed because it is too large
Load Diff
822
doc/nghttpx.1.rst
Normal file
822
doc/nghttpx.1.rst
Normal file
@@ -0,0 +1,822 @@
|
||||
|
||||
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
|
||||
|
||||
.. program:: nghttpx
|
||||
|
||||
nghttpx(1)
|
||||
==========
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
|
||||
**nghttpx** [OPTIONS]... [<PRIVATE_KEY> <CERT>]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
|
||||
A reverse proxy for HTTP/2, HTTP/1 and SPDY.
|
||||
|
||||
.. describe:: <PRIVATE_KEY>
|
||||
|
||||
|
||||
Set path to server's private key. Required unless :option:`-p`\,
|
||||
:option:`--client` or :option:`\--frontend-no-tls` are given.
|
||||
|
||||
.. describe:: <CERT>
|
||||
|
||||
Set path to server's certificate. Required unless :option:`-p`\,
|
||||
:option:`--client` or :option:`\--frontend-no-tls` are given. To make OCSP
|
||||
stapling work, this must be absolute path.
|
||||
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
|
||||
The options are categorized into several groups.
|
||||
|
||||
Connections
|
||||
~~~~~~~~~~~
|
||||
|
||||
.. option:: -b, --backend=<HOST,PORT>
|
||||
|
||||
Set backend host and port. The multiple backend
|
||||
addresses are accepted by repeating this option. UNIX
|
||||
domain socket can be specified by prefixing path name
|
||||
with "unix:" (e.g., unix:/var/run/backend.sock)
|
||||
|
||||
Default: ``127.0.0.1,80``
|
||||
|
||||
.. option:: -f, --frontend=<HOST,PORT>
|
||||
|
||||
Set frontend host and port. If <HOST> is '\*', it
|
||||
assumes all addresses including both IPv4 and IPv6.
|
||||
UNIX domain socket can be specified by prefixing path
|
||||
name with "unix:" (e.g., unix:/var/run/nghttpx.sock)
|
||||
|
||||
Default: ``*,3000``
|
||||
|
||||
.. option:: --backlog=<N>
|
||||
|
||||
Set listen backlog size.
|
||||
|
||||
Default: ``512``
|
||||
|
||||
.. option:: --backend-ipv4
|
||||
|
||||
Resolve backend hostname to IPv4 address only.
|
||||
|
||||
.. option:: --backend-ipv6
|
||||
|
||||
Resolve backend hostname to IPv6 address only.
|
||||
|
||||
.. option:: --backend-http-proxy-uri=<URI>
|
||||
|
||||
Specify proxy URI in the form
|
||||
http://[<USER>:<PASS>@]<PROXY>:<PORT>. If a proxy
|
||||
requires authentication, specify <USER> and <PASS>.
|
||||
Note that they must be properly percent-encoded. This
|
||||
proxy is used when the backend connection is HTTP/2.
|
||||
First, make a CONNECT request to the proxy and it
|
||||
connects to the backend on behalf of nghttpx. This
|
||||
forms tunnel. After that, nghttpx performs SSL/TLS
|
||||
handshake with the downstream through the tunnel. The
|
||||
timeouts when connecting and making CONNECT request can
|
||||
be specified by :option:`--backend-read-timeout` and
|
||||
:option:`--backend-write-timeout` options.
|
||||
|
||||
|
||||
Performance
|
||||
~~~~~~~~~~~
|
||||
|
||||
.. option:: -n, --workers=<N>
|
||||
|
||||
Set the number of worker threads.
|
||||
|
||||
Default: ``1``
|
||||
|
||||
.. option:: --read-rate=<SIZE>
|
||||
|
||||
Set maximum average read rate on frontend connection.
|
||||
Setting 0 to this option means read rate is unlimited.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --read-burst=<SIZE>
|
||||
|
||||
Set maximum read burst size on frontend connection.
|
||||
Setting 0 to this option means read burst size is
|
||||
unlimited.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --write-rate=<SIZE>
|
||||
|
||||
Set maximum average write rate on frontend connection.
|
||||
Setting 0 to this option means write rate is unlimited.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --write-burst=<SIZE>
|
||||
|
||||
Set maximum write burst size on frontend connection.
|
||||
Setting 0 to this option means write burst size is
|
||||
unlimited.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --worker-read-rate=<SIZE>
|
||||
|
||||
Set maximum average read rate on frontend connection per
|
||||
worker. Setting 0 to this option means read rate is
|
||||
unlimited. Not implemented yet.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --worker-read-burst=<SIZE>
|
||||
|
||||
Set maximum read burst size on frontend connection per
|
||||
worker. Setting 0 to this option means read burst size
|
||||
is unlimited. Not implemented yet.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --worker-write-rate=<SIZE>
|
||||
|
||||
Set maximum average write rate on frontend connection
|
||||
per worker. Setting 0 to this option means write rate
|
||||
is unlimited. Not implemented yet.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --worker-write-burst=<SIZE>
|
||||
|
||||
Set maximum write burst size on frontend connection per
|
||||
worker. Setting 0 to this option means write burst size
|
||||
is unlimited. Not implemented yet.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --worker-frontend-connections=<N>
|
||||
|
||||
Set maximum number of simultaneous connections frontend
|
||||
accepts. Setting 0 means unlimited.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --backend-http2-connections-per-worker=<N>
|
||||
|
||||
Set maximum number of HTTP/2 connections per worker.
|
||||
The default value is 0, which means the number of
|
||||
backend addresses specified by :option:`-b` option.
|
||||
|
||||
.. option:: --backend-http1-connections-per-host=<N>
|
||||
|
||||
Set maximum number of backend concurrent HTTP/1
|
||||
connections per origin host. This option is meaningful
|
||||
when :option:`-s` option is used. The origin host is determined
|
||||
by authority portion of requset URI (or :authority
|
||||
header field for HTTP/2). To limit the number of
|
||||
connections per frontend for default mode, use
|
||||
:option:`--backend-http1-connections-per-frontend`\.
|
||||
|
||||
Default: ``8``
|
||||
|
||||
.. option:: --backend-http1-connections-per-frontend=<N>
|
||||
|
||||
Set maximum number of backend concurrent HTTP/1
|
||||
connections per frontend. This option is only used for
|
||||
default mode. 0 means unlimited. To limit the number
|
||||
of connections per host for HTTP/2 or SPDY proxy mode
|
||||
(-s option), use :option:`--backend-http1-connections-per-host`\.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --rlimit-nofile=<N>
|
||||
|
||||
Set maximum number of open files (RLIMIT_NOFILE) to <N>.
|
||||
If 0 is given, nghttpx does not set the limit.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --backend-request-buffer=<SIZE>
|
||||
|
||||
Set buffer size used to store backend request.
|
||||
|
||||
Default: ``16K``
|
||||
|
||||
.. option:: --backend-response-buffer=<SIZE>
|
||||
|
||||
Set buffer size used to store backend response.
|
||||
|
||||
Default: ``16K``
|
||||
|
||||
|
||||
Timeout
|
||||
~~~~~~~
|
||||
|
||||
.. option:: --frontend-http2-read-timeout=<DURATION>
|
||||
|
||||
Specify read timeout for HTTP/2 and SPDY frontend
|
||||
connection.
|
||||
|
||||
Default: ``3m``
|
||||
|
||||
.. option:: --frontend-read-timeout=<DURATION>
|
||||
|
||||
Specify read timeout for HTTP/1.1 frontend connection.
|
||||
|
||||
Default: ``3m``
|
||||
|
||||
.. option:: --frontend-write-timeout=<DURATION>
|
||||
|
||||
Specify write timeout for all frontend connections.
|
||||
|
||||
Default: ``30s``
|
||||
|
||||
.. option:: --stream-read-timeout=<DURATION>
|
||||
|
||||
Specify read timeout for HTTP/2 and SPDY streams. 0
|
||||
means no timeout.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --stream-write-timeout=<DURATION>
|
||||
|
||||
Specify write timeout for HTTP/2 and SPDY streams. 0
|
||||
means no timeout.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --backend-read-timeout=<DURATION>
|
||||
|
||||
Specify read timeout for backend connection.
|
||||
|
||||
Default: ``3m``
|
||||
|
||||
.. option:: --backend-write-timeout=<DURATION>
|
||||
|
||||
Specify write timeout for backend connection.
|
||||
|
||||
Default: ``30s``
|
||||
|
||||
.. option:: --backend-keep-alive-timeout=<DURATION>
|
||||
|
||||
Specify keep-alive timeout for backend connection.
|
||||
|
||||
Default: ``2s``
|
||||
|
||||
.. option:: --listener-disable-timeout=<DURATION>
|
||||
|
||||
After accepting connection failed, connection listener
|
||||
is disabled for a given amount of time. Specifying 0
|
||||
disables this feature.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
|
||||
SSL/TLS
|
||||
~~~~~~~
|
||||
|
||||
.. option:: --ciphers=<SUITE>
|
||||
|
||||
Set allowed cipher list. The format of the string is
|
||||
described in OpenSSL ciphers(1).
|
||||
|
||||
.. option:: -k, --insecure
|
||||
|
||||
Don't verify backend server's certificate if :option:`-p`\,
|
||||
:option:`--client` or :option:`\--http2-bridge` are given and
|
||||
:option:`--backend-no-tls` is not given.
|
||||
|
||||
.. option:: --cacert=<PATH>
|
||||
|
||||
Set path to trusted CA certificate file if :option:`-p`\, :option:`--client`
|
||||
or :option:`--http2-bridge` are given and :option:`\--backend-no-tls` is not
|
||||
given. The file must be in PEM format. It can contain
|
||||
multiple certificates. If the linked OpenSSL is
|
||||
configured to load system wide certificates, they are
|
||||
loaded at startup regardless of this option.
|
||||
|
||||
.. option:: --private-key-passwd-file=<PATH>
|
||||
|
||||
Path to file that contains password for the server's
|
||||
private key. If none is given and the private key is
|
||||
password protected it'll be requested interactively.
|
||||
|
||||
.. option:: --subcert=<KEYPATH>:<CERTPATH>
|
||||
|
||||
Specify additional certificate and private key file.
|
||||
nghttpx will choose certificates based on the hostname
|
||||
indicated by client using TLS SNI extension. This
|
||||
option can be used multiple times. To make OCSP
|
||||
stapling work, <CERTPATH> must be absolute path.
|
||||
|
||||
.. option:: --backend-tls-sni-field=<HOST>
|
||||
|
||||
Explicitly set the content of the TLS SNI extension.
|
||||
This will default to the backend HOST name.
|
||||
|
||||
.. option:: --dh-param-file=<PATH>
|
||||
|
||||
Path to file that contains DH parameters in PEM format.
|
||||
Without this option, DHE cipher suites are not
|
||||
available.
|
||||
|
||||
.. option:: --npn-list=<LIST>
|
||||
|
||||
Comma delimited list of ALPN protocol identifier sorted
|
||||
in the order of preference. That means most desirable
|
||||
protocol comes first. This is used in both ALPN and
|
||||
NPN. The parameter must be delimited by a single comma
|
||||
only and any white spaces are treated as a part of
|
||||
protocol string.
|
||||
|
||||
Default: ``h2,h2-16,h2-14,spdy/3.1,http/1.1``
|
||||
|
||||
.. option:: --verify-client
|
||||
|
||||
Require and verify client certificate.
|
||||
|
||||
.. option:: --verify-client-cacert=<PATH>
|
||||
|
||||
Path to file that contains CA certificates to verify
|
||||
client certificate. The file must be in PEM format. It
|
||||
can contain multiple certificates.
|
||||
|
||||
.. option:: --client-private-key-file=<PATH>
|
||||
|
||||
Path to file that contains client private key used in
|
||||
backend client authentication.
|
||||
|
||||
.. option:: --client-cert-file=<PATH>
|
||||
|
||||
Path to file that contains client certificate used in
|
||||
backend client authentication.
|
||||
|
||||
.. option:: --tls-proto-list=<LIST>
|
||||
|
||||
Comma delimited list of SSL/TLS protocol to be enabled.
|
||||
The following protocols are available: TLSv1.2, TLSv1.1
|
||||
and TLSv1.0. The name matching is done in
|
||||
case-insensitive manner. The parameter must be
|
||||
delimited by a single comma only and any white spaces
|
||||
are treated as a part of protocol string.
|
||||
|
||||
Default: ``TLSv1.2,TLSv1.1``
|
||||
|
||||
.. option:: --tls-ticket-key-file=<PATH>
|
||||
|
||||
Path to file that contains 48 bytes random data to
|
||||
construct TLS session ticket parameters. This options
|
||||
can be used repeatedly to specify multiple ticket
|
||||
parameters. If several files are given, only the first
|
||||
key is used to encrypt TLS session tickets. Other keys
|
||||
are accepted but server will issue new session ticket
|
||||
with first key. This allows session key rotation.
|
||||
Please note that key rotation does not occur
|
||||
automatically. User should rearrange files or change
|
||||
options values and restart nghttpx gracefully. If
|
||||
opening or reading given file fails, all loaded keys are
|
||||
discarded and it is treated as if none of this option is
|
||||
given. If this option is not given or an error occurred
|
||||
while opening or reading a file, key is generated
|
||||
automatically and renewed every 12hrs. At most 2 keys
|
||||
are stored in memory.
|
||||
|
||||
.. option:: --fetch-ocsp-response-file=<PATH>
|
||||
|
||||
Path to fetch-ocsp-response script file. It should be
|
||||
absolute path.
|
||||
|
||||
Default: ``/usr/local/share/nghttp2/fetch-ocsp-response``
|
||||
|
||||
.. option:: --ocsp-update-interval=<DURATION>
|
||||
|
||||
Set interval to update OCSP response cache.
|
||||
|
||||
Default: ``4h``
|
||||
|
||||
.. option:: --no-ocsp
|
||||
|
||||
Disable OCSP stapling.
|
||||
|
||||
|
||||
HTTP/2 and SPDY
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
.. option:: -c, --http2-max-concurrent-streams=<N>
|
||||
|
||||
Set the maximum number of the concurrent streams in one
|
||||
HTTP/2 and SPDY session.
|
||||
|
||||
Default: ``100``
|
||||
|
||||
.. option:: --frontend-http2-window-bits=<N>
|
||||
|
||||
Sets the per-stream initial window size of HTTP/2 SPDY
|
||||
frontend connection. For HTTP/2, the size is 2\*\*<N>-1.
|
||||
For SPDY, the size is 2\*\*<N>.
|
||||
|
||||
Default: ``16``
|
||||
|
||||
.. option:: --frontend-http2-connection-window-bits=<N>
|
||||
|
||||
Sets the per-connection window size of HTTP/2 and SPDY
|
||||
frontend connection. For HTTP/2, the size is
|
||||
2**<N>-1. For SPDY, the size is 2\*\*<N>.
|
||||
|
||||
Default: ``16``
|
||||
|
||||
.. option:: --frontend-no-tls
|
||||
|
||||
Disable SSL/TLS on frontend connections.
|
||||
|
||||
.. option:: --backend-http2-window-bits=<N>
|
||||
|
||||
Sets the initial window size of HTTP/2 backend
|
||||
connection to 2\*\*<N>-1.
|
||||
|
||||
Default: ``16``
|
||||
|
||||
.. option:: --backend-http2-connection-window-bits=<N>
|
||||
|
||||
Sets the per-connection window size of HTTP/2 backend
|
||||
connection to 2\*\*<N>-1.
|
||||
|
||||
Default: ``16``
|
||||
|
||||
.. option:: --backend-no-tls
|
||||
|
||||
Disable SSL/TLS on backend connections.
|
||||
|
||||
.. option:: --http2-no-cookie-crumbling
|
||||
|
||||
Don't crumble cookie header field.
|
||||
|
||||
.. option:: --padding=<N>
|
||||
|
||||
Add at most <N> bytes to a HTTP/2 frame payload as
|
||||
padding. Specify 0 to disable padding. This option is
|
||||
meant for debugging purpose and not intended to enhance
|
||||
protocol security.
|
||||
|
||||
.. option:: --no-server-push
|
||||
|
||||
Disable HTTP/2 server push. Server push is only
|
||||
supported by default mode and HTTP/2 frontend. SPDY
|
||||
frontend does not support server push.
|
||||
|
||||
|
||||
Mode
|
||||
~~~~
|
||||
|
||||
.. describe:: (default mode)
|
||||
|
||||
|
||||
Accept HTTP/2, SPDY and HTTP/1.1 over SSL/TLS. If
|
||||
:option:`--frontend-no-tls` is used, accept HTTP/2 and HTTP/1.1.
|
||||
The incoming HTTP/1.1 connection can be upgraded to
|
||||
HTTP/2 through HTTP Upgrade. The protocol to the
|
||||
backend is HTTP/1.1.
|
||||
|
||||
.. option:: -s, --http2-proxy
|
||||
|
||||
Like default mode, but enable secure proxy mode.
|
||||
|
||||
.. option:: --http2-bridge
|
||||
|
||||
Like default mode, but communicate with the backend in
|
||||
HTTP/2 over SSL/TLS. Thus the incoming all connections
|
||||
are converted to HTTP/2 connection and relayed to the
|
||||
backend. See :option:`--backend-http-proxy-uri` option if you are
|
||||
behind the proxy and want to connect to the outside
|
||||
HTTP/2 proxy.
|
||||
|
||||
.. option:: --client
|
||||
|
||||
Accept HTTP/2 and HTTP/1.1 without SSL/TLS. The
|
||||
incoming HTTP/1.1 connection can be upgraded to HTTP/2
|
||||
connection through HTTP Upgrade. The protocol to the
|
||||
backend is HTTP/2. To use nghttpx as a forward proxy,
|
||||
use :option:`-p` option instead.
|
||||
|
||||
.. option:: -p, --client-proxy
|
||||
|
||||
Like :option:`--client` option, but it also requires the request
|
||||
path from frontend must be an absolute URI, suitable for
|
||||
use as a forward proxy.
|
||||
|
||||
|
||||
Logging
|
||||
~~~~~~~
|
||||
|
||||
.. option:: -L, --log-level=<LEVEL>
|
||||
|
||||
Set the severity level of log output. <LEVEL> must be
|
||||
one of INFO, NOTICE, WARN, ERROR and FATAL.
|
||||
|
||||
Default: ``NOTICE``
|
||||
|
||||
.. option:: --accesslog-file=<PATH>
|
||||
|
||||
Set path to write access log. To reopen file, send USR1
|
||||
signal to nghttpx.
|
||||
|
||||
.. option:: --accesslog-syslog
|
||||
|
||||
Send access log to syslog. If this option is used,
|
||||
:option:`--accesslog-file` option is ignored.
|
||||
|
||||
.. option:: --accesslog-format=<FORMAT>
|
||||
|
||||
Specify format string for access log. The default
|
||||
format is combined format. The following variables are
|
||||
available:
|
||||
|
||||
* $remote_addr: client IP address.
|
||||
* $time_local: local time in Common Log format.
|
||||
* $time_iso8601: local time in ISO 8601 format.
|
||||
* $request: HTTP request line.
|
||||
* $status: HTTP response status code.
|
||||
* $body_bytes_sent: the number of bytes sent to client
|
||||
as response body.
|
||||
* $http_<VAR>: value of HTTP request header <VAR> where
|
||||
'_' in <VAR> is replaced with '-'.
|
||||
* $remote_port: client port.
|
||||
* $server_port: server port.
|
||||
* $request_time: request processing time in seconds with
|
||||
milliseconds resolution.
|
||||
* $pid: PID of the running process.
|
||||
* $alpn: ALPN identifier of the protocol which generates
|
||||
the response. For HTTP/1, ALPN is always http/1.1,
|
||||
regardless of minor version.
|
||||
|
||||
|
||||
Default: ``$remote_addr - - [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"``
|
||||
|
||||
.. option:: --errorlog-file=<PATH>
|
||||
|
||||
Set path to write error log. To reopen file, send USR1
|
||||
signal to nghttpx. stderr will be redirected to the
|
||||
error log file unless :option:`--errorlog-syslog` is used.
|
||||
|
||||
Default: ``/dev/stderr``
|
||||
|
||||
.. option:: --errorlog-syslog
|
||||
|
||||
Send error log to syslog. If this option is used,
|
||||
:option:`--errorlog-file` option is ignored.
|
||||
|
||||
.. option:: --syslog-facility=<FACILITY>
|
||||
|
||||
Set syslog facility to <FACILITY>.
|
||||
|
||||
Default: ``daemon``
|
||||
|
||||
|
||||
HTTP
|
||||
~~~~
|
||||
|
||||
.. option:: --add-x-forwarded-for
|
||||
|
||||
Append X-Forwarded-For header field to the downstream
|
||||
request.
|
||||
|
||||
.. option:: --strip-incoming-x-forwarded-for
|
||||
|
||||
Strip X-Forwarded-For header field from inbound client
|
||||
requests.
|
||||
|
||||
.. option:: --no-via
|
||||
|
||||
Don't append to Via header field. If Via header field
|
||||
is received, it is left unaltered.
|
||||
|
||||
.. option:: --no-location-rewrite
|
||||
|
||||
Don't rewrite location header field on :option:`--http2-bridge`\,
|
||||
:option:`--client` and default mode. For :option:`\--http2-proxy` and
|
||||
:option:`--client-proxy` mode, location header field will not be
|
||||
altered regardless of this option.
|
||||
|
||||
.. option:: --no-host-rewrite
|
||||
|
||||
Don't rewrite host and :authority header fields on
|
||||
:option:`--http2-bridge`\, :option:`--client` and default mode. For
|
||||
:option:`--http2-proxy` and :option:`\--client-proxy` mode, these headers
|
||||
will not be altered regardless of this option.
|
||||
|
||||
.. option:: --altsvc=<PROTOID,PORT[,HOST,[ORIGIN]]>
|
||||
|
||||
Specify protocol ID, port, host and origin of
|
||||
alternative service. <HOST> and <ORIGIN> are optional.
|
||||
They are advertised in alt-svc header field only in
|
||||
HTTP/1.1 frontend. This option can be used multiple
|
||||
times to specify multiple alternative services.
|
||||
Example: :option:`--altsvc`\=h2,443
|
||||
|
||||
.. option:: --add-request-header=<HEADER>
|
||||
|
||||
Specify additional header field to add to request header
|
||||
set. This option just appends header field and won't
|
||||
replace anything already set. This option can be used
|
||||
several times to specify multiple header fields.
|
||||
Example: :option:`--add-request-header`\="foo: bar"
|
||||
|
||||
.. option:: --add-response-header=<HEADER>
|
||||
|
||||
Specify additional header field to add to response
|
||||
header set. This option just appends header field and
|
||||
won't replace anything already set. This option can be
|
||||
used several times to specify multiple header fields.
|
||||
Example: :option:`--add-response-header`\="foo: bar"
|
||||
|
||||
.. option:: --header-field-buffer=<SIZE>
|
||||
|
||||
Set maximum buffer size for incoming HTTP header field
|
||||
list. This is the sum of header name and value in
|
||||
bytes.
|
||||
|
||||
Default: ``64K``
|
||||
|
||||
.. option:: --max-header-fields=<N>
|
||||
|
||||
Set maximum number of incoming HTTP header fields, which
|
||||
appear in one request or response header field list.
|
||||
|
||||
Default: ``100``
|
||||
|
||||
|
||||
Debug
|
||||
~~~~~
|
||||
|
||||
.. option:: --frontend-http2-dump-request-header=<PATH>
|
||||
|
||||
Dumps request headers received by HTTP/2 frontend to the
|
||||
file denoted in <PATH>. The output is done in HTTP/1
|
||||
header field format and each header block is followed by
|
||||
an empty line. This option is not thread safe and MUST
|
||||
NOT be used with option :option:`-n`\<N>, where <N> >= 2.
|
||||
|
||||
.. option:: --frontend-http2-dump-response-header=<PATH>
|
||||
|
||||
Dumps response headers sent from HTTP/2 frontend to the
|
||||
file denoted in <PATH>. The output is done in HTTP/1
|
||||
header field format and each header block is followed by
|
||||
an empty line. This option is not thread safe and MUST
|
||||
NOT be used with option :option:`-n`\<N>, where <N> >= 2.
|
||||
|
||||
.. option:: -o, --frontend-frame-debug
|
||||
|
||||
Print HTTP/2 frames in frontend to stderr. This option
|
||||
is not thread safe and MUST NOT be used with option
|
||||
:option:`-n`\=N, where N >= 2.
|
||||
|
||||
|
||||
Process
|
||||
~~~~~~~
|
||||
|
||||
.. option:: -D, --daemon
|
||||
|
||||
Run in a background. If :option:`-D` is used, the current working
|
||||
directory is changed to '*/*'.
|
||||
|
||||
.. option:: --pid-file=<PATH>
|
||||
|
||||
Set path to save PID of this program.
|
||||
|
||||
.. option:: --user=<USER>
|
||||
|
||||
Run this program as <USER>. This option is intended to
|
||||
be used to drop root privileges.
|
||||
|
||||
|
||||
Misc
|
||||
~~~~
|
||||
|
||||
.. option:: --conf=<PATH>
|
||||
|
||||
Load configuration from <PATH>.
|
||||
|
||||
Default: ``/etc/nghttpx/nghttpx.conf``
|
||||
|
||||
.. option:: -v, --version
|
||||
|
||||
Print version and exit.
|
||||
|
||||
.. option:: -h, --help
|
||||
|
||||
Print 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.
|
||||
|
||||
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)`
|
||||
@@ -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.
|
||||
@@ -1 +0,0 @@
|
||||
.. include:: ../doc/sources/python-apiref.rst
|
||||
1
doc/python-apiref.rst.in
Normal file
1
doc/python-apiref.rst.in
Normal file
@@ -0,0 +1 @@
|
||||
.. include:: @top_srcdir@/doc/sources/python-apiref.rst
|
||||
163
doc/sources/building-android-binary.rst
Normal file
163
doc/sources/building-android-binary.rst
Normal file
@@ -0,0 +1,163 @@
|
||||
Building Android binary
|
||||
=======================
|
||||
|
||||
In this article, we briefly describe how to build Android binary using
|
||||
`Android NDK <http://developer.android.com/tools/sdk/ndk/index.html>`_
|
||||
cross-compiler on Debian Linux.
|
||||
|
||||
The easiest way to build android binary is use Dockerfile.android.
|
||||
See Dockerfile.android for more details. If you cannot use
|
||||
Dockerfile.android for whatever reason, continue to read the rest of
|
||||
this article.
|
||||
|
||||
We offer ``android-config`` and ``android-make`` scripts to make the
|
||||
build easier. To make these script work, NDK toolchain must be
|
||||
installed in the following way. First, let us introduce
|
||||
``ANDROID_HOME`` environment variable. We need to install toolchain
|
||||
under ``$ANDROID_HOME/toolchain``. An user can freely choose the path
|
||||
for ``ANDROID_HOME``. For example, to install toolchain under
|
||||
``$ANDROID_HOME/toolchain``, do this in the the directory where NDK is
|
||||
unpacked::
|
||||
|
||||
$ build/tools/make-standalone-toolchain.sh \
|
||||
--install-dir=$ANDROID_HOME/toolchain \
|
||||
--toolchain=arm-linux-androideabi-4.9 \
|
||||
--llvm-version=3.5 \
|
||||
--platform=android-16
|
||||
|
||||
The additional flag ``--system=linux-x86_64`` may be required if you
|
||||
are using x86_64 system.
|
||||
|
||||
The platform level is not important here because we don't use Android
|
||||
specific C/C++ API.
|
||||
|
||||
The dependent libraries, such as OpenSSL and libev should be built
|
||||
with the toolchain and installed under ``$ANDROID_HOME/usr/local``.
|
||||
We recommend to build these libraries as static library to make the
|
||||
deployment easier. libxml2 support is currently disabled.
|
||||
|
||||
Although zlib comes with Android NDK, it seems not to be a part of
|
||||
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
|
||||
spdylay as well.
|
||||
|
||||
Before running ``android-config`` and ``android-make``,
|
||||
``ANDROID_HOME`` environment variable must be set to point to the
|
||||
correct path. Also add ``$ANDROID_HOME/toolchain/bin`` to ``PATH``::
|
||||
|
||||
$ export PATH=$PATH:$ANDROID_HOME/toolchain/bin
|
||||
|
||||
To configure OpenSSL, use the following script:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
#!/bin/sh
|
||||
|
||||
if [ -z "$ANDROID_HOME" ]; then
|
||||
echo 'No $ANDROID_HOME specified.'
|
||||
exit 1
|
||||
fi
|
||||
PREFIX=$ANDROID_HOME/usr/local
|
||||
TOOLCHAIN=$ANDROID_HOME/toolchain
|
||||
PATH=$TOOLCHAIN/bin:$PATH
|
||||
|
||||
export CROSS_COMPILE=$TOOLCHAIN/bin/arm-linux-androideabi-
|
||||
./Configure --prefix=$PREFIX android
|
||||
|
||||
And run ``make install`` to build and install.
|
||||
|
||||
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
|
||||
|
||||
if [ -z "$ANDROID_HOME" ]; then
|
||||
echo 'No $ANDROID_HOME specified.'
|
||||
exit 1
|
||||
fi
|
||||
PREFIX=$ANDROID_HOME/usr/local
|
||||
TOOLCHAIN=$ANDROID_HOME/toolchain
|
||||
PATH=$TOOLCHAIN/bin:$PATH
|
||||
|
||||
./configure \
|
||||
--host=arm-linux-androideabi \
|
||||
--build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \
|
||||
--prefix=$PREFIX \
|
||||
--disable-shared \
|
||||
--enable-static \
|
||||
CPPFLAGS=-I$PREFIX/include \
|
||||
LDFLAGS=-L$PREFIX/lib
|
||||
|
||||
And run ``make install`` to build and install.
|
||||
|
||||
To configure 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
|
||||
echo 'No $ANDROID_HOME specified.'
|
||||
exit 1
|
||||
fi
|
||||
PREFIX=$ANDROID_HOME/usr/local
|
||||
TOOLCHAIN=$ANDROID_HOME/toolchain
|
||||
PATH=$TOOLCHAIN/bin:$PATH
|
||||
|
||||
./configure \
|
||||
--disable-shared \
|
||||
--host=arm-linux-androideabi \
|
||||
--build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \
|
||||
--prefix=$PREFIX \
|
||||
--without-libxml2 \
|
||||
--disable-src \
|
||||
--disable-examples \
|
||||
CPPFLAGS="-I$PREFIX/include" \
|
||||
PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \
|
||||
LDFLAGS="-L$PREFIX/lib"
|
||||
|
||||
And run ``make install`` to build and install.
|
||||
|
||||
After prerequisite libraries are prepared, run ``android-config`` and
|
||||
then ``android-make`` to compile nghttp2 source files.
|
||||
|
||||
If all went well, application binaries, such as nghttpx, are created
|
||||
under src directory. Strip debugging information from the binary
|
||||
using the following command::
|
||||
|
||||
$ arm-linux-androideabi-strip src/nghttpx
|
||||
57
doc/sources/contribute.rst
Normal file
57
doc/sources/contribute.rst
Normal file
@@ -0,0 +1,57 @@
|
||||
Contribution Guidelines
|
||||
=======================
|
||||
|
||||
[This text was composed based on 1.2. License section of curl/libcurl
|
||||
project.]
|
||||
|
||||
When contributing with code, you agree to put your changes and new
|
||||
code under the same license nghttp2 is already using unless stated and
|
||||
agreed otherwise.
|
||||
|
||||
When changing existing source code, you do not alter the copyright of
|
||||
the original file(s). The copyright will still be owned by the
|
||||
original creator(s) or those who have been assigned copyright by the
|
||||
original author(s).
|
||||
|
||||
By submitting a patch to the nghttp2 project, you are assumed to have
|
||||
the right to the code and to be allowed by your employer or whatever
|
||||
to hand over that patch/code to us. We will credit you for your
|
||||
changes as far as possible, to give credit but also to keep a trace
|
||||
back to who made what changes. Please always provide us with your
|
||||
full real name when contributing!
|
||||
|
||||
Coding style
|
||||
------------
|
||||
|
||||
We use clang-format to format source code consistently. The
|
||||
clang-format configuration file .clang-format is located at the root
|
||||
directory. Since clang-format produces slightly different results
|
||||
between versions, we currently use clang-format which comes with
|
||||
clang-3.5.
|
||||
|
||||
To detect any violation to the coding style, we recommend to setup git
|
||||
pre-commit hook to check coding style of the changes you introduced.
|
||||
The pre-commit file is located at the root directory. Copy it under
|
||||
.git/hooks and make sure that it is executable. The pre-commit script
|
||||
uses clang-format-diff.py to detect any style errors. If it is not in
|
||||
your PATH or it exists under different name (e.g.,
|
||||
clang-format-diff-3.5 in debian), either add it to PATH variable or
|
||||
add git option ``clangformatdiff.binary`` to point to the script.
|
||||
|
||||
For emacs users, integrating clang-format to emacs is very easy.
|
||||
clang-format.el should come with clang distribution. If it is not
|
||||
found, download it from `here
|
||||
<https://llvm.org/svn/llvm-project/cfe/trunk/tools/clang-format/clang-format.el>`_.
|
||||
And add these lines to your .emacs file:
|
||||
|
||||
.. code-block:: lisp
|
||||
|
||||
;; From
|
||||
;; https://code.google.com/p/chromium/wiki/Emacs#Use_Google's_C++_style!
|
||||
(load "/<path/to>/clang-format.el")
|
||||
(add-hook 'c-mode-common-hook
|
||||
(function (lambda () (local-set-key (kbd "TAB")
|
||||
'clang-format-region))))
|
||||
|
||||
You can find other editor integration in
|
||||
http://clang.llvm.org/docs/ClangFormat.html.
|
||||
91
doc/sources/h2load-howto.rst
Normal file
91
doc/sources/h2load-howto.rst
Normal file
@@ -0,0 +1,91 @@
|
||||
h2load - HTTP/2 benchmarking tool - HOW-TO
|
||||
==========================================
|
||||
|
||||
h2load is benchmarking tool for HTTP/2. If built with
|
||||
spdylay (http://tatsuhiro-t.github.io/spdylay/) library, it also
|
||||
supports SPDY protocol. It supports SSL/TLS and clear text for both
|
||||
HTTP/2 and SPDY.
|
||||
|
||||
Basic Usage
|
||||
-----------
|
||||
|
||||
In order to set benchmark settings, specify following 3 options.
|
||||
|
||||
``-n``
|
||||
The number of total requests. Default: 1
|
||||
|
||||
``-c``
|
||||
The number of concurrent clients. Default: 1
|
||||
|
||||
``-m``
|
||||
The max concurrent streams to issue per client.
|
||||
If ``auto`` is given, the number of given URIs is used.
|
||||
Default: ``auto``
|
||||
|
||||
Here is a command-line to perform benchmark to URI \https://localhost
|
||||
using total 100000 requests, 100 concurrent clients and 10 max
|
||||
concurrent streams::
|
||||
|
||||
$ h2load -n100000 -c100 -m10 https://localhost
|
||||
|
||||
The benchmarking result looks like this::
|
||||
|
||||
finished in 0 sec, 385 millisec and 851 microsec, 2591 req/s, 1689 kbytes/s
|
||||
requests: 1000 total, 1000 started, 1000 done, 1000 succeeded, 0 failed, 0 errored
|
||||
status codes: 1000 2xx, 0 3xx, 0 4xx, 0 5xx
|
||||
traffic: 667500 bytes total, 28700 bytes headers, 612000 bytes data
|
||||
|
||||
The number of ``failed`` is the number of requests returned with non
|
||||
2xx status. The number of ``error`` is the number of ``failed`` plus
|
||||
the number of requests which failed with connection error.
|
||||
|
||||
The number of ``total`` in ``traffic`` is the received application
|
||||
data. If SSL/TLS is used, this number is calculated after decryption.
|
||||
The number of ``headers`` is the sum of payload size of response
|
||||
HEADERS (or SYN_REPLY for SPDY). This number comes before
|
||||
decompressing header block. The number of ``data`` is the sum of
|
||||
response body.
|
||||
|
||||
Flow Control
|
||||
------------
|
||||
|
||||
HTTP/2 and SPDY/3 or later employ flow control and it may affect
|
||||
benchmarking results. By default, h2load uses large enough flow
|
||||
control window, which effectively disables flow control. To adjust
|
||||
receiver flow control window size, there are following options:
|
||||
|
||||
``-w``
|
||||
Sets the stream level initial window size to
|
||||
(2**<N>)-1. For SPDY, 2**<N> is used instead.
|
||||
|
||||
``-W``
|
||||
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.
|
||||
|
||||
Multi-Threading
|
||||
---------------
|
||||
|
||||
Sometimes benchmarking client itself becomes a bottleneck. To remedy
|
||||
this situation, use ``-t`` option to specify the number of native
|
||||
thread to use.
|
||||
|
||||
``-t``
|
||||
The number of native threads. Default: 1
|
||||
|
||||
Selecting protocol for clear text
|
||||
---------------------------------
|
||||
|
||||
By default, if \http:// URI is given, HTTP/2 protocol is used. To
|
||||
change the protocol to use for clear text, use ``-p`` option.
|
||||
|
||||
Multiple URIs
|
||||
-------------
|
||||
|
||||
If multiple URIs are specified, they are used in round robin manner.
|
||||
|
||||
.. note::
|
||||
|
||||
Please note that h2load uses scheme, host and port in the first URI
|
||||
and ignores those parts in the rest of the URIs.
|
||||
@@ -17,28 +17,37 @@ Contents:
|
||||
:maxdepth: 2
|
||||
|
||||
package_README
|
||||
contribute
|
||||
building-android-binary
|
||||
tutorial-client
|
||||
tutorial-server
|
||||
tutorial-hpack
|
||||
nghttp.1
|
||||
nghttpd.1
|
||||
nghttpx.1
|
||||
h2load.1
|
||||
nghttpx-howto
|
||||
h2load-howto
|
||||
programmers-guide
|
||||
apiref
|
||||
libnghttp2_asio
|
||||
python-apiref
|
||||
nghttp2.h
|
||||
nghttp2ver.h
|
||||
asio_http2_server.h
|
||||
asio_http2_client.h
|
||||
asio_http2.h
|
||||
Source <https://github.com/tatsuhiro-t/nghttp2>
|
||||
Issues <https://github.com/tatsuhiro-t/nghttp2/issues>
|
||||
nghttp2.org <https://nghttp2.org/>
|
||||
|
||||
Released Versions
|
||||
=================
|
||||
|
||||
* `v0.3.2 <released-versions/v0.3.2/>`_ `(Download v0.3.2) <https://github.com/tatsuhiro-t/nghttp2/releases/tag/v0.3.2>`_
|
||||
* `v0.3.1 <released-versions/v0.3.1/>`_ `(Download v0.3.1) <https://github.com/tatsuhiro-t/nghttp2/releases/tag/v0.3.1>`_
|
||||
* `v0.3.0 <released-versions/v0.3.0/>`_ `(Download v0.3.0) <https://github.com/tatsuhiro-t/nghttp2/releases/tag/v0.3.0>`_
|
||||
* `v0.2.0 <released-versions/v0.2.0/>`_ `(Download v0.2.0) <https://github.com/tatsuhiro-t/nghttp2/releases/tag/v0.2.0>`_
|
||||
* `v0.1.0 <released-versions/v0.1.0/>`_ `(Download v0.1.0) <https://github.com/tatsuhiro-t/nghttp2/releases/tag/v0.1.0>`_
|
||||
|
||||
https://github.com/tatsuhiro-t/nghttp2/releases
|
||||
|
||||
Resources
|
||||
---------
|
||||
|
||||
* http://tools.ietf.org/html/draft-ietf-httpbis-http2-12
|
||||
* http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-07
|
||||
* HTTP/2 https://tools.ietf.org/html/rfc7540
|
||||
* HPACK https://tools.ietf.org/html/rfc7541
|
||||
|
||||
433
doc/sources/libnghttp2_asio.rst
Normal file
433
doc/sources/libnghttp2_asio.rst
Normal file
@@ -0,0 +1,433 @@
|
||||
libnghttp2_asio: High level HTTP/2 C++ library
|
||||
==============================================
|
||||
|
||||
libnghttp2_asio is C++ library built on top of libnghttp2 and provides
|
||||
high level abstraction API to build HTTP/2 applications. It depends
|
||||
on Boost::ASIO library and OpenSSL. Currently libnghttp2_asio
|
||||
provides server and client side API.
|
||||
|
||||
libnghttp2_asio is not built by default. Use ``--enable-asio-lib``
|
||||
configure flag to build libnghttp2_asio. The required Boost libraries
|
||||
are:
|
||||
|
||||
* Boost::Asio
|
||||
* Boost::System
|
||||
* Boost::Thread
|
||||
|
||||
We have 3 header files for this library:
|
||||
|
||||
* :doc:`asio_http2_server.h`
|
||||
* :doc:`asio_http2_client.h`
|
||||
* :doc:`asio_http2.h`
|
||||
|
||||
asio_http2.h is included from the other two files.
|
||||
|
||||
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
|
||||
----------
|
||||
|
||||
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
|
||||
C++11 anonymous function and closure. The bare minimum example of
|
||||
HTTP/2 server looks like this:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
using namespace nghttp2::asio_http2;
|
||||
using namespace nghttp2::asio_http2::server;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
boost::system::error_code ec;
|
||||
http2 server;
|
||||
|
||||
server.handle("/", [](const request &req, const response &res) {
|
||||
res.write_head(200);
|
||||
res.end("hello, world\n");
|
||||
});
|
||||
|
||||
if (server.listen_and_serve(ec, "localhost", "3000")) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
First we instantiate ``nghttp2::asio_http2::server::http2`` object.
|
||||
``nghttp2::asio_http2::server::http2::handle`` function registers
|
||||
pattern and its handler function. In this example, we register "/" as
|
||||
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
|
||||
respectively. ``nghttp2::asio_http2_::server::response::write_head``
|
||||
constructs HTTP response header fields. The first argument is HTTP
|
||||
status code, in the above example, which is 200. The second argument,
|
||||
which is omitted in the above example, is additional header fields to
|
||||
send.
|
||||
|
||||
``nghttp2::asio_http2::server::response::end`` sends response body.
|
||||
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
|
||||
+++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
In this example, we serve a couple of static files and also enable
|
||||
SSL/TLS.
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#include <nghttp2/asio_http2_server.h>
|
||||
|
||||
using namespace nghttp2::asio_http2;
|
||||
using namespace nghttp2::asio_http2::server;
|
||||
|
||||
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;
|
||||
|
||||
server.handle("/index.html", [](const request &req, const response &res) {
|
||||
res.write_head(200);
|
||||
res.end(file_generator("index.html"));
|
||||
});
|
||||
|
||||
if (server.listen_and_serve(ec, tls, "localhost", "3000")) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
We first create ``boost::asio::ssl::context`` object and set path to
|
||||
private key file and certificate file.
|
||||
``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 "/index.html", we serve
|
||||
index.html file in the current working directory.
|
||||
``nghttp2::asio_http2::server::response::end`` has overload to take
|
||||
function of type ``nghttp2::asio_http2::generator_cb`` and application
|
||||
pass its implementation to generate response body. For the
|
||||
convenience, libnghttp2_asio library provides
|
||||
``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 is also supported.
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#include <nghttp2/asio_http2_server.h>
|
||||
|
||||
using namespace nghttp2::asio_http2;
|
||||
using namespace nghttp2::asio_http2::server;
|
||||
|
||||
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;
|
||||
|
||||
std::string style_css = "h1 { color: green; }";
|
||||
|
||||
server.handle("/", [&style_css](const request &req, const response &res) {
|
||||
boost::system::error_code ec;
|
||||
auto push = res.push(ec, "GET", "/style.css");
|
||||
push->write_head(200);
|
||||
push->end(style_css);
|
||||
|
||||
res.write_head(200);
|
||||
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>
|
||||
)");
|
||||
});
|
||||
|
||||
server.handle("/style.css",
|
||||
[&style_css](const request &req, const response &res) {
|
||||
res.write_head(200);
|
||||
res.end(style_css);
|
||||
});
|
||||
|
||||
if (server.listen_and_serve(ec, tls, "localhost", "3000")) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
When client requested any resource other than "/style.css", we push
|
||||
"/style.css". To push resource, call
|
||||
``nghttp2::asio_http2::server::response::push`` function with desired
|
||||
method and path. It returns another response object and use its
|
||||
functions to send push response.
|
||||
|
||||
Enable multi-threading
|
||||
++++++++++++++++++++++
|
||||
|
||||
Enabling multi-threading is very easy. Just call
|
||||
``nghttp2::asio_http2::server::http2::num_threads`` function with the
|
||||
desired number of threads:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
http2 server;
|
||||
|
||||
// Use 4 native threads
|
||||
server.num_threads(4);
|
||||
|
||||
Client API
|
||||
----------
|
||||
|
||||
To use client API, first include following header file:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#include <nghttp2/asio_http2_client.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::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 req = sess.submit(ec, "GET", "http://localhost:3000/");
|
||||
|
||||
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;
|
||||
|
||||
res.on_data([](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) {
|
||||
// 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();
|
||||
}
|
||||
|
||||
``nghttp2::asio_http2::client::session`` object takes
|
||||
``boost::asio::io_service`` object and remote server address. When
|
||||
connection is made, the callback function passed to
|
||||
``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.
|
||||
|
||||
The life time of req and res object ends after the callback set by
|
||||
``nghttp2::asio_http2::server::request::on_close`` function.
|
||||
Application must not use those objects after this call.
|
||||
|
||||
Normally, client does not stop even after all requests are done unless
|
||||
connection is lost. To stop client, call
|
||||
``nghttp2::asio_http2::server::session::shutdown()``.
|
||||
|
||||
Recieve server push and enable SSL/TLS
|
||||
++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
.. 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.
|
||||
@@ -1,5 +1,5 @@
|
||||
nghttpx - HOW-TO
|
||||
================
|
||||
nghttpx - HTTP/2 proxy - HOW-TO
|
||||
===============================
|
||||
|
||||
nghttpx is a proxy translating protocols between HTTP/2 and other
|
||||
protocols (e.g., HTTP/1, SPDY). It operates in several modes and each
|
||||
@@ -21,7 +21,7 @@ SSL/TLS, the frontend also supports SPDY protocol.
|
||||
By default, this mode's frontend connection is encrypted using
|
||||
SSL/TLS. So server's private key and certificate must be supplied to
|
||||
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
|
||||
frontend connection. In this case, SPDY protocol is not available
|
||||
@@ -47,37 +47,37 @@ HTTP/2 proxy mode
|
||||
|
||||
If nghttpx is invoked with ``-s`` option, it operates in HTTP/2 proxy
|
||||
mode. The supported protocols in frontend and backend connections are
|
||||
the same in `default mode`_. The difference is that this mode acts like
|
||||
forward proxy and assumes the backend is HTTP/1 proxy server (e.g.,
|
||||
squid). So HTTP/1 request must include absolute URI in request line.
|
||||
the same in `default mode`_. The difference is that this mode acts
|
||||
like forward proxy and assumes the backend is HTTP/1 proxy server
|
||||
(e.g., squid, traffic server). So HTTP/1 request must include
|
||||
absolute URI in request line.
|
||||
|
||||
By default, frontend connection is encrypted, this mode is also called
|
||||
secure proxy. If nghttpx is linked with spdylay, it supports SPDY
|
||||
protocols and it works as so called SPDY proxy.
|
||||
By default, frontend connection is encrypted. So this mode is also
|
||||
called secure proxy. If nghttpx is linked with spdylay, it supports
|
||||
SPDY protocols and it works as so called SPDY proxy.
|
||||
|
||||
With ``--frontend-no-tls`` option, SSL/TLS is turned off in frontend
|
||||
connection, so the connection gets insecure.
|
||||
|
||||
The backend must be HTTP/1 proxy server. nghttpx only supports 1
|
||||
backend server address. It translates incoming requests to HTTP/1
|
||||
The backend must be HTTP/1 proxy server. nghttpx supports multiple
|
||||
backend server addresses. It translates incoming requests to HTTP/1
|
||||
request to backend server. The backend server performs real proxy
|
||||
work for each request, for example, dispatching requests to the origin
|
||||
server and caching contents.
|
||||
|
||||
For example, to make nghttpx listen to encrypted HTTP/2 requests at
|
||||
port 8443, and a backend HTTP/1 proxy server is configured to listen
|
||||
to HTTP/1 request at port 3128 in the same host, run nghttpx
|
||||
to HTTP/1 request at port 8080 in the same host, run nghttpx
|
||||
command-line like this::
|
||||
|
||||
$ nghttpx -s -f0.0.0.0,8443 -b127.0.0.1,3128 /path/to/server.key /path/to/server.crt
|
||||
$ nghttpx -s -f'*,8443' -b127.0.0.1,8080 /path/to/server.key /path/to/server.crt
|
||||
|
||||
At the time of this writing, there is no known HTTP/2 client which
|
||||
supports HTTP/2 proxy in this fashion. You can use Google Chrome to
|
||||
use this as secure (SPDY) proxy to test it out, though it does not use
|
||||
HTTP/2 at all.
|
||||
At the time of this writing, Firefox nightly supports HTTP/2 proxy.
|
||||
Chromium can use nghttpx as secure (SPDY) proxy and will support
|
||||
HTTP/2 proxy in the near future.
|
||||
|
||||
The one way to configure Google Chrome to use secure proxy is create
|
||||
proxy.pac script like this:
|
||||
To make Firefox nightly or Chromium use nghttpx as HTTP/2 or SPDY
|
||||
proxy, user has to create proxy.pac script file like this:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
@@ -86,13 +86,32 @@ proxy.pac script like this:
|
||||
}
|
||||
|
||||
``SERVERADDR`` and ``PORT`` is the hostname/address and port of the
|
||||
machine nghttpx is running. Please note that Google Chrome requires
|
||||
valid certificate for secure proxy.
|
||||
machine nghttpx is running. Please note that both Firefox nightly and
|
||||
Chromium require valid certificate for secure proxy.
|
||||
|
||||
Then run Google Chrome with the following arguments::
|
||||
For Firefox nightly, open Preference window and select Advanced then
|
||||
click Network tab. Clicking Connection Settings button will show the
|
||||
dialog. Select "Automatic proxy configuration URL" and enter the path
|
||||
to proxy.pac file, something like this::
|
||||
|
||||
file:///path/to/proxy.pac
|
||||
|
||||
For Chromium, use following command-line::
|
||||
|
||||
$ google-chrome --proxy-pac-url=file:///path/to/proxy.pac --use-npn
|
||||
|
||||
As HTTP/1 proxy server, Squid may work as out-of-box. Traffic server
|
||||
requires to be configured as forward proxy. Here is the minimum
|
||||
configuration items to edit::
|
||||
|
||||
CONFIG proxy.config.reverse_proxy.enabled INT 0
|
||||
CONFIG proxy.config.url_remap.remap_required INT 0
|
||||
|
||||
Consult Traffic server `documentation
|
||||
<https://docs.trafficserver.apache.org/en/latest/admin/forward-proxy.en.html>`_
|
||||
to know how to configure traffic server as forward proxy and its
|
||||
security implications.
|
||||
|
||||
Client mode
|
||||
-----------
|
||||
|
||||
@@ -105,7 +124,9 @@ HTTP/1 frontend connection can be upgraded to HTTP/2 using HTTP
|
||||
Upgrade. To disable SSL/TLS in backend connection, use
|
||||
``--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.,
|
||||
nghttpd). The one use-case of this mode is utilize existing HTTP/1
|
||||
@@ -137,7 +158,9 @@ HTTP/1 frontend connection can be upgraded to HTTP/2 using HTTP
|
||||
Upgrade. To disable SSL/TLS in backend connection, use
|
||||
``--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
|
||||
`HTTP/2 proxy mode`_ as backend server. The one use-case of this mode
|
||||
@@ -155,7 +178,7 @@ that server, invoke nghttpx like this::
|
||||
|
||||
.. note::
|
||||
|
||||
You may need ``-k`` option if HTTP/2 server'ss certificate is
|
||||
You may need ``-k`` option if HTTP/2 server's certificate is
|
||||
self-signed. But please note that it is insecure.
|
||||
|
||||
Then you can use curl to issue HTTP request via HTTP/2 proxy::
|
||||
@@ -169,16 +192,22 @@ HTTP/2 bridge mode
|
||||
------------------
|
||||
|
||||
If nghttpx is invoked with ``--http2-bridge`` option, it operates in
|
||||
HTTP/2 bridge mode. The supported protocols in frontend and backend
|
||||
connections are the same in `default mode`_.
|
||||
HTTP/2 bridge mode. The supported protocols in frontend connections
|
||||
are the same in `default mode`_. The protocol in backend is HTTP/2
|
||||
only.
|
||||
|
||||
With ``--frontend-no-tls`` option, SSL/TLS is turned off in frontend
|
||||
connection, so the connection gets insecure.
|
||||
connection, so the connection gets insecure. To disable SSL/TLS in
|
||||
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
|
||||
proxy. Since HTTP/2 requests opaque between proxied and non-proxied
|
||||
request, the backend server may be proxy or just web server depending
|
||||
on the context of incoming requests.
|
||||
proxy. If backend server is HTTP/2 proxy, use
|
||||
``--no-location-rewrite`` and ``--no-host-rewrite`` options to disable
|
||||
rewriting location, host and :authority header field.
|
||||
|
||||
The use-case of this mode is aggregate the incoming connections to one
|
||||
HTTP/2 connection. One backend HTTP/2 connection is created per
|
||||
@@ -214,19 +243,61 @@ Read/write rate limit
|
||||
---------------------
|
||||
|
||||
nghttpx supports transfer rate limiting on frontend connections. You
|
||||
can do rate limit per connection or per worker (thread) for reading
|
||||
and writeing individually.
|
||||
can do rate limit per frontend connection for reading and writing
|
||||
individually.
|
||||
|
||||
To rate limit per connection for reading, use ``--read-rate`` and
|
||||
To perform rate limit for reading, use ``--read-rate`` and
|
||||
``--read-burst`` options. For writing, use ``--write-rate`` and
|
||||
``--write-burst`` options.
|
||||
|
||||
To rate limit per worker (thread), use ``--worker-read-rate`` and
|
||||
``--worker-read-burst`` options. For writing, use
|
||||
``--worker-write-rate`` and ``--worker-write-burst``.
|
||||
|
||||
If both per connection and per worker rate limit configurations are
|
||||
specified, the lower rate is used.
|
||||
``--write-burst``.
|
||||
|
||||
Please note that rate limit is performed on top of TCP and nothing to
|
||||
do with HTTP/2 flow control.
|
||||
|
||||
Rewriting location header field
|
||||
-------------------------------
|
||||
|
||||
nghttpx automatically rewrites location response header field if the
|
||||
following all conditions satisfy:
|
||||
|
||||
* URI in location header field is not absolute URI or is not https URI.
|
||||
* URI in location header field includes non empty host component.
|
||||
* host (without port) in URI in location header field must match the
|
||||
host appearing in :authority or host header field.
|
||||
|
||||
When rewrite happens, URI scheme and port are replaced with the ones
|
||||
used in frontend, and host is replaced with which appears in
|
||||
:authority or host request header field. :authority header field has
|
||||
precedence. If the above conditions are not met with the host value
|
||||
in :authority header field, rewrite is retried with the value in host
|
||||
header field.
|
||||
|
||||
Hot swapping
|
||||
------------
|
||||
|
||||
nghttpx supports hot swapping using signals. The hot swapping in
|
||||
nghttpx is multi step process. First send USR2 signal to nghttpx
|
||||
process. It will do fork and execute new executable, using same
|
||||
command-line arguments and environment variables. At this point, both
|
||||
current and new processes can accept requests. To gracefully shutdown
|
||||
current process, send QUIT signal to current nghttpx process. When
|
||||
all existing frontend connections are done, the current process will
|
||||
exit. At this point, only new nghttpx process exists and serves
|
||||
incoming requests.
|
||||
|
||||
Re-opening log files
|
||||
--------------------
|
||||
|
||||
When rotating log files, it is desirable to re-open log files after
|
||||
log rotation daemon renamed existing log files. To tell nghttpx to
|
||||
re-open log files, send USR1 signal to nghttpx process. It will
|
||||
re-open files specified by ``--accesslog-file`` and
|
||||
``--errorlog-file`` options.
|
||||
|
||||
Multiple 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.
|
||||
|
||||
.. py:attribute:: headers
|
||||
|
||||
Request header fields.
|
||||
|
||||
A :py:class:`BaseRequestHandler` has the following methods:
|
||||
|
||||
.. py:method:: on_headers()
|
||||
@@ -249,10 +253,28 @@ HTTP/2 servers
|
||||
Send response. The *status* is HTTP status code. The *headers*
|
||||
is additional response headers. The *:status* header field will
|
||||
be appended by the library. The *body* is the response body.
|
||||
It could be ``None`` if response body is empty. Or it must be
|
||||
instance of either ``str``, ``bytes`` or :py:class:`io.IOBase`.
|
||||
If instance of ``str`` is specified, it will be encoded using
|
||||
UTF-8.
|
||||
It could be ``None`` if response body is empty. Or it must be
|
||||
instance of either ``str``, ``bytes``, :py:class:`io.IOBase` or
|
||||
callable, called body generator, which takes one parameter,
|
||||
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,
|
||||
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
|
||||
response headers. The ``:status`` header field is appended by
|
||||
the library. The *body* is the response body. It could be
|
||||
``None`` if response body is empty. Or it must be instance of
|
||||
either ``str``, ``bytes`` or ``io.IOBase``. If instance of
|
||||
``str`` is specified, it is encoded using UTF-8.
|
||||
the library. The *body* is the response body. It has the same
|
||||
semantics of *body* parameter of :py:meth:`send_response()`.
|
||||
|
||||
The headers and request_headers are a list of tuple of the form
|
||||
``(name, value)``. The ``name`` and ``value`` can be either byte
|
||||
@@ -288,6 +308,27 @@ HTTP/2 servers
|
||||
|
||||
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
|
||||
:py:class:`BaseRequestHandler` usage:
|
||||
|
||||
@@ -296,6 +337,7 @@ The following example illustrates :py:class:`HTTP2Server` and
|
||||
#!/usr/bin/env python
|
||||
|
||||
import io, ssl
|
||||
|
||||
import nghttp2
|
||||
|
||||
class Handler(nghttp2.BaseRequestHandler):
|
||||
@@ -311,9 +353,85 @@ The following example illustrates :py:class:`HTTP2Server` and
|
||||
body=io.BytesIO(b'nghttp2-python FTW'))
|
||||
|
||||
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')
|
||||
|
||||
# give None to ssl to make the server non-SSL/TLS
|
||||
server = nghttp2.HTTP2Server(('127.0.0.1', 8443), Handler, ssl=ctx)
|
||||
server.serve_forever()
|
||||
|
||||
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
|
||||
protocol the library supports::
|
||||
|
||||
static int select_next_proto_cb(SSL* ssl,
|
||||
unsigned char **out, unsigned char *outlen,
|
||||
const unsigned char *in, unsigned int inlen,
|
||||
void *arg)
|
||||
{
|
||||
if(nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) {
|
||||
static int select_next_proto_cb(SSL *ssl _U_, unsigned char **out,
|
||||
unsigned char *outlen, const unsigned char *in,
|
||||
unsigned int inlen, void *arg _U_) {
|
||||
if (nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) {
|
||||
errx(1, "Server did not advertise " NGHTTP2_PROTO_VERSION_ID);
|
||||
}
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
@@ -36,17 +34,17 @@ protocol the library supports::
|
||||
The callback is set to the SSL_CTX object using
|
||||
``SSL_CTX_set_next_proto_select_cb()`` function::
|
||||
|
||||
static SSL_CTX* create_ssl_ctx(void)
|
||||
{
|
||||
static SSL_CTX *create_ssl_ctx(void) {
|
||||
SSL_CTX *ssl_ctx;
|
||||
ssl_ctx = SSL_CTX_new(SSLv23_client_method());
|
||||
if(!ssl_ctx) {
|
||||
if (!ssl_ctx) {
|
||||
errx(1, "Could not create SSL/TLS context: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
SSL_CTX_set_options(ssl_ctx,
|
||||
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_COMPRESSION |
|
||||
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
|
||||
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
|
||||
SSL_OP_NO_COMPRESSION |
|
||||
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
|
||||
SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL);
|
||||
return ssl_ctx;
|
||||
}
|
||||
@@ -67,11 +65,11 @@ its stream specific data in ``http2_stream_data`` structure and the
|
||||
defined as follows::
|
||||
|
||||
typedef struct {
|
||||
/* The NULL-terminated URI string to retreive. */
|
||||
/* The NULL-terminated URI string to retrieve. */
|
||||
const char *uri;
|
||||
/* Parsed result of the |uri| */
|
||||
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;
|
||||
/* The path portion of the |uri|, including query, not
|
||||
NULL-terminated */
|
||||
@@ -91,25 +89,22 @@ respectively.
|
||||
Then we call function ``initiate_connection()`` to start connecting to
|
||||
the remote server::
|
||||
|
||||
static void initiate_connection(struct event_base *evbase,
|
||||
SSL_CTX *ssl_ctx,
|
||||
static void initiate_connection(struct event_base *evbase, SSL_CTX *ssl_ctx,
|
||||
const char *host, uint16_t port,
|
||||
http2_session_data *session_data)
|
||||
{
|
||||
http2_session_data *session_data) {
|
||||
int rv;
|
||||
struct bufferevent *bev;
|
||||
SSL *ssl;
|
||||
|
||||
ssl = create_ssl(ssl_ctx);
|
||||
bev = bufferevent_openssl_socket_new(evbase, -1, ssl,
|
||||
BUFFEREVENT_SSL_CONNECTING,
|
||||
BEV_OPT_DEFER_CALLBACKS |
|
||||
BEV_OPT_CLOSE_ON_FREE);
|
||||
bev = bufferevent_openssl_socket_new(
|
||||
evbase, -1, ssl, BUFFEREVENT_SSL_CONNECTING,
|
||||
BEV_OPT_DEFER_CALLBACKS | BEV_OPT_CLOSE_ON_FREE);
|
||||
bufferevent_setcb(bev, readcb, writecb, eventcb, session_data);
|
||||
rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase,
|
||||
AF_UNSPEC, host, port);
|
||||
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
errx(1, "Could not connect to the remote host %s", host);
|
||||
}
|
||||
session_data->bev = bev;
|
||||
@@ -122,10 +117,9 @@ The ``eventcb()`` is invoked by libevent event loop when an event
|
||||
(e.g., connection has been established, timeout, etc) happens on the
|
||||
underlying network socket::
|
||||
|
||||
static void eventcb(struct bufferevent *bev, short events, void *ptr)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
||||
if(events & BEV_EVENT_CONNECTED) {
|
||||
static void eventcb(struct bufferevent *bev, short events, void *ptr) {
|
||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||
if (events & BEV_EVENT_CONNECTED) {
|
||||
int fd = bufferevent_getfd(bev);
|
||||
int val = 1;
|
||||
fprintf(stderr, "Connected\n");
|
||||
@@ -133,16 +127,16 @@ underlying network socket::
|
||||
initialize_nghttp2_session(session_data);
|
||||
send_client_connection_header(session_data);
|
||||
submit_request(session_data);
|
||||
if(session_send(session_data) != 0) {
|
||||
if (session_send(session_data) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(events & BEV_EVENT_EOF) {
|
||||
if (events & BEV_EVENT_EOF) {
|
||||
warnx("Disconnected from the remote host");
|
||||
} else if(events & BEV_EVENT_ERROR) {
|
||||
} else if (events & BEV_EVENT_ERROR) {
|
||||
warnx("Network error");
|
||||
} else if(events & BEV_EVENT_TIMEOUT) {
|
||||
} else if (events & BEV_EVENT_TIMEOUT) {
|
||||
warnx("Timeout");
|
||||
}
|
||||
delete_http2_session_data(session_data);
|
||||
@@ -154,17 +148,31 @@ event, we just simply tear down the connection. The
|
||||
finished successfully. We first initialize nghttp2 session object in
|
||||
``initialize_nghttp2_session()`` function::
|
||||
|
||||
static void initialize_nghttp2_session(http2_session_data *session_data)
|
||||
{
|
||||
nghttp2_session_callbacks callbacks = {0};
|
||||
static void initialize_nghttp2_session(http2_session_data *session_data) {
|
||||
nghttp2_session_callbacks *callbacks;
|
||||
|
||||
callbacks.send_callback = send_callback;
|
||||
callbacks.on_frame_recv_callback = on_frame_recv_callback;
|
||||
callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback;
|
||||
callbacks.on_stream_close_callback = on_stream_close_callback;
|
||||
callbacks.on_header_callback = on_header_callback;
|
||||
callbacks.on_begin_headers_callback = on_begin_headers_callback;
|
||||
nghttp2_session_client_new(&session_data->session, &callbacks, session_data);
|
||||
nghttp2_session_callbacks_new(&callbacks);
|
||||
|
||||
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
|
||||
on_frame_recv_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
|
||||
callbacks, on_data_chunk_recv_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_stream_close_callback(
|
||||
callbacks, on_stream_close_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_header_callback(callbacks,
|
||||
on_header_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_begin_headers_callback(
|
||||
callbacks, on_begin_headers_callback);
|
||||
|
||||
nghttp2_session_client_new(&session_data->session, callbacks, session_data);
|
||||
|
||||
nghttp2_session_callbacks_del(callbacks);
|
||||
}
|
||||
|
||||
Since we are creating client, we use `nghttp2_session_client_new()` to
|
||||
@@ -176,30 +184,26 @@ its bufferevent, so it closes underlying connection as well. It also
|
||||
calls `nghttp2_session_del()` to delete nghttp2 session object.
|
||||
|
||||
We begin HTTP/2 communication by sending client connection preface,
|
||||
which is 24 bytes magic byte sequence
|
||||
(:macro:`NGHTTP2_CLIENT_CONNECTION_PREFACE`) and SETTINGS frame. The
|
||||
transmission of client connection header is done in
|
||||
which is 24 bytes magic byte string (:macro:`NGHTTP2_CLIENT_MAGIC`)
|
||||
followed by SETTINGS frame. First 24 bytes magic string is
|
||||
automatically sent by nghttp2 library. We send SETTINGS frame in
|
||||
``send_client_connection_header()``::
|
||||
|
||||
static void send_client_connection_header(http2_session_data *session_data)
|
||||
{
|
||||
static void send_client_connection_header(http2_session_data *session_data) {
|
||||
nghttp2_settings_entry iv[1] = {
|
||||
{ NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 }
|
||||
};
|
||||
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
|
||||
int rv;
|
||||
|
||||
bufferevent_write(session_data->bev,
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
|
||||
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE,
|
||||
iv, ARRLEN(iv));
|
||||
if(rv != 0) {
|
||||
/* client 24 bytes magic string will be sent by nghttp2 library */
|
||||
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
|
||||
ARRLEN(iv));
|
||||
if (rv != 0) {
|
||||
errx(1, "Could not submit SETTINGS: %s", nghttp2_strerror(rv));
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
for the transmission, we use `nghttp2_submit_settings()`. Note that
|
||||
`nghttp2_submit_settings()` function only queues the frame and not
|
||||
@@ -210,24 +214,22 @@ used, which is described about later.
|
||||
After the transmission of client connection header, we enqueue HTTP
|
||||
request in ``submit_request()`` function::
|
||||
|
||||
static void submit_request(http2_session_data *session_data)
|
||||
{
|
||||
static void submit_request(http2_session_data *session_data) {
|
||||
int32_t stream_id;
|
||||
http2_stream_data *stream_data = session_data->stream_data;
|
||||
const char *uri = stream_data->uri;
|
||||
const struct http_parser_url *u = stream_data->u;
|
||||
nghttp2_nv hdrs[] = {
|
||||
MAKE_NV2(":method", "GET"),
|
||||
MAKE_NV(":scheme",
|
||||
&uri[u->field_data[UF_SCHEMA].off], u->field_data[UF_SCHEMA].len),
|
||||
MAKE_NV(":authority", stream_data->authority, stream_data->authoritylen),
|
||||
MAKE_NV(":path", stream_data->path, stream_data->pathlen)
|
||||
};
|
||||
MAKE_NV2(":method", "GET"),
|
||||
MAKE_NV(":scheme", &uri[u->field_data[UF_SCHEMA].off],
|
||||
u->field_data[UF_SCHEMA].len),
|
||||
MAKE_NV(":authority", stream_data->authority, stream_data->authoritylen),
|
||||
MAKE_NV(":path", stream_data->path, stream_data->pathlen)};
|
||||
fprintf(stderr, "Request headers:\n");
|
||||
print_headers(stderr, hdrs, ARRLEN(hdrs));
|
||||
stream_id = nghttp2_submit_request(session_data->session, NULL,
|
||||
hdrs, ARRLEN(hdrs), NULL, stream_data);
|
||||
if(stream_id < 0) {
|
||||
stream_id = nghttp2_submit_request(session_data->session, NULL, hdrs,
|
||||
ARRLEN(hdrs), NULL, stream_data);
|
||||
if (stream_id < 0) {
|
||||
errx(1, "Could not submit HTTP request: %s", nghttp2_strerror(stream_id));
|
||||
}
|
||||
|
||||
@@ -246,21 +248,25 @@ this request.
|
||||
The next bufferevent callback is ``readcb()``, which is invoked when
|
||||
data is available to read in the bufferevent input buffer::
|
||||
|
||||
static void readcb(struct bufferevent *bev, void *ptr)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
||||
int rv;
|
||||
static void readcb(struct bufferevent *bev, void *ptr) {
|
||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||
ssize_t readlen;
|
||||
struct evbuffer *input = bufferevent_get_input(bev);
|
||||
size_t datalen = evbuffer_get_length(input);
|
||||
unsigned char *data = evbuffer_pullup(input, -1);
|
||||
rv = nghttp2_session_mem_recv(session_data->session, data, datalen);
|
||||
if(rv < 0) {
|
||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||
|
||||
readlen = nghttp2_session_mem_recv(session_data->session, data, datalen);
|
||||
if (readlen < 0) {
|
||||
warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
evbuffer_drain(input, rv);
|
||||
if(session_send(session_data) != 0) {
|
||||
if (evbuffer_drain(input, readlen) != 0) {
|
||||
warnx("Fatal error: evbuffer_drain failed");
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
if (session_send(session_data) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
@@ -273,12 +279,11 @@ invoke nghttp2 callbacks and also queue frames. Since there may be
|
||||
pending frames, we call ``session_send()`` function to send those
|
||||
frames. The ``session_send()`` function is defined as follows::
|
||||
|
||||
static int session_send(http2_session_data *session_data)
|
||||
{
|
||||
static int session_send(http2_session_data *session_data) {
|
||||
int rv;
|
||||
|
||||
rv = nghttp2_session_send(session_data->session);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||
return -1;
|
||||
}
|
||||
@@ -286,17 +291,13 @@ frames. The ``session_send()`` function is defined as follows::
|
||||
}
|
||||
|
||||
The `nghttp2_session_send()` function serializes the frame into wire
|
||||
format and call :member:`nghttp2_session_callbacks.send_callback` with
|
||||
it. We set ``send_callback()`` function to
|
||||
:member:`nghttp2_session_callbacks.send_callback` in
|
||||
``initialize_nghttp2_session()`` function described earlier. It is
|
||||
defined as follows::
|
||||
format and call ``send_callback()`` function of type
|
||||
:type:`nghttp2_send_callback`. The ``send_callback()`` is defined as
|
||||
follows::
|
||||
|
||||
static ssize_t send_callback(nghttp2_session *session,
|
||||
const uint8_t *data, size_t length,
|
||||
int flags, void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
|
||||
size_t length, int flags _U_, void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
struct bufferevent *bev = session_data->bev;
|
||||
bufferevent_write(bev, data, length);
|
||||
return length;
|
||||
@@ -306,25 +307,23 @@ Since we use bufferevent to abstract network I/O, we just write the
|
||||
data to the bufferevent object. Note that `nghttp2_session_send()`
|
||||
continues to write all frames queued so far. If we were writing the
|
||||
data to the non-blocking socket directly using ``write()`` system call
|
||||
in the :member:`nghttp2_session_callbacks.send_callback`, we will
|
||||
surely get ``EAGAIN`` or ``EWOULDBLOCK`` since the socket has limited
|
||||
send buffer. If that happens, we can return
|
||||
:macro:`NGHTTP2_ERR_WOULDBLOCK` to signal the nghttp2 library to stop
|
||||
sending further data. But writing to the bufferevent, we have to
|
||||
regulate the amount data to be buffered by ourselves to avoid possible
|
||||
huge memory consumption. In this example client, we do not limit
|
||||
anything. To see how to regulate the amount of buffered data, see the
|
||||
``send_callback()`` in the server tutorial.
|
||||
in the ``send_callback()``, we will surely get ``EAGAIN`` or
|
||||
``EWOULDBLOCK`` since the socket has limited send buffer. If that
|
||||
happens, we can return :macro:`NGHTTP2_ERR_WOULDBLOCK` to signal the
|
||||
nghttp2 library to stop sending further data. But writing to the
|
||||
bufferevent, we have to regulate the amount data to be buffered by
|
||||
ourselves to avoid possible huge memory consumption. In this example
|
||||
client, we do not limit anything. To see how to regulate the amount of
|
||||
buffered data, see the ``send_callback()`` in the server tutorial.
|
||||
|
||||
The third bufferevent callback is ``writecb()``, which is invoked when
|
||||
all data written in the bufferevent output buffer have been sent::
|
||||
|
||||
static void writecb(struct bufferevent *bev, void *ptr)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
||||
if(nghttp2_session_want_read(session_data->session) == 0 &&
|
||||
nghttp2_session_want_write(session_data->session) == 0 &&
|
||||
evbuffer_get_length(bufferevent_get_output(session_data->bev)) == 0) {
|
||||
static void writecb(struct bufferevent *bev _U_, void *ptr) {
|
||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||
if (nghttp2_session_want_read(session_data->session) == 0 &&
|
||||
nghttp2_session_want_write(session_data->session) == 0 &&
|
||||
evbuffer_get_length(bufferevent_get_output(session_data->bev)) == 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
}
|
||||
}
|
||||
@@ -350,18 +349,16 @@ Let's describe remaining nghttp2 callbacks we setup in
|
||||
Each request header name/value pair is emitted via
|
||||
``on_header_callback`` function::
|
||||
|
||||
static int on_header_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame,
|
||||
const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen,
|
||||
uint8_t flags,
|
||||
void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
switch(frame->hd.type) {
|
||||
static int on_header_callback(nghttp2_session *session _U_,
|
||||
const nghttp2_frame *frame, const uint8_t *name,
|
||||
size_t namelen, const uint8_t *value,
|
||||
size_t valuelen, uint8_t flags _U_,
|
||||
void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
switch (frame->hd.type) {
|
||||
case NGHTTP2_HEADERS:
|
||||
if(frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
|
||||
session_data->stream_data->stream_id == frame->hd.stream_id) {
|
||||
if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
|
||||
session_data->stream_data->stream_id == frame->hd.stream_id) {
|
||||
/* Print response headers for the initiated request. */
|
||||
print_header(stderr, name, namelen, value, valuelen);
|
||||
break;
|
||||
@@ -370,19 +367,18 @@ Each request header name/value pair is emitted via
|
||||
return 0;
|
||||
}
|
||||
|
||||
In this turotial, we just print the name/value pair.
|
||||
In this tutorial, we just print the name/value pair.
|
||||
|
||||
After all name/value pairs are emitted for a frame,
|
||||
``on_frame_recv_callback`` function is called::
|
||||
|
||||
static int on_frame_recv_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame, void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
switch(frame->hd.type) {
|
||||
static int on_frame_recv_callback(nghttp2_session *session _U_,
|
||||
const nghttp2_frame *frame, void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
switch (frame->hd.type) {
|
||||
case NGHTTP2_HEADERS:
|
||||
if(frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
|
||||
session_data->stream_data->stream_id == frame->hd.stream_id) {
|
||||
if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
|
||||
session_data->stream_data->stream_id == frame->hd.stream_id) {
|
||||
fprintf(stderr, "All headers received\n");
|
||||
}
|
||||
break;
|
||||
@@ -391,46 +387,43 @@ After all name/value pairs are emitted for a frame,
|
||||
}
|
||||
|
||||
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
|
||||
its stream ID.
|
||||
|
||||
The ``on_data_chunk_recv_callback()`` function is invoked when a chunk
|
||||
of data is received from the remote peer::
|
||||
|
||||
static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
||||
int32_t stream_id,
|
||||
static int on_data_chunk_recv_callback(nghttp2_session *session _U_,
|
||||
uint8_t flags _U_, int32_t stream_id,
|
||||
const uint8_t *data, size_t len,
|
||||
void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
if(session_data->stream_data->stream_id == stream_id) {
|
||||
void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
if (session_data->stream_data->stream_id == stream_id) {
|
||||
fwrite(data, len, 1, stdout);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
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
|
||||
some binary data.
|
||||
|
||||
The ``on_stream_close_callback()`` function is invoked when the stream
|
||||
is about to close::
|
||||
|
||||
static int on_stream_close_callback(nghttp2_session *session,
|
||||
int32_t stream_id,
|
||||
static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||
nghttp2_error_code error_code,
|
||||
void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
int rv;
|
||||
|
||||
if(session_data->stream_data->stream_id == stream_id) {
|
||||
fprintf(stderr, "Stream %d closed with error_code=%d\n",
|
||||
stream_id, error_code);
|
||||
if (session_data->stream_data->stream_id == stream_id) {
|
||||
fprintf(stderr, "Stream %d closed with error_code=%d\n", stream_id,
|
||||
error_code);
|
||||
rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
118
doc/sources/tutorial-hpack.rst
Normal file
118
doc/sources/tutorial-hpack.rst
Normal file
@@ -0,0 +1,118 @@
|
||||
Tutorial: HPACK API
|
||||
===================
|
||||
|
||||
In this tutorial, we describe basic use of HPACK API in nghttp2
|
||||
library. We briefly describe APIs for deflating and inflating header
|
||||
fields. The example of using these APIs are presented as complete
|
||||
source code `deflate.c`_.
|
||||
|
||||
Deflating (encoding) headers
|
||||
----------------------------
|
||||
|
||||
First we need to initialize :type:`nghttp2_hd_deflater` object using
|
||||
`nghttp2_hd_deflate_new()` function::
|
||||
|
||||
int nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr,
|
||||
size_t deflate_hd_table_bufsize_max);
|
||||
|
||||
This function allocates :type:`nghttp2_hd_deflater` object and
|
||||
initializes it and assigns its pointer to ``*deflater_ptr`` passed by
|
||||
parameter. The *deflate_hd_table_bufsize_max* is the upper bound of
|
||||
header table size the deflater will use. This will limit the memory
|
||||
usage in deflater object for dynamic header table. If you doubt, just
|
||||
specify 4096 here, which is the default upper bound of dynamic header
|
||||
table buffer size.
|
||||
|
||||
To encode header fields, `nghttp2_hd_deflate_hd()` function::
|
||||
|
||||
ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater,
|
||||
uint8_t *buf, size_t buflen,
|
||||
const nghttp2_nv *nva, size_t nvlen);
|
||||
|
||||
The *deflater* is the deflater object initialized by
|
||||
`nghttp2_hd_deflate_new()` function described above. The *buf* is a
|
||||
pointer to buffer to store encoded byte string. The *buflen* is
|
||||
capacity of *buf*. The *nva* is a pointer to :type:`nghttp2_nv`,
|
||||
which is an array of header fields to deflate. The *nvlen* is the
|
||||
number of header fields which *nva* contains.
|
||||
|
||||
It is important to initialize and assign all members of
|
||||
:type:`nghttp2_nv`. If a header field should not be inserted in
|
||||
dynamic header table for a security reason, set
|
||||
:macro:`NGHTTP2_NV_FLAG_NO_INDEX` flag in :member:`nghttp2_nv.flags`.
|
||||
|
||||
`nghttp2_hd_deflate_hd()` processes all headers given in *nva*. The
|
||||
*nva* must include all request or response header fields to be sent in
|
||||
one HEADERS (or optionally following (multiple) CONTINUATION
|
||||
frame(s)). The *buf* must have enough space to store the encoded
|
||||
result. Otherwise, the function will fail. To estimate the upper
|
||||
bound of encoded result, use `nghttp2_hd_deflate_bound()` function::
|
||||
|
||||
size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater,
|
||||
const nghttp2_nv *nva, size_t nvlen);
|
||||
|
||||
Pass this function with the same parameters *deflater*, *nva* and
|
||||
*nvlen* which will be passed to `nghttp2_hd_deflate_hd()`.
|
||||
|
||||
The subsequent call of `nghttp2_hd_deflate_hd()` will use current
|
||||
encoder state and perform differential encoding which is the
|
||||
fundamental compression gain for HPACK.
|
||||
|
||||
Once `nghttp2_hd_deflate_hd()` fails, it cannot be undone and its
|
||||
further call with the same deflater object shall fail. So it is very
|
||||
important to use `nghttp2_hd_deflate_bound()` to know the required
|
||||
size of buffer.
|
||||
|
||||
To delete :type:`nghttp2_hd_deflater` object, use `nghttp2_hd_deflate_del()`
|
||||
function.
|
||||
|
||||
Inflating (decoding) headers
|
||||
----------------------------
|
||||
|
||||
We use :type:`nghttp2_hd_inflater` object to inflate compressed header
|
||||
data. To initialize the object, use `nghttp2_hd_inflate_new()`::
|
||||
|
||||
int nghttp2_hd_inflate_new(nghttp2_hd_inflater **inflater_ptr);
|
||||
|
||||
To inflate header data, use `nghttp2_hd_inflate_hd()` function::
|
||||
|
||||
ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater,
|
||||
nghttp2_nv *nv_out, int *inflate_flags,
|
||||
uint8_t *in, size_t inlen, int in_final);
|
||||
|
||||
The *inflater* is the inflater object initialized above. The *nv_out*
|
||||
is a pointer to :type:`nghttp2_nv` to store the result. The *in* is a
|
||||
pointer to input data and *inlen* is its length. The caller is not
|
||||
required to specify whole deflated header data to *in* at once. It
|
||||
can call this function multiple times for portion of the data in
|
||||
streaming way. If *in_final* is nonzero, it tells the function that
|
||||
the passed data is the final sequence of deflated header data. The
|
||||
*inflate_flags* is output parameter and successful call of this
|
||||
function stores a set of flags in it. It will be described later.
|
||||
|
||||
This function returns when each header field is inflated. When this
|
||||
happens, the function sets :macro:`NGHTTP2_HD_INFLATE_EMIT` flag to
|
||||
*inflate_flag* parameter and header field is stored in *nv_out*. The
|
||||
return value indicates the number of data read from *in* to processed
|
||||
so far. It may be less than *inlen*. The caller should call the
|
||||
function repeatedly until all data are processed by adjusting *in* and
|
||||
*inlen* with the processed bytes.
|
||||
|
||||
If *in_final* is nonzero and all given data was processed, the
|
||||
function sets :macro:`NGHTTP2_HD_INFLATE_FINAL` flag to
|
||||
*inflate_flag*. If the caller sees this flag set, call
|
||||
`nghttp2_hd_inflate_end_headers()` function.
|
||||
|
||||
If *in_final* is zero and :macro:`NGHTTP2_HD_INFLATE_EMIT` flag is not
|
||||
set, it indicates that all given data was processed. The caller is
|
||||
required to pass subsequent data.
|
||||
|
||||
It is important to note that the function may produce one or more
|
||||
header fields even if *inlen* is 0 when *in_final* is nonzero, due to
|
||||
differential encoding.
|
||||
|
||||
The example use of `nghttp2_hd_inflate_hd()` is shown in
|
||||
`inflate_header_block()` function in `deflate.c`_.
|
||||
|
||||
To delete :type:`nghttp2_hd_inflater` object, use `nghttp2_hd_inflate_del()`
|
||||
function.
|
||||
@@ -3,45 +3,43 @@ Tutorial: HTTP/2 server
|
||||
|
||||
In this tutorial, we are going to write single-threaded, event-based
|
||||
HTTP/2 web server, which supports HTTPS only. It can handle
|
||||
concurrent multiple requests, but only GET method is supported. The
|
||||
concurrent multiple requests, but only the GET method is supported. The
|
||||
complete source code, `libevent-server.c`_, is attached at the end of
|
||||
this page. It also resides in examples directory in the archive or
|
||||
repository.
|
||||
|
||||
This simple server takes 3 arguments, a port number to listen to, a
|
||||
path to SSL/TLS private key file and certificate file. Its synopsis
|
||||
is like this::
|
||||
This simple server takes 3 arguments, a port number to listen to, a path to
|
||||
your SSL/TLS private key file and a path to your certificate file. Its
|
||||
synopsis is like this::
|
||||
|
||||
$ libevent-server PORT /path/to/server.key /path/to/server.crt
|
||||
|
||||
We use libevent in this tutorial to handle networking I/O. Please
|
||||
note that nghttp2 itself does not depend on libevent.
|
||||
|
||||
First we do some setup routine for libevent and OpenSSL library in
|
||||
function ``main()`` and ``run()``, which is not so relevant to nghttp2
|
||||
library use. The one thing you should look at is setup NPN callback.
|
||||
The NPN callback is used for the server to advertise the application
|
||||
protocols the server supports to a client. In this example program,
|
||||
when creating ``SSL_CTX`` object, we stores the application protocol
|
||||
name in the wire format of NPN in statically allocated buffer. This is
|
||||
safe because we only create 1 ``SSL_CTX`` object in the entire program
|
||||
life time::
|
||||
First we create a setup routine for libevent and OpenSSL in the functions
|
||||
``main()`` and ``run()``. One thing in there you should look at, is the setup
|
||||
of the NPN callback. The NPN callback is used for the server to advertise
|
||||
which application protocols the server supports to a client. In this example
|
||||
program, when creating ``SSL_CTX`` object, we store the application protocol
|
||||
name in the wire format of NPN in a statically allocated buffer. This is safe
|
||||
because we only create one ``SSL_CTX`` object in the program's entire life
|
||||
time::
|
||||
|
||||
static unsigned char next_proto_list[256];
|
||||
static size_t next_proto_list_len;
|
||||
|
||||
static int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len,
|
||||
void *arg)
|
||||
{
|
||||
static int next_proto_cb(SSL *s _U_, const unsigned char **data,
|
||||
unsigned int *len, void *arg _U_) {
|
||||
*data = next_proto_list;
|
||||
*len = next_proto_list_len;
|
||||
*len = (unsigned int)next_proto_list_len;
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
static SSL_CTX* create_ssl_ctx(const char *key_file, const char *cert_file)
|
||||
{
|
||||
static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) {
|
||||
SSL_CTX *ssl_ctx;
|
||||
EC_KEY *ecdh;
|
||||
|
||||
ssl_ctx = SSL_CTX_new(SSLv23_server_method());
|
||||
|
||||
...
|
||||
@@ -55,25 +53,25 @@ life time::
|
||||
return ssl_ctx;
|
||||
}
|
||||
|
||||
The wire format of NPN is a sequence of length prefixed string. The
|
||||
exactly one byte is used to specify the length of each protocol
|
||||
identifier. In this tutorial, we advertise the HTTP/2 protocol the
|
||||
nghttp2 library supports. The nghttp2 library exports its identifier
|
||||
in :macro:`NGHTTP2_PROTO_VERSION_ID`. The ``next_proto_cb()`` function
|
||||
is the server-side NPN callback. In OpenSSL implementation, we just
|
||||
assign the pointer to the NPN buffers we filled earlier. The NPN
|
||||
callback function is set to ``SSL_CTX`` object using
|
||||
The wire format of NPN is a sequence of length prefixed string. Exactly one
|
||||
byte is used to specify the length of each protocol identifier. In this
|
||||
tutorial, we advertise the specific HTTP/2 protocol version the current
|
||||
nghttp2 library supports. The nghttp2 library exports its identifier in
|
||||
:macro:`NGHTTP2_PROTO_VERSION_ID`. The ``next_proto_cb()`` function is the
|
||||
server-side NPN callback. In the OpenSSL implementation, we just assign the
|
||||
pointer to the NPN buffers we filled in earlier. The NPN callback function is
|
||||
set to the ``SSL_CTX`` object using
|
||||
``SSL_CTX_set_next_protos_advertised_cb()``.
|
||||
|
||||
We use ``app_content`` structure to store the application-wide data::
|
||||
We use the ``app_content`` structure to store application-wide data::
|
||||
|
||||
struct app_context {
|
||||
SSL_CTX *ssl_ctx;
|
||||
struct event_base *evbase;
|
||||
};
|
||||
|
||||
We use ``http2_session_data`` structure to store the session-level
|
||||
(which corresponds to 1 HTTP/2 connection) data::
|
||||
We use the ``http2_session_data`` structure to store session-level
|
||||
(which corresponds to one HTTP/2 connection) data::
|
||||
|
||||
typedef struct http2_session_data {
|
||||
struct http2_stream_data root;
|
||||
@@ -81,11 +79,9 @@ We use ``http2_session_data`` structure to store the session-level
|
||||
app_context *app_ctx;
|
||||
nghttp2_session *session;
|
||||
char *client_addr;
|
||||
size_t handshake_leftlen;
|
||||
} http2_session_data;
|
||||
|
||||
We use ``http2_stream_data`` structure to store the stream-level
|
||||
data::
|
||||
We use the ``http2_stream_data`` structure to store stream-level data::
|
||||
|
||||
typedef struct http2_stream_data {
|
||||
struct http2_stream_data *prev, *next;
|
||||
@@ -94,27 +90,21 @@ data::
|
||||
int fd;
|
||||
} http2_stream_data;
|
||||
|
||||
1 HTTP/2 session can have multiple streams. We manage these multiple
|
||||
streams by intrusive doubly linked list to add and remove the object
|
||||
in O(1). The first element of this list is pointed by the
|
||||
``root->next`` in ``http2_session_data``. Initially, ``root->next``
|
||||
is ``NULL``. The ``handshake_leftlen`` member of
|
||||
``http2_session_data`` is used to track the number of bytes remaining
|
||||
when receiving first client connection preface
|
||||
(:macro:`NGHTTP2_CLIENT_CONNECTION_PREFACE`), which is 24 bytes magic
|
||||
byte string, from the client. We use libevent's bufferevent structure
|
||||
to perform network I/O. Notice that bufferevent object is in
|
||||
``http2_session_data`` and not in ``http2_stream_data``. This is
|
||||
because ``http2_stream_data`` is just a logical stream multiplexed
|
||||
A single HTTP/2 session can have multiple streams. We manage these
|
||||
multiple streams with a doubly linked list. The first element of this
|
||||
list is pointed to by the ``root->next`` in ``http2_session_data``.
|
||||
Initially, ``root->next`` is ``NULL``. We use libevent's bufferevent
|
||||
structure to perform network I/O. Note that the bufferevent object is
|
||||
kept in ``http2_session_data`` and not in ``http2_stream_data``. This
|
||||
is because ``http2_stream_data`` is just a logical stream multiplexed
|
||||
over the single connection managed by bufferevent in
|
||||
``http2_session_data``.
|
||||
|
||||
We first create listener object to accept incoming connections.
|
||||
We use libevent's ``struct evconnlistener`` for this purpose::
|
||||
We first create a listener object to accept incoming connections. We use
|
||||
libevent's ``struct evconnlistener`` for this purpose::
|
||||
|
||||
static void start_listen(struct event_base *evbase, const char *service,
|
||||
app_context *app_ctx)
|
||||
{
|
||||
app_context *app_ctx) {
|
||||
int rv;
|
||||
struct addrinfo hints;
|
||||
struct addrinfo *res, *rp;
|
||||
@@ -125,196 +115,176 @@ We use libevent's ``struct evconnlistener`` for this purpose::
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
#ifdef AI_ADDRCONFIG
|
||||
hints.ai_flags |= AI_ADDRCONFIG;
|
||||
#endif // AI_ADDRCONFIG
|
||||
#endif /* AI_ADDRCONFIG */
|
||||
|
||||
rv = getaddrinfo(NULL, service, &hints, &res);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
errx(1, NULL);
|
||||
}
|
||||
for(rp = res; rp; rp = rp->ai_next) {
|
||||
for (rp = res; rp; rp = rp->ai_next) {
|
||||
struct evconnlistener *listener;
|
||||
listener = evconnlistener_new_bind(evbase, acceptcb, app_ctx,
|
||||
LEV_OPT_CLOSE_ON_FREE |
|
||||
LEV_OPT_REUSEABLE, -1,
|
||||
rp->ai_addr, rp->ai_addrlen);
|
||||
if(listener) {
|
||||
listener = evconnlistener_new_bind(
|
||||
evbase, acceptcb, app_ctx, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
|
||||
16, rp->ai_addr, rp->ai_addrlen);
|
||||
if (listener) {
|
||||
freeaddrinfo(res);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
errx(1, "Could not start listener");
|
||||
}
|
||||
|
||||
We specify ``acceptcb`` callback which is called when a new connection
|
||||
is accepted::
|
||||
We specify the ``acceptcb`` callback which is called when a new connection is
|
||||
accepted::
|
||||
|
||||
static void acceptcb(struct evconnlistener *listener, int fd,
|
||||
struct sockaddr *addr, int addrlen, void *arg)
|
||||
{
|
||||
app_context *app_ctx = (app_context*)arg;
|
||||
static void acceptcb(struct evconnlistener *listener _U_, int fd,
|
||||
struct sockaddr *addr, int addrlen, void *arg) {
|
||||
app_context *app_ctx = (app_context *)arg;
|
||||
http2_session_data *session_data;
|
||||
|
||||
session_data = create_http2_session_data(app_ctx, fd, addr, addrlen);
|
||||
bufferevent_setcb(session_data->bev, handshake_readcb, NULL, eventcb,
|
||||
session_data);
|
||||
|
||||
bufferevent_setcb(session_data->bev, readcb, writecb, eventcb, session_data);
|
||||
}
|
||||
|
||||
Here we create ``http2_session_data`` object. The bufferevent for this
|
||||
connection is also initialized at this time. We specify 2 callbacks
|
||||
for the bufferevent: ``handshake_readcb`` and ``eventcb``.
|
||||
Here we create the ``http2_session_data`` object. The bufferevent for
|
||||
this connection is also initialized at this time. We specify three
|
||||
callbacks for the bufferevent: ``readcb``, ``writecb`` and
|
||||
``eventcb``.
|
||||
|
||||
The ``eventcb()`` is invoked by libevent event loop when an event
|
||||
The ``eventcb()`` callback is invoked by the libevent event loop when an event
|
||||
(e.g., connection has been established, timeout, etc) happens on the
|
||||
underlying network socket::
|
||||
|
||||
static void eventcb(struct bufferevent *bev, short events, void *ptr)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
||||
if(events & BEV_EVENT_CONNECTED) {
|
||||
static void eventcb(struct bufferevent *bev _U_, short events, void *ptr) {
|
||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||
if (events & BEV_EVENT_CONNECTED) {
|
||||
fprintf(stderr, "%s connected\n", session_data->client_addr);
|
||||
|
||||
initialize_nghttp2_session(session_data);
|
||||
|
||||
if (send_server_connection_header(session_data) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
if(events & BEV_EVENT_EOF) {
|
||||
if (events & BEV_EVENT_EOF) {
|
||||
fprintf(stderr, "%s EOF\n", session_data->client_addr);
|
||||
} else if(events & BEV_EVENT_ERROR) {
|
||||
} else if (events & BEV_EVENT_ERROR) {
|
||||
fprintf(stderr, "%s network error\n", session_data->client_addr);
|
||||
} else if(events & BEV_EVENT_TIMEOUT) {
|
||||
} else if (events & BEV_EVENT_TIMEOUT) {
|
||||
fprintf(stderr, "%s timeout\n", session_data->client_addr);
|
||||
}
|
||||
delete_http2_session_data(session_data);
|
||||
}
|
||||
|
||||
For ``BEV_EVENT_EOF``, ``BEV_EVENT_ERROR`` and ``BEV_EVENT_TIMEOUT``
|
||||
event, we just simply tear down the connection. The
|
||||
``delete_http2_session_data()`` function destroys
|
||||
``http2_session_data`` object and thus its bufferevent member. As a
|
||||
result, the underlying connection is closed. The
|
||||
For the ``BEV_EVENT_EOF``, ``BEV_EVENT_ERROR`` and
|
||||
``BEV_EVENT_TIMEOUT`` events, we just simply tear down the connection.
|
||||
The ``delete_http2_session_data()`` function destroys the
|
||||
``http2_session_data`` object and thus also its bufferevent member.
|
||||
As a result, the underlying connection is closed. The
|
||||
``BEV_EVENT_CONNECTED`` event is invoked when SSL/TLS handshake is
|
||||
finished successfully.
|
||||
finished successfully. Now we are ready to start the HTTP/2
|
||||
communication.
|
||||
|
||||
The ``handshake_readcb()`` is a callback function to handle 24 bytes
|
||||
magic byte string from a client, since nghttp2 library does not handle
|
||||
it::
|
||||
|
||||
static void handshake_readcb(struct bufferevent *bev, void *ptr)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
||||
uint8_t data[24];
|
||||
struct evbuffer *input = bufferevent_get_input(session_data->bev);
|
||||
int readlen = evbuffer_remove(input, data, session_data->handshake_leftlen);
|
||||
const char *conhead = NGHTTP2_CLIENT_CONNECTION_PREFACE;
|
||||
|
||||
if(memcmp(conhead + NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN
|
||||
- session_data->handshake_leftlen, data, readlen) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
session_data->handshake_leftlen -= readlen;
|
||||
if(session_data->handshake_leftlen == 0) {
|
||||
bufferevent_setcb(session_data->bev, readcb, writecb, eventcb, ptr);
|
||||
/* Process pending data in buffer since they are not notified
|
||||
further */
|
||||
initialize_nghttp2_session(session_data);
|
||||
if(send_server_connection_header(session_data) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
if(session_recv(session_data) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
We check that the received byte string matches
|
||||
:macro:`NGHTTP2_CLIENT_CONNECTION_PREFACE`. When they match, the
|
||||
connection state is ready for starting HTTP/2 communication. First
|
||||
we change the callback functions for the bufferevent object. We use
|
||||
same ``eventcb`` as before. But we specify new ``readcb`` and
|
||||
``writecb`` function to handle HTTP/2 communication. We describe
|
||||
these 2 functions later.
|
||||
|
||||
We initialize nghttp2 session object which is done in
|
||||
We initialize a nghttp2 session object which is done in
|
||||
``initialize_nghttp2_session()``::
|
||||
|
||||
static void initialize_nghttp2_session(http2_session_data *session_data)
|
||||
{
|
||||
nghttp2_session_callbacks callbacks = {0};
|
||||
static void initialize_nghttp2_session(http2_session_data *session_data) {
|
||||
nghttp2_session_callbacks *callbacks;
|
||||
|
||||
callbacks.send_callback = send_callback;
|
||||
callbacks.on_frame_recv_callback = on_frame_recv_callback;
|
||||
callbacks.on_stream_close_callback = on_stream_close_callback;
|
||||
callbacks.on_header_callback = on_header_callback;
|
||||
callbacks.on_begin_headers_callback = on_begin_headers_callback;
|
||||
nghttp2_session_server_new(&session_data->session, &callbacks, session_data);
|
||||
nghttp2_session_callbacks_new(&callbacks);
|
||||
|
||||
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
|
||||
on_frame_recv_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_stream_close_callback(
|
||||
callbacks, on_stream_close_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_header_callback(callbacks,
|
||||
on_header_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_begin_headers_callback(
|
||||
callbacks, on_begin_headers_callback);
|
||||
|
||||
nghttp2_session_server_new(&session_data->session, callbacks, session_data);
|
||||
|
||||
nghttp2_session_callbacks_del(callbacks);
|
||||
}
|
||||
|
||||
Since we are creating server, nghttp2 session object is created using
|
||||
`nghttp2_session_server_new()` function. We registers 5 callbacks to
|
||||
nghttp2 session object. We'll talk about these callbacks later.
|
||||
Since we are creating a server and uses options, the nghttp2 session
|
||||
object is created using `nghttp2_session_server_new2()` function. We
|
||||
registers five callbacks for nghttp2 session object. We'll talk about
|
||||
these callbacks later.
|
||||
|
||||
After initialization of nghttp2 session object, we are going to send
|
||||
server connection header in ``send_server_connection_header()``::
|
||||
After initialization of the nghttp2 session object, we are going to send
|
||||
a server connection header in ``send_server_connection_header()``::
|
||||
|
||||
static int send_server_connection_header(http2_session_data *session_data)
|
||||
{
|
||||
static int send_server_connection_header(http2_session_data *session_data) {
|
||||
nghttp2_settings_entry iv[1] = {
|
||||
{ NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 }
|
||||
};
|
||||
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
|
||||
int rv;
|
||||
|
||||
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE,
|
||||
iv, ARRLEN(iv));
|
||||
if(rv != 0) {
|
||||
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
|
||||
ARRLEN(iv));
|
||||
if (rv != 0) {
|
||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
The server connection header is SETTINGS frame. We specify
|
||||
SETTINGS_MAX_CONCURRENT_STREAMS to 100 in SETTINGS frame. To queue
|
||||
The server connection header is a SETTINGS frame. We specify
|
||||
SETTINGS_MAX_CONCURRENT_STREAMS to 100 in the SETTINGS frame. To queue
|
||||
the SETTINGS frame for the transmission, we use
|
||||
`nghttp2_submit_settings()`. Note that `nghttp2_submit_settings()`
|
||||
function only queues the frame and not actually send it. All
|
||||
``nghttp2_submit_*()`` family functions have this property. To
|
||||
actually send the frame, `nghttp2_session_send()` is used, which is
|
||||
described about later.
|
||||
function only queues the frame and it does not actually send it. All
|
||||
functions in the ``nghttp2_submit_*()`` family have this property. To
|
||||
actually send the frame, `nghttp2_session_send()` should be used, as
|
||||
described later.
|
||||
|
||||
Since bufferevent may buffer more than first 24 bytes from the client,
|
||||
we have to process them here since libevent won't invoke callback
|
||||
functions for these pending data. To process received data, we call
|
||||
Since bufferevent may buffer more than the first 24 bytes from the client, we
|
||||
have to process them here since libevent won't invoke callback functions for
|
||||
this pending data. To process the received data, we call the
|
||||
``session_recv()`` function::
|
||||
|
||||
static int session_recv(http2_session_data *session_data)
|
||||
{
|
||||
int rv;
|
||||
static int session_recv(http2_session_data *session_data) {
|
||||
ssize_t readlen;
|
||||
struct evbuffer *input = bufferevent_get_input(session_data->bev);
|
||||
size_t datalen = evbuffer_get_length(input);
|
||||
unsigned char *data = evbuffer_pullup(input, -1);
|
||||
rv = nghttp2_session_mem_recv(session_data->session, data, datalen);
|
||||
if(rv < 0) {
|
||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||
|
||||
readlen = nghttp2_session_mem_recv(session_data->session, data, datalen);
|
||||
if (readlen < 0) {
|
||||
warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
|
||||
return -1;
|
||||
}
|
||||
evbuffer_drain(input, rv);
|
||||
if(session_send(session_data) != 0) {
|
||||
if (evbuffer_drain(input, readlen) != 0) {
|
||||
warnx("Fatal error: evbuffer_drain failed");
|
||||
return -1;
|
||||
}
|
||||
if (session_send(session_data) != 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
In this function, we feed all unprocessed, received data to nghttp2
|
||||
session object using `nghttp2_session_mem_recv()` function. The
|
||||
`nghttp2_session_mem_recv()` processes the received data and may
|
||||
invoke nghttp2 callbacks and also queue outgoing frames. Since there
|
||||
may be pending frames, we call ``session_send()`` function to send
|
||||
those frames. The ``session_send()`` function is defined as follows::
|
||||
In this function, we feed all unprocessed but already received data to the
|
||||
nghttp2 session object using the `nghttp2_session_mem_recv()` function. The
|
||||
`nghttp2_session_mem_recv()` function processes the data and may invoke the
|
||||
nghttp2 callbacks and also queue outgoing frames. Since there may be pending
|
||||
outgoing frames, we call ``session_send()`` function to send off those
|
||||
frames. The ``session_send()`` function is defined as follows::
|
||||
|
||||
static int session_send(http2_session_data *session_data)
|
||||
{
|
||||
static int session_send(http2_session_data *session_data) {
|
||||
int rv;
|
||||
rv = nghttp2_session_send(session_data->session);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||
return -1;
|
||||
}
|
||||
@@ -322,21 +292,17 @@ those frames. The ``session_send()`` function is defined as follows::
|
||||
}
|
||||
|
||||
The `nghttp2_session_send()` function serializes the frame into wire
|
||||
format and call :member:`nghttp2_session_callbacks.send_callback` with
|
||||
it. We set ``send_callback()`` function to
|
||||
:member:`nghttp2_session_callbacks.send_callback` in
|
||||
``initialize_nghttp2_session()`` function described earlier. It is
|
||||
defined as follows::
|
||||
format and calls ``send_callback()`` of type
|
||||
:type:`nghttp2_send_callback`. The ``send_callback()`` is defined as
|
||||
follows::
|
||||
|
||||
static ssize_t send_callback(nghttp2_session *session,
|
||||
const uint8_t *data, size_t length,
|
||||
int flags, void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
|
||||
size_t length, int flags _U_, void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
struct bufferevent *bev = session_data->bev;
|
||||
/* Avoid excessive buffering in server side. */
|
||||
if(evbuffer_get_length(bufferevent_get_output(session_data->bev)) >=
|
||||
OUTPUT_WOULDBLOCK_THRESHOLD) {
|
||||
if (evbuffer_get_length(bufferevent_get_output(session_data->bev)) >=
|
||||
OUTPUT_WOULDBLOCK_THRESHOLD) {
|
||||
return NGHTTP2_ERR_WOULDBLOCK;
|
||||
}
|
||||
bufferevent_write(bev, data, length);
|
||||
@@ -346,26 +312,24 @@ defined as follows::
|
||||
Since we use bufferevent to abstract network I/O, we just write the
|
||||
data to the bufferevent object. Note that `nghttp2_session_send()`
|
||||
continues to write all frames queued so far. If we were writing the
|
||||
data to the non-blocking socket directly using ``write()`` system call
|
||||
in the :member:`nghttp2_session_callbacks.send_callback`, we will
|
||||
surely get ``EAGAIN`` or ``EWOULDBLOCK`` since the socket has limited
|
||||
send buffer. If that happens, we can return
|
||||
:macro:`NGHTTP2_ERR_WOULDBLOCK` to signal the nghttp2 library to stop
|
||||
sending further data. But writing to the bufferevent, we have to
|
||||
regulate the amount data to be buffered by ourselves to avoid possible
|
||||
huge memory consumption. To achieve this, we check the size of output
|
||||
buffer and if it is more than or equal to
|
||||
``OUTPUT_WOULDBLOCK_THRESHOLD`` bytes, we stop writing data and return
|
||||
:macro:`NGHTTP2_ERR_WOULDBLOCK` to tell the library to stop calling
|
||||
send_callback.
|
||||
data to a non-blocking socket directly using ``write()`` system call
|
||||
in the ``send_callback()``, we would surely get ``EAGAIN`` or
|
||||
``EWOULDBLOCK`` back since the socket has limited send buffer. If that
|
||||
happens, we can return :macro:`NGHTTP2_ERR_WOULDBLOCK` to signal the
|
||||
nghttp2 library to stop sending further data. But when writing to the
|
||||
bufferevent, we have to regulate the amount data to get buffered
|
||||
ourselves to avoid using huge amounts of memory. To achieve this, we
|
||||
check the size of the output buffer and if it reaches more than or
|
||||
equal to ``OUTPUT_WOULDBLOCK_THRESHOLD`` bytes, we stop writing data
|
||||
and return :macro:`NGHTTP2_ERR_WOULDBLOCK` to tell the library to stop
|
||||
calling send_callback.
|
||||
|
||||
The next bufferevent callback is ``readcb()``, which is invoked when
|
||||
data is available to read in the bufferevent input buffer::
|
||||
|
||||
static void readcb(struct bufferevent *bev, void *ptr)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
||||
if(session_recv(session_data) != 0) {
|
||||
static void readcb(struct bufferevent *bev _U_, void *ptr) {
|
||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||
if (session_recv(session_data) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
@@ -374,59 +338,56 @@ data is available to read in the bufferevent input buffer::
|
||||
In this function, we just call ``session_recv()`` to process incoming
|
||||
data.
|
||||
|
||||
The third bufferevent callback is ``writecb()``, which is invoked when
|
||||
all data written in the bufferevent output buffer have been sent::
|
||||
The third bufferevent callback is ``writecb()``, which is invoked when all
|
||||
data in the bufferevent output buffer has been sent::
|
||||
|
||||
static void writecb(struct bufferevent *bev, void *ptr)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
||||
if(evbuffer_get_length(bufferevent_get_output(bev)) > 0) {
|
||||
static void writecb(struct bufferevent *bev, void *ptr) {
|
||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||
if (evbuffer_get_length(bufferevent_get_output(bev)) > 0) {
|
||||
return;
|
||||
}
|
||||
if(nghttp2_session_want_read(session_data->session) == 0 &&
|
||||
nghttp2_session_want_write(session_data->session) == 0) {
|
||||
if (nghttp2_session_want_read(session_data->session) == 0 &&
|
||||
nghttp2_session_want_write(session_data->session) == 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
if(session_send(session_data) != 0) {
|
||||
if (session_send(session_data) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
First we check whether we should drop connection or not. The nghttp2
|
||||
session object keeps track of reception and transmission of GOAWAY
|
||||
frame and other error conditions as well. Using these information,
|
||||
nghttp2 session object will tell whether the connection should be
|
||||
dropped or not. More specifically, both `nghttp2_session_want_read()`
|
||||
and `nghttp2_session_want_write()` return 0, we have no business in
|
||||
the connection. But since we are using bufferevent and its deferred
|
||||
callback option, the bufferevent output buffer may contain the pending
|
||||
data when the ``writecb()`` is called. To handle this situation, we
|
||||
also check whether the output buffer is empty or not. If these
|
||||
conditions are met, we drop connection.
|
||||
First we check whether we should drop the connection or not. The nghttp2
|
||||
session object keeps track of reception and transmission of GOAWAY frames and
|
||||
other error conditions as well. Using this information, the nghttp2 session
|
||||
object will tell whether the connection should be dropped or not. More
|
||||
specifically, if both `nghttp2_session_want_read()` and
|
||||
`nghttp2_session_want_write()` return 0, we have no business left in the
|
||||
connection. But since we are using bufferevent and its deferred callback
|
||||
option, the bufferevent output buffer may contain pending data when the
|
||||
``writecb()`` is called. To handle this, we check whether the output buffer is
|
||||
empty or not. If all these conditions are met, we drop connection.
|
||||
|
||||
Otherwise, we call ``session_send()`` to process pending output
|
||||
data. Remember that in ``send_callback()``, we may not write all data
|
||||
to bufferevent to avoid excessive buffering. We continue process
|
||||
pending data when output buffer becomes empty.
|
||||
Otherwise, we call ``session_send()`` to process the pending output
|
||||
data. Remember that in ``send_callback()``, we must not write all data to
|
||||
bufferevent to avoid excessive buffering. We continue processing pending data
|
||||
when the output buffer becomes empty.
|
||||
|
||||
We have already described about nghttp2 callback ``send_callback()``.
|
||||
Let's describe remaining nghttp2 callbacks we setup in
|
||||
We have already described the nghttp2 callback ``send_callback()``. Let's
|
||||
learn about the remaining nghttp2 callbacks we setup in
|
||||
``initialize_nghttp2_setup()`` function.
|
||||
|
||||
The ``on_begin_headers_callback()`` function is invoked when reception
|
||||
of header block in HEADERS or PUSH_PROMISE frame is started::
|
||||
The ``on_begin_headers_callback()`` function is invoked when the reception of
|
||||
a header block in HEADERS or PUSH_PROMISE frame is started::
|
||||
|
||||
static int on_begin_headers_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame,
|
||||
void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
http2_stream_data *stream_data;
|
||||
|
||||
if(frame->hd.type != NGHTTP2_HEADERS ||
|
||||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
||||
if (frame->hd.type != NGHTTP2_HEADERS ||
|
||||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
||||
return 0;
|
||||
}
|
||||
stream_data = create_http2_stream_data(session_data, frame->hd.stream_id);
|
||||
@@ -435,40 +396,40 @@ of header block in HEADERS or PUSH_PROMISE frame is started::
|
||||
return 0;
|
||||
}
|
||||
|
||||
We only interested in HEADERS frame in this function. Since HEADERS
|
||||
frame has several roles in HTTP/2 protocol, we check that it is a
|
||||
request HEADERS, which opens new stream. If frame is request HEADERS,
|
||||
then we create ``http2_stream_data`` object to store stream related
|
||||
data. We associate created ``http2_stream_data`` object to the stream
|
||||
in nghttp2 session object using `nghttp2_set_stream_user_data()` in
|
||||
order to get the object without searching through doubly linked list.
|
||||
We are only interested in the HEADERS frame in this function. Since the
|
||||
HEADERS frame has several roles in the HTTP/2 protocol, we check that it is a
|
||||
request HEADERS, which opens new stream. If the frame is a request HEADERS, we
|
||||
create a ``http2_stream_data`` object to store the stream related data. We
|
||||
associate the created ``http2_stream_data`` object with the stream in the
|
||||
nghttp2 session object using `nghttp2_set_stream_user_data()` to get the
|
||||
object without searching through the doubly linked list.
|
||||
|
||||
In this example server, we want to serve files relative to the current
|
||||
working directory the program was invoked. Each header name/value pair
|
||||
is emitted via ``on_header_callback`` function, which is called after
|
||||
In this example server, we want to serve files relative to the current working
|
||||
directory in which the program was invoked. Each header name/value pair is
|
||||
emitted via ``on_header_callback`` function, which is called after
|
||||
``on_begin_headers_callback()``::
|
||||
|
||||
static int on_header_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame,
|
||||
const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen,
|
||||
void *user_data)
|
||||
{
|
||||
const nghttp2_frame *frame, const uint8_t *name,
|
||||
size_t namelen, const uint8_t *value,
|
||||
size_t valuelen, uint8_t flags _U_,
|
||||
void *user_data _U_) {
|
||||
http2_stream_data *stream_data;
|
||||
const char PATH[] = ":path";
|
||||
switch(frame->hd.type) {
|
||||
switch (frame->hd.type) {
|
||||
case NGHTTP2_HEADERS:
|
||||
if(frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
||||
if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
||||
break;
|
||||
}
|
||||
stream_data = nghttp2_session_get_stream_user_data(session,
|
||||
frame->hd.stream_id);
|
||||
if(!stream_data || stream_data->request_path) {
|
||||
stream_data =
|
||||
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
|
||||
if (!stream_data || stream_data->request_path) {
|
||||
break;
|
||||
}
|
||||
if(namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) {
|
||||
if (namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) {
|
||||
size_t j;
|
||||
for(j = 0; j < valuelen && value[j] != '?'; ++j);
|
||||
for (j = 0; j < valuelen && value[j] != '?'; ++j)
|
||||
;
|
||||
stream_data->request_path = percent_decode(value, j);
|
||||
}
|
||||
break;
|
||||
@@ -476,29 +437,28 @@ is emitted via ``on_header_callback`` function, which is called after
|
||||
return 0;
|
||||
}
|
||||
|
||||
We search ``:path`` header field in request headers and keep the
|
||||
requested path in ``http2_stream_data`` object. In this example
|
||||
program, we ignore ``:method`` header field and always treat the
|
||||
request as GET request.
|
||||
We search for the ``:path`` header field among the request headers and store
|
||||
the requested path in the ``http2_stream_data`` object. In this example
|
||||
program, we ignore ``:method`` header field and always treat the request as a
|
||||
GET request.
|
||||
|
||||
The ``on_frame_recv_callback()`` function is invoked when a frame is
|
||||
fully received::
|
||||
|
||||
static int on_frame_recv_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame, void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
const nghttp2_frame *frame, void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
http2_stream_data *stream_data;
|
||||
switch(frame->hd.type) {
|
||||
switch (frame->hd.type) {
|
||||
case NGHTTP2_DATA:
|
||||
case NGHTTP2_HEADERS:
|
||||
/* Check that the client request has finished */
|
||||
if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||
stream_data = nghttp2_session_get_stream_user_data(session,
|
||||
frame->hd.stream_id);
|
||||
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||
stream_data =
|
||||
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
|
||||
/* For DATA and HEADERS frame, this callback may be called after
|
||||
on_stream_close_callback. Check that stream still alive. */
|
||||
if(!stream_data) {
|
||||
if (!stream_data) {
|
||||
return 0;
|
||||
}
|
||||
return on_request_recv(session, session_data, stream_data);
|
||||
@@ -510,78 +470,75 @@ fully received::
|
||||
return 0;
|
||||
}
|
||||
|
||||
First we retrieve ``http2_stream_data`` object associated to the
|
||||
stream in ``on_begin_headers_callback()``. It is done using
|
||||
`nghttp2_session_get_stream_user_data()`. If the requested path cannot
|
||||
be served for some reasons (e.g., file is not found), we send 404
|
||||
response, which is done in ``error_reply()``. Otherwise, we open
|
||||
requested file and send its content. We send 1 header field
|
||||
``:status`` as a response header.
|
||||
First we retrieve the ``http2_stream_data`` object associated with the stream
|
||||
in ``on_begin_headers_callback()``. It is done using
|
||||
`nghttp2_session_get_stream_user_data()`. If the requested path cannot be
|
||||
served for some reason (e.g., file is not found), we send a 404 response,
|
||||
which is done in ``error_reply()``. Otherwise, we open the requested file and
|
||||
send its content. We send the header field ``:status`` as a single response
|
||||
header.
|
||||
|
||||
Sending content of a file is done in ``send_response()`` function::
|
||||
Sending the content of the file is done in ``send_response()`` function::
|
||||
|
||||
static int send_response(nghttp2_session *session, int32_t stream_id,
|
||||
nghttp2_nv *nva, size_t nvlen, int fd)
|
||||
{
|
||||
nghttp2_nv *nva, size_t nvlen, int fd) {
|
||||
int rv;
|
||||
nghttp2_data_provider data_prd;
|
||||
data_prd.source.fd = fd;
|
||||
data_prd.read_callback = file_read_callback;
|
||||
|
||||
rv = nghttp2_submit_response(session, stream_id, nva, nvlen, &data_prd);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
The nghttp2 library uses :type:`nghttp2_data_provider` structure to
|
||||
The nghttp2 library uses the :type:`nghttp2_data_provider` structure to
|
||||
send entity body to the remote peer. The ``source`` member of this
|
||||
structure is a union and it can be either void pointer or int which is
|
||||
intended to be used as file descriptor. In this example server, we use
|
||||
file descriptor. We also set ``file_read_callback()`` callback
|
||||
function to read content of the file::
|
||||
the file descriptor. We also set the ``file_read_callback()`` callback
|
||||
function to read the contents of the file::
|
||||
|
||||
static ssize_t file_read_callback
|
||||
(nghttp2_session *session, int32_t stream_id,
|
||||
uint8_t *buf, size_t length, uint32_t *data_flags,
|
||||
nghttp2_data_source *source, void *user_data)
|
||||
{
|
||||
static ssize_t file_read_callback(nghttp2_session *session _U_,
|
||||
int32_t stream_id _U_, uint8_t *buf,
|
||||
size_t length, uint32_t *data_flags,
|
||||
nghttp2_data_source *source,
|
||||
void *user_data _U_) {
|
||||
int fd = source->fd;
|
||||
ssize_t r;
|
||||
while((r = read(fd, buf, length)) == -1 && errno == EINTR);
|
||||
if(r == -1) {
|
||||
while ((r = read(fd, buf, length)) == -1 && errno == EINTR)
|
||||
;
|
||||
if (r == -1) {
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
if(r == 0) {
|
||||
if (r == 0) {
|
||||
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
If error happens while reading file, we return
|
||||
If an error happens while reading the file, we return
|
||||
:macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. This tells the
|
||||
library to send RST_STREAM to the stream. When all data are read, set
|
||||
:macro:`NGHTTP2_DATA_FLAG_EOF` flag to ``*data_flags`` to tell the
|
||||
nghttp2 library that we have finished reading file.
|
||||
library to send RST_STREAM to the stream. When all data has been read, set
|
||||
the :macro:`NGHTTP2_DATA_FLAG_EOF` flag to ``*data_flags`` to tell the
|
||||
nghttp2 library that we have finished reading the file.
|
||||
|
||||
The `nghttp2_submit_response()` is used to send response to the remote
|
||||
peer.
|
||||
The `nghttp2_submit_response()` function is used to send the response to the
|
||||
remote peer.
|
||||
|
||||
The ``on_stream_close_callback()`` function is invoked when the stream
|
||||
is about to close::
|
||||
|
||||
static int on_stream_close_callback(nghttp2_session *session,
|
||||
int32_t stream_id,
|
||||
nghttp2_error_code error_code,
|
||||
void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||
uint32_t error_code _U_, void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
http2_stream_data *stream_data;
|
||||
|
||||
stream_data = nghttp2_session_get_stream_user_data(session, stream_id);
|
||||
if(!stream_data) {
|
||||
if (!stream_data) {
|
||||
return 0;
|
||||
}
|
||||
remove_stream(session_data, stream_data);
|
||||
@@ -589,5 +546,5 @@ is about to close::
|
||||
return 0;
|
||||
}
|
||||
|
||||
We destroy ``http2_stream_data`` object in this function since the
|
||||
stream is about to close and we no longer use that object.
|
||||
We destroy the ``http2_stream_data`` object in this function since the stream
|
||||
is about to close and we no longer use that object.
|
||||
|
||||
6
doc/tutorial-hpack.rst.in
Normal file
6
doc/tutorial-hpack.rst.in
Normal file
@@ -0,0 +1,6 @@
|
||||
.. include:: @top_srcdir@/doc/sources/tutorial-hpack.rst
|
||||
|
||||
deflate.c
|
||||
---------
|
||||
|
||||
.. literalinclude:: @top_srcdir@/examples/deflate.c
|
||||
6
examples/.gitignore
vendored
6
examples/.gitignore
vendored
@@ -1,3 +1,9 @@
|
||||
client
|
||||
libevent-client
|
||||
libevent-server
|
||||
deflate
|
||||
tiny-nghttpd
|
||||
asio-sv
|
||||
asio-sv2
|
||||
asio-cl
|
||||
asio-cl2
|
||||
|
||||
@@ -23,22 +23,22 @@
|
||||
|
||||
if ENABLE_EXAMPLES
|
||||
|
||||
AM_CFLAGS = $(WARNCFLAGS)
|
||||
AM_CPPFLAGS = \
|
||||
-Wall \
|
||||
-I$(top_srcdir)/lib/includes \
|
||||
-I$(top_builddir)/lib/includes \
|
||||
-I$(top_srcdir)/src/includes \
|
||||
-I$(top_srcdir)/third-party \
|
||||
@LIBEVENT_OPENSSL_CFLAGS@ \
|
||||
@OPENSSL_CFLAGS@ \
|
||||
@DEFS@
|
||||
AM_LDFLAGS = \
|
||||
LDADD = $(top_builddir)/lib/libnghttp2.la \
|
||||
$(top_builddir)/third-party/libhttp-parser.la \
|
||||
@LIBEVENT_OPENSSL_LIBS@ \
|
||||
@OPENSSL_LIBS@
|
||||
LDADD = \
|
||||
$(top_builddir)/lib/libnghttp2.la \
|
||||
$(top_builddir)/third-party/libhttp-parser.la
|
||||
|
||||
noinst_PROGRAMS = client libevent-client libevent-server
|
||||
noinst_PROGRAMS = client libevent-client libevent-server deflate
|
||||
|
||||
client_SOURCES = client.c
|
||||
|
||||
@@ -46,4 +46,50 @@ libevent_client_SOURCES = libevent-client.c
|
||||
|
||||
libevent_server_SOURCES = libevent-server.c
|
||||
|
||||
deflate_SOURCES = deflate.c
|
||||
|
||||
if ENABLE_TINY_NGHTTPD
|
||||
|
||||
noinst_PROGRAMS += tiny-nghttpd
|
||||
|
||||
tiny_nghttpd_SOURCES = tiny-nghttpd.c
|
||||
|
||||
endif # ENABLE_TINY_NGHTTPD
|
||||
|
||||
if ENABLE_ASIO_LIB
|
||||
|
||||
noinst_PROGRAMS += asio-sv asio-sv2 asio-cl asio-cl2
|
||||
|
||||
# AM_CPPFLAGS must be placed first, so that header file (e.g.,
|
||||
# nghttp2/nghttp2.h) in this package is used rather than installed
|
||||
# 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_CPPFLAGS = ${ASIOCPPFLAGS}
|
||||
asio_sv_LDADD = ${ASIOLDADD}
|
||||
|
||||
asio_sv2_SOURCES = asio-sv2.cc
|
||||
asio_sv2_CPPFLAGS = ${ASIOCPPFLAGS}
|
||||
asio_sv2_LDADD = ${ASIOLDADD}
|
||||
|
||||
asio_cl_SOURCES = asio-cl.cc
|
||||
asio_cl_CPPFLAGS = ${ASIOCPPFLAGS}
|
||||
asio_cl_LDADD = ${ASIOLDADD}
|
||||
|
||||
asio_cl2_SOURCES = asio-cl2.cc
|
||||
asio_cl2_CPPFLAGS = ${ASIOCPPFLAGS}
|
||||
asio_cl2_LDADD = ${ASIOLDADD}
|
||||
|
||||
endif # ENABLE_ASIO_LIB
|
||||
|
||||
endif # ENABLE_EXAMPLES
|
||||
|
||||
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;
|
||||
}
|
||||
149
examples/asio-sv.cc
Normal file
149
examples/asio-sv.cc
Normal file
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2014 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
// We wrote this code based on the original code which has the
|
||||
// following license:
|
||||
//
|
||||
// main.cpp
|
||||
// ~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <nghttp2/asio_http2_server.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-sv <address> <port> <threads> [<private-key-file> "
|
||||
<< "<cert-file>]\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
boost::system::error_code ec;
|
||||
|
||||
std::string addr = argv[1];
|
||||
std::string port = argv[2];
|
||||
std::size_t num_threads = std::stoi(argv[3]);
|
||||
|
||||
http2 server;
|
||||
|
||||
server.num_threads(num_threads);
|
||||
|
||||
server.handle("/", [](const request &req, const response &res) {
|
||||
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");
|
||||
}
|
||||
|
||||
res.write_head(200);
|
||||
res.end("you'll receive server push!\n");
|
||||
});
|
||||
server.handle("/delay", [](const request &req, const response &res) {
|
||||
res.write_head(200);
|
||||
|
||||
auto timer = std::make_shared<boost::asio::deadline_timer>(
|
||||
res.io_service(), boost::posix_time::seconds(3));
|
||||
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";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
125
examples/asio-sv2.cc
Normal file
125
examples/asio-sv2.cc
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2014 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
// We wrote this code based on the original code which has the
|
||||
// following license:
|
||||
//
|
||||
// main.cpp
|
||||
// ~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif // HAVE_UNISTD_H
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif // HAVE_FCNTL_H
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <nghttp2/asio_http2_server.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 < 5) {
|
||||
std::cerr << "Usage: asio-sv2 <address> <port> <threads> <doc-root> "
|
||||
<< "[<private-key-file> <cert-file>]\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
boost::system::error_code ec;
|
||||
|
||||
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;
|
||||
|
||||
server.num_threads(num_threads);
|
||||
|
||||
server.handle("/", [&docroot](const request &req, const response &res) {
|
||||
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) {
|
||||
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
|
||||
* intentionally made simple.
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif /* HAVE_UNISTD_H */
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif /* HAVE_FCNTL_H */
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_SYS_SOCKET_H
|
||||
#include <sys/socket.h>
|
||||
#endif /* HAVE_SYS_SOCKET_H */
|
||||
#ifdef HAVE_NETDB_H
|
||||
#include <netdb.h>
|
||||
#endif /* HAVE_NETDB_H */
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
#include <netinet/in.h>
|
||||
#endif /* HAVE_NETINET_IN_H */
|
||||
#include <netinet/tcp.h>
|
||||
#include <poll.h>
|
||||
#include <signal.h>
|
||||
@@ -44,20 +58,21 @@
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/conf.h>
|
||||
|
||||
enum {
|
||||
IO_NONE,
|
||||
WANT_READ,
|
||||
WANT_WRITE
|
||||
};
|
||||
enum { IO_NONE, WANT_READ, WANT_WRITE };
|
||||
|
||||
#define MAKE_NV(NAME, VALUE) \
|
||||
{(uint8_t*)NAME, (uint8_t*)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \
|
||||
NGHTTP2_NV_FLAG_NONE}
|
||||
#define MAKE_NV(NAME, VALUE) \
|
||||
{ \
|
||||
(uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \
|
||||
NGHTTP2_NV_FLAG_NONE \
|
||||
}
|
||||
|
||||
#define MAKE_NV_CS(NAME, VALUE) \
|
||||
{(uint8_t*)NAME, (uint8_t*)VALUE, sizeof(NAME) - 1, strlen(VALUE), \
|
||||
NGHTTP2_NV_FLAG_NONE}
|
||||
#define MAKE_NV_CS(NAME, VALUE) \
|
||||
{ \
|
||||
(uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, strlen(VALUE), \
|
||||
NGHTTP2_NV_FLAG_NONE \
|
||||
}
|
||||
|
||||
struct Connection {
|
||||
SSL *ssl;
|
||||
@@ -98,10 +113,9 @@ struct URI {
|
||||
* Returns copy of string |s| with the length |len|. The returned
|
||||
* string is NULL-terminated.
|
||||
*/
|
||||
static char* strcopy(const char *s, size_t len)
|
||||
{
|
||||
static char *strcopy(const char *s, size_t len) {
|
||||
char *dst;
|
||||
dst = malloc(len+1);
|
||||
dst = malloc(len + 1);
|
||||
memcpy(dst, s, len);
|
||||
dst[len] = '\0';
|
||||
return dst;
|
||||
@@ -110,8 +124,7 @@ static char* strcopy(const char *s, size_t len)
|
||||
/*
|
||||
* Prints error message |msg| and exit.
|
||||
*/
|
||||
static void die(const char *msg)
|
||||
{
|
||||
static void die(const char *msg) {
|
||||
fprintf(stderr, "FATAL: %s\n", msg);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
@@ -120,8 +133,7 @@ static void die(const char *msg)
|
||||
* Prints error containing the function name |func| and message |msg|
|
||||
* and exit.
|
||||
*/
|
||||
static void dief(const char *func, const char *msg)
|
||||
{
|
||||
static void dief(const char *func, const char *msg) {
|
||||
fprintf(stderr, "FATAL: %s: %s\n", func, msg);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
@@ -130,8 +142,7 @@ static void dief(const char *func, const char *msg)
|
||||
* Prints error containing the function name |func| and error code
|
||||
* |error_code| and exit.
|
||||
*/
|
||||
static void diec(const char *func, int error_code)
|
||||
{
|
||||
static void diec(const char *func, int error_code) {
|
||||
fprintf(stderr, "FATAL: %s: error_code=%d, msg=%s\n", func, error_code,
|
||||
nghttp2_strerror(error_code));
|
||||
exit(EXIT_FAILURE);
|
||||
@@ -143,21 +154,19 @@ static void diec(const char *func, int error_code)
|
||||
* bytes actually written. See the documentation of
|
||||
* nghttp2_send_callback for the details.
|
||||
*/
|
||||
static ssize_t send_callback(nghttp2_session *session,
|
||||
const uint8_t *data, size_t length, int flags,
|
||||
void *user_data)
|
||||
{
|
||||
static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
|
||||
size_t length, int flags _U_, void *user_data) {
|
||||
struct Connection *connection;
|
||||
ssize_t rv;
|
||||
connection = (struct Connection*)user_data;
|
||||
int rv;
|
||||
connection = (struct Connection *)user_data;
|
||||
connection->want_io = IO_NONE;
|
||||
ERR_clear_error();
|
||||
rv = SSL_write(connection->ssl, data, length);
|
||||
if(rv < 0) {
|
||||
rv = SSL_write(connection->ssl, data, (int)length);
|
||||
if (rv <= 0) {
|
||||
int err = SSL_get_error(connection->ssl, rv);
|
||||
if(err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
|
||||
connection->want_io = (err == SSL_ERROR_WANT_READ ?
|
||||
WANT_READ : WANT_WRITE);
|
||||
if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
|
||||
connection->want_io =
|
||||
(err == SSL_ERROR_WANT_READ ? WANT_READ : WANT_WRITE);
|
||||
rv = NGHTTP2_ERR_WOULDBLOCK;
|
||||
} else {
|
||||
rv = NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
@@ -172,41 +181,39 @@ static ssize_t send_callback(nghttp2_session *session,
|
||||
* |length| bytes. Returns the number of bytes stored in |buf|. See
|
||||
* the documentation of nghttp2_recv_callback for the details.
|
||||
*/
|
||||
static ssize_t recv_callback(nghttp2_session *session,
|
||||
uint8_t *buf, size_t length, int flags,
|
||||
void *user_data)
|
||||
{
|
||||
static ssize_t recv_callback(nghttp2_session *session _U_, uint8_t *buf,
|
||||
size_t length, int flags _U_, void *user_data) {
|
||||
struct Connection *connection;
|
||||
ssize_t rv;
|
||||
connection = (struct Connection*)user_data;
|
||||
int rv;
|
||||
connection = (struct Connection *)user_data;
|
||||
connection->want_io = IO_NONE;
|
||||
ERR_clear_error();
|
||||
rv = SSL_read(connection->ssl, buf, length);
|
||||
if(rv < 0) {
|
||||
rv = SSL_read(connection->ssl, buf, (int)length);
|
||||
if (rv < 0) {
|
||||
int err = SSL_get_error(connection->ssl, rv);
|
||||
if(err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
|
||||
connection->want_io = (err == SSL_ERROR_WANT_READ ?
|
||||
WANT_READ : WANT_WRITE);
|
||||
if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
|
||||
connection->want_io =
|
||||
(err == SSL_ERROR_WANT_READ ? WANT_READ : WANT_WRITE);
|
||||
rv = NGHTTP2_ERR_WOULDBLOCK;
|
||||
} else {
|
||||
rv = NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
} else if(rv == 0) {
|
||||
} else if (rv == 0) {
|
||||
rv = NGHTTP2_ERR_EOF;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int on_frame_send_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame, void *user_data)
|
||||
{
|
||||
const nghttp2_frame *frame,
|
||||
void *user_data _U_) {
|
||||
size_t i;
|
||||
switch(frame->hd.type) {
|
||||
switch (frame->hd.type) {
|
||||
case NGHTTP2_HEADERS:
|
||||
if(nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)) {
|
||||
if (nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)) {
|
||||
const nghttp2_nv *nva = frame->headers.nva;
|
||||
printf("[INFO] C ----------------------------> S (HEADERS)\n");
|
||||
for(i = 0; i < frame->headers.nvlen; ++i) {
|
||||
for (i = 0; i < frame->headers.nvlen; ++i) {
|
||||
fwrite(nva[i].name, nva[i].namelen, 1, stdout);
|
||||
printf(": ");
|
||||
fwrite(nva[i].value, nva[i].valuelen, 1, stdout);
|
||||
@@ -225,18 +232,18 @@ static int on_frame_send_callback(nghttp2_session *session,
|
||||
}
|
||||
|
||||
static int on_frame_recv_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame, void *user_data)
|
||||
{
|
||||
const nghttp2_frame *frame,
|
||||
void *user_data _U_) {
|
||||
size_t i;
|
||||
switch(frame->hd.type) {
|
||||
switch (frame->hd.type) {
|
||||
case NGHTTP2_HEADERS:
|
||||
if(frame->headers.cat == NGHTTP2_HCAT_RESPONSE) {
|
||||
if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE) {
|
||||
const nghttp2_nv *nva = frame->headers.nva;
|
||||
struct Request *req;
|
||||
req = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
|
||||
if(req) {
|
||||
if (req) {
|
||||
printf("[INFO] C <---------------------------- S (HEADERS)\n");
|
||||
for(i = 0; i < frame->headers.nvlen; ++i) {
|
||||
for (i = 0; i < frame->headers.nvlen; ++i) {
|
||||
fwrite(nva[i].name, nva[i].namelen, 1, stdout);
|
||||
printf(": ");
|
||||
fwrite(nva[i].value, nva[i].valuelen, 1, stdout);
|
||||
@@ -261,19 +268,17 @@ static int on_frame_recv_callback(nghttp2_session *session,
|
||||
* fetch 1 resource in this program, after reception of the response,
|
||||
* we submit GOAWAY and close the session.
|
||||
*/
|
||||
static int on_stream_close_callback(nghttp2_session *session,
|
||||
int32_t stream_id,
|
||||
nghttp2_error_code error_code,
|
||||
void *user_data)
|
||||
{
|
||||
static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||
uint32_t error_code _U_,
|
||||
void *user_data _U_) {
|
||||
struct Request *req;
|
||||
req = nghttp2_session_get_stream_user_data(session, stream_id);
|
||||
if(req) {
|
||||
if (req) {
|
||||
int rv;
|
||||
rv = nghttp2_submit_goaway(session, NGHTTP2_FLAG_NONE, NGHTTP2_NO_ERROR,
|
||||
NULL, 0);
|
||||
if(rv != 0) {
|
||||
diec("nghttp2_submit_goaway", rv);
|
||||
rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
|
||||
|
||||
if (rv != 0) {
|
||||
diec("nghttp2_session_terminate_session", rv);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@@ -285,16 +290,16 @@ static int on_stream_close_callback(nghttp2_session *session,
|
||||
* The implementation of nghttp2_on_data_chunk_recv_callback type. We
|
||||
* use this function to print the received response body.
|
||||
*/
|
||||
static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
||||
int32_t stream_id,
|
||||
static int on_data_chunk_recv_callback(nghttp2_session *session,
|
||||
uint8_t flags _U_, int32_t stream_id,
|
||||
const uint8_t *data, size_t len,
|
||||
void *user_data)
|
||||
{
|
||||
void *user_data _U_) {
|
||||
struct Request *req;
|
||||
req = nghttp2_session_get_stream_user_data(session, stream_id);
|
||||
if(req) {
|
||||
if (req) {
|
||||
printf("[INFO] C <---------------------------- S (DATA chunk)\n"
|
||||
"%lu bytes\n", (unsigned long int)len);
|
||||
"%lu bytes\n",
|
||||
(unsigned long int)len);
|
||||
fwrite(data, 1, len, stdout);
|
||||
printf("\n");
|
||||
}
|
||||
@@ -307,15 +312,22 @@ static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
||||
* always required. Since we use nghttp2_session_recv(), the
|
||||
* recv_callback is also required.
|
||||
*/
|
||||
static void setup_nghttp2_callbacks(nghttp2_session_callbacks *callbacks)
|
||||
{
|
||||
memset(callbacks, 0, sizeof(nghttp2_session_callbacks));
|
||||
callbacks->send_callback = send_callback;
|
||||
callbacks->recv_callback = recv_callback;
|
||||
callbacks->on_frame_send_callback = on_frame_send_callback;
|
||||
callbacks->on_frame_recv_callback = on_frame_recv_callback;
|
||||
callbacks->on_stream_close_callback = on_stream_close_callback;
|
||||
callbacks->on_data_chunk_recv_callback = on_data_chunk_recv_callback;
|
||||
static void setup_nghttp2_callbacks(nghttp2_session_callbacks *callbacks) {
|
||||
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_recv_callback(callbacks, recv_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_frame_send_callback(callbacks,
|
||||
on_frame_send_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
|
||||
on_frame_recv_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_stream_close_callback(
|
||||
callbacks, on_stream_close_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
|
||||
callbacks, on_data_chunk_recv_callback);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -323,16 +335,14 @@ static void setup_nghttp2_callbacks(nghttp2_session_callbacks *callbacks)
|
||||
* HTTP/2 protocol, if server does not offer HTTP/2 the nghttp2
|
||||
* library supports, we terminate program.
|
||||
*/
|
||||
static int select_next_proto_cb(SSL* ssl,
|
||||
unsigned char **out, unsigned char *outlen,
|
||||
const unsigned char *in, unsigned int inlen,
|
||||
void *arg)
|
||||
{
|
||||
static int select_next_proto_cb(SSL *ssl _U_, unsigned char **out,
|
||||
unsigned char *outlen, const unsigned char *in,
|
||||
unsigned int inlen, void *arg _U_) {
|
||||
int rv;
|
||||
/* nghttp2_select_next_protocol() selects HTTP/2 protocol the
|
||||
nghttp2 library supports. */
|
||||
rv = nghttp2_select_next_protocol(out, outlen, in, inlen);
|
||||
if(rv <= 0) {
|
||||
if (rv <= 0) {
|
||||
die("Server did not advertise HTTP/2 protocol");
|
||||
}
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
@@ -341,25 +351,23 @@ static int select_next_proto_cb(SSL* ssl,
|
||||
/*
|
||||
* Setup SSL/TLS context.
|
||||
*/
|
||||
static void init_ssl_ctx(SSL_CTX *ssl_ctx)
|
||||
{
|
||||
static void init_ssl_ctx(SSL_CTX *ssl_ctx) {
|
||||
/* Disable SSLv2 and enable all workarounds for buggy servers */
|
||||
SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2);
|
||||
SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2);
|
||||
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
|
||||
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
|
||||
/* Set NPN callback */
|
||||
SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL);
|
||||
}
|
||||
|
||||
static void ssl_handshake(SSL *ssl, int fd)
|
||||
{
|
||||
static void ssl_handshake(SSL *ssl, int fd) {
|
||||
int rv;
|
||||
if(SSL_set_fd(ssl, fd) == 0) {
|
||||
if (SSL_set_fd(ssl, fd) == 0) {
|
||||
dief("SSL_set_fd", ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
ERR_clear_error();
|
||||
rv = SSL_connect(ssl);
|
||||
if(rv <= 0) {
|
||||
if (rv <= 0) {
|
||||
dief("SSL_connect", ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
}
|
||||
@@ -368,8 +376,7 @@ static void ssl_handshake(SSL *ssl, int fd)
|
||||
* Connects to the host |host| and port |port|. This function returns
|
||||
* the file descriptor of the client socket.
|
||||
*/
|
||||
static int connect_to(const char *host, uint16_t port)
|
||||
{
|
||||
static int connect_to(const char *host, uint16_t port) {
|
||||
struct addrinfo hints;
|
||||
int fd = -1;
|
||||
int rv;
|
||||
@@ -380,17 +387,18 @@ static int connect_to(const char *host, uint16_t port)
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
rv = getaddrinfo(host, service, &hints, &res);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
dief("getaddrinfo", gai_strerror(rv));
|
||||
}
|
||||
for(rp = res; rp; rp = rp->ai_next) {
|
||||
for (rp = res; rp; rp = rp->ai_next) {
|
||||
fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
||||
if(fd == -1) {
|
||||
if (fd == -1) {
|
||||
continue;
|
||||
}
|
||||
while((rv = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 &&
|
||||
errno == EINTR);
|
||||
if(rv == 0) {
|
||||
while ((rv = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 &&
|
||||
errno == EINTR)
|
||||
;
|
||||
if (rv == 0) {
|
||||
break;
|
||||
}
|
||||
close(fd);
|
||||
@@ -400,25 +408,25 @@ static int connect_to(const char *host, uint16_t port)
|
||||
return fd;
|
||||
}
|
||||
|
||||
static void make_non_block(int fd)
|
||||
{
|
||||
static void make_non_block(int fd) {
|
||||
int flags, rv;
|
||||
while((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR);
|
||||
if(flags == -1) {
|
||||
while ((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR)
|
||||
;
|
||||
if (flags == -1) {
|
||||
dief("fcntl", strerror(errno));
|
||||
}
|
||||
while((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR);
|
||||
if(rv == -1) {
|
||||
while ((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR)
|
||||
;
|
||||
if (rv == -1) {
|
||||
dief("fcntl", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static void set_tcp_nodelay(int fd)
|
||||
{
|
||||
static void set_tcp_nodelay(int fd) {
|
||||
int val = 1;
|
||||
int rv;
|
||||
rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val));
|
||||
if(rv == -1) {
|
||||
if (rv == -1) {
|
||||
dief("setsockopt", strerror(errno));
|
||||
}
|
||||
}
|
||||
@@ -426,15 +434,14 @@ static void set_tcp_nodelay(int fd)
|
||||
/*
|
||||
* Update |pollfd| based on the state of |connection|.
|
||||
*/
|
||||
static void ctl_poll(struct pollfd *pollfd, struct Connection *connection)
|
||||
{
|
||||
static void ctl_poll(struct pollfd *pollfd, struct Connection *connection) {
|
||||
pollfd->events = 0;
|
||||
if(nghttp2_session_want_read(connection->session) ||
|
||||
connection->want_io == WANT_READ) {
|
||||
if (nghttp2_session_want_read(connection->session) ||
|
||||
connection->want_io == WANT_READ) {
|
||||
pollfd->events |= POLLIN;
|
||||
}
|
||||
if(nghttp2_session_want_write(connection->session) ||
|
||||
connection->want_io == WANT_WRITE) {
|
||||
if (nghttp2_session_want_write(connection->session) ||
|
||||
connection->want_io == WANT_WRITE) {
|
||||
pollfd->events |= POLLOUT;
|
||||
}
|
||||
}
|
||||
@@ -444,24 +451,20 @@ static void ctl_poll(struct pollfd *pollfd, struct Connection *connection)
|
||||
* function does not send packets; just append the request to the
|
||||
* internal queue in |connection->session|.
|
||||
*/
|
||||
static void submit_request(struct Connection *connection, struct Request *req)
|
||||
{
|
||||
static void submit_request(struct Connection *connection, struct Request *req) {
|
||||
int32_t stream_id;
|
||||
const nghttp2_nv nva[] = {
|
||||
/* Make sure that the last item is NULL */
|
||||
MAKE_NV(":method", "GET"),
|
||||
MAKE_NV_CS(":path", req->path),
|
||||
MAKE_NV(":scheme", "https"),
|
||||
MAKE_NV_CS(":authority", req->hostport),
|
||||
MAKE_NV("accept", "*/*"),
|
||||
MAKE_NV("user-agent", "nghttp2/"NGHTTP2_VERSION)
|
||||
};
|
||||
/* Make sure that the last item is NULL */
|
||||
const nghttp2_nv nva[] = {MAKE_NV(":method", "GET"),
|
||||
MAKE_NV_CS(":path", req->path),
|
||||
MAKE_NV(":scheme", "https"),
|
||||
MAKE_NV_CS(":authority", req->hostport),
|
||||
MAKE_NV("accept", "*/*"),
|
||||
MAKE_NV("user-agent", "nghttp2/" NGHTTP2_VERSION)};
|
||||
|
||||
stream_id = nghttp2_submit_request(connection->session, NULL,
|
||||
nva, sizeof(nva)/sizeof(nva[0]),
|
||||
NULL, req);
|
||||
stream_id = nghttp2_submit_request(connection->session, NULL, nva,
|
||||
sizeof(nva) / sizeof(nva[0]), NULL, req);
|
||||
|
||||
if(stream_id < 0) {
|
||||
if (stream_id < 0) {
|
||||
diec("nghttp2_submit_request", stream_id);
|
||||
}
|
||||
|
||||
@@ -472,21 +475,19 @@ static void submit_request(struct Connection *connection, struct Request *req)
|
||||
/*
|
||||
* Performs the network I/O.
|
||||
*/
|
||||
static void exec_io(struct Connection *connection)
|
||||
{
|
||||
static void exec_io(struct Connection *connection) {
|
||||
int rv;
|
||||
rv = nghttp2_session_recv(connection->session);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
diec("nghttp2_session_recv", rv);
|
||||
}
|
||||
rv = nghttp2_session_send(connection->session);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
diec("nghttp2_session_send", rv);
|
||||
}
|
||||
}
|
||||
|
||||
static void request_init(struct Request *req, const struct URI *uri)
|
||||
{
|
||||
static void request_init(struct Request *req, const struct URI *uri) {
|
||||
req->host = strcopy(uri->host, uri->hostlen);
|
||||
req->port = uri->port;
|
||||
req->path = strcopy(uri->path, uri->pathlen);
|
||||
@@ -494,8 +495,7 @@ static void request_init(struct Request *req, const struct URI *uri)
|
||||
req->stream_id = -1;
|
||||
}
|
||||
|
||||
static void request_free(struct Request *req)
|
||||
{
|
||||
static void request_free(struct Request *req) {
|
||||
free(req->host);
|
||||
free(req->path);
|
||||
free(req->hostport);
|
||||
@@ -504,9 +504,8 @@ static void request_free(struct Request *req)
|
||||
/*
|
||||
* Fetches the resource denoted by |uri|.
|
||||
*/
|
||||
static void fetch_uri(const struct URI *uri)
|
||||
{
|
||||
nghttp2_session_callbacks callbacks;
|
||||
static void fetch_uri(const struct URI *uri) {
|
||||
nghttp2_session_callbacks *callbacks;
|
||||
int fd;
|
||||
SSL_CTX *ssl_ctx;
|
||||
SSL *ssl;
|
||||
@@ -518,20 +517,18 @@ static void fetch_uri(const struct URI *uri)
|
||||
|
||||
request_init(&req, uri);
|
||||
|
||||
setup_nghttp2_callbacks(&callbacks);
|
||||
|
||||
/* Establish connection and setup SSL */
|
||||
fd = connect_to(req.host, req.port);
|
||||
if(fd == -1) {
|
||||
if (fd == -1) {
|
||||
die("Could not open file descriptor");
|
||||
}
|
||||
ssl_ctx = SSL_CTX_new(SSLv23_client_method());
|
||||
if(ssl_ctx == NULL) {
|
||||
if (ssl_ctx == NULL) {
|
||||
dief("SSL_CTX_new", ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
init_ssl_ctx(ssl_ctx);
|
||||
ssl = SSL_new(ssl_ctx);
|
||||
if(ssl == NULL) {
|
||||
if (ssl == NULL) {
|
||||
dief("SSL_new", ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
/* To simplify the program, we perform SSL/TLS handshake in blocking
|
||||
@@ -541,21 +538,30 @@ static void fetch_uri(const struct URI *uri)
|
||||
connection.ssl = ssl;
|
||||
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 */
|
||||
make_non_block(fd);
|
||||
set_tcp_nodelay(fd);
|
||||
|
||||
printf("[INFO] SSL/TLS handshake completed\n");
|
||||
rv = nghttp2_session_client_new(&connection.session, &callbacks,
|
||||
&connection);
|
||||
if(rv != 0) {
|
||||
|
||||
rv = nghttp2_session_callbacks_new(&callbacks);
|
||||
|
||||
if (rv != 0) {
|
||||
diec("nghttp2_session_callbacks_new", rv);
|
||||
}
|
||||
|
||||
setup_nghttp2_callbacks(callbacks);
|
||||
|
||||
rv = nghttp2_session_client_new(&connection.session, callbacks, &connection);
|
||||
|
||||
nghttp2_session_callbacks_del(callbacks);
|
||||
|
||||
if (rv != 0) {
|
||||
diec("nghttp2_session_client_new", rv);
|
||||
}
|
||||
|
||||
nghttp2_submit_settings(connection.session, NGHTTP2_FLAG_NONE, NULL, 0);
|
||||
|
||||
/* Submit the HTTP request to the outbound queue. */
|
||||
submit_request(&connection, &req);
|
||||
|
||||
@@ -563,16 +569,16 @@ static void fetch_uri(const struct URI *uri)
|
||||
ctl_poll(pollfds, &connection);
|
||||
|
||||
/* Event loop */
|
||||
while(nghttp2_session_want_read(connection.session) ||
|
||||
nghttp2_session_want_write(connection.session)) {
|
||||
while (nghttp2_session_want_read(connection.session) ||
|
||||
nghttp2_session_want_write(connection.session)) {
|
||||
int nfds = poll(pollfds, npollfds, -1);
|
||||
if(nfds == -1) {
|
||||
if (nfds == -1) {
|
||||
dief("poll", strerror(errno));
|
||||
}
|
||||
if(pollfds[0].revents & (POLLIN | POLLOUT)) {
|
||||
if (pollfds[0].revents & (POLLIN | POLLOUT)) {
|
||||
exec_io(&connection);
|
||||
}
|
||||
if((pollfds[0].revents & POLLHUP) || (pollfds[0].revents & POLLERR)) {
|
||||
if ((pollfds[0].revents & POLLHUP) || (pollfds[0].revents & POLLERR)) {
|
||||
die("Connection error");
|
||||
}
|
||||
ctl_poll(pollfds, &connection);
|
||||
@@ -588,96 +594,94 @@ static void fetch_uri(const struct URI *uri)
|
||||
request_free(&req);
|
||||
}
|
||||
|
||||
static int parse_uri(struct URI *res, const char *uri)
|
||||
{
|
||||
static int parse_uri(struct URI *res, const char *uri) {
|
||||
/* We only interested in https */
|
||||
size_t len, i, offset;
|
||||
int ipv6addr = 0;
|
||||
memset(res, 0, sizeof(struct URI));
|
||||
len = strlen(uri);
|
||||
if(len < 9 || memcmp("https://", uri, 8) != 0) {
|
||||
if (len < 9 || memcmp("https://", uri, 8) != 0) {
|
||||
return -1;
|
||||
}
|
||||
offset = 8;
|
||||
res->host = res->hostport = &uri[offset];
|
||||
res->hostlen = 0;
|
||||
if(uri[offset] == '[') {
|
||||
if (uri[offset] == '[') {
|
||||
/* IPv6 literal address */
|
||||
++offset;
|
||||
++res->host;
|
||||
ipv6addr = 1;
|
||||
for(i = offset; i < len; ++i) {
|
||||
if(uri[i] == ']') {
|
||||
res->hostlen = i-offset;
|
||||
offset = i+1;
|
||||
for (i = offset; i < len; ++i) {
|
||||
if (uri[i] == ']') {
|
||||
res->hostlen = i - offset;
|
||||
offset = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const char delims[] = ":/?#";
|
||||
for(i = offset; i < len; ++i) {
|
||||
if(strchr(delims, uri[i]) != NULL) {
|
||||
for (i = offset; i < len; ++i) {
|
||||
if (strchr(delims, uri[i]) != NULL) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
res->hostlen = i-offset;
|
||||
res->hostlen = i - offset;
|
||||
offset = i;
|
||||
}
|
||||
if(res->hostlen == 0) {
|
||||
if (res->hostlen == 0) {
|
||||
return -1;
|
||||
}
|
||||
/* Assuming https */
|
||||
res->port = 443;
|
||||
if(offset < len) {
|
||||
if(uri[offset] == ':') {
|
||||
if (offset < len) {
|
||||
if (uri[offset] == ':') {
|
||||
/* port */
|
||||
const char delims[] = "/?#";
|
||||
int port = 0;
|
||||
++offset;
|
||||
for(i = offset; i < len; ++i) {
|
||||
if(strchr(delims, uri[i]) != NULL) {
|
||||
for (i = offset; i < len; ++i) {
|
||||
if (strchr(delims, uri[i]) != NULL) {
|
||||
break;
|
||||
}
|
||||
if('0' <= uri[i] && uri[i] <= '9') {
|
||||
if ('0' <= uri[i] && uri[i] <= '9') {
|
||||
port *= 10;
|
||||
port += uri[i]-'0';
|
||||
if(port > 65535) {
|
||||
port += uri[i] - '0';
|
||||
if (port > 65535) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if(port == 0) {
|
||||
if (port == 0) {
|
||||
return -1;
|
||||
}
|
||||
offset = i;
|
||||
res->port = port;
|
||||
}
|
||||
}
|
||||
res->hostportlen = uri+offset+ipv6addr-res->host;
|
||||
for(i = offset; i < len; ++i) {
|
||||
if(uri[i] == '#') {
|
||||
res->hostportlen = uri + offset + ipv6addr - res->host;
|
||||
for (i = offset; i < len; ++i) {
|
||||
if (uri[i] == '#') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(i-offset == 0) {
|
||||
if (i - offset == 0) {
|
||||
res->path = "/";
|
||||
res->pathlen = 1;
|
||||
} else {
|
||||
res->path = &uri[offset];
|
||||
res->pathlen = i-offset;
|
||||
res->pathlen = i - offset;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int main(int argc, char **argv) {
|
||||
struct URI uri;
|
||||
struct sigaction act;
|
||||
int rv;
|
||||
|
||||
if(argc < 2) {
|
||||
if (argc < 2) {
|
||||
die("Specify a https URI");
|
||||
}
|
||||
|
||||
@@ -687,9 +691,11 @@ int main(int argc, char **argv)
|
||||
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
OpenSSL_add_all_algorithms();
|
||||
OPENSSL_config(NULL);
|
||||
|
||||
rv = parse_uri(&uri, argv[1]);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
die("parse_uri failed");
|
||||
}
|
||||
fetch_uri(&uri);
|
||||
|
||||
206
examples/deflate.c
Normal file
206
examples/deflate.c
Normal file
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /* !HAVE_CONFIG_H */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
#define MAKE_NV(K, V) \
|
||||
{ \
|
||||
(uint8_t *) K, (uint8_t *)V, sizeof(K) - 1, sizeof(V) - 1, \
|
||||
NGHTTP2_NV_FLAG_NONE \
|
||||
}
|
||||
|
||||
static void deflate(nghttp2_hd_deflater *deflater,
|
||||
nghttp2_hd_inflater *inflater, const nghttp2_nv *const nva,
|
||||
size_t nvlen);
|
||||
|
||||
static int inflate_header_block(nghttp2_hd_inflater *inflater, uint8_t *in,
|
||||
size_t inlen, int final);
|
||||
|
||||
int main(int argc _U_, char **argv _U_) {
|
||||
int rv;
|
||||
nghttp2_hd_deflater *deflater;
|
||||
nghttp2_hd_inflater *inflater;
|
||||
/* Define 1st header set. This is looks like a HTTP request. */
|
||||
nghttp2_nv nva1[] = {
|
||||
MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "example.org"),
|
||||
MAKE_NV(":path", "/"), MAKE_NV("user-agent", "libnghttp2"),
|
||||
MAKE_NV("accept-encoding", "gzip, deflate")};
|
||||
/* Define 2nd header set */
|
||||
nghttp2_nv nva2[] = {MAKE_NV(":scheme", "https"),
|
||||
MAKE_NV(":authority", "example.org"),
|
||||
MAKE_NV(":path", "/stylesheet/style.css"),
|
||||
MAKE_NV("user-agent", "libnghttp2"),
|
||||
MAKE_NV("accept-encoding", "gzip, deflate"),
|
||||
MAKE_NV("referer", "https://example.org")};
|
||||
|
||||
rv = nghttp2_hd_deflate_new(&deflater, 4096);
|
||||
|
||||
if (rv != 0) {
|
||||
fprintf(stderr, "nghttp2_hd_deflate_init failed with error: %s\n",
|
||||
nghttp2_strerror(rv));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
rv = nghttp2_hd_inflate_new(&inflater);
|
||||
|
||||
if (rv != 0) {
|
||||
fprintf(stderr, "nghttp2_hd_inflate_init failed with error: %s\n",
|
||||
nghttp2_strerror(rv));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Encode and decode 1st header set */
|
||||
deflate(deflater, inflater, nva1, sizeof(nva1) / sizeof(nva1[0]));
|
||||
|
||||
/* Encode and decode 2nd header set, using differential encoding
|
||||
using state after encoding 1st header set. */
|
||||
deflate(deflater, inflater, nva2, sizeof(nva2) / sizeof(nva2[0]));
|
||||
|
||||
nghttp2_hd_inflate_del(inflater);
|
||||
nghttp2_hd_deflate_del(deflater);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void deflate(nghttp2_hd_deflater *deflater,
|
||||
nghttp2_hd_inflater *inflater, const nghttp2_nv *const nva,
|
||||
size_t nvlen) {
|
||||
ssize_t rv;
|
||||
uint8_t *buf;
|
||||
size_t buflen;
|
||||
size_t outlen;
|
||||
size_t i;
|
||||
size_t sum;
|
||||
|
||||
sum = 0;
|
||||
|
||||
for (i = 0; i < nvlen; ++i) {
|
||||
sum += nva[i].namelen + nva[i].valuelen;
|
||||
}
|
||||
|
||||
printf("Input (%zu byte(s)):\n\n", sum);
|
||||
|
||||
for (i = 0; i < nvlen; ++i) {
|
||||
fwrite(nva[i].name, nva[i].namelen, 1, stdout);
|
||||
printf(": ");
|
||||
fwrite(nva[i].value, nva[i].valuelen, 1, stdout);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
buflen = nghttp2_hd_deflate_bound(deflater, nva, nvlen);
|
||||
buf = malloc(buflen);
|
||||
|
||||
rv = nghttp2_hd_deflate_hd(deflater, buf, buflen, nva, nvlen);
|
||||
|
||||
if (rv < 0) {
|
||||
fprintf(stderr, "nghttp2_hd_deflate_hd() failed with error: %s\n",
|
||||
nghttp2_strerror((int)rv));
|
||||
|
||||
free(buf);
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
outlen = rv;
|
||||
|
||||
printf("\nDeflate (%zu byte(s), ratio %.02f):\n\n", outlen,
|
||||
sum == 0 ? 0 : (double)outlen / sum);
|
||||
|
||||
for (i = 0; i < outlen; ++i) {
|
||||
if ((i & 0x0fu) == 0) {
|
||||
printf("%08zX: ", i);
|
||||
}
|
||||
|
||||
printf("%02X ", buf[i]);
|
||||
|
||||
if (((i + 1) & 0x0fu) == 0) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n\nInflate:\n\n");
|
||||
|
||||
/* We pass 1 to final parameter, because buf contains whole deflated
|
||||
header data. */
|
||||
rv = inflate_header_block(inflater, buf, outlen, 1);
|
||||
|
||||
if (rv != 0) {
|
||||
free(buf);
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
printf("\n-----------------------------------------------------------"
|
||||
"--------------------\n");
|
||||
|
||||
free(buf);
|
||||
}
|
||||
|
||||
int inflate_header_block(nghttp2_hd_inflater *inflater, uint8_t *in,
|
||||
size_t inlen, int final) {
|
||||
ssize_t rv;
|
||||
|
||||
for (;;) {
|
||||
nghttp2_nv nv;
|
||||
int inflate_flags = 0;
|
||||
size_t proclen;
|
||||
|
||||
rv = nghttp2_hd_inflate_hd(inflater, &nv, &inflate_flags, in, inlen, final);
|
||||
|
||||
if (rv < 0) {
|
||||
fprintf(stderr, "inflate failed with error code %zd", rv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
proclen = rv;
|
||||
|
||||
in += proclen;
|
||||
inlen -= proclen;
|
||||
|
||||
if (inflate_flags & NGHTTP2_HD_INFLATE_EMIT) {
|
||||
fwrite(nv.name, nv.namelen, 1, stderr);
|
||||
fprintf(stderr, ": ");
|
||||
fwrite(nv.value, nv.valuelen, 1, stderr);
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) {
|
||||
nghttp2_hd_inflate_end_headers(inflater);
|
||||
break;
|
||||
}
|
||||
|
||||
if ((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && inlen == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -22,16 +22,27 @@
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif /* HAVE_UNISTD_H */
|
||||
#ifdef HAVE_SYS_SOCKET_H
|
||||
#include <sys/socket.h>
|
||||
#endif /* HAVE_SYS_SOCKET_H */
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
#include <netinet/in.h>
|
||||
#endif /* HAVE_NETINET_IN_H */
|
||||
#include <netinet/tcp.h>
|
||||
#include <err.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/conf.h>
|
||||
|
||||
#include <event.h>
|
||||
#include <event2/event.h>
|
||||
@@ -42,14 +53,14 @@
|
||||
|
||||
#include "http-parser/http_parser.h"
|
||||
|
||||
#define ARRLEN(x) (sizeof(x)/sizeof(x[0]))
|
||||
#define ARRLEN(x) (sizeof(x) / sizeof(x[0]))
|
||||
|
||||
typedef struct {
|
||||
/* The NULL-terminated URI string to retreive. */
|
||||
/* The NULL-terminated URI string to retrieve. */
|
||||
const char *uri;
|
||||
/* Parsed result of the |uri| */
|
||||
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;
|
||||
/* The path portion of the |uri|, including query, not
|
||||
NULL-terminated */
|
||||
@@ -69,9 +80,8 @@ typedef struct {
|
||||
http2_stream_data *stream_data;
|
||||
} http2_session_data;
|
||||
|
||||
static http2_stream_data* create_http2_stream_data(const char *uri,
|
||||
struct http_parser_url *u)
|
||||
{
|
||||
static http2_stream_data *create_http2_stream_data(const char *uri,
|
||||
struct http_parser_url *u) {
|
||||
/* MAX 5 digits (max 65535) + 1 ':' + 1 NULL (because of snprintf) */
|
||||
size_t extra = 7;
|
||||
http2_stream_data *stream_data = malloc(sizeof(http2_stream_data));
|
||||
@@ -82,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->authority = malloc(stream_data->authoritylen + extra);
|
||||
memcpy(stream_data->authority,
|
||||
&uri[u->field_data[UF_HOST].off], u->field_data[UF_HOST].len);
|
||||
if(u->field_set & (1 << UF_PORT)) {
|
||||
memcpy(stream_data->authority, &uri[u->field_data[UF_HOST].off],
|
||||
u->field_data[UF_HOST].len);
|
||||
if (u->field_set & (1 << UF_PORT)) {
|
||||
stream_data->authoritylen +=
|
||||
snprintf(stream_data->authority + u->field_data[UF_HOST].len, extra,
|
||||
":%u", u->port);
|
||||
snprintf(stream_data->authority + u->field_data[UF_HOST].len, extra,
|
||||
":%u", u->port);
|
||||
}
|
||||
|
||||
stream_data->pathlen = 0;
|
||||
if(u->field_set & (1 << UF_PATH)) {
|
||||
/* If we don't have path in URI, we use "/" as path. */
|
||||
stream_data->pathlen = 1;
|
||||
if (u->field_set & (1 << UF_PATH)) {
|
||||
stream_data->pathlen = u->field_data[UF_PATH].len;
|
||||
}
|
||||
if(u->field_set & (1 << UF_QUERY)) {
|
||||
if (u->field_set & (1 << UF_QUERY)) {
|
||||
/* +1 for '?' character */
|
||||
stream_data->pathlen += u->field_data[UF_QUERY].len + 1;
|
||||
}
|
||||
if(stream_data->pathlen > 0) {
|
||||
stream_data->path = malloc(stream_data->pathlen);
|
||||
if(u->field_set & (1 << UF_PATH)) {
|
||||
memcpy(stream_data->path,
|
||||
&uri[u->field_data[UF_PATH].off], u->field_data[UF_PATH].len);
|
||||
}
|
||||
if(u->field_set & (1 << UF_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);
|
||||
}
|
||||
|
||||
stream_data->path = malloc(stream_data->pathlen);
|
||||
if (u->field_set & (1 << UF_PATH)) {
|
||||
memcpy(stream_data->path, &uri[u->field_data[UF_PATH].off],
|
||||
u->field_data[UF_PATH].len);
|
||||
} 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;
|
||||
}
|
||||
|
||||
static void delete_http2_stream_data(http2_stream_data *stream_data)
|
||||
{
|
||||
static void delete_http2_stream_data(http2_stream_data *stream_data) {
|
||||
free(stream_data->path);
|
||||
free(stream_data->authority);
|
||||
free(stream_data);
|
||||
}
|
||||
|
||||
/* Initializes |session_data| */
|
||||
static http2_session_data *create_http2_session_data(struct event_base *evbase)
|
||||
{
|
||||
static http2_session_data *
|
||||
create_http2_session_data(struct event_base *evbase) {
|
||||
http2_session_data *session_data = malloc(sizeof(http2_session_data));
|
||||
|
||||
memset(session_data, 0, sizeof(http2_session_data));
|
||||
@@ -131,11 +144,10 @@ static http2_session_data *create_http2_session_data(struct event_base *evbase)
|
||||
return session_data;
|
||||
}
|
||||
|
||||
static void delete_http2_session_data(http2_session_data *session_data)
|
||||
{
|
||||
static void delete_http2_session_data(http2_session_data *session_data) {
|
||||
SSL *ssl = bufferevent_openssl_get_ssl(session_data->bev);
|
||||
|
||||
if(ssl) {
|
||||
if (ssl) {
|
||||
SSL_shutdown(ssl);
|
||||
}
|
||||
bufferevent_free(session_data->bev);
|
||||
@@ -144,17 +156,15 @@ static void delete_http2_session_data(http2_session_data *session_data)
|
||||
session_data->dnsbase = NULL;
|
||||
nghttp2_session_del(session_data->session);
|
||||
session_data->session = NULL;
|
||||
if(session_data->stream_data) {
|
||||
if (session_data->stream_data) {
|
||||
delete_http2_stream_data(session_data->stream_data);
|
||||
session_data->stream_data = NULL;
|
||||
}
|
||||
free(session_data);
|
||||
}
|
||||
|
||||
static void print_header(FILE *f,
|
||||
const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen)
|
||||
{
|
||||
static void print_header(FILE *f, const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen) {
|
||||
fwrite(name, namelen, 1, f);
|
||||
fprintf(f, ": ");
|
||||
fwrite(value, valuelen, 1, f);
|
||||
@@ -164,13 +174,10 @@ static void print_header(FILE *f,
|
||||
/* Print HTTP headers to |f|. Please note that this function does not
|
||||
take into account that header name and value are sequence of
|
||||
octets, therefore they may contain non-printable characters. */
|
||||
static void print_headers(FILE *f, nghttp2_nv *nva, size_t nvlen)
|
||||
{
|
||||
static void print_headers(FILE *f, nghttp2_nv *nva, size_t nvlen) {
|
||||
size_t i;
|
||||
for(i = 0; i < nvlen; ++i) {
|
||||
print_header(f,
|
||||
nva[i].name, nva[i].namelen,
|
||||
nva[i].value, nva[i].valuelen);
|
||||
for (i = 0; i < nvlen; ++i) {
|
||||
print_header(f, nva[i].name, nva[i].namelen, nva[i].value, nva[i].valuelen);
|
||||
}
|
||||
fprintf(f, "\n");
|
||||
}
|
||||
@@ -178,11 +185,9 @@ static void print_headers(FILE *f, nghttp2_nv *nva, size_t nvlen)
|
||||
/* nghttp2_send_callback. Here we transmit the |data|, |length| bytes,
|
||||
to the network. Because we are using libevent bufferevent, we just
|
||||
write those bytes into bufferevent buffer. */
|
||||
static ssize_t send_callback(nghttp2_session *session,
|
||||
const uint8_t *data, size_t length,
|
||||
int flags, void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
|
||||
size_t length, int flags _U_, void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
struct bufferevent *bev = session_data->bev;
|
||||
bufferevent_write(bev, data, length);
|
||||
return length;
|
||||
@@ -190,18 +195,16 @@ static ssize_t send_callback(nghttp2_session *session,
|
||||
|
||||
/* nghttp2_on_header_callback: Called when nghttp2 library emits
|
||||
single header name/value pair. */
|
||||
static int on_header_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame,
|
||||
const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen,
|
||||
uint8_t flags,
|
||||
void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
switch(frame->hd.type) {
|
||||
static int on_header_callback(nghttp2_session *session _U_,
|
||||
const nghttp2_frame *frame, const uint8_t *name,
|
||||
size_t namelen, const uint8_t *value,
|
||||
size_t valuelen, uint8_t flags _U_,
|
||||
void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
switch (frame->hd.type) {
|
||||
case NGHTTP2_HEADERS:
|
||||
if(frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
|
||||
session_data->stream_data->stream_id == frame->hd.stream_id) {
|
||||
if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
|
||||
session_data->stream_data->stream_id == frame->hd.stream_id) {
|
||||
/* Print response headers for the initiated request. */
|
||||
print_header(stderr, name, namelen, value, valuelen);
|
||||
break;
|
||||
@@ -212,15 +215,14 @@ static int on_header_callback(nghttp2_session *session,
|
||||
|
||||
/* nghttp2_on_begin_headers_callback: Called when nghttp2 library gets
|
||||
started to receive header block. */
|
||||
static int on_begin_headers_callback(nghttp2_session *session,
|
||||
static int on_begin_headers_callback(nghttp2_session *session _U_,
|
||||
const nghttp2_frame *frame,
|
||||
void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
switch(frame->hd.type) {
|
||||
void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
switch (frame->hd.type) {
|
||||
case NGHTTP2_HEADERS:
|
||||
if(frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
|
||||
session_data->stream_data->stream_id == frame->hd.stream_id) {
|
||||
if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
|
||||
session_data->stream_data->stream_id == frame->hd.stream_id) {
|
||||
fprintf(stderr, "Response headers for stream ID=%d:\n",
|
||||
frame->hd.stream_id);
|
||||
}
|
||||
@@ -231,14 +233,13 @@ static int on_begin_headers_callback(nghttp2_session *session,
|
||||
|
||||
/* nghttp2_on_frame_recv_callback: Called when nghttp2 library
|
||||
received a complete frame from the remote peer. */
|
||||
static int on_frame_recv_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame, void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
switch(frame->hd.type) {
|
||||
static int on_frame_recv_callback(nghttp2_session *session _U_,
|
||||
const nghttp2_frame *frame, void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
switch (frame->hd.type) {
|
||||
case NGHTTP2_HEADERS:
|
||||
if(frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
|
||||
session_data->stream_data->stream_id == frame->hd.stream_id) {
|
||||
if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
|
||||
session_data->stream_data->stream_id == frame->hd.stream_id) {
|
||||
fprintf(stderr, "All headers received\n");
|
||||
}
|
||||
break;
|
||||
@@ -251,13 +252,12 @@ static int on_frame_recv_callback(nghttp2_session *session,
|
||||
is meant to the stream we initiated, print the received data in
|
||||
stdout, so that the user can redirect its output to the file
|
||||
easily. */
|
||||
static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
||||
int32_t stream_id,
|
||||
static int on_data_chunk_recv_callback(nghttp2_session *session _U_,
|
||||
uint8_t flags _U_, int32_t stream_id,
|
||||
const uint8_t *data, size_t len,
|
||||
void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
if(session_data->stream_data->stream_id == stream_id) {
|
||||
void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
if (session_data->stream_data->stream_id == stream_id) {
|
||||
fwrite(data, len, 1, stdout);
|
||||
}
|
||||
return 0;
|
||||
@@ -267,19 +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
|
||||
stream), if it is closed, we send GOAWAY and tear down the
|
||||
session */
|
||||
static int on_stream_close_callback(nghttp2_session *session,
|
||||
int32_t stream_id,
|
||||
nghttp2_error_code error_code,
|
||||
void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||
uint32_t error_code, void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
int rv;
|
||||
|
||||
if(session_data->stream_data->stream_id == stream_id) {
|
||||
fprintf(stderr, "Stream %d closed with error_code=%d\n",
|
||||
stream_id, error_code);
|
||||
if (session_data->stream_data->stream_id == stream_id) {
|
||||
fprintf(stderr, "Stream %d closed with error_code=%d\n", stream_id,
|
||||
error_code);
|
||||
rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
}
|
||||
@@ -289,104 +286,111 @@ static int on_stream_close_callback(nghttp2_session *session,
|
||||
/* NPN TLS extension client callback. We check that server advertised
|
||||
the HTTP/2 protocol the nghttp2 library supports. If not, exit
|
||||
the program. */
|
||||
static int select_next_proto_cb(SSL* ssl,
|
||||
unsigned char **out, unsigned char *outlen,
|
||||
const unsigned char *in, unsigned int inlen,
|
||||
void *arg)
|
||||
{
|
||||
if(nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) {
|
||||
static int select_next_proto_cb(SSL *ssl _U_, unsigned char **out,
|
||||
unsigned char *outlen, const unsigned char *in,
|
||||
unsigned int inlen, void *arg _U_) {
|
||||
if (nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) {
|
||||
errx(1, "Server did not advertise " NGHTTP2_PROTO_VERSION_ID);
|
||||
}
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
|
||||
/* Create SSL_CTX. */
|
||||
static SSL_CTX* create_ssl_ctx(void)
|
||||
{
|
||||
static SSL_CTX *create_ssl_ctx(void) {
|
||||
SSL_CTX *ssl_ctx;
|
||||
ssl_ctx = SSL_CTX_new(SSLv23_client_method());
|
||||
if(!ssl_ctx) {
|
||||
if (!ssl_ctx) {
|
||||
errx(1, "Could not create SSL/TLS context: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
SSL_CTX_set_options(ssl_ctx,
|
||||
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_COMPRESSION |
|
||||
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
|
||||
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
|
||||
SSL_OP_NO_COMPRESSION |
|
||||
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
|
||||
SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL);
|
||||
return ssl_ctx;
|
||||
}
|
||||
|
||||
/* Create SSL object */
|
||||
static SSL* create_ssl(SSL_CTX *ssl_ctx)
|
||||
{
|
||||
static SSL *create_ssl(SSL_CTX *ssl_ctx) {
|
||||
SSL *ssl;
|
||||
ssl = SSL_new(ssl_ctx);
|
||||
if(!ssl) {
|
||||
if (!ssl) {
|
||||
errx(1, "Could not create SSL/TLS session object: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
return ssl;
|
||||
}
|
||||
|
||||
static void initialize_nghttp2_session(http2_session_data *session_data)
|
||||
{
|
||||
nghttp2_session_callbacks callbacks;
|
||||
static void initialize_nghttp2_session(http2_session_data *session_data) {
|
||||
nghttp2_session_callbacks *callbacks;
|
||||
|
||||
memset(&callbacks, 0, sizeof(callbacks));
|
||||
nghttp2_session_callbacks_new(&callbacks);
|
||||
|
||||
callbacks.send_callback = send_callback;
|
||||
callbacks.on_frame_recv_callback = on_frame_recv_callback;
|
||||
callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback;
|
||||
callbacks.on_stream_close_callback = on_stream_close_callback;
|
||||
callbacks.on_header_callback = on_header_callback;
|
||||
callbacks.on_begin_headers_callback = on_begin_headers_callback;
|
||||
nghttp2_session_client_new(&session_data->session, &callbacks, session_data);
|
||||
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
|
||||
on_frame_recv_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
|
||||
callbacks, on_data_chunk_recv_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_stream_close_callback(
|
||||
callbacks, on_stream_close_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_header_callback(callbacks,
|
||||
on_header_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_begin_headers_callback(
|
||||
callbacks, on_begin_headers_callback);
|
||||
|
||||
nghttp2_session_client_new(&session_data->session, callbacks, session_data);
|
||||
|
||||
nghttp2_session_callbacks_del(callbacks);
|
||||
}
|
||||
|
||||
static void send_client_connection_header(http2_session_data *session_data)
|
||||
{
|
||||
static void send_client_connection_header(http2_session_data *session_data) {
|
||||
nghttp2_settings_entry iv[1] = {
|
||||
{ NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 }
|
||||
};
|
||||
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
|
||||
int rv;
|
||||
|
||||
bufferevent_write(session_data->bev,
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
|
||||
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE,
|
||||
iv, ARRLEN(iv));
|
||||
if(rv != 0) {
|
||||
/* client 24 bytes magic string will be sent by nghttp2 library */
|
||||
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
|
||||
ARRLEN(iv));
|
||||
if (rv != 0) {
|
||||
errx(1, "Could not submit SETTINGS: %s", nghttp2_strerror(rv));
|
||||
}
|
||||
}
|
||||
|
||||
#define MAKE_NV(NAME, VALUE, VALUELEN) \
|
||||
{ (uint8_t*)NAME, (uint8_t*)VALUE, sizeof(NAME) - 1, VALUELEN, \
|
||||
NGHTTP2_NV_FLAG_NONE }
|
||||
#define MAKE_NV(NAME, VALUE, VALUELEN) \
|
||||
{ \
|
||||
(uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, VALUELEN, \
|
||||
NGHTTP2_NV_FLAG_NONE \
|
||||
}
|
||||
|
||||
#define MAKE_NV2(NAME, VALUE) \
|
||||
{ (uint8_t*)NAME, (uint8_t*)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \
|
||||
NGHTTP2_NV_FLAG_NONE }
|
||||
#define MAKE_NV2(NAME, VALUE) \
|
||||
{ \
|
||||
(uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \
|
||||
NGHTTP2_NV_FLAG_NONE \
|
||||
}
|
||||
|
||||
/* Send HTTP request to the remote peer */
|
||||
static void submit_request(http2_session_data *session_data)
|
||||
{
|
||||
static void submit_request(http2_session_data *session_data) {
|
||||
int32_t stream_id;
|
||||
http2_stream_data *stream_data = session_data->stream_data;
|
||||
const char *uri = stream_data->uri;
|
||||
const struct http_parser_url *u = stream_data->u;
|
||||
nghttp2_nv hdrs[] = {
|
||||
MAKE_NV2(":method", "GET"),
|
||||
MAKE_NV(":scheme",
|
||||
&uri[u->field_data[UF_SCHEMA].off], u->field_data[UF_SCHEMA].len),
|
||||
MAKE_NV(":authority", stream_data->authority, stream_data->authoritylen),
|
||||
MAKE_NV(":path", stream_data->path, stream_data->pathlen)
|
||||
};
|
||||
MAKE_NV2(":method", "GET"),
|
||||
MAKE_NV(":scheme", &uri[u->field_data[UF_SCHEMA].off],
|
||||
u->field_data[UF_SCHEMA].len),
|
||||
MAKE_NV(":authority", stream_data->authority, stream_data->authoritylen),
|
||||
MAKE_NV(":path", stream_data->path, stream_data->pathlen)};
|
||||
fprintf(stderr, "Request headers:\n");
|
||||
print_headers(stderr, hdrs, ARRLEN(hdrs));
|
||||
stream_id = nghttp2_submit_request(session_data->session, NULL,
|
||||
hdrs, ARRLEN(hdrs), NULL, stream_data);
|
||||
if(stream_id < 0) {
|
||||
stream_id = nghttp2_submit_request(session_data->session, NULL, hdrs,
|
||||
ARRLEN(hdrs), NULL, stream_data);
|
||||
if (stream_id < 0) {
|
||||
errx(1, "Could not submit HTTP request: %s", nghttp2_strerror(stream_id));
|
||||
}
|
||||
|
||||
@@ -395,12 +399,11 @@ static void submit_request(http2_session_data *session_data)
|
||||
|
||||
/* Serialize the frame and send (or buffer) the data to
|
||||
bufferevent. */
|
||||
static int session_send(http2_session_data *session_data)
|
||||
{
|
||||
static int session_send(http2_session_data *session_data) {
|
||||
int rv;
|
||||
|
||||
rv = nghttp2_session_send(session_data->session);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||
return -1;
|
||||
}
|
||||
@@ -411,21 +414,25 @@ static int session_send(http2_session_data *session_data)
|
||||
of bufferevent and feed them to nghttp2 library. This may invoke
|
||||
nghttp2 callbacks. It may also queues the frame in nghttp2 session
|
||||
context. To send them, we call session_send() in the end. */
|
||||
static void readcb(struct bufferevent *bev, void *ptr)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
||||
int rv;
|
||||
static void readcb(struct bufferevent *bev, void *ptr) {
|
||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||
ssize_t readlen;
|
||||
struct evbuffer *input = bufferevent_get_input(bev);
|
||||
size_t datalen = evbuffer_get_length(input);
|
||||
unsigned char *data = evbuffer_pullup(input, -1);
|
||||
rv = nghttp2_session_mem_recv(session_data->session, data, datalen);
|
||||
if(rv < 0) {
|
||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||
|
||||
readlen = nghttp2_session_mem_recv(session_data->session, data, datalen);
|
||||
if (readlen < 0) {
|
||||
warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
evbuffer_drain(input, rv);
|
||||
if(session_send(session_data) != 0) {
|
||||
if (evbuffer_drain(input, readlen) != 0) {
|
||||
warnx("Fatal error: evbuffer_drain failed");
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
if (session_send(session_data) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
@@ -435,12 +442,11 @@ static void readcb(struct bufferevent *bev, void *ptr)
|
||||
receiving GOAWAY, we check the some conditions on the nghttp2
|
||||
library and output buffer of bufferevent. If it indicates we have
|
||||
no business to this session, tear down the connection. */
|
||||
static void writecb(struct bufferevent *bev, void *ptr)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
||||
if(nghttp2_session_want_read(session_data->session) == 0 &&
|
||||
nghttp2_session_want_write(session_data->session) == 0 &&
|
||||
evbuffer_get_length(bufferevent_get_output(session_data->bev)) == 0) {
|
||||
static void writecb(struct bufferevent *bev _U_, void *ptr) {
|
||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||
if (nghttp2_session_want_read(session_data->session) == 0 &&
|
||||
nghttp2_session_want_write(session_data->session) == 0 &&
|
||||
evbuffer_get_length(bufferevent_get_output(session_data->bev)) == 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
}
|
||||
}
|
||||
@@ -450,10 +456,9 @@ static void writecb(struct bufferevent *bev, void *ptr)
|
||||
peer verification. After SSL/TLS handshake is over, initialize
|
||||
nghttp2 library session, and send client connection header. Then
|
||||
send HTTP request. */
|
||||
static void eventcb(struct bufferevent *bev, short events, void *ptr)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
||||
if(events & BEV_EVENT_CONNECTED) {
|
||||
static void eventcb(struct bufferevent *bev, short events, void *ptr) {
|
||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||
if (events & BEV_EVENT_CONNECTED) {
|
||||
int fd = bufferevent_getfd(bev);
|
||||
int val = 1;
|
||||
fprintf(stderr, "Connected\n");
|
||||
@@ -461,41 +466,38 @@ static void eventcb(struct bufferevent *bev, short events, void *ptr)
|
||||
initialize_nghttp2_session(session_data);
|
||||
send_client_connection_header(session_data);
|
||||
submit_request(session_data);
|
||||
if(session_send(session_data) != 0) {
|
||||
if (session_send(session_data) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(events & BEV_EVENT_EOF) {
|
||||
if (events & BEV_EVENT_EOF) {
|
||||
warnx("Disconnected from the remote host");
|
||||
} else if(events & BEV_EVENT_ERROR) {
|
||||
} else if (events & BEV_EVENT_ERROR) {
|
||||
warnx("Network error");
|
||||
} else if(events & BEV_EVENT_TIMEOUT) {
|
||||
} else if (events & BEV_EVENT_TIMEOUT) {
|
||||
warnx("Timeout");
|
||||
}
|
||||
delete_http2_session_data(session_data);
|
||||
}
|
||||
|
||||
/* Start connecting to the remote peer |host:port| */
|
||||
static void initiate_connection(struct event_base *evbase,
|
||||
SSL_CTX *ssl_ctx,
|
||||
static void initiate_connection(struct event_base *evbase, SSL_CTX *ssl_ctx,
|
||||
const char *host, uint16_t port,
|
||||
http2_session_data *session_data)
|
||||
{
|
||||
http2_session_data *session_data) {
|
||||
int rv;
|
||||
struct bufferevent *bev;
|
||||
SSL *ssl;
|
||||
|
||||
ssl = create_ssl(ssl_ctx);
|
||||
bev = bufferevent_openssl_socket_new(evbase, -1, ssl,
|
||||
BUFFEREVENT_SSL_CONNECTING,
|
||||
BEV_OPT_DEFER_CALLBACKS |
|
||||
BEV_OPT_CLOSE_ON_FREE);
|
||||
bev = bufferevent_openssl_socket_new(
|
||||
evbase, -1, ssl, BUFFEREVENT_SSL_CONNECTING,
|
||||
BEV_OPT_DEFER_CALLBACKS | BEV_OPT_CLOSE_ON_FREE);
|
||||
bufferevent_setcb(bev, readcb, writecb, eventcb, session_data);
|
||||
rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase,
|
||||
AF_UNSPEC, host, port);
|
||||
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
errx(1, "Could not connect to the remote host %s", host);
|
||||
}
|
||||
session_data->bev = bev;
|
||||
@@ -503,8 +505,7 @@ static void initiate_connection(struct event_base *evbase,
|
||||
|
||||
/* Get resource denoted by the |uri|. The debug and error messages are
|
||||
printed in stderr, while the response body is printed in stdout. */
|
||||
static void run(const char *uri)
|
||||
{
|
||||
static void run(const char *uri) {
|
||||
struct http_parser_url u;
|
||||
char *host;
|
||||
uint16_t port;
|
||||
@@ -515,11 +516,11 @@ static void run(const char *uri)
|
||||
|
||||
/* Parse the |uri| and stores its components in |u| */
|
||||
rv = http_parser_parse_url(uri, strlen(uri), 0, &u);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
errx(1, "Could not parse URI %s", uri);
|
||||
}
|
||||
host = strndup(&uri[u.field_data[UF_HOST].off], u.field_data[UF_HOST].len);
|
||||
if(!(u.field_set & (1 << UF_PORT))) {
|
||||
if (!(u.field_set & (1 << UF_PORT))) {
|
||||
port = 443;
|
||||
} else {
|
||||
port = u.port;
|
||||
@@ -542,11 +543,10 @@ static void run(const char *uri)
|
||||
SSL_CTX_free(ssl_ctx);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int main(int argc, char **argv) {
|
||||
struct sigaction act;
|
||||
|
||||
if(argc < 2) {
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "Usage: libevent-client HTTPS_URI\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
@@ -557,6 +557,8 @@ int main(int argc, char **argv)
|
||||
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
OpenSSL_add_all_algorithms();
|
||||
OPENSSL_config(NULL);
|
||||
|
||||
run(argv[1]);
|
||||
return 0;
|
||||
|
||||
@@ -22,20 +22,35 @@
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_SYS_SOCKET_H
|
||||
#include <sys/socket.h>
|
||||
#endif /* HAVE_SYS_SOCKET_H */
|
||||
#ifdef HAVE_NETDB_H
|
||||
#include <netdb.h>
|
||||
#endif /* HAVE_NETDB_H */
|
||||
#include <signal.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif /* HAVE_UNISTD_H */
|
||||
#include <sys/stat.h>
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif /* HAVE_FCNTL_H */
|
||||
#include <ctype.h>
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
#include <netinet/in.h>
|
||||
#endif /* HAVE_NETINET_IN_H */
|
||||
#include <netinet/tcp.h>
|
||||
#include <err.h>
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/conf.h>
|
||||
|
||||
#include <event.h>
|
||||
#include <event2/event.h>
|
||||
@@ -46,11 +61,13 @@
|
||||
|
||||
#define OUTPUT_WOULDBLOCK_THRESHOLD (1 << 16)
|
||||
|
||||
#define ARRLEN(x) (sizeof(x)/sizeof(x[0]))
|
||||
#define ARRLEN(x) (sizeof(x) / sizeof(x[0]))
|
||||
|
||||
#define MAKE_NV(NAME, VALUE) \
|
||||
{ (uint8_t*)NAME, (uint8_t*)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \
|
||||
NGHTTP2_NV_FLAG_NONE }
|
||||
#define MAKE_NV(NAME, VALUE) \
|
||||
{ \
|
||||
(uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \
|
||||
NGHTTP2_NV_FLAG_NONE \
|
||||
}
|
||||
|
||||
struct app_context;
|
||||
typedef struct app_context app_context;
|
||||
@@ -68,7 +85,6 @@ typedef struct http2_session_data {
|
||||
app_context *app_ctx;
|
||||
nghttp2_session *session;
|
||||
char *client_addr;
|
||||
size_t handshake_leftlen;
|
||||
} http2_session_data;
|
||||
|
||||
struct app_context {
|
||||
@@ -79,32 +95,40 @@ struct app_context {
|
||||
static unsigned char next_proto_list[256];
|
||||
static size_t next_proto_list_len;
|
||||
|
||||
static int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len,
|
||||
void *arg)
|
||||
{
|
||||
static int next_proto_cb(SSL *s _U_, const unsigned char **data,
|
||||
unsigned int *len, void *arg _U_) {
|
||||
*data = next_proto_list;
|
||||
*len = next_proto_list_len;
|
||||
*len = (unsigned int)next_proto_list_len;
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
|
||||
/* Create SSL_CTX. */
|
||||
static SSL_CTX* create_ssl_ctx(const char *key_file, const char *cert_file)
|
||||
{
|
||||
static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) {
|
||||
SSL_CTX *ssl_ctx;
|
||||
EC_KEY *ecdh;
|
||||
|
||||
ssl_ctx = SSL_CTX_new(SSLv23_server_method());
|
||||
if(!ssl_ctx) {
|
||||
if (!ssl_ctx) {
|
||||
errx(1, "Could not create SSL/TLS context: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
SSL_CTX_set_options(ssl_ctx,
|
||||
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_COMPRESSION |
|
||||
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
|
||||
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
|
||||
SSL_OP_NO_COMPRESSION |
|
||||
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
|
||||
|
||||
if(SSL_CTX_use_PrivateKey_file(ssl_ctx, key_file,
|
||||
SSL_FILETYPE_PEM) != 1) {
|
||||
ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
|
||||
if (!ecdh) {
|
||||
errx(1, "EC_KEY_new_by_curv_name failed: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh);
|
||||
EC_KEY_free(ecdh);
|
||||
|
||||
if (SSL_CTX_use_PrivateKey_file(ssl_ctx, key_file, SSL_FILETYPE_PEM) != 1) {
|
||||
errx(1, "Could not read private key file %s", key_file);
|
||||
}
|
||||
if(SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file) != 1) {
|
||||
if (SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file) != 1) {
|
||||
errx(1, "Could not read certificate file %s", cert_file);
|
||||
}
|
||||
|
||||
@@ -118,11 +142,10 @@ static SSL_CTX* create_ssl_ctx(const char *key_file, const char *cert_file)
|
||||
}
|
||||
|
||||
/* Create SSL object */
|
||||
static SSL* create_ssl(SSL_CTX *ssl_ctx)
|
||||
{
|
||||
static SSL *create_ssl(SSL_CTX *ssl_ctx) {
|
||||
SSL *ssl;
|
||||
ssl = SSL_new(ssl_ctx);
|
||||
if(!ssl) {
|
||||
if (!ssl) {
|
||||
errx(1, "Could not create SSL/TLS session object: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
@@ -130,28 +153,25 @@ static SSL* create_ssl(SSL_CTX *ssl_ctx)
|
||||
}
|
||||
|
||||
static void add_stream(http2_session_data *session_data,
|
||||
http2_stream_data *stream_data)
|
||||
{
|
||||
http2_stream_data *stream_data) {
|
||||
stream_data->next = session_data->root.next;
|
||||
session_data->root.next = stream_data;
|
||||
stream_data->prev = &session_data->root;
|
||||
if(stream_data->next) {
|
||||
if (stream_data->next) {
|
||||
stream_data->next->prev = stream_data;
|
||||
}
|
||||
}
|
||||
|
||||
static void remove_stream(http2_session_data *session_data,
|
||||
http2_stream_data *stream_data)
|
||||
{
|
||||
static void remove_stream(http2_session_data *session_data _U_,
|
||||
http2_stream_data *stream_data) {
|
||||
stream_data->prev->next = stream_data->next;
|
||||
if(stream_data->next) {
|
||||
if (stream_data->next) {
|
||||
stream_data->next->prev = stream_data->prev;
|
||||
}
|
||||
}
|
||||
|
||||
static http2_stream_data* create_http2_stream_data
|
||||
(http2_session_data *session_data, int32_t stream_id)
|
||||
{
|
||||
static http2_stream_data *
|
||||
create_http2_stream_data(http2_session_data *session_data, int32_t stream_id) {
|
||||
http2_stream_data *stream_data;
|
||||
stream_data = malloc(sizeof(http2_stream_data));
|
||||
memset(stream_data, 0, sizeof(http2_stream_data));
|
||||
@@ -162,20 +182,18 @@ static http2_stream_data* create_http2_stream_data
|
||||
return stream_data;
|
||||
}
|
||||
|
||||
static void delete_http2_stream_data(http2_stream_data *stream_data)
|
||||
{
|
||||
if(stream_data->fd != -1) {
|
||||
static void delete_http2_stream_data(http2_stream_data *stream_data) {
|
||||
if (stream_data->fd != -1) {
|
||||
close(stream_data->fd);
|
||||
}
|
||||
free(stream_data->request_path);
|
||||
free(stream_data);
|
||||
}
|
||||
|
||||
static http2_session_data* create_http2_session_data(app_context *app_ctx,
|
||||
static http2_session_data *create_http2_session_data(app_context *app_ctx,
|
||||
int fd,
|
||||
struct sockaddr *addr,
|
||||
int addrlen)
|
||||
{
|
||||
int addrlen) {
|
||||
int rv;
|
||||
http2_session_data *session_data;
|
||||
SSL *ssl;
|
||||
@@ -187,13 +205,11 @@ static http2_session_data* create_http2_session_data(app_context *app_ctx,
|
||||
memset(session_data, 0, sizeof(http2_session_data));
|
||||
session_data->app_ctx = app_ctx;
|
||||
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val));
|
||||
session_data->bev = bufferevent_openssl_socket_new
|
||||
(app_ctx->evbase, fd, ssl,
|
||||
BUFFEREVENT_SSL_ACCEPTING,
|
||||
BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
|
||||
session_data->handshake_leftlen = NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN;
|
||||
session_data->bev = bufferevent_openssl_socket_new(
|
||||
app_ctx->evbase, fd, ssl, BUFFEREVENT_SSL_ACCEPTING,
|
||||
BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
|
||||
rv = getnameinfo(addr, addrlen, host, sizeof(host), NULL, 0, NI_NUMERICHOST);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
session_data->client_addr = strdup("(unknown)");
|
||||
} else {
|
||||
session_data->client_addr = strdup(host);
|
||||
@@ -202,17 +218,16 @@ static http2_session_data* create_http2_session_data(app_context *app_ctx,
|
||||
return session_data;
|
||||
}
|
||||
|
||||
static void delete_http2_session_data(http2_session_data *session_data)
|
||||
{
|
||||
static void delete_http2_session_data(http2_session_data *session_data) {
|
||||
http2_stream_data *stream_data;
|
||||
SSL *ssl = bufferevent_openssl_get_ssl(session_data->bev);
|
||||
fprintf(stderr, "%s disconnected\n", session_data->client_addr);
|
||||
if(ssl) {
|
||||
if (ssl) {
|
||||
SSL_shutdown(ssl);
|
||||
}
|
||||
bufferevent_free(session_data->bev);
|
||||
nghttp2_session_del(session_data->session);
|
||||
for(stream_data = session_data->root.next; stream_data;) {
|
||||
for (stream_data = session_data->root.next; stream_data;) {
|
||||
http2_stream_data *next = stream_data->next;
|
||||
delete_http2_stream_data(stream_data);
|
||||
stream_data = next;
|
||||
@@ -223,11 +238,10 @@ static void delete_http2_session_data(http2_session_data *session_data)
|
||||
|
||||
/* Serialize the frame and send (or buffer) the data to
|
||||
bufferevent. */
|
||||
static int session_send(http2_session_data *session_data)
|
||||
{
|
||||
static int session_send(http2_session_data *session_data) {
|
||||
int rv;
|
||||
rv = nghttp2_session_send(session_data->session);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||
return -1;
|
||||
}
|
||||
@@ -238,33 +252,34 @@ static int session_send(http2_session_data *session_data)
|
||||
function. Invocation of nghttp2_session_mem_recv() may make
|
||||
additional pending frames, so call session_send() at the end of the
|
||||
function. */
|
||||
static int session_recv(http2_session_data *session_data)
|
||||
{
|
||||
int rv;
|
||||
static int session_recv(http2_session_data *session_data) {
|
||||
ssize_t readlen;
|
||||
struct evbuffer *input = bufferevent_get_input(session_data->bev);
|
||||
size_t datalen = evbuffer_get_length(input);
|
||||
unsigned char *data = evbuffer_pullup(input, -1);
|
||||
rv = nghttp2_session_mem_recv(session_data->session, data, datalen);
|
||||
if(rv < 0) {
|
||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||
|
||||
readlen = nghttp2_session_mem_recv(session_data->session, data, datalen);
|
||||
if (readlen < 0) {
|
||||
warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
|
||||
return -1;
|
||||
}
|
||||
evbuffer_drain(input, rv);
|
||||
if(session_send(session_data) != 0) {
|
||||
if (evbuffer_drain(input, readlen) != 0) {
|
||||
warnx("Fatal error: evbuffer_drain failed");
|
||||
return -1;
|
||||
}
|
||||
if (session_send(session_data) != 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t send_callback(nghttp2_session *session,
|
||||
const uint8_t *data, size_t length,
|
||||
int flags, void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
|
||||
size_t length, int flags _U_, void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
struct bufferevent *bev = session_data->bev;
|
||||
/* Avoid excessive buffering in server side. */
|
||||
if(evbuffer_get_length(bufferevent_get_output(session_data->bev)) >=
|
||||
OUTPUT_WOULDBLOCK_THRESHOLD) {
|
||||
if (evbuffer_get_length(bufferevent_get_output(session_data->bev)) >=
|
||||
OUTPUT_WOULDBLOCK_THRESHOLD) {
|
||||
return NGHTTP2_ERR_WOULDBLOCK;
|
||||
}
|
||||
bufferevent_write(bev, data, length);
|
||||
@@ -272,26 +287,24 @@ static ssize_t send_callback(nghttp2_session *session,
|
||||
}
|
||||
|
||||
/* Returns nonzero if the string |s| ends with the substring |sub| */
|
||||
static int ends_with(const char *s, const char *sub)
|
||||
{
|
||||
static int ends_with(const char *s, const char *sub) {
|
||||
size_t slen = strlen(s);
|
||||
size_t sublen = strlen(sub);
|
||||
if(slen < sublen) {
|
||||
if (slen < sublen) {
|
||||
return 0;
|
||||
}
|
||||
return memcmp(s + slen - sublen, sub, sublen) == 0;
|
||||
}
|
||||
|
||||
/* Returns int value of hex string character |c| */
|
||||
static uint8_t hex_to_uint(uint8_t c)
|
||||
{
|
||||
if('0' <= c && c <= '9') {
|
||||
static uint8_t hex_to_uint(uint8_t c) {
|
||||
if ('0' <= c && c <= '9') {
|
||||
return c - '0';
|
||||
}
|
||||
if('A' <= c && c <= 'F') {
|
||||
if ('A' <= c && c <= 'F') {
|
||||
return c - 'A' + 10;
|
||||
}
|
||||
if('a' <= c && c <= 'f') {
|
||||
if ('a' <= c && c <= 'f') {
|
||||
return c - 'a' + 10;
|
||||
}
|
||||
return 0;
|
||||
@@ -301,16 +314,15 @@ static uint8_t hex_to_uint(uint8_t c)
|
||||
and returns the decoded byte string in allocated buffer. The return
|
||||
value is NULL terminated. The caller must free the returned
|
||||
string. */
|
||||
static char* percent_decode(const uint8_t *value, size_t valuelen)
|
||||
{
|
||||
static char *percent_decode(const uint8_t *value, size_t valuelen) {
|
||||
char *res;
|
||||
|
||||
res = malloc(valuelen + 1);
|
||||
if(valuelen > 3) {
|
||||
if (valuelen > 3) {
|
||||
size_t i, j;
|
||||
for(i = 0, j = 0; i < valuelen - 2;) {
|
||||
if(value[i] != '%' ||
|
||||
!isxdigit(value[i + 1]) || !isxdigit(value[i + 2])) {
|
||||
for (i = 0, j = 0; i < valuelen - 2;) {
|
||||
if (value[i] != '%' || !isxdigit(value[i + 1]) ||
|
||||
!isxdigit(value[i + 2])) {
|
||||
res[j++] = value[i++];
|
||||
continue;
|
||||
}
|
||||
@@ -326,33 +338,33 @@ static char* percent_decode(const uint8_t *value, size_t valuelen)
|
||||
return res;
|
||||
}
|
||||
|
||||
static ssize_t file_read_callback
|
||||
(nghttp2_session *session, int32_t stream_id,
|
||||
uint8_t *buf, size_t length, uint32_t *data_flags,
|
||||
nghttp2_data_source *source, void *user_data)
|
||||
{
|
||||
static ssize_t file_read_callback(nghttp2_session *session _U_,
|
||||
int32_t stream_id _U_, uint8_t *buf,
|
||||
size_t length, uint32_t *data_flags,
|
||||
nghttp2_data_source *source,
|
||||
void *user_data _U_) {
|
||||
int fd = source->fd;
|
||||
ssize_t r;
|
||||
while((r = read(fd, buf, length)) == -1 && errno == EINTR);
|
||||
if(r == -1) {
|
||||
while ((r = read(fd, buf, length)) == -1 && errno == EINTR)
|
||||
;
|
||||
if (r == -1) {
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
if(r == 0) {
|
||||
if (r == 0) {
|
||||
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
static int send_response(nghttp2_session *session, int32_t stream_id,
|
||||
nghttp2_nv *nva, size_t nvlen, int fd)
|
||||
{
|
||||
nghttp2_nv *nva, size_t nvlen, int fd) {
|
||||
int rv;
|
||||
nghttp2_data_provider data_prd;
|
||||
data_prd.source.fd = fd;
|
||||
data_prd.read_callback = file_read_callback;
|
||||
|
||||
rv = nghttp2_submit_response(session, stream_id, nva, nvlen, &data_prd);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||
return -1;
|
||||
}
|
||||
@@ -360,42 +372,40 @@ static int send_response(nghttp2_session *session, int32_t stream_id,
|
||||
}
|
||||
|
||||
const char ERROR_HTML[] = "<html><head><title>404</title></head>"
|
||||
"<body><h1>404 Not Found</h1></body></html>";
|
||||
"<body><h1>404 Not Found</h1></body></html>";
|
||||
|
||||
static int error_reply(nghttp2_session *session,
|
||||
http2_stream_data *stream_data)
|
||||
{
|
||||
http2_stream_data *stream_data) {
|
||||
int rv;
|
||||
ssize_t writelen;
|
||||
int pipefd[2];
|
||||
nghttp2_nv hdrs[] = {
|
||||
MAKE_NV(":status", "404")
|
||||
};
|
||||
nghttp2_nv hdrs[] = {MAKE_NV(":status", "404")};
|
||||
|
||||
rv = pipe(pipefd);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
warn("Could not create pipe");
|
||||
rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
|
||||
stream_data->stream_id,
|
||||
NGHTTP2_INTERNAL_ERROR);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
rv = write(pipefd[1], ERROR_HTML, sizeof(ERROR_HTML) - 1);
|
||||
writelen = write(pipefd[1], ERROR_HTML, sizeof(ERROR_HTML) - 1);
|
||||
close(pipefd[1]);
|
||||
|
||||
if(rv != sizeof(ERROR_HTML)) {
|
||||
if (writelen != sizeof(ERROR_HTML) - 1) {
|
||||
close(pipefd[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
stream_data->fd = pipefd[0];
|
||||
|
||||
if(send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs),
|
||||
pipefd[0]) != 0) {
|
||||
if (send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs),
|
||||
pipefd[0]) != 0) {
|
||||
close(pipefd[0]);
|
||||
return -1;
|
||||
}
|
||||
@@ -405,27 +415,26 @@ static int error_reply(nghttp2_session *session,
|
||||
/* nghttp2_on_header_callback: Called when nghttp2 library emits
|
||||
single header name/value pair. */
|
||||
static int on_header_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame,
|
||||
const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen,
|
||||
uint8_t flags,
|
||||
void *user_data)
|
||||
{
|
||||
const nghttp2_frame *frame, const uint8_t *name,
|
||||
size_t namelen, const uint8_t *value,
|
||||
size_t valuelen, uint8_t flags _U_,
|
||||
void *user_data _U_) {
|
||||
http2_stream_data *stream_data;
|
||||
const char PATH[] = ":path";
|
||||
switch(frame->hd.type) {
|
||||
switch (frame->hd.type) {
|
||||
case NGHTTP2_HEADERS:
|
||||
if(frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
||||
if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
||||
break;
|
||||
}
|
||||
stream_data = nghttp2_session_get_stream_user_data(session,
|
||||
frame->hd.stream_id);
|
||||
if(!stream_data || stream_data->request_path) {
|
||||
stream_data =
|
||||
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
|
||||
if (!stream_data || stream_data->request_path) {
|
||||
break;
|
||||
}
|
||||
if(namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) {
|
||||
if (namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) {
|
||||
size_t j;
|
||||
for(j = 0; j < valuelen && value[j] != '?'; ++j);
|
||||
for (j = 0; j < valuelen && value[j] != '?'; ++j)
|
||||
;
|
||||
stream_data->request_path = percent_decode(value, j);
|
||||
}
|
||||
break;
|
||||
@@ -435,13 +444,12 @@ static int on_header_callback(nghttp2_session *session,
|
||||
|
||||
static int on_begin_headers_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame,
|
||||
void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
http2_stream_data *stream_data;
|
||||
|
||||
if(frame->hd.type != NGHTTP2_HEADERS ||
|
||||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
||||
if (frame->hd.type != NGHTTP2_HEADERS ||
|
||||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
||||
return 0;
|
||||
}
|
||||
stream_data = create_http2_stream_data(session_data, frame->hd.stream_id);
|
||||
@@ -452,52 +460,47 @@ static int on_begin_headers_callback(nghttp2_session *session,
|
||||
|
||||
/* Minimum check for directory traversal. Returns nonzero if it is
|
||||
safe. */
|
||||
static int check_path(const char *path)
|
||||
{
|
||||
static int check_path(const char *path) {
|
||||
/* We don't like '\' in url. */
|
||||
return path[0] && path[0] == '/' &&
|
||||
strchr(path, '\\') == NULL &&
|
||||
strstr(path, "/../") == NULL &&
|
||||
strstr(path, "/./") == NULL &&
|
||||
!ends_with(path, "/..") && !ends_with(path, "/.");
|
||||
return path[0] && path[0] == '/' && strchr(path, '\\') == NULL &&
|
||||
strstr(path, "/../") == NULL && strstr(path, "/./") == NULL &&
|
||||
!ends_with(path, "/..") && !ends_with(path, "/.");
|
||||
}
|
||||
|
||||
static int on_request_recv(nghttp2_session *session,
|
||||
http2_session_data *session_data,
|
||||
http2_stream_data *stream_data)
|
||||
{
|
||||
http2_stream_data *stream_data) {
|
||||
int fd;
|
||||
nghttp2_nv hdrs[] = {
|
||||
MAKE_NV(":status", "200")
|
||||
};
|
||||
nghttp2_nv hdrs[] = {MAKE_NV(":status", "200")};
|
||||
char *rel_path;
|
||||
|
||||
if(!stream_data->request_path) {
|
||||
if(error_reply(session, stream_data) != 0) {
|
||||
if (!stream_data->request_path) {
|
||||
if (error_reply(session, stream_data) != 0) {
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
fprintf(stderr, "%s GET %s\n", session_data->client_addr,
|
||||
stream_data->request_path);
|
||||
if(!check_path(stream_data->request_path)) {
|
||||
if(error_reply(session, stream_data) != 0) {
|
||||
if (!check_path(stream_data->request_path)) {
|
||||
if (error_reply(session, stream_data) != 0) {
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
for(rel_path = stream_data->request_path; *rel_path == '/'; ++rel_path);
|
||||
for (rel_path = stream_data->request_path; *rel_path == '/'; ++rel_path)
|
||||
;
|
||||
fd = open(rel_path, O_RDONLY);
|
||||
if(fd == -1) {
|
||||
if(error_reply(session, stream_data) != 0) {
|
||||
if (fd == -1) {
|
||||
if (error_reply(session, stream_data) != 0) {
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
stream_data->fd = fd;
|
||||
|
||||
if(send_response(session, stream_data->stream_id, hdrs,
|
||||
ARRLEN(hdrs), fd) != 0) {
|
||||
if (send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs), fd) !=
|
||||
0) {
|
||||
close(fd);
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
@@ -505,20 +508,19 @@ static int on_request_recv(nghttp2_session *session,
|
||||
}
|
||||
|
||||
static int on_frame_recv_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame, void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
const nghttp2_frame *frame, void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
http2_stream_data *stream_data;
|
||||
switch(frame->hd.type) {
|
||||
switch (frame->hd.type) {
|
||||
case NGHTTP2_DATA:
|
||||
case NGHTTP2_HEADERS:
|
||||
/* Check that the client request has finished */
|
||||
if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||
stream_data = nghttp2_session_get_stream_user_data(session,
|
||||
frame->hd.stream_id);
|
||||
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||
stream_data =
|
||||
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
|
||||
/* For DATA and HEADERS frame, this callback may be called after
|
||||
on_stream_close_callback. Check that stream still alive. */
|
||||
if(!stream_data) {
|
||||
if (!stream_data) {
|
||||
return 0;
|
||||
}
|
||||
return on_request_recv(session, session_data, stream_data);
|
||||
@@ -530,16 +532,13 @@ static int on_frame_recv_callback(nghttp2_session *session,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_stream_close_callback(nghttp2_session *session,
|
||||
int32_t stream_id,
|
||||
nghttp2_error_code error_code,
|
||||
void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||
uint32_t error_code _U_, void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
http2_stream_data *stream_data;
|
||||
|
||||
stream_data = nghttp2_session_get_stream_user_data(session, stream_id);
|
||||
if(!stream_data) {
|
||||
if (!stream_data) {
|
||||
return 0;
|
||||
}
|
||||
remove_stream(session_data, stream_data);
|
||||
@@ -547,32 +546,40 @@ static int on_stream_close_callback(nghttp2_session *session,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void initialize_nghttp2_session(http2_session_data *session_data)
|
||||
{
|
||||
nghttp2_session_callbacks callbacks;
|
||||
static void initialize_nghttp2_session(http2_session_data *session_data) {
|
||||
nghttp2_session_callbacks *callbacks;
|
||||
|
||||
memset(&callbacks, 0, sizeof(callbacks));
|
||||
nghttp2_session_callbacks_new(&callbacks);
|
||||
|
||||
callbacks.send_callback = send_callback;
|
||||
callbacks.on_frame_recv_callback = on_frame_recv_callback;
|
||||
callbacks.on_stream_close_callback = on_stream_close_callback;
|
||||
callbacks.on_header_callback = on_header_callback;
|
||||
callbacks.on_begin_headers_callback = on_begin_headers_callback;
|
||||
nghttp2_session_server_new(&session_data->session, &callbacks, session_data);
|
||||
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
|
||||
on_frame_recv_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_stream_close_callback(
|
||||
callbacks, on_stream_close_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_header_callback(callbacks,
|
||||
on_header_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_begin_headers_callback(
|
||||
callbacks, on_begin_headers_callback);
|
||||
|
||||
nghttp2_session_server_new(&session_data->session, callbacks, session_data);
|
||||
|
||||
nghttp2_session_callbacks_del(callbacks);
|
||||
}
|
||||
|
||||
/* Send HTTP/2 client connection header, which includes 24 bytes
|
||||
magic octets and SETTINGS frame */
|
||||
static int send_server_connection_header(http2_session_data *session_data)
|
||||
{
|
||||
static int send_server_connection_header(http2_session_data *session_data) {
|
||||
nghttp2_settings_entry iv[1] = {
|
||||
{ NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 }
|
||||
};
|
||||
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
|
||||
int rv;
|
||||
|
||||
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE,
|
||||
iv, ARRLEN(iv));
|
||||
if(rv != 0) {
|
||||
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
|
||||
ARRLEN(iv));
|
||||
if (rv != 0) {
|
||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||
return -1;
|
||||
}
|
||||
@@ -581,10 +588,9 @@ static int send_server_connection_header(http2_session_data *session_data)
|
||||
|
||||
/* readcb for bufferevent after client connection header was
|
||||
checked. */
|
||||
static void readcb(struct bufferevent *bev, void *ptr)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
||||
if(session_recv(session_data) != 0) {
|
||||
static void readcb(struct bufferevent *bev _U_, void *ptr) {
|
||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||
if (session_recv(session_data) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
@@ -598,88 +604,60 @@ static void readcb(struct bufferevent *bev, void *ptr)
|
||||
process pending data in the output buffer. This is necessary
|
||||
because we have a threshold on the buffer size to avoid too much
|
||||
buffering. See send_callback(). */
|
||||
static void writecb(struct bufferevent *bev, void *ptr)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
||||
if(evbuffer_get_length(bufferevent_get_output(bev)) > 0) {
|
||||
static void writecb(struct bufferevent *bev, void *ptr) {
|
||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||
if (evbuffer_get_length(bufferevent_get_output(bev)) > 0) {
|
||||
return;
|
||||
}
|
||||
if(nghttp2_session_want_read(session_data->session) == 0 &&
|
||||
nghttp2_session_want_write(session_data->session) == 0) {
|
||||
if (nghttp2_session_want_read(session_data->session) == 0 &&
|
||||
nghttp2_session_want_write(session_data->session) == 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
if(session_send(session_data) != 0) {
|
||||
if (session_send(session_data) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* eventcb for bufferevent */
|
||||
static void eventcb(struct bufferevent *bev, short events, void *ptr)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
||||
if(events & BEV_EVENT_CONNECTED) {
|
||||
static void eventcb(struct bufferevent *bev _U_, short events, void *ptr) {
|
||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||
if (events & BEV_EVENT_CONNECTED) {
|
||||
fprintf(stderr, "%s connected\n", session_data->client_addr);
|
||||
|
||||
initialize_nghttp2_session(session_data);
|
||||
|
||||
if (send_server_connection_header(session_data) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
if(events & BEV_EVENT_EOF) {
|
||||
if (events & BEV_EVENT_EOF) {
|
||||
fprintf(stderr, "%s EOF\n", session_data->client_addr);
|
||||
} else if(events & BEV_EVENT_ERROR) {
|
||||
} else if (events & BEV_EVENT_ERROR) {
|
||||
fprintf(stderr, "%s network error\n", session_data->client_addr);
|
||||
} else if(events & BEV_EVENT_TIMEOUT) {
|
||||
} else if (events & BEV_EVENT_TIMEOUT) {
|
||||
fprintf(stderr, "%s timeout\n", session_data->client_addr);
|
||||
}
|
||||
delete_http2_session_data(session_data);
|
||||
}
|
||||
|
||||
/* readcb for bufferevent to check first 24 bytes client connection
|
||||
header. */
|
||||
static void handshake_readcb(struct bufferevent *bev, void *ptr)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
||||
uint8_t data[24];
|
||||
struct evbuffer *input = bufferevent_get_input(session_data->bev);
|
||||
int readlen = evbuffer_remove(input, data, session_data->handshake_leftlen);
|
||||
const char *conhead = NGHTTP2_CLIENT_CONNECTION_PREFACE;
|
||||
|
||||
if(memcmp(conhead + NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN
|
||||
- session_data->handshake_leftlen, data, readlen) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
session_data->handshake_leftlen -= readlen;
|
||||
if(session_data->handshake_leftlen == 0) {
|
||||
bufferevent_setcb(session_data->bev, readcb, writecb, eventcb, ptr);
|
||||
/* Process pending data in buffer since they are not notified
|
||||
further */
|
||||
initialize_nghttp2_session(session_data);
|
||||
if(send_server_connection_header(session_data) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
if(session_recv(session_data) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* callback for evconnlistener */
|
||||
static void acceptcb(struct evconnlistener *listener, int fd,
|
||||
struct sockaddr *addr, int addrlen, void *arg)
|
||||
{
|
||||
app_context *app_ctx = (app_context*)arg;
|
||||
static void acceptcb(struct evconnlistener *listener _U_, int fd,
|
||||
struct sockaddr *addr, int addrlen, void *arg) {
|
||||
app_context *app_ctx = (app_context *)arg;
|
||||
http2_session_data *session_data;
|
||||
|
||||
session_data = create_http2_session_data(app_ctx, fd, addr, addrlen);
|
||||
bufferevent_setcb(session_data->bev, handshake_readcb, NULL, eventcb,
|
||||
session_data);
|
||||
|
||||
bufferevent_setcb(session_data->bev, readcb, writecb, eventcb, session_data);
|
||||
}
|
||||
|
||||
static void start_listen(struct event_base *evbase, const char *service,
|
||||
app_context *app_ctx)
|
||||
{
|
||||
app_context *app_ctx) {
|
||||
int rv;
|
||||
struct addrinfo hints;
|
||||
struct addrinfo *res, *rp;
|
||||
@@ -693,16 +671,17 @@ static void start_listen(struct event_base *evbase, const char *service,
|
||||
#endif /* AI_ADDRCONFIG */
|
||||
|
||||
rv = getaddrinfo(NULL, service, &hints, &res);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
errx(1, NULL);
|
||||
}
|
||||
for(rp = res; rp; rp = rp->ai_next) {
|
||||
for (rp = res; rp; rp = rp->ai_next) {
|
||||
struct evconnlistener *listener;
|
||||
listener = evconnlistener_new_bind(evbase, acceptcb, app_ctx,
|
||||
LEV_OPT_CLOSE_ON_FREE |
|
||||
LEV_OPT_REUSEABLE, 16,
|
||||
rp->ai_addr, rp->ai_addrlen);
|
||||
if(listener) {
|
||||
listener = evconnlistener_new_bind(
|
||||
evbase, acceptcb, app_ctx, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
|
||||
16, rp->ai_addr, rp->ai_addrlen);
|
||||
if (listener) {
|
||||
freeaddrinfo(res);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -710,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,
|
||||
struct event_base *evbase)
|
||||
{
|
||||
struct event_base *evbase) {
|
||||
memset(app_ctx, 0, sizeof(app_context));
|
||||
app_ctx->ssl_ctx = ssl_ctx;
|
||||
app_ctx->evbase = evbase;
|
||||
}
|
||||
|
||||
static void run(const char *service,
|
||||
const char *key_file, const char *cert_file)
|
||||
{
|
||||
static void run(const char *service, const char *key_file,
|
||||
const char *cert_file) {
|
||||
SSL_CTX *ssl_ctx;
|
||||
app_context app_ctx;
|
||||
struct event_base *evbase;
|
||||
@@ -735,11 +712,10 @@ static void run(const char *service,
|
||||
SSL_CTX_free(ssl_ctx);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int main(int argc, char **argv) {
|
||||
struct sigaction act;
|
||||
|
||||
if(argc < 4) {
|
||||
if (argc < 4) {
|
||||
fprintf(stderr, "Usage: libevent-server PORT KEY_FILE CERT_FILE\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
@@ -750,6 +726,8 @@ int main(int argc, char **argv)
|
||||
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
OpenSSL_add_all_algorithms();
|
||||
OPENSSL_config(NULL);
|
||||
|
||||
run(argv[1], argv[2], argv[3]);
|
||||
return 0;
|
||||
|
||||
1342
examples/tiny-nghttpd.c
Normal file
1342
examples/tiny-nghttpd.c
Normal file
File diff suppressed because it is too large
Load Diff
30
gendowncasetbl.py
Executable file
30
gendowncasetbl.py
Executable file
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env python
|
||||
import sys
|
||||
|
||||
def name(i):
|
||||
if i < 0x20:
|
||||
return \
|
||||
['NUL ', 'SOH ', 'STX ', 'ETX ', 'EOT ', 'ENQ ', 'ACK ', 'BEL ',
|
||||
'BS ', 'HT ', 'LF ', 'VT ', 'FF ', 'CR ', 'SO ', 'SI ',
|
||||
'DLE ', 'DC1 ', 'DC2 ', 'DC3 ', 'DC4 ', 'NAK ', 'SYN ', 'ETB ',
|
||||
'CAN ', 'EM ', 'SUB ', 'ESC ', 'FS ', 'GS ', 'RS ', 'US '][i]
|
||||
elif i == 0x7f:
|
||||
return 'DEL '
|
||||
|
||||
for i in range(256):
|
||||
if chr(i) == ' ':
|
||||
sys.stdout.write('{} /* SPC */, '.format(i))
|
||||
elif chr(i) == '\t':
|
||||
sys.stdout.write('{} /* HT */, '.format(i))
|
||||
elif 'A' <= chr(i) and chr(i) <= 'Z':
|
||||
sys.stdout.write('{} /* {} */, '.format(i - ord('A') + ord('a'), chr(i)))
|
||||
elif (0x21 <= i and i < 0x7f):
|
||||
sys.stdout.write('{} /* {} */, '.format(i, chr(i)))
|
||||
elif 0x80 <= i:
|
||||
sys.stdout.write('{} /* {} */, '.format(i, hex(i)))
|
||||
elif 0 == i:
|
||||
sys.stdout.write('{} /* NUL */, '.format(i))
|
||||
else:
|
||||
sys.stdout.write('{} /* {} */, '.format(i, name(i)))
|
||||
if (i + 1)%4 == 0:
|
||||
sys.stdout.write('\n')
|
||||
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')
|
||||
0
gennmchartbl.py
Normal file → Executable file
0
gennmchartbl.py
Normal file → Executable file
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)
|
||||
0
genvchartbl.py
Normal file → Executable file
0
genvchartbl.py
Normal file → Executable file
484
git-clang-format
Executable file
484
git-clang-format
Executable file
@@ -0,0 +1,484 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
#===- git-clang-format - ClangFormat Git Integration ---------*- python -*--===#
|
||||
#
|
||||
# The LLVM Compiler Infrastructure
|
||||
#
|
||||
# This file is distributed under the University of Illinois Open Source
|
||||
# License. See LICENSE.TXT for details.
|
||||
#
|
||||
#===------------------------------------------------------------------------===#
|
||||
|
||||
r"""
|
||||
clang-format git integration
|
||||
============================
|
||||
|
||||
This file provides a clang-format integration for git. Put it somewhere in your
|
||||
path and ensure that it is executable. Then, "git clang-format" will invoke
|
||||
clang-format on the changes in current files or a specific commit.
|
||||
|
||||
For further details, run:
|
||||
git clang-format -h
|
||||
|
||||
Requires Python 2.7
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import collections
|
||||
import contextlib
|
||||
import errno
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
usage = 'git clang-format [OPTIONS] [<commit>] [--] [<file>...]'
|
||||
|
||||
desc = '''
|
||||
Run clang-format on all lines that differ between the working directory
|
||||
and <commit>, which defaults to HEAD. Changes are only applied to the working
|
||||
directory.
|
||||
|
||||
The following git-config settings set the default of the corresponding option:
|
||||
clangFormat.binary
|
||||
clangFormat.commit
|
||||
clangFormat.extension
|
||||
clangFormat.style
|
||||
'''
|
||||
|
||||
# Name of the temporary index file in which save the output of clang-format.
|
||||
# This file is created within the .git directory.
|
||||
temp_index_basename = 'clang-format-index'
|
||||
|
||||
|
||||
Range = collections.namedtuple('Range', 'start, count')
|
||||
|
||||
|
||||
def main():
|
||||
config = load_git_config()
|
||||
|
||||
# In order to keep '--' yet allow options after positionals, we need to
|
||||
# check for '--' ourselves. (Setting nargs='*' throws away the '--', while
|
||||
# nargs=argparse.REMAINDER disallows options after positionals.)
|
||||
argv = sys.argv[1:]
|
||||
try:
|
||||
idx = argv.index('--')
|
||||
except ValueError:
|
||||
dash_dash = []
|
||||
else:
|
||||
dash_dash = argv[idx:]
|
||||
argv = argv[:idx]
|
||||
|
||||
default_extensions = ','.join([
|
||||
# From clang/lib/Frontend/FrontendOptions.cpp, all lower case
|
||||
'c', 'h', # C
|
||||
'm', # ObjC
|
||||
'mm', # ObjC++
|
||||
'cc', 'cp', 'cpp', 'c++', 'cxx', 'hpp', # C++
|
||||
# Other languages that clang-format supports
|
||||
'proto', 'protodevel', # Protocol Buffers
|
||||
'js', # JavaScript
|
||||
])
|
||||
|
||||
p = argparse.ArgumentParser(
|
||||
usage=usage, formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
description=desc)
|
||||
p.add_argument('--binary',
|
||||
default=config.get('clangformat.binary', 'clang-format'),
|
||||
help='path to clang-format'),
|
||||
p.add_argument('--commit',
|
||||
default=config.get('clangformat.commit', 'HEAD'),
|
||||
help='default commit to use if none is specified'),
|
||||
p.add_argument('--diff', action='store_true',
|
||||
help='print a diff instead of applying the changes')
|
||||
p.add_argument('--extensions',
|
||||
default=config.get('clangformat.extensions',
|
||||
default_extensions),
|
||||
help=('comma-separated list of file extensions to format, '
|
||||
'excluding the period and case-insensitive')),
|
||||
p.add_argument('-f', '--force', action='store_true',
|
||||
help='allow changes to unstaged files')
|
||||
p.add_argument('-p', '--patch', action='store_true',
|
||||
help='select hunks interactively')
|
||||
p.add_argument('-q', '--quiet', action='count', default=0,
|
||||
help='print less information')
|
||||
p.add_argument('--style',
|
||||
default=config.get('clangformat.style', None),
|
||||
help='passed to clang-format'),
|
||||
p.add_argument('-v', '--verbose', action='count', default=0,
|
||||
help='print extra information')
|
||||
# We gather all the remaining positional arguments into 'args' since we need
|
||||
# to use some heuristics to determine whether or not <commit> was present.
|
||||
# However, to print pretty messages, we make use of metavar and help.
|
||||
p.add_argument('args', nargs='*', metavar='<commit>',
|
||||
help='revision from which to compute the diff')
|
||||
p.add_argument('ignored', nargs='*', metavar='<file>...',
|
||||
help='if specified, only consider differences in these files')
|
||||
opts = p.parse_args(argv)
|
||||
|
||||
opts.verbose -= opts.quiet
|
||||
del opts.quiet
|
||||
|
||||
commit, files = interpret_args(opts.args, dash_dash, opts.commit)
|
||||
changed_lines = compute_diff_and_extract_lines(commit, files)
|
||||
if opts.verbose >= 1:
|
||||
ignored_files = set(changed_lines)
|
||||
filter_by_extension(changed_lines, opts.extensions.lower().split(','))
|
||||
if opts.verbose >= 1:
|
||||
ignored_files.difference_update(changed_lines)
|
||||
if ignored_files:
|
||||
print 'Ignoring changes in the following files (wrong extension):'
|
||||
for filename in ignored_files:
|
||||
print ' ', filename
|
||||
if changed_lines:
|
||||
print 'Running clang-format on the following files:'
|
||||
for filename in changed_lines:
|
||||
print ' ', filename
|
||||
if not changed_lines:
|
||||
print 'no modified files to format'
|
||||
return
|
||||
# The computed diff outputs absolute paths, so we must cd before accessing
|
||||
# those files.
|
||||
cd_to_toplevel()
|
||||
old_tree = create_tree_from_workdir(changed_lines)
|
||||
new_tree = run_clang_format_and_save_to_tree(changed_lines,
|
||||
binary=opts.binary,
|
||||
style=opts.style)
|
||||
if opts.verbose >= 1:
|
||||
print 'old tree:', old_tree
|
||||
print 'new tree:', new_tree
|
||||
if old_tree == new_tree:
|
||||
if opts.verbose >= 0:
|
||||
print 'clang-format did not modify any files'
|
||||
elif opts.diff:
|
||||
print_diff(old_tree, new_tree)
|
||||
else:
|
||||
changed_files = apply_changes(old_tree, new_tree, force=opts.force,
|
||||
patch_mode=opts.patch)
|
||||
if (opts.verbose >= 0 and not opts.patch) or opts.verbose >= 1:
|
||||
print 'changed files:'
|
||||
for filename in changed_files:
|
||||
print ' ', filename
|
||||
|
||||
|
||||
def load_git_config(non_string_options=None):
|
||||
"""Return the git configuration as a dictionary.
|
||||
|
||||
All options are assumed to be strings unless in `non_string_options`, in which
|
||||
is a dictionary mapping option name (in lower case) to either "--bool" or
|
||||
"--int"."""
|
||||
if non_string_options is None:
|
||||
non_string_options = {}
|
||||
out = {}
|
||||
for entry in run('git', 'config', '--list', '--null').split('\0'):
|
||||
if entry:
|
||||
name, value = entry.split('\n', 1)
|
||||
if name in non_string_options:
|
||||
value = run('git', 'config', non_string_options[name], name)
|
||||
out[name] = value
|
||||
return out
|
||||
|
||||
|
||||
def interpret_args(args, dash_dash, default_commit):
|
||||
"""Interpret `args` as "[commit] [--] [files...]" and return (commit, files).
|
||||
|
||||
It is assumed that "--" and everything that follows has been removed from
|
||||
args and placed in `dash_dash`.
|
||||
|
||||
If "--" is present (i.e., `dash_dash` is non-empty), the argument to its
|
||||
left (if present) is taken as commit. Otherwise, the first argument is
|
||||
checked if it is a commit or a file. If commit is not given,
|
||||
`default_commit` is used."""
|
||||
if dash_dash:
|
||||
if len(args) == 0:
|
||||
commit = default_commit
|
||||
elif len(args) > 1:
|
||||
die('at most one commit allowed; %d given' % len(args))
|
||||
else:
|
||||
commit = args[0]
|
||||
object_type = get_object_type(commit)
|
||||
if object_type not in ('commit', 'tag'):
|
||||
if object_type is None:
|
||||
die("'%s' is not a commit" % commit)
|
||||
else:
|
||||
die("'%s' is a %s, but a commit was expected" % (commit, object_type))
|
||||
files = dash_dash[1:]
|
||||
elif args:
|
||||
if disambiguate_revision(args[0]):
|
||||
commit = args[0]
|
||||
files = args[1:]
|
||||
else:
|
||||
commit = default_commit
|
||||
files = args
|
||||
else:
|
||||
commit = default_commit
|
||||
files = []
|
||||
return commit, files
|
||||
|
||||
|
||||
def disambiguate_revision(value):
|
||||
"""Returns True if `value` is a revision, False if it is a file, or dies."""
|
||||
# If `value` is ambiguous (neither a commit nor a file), the following
|
||||
# command will die with an appropriate error message.
|
||||
run('git', 'rev-parse', value, verbose=False)
|
||||
object_type = get_object_type(value)
|
||||
if object_type is None:
|
||||
return False
|
||||
if object_type in ('commit', 'tag'):
|
||||
return True
|
||||
die('`%s` is a %s, but a commit or filename was expected' %
|
||||
(value, object_type))
|
||||
|
||||
|
||||
def get_object_type(value):
|
||||
"""Returns a string description of an object's type, or None if it is not
|
||||
a valid git object."""
|
||||
cmd = ['git', 'cat-file', '-t', value]
|
||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
stdout, stderr = p.communicate()
|
||||
if p.returncode != 0:
|
||||
return None
|
||||
return stdout.strip()
|
||||
|
||||
|
||||
def compute_diff_and_extract_lines(commit, files):
|
||||
"""Calls compute_diff() followed by extract_lines()."""
|
||||
diff_process = compute_diff(commit, files)
|
||||
changed_lines = extract_lines(diff_process.stdout)
|
||||
diff_process.stdout.close()
|
||||
diff_process.wait()
|
||||
if diff_process.returncode != 0:
|
||||
# Assume error was already printed to stderr.
|
||||
sys.exit(2)
|
||||
return changed_lines
|
||||
|
||||
|
||||
def compute_diff(commit, files):
|
||||
"""Return a subprocess object producing the diff from `commit`.
|
||||
|
||||
The return value's `stdin` file object will produce a patch with the
|
||||
differences between the working directory and `commit`, filtered on `files`
|
||||
(if non-empty). Zero context lines are used in the patch."""
|
||||
cmd = ['git', 'diff-index', '-p', '-U0', commit, '--']
|
||||
cmd.extend(files)
|
||||
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||
p.stdin.close()
|
||||
return p
|
||||
|
||||
|
||||
def extract_lines(patch_file):
|
||||
"""Extract the changed lines in `patch_file`.
|
||||
|
||||
The return value is a dictionary mapping filename to a list of (start_line,
|
||||
line_count) pairs.
|
||||
|
||||
The input must have been produced with ``-U0``, meaning unidiff format with
|
||||
zero lines of context. The return value is a dict mapping filename to a
|
||||
list of line `Range`s."""
|
||||
matches = {}
|
||||
for line in patch_file:
|
||||
match = re.search(r'^\+\+\+\ [^/]+/(.*)', line)
|
||||
if match:
|
||||
filename = match.group(1).rstrip('\r\n')
|
||||
match = re.search(r'^@@ -[0-9,]+ \+(\d+)(,(\d+))?', line)
|
||||
if match:
|
||||
start_line = int(match.group(1))
|
||||
line_count = 1
|
||||
if match.group(3):
|
||||
line_count = int(match.group(3))
|
||||
if line_count > 0:
|
||||
matches.setdefault(filename, []).append(Range(start_line, line_count))
|
||||
return matches
|
||||
|
||||
|
||||
def filter_by_extension(dictionary, allowed_extensions):
|
||||
"""Delete every key in `dictionary` that doesn't have an allowed extension.
|
||||
|
||||
`allowed_extensions` must be a collection of lowercase file extensions,
|
||||
excluding the period."""
|
||||
allowed_extensions = frozenset(allowed_extensions)
|
||||
for filename in dictionary.keys():
|
||||
base_ext = filename.rsplit('.', 1)
|
||||
if len(base_ext) == 1 or base_ext[1].lower() not in allowed_extensions:
|
||||
del dictionary[filename]
|
||||
|
||||
|
||||
def cd_to_toplevel():
|
||||
"""Change to the top level of the git repository."""
|
||||
toplevel = run('git', 'rev-parse', '--show-toplevel')
|
||||
os.chdir(toplevel)
|
||||
|
||||
|
||||
def create_tree_from_workdir(filenames):
|
||||
"""Create a new git tree with the given files from the working directory.
|
||||
|
||||
Returns the object ID (SHA-1) of the created tree."""
|
||||
return create_tree(filenames, '--stdin')
|
||||
|
||||
|
||||
def run_clang_format_and_save_to_tree(changed_lines, binary='clang-format',
|
||||
style=None):
|
||||
"""Run clang-format on each file and save the result to a git tree.
|
||||
|
||||
Returns the object ID (SHA-1) of the created tree."""
|
||||
def index_info_generator():
|
||||
for filename, line_ranges in changed_lines.iteritems():
|
||||
mode = oct(os.stat(filename).st_mode)
|
||||
blob_id = clang_format_to_blob(filename, line_ranges, binary=binary,
|
||||
style=style)
|
||||
yield '%s %s\t%s' % (mode, blob_id, filename)
|
||||
return create_tree(index_info_generator(), '--index-info')
|
||||
|
||||
|
||||
def create_tree(input_lines, mode):
|
||||
"""Create a tree object from the given input.
|
||||
|
||||
If mode is '--stdin', it must be a list of filenames. If mode is
|
||||
'--index-info' is must be a list of values suitable for "git update-index
|
||||
--index-info", such as "<mode> <SP> <sha1> <TAB> <filename>". Any other mode
|
||||
is invalid."""
|
||||
assert mode in ('--stdin', '--index-info')
|
||||
cmd = ['git', 'update-index', '--add', '-z', mode]
|
||||
with temporary_index_file():
|
||||
p = subprocess.Popen(cmd, stdin=subprocess.PIPE)
|
||||
for line in input_lines:
|
||||
p.stdin.write('%s\0' % line)
|
||||
p.stdin.close()
|
||||
if p.wait() != 0:
|
||||
die('`%s` failed' % ' '.join(cmd))
|
||||
tree_id = run('git', 'write-tree')
|
||||
return tree_id
|
||||
|
||||
|
||||
def clang_format_to_blob(filename, line_ranges, binary='clang-format',
|
||||
style=None):
|
||||
"""Run clang-format on the given file and save the result to a git blob.
|
||||
|
||||
Returns the object ID (SHA-1) of the created blob."""
|
||||
clang_format_cmd = [binary, filename]
|
||||
if style:
|
||||
clang_format_cmd.extend(['-style='+style])
|
||||
clang_format_cmd.extend([
|
||||
'-lines=%s:%s' % (start_line, start_line+line_count-1)
|
||||
for start_line, line_count in line_ranges])
|
||||
try:
|
||||
clang_format = subprocess.Popen(clang_format_cmd, stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE)
|
||||
except OSError as e:
|
||||
if e.errno == errno.ENOENT:
|
||||
die('cannot find executable "%s"' % binary)
|
||||
else:
|
||||
raise
|
||||
clang_format.stdin.close()
|
||||
hash_object_cmd = ['git', 'hash-object', '-w', '--path='+filename, '--stdin']
|
||||
hash_object = subprocess.Popen(hash_object_cmd, stdin=clang_format.stdout,
|
||||
stdout=subprocess.PIPE)
|
||||
clang_format.stdout.close()
|
||||
stdout = hash_object.communicate()[0]
|
||||
if hash_object.returncode != 0:
|
||||
die('`%s` failed' % ' '.join(hash_object_cmd))
|
||||
if clang_format.wait() != 0:
|
||||
die('`%s` failed' % ' '.join(clang_format_cmd))
|
||||
return stdout.rstrip('\r\n')
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def temporary_index_file(tree=None):
|
||||
"""Context manager for setting GIT_INDEX_FILE to a temporary file and deleting
|
||||
the file afterward."""
|
||||
index_path = create_temporary_index(tree)
|
||||
old_index_path = os.environ.get('GIT_INDEX_FILE')
|
||||
os.environ['GIT_INDEX_FILE'] = index_path
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
if old_index_path is None:
|
||||
del os.environ['GIT_INDEX_FILE']
|
||||
else:
|
||||
os.environ['GIT_INDEX_FILE'] = old_index_path
|
||||
os.remove(index_path)
|
||||
|
||||
|
||||
def create_temporary_index(tree=None):
|
||||
"""Create a temporary index file and return the created file's path.
|
||||
|
||||
If `tree` is not None, use that as the tree to read in. Otherwise, an
|
||||
empty index is created."""
|
||||
gitdir = run('git', 'rev-parse', '--git-dir')
|
||||
path = os.path.join(gitdir, temp_index_basename)
|
||||
if tree is None:
|
||||
tree = '--empty'
|
||||
run('git', 'read-tree', '--index-output='+path, tree)
|
||||
return path
|
||||
|
||||
|
||||
def print_diff(old_tree, new_tree):
|
||||
"""Print the diff between the two trees to stdout."""
|
||||
# We use the porcelain 'diff' and not plumbing 'diff-tree' because the output
|
||||
# is expected to be viewed by the user, and only the former does nice things
|
||||
# like color and pagination.
|
||||
subprocess.check_call(['git', 'diff', old_tree, new_tree, '--'])
|
||||
|
||||
|
||||
def apply_changes(old_tree, new_tree, force=False, patch_mode=False):
|
||||
"""Apply the changes in `new_tree` to the working directory.
|
||||
|
||||
Bails if there are local changes in those files and not `force`. If
|
||||
`patch_mode`, runs `git checkout --patch` to select hunks interactively."""
|
||||
changed_files = run('git', 'diff-tree', '-r', '-z', '--name-only', old_tree,
|
||||
new_tree).rstrip('\0').split('\0')
|
||||
if not force:
|
||||
unstaged_files = run('git', 'diff-files', '--name-status', *changed_files)
|
||||
if unstaged_files:
|
||||
print >>sys.stderr, ('The following files would be modified but '
|
||||
'have unstaged changes:')
|
||||
print >>sys.stderr, unstaged_files
|
||||
print >>sys.stderr, 'Please commit, stage, or stash them first.'
|
||||
sys.exit(2)
|
||||
if patch_mode:
|
||||
# In patch mode, we could just as well create an index from the new tree
|
||||
# and checkout from that, but then the user will be presented with a
|
||||
# message saying "Discard ... from worktree". Instead, we use the old
|
||||
# tree as the index and checkout from new_tree, which gives the slightly
|
||||
# better message, "Apply ... to index and worktree". This is not quite
|
||||
# right, since it won't be applied to the user's index, but oh well.
|
||||
with temporary_index_file(old_tree):
|
||||
subprocess.check_call(['git', 'checkout', '--patch', new_tree])
|
||||
index_tree = old_tree
|
||||
else:
|
||||
with temporary_index_file(new_tree):
|
||||
run('git', 'checkout-index', '-a', '-f')
|
||||
return changed_files
|
||||
|
||||
|
||||
def run(*args, **kwargs):
|
||||
stdin = kwargs.pop('stdin', '')
|
||||
verbose = kwargs.pop('verbose', True)
|
||||
strip = kwargs.pop('strip', True)
|
||||
for name in kwargs:
|
||||
raise TypeError("run() got an unexpected keyword argument '%s'" % name)
|
||||
p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
||||
stdin=subprocess.PIPE)
|
||||
stdout, stderr = p.communicate(input=stdin)
|
||||
if p.returncode == 0:
|
||||
if stderr:
|
||||
if verbose:
|
||||
print >>sys.stderr, '`%s` printed to stderr:' % ' '.join(args)
|
||||
print >>sys.stderr, stderr.rstrip()
|
||||
if strip:
|
||||
stdout = stdout.rstrip('\r\n')
|
||||
return stdout
|
||||
if verbose:
|
||||
print >>sys.stderr, '`%s` returned %s' % (' '.join(args), p.returncode)
|
||||
if stderr:
|
||||
print >>sys.stderr, stderr.rstrip()
|
||||
sys.exit(2)
|
||||
|
||||
|
||||
def die(message):
|
||||
print >>sys.stderr, 'error:', message
|
||||
sys.exit(2)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user