mirror of
https://github.com/nghttp2/nghttp2.git
synced 2025-12-07 02:28:53 +08:00
Compare commits
1553 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d6ed16ab9 | ||
|
|
5a0f8b1f1f | ||
|
|
666308d8f6 | ||
|
|
789eb8a590 | ||
|
|
4cad48da16 | ||
|
|
e6566a1e3a | ||
|
|
fb4b152a73 | ||
|
|
4f090d90c0 | ||
|
|
891134eba9 | ||
|
|
36d562927f | ||
|
|
928a81885c | ||
|
|
647e30619f | ||
|
|
a4156cded3 | ||
|
|
4a99853021 | ||
|
|
176411aada | ||
|
|
4b4a0f7492 | ||
|
|
fe4efb0a5e | ||
|
|
1006caf8e9 | ||
|
|
780a0381ba | ||
|
|
e8ec7867b2 | ||
|
|
2423b21c71 | ||
|
|
08bf8a8f1a | ||
|
|
1c12606e70 | ||
|
|
059280d1a5 | ||
|
|
836b1de0d5 | ||
|
|
12cecaa030 | ||
|
|
50ae0ce739 | ||
|
|
cd4758227d | ||
|
|
2e643424ac | ||
|
|
9da6c88d25 | ||
|
|
04aaaa3821 | ||
|
|
da3bd559a9 | ||
|
|
727ba4906c | ||
|
|
71623b674e | ||
|
|
6a511aef0a | ||
|
|
627d04fe6b | ||
|
|
165cab7a29 | ||
|
|
5b59e46e2b | ||
|
|
73b77964ef | ||
|
|
8ce8b2af46 | ||
|
|
d1e49a196d | ||
|
|
8c8d1f6e14 | ||
|
|
48244b9aca | ||
|
|
eb42fb4f49 | ||
|
|
b37834c584 | ||
|
|
d197d115dc | ||
|
|
3a1c37248a | ||
|
|
f7fa9eb21b | ||
|
|
4839d6d513 | ||
|
|
e1621584fa | ||
|
|
97566ce4e3 | ||
|
|
900dcf4ced | ||
|
|
06a0f3480e | ||
|
|
473311269d | ||
|
|
0c4cbc800b | ||
|
|
b8f05c89bd | ||
|
|
e91a576179 | ||
|
|
72c661f1dd | ||
|
|
2277cc771a | ||
|
|
f8c30d0229 | ||
|
|
53b5ffa103 | ||
|
|
b384b76f66 | ||
|
|
b406d2da9e | ||
|
|
dd97b53554 | ||
|
|
0f7e84bb62 | ||
|
|
81add96b1e | ||
|
|
020e66b9da | ||
|
|
d2a63a88a0 | ||
|
|
e5a9f6c163 | ||
|
|
560955f50d | ||
|
|
8410f684fb | ||
|
|
ff44e211ed | ||
|
|
73442ba5ba | ||
|
|
b78662e506 | ||
|
|
8a7bc6f7bc | ||
|
|
66f2097627 | ||
|
|
a67d15be58 | ||
|
|
2e7cee0faa | ||
|
|
50eac7bdf0 | ||
|
|
846da5fb3f | ||
|
|
3e53cd189c | ||
|
|
9331dc755e | ||
|
|
bf8220fd5e | ||
|
|
6a70f34c73 | ||
|
|
c50ea91a3f | ||
|
|
388285675b | ||
|
|
0481d44c69 | ||
|
|
3d0905a90f | ||
|
|
091055edec | ||
|
|
4f0ff5bfb1 | ||
|
|
c941bff929 | ||
|
|
0bbff5b738 | ||
|
|
e66bd490a4 | ||
|
|
e9e9626c2c | ||
|
|
a152c6346d | ||
|
|
96a6dcb152 | ||
|
|
8c701bb139 | ||
|
|
ebf27e6523 | ||
|
|
c379b46a26 | ||
|
|
762bafa47b | ||
|
|
0ed8ed3f8d | ||
|
|
6446660113 | ||
|
|
d0a37d59a5 | ||
|
|
58dd924343 | ||
|
|
a73cfd5f7b | ||
|
|
a1288a5826 | ||
|
|
80d96590c5 | ||
|
|
e8c83798da | ||
|
|
6ad332b801 | ||
|
|
f1b163a32c | ||
|
|
7a3dff5a65 | ||
|
|
4d7be08585 | ||
|
|
a6fdca730d | ||
|
|
4949dd4888 | ||
|
|
2f2a300e83 | ||
|
|
a4a9cfd650 | ||
|
|
bb228c27de | ||
|
|
7152e0f6b8 | ||
|
|
72cfdbc0fa | ||
|
|
f472e6ff48 | ||
|
|
1708d2e69e | ||
|
|
e3cdfd12ea | ||
|
|
cd25c6846e | ||
|
|
efcd43a367 | ||
|
|
90b4b48c7e | ||
|
|
9eb234d321 | ||
|
|
fea70ec832 | ||
|
|
3e333f49fe | ||
|
|
12013508bf | ||
|
|
871a93e4aa | ||
|
|
85e4151bca | ||
|
|
278247faa9 | ||
|
|
e2808dcd96 | ||
|
|
adec2c06bf | ||
|
|
6c8243f6d2 | ||
|
|
afbb99ecf7 | ||
|
|
abce7c7210 | ||
|
|
ec617ae6e5 | ||
|
|
d6786f75cb | ||
|
|
584f5f3734 | ||
|
|
a04c67bcd6 | ||
|
|
8325c6ccdf | ||
|
|
30235f8809 | ||
|
|
9b63fc011e | ||
|
|
cab6c7871c | ||
|
|
04bd25d468 | ||
|
|
cd2c751f82 | ||
|
|
65df3c63bf | ||
|
|
aa012f7a58 | ||
|
|
f6a8c8d078 | ||
|
|
7f71fed963 | ||
|
|
44cbc785fc | ||
|
|
916e27489f | ||
|
|
650e463a49 | ||
|
|
52152ad96d | ||
|
|
f3288092e8 | ||
|
|
18234b8741 | ||
|
|
26b7bee16f | ||
|
|
921e393dcd | ||
|
|
0ec1b98f27 | ||
|
|
a8574fdef2 | ||
|
|
dd8ce1e9d2 | ||
|
|
ca3444c34c | ||
|
|
5c96ecd77d | ||
|
|
fa7a74cfa8 | ||
|
|
b37716ab6a | ||
|
|
5dc060c1a2 | ||
|
|
05d5c404e2 | ||
|
|
cce64e6728 | ||
|
|
006ac7b1b0 | ||
|
|
5465cdf4a4 | ||
|
|
f2a1c8ee60 | ||
|
|
a78fcdb7bf | ||
|
|
ad87266a58 | ||
|
|
d326e28c92 | ||
|
|
c1c177addc | ||
|
|
e8167ceea7 | ||
|
|
ac301537ef | ||
|
|
03034c0081 | ||
|
|
fa872a6b48 | ||
|
|
a23ab00686 | ||
|
|
eeba12144c | ||
|
|
ce00c50720 | ||
|
|
7d2ea42c38 | ||
|
|
e0d7ce439d | ||
|
|
ccfe7f8548 | ||
|
|
c470ac7b00 | ||
|
|
6cf772da6a | ||
|
|
5892385e5c | ||
|
|
1ce3a000a1 | ||
|
|
8585599b4b | ||
|
|
2bac00ea5f | ||
|
|
3396c71c3c | ||
|
|
f821f6bd80 | ||
|
|
7a3012cc1b | ||
|
|
ba31b990a2 | ||
|
|
fc062976a1 | ||
|
|
20e63151a5 | ||
|
|
e63e775fea | ||
|
|
031fb31248 | ||
|
|
6d10799301 | ||
|
|
d6551de8d4 | ||
|
|
6b53f7ee19 | ||
|
|
f54745fe4c | ||
|
|
da51389927 | ||
|
|
a06c16c99a | ||
|
|
3db03a3f51 | ||
|
|
5e966d000a | ||
|
|
1c7431293d | ||
|
|
9c8bc1218d | ||
|
|
860da8bf87 | ||
|
|
326ac31a23 | ||
|
|
a2c78cfc69 | ||
|
|
4fed7a1476 | ||
|
|
8c1e863523 | ||
|
|
27da08ee68 | ||
|
|
0a6877d091 | ||
|
|
7f7b6d641d | ||
|
|
7c301defbd | ||
|
|
2482dd7f77 | ||
|
|
fb7775e382 | ||
|
|
f96edbf987 | ||
|
|
3db0badc35 | ||
|
|
3097547491 | ||
|
|
6307f96fb3 | ||
|
|
8a2543d7b7 | ||
|
|
fa7069a273 | ||
|
|
1a63cd94aa | ||
|
|
7c216c6df8 | ||
|
|
e7724914a9 | ||
|
|
19e47a1922 | ||
|
|
d62e4dbc5e | ||
|
|
c2e4ed9624 | ||
|
|
d457f39b1e | ||
|
|
6d556755ee | ||
|
|
3119fc259c | ||
|
|
1940413eb3 | ||
|
|
237f742100 | ||
|
|
764cd17316 | ||
|
|
c70cfe64c4 | ||
|
|
693fba3b64 | ||
|
|
8fcf5f60e4 | ||
|
|
c00f8e3847 | ||
|
|
7af638f921 | ||
|
|
580c1f3af3 | ||
|
|
d10228cdf7 | ||
|
|
1dd3564d15 | ||
|
|
e3fd81995b | ||
|
|
d4e1b63c19 | ||
|
|
3973d9ce12 | ||
|
|
43d6f6b16a | ||
|
|
2f7839fa42 | ||
|
|
49c3d75f9a | ||
|
|
dc7232fa53 | ||
|
|
51ef646678 | ||
|
|
515c313073 | ||
|
|
197493afd4 | ||
|
|
2ea1deafd0 | ||
|
|
b06e339dbb | ||
|
|
68c0f8a310 | ||
|
|
5182dc6cc9 | ||
|
|
68f6df61e5 | ||
|
|
078337de3c | ||
|
|
9e82687cbe | ||
|
|
660f90e13f | ||
|
|
2eec42df39 | ||
|
|
34b92a30de | ||
|
|
bab99af565 | ||
|
|
2e8e38ec35 | ||
|
|
d6ca95a0c9 | ||
|
|
285c74c394 | ||
|
|
7780c07838 | ||
|
|
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 |
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
|
||||
...
|
||||
|
||||
48
.gitignore
vendored
48
.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,27 +20,21 @@ 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
|
||||
doc/h2load-howto.rst
|
||||
doc/tutorial-hpack.rst
|
||||
doc/python-apiref.rst
|
||||
doc/building-android-binary.rst
|
||||
|
||||
# test logs generated by `make check`
|
||||
*.log
|
||||
*.trs
|
||||
|
||||
lib/MSVC_obj/
|
||||
_VC_ROOT/
|
||||
.depend.MSVC
|
||||
*.pyd
|
||||
*.egg-info/
|
||||
python/nghttp2.c
|
||||
|
||||
47
.travis.yml
47
.travis.yml
@@ -1,32 +1,31 @@
|
||||
language: cpp
|
||||
compiler:
|
||||
- clang
|
||||
#Disable gcc build for the moment...
|
||||
# - gcc
|
||||
python:
|
||||
- "3.4"
|
||||
- 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
|
||||
- sudo add-apt-repository --yes ppa:ubuntu-toolchain-r/test
|
||||
- sudo apt-get update -qq
|
||||
#Install and use gcc-4.8 (don't build with gcc-4.6)
|
||||
#libstdc++-4.8 is needed by Clang to build too
|
||||
- sudo apt-get -qq install g++-4.8 libstdc++-4.8-dev
|
||||
- >
|
||||
sudo apt-get install --no-install-recommends -qq
|
||||
autoconf
|
||||
automake
|
||||
autotools-dev
|
||||
libtool
|
||||
pkg-config
|
||||
zlib1g-dev
|
||||
libcunit1-dev
|
||||
libssl-dev
|
||||
libxml2-dev
|
||||
libevent-dev
|
||||
libjansson-dev
|
||||
libjemalloc-dev
|
||||
- if [ "$CXX" = "g++" ]; then export CXX="g++-4.8" CC="gcc-4.8"; fi
|
||||
- if [ "$CXX" = "g++" ]; then export CXX="g++-4.9" CC="gcc-4.9"; fi
|
||||
- $CC --version
|
||||
before_script:
|
||||
- autoreconf -i
|
||||
|
||||
18
CONTRIBUTION
Normal file
18
CONTRIBUTION
Normal file
@@ -0,0 +1,18 @@
|
||||
[The text below was composed based on 1.2. License section of
|
||||
curl/libcurl project.]
|
||||
|
||||
When contributing with code, you agree to put your changes and new
|
||||
code under the same license nghttp2 is already using unless stated and
|
||||
agreed otherwise.
|
||||
|
||||
When changing existing source code, you do not alter the copyright of
|
||||
the original file(s). The copyright will still be owned by the
|
||||
original creator(s) or those who have been assigned copyright by the
|
||||
original author(s).
|
||||
|
||||
By submitting a patch to the nghttp2 project, you are assumed to have
|
||||
the right to the code and to be allowed by your employer or whatever
|
||||
to hand over that patch/code to us. We will credit you for your
|
||||
changes as far as possible, to give credit but also to keep a trace
|
||||
back to who made what changes. Please always provide us with your
|
||||
full real name when contributing!
|
||||
21
COPYING
21
COPYING
@@ -1,6 +1,6 @@
|
||||
The MIT License
|
||||
|
||||
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
|
||||
@@ -20,22 +20,3 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
[The text below was composed based on 1.2. License section of
|
||||
curl/libcurl project.]
|
||||
|
||||
When contributing with code, you agree to put your changes and new
|
||||
code under the same license nghttp2 is already using unless stated and
|
||||
agreed otherwise.
|
||||
|
||||
When changing existing source code, you do not alter the copyright of
|
||||
the original file(s). The copyright will still be owned by the
|
||||
original creator(s) or those who have been assigned copyright by the
|
||||
original author(s).
|
||||
|
||||
By submitting a patch to the nghttp2 project, you are assumed to have
|
||||
the right to the code and to be allowed by your employer or whatever
|
||||
to hand over that patch/code to us. We will credit you for your
|
||||
changes as far as possible, to give credit but also to keep a trace
|
||||
back to who made what changes. Please always provide us with your
|
||||
full real name when contributing!
|
||||
|
||||
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
|
||||
22
Makefile.am
22
Makefile.am
@@ -20,11 +20,29 @@
|
||||
# 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
|
||||
|
||||
# Now with python setuptools, make uninstall will leave many files we
|
||||
# cannot easily remove (e.g., easy-install.pth). Disable it for
|
||||
# distcheck rule.
|
||||
AM_DISTCHECK_CONFIGURE_FLAGS = --disable-python-bindings
|
||||
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
dist_doc_DATA = README.rst
|
||||
|
||||
EXTRA_DIST = nghttpx.conf.sample proxy.pac.sample android-config android-make \
|
||||
Makefile.msvc
|
||||
Dockerfile.android
|
||||
|
||||
.PHONY: clang-format
|
||||
|
||||
# Format source files using clang-format. Don't format source files
|
||||
# under third-party directory since we are not responsible for thier
|
||||
# coding style.
|
||||
clang-format:
|
||||
CLANGFORMAT=`git config --get clangformat.binary`; \
|
||||
test -z $${CLANGFORMAT} && CLANGFORMAT="clang-format"; \
|
||||
$${CLANGFORMAT} -i lib/*.{c,h} lib/includes/nghttp2/*.h \
|
||||
src/*.{c,cc,h} src/includes/nghttp2/*.h examples/*.{c,cc} \
|
||||
tests/*.{c,h}
|
||||
|
||||
266
Makefile.msvc
266
Makefile.msvc
@@ -1,266 +0,0 @@
|
||||
#
|
||||
# GNU Makefile for nghttp2 / MSVC.
|
||||
#
|
||||
# By G. Vanem <gvanem@yahoo.no> 2013
|
||||
# The MIT License apply.
|
||||
#
|
||||
|
||||
#
|
||||
# Choose your weapons:
|
||||
# Set 'ZLIB_ROOT' to the root of zlib.
|
||||
# Set 'USE_CYTHON=1' to build and install the 'nghttp2.pyd' Python extension.
|
||||
#
|
||||
ZLIB_ROOT = g:/MingW32/src/Compression/zlib-1.2.8
|
||||
USE_CYTHON = 1
|
||||
|
||||
_VERSION := $(shell grep AC_INIT ../configure.ac | cut -d'[' -f3 | sed -e 's/-DEV], //g')
|
||||
_VERSION := $(subst ., ,$(_VERSION))
|
||||
VER_MAJOR = $(word 1,$(_VERSION))
|
||||
VER_MINOR = $(word 2,$(_VERSION))
|
||||
VER_MICRO = $(word 3,$(_VERSION))
|
||||
VERSION = $(VER_MAJOR).$(VER_MINOR).$(VER_MICRO)
|
||||
VERSION_NUM = ($(VER_MAJOR) << 16) + ($(VER_MINOR) << 8) + $(VER_MICRO)
|
||||
|
||||
GENERATED = 'Generated by $(realpath Makefile.MSVC)'
|
||||
|
||||
#
|
||||
# Where to copy nghttp2.dll + lib + headers to.
|
||||
# Note: 'make install' is not in default targets. Do it explicitly.
|
||||
#
|
||||
VC_ROOT = $(realpath $(VCINSTALLDIR))
|
||||
INSTALL_BIN = $(VC_ROOT)/bin
|
||||
INSTALL_LIB = $(VC_ROOT)/lib
|
||||
INSTALL_HDR = $(VC_ROOT)/include
|
||||
|
||||
#
|
||||
# Build for DEBUG-model and RELEASE at the same time.
|
||||
#
|
||||
TARGETS = nghttp2.lib nghttp2.dll nghttp2_imp.lib \
|
||||
nghttp2d.lib nghttp2d.dll nghttp2d_imp.lib
|
||||
|
||||
EXT_LIBS = $(ZLIB_ROOT)/zlib.lib ws2_32.lib
|
||||
|
||||
OBJ_DIR = MSVC_obj
|
||||
|
||||
NGHTTP2_PDB_R = $(OBJ_DIR)/nghttp2.pdb
|
||||
NGHTTP2_PDB_D = $(OBJ_DIR)/nghttp2d.pdb
|
||||
|
||||
CC = cl
|
||||
CFLAGS = -I./includes -I$(ZLIB_ROOT) -DHAVE_WINSOCK2_H -Dssize_t=long
|
||||
|
||||
CFLAGS_R = -nologo -MD -W3 -Zi -Fd./$(NGHTTP2_PDB_R)
|
||||
CFLAGS_D = -nologo -MDd -W3 -Zi -Fd./$(NGHTTP2_PDB_D) \
|
||||
-Ot -D_DEBUG -GF -RTCs -RTCu # -RTCc -GS
|
||||
|
||||
LDFLAGS = -nologo -machine:i386 -map -debug -incremental:no # -verbose
|
||||
|
||||
NGHTTP2_SRC = nghttp2_buf.c \
|
||||
nghttp2_callbacks.c \
|
||||
nghttp2_frame.c \
|
||||
nghttp2_helper.c \
|
||||
nghttp2_hd.c \
|
||||
nghttp2_hd_huffman.c \
|
||||
nghttp2_hd_huffman_data.c \
|
||||
nghttp2_map.c \
|
||||
nghttp2_npn.c \
|
||||
nghttp2_option.c \
|
||||
nghttp2_outbound_item.c \
|
||||
nghttp2_priority_spec.c \
|
||||
nghttp2_pq.c \
|
||||
nghttp2_queue.c \
|
||||
nghttp2_session.c \
|
||||
nghttp2_stream.c \
|
||||
nghttp2_submit.c \
|
||||
nghttp2_version.c
|
||||
|
||||
NGHTTP2_OBJ_R = $(addprefix $(OBJ_DIR)/r_, $(notdir $(NGHTTP2_SRC:.c=.obj)))
|
||||
NGHTTP2_OBJ_D = $(addprefix $(OBJ_DIR)/d_, $(notdir $(NGHTTP2_SRC:.c=.obj)))
|
||||
|
||||
all: intro $(OBJ_DIR) $(TARGETS)
|
||||
@echo 'Welcome to NgHTTP2 (release + debug).'
|
||||
@echo 'Do a "make -f Makefile.MSVC install" at own risk!'
|
||||
|
||||
intro:
|
||||
@echo 'Building NgHTTP (MSVC) ver. "$(VERSION)".'
|
||||
|
||||
test_ver:
|
||||
@echo '$$(VERSION): "$(VERSION)".'
|
||||
@echo '$$(_VERSION): "$(_VERSION)".'
|
||||
@echo '$$(VER_MAJOR): "$(VER_MAJOR)".'
|
||||
@echo '$$(VER_MINOR): "$(VER_MINOR)".'
|
||||
@echo '$$(VER_MICRO): "$(VER_MICRO)".'
|
||||
|
||||
$(OBJ_DIR):
|
||||
- mkdir $(OBJ_DIR)
|
||||
|
||||
install: includes/nghttp2/nghttp2.h includes/nghttp2/nghttp2ver.h \
|
||||
nghttp2.dll nghttp2.lib nghttp2_imp.lib \
|
||||
nghttp2d.dll nghttp2d.lib nghttp2d_imp.lib \
|
||||
copy_headers_and_libs install_nghttp2_pyd_$(USE_CYTHON)
|
||||
|
||||
#
|
||||
# This MUST be done before using the 'install_nghttp2_pyd_1' rule.
|
||||
#
|
||||
copy_headers_and_libs:
|
||||
- mkdir $(INSTALL_HDR)/nghttp2
|
||||
cp --update $(addprefix includes/nghttp2/, nghttp2.h nghttp2ver.h) $(INSTALL_HDR)/nghttp2
|
||||
cp --update nghttp2.dll nghttp2d.dll $(NGHTTP2_PDB_R) $(NGHTTP2_PDB_D) $(INSTALL_BIN)
|
||||
cp --update nghttp2.lib nghttp2d.lib nghttp2_imp.lib nghttp2d_imp.lib $(INSTALL_LIB)
|
||||
@echo
|
||||
|
||||
nghttp2.lib: $(NGHTTP2_OBJ_R)
|
||||
lib -nologo -out:$@ $^
|
||||
@echo
|
||||
|
||||
nghttp2d.lib: $(NGHTTP2_OBJ_D)
|
||||
lib -nologo -out:$@ $^
|
||||
@echo
|
||||
|
||||
nghttp2.dll nghttp2_imp.lib: $(NGHTTP2_OBJ_R) $(OBJ_DIR)/r_nghttp2.res $(OBJ_DIR)/r_nghttp2.def
|
||||
link $(LDFLAGS) -dll -out:nghttp2.dll -implib:nghttp2_imp.lib -def:$(OBJ_DIR)/r_nghttp2.def \
|
||||
$(NGHTTP2_OBJ_R) $(OBJ_DIR)/r_nghttp2.res $(EXT_LIBS)
|
||||
@echo
|
||||
|
||||
nghttp2d.dll nghttp2d_imp.lib: $(NGHTTP2_OBJ_D) $(OBJ_DIR)/d_nghttp2.res $(OBJ_DIR)/d_nghttp2.def
|
||||
link $(LDFLAGS) -dll -out:nghttp2d.dll -implib:nghttp2d_imp.lib -def:$(OBJ_DIR)/d_nghttp2.def \
|
||||
$(NGHTTP2_OBJ_D) $(OBJ_DIR)/d_nghttp2.res $(EXT_LIBS)
|
||||
@echo
|
||||
|
||||
install_nghttp2_pyd_0: ;
|
||||
|
||||
install_nghttp2_pyd_1: $(addprefix ../python/, setup.py.in nghttp2.pyx)
|
||||
cd ../python ; \
|
||||
echo '# $(GENERATED). DO NOT EDIT.' > setup.py ; \
|
||||
sed -e 's/@top_srcdir@/../' \
|
||||
-e 's/@top_builddir@/../' \
|
||||
-e 's/@PACKAGE_VERSION@/$(VERSION)/' setup.py.in >> setup.py ; \
|
||||
cython -v nghttp2.pyx ; \
|
||||
python setup.py install
|
||||
|
||||
clean_nghttp2_pyd_0: ;
|
||||
|
||||
clean_nghttp2_pyd_1:
|
||||
cd ../python ; \
|
||||
rm -f setup.py nghttp2.c ; \
|
||||
rm -fR build/*
|
||||
|
||||
$(OBJ_DIR)/r_%.obj: %.c
|
||||
$(CC) $(CFLAGS_R) $(CFLAGS) -Fo$@ -c $<
|
||||
@echo
|
||||
|
||||
$(OBJ_DIR)/d_%.obj: %.c
|
||||
$(CC) $(CFLAGS_D) $(CFLAGS) -Fo$@ -c $<
|
||||
@echo
|
||||
|
||||
$(OBJ_DIR)/r_nghttp2.res: nghttp2.rc
|
||||
rc -nologo -D_RELEASE -Fo $@ $<
|
||||
@echo
|
||||
|
||||
$(OBJ_DIR)/d_nghttp2.res: nghttp2.rc
|
||||
rc -nologo -D_DEBUG -Fo $@ $<
|
||||
@echo
|
||||
|
||||
includes/nghttp2/nghttp2ver.h: includes/nghttp2/nghttp2ver.h.in
|
||||
sed < includes/nghttp2/nghttp2ver.h.in \
|
||||
-e 's/@PACKAGE_VERSION@/$(VERSION)/g' \
|
||||
-e 's/@PACKAGE_VERSION_NUM@/($(VERSION_NUM))/g' > $@
|
||||
touch --reference=includes/nghttp2/nghttp2ver.h.in $@
|
||||
|
||||
|
||||
define RES_FILE
|
||||
#include <winver.h>
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION $(VER_MAJOR), $(VER_MINOR), $(VER_MICRO), 0
|
||||
PRODUCTVERSION $(VER_MAJOR), $(VER_MINOR), $(VER_MICRO), 0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
FILEOS 0x40004L
|
||||
FILETYPE 0x2L
|
||||
FILESUBTYPE 0x0L
|
||||
#ifdef _DEBUG
|
||||
#define VER_STR "$(VERSION).0 (MSVC debug)"
|
||||
#define DBG "d"
|
||||
FILEFLAGS 0x1L
|
||||
#else
|
||||
#define VER_STR "$(VERSION).0 (MSVC release)"
|
||||
#define DBG ""
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "http://tatsuhiro-t.github.io/nghttp2/"
|
||||
VALUE "FileDescription", "nghttp2; HTTP/2 C library"
|
||||
VALUE "FileVersion", VER_STR
|
||||
VALUE "InternalName", "nghttp2" DBG
|
||||
VALUE "LegalCopyright", "The MIT License"
|
||||
VALUE "LegalTrademarks", ""
|
||||
VALUE "OriginalFilename", "nghttp2" DBG ".dll"
|
||||
VALUE "ProductName", "NGHTTP2."
|
||||
VALUE "ProductVersion", VER_STR
|
||||
VALUE "PrivateBuild", "The privat build of <gvanem@yahoo.no>."
|
||||
VALUE "SpecialBuild", ""
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
||||
endef
|
||||
|
||||
export RES_FILE
|
||||
|
||||
nghttp2.rc: Makefile.MSVC
|
||||
@echo 'Generating $@...'
|
||||
@echo ' /* $(GENERATED). DO NOT EDIT.' > $@
|
||||
@echo ' */' >> $@
|
||||
@echo "$$RES_FILE" >> $@
|
||||
|
||||
$(OBJ_DIR)/r_nghttp2.def: Makefile.MSVC
|
||||
@echo 'Generating $@...'
|
||||
@echo '; $(GENERATED). DO NOT EDIT.' > $@
|
||||
@echo ';' >> $@
|
||||
@echo 'LIBRARY nghttp2.dll' >> $@
|
||||
@echo 'EXPORTS' >> $@
|
||||
nm $(NGHTTP2_OBJ_R) | grep ' T .*_nghttp2' | sed 's/^.* _/ /' >> $@
|
||||
@echo 'NGHTTP2_STATIC_TABLE_LENGTH DATA' >> $@
|
||||
|
||||
$(OBJ_DIR)/d_nghttp2.def: Makefile.MSVC
|
||||
@echo 'Generating $@...'
|
||||
@echo '; $(GENERATED). DO NOT EDIT.' > $@
|
||||
@echo ';' >> $@
|
||||
@echo 'LIBRARY nghttp2d.dll' >> $@
|
||||
@echo 'EXPORTS' >> $@
|
||||
nm $(NGHTTP2_OBJ_D) | grep ' T .*_nghttp2' | sed 's/^.* _/ /' >> $@
|
||||
@echo 'NGHTTP2_STATIC_TABLE_LENGTH DATA' >> $@
|
||||
|
||||
clean:
|
||||
rm -f $(OBJ_DIR)/* nghttp2_imp.exp nghttp2_imp.exp \
|
||||
nghttp2.map nghttp2d.map nghttp2.rc includes/nghttp2/nghttp2ver.h
|
||||
@echo
|
||||
|
||||
vclean realclean: clean clean_nghttp2_pyd_$(USE_CYTHON)
|
||||
rm -f $(TARGETS) nghttp2.pdb nghttp2d.pdb nghttp2_imp.exp nghttp2d_imp.exp .depend.MSVC
|
||||
- rmdir $(OBJ_DIR)
|
||||
|
||||
#
|
||||
# Use gcc to generated the dependencies. No MSVC specific args please!
|
||||
#
|
||||
REPLACE_R = 's/\(.*\)\.o: /\n$$(OBJ_DIR)\/r_\1.obj: /'
|
||||
REPLACE_D = 's/\(.*\)\.o: /\n$$(OBJ_DIR)\/d_\1.obj: /'
|
||||
|
||||
depend: includes/nghttp2/nghttp2ver.h
|
||||
@echo '# $(GENERATED). DO NOT EDIT.' > .depend.MSVC
|
||||
gcc -MM $(CFLAGS) $(NGHTTP2_SRC) >> .depend.tmp
|
||||
@echo '#' >> .depend.MSVC
|
||||
@echo '# Release lib objects:' >> .depend.MSVC
|
||||
sed -e $(REPLACE_R) .depend.tmp >> .depend.MSVC
|
||||
@echo '#' >> .depend.MSVC
|
||||
@echo '# Debug lib objects:' >> .depend.MSVC
|
||||
sed -e $(REPLACE_D) .depend.tmp >> .depend.MSVC
|
||||
rm -f .depend.tmp
|
||||
|
||||
-include .depend.MSVC
|
||||
970
README.rst
970
README.rst
File diff suppressed because it is too large
Load Diff
@@ -39,9 +39,9 @@ PATH=$TOOLCHAIN/bin:$PATH
|
||||
--without-libxml2 \
|
||||
--disable-python-bindings \
|
||||
--disable-examples \
|
||||
--disable-threads \
|
||||
CC=arm-linux-androideabi-clang \
|
||||
CXX=arm-linux-androideabi-clang++ \
|
||||
CPPFLAGS="-I$PREFIX/include" \
|
||||
--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"
|
||||
|
||||
406
configure.ac
406
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,15 +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.6.1], [t-tujikawa@users.sourceforge.net])
|
||||
AC_INIT([nghttp2], [1.3.0], [t-tujikawa@users.sourceforge.net])
|
||||
AC_CONFIG_AUX_DIR([.])
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
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])
|
||||
|
||||
# AM_EXTRA_RECURSIVE_TARGETS requires automake 1.13 or higher
|
||||
m4_ifdef([AM_EXTRA_RECURSIVE_TARGETS], [AM_EXTRA_RECURSIVE_TARGETS([it itprep])])
|
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
|
||||
dnl See versioning rule:
|
||||
dnl http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
||||
AC_SUBST(LT_CURRENT, 5)
|
||||
AC_SUBST(LT_CURRENT, 15)
|
||||
AC_SUBST(LT_REVISION, 0)
|
||||
AC_SUBST(LT_AGE, 0)
|
||||
AC_SUBST(LT_AGE, 1)
|
||||
|
||||
major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"`
|
||||
minor=`echo $PACKAGE_VERSION |cut -d. -f2 | sed -e "s/[^0-9]//g"`
|
||||
@@ -38,18 +58,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 +84,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 +100,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 +130,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,28 +151,24 @@ 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_MSG_CHECKING([whether std::chrono::steady_clock is available])
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
|
||||
[[
|
||||
#include <chrono>
|
||||
]],
|
||||
[[
|
||||
auto tp = std::chrono::steady_clock::now();
|
||||
]])],
|
||||
[AC_DEFINE([HAVE_STEADY_CLOCK], [1],
|
||||
[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(
|
||||
@@ -174,53 +186,49 @@ std::vector<std::future<int>> v;
|
||||
[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++"
|
||||
;;
|
||||
*)
|
||||
PTHREAD_LDFLAGS="-pthread"
|
||||
SRC_LIBS="$SRC_LIBS $PTHREAD_LDFLAGS"
|
||||
APPLDFLAGS="$APPLDFLAGS $PTHREAD_LDFLAGS"
|
||||
;;
|
||||
esac
|
||||
|
||||
# zlib
|
||||
if test "x$android_build" = "xyes"; then
|
||||
# Use zlib provided by NDK
|
||||
SRC_LIBS="-lz $SRC_LIBS"
|
||||
have_zlib=yes
|
||||
else
|
||||
PKG_CHECK_MODULES([ZLIB], [zlib >= 1.2.3], [have_zlib=yes], [have_zlib=no])
|
||||
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
|
||||
@@ -252,6 +260,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])
|
||||
@@ -259,7 +283,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])
|
||||
@@ -267,10 +291,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
|
||||
|
||||
@@ -297,9 +324,23 @@ if test "x${request_jemalloc}" != "xno"; then
|
||||
AC_SEARCH_LIBS([malloc_stats_print], [jemalloc], [have_jemalloc=yes], [],
|
||||
[$PTHREAD_LDFLAGS])
|
||||
LIBS=$LIBS_OLD
|
||||
|
||||
if test "x${have_jemalloc}" = "xyes"; then
|
||||
jemalloc_libs=${ac_cv_search_malloc_stats_print}
|
||||
else
|
||||
# On Darwin, malloc_stats_print is je_malloc_stats_print
|
||||
AC_SEARCH_LIBS([je_malloc_stats_print], [jemalloc], [have_jemalloc=yes], [],
|
||||
[$PTHREAD_LDFLAGS])
|
||||
LIBS=$LIBS_OLD
|
||||
|
||||
if test "x${have_jemalloc}" = "xyes"; then
|
||||
jemalloc_libs=${ac_cv_search_je_malloc_stats_print}
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "x${have_jemalloc}" = "xyes" &&
|
||||
test "x${ac_cv_search_malloc_stats_print}" != "xnone required"; then
|
||||
JEMALLOC_LIBS=${ac_cv_search_malloc_stats_print}
|
||||
test "x${jemalloc_libs}" != "xnone required"; then
|
||||
JEMALLOC_LIBS=${jemalloc_libs}
|
||||
AC_SUBST([JEMALLOC_LIBS])
|
||||
fi
|
||||
fi
|
||||
@@ -312,7 +353,7 @@ fi
|
||||
# spdylay (for src/nghttpx and src/h2load)
|
||||
have_spdylay=no
|
||||
if test "x${request_spdylay}" != "xno"; then
|
||||
PKG_CHECK_MODULES([LIBSPDYLAY], [libspdylay >= 1.3.0],
|
||||
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.])
|
||||
@@ -329,13 +370,32 @@ fi
|
||||
|
||||
AM_CONDITIONAL([HAVE_SPDYLAY], [ test "x${have_spdylay}" = "xyes" ])
|
||||
|
||||
# Check Boost Asio library
|
||||
have_asio_lib=no
|
||||
|
||||
if test "x${request_asio_lib}" = "xyes"; then
|
||||
AX_BOOST_BASE([1.54.0], [have_boost_base=yes], [have_boost_base=no])
|
||||
|
||||
if test "x${have_boost_base}" = "xyes"; then
|
||||
AX_BOOST_ASIO()
|
||||
AX_BOOST_SYSTEM()
|
||||
AX_BOOST_THREAD()
|
||||
|
||||
if test "x${ax_cv_boost_asio}" = "xyes" &&
|
||||
test "x${ax_cv_boost_system}" = "xyes" &&
|
||||
test "x${ax_cv_boost_thread}" = "xyes"; then
|
||||
have_asio_lib=yes
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# The nghttp, nghttpd and nghttpx under src depend on zlib, OpenSSL
|
||||
# and libevent_openssl
|
||||
# and libev
|
||||
enable_app=no
|
||||
if test "x${request_app}" != "xno" &&
|
||||
test "x${have_zlib}" = "xyes" &&
|
||||
test "x${have_openssl}" = "xyes" &&
|
||||
test "x${have_libevent_openssl}" = "xyes"; then
|
||||
test "x${have_libev}" = "xyes"; then
|
||||
enable_app=yes
|
||||
fi
|
||||
|
||||
@@ -345,7 +405,6 @@ if test "x${request_app}" = "xyes" &&
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL([ENABLE_APP], [ test "x${enable_app}" = "xyes" ])
|
||||
AM_CONDITIONAL([ENABLE_H2LOAD], [ test "x${have_std_future}" = "xyes" ])
|
||||
|
||||
enable_hpack_tools=no
|
||||
# HPACK tools requires jansson
|
||||
@@ -361,6 +420,16 @@ fi
|
||||
|
||||
AM_CONDITIONAL([ENABLE_HPACK_TOOLS], [ test "x${enable_hpack_tools}" = "xyes" ])
|
||||
|
||||
# C++ library libnghttp2_asio
|
||||
|
||||
enable_asio_lib=no
|
||||
if test "x${request_asio_lib}" != "xno" &&
|
||||
test "x${have_asio_lib}" = "xyes"; then
|
||||
enable_asio_lib=yes
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL([ENABLE_ASIO_LIB], [ test "x${enable_asio_lib}" = "xyes" ])
|
||||
|
||||
# The example programs depend on OpenSSL and libevent_openssl
|
||||
enable_examples=no
|
||||
if test "x${request_examples}" != "xno" &&
|
||||
@@ -376,6 +445,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" &&
|
||||
@@ -398,29 +478,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
|
||||
@@ -428,27 +513,82 @@ 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
|
||||
|
||||
# Checks for library functions.
|
||||
if test "x$cross_compiling" != "xyes"; then
|
||||
AC_FUNC_MALLOC
|
||||
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 *])
|
||||
|
||||
AC_CHECK_SIZEOF([time_t])
|
||||
|
||||
# Checks for library functions.
|
||||
|
||||
# Don't check malloc, since it does not play nicely with C++ stdlib
|
||||
# AC_FUNC_MALLOC
|
||||
|
||||
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"])
|
||||
@@ -471,7 +611,6 @@ if test "x$werror" != "xno"; then
|
||||
AX_CHECK_COMPILE_FLAG([-Wcast-align], [CFLAGS="$CFLAGS -Wcast-align"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wclobbered], [CFLAGS="$CFLAGS -Wclobbered"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wvla], [CFLAGS="$CFLAGS -Wvla"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wno-unused-parameter], [CFLAGS="$CFLAGS -Wno-unused-parameter"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wpragmas], [CFLAGS="$CFLAGS -Wpragmas"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wunreachable-code], [CFLAGS="$CFLAGS -Wunreachable-code"])
|
||||
AX_CHECK_COMPILE_FLAG([-Waddress], [CFLAGS="$CFLAGS -Waddress"])
|
||||
@@ -483,16 +622,26 @@ if test "x$werror" != "xno"; then
|
||||
AX_CHECK_COMPILE_FLAG([-Wheader-guard], [CFLAGS="$CFLAGS -Wheader-guard"])
|
||||
fi
|
||||
|
||||
WARNCFLAGS=$CFLAGS
|
||||
CFLAGS=$ac_save_CFLAGS
|
||||
|
||||
AC_SUBST([WARNCFLAGS])
|
||||
|
||||
if test "x$debug" != "xno"; then
|
||||
AC_DEFINE([DEBUGBUILD], [1], [Define to 1 to enable debug output.])
|
||||
fi
|
||||
|
||||
if test "x$threads" != "xyes"; then
|
||||
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
|
||||
@@ -504,9 +653,14 @@ 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
|
||||
@@ -516,10 +670,17 @@ AC_CONFIG_FILES([
|
||||
doc/tutorial-hpack.rst
|
||||
doc/nghttpx-howto.rst
|
||||
doc/h2load-howto.rst
|
||||
doc/libnghttp2_asio.rst
|
||||
doc/python-apiref.rst
|
||||
doc/building-android-binary.rst
|
||||
doc/nghttp2.h.rst
|
||||
doc/nghttp2ver.h.rst
|
||||
doc/asio_http2.h.rst
|
||||
doc/asio_http2_server.h.rst
|
||||
doc/asio_http2_client.h.rst
|
||||
doc/contribute.rst
|
||||
contrib/Makefile
|
||||
script/Makefile
|
||||
])
|
||||
AC_OUTPUT
|
||||
|
||||
@@ -530,6 +691,7 @@ AC_MSG_NOTICE([summary of build options:
|
||||
Install prefix: ${prefix}
|
||||
C compiler: ${CC}
|
||||
CFLAGS: ${CFLAGS}
|
||||
WARNCFLAGS: ${WARNCFLAGS}
|
||||
LDFLAGS: ${LDFLAGS}
|
||||
LIBS: ${LIBS}
|
||||
CPPFLAGS: ${CPPFLAGS}
|
||||
@@ -538,23 +700,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 tlsticketupdate.go
|
||||
|
||||
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
|
||||
113
contrib/tlsticketupdate.go
Normal file
113
contrib/tlsticketupdate.go
Normal file
@@ -0,0 +1,113 @@
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/bradfitz/gomemcache/memcache"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
func makeKey(len int) []byte {
|
||||
b := make([]byte, len)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
log.Fatalf("rand.Read: %v", err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func main() {
|
||||
var host = flag.String("host", "127.0.0.1", "memcached host")
|
||||
var port = flag.Int("port", 11211, "memcached port")
|
||||
var cipher = flag.String("cipher", "aes-128-cbc", "cipher for TLS ticket encryption")
|
||||
var interval = flag.Int("interval", 3600, "interval to update TLS ticket keys")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
var keylen int
|
||||
switch *cipher {
|
||||
case "aes-128-cbc":
|
||||
keylen = 48
|
||||
case "aes-256-cbc":
|
||||
keylen = 80
|
||||
default:
|
||||
log.Fatalf("cipher: unknown cipher %v", cipher)
|
||||
}
|
||||
|
||||
mc := memcache.New(fmt.Sprintf("%v:%v", *host, *port))
|
||||
|
||||
keys := [][]byte{
|
||||
makeKey(keylen), // current encryption key
|
||||
makeKey(keylen), // next encryption key; now decryption only
|
||||
}
|
||||
|
||||
for {
|
||||
buf := new(bytes.Buffer)
|
||||
if err := binary.Write(buf, binary.BigEndian, uint32(1)); err != nil {
|
||||
log.Fatalf("failed to write version: %v", err)
|
||||
}
|
||||
|
||||
for _, key := range keys {
|
||||
if err := binary.Write(buf, binary.BigEndian, uint16(keylen)); err != nil {
|
||||
log.Fatalf("failed to write length: %v", err)
|
||||
}
|
||||
if _, err := buf.Write(key); err != nil {
|
||||
log.Fatalf("buf.Write: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
mc.Set(&memcache.Item{
|
||||
Key: "nghttpx:tls-ticket-key",
|
||||
Value: buf.Bytes(),
|
||||
Expiration: int32((*interval) + 300),
|
||||
})
|
||||
|
||||
select {
|
||||
case <-time.After(time.Duration(*interval) * time.Second):
|
||||
}
|
||||
|
||||
// rotate keys. the last key is now encryption key.
|
||||
// generate new key and append it to the last, so that
|
||||
// we can at least decrypt TLS ticket encrypted by new
|
||||
// key on the host which does not get new key yet.
|
||||
// keep at most past 11 keys as decryption only key
|
||||
n := len(keys) + 1
|
||||
if n > 13 {
|
||||
n = 13
|
||||
}
|
||||
newKeys := make([][]byte, n)
|
||||
newKeys[0] = keys[len(keys)-1]
|
||||
copy(newKeys[1:], keys[0:n-2])
|
||||
newKeys[n-1] = makeKey(keylen)
|
||||
|
||||
keys = newKeys
|
||||
}
|
||||
|
||||
}
|
||||
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
|
||||
|
||||
160
doc/Makefile.am
160
doc/Makefile.am
@@ -23,10 +23,120 @@
|
||||
|
||||
man_MANS = nghttp.1 nghttpd.1 nghttpx.1 h2load.1
|
||||
|
||||
APIDOCS= \
|
||||
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_max_reserved_remote_streams.rst \
|
||||
nghttp2_option_set_no_auto_window_update.rst \
|
||||
nghttp2_option_set_no_http_messaging.rst \
|
||||
nghttp2_option_set_no_recv_client_magic.rst \
|
||||
nghttp2_option_set_peer_max_concurrent_streams.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_find_stream.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_root_stream.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_stream_get_first_child.rst \
|
||||
nghttp2_stream_get_next_sibling.rst \
|
||||
nghttp2_stream_get_parent.rst \
|
||||
nghttp2_stream_get_previous_sibling.rst \
|
||||
nghttp2_stream_get_state.rst \
|
||||
nghttp2_stream_get_sum_dependency_weight.rst \
|
||||
nghttp2_stream_get_weight.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 \
|
||||
@@ -37,26 +147,32 @@ EXTRA_DIST = \
|
||||
sources/tutorial-hpack.rst \
|
||||
sources/nghttpx-howto.rst \
|
||||
sources/h2load-howto.rst \
|
||||
sources/libnghttp2_asio.rst \
|
||||
sources/python-apiref.rst \
|
||||
sources/building-android-binary.rst \
|
||||
_themes/sphinx_rtd_theme/footer.html \
|
||||
_themes/sphinx_rtd_theme/theme.conf \
|
||||
_themes/sphinx_rtd_theme/layout_old.html \
|
||||
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
|
||||
#
|
||||
@@ -93,16 +209,20 @@ 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: \
|
||||
$(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 $^ > $@
|
||||
apiref.rst macros.rst enums.rst types.rst . $^
|
||||
|
||||
$(APIDOCS): apiref.rst
|
||||
|
||||
clean-local:
|
||||
-rm apiref.rst
|
||||
-rm -f apiref.rst
|
||||
-rm -f $(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."
|
||||
@@ -174,7 +294,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>
|
||||
|
||||
|
||||
41
doc/_themes/sphinx_rtd_theme/layout.html
vendored
41
doc/_themes/sphinx_rtd_theme/layout.html
vendored
@@ -12,6 +12,7 @@
|
||||
<!--[if gt IE 8]><!--> <html class="no-js" lang="en" > <!--<![endif]-->
|
||||
<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,400italic,700italic|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>
|
||||
|
||||
@@ -83,19 +87,34 @@
|
||||
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
|
||||
<div class="wy-side-nav-search">
|
||||
{% block sidebartitle %}
|
||||
<a href="{{ pathto(master_doc) }}" class="fa fa-home"> {{ project }}</a>
|
||||
{% endblock %}
|
||||
|
||||
{% if logo and theme_logo_only %}
|
||||
<a href="{{ pathto(master_doc) }}">
|
||||
{% else %}
|
||||
<a href="{{ pathto(master_doc) }}" class="icon icon-home"> {{ project }}
|
||||
{% endif %}
|
||||
|
||||
{% if logo %}
|
||||
{# Not strictly valid HTML, but it's the only way to display/scale it properly, without weird scripting or heaps of work #}
|
||||
<img src="{{ pathto('_static/' + logo, 1) }}" class="logo" />
|
||||
{% endif %}
|
||||
</a>
|
||||
|
||||
{% include "searchbox.html" %}
|
||||
|
||||
{% 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>
|
||||
|
||||
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 %}
|
||||
|
||||
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 --num-conns --data --verbose --timing-script-file --ciphers --connection-active-timeout --rate --connection-inactivity-timeout --base-uri --window-bits --clients --no-tls-proto --version --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 --max-concurrent-streams --continuation --connection-window-bits --peer-max-concurrent-streams --timeout --data --no-content-length --version --color --cert --upgrade --remote-name --trailer --weight --help --key --null-out --window-bits --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 --include --backend-request-buffer --backend-http2-connection-window-bits --conf --worker-write-burst --npn-list --fetch-ocsp-response-file --stream-read-timeout --tls-ticket-key-memcached --accesslog-syslog --frontend-http2-read-timeout --listener-disable-timeout --frontend-http2-connection-window-bits --ciphers --strip-incoming-x-forwarded-for --private-key-passwd-file --backend-keep-alive-timeout --backend-http-proxy-uri --backend-http1-connections-per-host --rlimit-nofile --no-via --ocsp-update-interval --backend-write-timeout --client --tls-ticket-key-memcached-max-retry --http2-no-cookie-crumbling --worker-read-burst --client-proxy --http2-bridge --accesslog-format --errorlog-syslog --errorlog-file --http2-max-concurrent-streams --frontend-write-timeout --tls-ticket-key-cipher --read-burst --backend-ipv4 --backend-ipv6 --backend --insecure --log-level --host-rewrite --tls-proto-list --backend-http2-connections-per-worker --tls-ticket-key-memcached-interval --dh-param-file --worker-frontend-connections --header-field-buffer --no-server-push --no-location-rewrite --tls-session-cache-memcached --no-ocsp --backend-response-buffer --workers --frontend-http2-window-bits --worker-write-rate --add-request-header --backend-tls-sni-field --subcert --help --frontend-frame-debug --pid-file --frontend-http2-dump-request-header --daemon --write-rate --altsvc --user --add-x-forwarded-for --syslog-facility --frontend-read-timeout --tls-ticket-key-memcached-max-fail --backlog --write-burst --backend-http2-window-bits --padding --stream-write-timeout --cacert --version --verify-client --backend-read-timeout --frontend --accesslog-file --http2-proxy --max-header-fields --backend-no-tls --client-private-key-file --client-cert-file --add-response-header --read-rate ' -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
_filedir
|
||||
return 0
|
||||
esac
|
||||
return 0
|
||||
}
|
||||
complete -F _nghttpx nghttpx
|
||||
@@ -64,7 +64,7 @@ master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
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
|
||||
382
doc/h2load.1
382
doc/h2load.1
@@ -1,61 +1,359 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.1.
|
||||
.TH H2LOAD "1" "September 2014" "h2load nghttp2/0.6.1" "User Commands"
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "H2LOAD" "1" "August 30, 2015" "1.3.0" "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. Definition of a base URI overrides all
|
||||
scheme, host or port values.
|
||||
.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=<PATH>
|
||||
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 <PATH>, 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. Definition of a base URI overrides all
|
||||
scheme, host or port values.
|
||||
.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\-14
|
||||
Default: h2c\-14
|
||||
.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 \-\-ciphers=<SUITE>
|
||||
Set allowed cipher list. The format of the string is
|
||||
described in OpenSSL ciphers(1).
|
||||
.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=<PATH>
|
||||
Post FILE to server. The request method is changed to
|
||||
POST.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-r, \-\-rate=<N>
|
||||
Specifies the fixed rate at which connections are
|
||||
created. The rate must be a positive integer,
|
||||
representing the number of connections to be made per
|
||||
second. When the rate is 0, the program will run as it
|
||||
normally does, creating connections at whatever variable
|
||||
rate it wants. The default value for this option is 0.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-C, \-\-num\-conns=<N>
|
||||
Specifies the total number of connections to create.
|
||||
The total number of connections must be a positive
|
||||
integer. On each connection, \fI\%\-m\fP requests are made. The
|
||||
test stops once as soon as the <N> connections have
|
||||
either completed or failed. When the number of
|
||||
connections is 0, the program will run as it normally
|
||||
does, creating as many connections as it needs in order
|
||||
to make the \fI\%\-n\fP requests specified. The default value
|
||||
for this option is 0. The \fI\%\-n\fP option is not required if
|
||||
the \fI\%\-C\fP option is being used.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-T, \-\-connection\-active\-timeout=<N>
|
||||
Specifies the maximum time that h2load is willing to
|
||||
keep a connection open, regardless of the activity on
|
||||
said connection. <N> must be a positive integer,
|
||||
specifying the number of seconds to wait. When no
|
||||
timeout value is set (either active or inactive), h2load
|
||||
will keep a connection open indefinitely, waiting for a
|
||||
response.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-N, \-\-connection\-inactivity\-timeout=<N>
|
||||
Specifies the amount of time that h2load is willing to
|
||||
wait to see activity on a given connection. <N> must be
|
||||
a positive integer, specifying the number of seconds to
|
||||
wait. When no timeout value is set (either active or
|
||||
inactive), h2load will keep a connection open
|
||||
indefinitely, waiting for a response.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-timing\-script\-file=<PATH>
|
||||
Path of a file containing one or more lines separated by
|
||||
EOLs. Each script line is composed of two tab\-separated
|
||||
fields. The first field represents the time offset from
|
||||
the start of execution, expressed as milliseconds with
|
||||
microsecond resolution. The second field represents the
|
||||
URI. This option will disable URIs getting from
|
||||
command\-line. If \(aq\-\(aq is given as <PATH>, script lines
|
||||
will be read from stdin. Script lines are used in order
|
||||
for each client. If \fI\%\-n\fP is given, it must be less than
|
||||
or equal to the number of script lines, larger values are
|
||||
clamped to the number of script lines. If \fI\%\-n\fP is
|
||||
not given, the number of requests will default to the
|
||||
number of script lines. The scheme, host and port defined
|
||||
in the first URI are used solely. Values contained in
|
||||
other URIs, if present, are ignored. Definition of a
|
||||
base URI overrides all scheme, host or port values.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-B, \-\-base\-uri=<URI>
|
||||
Specify URI from which the scheme, host and port will be
|
||||
used for all requests. The base URI overrides all
|
||||
values defined either at the command line or inside
|
||||
input files.
|
||||
.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.
|
||||
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.
|
||||
.TP
|
||||
.B timeout
|
||||
The number of requests whose connection timed out before they were
|
||||
completed. This is the subset of the number reported in
|
||||
\fBerrored\fP\&.
|
||||
.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.
|
||||
.
|
||||
|
||||
269
doc/h2load.1.rst
269
doc/h2load.1.rst
@@ -1,93 +1,282 @@
|
||||
|
||||
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
|
||||
|
||||
.. program:: h2load
|
||||
|
||||
h2load(1)
|
||||
=========
|
||||
|
||||
NAME
|
||||
----
|
||||
h2load - HTTP/2 benchmarking tool
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
**h2load** [OPTIONS]... <URI>...
|
||||
|
||||
**h2load** [OPTIONS]... [URI]...
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
|
||||
benchmarking tool for HTTP/2 and SPDY server
|
||||
|
||||
.. option:: URI
|
||||
.. 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.
|
||||
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. Definition of a base URI overrides all
|
||||
scheme, host or port values.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
|
||||
.. option:: -n, --requests=<N>
|
||||
|
||||
Number of requests. Default: 1
|
||||
Number of requests.
|
||||
|
||||
Default: ``1``
|
||||
|
||||
.. option:: -c, --clients=<N>
|
||||
|
||||
|
||||
Number of concurrent clients. Default: 1
|
||||
Number of concurrent clients.
|
||||
|
||||
Default: ``1``
|
||||
|
||||
.. option:: -t, --threads=<N>
|
||||
|
||||
|
||||
Number of native threads. Default: 1
|
||||
Number of native threads.
|
||||
|
||||
Default: ``1``
|
||||
|
||||
.. option:: -i, --input-file=<PATH>
|
||||
|
||||
Path of a file with multiple URIs are separated by EOLs.
|
||||
This option will disable URIs getting from command-line.
|
||||
If '-' is given as <PATH>, 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. Definition of a base URI overrides all
|
||||
scheme, host or port values.
|
||||
|
||||
.. 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
|
||||
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.
|
||||
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.
|
||||
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:: --ciphers=<SUITE>
|
||||
|
||||
Set allowed cipher list. The format of the string is
|
||||
described in OpenSSL ciphers(1).
|
||||
|
||||
.. option:: -p, --no-tls-proto=<PROTOID>
|
||||
|
||||
|
||||
Specify ALPN identifier of the protocol to be
|
||||
used when accessing http URI without SSL/TLS.
|
||||
Available protocols: spdy/2, spdy/3, spdy/3.1 and
|
||||
h2c-14
|
||||
Default: h2c-14
|
||||
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=<PATH>
|
||||
|
||||
Post FILE to server. The request method is changed to
|
||||
POST.
|
||||
|
||||
.. option:: -r, --rate=<N>
|
||||
|
||||
Specifies the fixed rate at which connections are
|
||||
created. The rate must be a positive integer,
|
||||
representing the number of connections to be made per
|
||||
second. When the rate is 0, the program will run as it
|
||||
normally does, creating connections at whatever variable
|
||||
rate it wants. The default value for this option is 0.
|
||||
|
||||
.. option:: -C, --num-conns=<N>
|
||||
|
||||
Specifies the total number of connections to create.
|
||||
The total number of connections must be a positive
|
||||
integer. On each connection, :option:`-m` requests are made. The
|
||||
test stops once as soon as the <N> connections have
|
||||
either completed or failed. When the number of
|
||||
connections is 0, the program will run as it normally
|
||||
does, creating as many connections as it needs in order
|
||||
to make the :option:`-n` requests specified. The default value
|
||||
for this option is 0. The :option:`-n` option is not required if
|
||||
the :option:`-C` option is being used.
|
||||
|
||||
.. option:: -T, --connection-active-timeout=<N>
|
||||
|
||||
Specifies the maximum time that h2load is willing to
|
||||
keep a connection open, regardless of the activity on
|
||||
said connection. <N> must be a positive integer,
|
||||
specifying the number of seconds to wait. When no
|
||||
timeout value is set (either active or inactive), h2load
|
||||
will keep a connection open indefinitely, waiting for a
|
||||
response.
|
||||
|
||||
.. option:: -N, --connection-inactivity-timeout=<N>
|
||||
|
||||
Specifies the amount of time that h2load is willing to
|
||||
wait to see activity on a given connection. <N> must be
|
||||
a positive integer, specifying the number of seconds to
|
||||
wait. When no timeout value is set (either active or
|
||||
inactive), h2load will keep a connection open
|
||||
indefinitely, waiting for a response.
|
||||
|
||||
.. option:: --timing-script-file=<PATH>
|
||||
|
||||
Path of a file containing one or more lines separated by
|
||||
EOLs. Each script line is composed of two tab-separated
|
||||
fields. The first field represents the time offset from
|
||||
the start of execution, expressed as milliseconds with
|
||||
microsecond resolution. The second field represents the
|
||||
URI. This option will disable URIs getting from
|
||||
command-line. If '-' is given as <PATH>, script lines
|
||||
will be read from stdin. Script lines are used in order
|
||||
for each client. If :option:`-n` is given, it must be less than
|
||||
or equal to the number of script lines, larger values are
|
||||
clamped to the number of script lines. If :option:`-n` is
|
||||
not given, the number of requests will default to the
|
||||
number of script lines. The scheme, host and port defined
|
||||
in the first URI are used solely. Values contained in
|
||||
other URIs, if present, are ignored. Definition of a
|
||||
base URI overrides all scheme, host or port values.
|
||||
|
||||
.. option:: -B, --base-uri=<URI>
|
||||
|
||||
Specify URI from which the scheme, host and port will be
|
||||
used for all requests. The base URI overrides all
|
||||
values defined either at the command line or inside
|
||||
input files.
|
||||
|
||||
.. 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.
|
||||
This is the subset of the number reported in ``failed`` and most
|
||||
likely the network level failures or stream was reset by
|
||||
RST_STREAM.
|
||||
timeout
|
||||
The number of requests whose connection timed out before they were
|
||||
completed. This is the subset of the number reported in
|
||||
``errored``.
|
||||
|
||||
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
|
||||
--------
|
||||
|
||||
nghttp(1), nghttpd(1), nghttpx(1)
|
||||
:manpage:`nghttp(1)`, :manpage:`nghttpd(1)`, :manpage:`nghttpx(1)`
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
[SEE ALSO]
|
||||
|
||||
nghttp(1), nghttpd(1), nghttpx(1)
|
||||
97
doc/h2load.h2r
Normal file
97
doc/h2load.h2r
Normal file
@@ -0,0 +1,97 @@
|
||||
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.
|
||||
This is the subset of the number reported in ``failed`` and most
|
||||
likely the network level failures or stream was reset by
|
||||
RST_STREAM.
|
||||
timeout
|
||||
The number of requests whose connection timed out before they were
|
||||
completed. This is the subset of the number reported in
|
||||
``errored``.
|
||||
|
||||
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)
|
||||
|
||||
327
doc/nghttp.1
327
doc/nghttp.1
@@ -1,108 +1,295 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.1.
|
||||
.TH NGHTTP "1" "September 2014" "nghttp nghttp2/0.6.1" "User Commands"
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTP" "1" "August 30, 2015" "1.3.0" "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\-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 \-d, \-\-data=<PATH>
|
||||
Post FILE to server. If \(aq\-\(aq is given, data will be read
|
||||
from stdin.
|
||||
.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 \-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\-p\fR, \fB\-\-weight=\fR<WEIGHT>
|
||||
Sets priority group weight. The valid value
|
||||
range is [1, 256], inclusive.
|
||||
Default: 16
|
||||
.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\-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 \-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\-c\fR, \fB\-\-header\-table\-size=\fR<N>
|
||||
.B \-M, \-\-peer\-max\-concurrent\-streams=<N>
|
||||
Use <N> as SETTINGS_MAX_CONCURRENT_STREAMS value of
|
||||
remote endpoint as if it is received in SETTINGS frame.
|
||||
The default is large enough as it is seen as unlimited.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-c, \-\-header\-table\-size=<SIZE>
|
||||
Specify decoder header table size.
|
||||
.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=<PATH>
|
||||
Output HTTP transactions <PATH> 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\-\-no\-content\-length\fR
|
||||
Don't send content\-length header field.
|
||||
.B \-\-no\-content\-length
|
||||
Don\(aqt send content\-length header field.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-\-version\fR
|
||||
.B \-\-no\-dep
|
||||
Don\(aqt send dependency based priority hint to server.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-hexdump
|
||||
Display the incoming traffic in hexadecimal (Canonical
|
||||
hex+ASCII display). If SSL/TLS is used, decrypted data
|
||||
are used.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-no\-push
|
||||
Disable server push.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-max\-concurrent\-streams=<N>
|
||||
The number of concurrent pushed streams this client
|
||||
accepts.
|
||||
.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.
|
||||
.
|
||||
|
||||
212
doc/nghttp.1.rst
212
doc/nghttp.1.rst
@@ -1,21 +1,22 @@
|
||||
|
||||
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
|
||||
|
||||
.. program:: nghttp
|
||||
|
||||
nghttp(1)
|
||||
=========
|
||||
|
||||
NAME
|
||||
----
|
||||
nghttp - HTTP/2 experimental client
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
|
||||
**nghttp** [OPTIONS]... <URI>...
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
|
||||
HTTP/2 experimental client
|
||||
|
||||
.. option:: URI
|
||||
.. describe:: <URI>
|
||||
|
||||
Specify URI to access.
|
||||
|
||||
@@ -24,143 +25,214 @@ OPTIONS
|
||||
|
||||
.. option:: -v, --verbose
|
||||
|
||||
|
||||
Print debug information such as reception and
|
||||
transmission of frames and name/value pairs.
|
||||
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.
|
||||
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=<N>
|
||||
.. option:: -t, --timeout=<DURATION>
|
||||
|
||||
|
||||
Timeout each request after <N> seconds.
|
||||
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.
|
||||
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
|
||||
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.
|
||||
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
|
||||
.. option:: -H, --header=<HEADER>
|
||||
|
||||
|
||||
Add a header to the requests.
|
||||
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.
|
||||
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.
|
||||
Use the client private key file. The file must be in
|
||||
PEM format.
|
||||
|
||||
.. option:: -d, --data=<FILE>
|
||||
.. option:: -d, --data=<PATH>
|
||||
|
||||
|
||||
Post FILE to server. If '-' is given, data will
|
||||
be read from stdin.
|
||||
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.
|
||||
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.
|
||||
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
|
||||
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.
|
||||
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=<N>
|
||||
.. 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.
|
||||
Add at most <N> bytes to a frame payload as padding.
|
||||
Specify 0 to disable padding.
|
||||
|
||||
.. option:: -r, --har=<PATH>
|
||||
|
||||
Output HTTP transactions <PATH> 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:: --max-concurrent-streams=<N>
|
||||
|
||||
The number of concurrent pushed streams this client
|
||||
accepts.
|
||||
|
||||
.. 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
|
||||
--------
|
||||
|
||||
nghttpd(1), nghttpx(1), h2load(1)
|
||||
:manpage:`nghttpd(1)`, :manpage:`nghttpx(1)`, :manpage:`h2load(1)`
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
[SEE ALSO]
|
||||
|
||||
nghttpd(1), nghttpx(1), h2load(1)
|
||||
55
doc/nghttp.h2r
Normal file
55
doc/nghttp.h2r
Normal file
@@ -0,0 +1,55 @@
|
||||
DEPENDENCY BASED PRIORITY
|
||||
-------------------------
|
||||
|
||||
nghttp sends priority hints to server by default unless
|
||||
:option:`--no-dep` is used. nghttp mimics the way Firefox employs to
|
||||
manages dependency using idle streams. We follows the behaviour of
|
||||
Firefox Nightly as of April, 2015, and nghttp's behaviour is very
|
||||
static and could be different from Firefox in detail. But reproducing
|
||||
the same behaviour of Firefox is not our goal. The goal is provide
|
||||
the easy way to test out the dependency priority in server
|
||||
implementation.
|
||||
|
||||
When connection is established, nghttp sends 5 PRIORITY frames to idle
|
||||
streams 3, 5, 7, 9 and 11 to create "anchor" nodes in dependency
|
||||
tree::
|
||||
|
||||
+-----+
|
||||
|id=0 |
|
||||
+-----+
|
||||
^ ^ ^
|
||||
w=201 / | \ w=1
|
||||
/ | \
|
||||
/ w=101| \
|
||||
+-----+ +-----+ +-----+
|
||||
|id=3 | |id=5 | |id=7 |
|
||||
+-----+ +-----+ +-----+
|
||||
^ ^
|
||||
w=1 | w=1 |
|
||||
| |
|
||||
+-----+ +-----+
|
||||
|id=11| |id=9 |
|
||||
+-----+ +-----+
|
||||
|
||||
In the above figure, ``id`` means stream ID, and ``w`` means weight.
|
||||
The stream 0 is non-existence stream, and forms the root of the tree.
|
||||
The stream 7 and 9 are not used for now.
|
||||
|
||||
The URIs given in the command-line depend on stream 11 with the weight
|
||||
given in :option:`-p` option, which defaults to 16.
|
||||
|
||||
If :option:`-a` option is used, nghttp parses the resource pointed by
|
||||
URI given in command-line as html, and extracts resource links from
|
||||
it. When requesting those resources, nghttp uses dependency according
|
||||
to its resource type.
|
||||
|
||||
For CSS, and Javascript files inside "head" element, they depend on
|
||||
stream 3 with the weight 2. The Javascript files outside "head"
|
||||
element depend on stream 5 with the weight 2. The mages depend on
|
||||
stream 11 with the weight 12. The other resources (e.g., icon) depend
|
||||
on stream 11 with the weight 2.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
|
||||
:manpage:`nghttpd(1)`, :manpage:`nghttpx(1)`, :manpage:`h2load(1)`
|
||||
223
doc/nghttpd.1
223
doc/nghttpd.1
@@ -1,93 +1,194 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.1.
|
||||
.TH NGHTTPD "1" "September 2014" "nghttpd nghttp2/0.6.1" "User Commands"
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTPD" "1" "August 30, 2015" "1.3.0" "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\-\-dh\-param\-file=\fR<PATH>
|
||||
Path to file that contains DH parameters in PEM
|
||||
format. Without this option, DHE cipher suites
|
||||
are not available.
|
||||
.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
|
||||
\fB\-\-early\-response\fR
|
||||
Start sending response when request HEADERS is
|
||||
received, rather than complete request is
|
||||
received.
|
||||
.B \-\-early\-response
|
||||
Start sending response when request HEADERS is received,
|
||||
rather than complete request is received.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
\fB\-\-version\fR
|
||||
.B \-\-trailer=<HEADER>
|
||||
Add a trailer header to a response. <HEADER> must not
|
||||
include pseudo header field (header field name starting
|
||||
with \(aq:\(aq). The trailer is sent only if a response has
|
||||
body part. Example: \fI\%\-\-trailer\fP \(aqfoo: bar\(aq.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-hexdump
|
||||
Display the incoming traffic in hexadecimal (Canonical
|
||||
hex+ASCII display). If SSL/TLS is used, decrypted data
|
||||
are used.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-echo\-upload
|
||||
Send back uploaded content if method is POST or PUT.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-version
|
||||
Display version information and exit.
|
||||
.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.
|
||||
.
|
||||
|
||||
@@ -1,136 +1,151 @@
|
||||
|
||||
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
|
||||
|
||||
.. program:: nghttpd
|
||||
|
||||
nghttpd(1)
|
||||
==========
|
||||
|
||||
NAME
|
||||
----
|
||||
nghttpd - HTTP/2 experimental server
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
**nghttpd** [OPTION]... <PORT> <PRIVATE_KEY> <CERT>
|
||||
|
||||
**nghttpd** --no-tls [OPTION]... <PORT>
|
||||
**nghttpd** [OPTION]... <PORT> [<PRIVATE_KEY> <CERT>]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
|
||||
HTTP/2 experimental server
|
||||
|
||||
.. option:: PORT
|
||||
.. describe:: <PORT>
|
||||
|
||||
Specify listening port number.
|
||||
|
||||
.. option:: PRIVATE_KEY
|
||||
.. describe:: <PRIVATE_KEY>
|
||||
|
||||
Set path to server's private key. Required
|
||||
unless :option:`--no-tls` is specified.
|
||||
|
||||
Set path to server's private key. Required unless
|
||||
:option:`--no-tls` is specified.
|
||||
|
||||
.. option:: CERT
|
||||
.. describe:: <CERT>
|
||||
|
||||
Set path to server's certificate. Required
|
||||
unless :option:`--no-tls` is specified.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
Print debug information such as reception/ transmission
|
||||
of frames and name/value pairs.
|
||||
|
||||
.. option:: --no-tls
|
||||
|
||||
|
||||
Disable SSL/TLS.
|
||||
|
||||
.. option:: -c, --header-table-size=<N>
|
||||
.. 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: -p/=/foo.png
|
||||
-p/doc=/bar.css
|
||||
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.
|
||||
Add at most <N> bytes to a frame payload as padding.
|
||||
Specify 0 to disable padding.
|
||||
|
||||
.. option:: -n, --workers=<CORE>
|
||||
.. option:: -m, --max-concurrent-streams=<N>
|
||||
|
||||
Set the maximum number of the concurrent streams in one
|
||||
HTTP/2 session.
|
||||
|
||||
Default: ``100``
|
||||
|
||||
.. option:: -n, --workers=<N>
|
||||
|
||||
|
||||
Set the number of worker threads.
|
||||
Default: 1
|
||||
|
||||
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.
|
||||
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.
|
||||
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
|
||||
--------
|
||||
|
||||
nghttp(1), nghttpx(1), h2load(1)
|
||||
:manpage:`nghttp(1)`, :manpage:`nghttpx(1)`, :manpage:`h2load(1)`
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
[SEE ALSO]
|
||||
|
||||
nghttp(1), nghttpx(1), h2load(1)
|
||||
4
doc/nghttpd.h2r
Normal file
4
doc/nghttpd.h2r
Normal file
@@ -0,0 +1,4 @@
|
||||
SEE ALSO
|
||||
--------
|
||||
|
||||
:manpage:`nghttp(1)`, :manpage:`nghttpx(1)`, :manpage:`h2load(1)`
|
||||
1344
doc/nghttpx.1
1344
doc/nghttpx.1
File diff suppressed because it is too large
Load Diff
1099
doc/nghttpx.1.rst
1099
doc/nghttpx.1.rst
File diff suppressed because it is too large
Load Diff
@@ -1,3 +0,0 @@
|
||||
[SEE ALSO]
|
||||
|
||||
nghttp(1), nghttpd(1), h2load(1)
|
||||
164
doc/nghttpx.h2r
Normal file
164
doc/nghttpx.h2r
Normal file
@@ -0,0 +1,164 @@
|
||||
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.
|
||||
|
||||
If OCSP query is failed, previous OCSP response, if any, is continued
|
||||
to be used.
|
||||
|
||||
TLS SESSION RESUMPTION
|
||||
----------------------
|
||||
|
||||
nghttpx supports TLS session resumption through both session ID and
|
||||
session ticket.
|
||||
|
||||
SESSION ID RESUMPTION
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
By default, session ID is shared by all worker threads.
|
||||
|
||||
If :option:`--tls-session-cache-memcached` is given, nghttpx will
|
||||
insert serialized session data to memcached with
|
||||
``nghttpx:tls-session-cache:`` + lowercased hex string of session ID
|
||||
as a memcached entry key, with expiry time 12 hours. Session timeout
|
||||
is set to 12 hours.
|
||||
|
||||
TLS SESSION TICKET RESUMPTION
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
By default, session ticket is shared by all worker threads. The
|
||||
automatic key rotation is also enabled by default. Every an hour, new
|
||||
encryption key is generated, and previous encryption key becomes
|
||||
decryption only key. We set session timeout to 12 hours, and thus we
|
||||
keep at most 12 keys.
|
||||
|
||||
If :option:`--tls-ticket-key-memcached` is given, encryption keys are
|
||||
retrieved from memcached. nghttpx just reads keys from memcached; one
|
||||
has to deploy key generator program to update keys frequently (e.g.,
|
||||
every 1 hour). The example key generator tlsticketupdate.go is
|
||||
available under contrib directory in nghttp2 archive. The memcached
|
||||
entry key is ``nghttpx:tls-ticket-key``. The data format stored in
|
||||
memcached is the binary format described below::
|
||||
|
||||
+--------------+-------+----------------+
|
||||
| VERSION (4) |LEN (2)|KEY(48 or 80) ...
|
||||
+--------------+-------+----------------+
|
||||
^ |
|
||||
| |
|
||||
+------------------------+
|
||||
(LEN, KEY) pair can be repeated
|
||||
|
||||
All numbers in the above figure is bytes. All integer fields are
|
||||
network byte order.
|
||||
|
||||
First 4 bytes integer VERSION field, which must be 1. The 2 bytes
|
||||
integer LEN field gives the length of following KEY field, which
|
||||
contains key. If :option:`--tls-ticket-key-cipher`\=aes-128-cbc is
|
||||
used, LEN must be 48. If
|
||||
:option:`--tls-ticket-key-cipher`\=aes-256-cbc is used, LEN must be
|
||||
80. LEN and KEY pair can be repeated multiple times to store multiple
|
||||
keys. The key appeared first is used as encryption key. All the
|
||||
remaining keys are used as decryption only.
|
||||
|
||||
If :option:`--tls-ticket-key-file` is given, encryption key is read
|
||||
from the given file. In this case, nghttpx does not rotate key
|
||||
automatically. To rotate key, one has to restart nghttpx (see
|
||||
SIGNALS).
|
||||
|
||||
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.
|
||||
@@ -5,6 +5,11 @@ In this article, we briefly describe how to build Android binary using
|
||||
`Android NDK <http://developer.android.com/tools/sdk/ndk/index.html>`_
|
||||
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
|
||||
@@ -16,8 +21,9 @@ unpacked::
|
||||
|
||||
$ build/tools/make-standalone-toolchain.sh \
|
||||
--install-dir=$ANDROID_HOME/toolchain \
|
||||
--toolchain=arm-linux-androideabi-4.8 \
|
||||
--llvm-version=3.4
|
||||
--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.
|
||||
@@ -25,13 +31,14 @@ are using x86_64 system.
|
||||
The platform level is not important here because we don't use Android
|
||||
specific C/C++ API.
|
||||
|
||||
The dependent libraries, such as OpenSSL and libevent should be built
|
||||
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.
|
||||
|
||||
We use zlib which comes with Android NDK, so we don't have to build it
|
||||
by ourselves.
|
||||
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.
|
||||
@@ -42,7 +49,9 @@ correct path. Also add ``$ANDROID_HOME/toolchain/bin`` to ``PATH``::
|
||||
|
||||
$ export PATH=$PATH:$ANDROID_HOME/toolchain/bin
|
||||
|
||||
To configure OpenSSL, use the following script::
|
||||
To configure OpenSSL, use the following script:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
#!/bin/sh
|
||||
|
||||
@@ -59,7 +68,12 @@ To configure OpenSSL, use the following script::
|
||||
|
||||
And run ``make install`` to build and install.
|
||||
|
||||
To configure libevent, use the following script::
|
||||
We cannot compile libev without modification. Apply `this patch
|
||||
<https://gist.github.com/tatsuhiro-t/48c45f08950f587180ed>`_ before
|
||||
configuring libev. This patch is for libev-4.19. After applying the
|
||||
patch, to configure libev, use the following script:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
#!/bin/sh
|
||||
|
||||
@@ -82,7 +96,40 @@ To configure libevent, use the following script::
|
||||
|
||||
And run ``make install`` to build and install.
|
||||
|
||||
To configure spdylay, use the following script::
|
||||
To configure zlib, use the following script:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
#!/bin/sh -e
|
||||
|
||||
if [ -z "$ANDROID_HOME" ]; then
|
||||
echo 'No $ANDROID_HOME specified.'
|
||||
exit 1
|
||||
fi
|
||||
PREFIX=$ANDROID_HOME/usr/local
|
||||
TOOLCHAIN=$ANDROID_HOME/toolchain
|
||||
PATH=$TOOLCHAIN/bin:$PATH
|
||||
|
||||
HOST=arm-linux-androideabi
|
||||
|
||||
CC=$HOST-gcc \
|
||||
AR=$HOST-ar \
|
||||
LD=$HOST-ld \
|
||||
RANLIB=$HOST-ranlib \
|
||||
STRIP=$HOST-strip \
|
||||
./configure \
|
||||
--prefix=$PREFIX \
|
||||
--libdir=$PREFIX/lib \
|
||||
--includedir=$PREFIX/include \
|
||||
--static
|
||||
|
||||
And run ``make install`` to build and install.
|
||||
|
||||
To configure spdylay, use the following script:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
#!/bin/sh -e
|
||||
|
||||
if [ -z "$ANDROID_HOME" ]; then
|
||||
echo 'No $ANDROID_HOME specified.'
|
||||
@@ -104,11 +151,7 @@ To configure spdylay, use the following script::
|
||||
PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \
|
||||
LDFLAGS="-L$PREFIX/lib"
|
||||
|
||||
And run ``make install`` to build and install. After spdylay
|
||||
installation, edit $ANDROID_HOME/usr/local/lib/pkgconfig/libspdylay.pc
|
||||
and remove the following line::
|
||||
|
||||
Requires.private: zlib
|
||||
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.
|
||||
|
||||
57
doc/sources/contribute.rst
Normal file
57
doc/sources/contribute.rst
Normal file
@@ -0,0 +1,57 @@
|
||||
Contribution Guidelines
|
||||
=======================
|
||||
|
||||
[This text was composed based on 1.2. License section of curl/libcurl
|
||||
project.]
|
||||
|
||||
When contributing with code, you agree to put your changes and new
|
||||
code under the same license nghttp2 is already using unless stated and
|
||||
agreed otherwise.
|
||||
|
||||
When changing existing source code, you do not alter the copyright of
|
||||
the original file(s). The copyright will still be owned by the
|
||||
original creator(s) or those who have been assigned copyright by the
|
||||
original author(s).
|
||||
|
||||
By submitting a patch to the nghttp2 project, you are assumed to have
|
||||
the right to the code and to be allowed by your employer or whatever
|
||||
to hand over that patch/code to us. We will credit you for your
|
||||
changes as far as possible, to give credit but also to keep a trace
|
||||
back to who made what changes. Please always provide us with your
|
||||
full real name when contributing!
|
||||
|
||||
Coding style
|
||||
------------
|
||||
|
||||
We use clang-format to format source code consistently. The
|
||||
clang-format configuration file .clang-format is located at the root
|
||||
directory. Since clang-format produces slightly different results
|
||||
between versions, we currently use clang-format which comes with
|
||||
clang-3.5.
|
||||
|
||||
To detect any violation to the coding style, we recommend to setup git
|
||||
pre-commit hook to check coding style of the changes you introduced.
|
||||
The pre-commit file is located at the root directory. Copy it under
|
||||
.git/hooks and make sure that it is executable. The pre-commit script
|
||||
uses clang-format-diff.py to detect any style errors. If it is not in
|
||||
your PATH or it exists under different name (e.g.,
|
||||
clang-format-diff-3.5 in debian), either add it to PATH variable or
|
||||
add git option ``clangformatdiff.binary`` to point to the script.
|
||||
|
||||
For emacs users, integrating clang-format to emacs is very easy.
|
||||
clang-format.el should come with clang distribution. If it is not
|
||||
found, download it from `here
|
||||
<https://llvm.org/svn/llvm-project/cfe/trunk/tools/clang-format/clang-format.el>`_.
|
||||
And add these lines to your .emacs file:
|
||||
|
||||
.. code-block:: lisp
|
||||
|
||||
;; From
|
||||
;; https://code.google.com/p/chromium/wiki/Emacs#Use_Google's_C++_style!
|
||||
(load "/<path/to>/clang-format.el")
|
||||
(add-hook 'c-mode-common-hook
|
||||
(function (lambda () (local-set-key (kbd "TAB")
|
||||
'clang-format-region))))
|
||||
|
||||
You can find other editor integration in
|
||||
http://clang.llvm.org/docs/ClangFormat.html.
|
||||
@@ -50,8 +50,9 @@ Flow Control
|
||||
------------
|
||||
|
||||
HTTP/2 and SPDY/3 or later employ flow control and it may affect
|
||||
benchmarking results. To adjust receiver flow control window size,
|
||||
there is following options:
|
||||
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
|
||||
@@ -86,5 +87,5 @@ If multiple URIs are specified, they are used in round robin manner.
|
||||
|
||||
.. note::
|
||||
|
||||
Please note that h2load uses sheme, host and port in the first URI
|
||||
Please note that h2load uses scheme, host and port in the first URI
|
||||
and ignores those parts in the rest of the URIs.
|
||||
|
||||
@@ -17,6 +17,7 @@ Contents:
|
||||
:maxdepth: 2
|
||||
|
||||
package_README
|
||||
contribute
|
||||
building-android-binary
|
||||
tutorial-client
|
||||
tutorial-server
|
||||
@@ -27,26 +28,26 @@ Contents:
|
||||
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-14
|
||||
* http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-09
|
||||
* http://tools.ietf.org/html/draft-ietf-httpbis-alt-svc-02
|
||||
* 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.
|
||||
@@ -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 be 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
|
||||
@@ -52,15 +52,15 @@ like forward proxy and assumes the backend is HTTP/1 proxy server
|
||||
(e.g., squid, traffic server). So HTTP/1 request must include
|
||||
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.
|
||||
@@ -87,7 +87,7 @@ proxy, user has to create proxy.pac script file like this:
|
||||
|
||||
``SERVERADDR`` and ``PORT`` is the hostname/address and port of the
|
||||
machine nghttpx is running. Please note that both Firefox nightly and
|
||||
Chromium requires valid certificate for secure proxy.
|
||||
Chromium require valid certificate for secure proxy.
|
||||
|
||||
For Firefox nightly, open Preference window and select Advanced then
|
||||
click Network tab. Clicking Connection Settings button will show the
|
||||
@@ -100,9 +100,9 @@ For Chromium, use following command-line::
|
||||
|
||||
$ google-chrome --proxy-pac-url=file:///path/to/proxy.pac --use-npn
|
||||
|
||||
Squid may work as out-of-box. Traffic server requires to be
|
||||
configured as forward proxy. Here is the minimum configuration items
|
||||
to edit::
|
||||
As HTTP/1 proxy server, Squid may work as out-of-box. Traffic server
|
||||
requires to be configured as forward proxy. Here is the minimum
|
||||
configuration items to edit::
|
||||
|
||||
CONFIG proxy.config.reverse_proxy.enabled INT 0
|
||||
CONFIG proxy.config.url_remap.remap_required INT 0
|
||||
@@ -124,7 +124,9 @@ HTTP/1 frontend connection can be upgraded to HTTP/2 using HTTP
|
||||
Upgrade. To disable SSL/TLS in backend connection, use
|
||||
``--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
|
||||
@@ -156,7 +158,9 @@ HTTP/1 frontend connection can be upgraded to HTTP/2 using HTTP
|
||||
Upgrade. To disable SSL/TLS in backend connection, use
|
||||
``--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
|
||||
@@ -196,10 +200,14 @@ With ``--frontend-no-tls`` option, SSL/TLS is turned off in frontend
|
||||
connection, so the connection gets insecure. To disable SSL/TLS in
|
||||
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. If backend server is HTTP/2 proxy, use
|
||||
``--no-location-rewrite`` option to disable rewriting location header
|
||||
field.
|
||||
``--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
|
||||
@@ -235,12 +243,12 @@ Read/write rate limit
|
||||
---------------------
|
||||
|
||||
nghttpx supports transfer rate limiting on frontend connections. You
|
||||
can do rate limit per worker (thread) for reading and writeing
|
||||
can do rate limit per frontend connection for reading and writing
|
||||
individually.
|
||||
|
||||
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``.
|
||||
To perform rate limit for reading, use ``--read-rate`` and
|
||||
``--read-burst`` options. For writing, use ``--write-rate`` and
|
||||
``--write-burst``.
|
||||
|
||||
Please note that rate limit is performed on top of TCP and nothing to
|
||||
do with HTTP/2 flow control.
|
||||
@@ -263,10 +271,10 @@ precedence. If the above conditions are not met with the host value
|
||||
in :authority header field, rewrite is retried with the value in host
|
||||
header field.
|
||||
|
||||
Hot deploy
|
||||
----------
|
||||
Hot swapping
|
||||
------------
|
||||
|
||||
nghttpx supports hot deploy feature using signals. The hot deploy in
|
||||
nghttpx supports hot swapping using signals. The hot swapping in
|
||||
nghttpx is multi step process. First send USR2 signal to nghttpx
|
||||
process. It will do fork and execute new executable, using same
|
||||
command-line arguments and environment variables. At this point, both
|
||||
@@ -284,3 +292,12 @@ log rotation daemon renamed existing log files. To tell nghttpx to
|
||||
re-open log files, send USR1 signal to nghttpx process. It will
|
||||
re-open 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()
|
||||
|
||||
@@ -1,58 +1,58 @@
|
||||
Tutorial: HTTP/2 client
|
||||
=========================
|
||||
|
||||
In this tutorial, we are going to write very primitive HTTP/2
|
||||
In this tutorial, we are going to write a very primitive HTTP/2
|
||||
client. The complete source code, `libevent-client.c`_, is attached at
|
||||
the end of this page. It also resides in examples directory in the
|
||||
archive or repository.
|
||||
the end of this page. It also resides in the examples directory in
|
||||
the archive or repository.
|
||||
|
||||
This simple client takes 1 argument, HTTPS URI, and retrieves the
|
||||
resource denoted by the URI. Its synopsis is like this::
|
||||
This simple client takes a single HTTPS URI and retrieves the resource
|
||||
at the URI. The synopsis is::
|
||||
|
||||
$ libevent-client HTTPS_URI
|
||||
|
||||
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 client to select the next application
|
||||
protocol over the SSL/TLS transport. In this tutorial, we use
|
||||
`nghttp2_select_next_protocol()` function to select the HTTP/2
|
||||
The client starts with some libevent and OpenSSL setup in the
|
||||
``main()`` and ``run()`` functions. This setup isn't specific to
|
||||
nghttp2, but one thing you should look at is setup of the NPN
|
||||
callback. The NPN callback is used by the client to select the next
|
||||
application protocol over TLS. In this tutorial, we use the
|
||||
`nghttp2_select_next_protocol()` helper 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;
|
||||
}
|
||||
|
||||
The callback is set to the SSL_CTX object using
|
||||
``SSL_CTX_set_next_proto_select_cb()`` function::
|
||||
The callback is added to the SSL_CTX object using
|
||||
``SSL_CTX_set_next_proto_select_cb()``::
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
We use ``http2_session_data`` structure to store the data related to
|
||||
the HTTP/2 session::
|
||||
The example client defines a couple of structs:
|
||||
|
||||
We define and use a ``http2_session_data`` structure to store data
|
||||
related to the HTTP/2 session::
|
||||
|
||||
typedef struct {
|
||||
nghttp2_session *session;
|
||||
@@ -61,17 +61,17 @@ the HTTP/2 session::
|
||||
http2_stream_data *stream_data;
|
||||
} http2_session_data;
|
||||
|
||||
Since this program only handles 1 URI, it uses only 1 stream. We store
|
||||
its stream specific data in ``http2_stream_data`` structure and the
|
||||
``stream_data`` points to it. The ``struct http2_stream_data`` is
|
||||
defined as follows::
|
||||
Since this program only handles one URI, it uses only one stream. We
|
||||
store the single stream's data in a ``http2_stream_data`` structure
|
||||
and the ``stream_data`` points to it. The ``http2_stream_data``
|
||||
structure is 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 */
|
||||
@@ -84,48 +84,44 @@ defined as follows::
|
||||
int32_t stream_id;
|
||||
} http2_stream_data;
|
||||
|
||||
We creates and initializes these structures in
|
||||
We create and initialize these structures in
|
||||
``create_http2_session_data()`` and ``create_http2_stream_data()``
|
||||
respectively.
|
||||
|
||||
Then we call function ``initiate_connection()`` to start connecting to
|
||||
the remote server::
|
||||
``initiate_connection()`` is called to start the connection to the
|
||||
remote server. It's defined as::
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
We set 3 callbacks for the bufferevent: ``reacb``, ``writecb`` and
|
||||
``eventcb``.
|
||||
``initiate_connection()`` creates a bufferevent for the connection and
|
||||
sets up three callbacks: ``readcb``, ``writecb``, and ``eventcb``.
|
||||
|
||||
The ``eventcb()`` is invoked by libevent event loop when an event
|
||||
(e.g., connection has been established, timeout, etc) happens on the
|
||||
The ``eventcb()`` is invoked by the libevent event loop when an event
|
||||
(e.g. connection has been established, timeout, etc.) occurs 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,188 +129,184 @@ 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);
|
||||
}
|
||||
|
||||
For ``BEV_EVENT_EOF``, ``BEV_EVENT_ERROR`` and ``BEV_EVENT_TIMEOUT``
|
||||
event, we just simply tear down the connection. The
|
||||
``BEV_EVENT_CONNECTED`` event is invoked when SSL/TLS handshake is
|
||||
finished successfully. We first initialize nghttp2 session object in
|
||||
``initialize_nghttp2_session()`` function::
|
||||
For ``BEV_EVENT_EOF``, ``BEV_EVENT_ERROR``, and ``BEV_EVENT_TIMEOUT``
|
||||
events, we just simply tear down the connection.
|
||||
|
||||
static void initialize_nghttp2_session(http2_session_data *session_data)
|
||||
{
|
||||
The ``BEV_EVENT_CONNECTED`` event is invoked when the SSL/TLS
|
||||
handshake has completed successfully. After this we're ready to begin
|
||||
communicating via HTTP/2.
|
||||
|
||||
The ``initialize_nghttp2_session()`` function initializes the nghttp2
|
||||
session object and several callbacks::
|
||||
|
||||
static void initialize_nghttp2_session(http2_session_data *session_data) {
|
||||
nghttp2_session_callbacks *callbacks;
|
||||
|
||||
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_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_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_stream_close_callback(
|
||||
callbacks, on_stream_close_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_header_callback
|
||||
(callbacks, on_header_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_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
|
||||
initialize nghttp2 session object. We setup 7 callbacks for the
|
||||
nghttp2 session. We'll explain these callbacks later.
|
||||
Since we are creating a client, we use `nghttp2_session_client_new()`
|
||||
to initialize the nghttp2 session object. The callbacks setup are
|
||||
explained later.
|
||||
|
||||
The `delete_http2_session_data()` destroys ``session_data`` and frees
|
||||
its bufferevent, so it closes underlying connection as well. It also
|
||||
calls `nghttp2_session_del()` to delete nghttp2 session object.
|
||||
The `delete_http2_session_data()` function destroys ``session_data``
|
||||
and frees its bufferevent, so the underlying connection is closed. It
|
||||
also calls `nghttp2_session_del()` to delete the 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
|
||||
A HTTP/2 connection begins by sending the client connection preface,
|
||||
which is a 24 byte magic byte string (:macro:`NGHTTP2_CLIENT_MAGIC`),
|
||||
followed by a SETTINGS frame. The 24 byte magic string is sent
|
||||
automatically by nghttp2. We send the 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
|
||||
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
|
||||
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.
|
||||
Here we specify SETTINGS_MAX_CONCURRENT_STREAMS as 100. This is not
|
||||
needed for this tiny example program, it just demonstrates use of the
|
||||
SETTINGS frame. To queue the SETTINGS frame for transmission, we call
|
||||
`nghttp2_submit_settings()`. Note that `nghttp2_submit_settings()`
|
||||
only queues the frame for transmission, and doesn't actually send it.
|
||||
All ``nghttp2_submit_*()`` family functions have this property. To
|
||||
actually send the frame, `nghttp2_session_send()` has to be called,
|
||||
which is described (and called) later.
|
||||
|
||||
After the transmission of client connection header, we enqueue HTTP
|
||||
request in ``submit_request()`` function::
|
||||
After the transmission of the client connection header, we enqueue the
|
||||
HTTP request in the ``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));
|
||||
}
|
||||
|
||||
stream_data->stream_id = stream_id;
|
||||
}
|
||||
|
||||
We build HTTP request header fields in ``hdrs`` which is an array of
|
||||
:type:`nghttp2_nv`. There are 4 header fields to be sent: ``:method``,
|
||||
``:scheme``, ``:authority`` and ``:path``. To queue this HTTP request,
|
||||
we use `nghttp2_submit_request()` function. The `stream_data` is
|
||||
passed in *stream_user_data* parameter. It is used in nghttp2
|
||||
callbacks which we'll describe about later.
|
||||
We build the HTTP request header fields in ``hdrs``, which is an array
|
||||
of :type:`nghttp2_nv`. There are four header fields to be sent:
|
||||
``:method``, ``:scheme``, ``:authority``, and ``:path``. To queue the
|
||||
HTTP request, we call `nghttp2_submit_request()`. The ``stream_data``
|
||||
is passed via the *stream_user_data* parameter, which is helpfully
|
||||
later passed back to callback functions.
|
||||
|
||||
`nghttp2_submit_request()` returns the newly assigned stream ID for
|
||||
this request.
|
||||
the request.
|
||||
|
||||
The next bufferevent callback is ``readcb()``, which is invoked when
|
||||
data is available to read in the bufferevent input buffer::
|
||||
data is available to read from the bufferevent input buffer::
|
||||
|
||||
static void readcb(struct bufferevent *bev, void *ptr)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
||||
static void readcb(struct bufferevent *bev, void *ptr) {
|
||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||
ssize_t readlen;
|
||||
struct evbuffer *input = bufferevent_get_input(bev);
|
||||
size_t datalen = evbuffer_get_length(input);
|
||||
unsigned char *data = evbuffer_pullup(input, -1);
|
||||
|
||||
readlen = nghttp2_session_mem_recv(session_data->session, data, datalen);
|
||||
if(readlen < 0) {
|
||||
if (readlen < 0) {
|
||||
warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
if(evbuffer_drain(input, readlen) != 0) {
|
||||
if (evbuffer_drain(input, readlen) != 0) {
|
||||
warnx("Fatal error: evbuffer_drain failed");
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
if(session_send(session_data) != 0) {
|
||||
if (session_send(session_data) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
In this function, we feed all unprocessed, received data to nghttp2
|
||||
session object using `nghttp2_session_mem_recv()` function. The
|
||||
In this function we feed all unprocessed, received data to the nghttp2
|
||||
session object using the `nghttp2_session_mem_recv()` function.
|
||||
`nghttp2_session_mem_recv()` processes the received data and may
|
||||
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::
|
||||
invoke nghttp2 callbacks and queue frames for transmission. Since
|
||||
there may be pending frames for transmission, we call immediately
|
||||
``session_send()`` to send them. ``session_send()`` 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;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
The `nghttp2_session_send()` function serializes the frame into wire
|
||||
format and call ``send_callback()`` function of type
|
||||
:type:`nghttp2_send_callback`. The ``send_callback()`` is defined as
|
||||
follows::
|
||||
The `nghttp2_session_send()` function serializes pending frames into
|
||||
wire format and calls the ``send_callback()`` function to send them.
|
||||
``send_callback()`` has type :type:`nghttp2_send_callback` and is
|
||||
defined as::
|
||||
|
||||
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;
|
||||
@@ -323,62 +315,59 @@ 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 ``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.
|
||||
data to the non-blocking socket directly using the ``write()`` system
|
||||
call, we'd soon receive an ``EAGAIN`` or ``EWOULDBLOCK`` error, since
|
||||
sockets have a limited send buffer. If that happens, it's possible to
|
||||
return :macro:`NGHTTP2_ERR_WOULDBLOCK` to signal the nghttp2 library
|
||||
to stop sending further data. When writing to a bufferevent, you
|
||||
should regulate the amount of data written, to avoid possible huge
|
||||
memory consumption. In this example client however we don't implement
|
||||
a limit. 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::
|
||||
all data written 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(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);
|
||||
}
|
||||
}
|
||||
|
||||
As described earlier, we just write off all data in `send_callback()`,
|
||||
we have no data to write in this function. All we have to do is check
|
||||
we have to 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.
|
||||
so there is no data to write in this function. All we have to do is
|
||||
check if the connection should be dropped or not. The nghttp2 session
|
||||
object keeps track of reception and transmission of GOAWAY frames and
|
||||
other error conditions. Using this information, the nghttp2 session
|
||||
object can state whether the connection should be dropped or not.
|
||||
More specifically, when both `nghttp2_session_want_read()` and
|
||||
`nghttp2_session_want_write()` return 0, the connection is no-longer
|
||||
required and can be closed. Since we're using bufferevent and its
|
||||
deferred callback option, the bufferevent output buffer may still
|
||||
contain pending data when the ``writecb()`` is called. To handle this
|
||||
situation, we also check whether the output buffer is empty or not. If
|
||||
all of these conditions are met, then we drop the connection.
|
||||
|
||||
We have already described about nghttp2 callback ``send_callback()``.
|
||||
Let's describe remaining nghttp2 callbacks we setup in
|
||||
Now let's look at the remaining nghttp2 callbacks setup in the
|
||||
``initialize_nghttp2_setup()`` function.
|
||||
|
||||
Each request header name/value pair is emitted via
|
||||
``on_header_callback`` function::
|
||||
A server responds to the request by first sending a HEADERS frame.
|
||||
The HEADERS frame consists of response header name/value pairs, and
|
||||
the ``on_header_callback()`` is called for each 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;
|
||||
@@ -387,19 +376,19 @@ 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 pairs on stdout.
|
||||
|
||||
After all name/value pairs are emitted for a frame,
|
||||
``on_frame_recv_callback`` function is called::
|
||||
After the HEADERS frame has been fully received (and thus all response
|
||||
header name/value pairs have been received), the
|
||||
``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;
|
||||
@@ -407,47 +396,47 @@ After all name/value pairs are emitted for a frame,
|
||||
return 0;
|
||||
}
|
||||
|
||||
In this tutorial, we are just interested in the HTTP response
|
||||
HEADERS. We check te frame type and its category (it should be
|
||||
:macro:`NGHTTP2_HCAT_RESPONSE` for HTTP response HEADERS). Also check
|
||||
its stream ID.
|
||||
``on_frame_recv_callback()`` is called for other frame types too.
|
||||
|
||||
The ``on_data_chunk_recv_callback()`` function is invoked when a chunk
|
||||
of data is received from the remote peer::
|
||||
In this tutorial, we are just interested in the HTTP response HEADERS
|
||||
frame. We check the frame type and its category (it should be
|
||||
:macro:`NGHTTP2_HCAT_RESPONSE` for HTTP response HEADERS). We also
|
||||
check its stream ID.
|
||||
|
||||
static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
||||
int32_t stream_id,
|
||||
Next, zero or more DATA frames can be received. 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 _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
|
||||
output in the terminal may be corrupted if the response body contains
|
||||
some binary data.
|
||||
In our case, a chunk of data is HTTP response body. After checking the
|
||||
stream ID, we just write the received data to stdout. Note 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;
|
||||
}
|
||||
}
|
||||
@@ -455,9 +444,9 @@ is about to close::
|
||||
}
|
||||
|
||||
If the stream ID matches the one we initiated, it means that its
|
||||
stream is going to be closed. Since we have finished to get the
|
||||
resource we want (or the stream was reset by RST_STREAM from the
|
||||
stream is going to be closed. Since we have finished receiving
|
||||
resource we wanted (or the stream was reset by RST_STREAM from the
|
||||
remote peer), we call `nghttp2_session_terminate_session()` to
|
||||
commencing the closure of the HTTP/2 session gracefully. If you have
|
||||
commence closure of the HTTP/2 session gracefully. If you have
|
||||
some data associated for the stream to be closed, you may delete it
|
||||
here.
|
||||
|
||||
@@ -1,130 +1,129 @@
|
||||
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`_.
|
||||
In this tutorial, we describe basic use of nghttp2's HPACK API. We
|
||||
briefly describe the APIs for deflating and inflating header fields.
|
||||
The full example of using these APIs, `deflate.c`_, is attached at the
|
||||
end of this page. It also resides in the examples directory in the
|
||||
archive or repository.
|
||||
|
||||
Deflating (encoding) headers
|
||||
----------------------------
|
||||
|
||||
First we need to initialize :type:`nghttp2_hd_deflater` object using
|
||||
`nghttp2_hd_deflate_new()` function::
|
||||
First we need to initialize a :type:`nghttp2_hd_deflater` object using
|
||||
the `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
|
||||
This function allocates a :type:`nghttp2_hd_deflater` object,
|
||||
initializes it, and assigns its pointer to ``*deflater_ptr``. The
|
||||
*deflate_hd_table_bufsize_max* is the upper bound of header table size
|
||||
the deflater will use. This will limit the memory usage by the
|
||||
deflater object for the dynamic header table. If in 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::
|
||||
To encode header fields, use the `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.
|
||||
`nghttp2_hd_deflate_new()` described above. The encoded byte string is
|
||||
written to the buffer *buf*, which has length *buflen*. The *nva* is
|
||||
a pointer to an array of headers fields, each of type
|
||||
:type:`nghttp2_nv`. *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`.
|
||||
:type:`nghttp2_nv`. For security sensitive header fields (such as
|
||||
cookies), set the :macro:`NGHTTP2_NV_FLAG_NO_INDEX` flag in
|
||||
:member:`nghttp2_nv.flags`. Setting this flag prevents recovery of
|
||||
sensitive header fields by compression based attacks: This is achieved
|
||||
by not inserting the header field into the dynamic header table.
|
||||
|
||||
`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::
|
||||
result, otherwise the function will fail. To estimate the upper bound
|
||||
of the encoded result length, use `nghttp2_hd_deflate_bound()`::
|
||||
|
||||
size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater,
|
||||
const nghttp2_nv *nva, size_t nvlen);
|
||||
|
||||
Pass this function with the same paramters *deflater*, *nva* and
|
||||
*nvlen* which will be passed to `nghttp2_hd_deflate_hd()`.
|
||||
Pass this function 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.
|
||||
Subsequent calls to `nghttp2_hd_deflate_hd()` will use the current
|
||||
encoder state and perform differential encoding, which yields HPAC's
|
||||
fundamental compression gain.
|
||||
|
||||
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.
|
||||
If `nghttp2_hd_deflate_hd()` fails, the failure is fatal and any
|
||||
further calls with the same deflater object will fail. Thus it's very
|
||||
important to use `nghttp2_hd_deflate_bound()` to determine the
|
||||
required size of the output buffer.
|
||||
|
||||
To delete :type:`nghttp2_hd_deflater` object, use `nghttp2_hd_deflate_del()`
|
||||
function.
|
||||
|
||||
.. note::
|
||||
|
||||
Generally, the order of header fields passed to
|
||||
`nghttp2_hd_deflate_hd()` function is not preserved. It is known
|
||||
that the relative ordering of header fields which do not share the
|
||||
same name is insignificant. But some header fields sharing the
|
||||
same name require the explicit ordering. To preserve this
|
||||
ordering, those header values are concatenated into single header
|
||||
field value using NULL (0x00) as delimiter. This is transparent to
|
||||
HPACK API. Therefore, the application should examine the inflated
|
||||
header values and split into multiple header field values by NULL.
|
||||
To delete a :type:`nghttp2_hd_deflater` object, use the
|
||||
`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()`::
|
||||
A :type:`nghttp2_hd_inflater` object is used 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::
|
||||
To inflate header data, use `nghttp2_hd_inflate_hd()`::
|
||||
|
||||
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);
|
||||
|
||||
`nghttp2_hd_inflate_hd()` reads a stream of bytes and outputs a single
|
||||
header field at a time. Multiple calls are normally required to read a
|
||||
full stream of bytes and output all of the header fields.
|
||||
|
||||
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.
|
||||
is a pointer to a :type:`nghttp2_nv` into which one header field may
|
||||
be stored. The *in* is a pointer to input data, and *inlen* is its
|
||||
length. The caller is not required to specify the whole deflated
|
||||
header data via *in* at once: Instead it can call this function
|
||||
multiple times as additional data bytes become available. 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 an output parameter; on success the function
|
||||
sets it to a bitset of flags. 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.
|
||||
happens, the function sets the :macro:`NGHTTP2_HD_INFLATE_EMIT` flag
|
||||
in *inflate_flags*, and a header field is stored in *nv_out*. The
|
||||
return value indicates the number of bytes read from *in* processed so
|
||||
far, which may be less than *inlen*. The caller should call the
|
||||
function repeatedly until all bytes are processed. Processed bytes
|
||||
should be removed from *in*, and *inlen* should be adjusted
|
||||
appropriately.
|
||||
|
||||
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
|
||||
function sets the :macro:`NGHTTP2_HD_INFLATE_FINAL` flag in
|
||||
*inflate_flags*. When you see this flag set, call the
|
||||
`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.
|
||||
If *in_final* is zero and the :macro:`NGHTTP2_HD_INFLATE_EMIT` flag is
|
||||
not set, it indicates that all given data was processed. The caller
|
||||
is required to pass additional 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
|
||||
Example usage of `nghttp2_hd_inflate_hd()` is shown in the
|
||||
`inflate_header_block()` function in `deflate.c`_.
|
||||
|
||||
To delete :type:`nghttp2_hd_inflater` object, use `nghttp2_hd_inflate_del()`
|
||||
function.
|
||||
Finally, to delete a :type:`nghttp2_hd_inflater` object, use
|
||||
`nghttp2_hd_inflate_del()`.
|
||||
|
||||
@@ -1,45 +1,46 @@
|
||||
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 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.
|
||||
In this tutorial, we are going to write a single-threaded, event-based
|
||||
HTTP/2 web server, which supports HTTPS only. It can handle 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. The source also resides in the examples directory in the
|
||||
archive or repository.
|
||||
|
||||
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::
|
||||
This simple server takes 3 arguments: The port number to listen on,
|
||||
the path to your SSL/TLS private key file, and the path to your
|
||||
certificate file. The synopsis is::
|
||||
|
||||
$ 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 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::
|
||||
The server starts with some libevent and OpenSSL setup in the
|
||||
``main()`` and ``run()`` functions. This setup isn't specific to
|
||||
nghttp2, but one thing you should look at is setup of the NPN
|
||||
callback. The NPN callback is used by the server to advertise which
|
||||
application protocols the server supports to a client. In this
|
||||
example program, when creating the ``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 lifetime::
|
||||
|
||||
static unsigned char next_proto_list[256];
|
||||
static size_t next_proto_list_len;
|
||||
|
||||
static int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len,
|
||||
void *arg)
|
||||
{
|
||||
static int next_proto_cb(SSL *s _U_, const unsigned char **data,
|
||||
unsigned int *len, void *arg _U_) {
|
||||
*data = next_proto_list;
|
||||
*len = (unsigned int)next_proto_list_len;
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
|
||||
static SSL_CTX* create_ssl_ctx(const char *key_file, const char *cert_file)
|
||||
{
|
||||
static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) {
|
||||
SSL_CTX *ssl_ctx;
|
||||
EC_KEY *ecdh;
|
||||
|
||||
ssl_ctx = SSL_CTX_new(SSLv23_server_method());
|
||||
|
||||
...
|
||||
@@ -53,17 +54,20 @@ time::
|
||||
return ssl_ctx;
|
||||
}
|
||||
|
||||
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()``.
|
||||
The wire format of NPN is a sequence of length prefixed strings, with
|
||||
exactly one byte 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, which is
|
||||
exported in the identifier :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 the ``app_content`` structure to store application-wide data::
|
||||
Next, let's take a look at the main structures used by the example
|
||||
application:
|
||||
|
||||
We use the ``app_context`` structure to store application-wide data::
|
||||
|
||||
struct app_context {
|
||||
SSL_CTX *ssl_ctx;
|
||||
@@ -79,7 +83,6 @@ We use the ``http2_session_data`` structure to store session-level
|
||||
app_context *app_ctx;
|
||||
nghttp2_session *session;
|
||||
char *client_addr;
|
||||
size_t handshake_leftlen;
|
||||
} http2_session_data;
|
||||
|
||||
We use the ``http2_stream_data`` structure to store stream-level data::
|
||||
@@ -91,24 +94,24 @@ We use the ``http2_stream_data`` structure to store stream-level data::
|
||||
int fd;
|
||||
} http2_stream_data;
|
||||
|
||||
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``. The ``handshake_leftlen`` member of ``http2_session_data`` is
|
||||
used to track the number of bytes remaining when receiving the first client
|
||||
connection preface (:macro:`NGHTTP2_CLIENT_CONNECTION_PREFACE`), which is a 24
|
||||
bytes long magic string from the client. 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``.
|
||||
A single HTTP/2 session can have multiple streams. To manage them, we
|
||||
use 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 first create a listener object to accept incoming connections. We use
|
||||
libevent's ``struct evconnlistener`` for this purpose::
|
||||
libevent's bufferevent structure is used to perform network I/O, with
|
||||
the pointer to the bufferevent stored in the ``http2_session_data``
|
||||
structure. 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 the bufferevent in
|
||||
``http2_session_data``.
|
||||
|
||||
We first create a listener object to accept incoming connections.
|
||||
libevent's ``struct evconnlistener`` is used 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;
|
||||
@@ -119,167 +122,136 @@ 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 the ``acceptcb`` callback which is called when a new connection is
|
||||
We specify the ``acceptcb`` callback, which is called when a new connection is
|
||||
accepted::
|
||||
|
||||
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 the ``http2_session_data`` object. The bufferevent for this
|
||||
connection is also initialized at this time. We specify two callbacks for the
|
||||
bufferevent: ``handshake_readcb`` and ``eventcb``.
|
||||
Here we create the ``http2_session_data`` object. The connection's
|
||||
bufferevent is initialized at the same time. We specify three
|
||||
callbacks for the bufferevent: ``readcb``, ``writecb``, and
|
||||
``eventcb``.
|
||||
|
||||
The ``eventcb()`` callback is invoked by the libevent event loop when an event
|
||||
(e.g., connection has been established, timeout, etc) happens on the
|
||||
(e.g. connection has been established, timeout, etc.) occurs 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 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.
|
||||
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 its associated bufferevent member.
|
||||
As a result, the underlying connection is closed.
|
||||
|
||||
``handshake_readcb()`` is a callback function to handle a 24 bytes magic byte
|
||||
string coming from a client, since the nghttp2 library does not handle it::
|
||||
The
|
||||
``BEV_EVENT_CONNECTED`` event is invoked when SSL/TLS handshake has
|
||||
completed successfully. After this we are ready to begin communicating
|
||||
via HTTP/2.
|
||||
|
||||
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;
|
||||
The ``initialize_nghttp2_session()`` function initializes the nghttp2
|
||||
session object and several callbacks::
|
||||
|
||||
if(memcmp(conhead + NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN
|
||||
- session_data->handshake_leftlen, data, readlen) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
session_data->handshake_leftlen -= readlen;
|
||||
if(session_data->handshake_leftlen == 0) {
|
||||
bufferevent_setcb(session_data->bev, readcb, writecb, eventcb, ptr);
|
||||
/* Process pending data in buffer since they are not notified
|
||||
further */
|
||||
initialize_nghttp2_session(session_data);
|
||||
if(send_server_connection_header(session_data) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
if(session_recv(session_data) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
We check that the received byte string matches
|
||||
:macro:`NGHTTP2_CLIENT_CONNECTION_PREFACE`. When they match, the connection
|
||||
state is ready to start the HTTP/2 communication. First we change the callback
|
||||
functions for the bufferevent object. We use the same ``eventcb`` callback as
|
||||
before, but we specify new ``readcb`` and ``writecb`` functions to handle the
|
||||
HTTP/2 communication. These two functions are described later.
|
||||
|
||||
We initialize a nghttp2 session object which is done in
|
||||
``initialize_nghttp2_session()``::
|
||||
|
||||
static void initialize_nghttp2_session(http2_session_data *session_data)
|
||||
{
|
||||
static void initialize_nghttp2_session(http2_session_data *session_data) {
|
||||
nghttp2_session_callbacks *callbacks;
|
||||
|
||||
nghttp2_session_callbacks_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_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_stream_close_callback(
|
||||
callbacks, on_stream_close_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_header_callback
|
||||
(callbacks, on_header_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_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 a server, the nghttp2 session object is created using
|
||||
`nghttp2_session_server_new()` function. We registers five callbacks for
|
||||
nghttp2 session object. We'll talk about these callbacks later.
|
||||
Since we are creating a server, we use `nghttp2_session_server_new()`
|
||||
to initialize the nghttp2 session object. We also setup 5 callbacks
|
||||
for the nghttp2 session, these are explained later.
|
||||
|
||||
After initialization of the nghttp2 session object, we are going to send
|
||||
a server connection header in ``send_server_connection_header()``::
|
||||
The server now begins by sending the server connection preface, which
|
||||
always consists of a SETTINGS frame.
|
||||
``send_server_connection_header()`` configures and submits it::
|
||||
|
||||
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 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 it does not actually send it. All
|
||||
In the example SETTINGS frame we've set
|
||||
SETTINGS_MAX_CONCURRENT_STREAMS to 100. `nghttp2_submit_settings()`
|
||||
is used to queue the frame for transmission, but note it only queues
|
||||
the frame for transmission, and doesn't 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.
|
||||
@@ -289,40 +261,40 @@ have to process them here since libevent won't invoke callback functions for
|
||||
this pending data. To process the received data, we call the
|
||||
``session_recv()`` function::
|
||||
|
||||
static int session_recv(http2_session_data *session_data)
|
||||
{
|
||||
static int session_recv(http2_session_data *session_data) {
|
||||
ssize_t readlen;
|
||||
struct evbuffer *input = bufferevent_get_input(session_data->bev);
|
||||
size_t datalen = evbuffer_get_length(input);
|
||||
unsigned char *data = evbuffer_pullup(input, -1);
|
||||
|
||||
readlen = nghttp2_session_mem_recv(session_data->session, data, datalen);
|
||||
if(readlen < 0) {
|
||||
if (readlen < 0) {
|
||||
warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
|
||||
return -1;
|
||||
}
|
||||
if(evbuffer_drain(input, readlen) != 0) {
|
||||
if (evbuffer_drain(input, readlen) != 0) {
|
||||
warnx("Fatal error: evbuffer_drain failed");
|
||||
return -1;
|
||||
}
|
||||
if(session_send(session_data) != 0) {
|
||||
if (session_send(session_data) != 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
In this function, we feed all unprocessed 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::
|
||||
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 both invoke the previously setup callbacks and also queue
|
||||
outgoing frames. To send any pending outgoing frames, we immediately
|
||||
call ``session_send()``.
|
||||
|
||||
static int session_send(http2_session_data *session_data)
|
||||
{
|
||||
The ``session_send()`` function is defined as follows::
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -330,19 +302,17 @@ frames. The ``session_send()`` function is defined as follows::
|
||||
}
|
||||
|
||||
The `nghttp2_session_send()` function serializes the frame into wire
|
||||
format and calls ``send_callback()`` of type
|
||||
format and calls the ``send_callback()``, which is 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);
|
||||
@@ -352,25 +322,23 @@ 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 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.
|
||||
data to a non-blocking socket directly using the ``write()`` system
|
||||
call in the ``send_callback()``, we'd soon receive an ``EAGAIN`` or
|
||||
``EWOULDBLOCK`` error since sockets have a limited send buffer. If
|
||||
that happens, it's possible to return :macro:`NGHTTP2_ERR_WOULDBLOCK`
|
||||
to signal the nghttp2 library to stop sending further data. But here,
|
||||
when writing to the bufferevent, we have to regulate the amount data
|
||||
to 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`.
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -382,33 +350,34 @@ data.
|
||||
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 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.
|
||||
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 can state whether the
|
||||
connection should be dropped or not. More specifically, if both
|
||||
`nghttp2_session_want_read()` and `nghttp2_session_want_write()`
|
||||
return 0, the connection is no-longer required and can be closed.
|
||||
Since we are using bufferevent and its deferred callback option, the
|
||||
bufferevent output buffer may still contain pending data when the
|
||||
``writecb()`` is called. To handle this, we check whether the output
|
||||
buffer is empty or not. If all of these conditions are met, we drop
|
||||
connection.
|
||||
|
||||
Otherwise, we call ``session_send()`` to process the pending output
|
||||
data. Remember that in ``send_callback()``, we must not write all data to
|
||||
@@ -416,7 +385,7 @@ bufferevent to avoid excessive buffering. We continue processing pending data
|
||||
when the output buffer becomes empty.
|
||||
|
||||
We have already described the nghttp2 callback ``send_callback()``. Let's
|
||||
learn about the remaining nghttp2 callbacks we setup in
|
||||
learn about the remaining nghttp2 callbacks setup in
|
||||
``initialize_nghttp2_setup()`` function.
|
||||
|
||||
The ``on_begin_headers_callback()`` function is invoked when the reception of
|
||||
@@ -424,13 +393,12 @@ 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);
|
||||
@@ -439,13 +407,15 @@ a header block in HEADERS or PUSH_PROMISE frame is started::
|
||||
return 0;
|
||||
}
|
||||
|
||||
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.
|
||||
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()`. The
|
||||
``http2_stream_data`` object can later be easily retrieved from the
|
||||
stream, without searching through the doubly linked list.
|
||||
|
||||
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
|
||||
@@ -453,26 +423,26 @@ 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;
|
||||
@@ -480,29 +450,28 @@ emitted via ``on_header_callback`` function, which is called after
|
||||
return 0;
|
||||
}
|
||||
|
||||
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.
|
||||
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 the ``: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);
|
||||
@@ -514,61 +483,62 @@ fully received::
|
||||
return 0;
|
||||
}
|
||||
|
||||
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.
|
||||
First we retrieve the ``http2_stream_data`` object associated with the
|
||||
stream in ``on_begin_headers_callback()`` 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 using ``error_reply()``. Otherwise, we open
|
||||
the requested file and send its content. We send the header field
|
||||
``:status`` as a single response header.
|
||||
|
||||
Sending the content of the file is done in ``send_response()`` function::
|
||||
Sending the file content is performed by the ``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 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
|
||||
the file descriptor. We also set the ``file_read_callback()`` callback
|
||||
function to read the contents of the file::
|
||||
nghttp2 uses the :type:`nghttp2_data_provider` structure to send the
|
||||
entity body to the remote peer. The ``source`` member of this
|
||||
structure is a union, which can be either a void pointer or an int
|
||||
(which is intended to be used as file descriptor). In this example
|
||||
server, we use it as a 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 an error happens while reading the file, we return
|
||||
If an error occurs 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 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.
|
||||
library to send RST_STREAM to the stream. When all data has been
|
||||
read, the :macro:`NGHTTP2_DATA_FLAG_EOF` flag is set to signal nghttp2
|
||||
that we have finished reading the file.
|
||||
|
||||
The `nghttp2_submit_response()` function is used to send the response to the
|
||||
remote peer.
|
||||
@@ -576,16 +546,13 @@ 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);
|
||||
@@ -593,5 +560,5 @@ is about to close::
|
||||
return 0;
|
||||
}
|
||||
|
||||
We destroy the ``http2_stream_data`` object in this function since the stream
|
||||
is about to close and we no longer use that object.
|
||||
Lastly, we destroy the ``http2_stream_data`` object in this function,
|
||||
since the stream is about to close and we no longer need the object.
|
||||
|
||||
5
examples/.gitignore
vendored
5
examples/.gitignore
vendored
@@ -2,3 +2,8 @@ client
|
||||
libevent-client
|
||||
libevent-server
|
||||
deflate
|
||||
tiny-nghttpd
|
||||
asio-sv
|
||||
asio-sv2
|
||||
asio-cl
|
||||
asio-cl2
|
||||
|
||||
@@ -23,20 +23,20 @@
|
||||
|
||||
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 deflate
|
||||
|
||||
@@ -48,4 +48,48 @@ 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>
|
||||
@@ -46,19 +60,19 @@
|
||||
#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;
|
||||
@@ -99,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;
|
||||
@@ -111,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);
|
||||
}
|
||||
@@ -121,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);
|
||||
}
|
||||
@@ -131,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);
|
||||
@@ -144,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;
|
||||
int rv;
|
||||
connection = (struct Connection*)user_data;
|
||||
connection = (struct Connection *)user_data;
|
||||
connection->want_io = IO_NONE;
|
||||
ERR_clear_error();
|
||||
rv = SSL_write(connection->ssl, data, (int)length);
|
||||
if(rv < 0) {
|
||||
if (rv <= 0) {
|
||||
int err = SSL_get_error(connection->ssl, rv);
|
||||
if(err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
|
||||
connection->want_io = (err == SSL_ERROR_WANT_READ ?
|
||||
WANT_READ : WANT_WRITE);
|
||||
if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
|
||||
connection->want_io =
|
||||
(err == SSL_ERROR_WANT_READ ? WANT_READ : WANT_WRITE);
|
||||
rv = NGHTTP2_ERR_WOULDBLOCK;
|
||||
} else {
|
||||
rv = NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
@@ -173,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;
|
||||
int rv;
|
||||
connection = (struct Connection*)user_data;
|
||||
connection = (struct Connection *)user_data;
|
||||
connection->want_io = IO_NONE;
|
||||
ERR_clear_error();
|
||||
rv = SSL_read(connection->ssl, buf, (int)length);
|
||||
if(rv < 0) {
|
||||
if (rv < 0) {
|
||||
int err = SSL_get_error(connection->ssl, rv);
|
||||
if(err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
|
||||
connection->want_io = (err == SSL_ERROR_WANT_READ ?
|
||||
WANT_READ : WANT_WRITE);
|
||||
if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
|
||||
connection->want_io =
|
||||
(err == SSL_ERROR_WANT_READ ? WANT_READ : WANT_WRITE);
|
||||
rv = NGHTTP2_ERR_WOULDBLOCK;
|
||||
} else {
|
||||
rv = NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
} else if(rv == 0) {
|
||||
} else if (rv == 0) {
|
||||
rv = NGHTTP2_ERR_EOF;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int on_frame_send_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame, void *user_data)
|
||||
{
|
||||
const nghttp2_frame *frame,
|
||||
void *user_data _U_) {
|
||||
size_t i;
|
||||
switch(frame->hd.type) {
|
||||
switch (frame->hd.type) {
|
||||
case NGHTTP2_HEADERS:
|
||||
if(nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)) {
|
||||
if (nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)) {
|
||||
const nghttp2_nv *nva = frame->headers.nva;
|
||||
printf("[INFO] C ----------------------------> S (HEADERS)\n");
|
||||
for(i = 0; i < frame->headers.nvlen; ++i) {
|
||||
for (i = 0; i < frame->headers.nvlen; ++i) {
|
||||
fwrite(nva[i].name, nva[i].namelen, 1, stdout);
|
||||
printf(": ");
|
||||
fwrite(nva[i].value, nva[i].valuelen, 1, stdout);
|
||||
@@ -226,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);
|
||||
@@ -262,18 +268,16 @@ static int on_frame_recv_callback(nghttp2_session *session,
|
||||
* fetch 1 resource in this program, after reception of the response,
|
||||
* we submit GOAWAY and close the session.
|
||||
*/
|
||||
static int on_stream_close_callback(nghttp2_session *session,
|
||||
int32_t stream_id,
|
||||
nghttp2_error_code error_code,
|
||||
void *user_data)
|
||||
{
|
||||
static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||
uint32_t error_code _U_,
|
||||
void *user_data _U_) {
|
||||
struct Request *req;
|
||||
req = nghttp2_session_get_stream_user_data(session, stream_id);
|
||||
if(req) {
|
||||
if (req) {
|
||||
int rv;
|
||||
rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
|
||||
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
diec("nghttp2_session_terminate_session", rv);
|
||||
}
|
||||
}
|
||||
@@ -286,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");
|
||||
}
|
||||
@@ -308,23 +312,22 @@ static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
||||
* always required. Since we use nghttp2_session_recv(), the
|
||||
* recv_callback is also required.
|
||||
*/
|
||||
static void setup_nghttp2_callbacks(nghttp2_session_callbacks *callbacks)
|
||||
{
|
||||
static void setup_nghttp2_callbacks(nghttp2_session_callbacks *callbacks) {
|
||||
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_recv_callback(callbacks, recv_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_frame_send_callback
|
||||
(callbacks, on_frame_send_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_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_stream_close_callback(
|
||||
callbacks, on_stream_close_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_data_chunk_recv_callback
|
||||
(callbacks, on_data_chunk_recv_callback);
|
||||
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
|
||||
callbacks, on_data_chunk_recv_callback);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -332,16 +335,14 @@ static void setup_nghttp2_callbacks(nghttp2_session_callbacks *callbacks)
|
||||
* HTTP/2 protocol, if server does not offer HTTP/2 the nghttp2
|
||||
* 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;
|
||||
@@ -350,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));
|
||||
}
|
||||
}
|
||||
@@ -377,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;
|
||||
@@ -389,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);
|
||||
@@ -409,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));
|
||||
}
|
||||
}
|
||||
@@ -435,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;
|
||||
}
|
||||
}
|
||||
@@ -453,24 +451,20 @@ static void ctl_poll(struct pollfd *pollfd, struct Connection *connection)
|
||||
* function does not send packets; just append the request to the
|
||||
* 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);
|
||||
}
|
||||
|
||||
@@ -481,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);
|
||||
@@ -503,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);
|
||||
@@ -513,8 +504,7 @@ static void request_free(struct Request *req)
|
||||
/*
|
||||
* Fetches the resource denoted by |uri|.
|
||||
*/
|
||||
static void fetch_uri(const struct URI *uri)
|
||||
{
|
||||
static void fetch_uri(const struct URI *uri) {
|
||||
nghttp2_session_callbacks *callbacks;
|
||||
int fd;
|
||||
SSL_CTX *ssl_ctx;
|
||||
@@ -529,16 +519,16 @@ static void fetch_uri(const struct URI *uri)
|
||||
|
||||
/* 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
|
||||
@@ -548,10 +538,6 @@ 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);
|
||||
@@ -560,21 +546,22 @@ static void fetch_uri(const struct URI *uri)
|
||||
|
||||
rv = nghttp2_session_callbacks_new(&callbacks);
|
||||
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
diec("nghttp2_session_callbacks_new", rv);
|
||||
}
|
||||
|
||||
setup_nghttp2_callbacks(callbacks);
|
||||
|
||||
rv = nghttp2_session_client_new(&connection.session, callbacks,
|
||||
&connection);
|
||||
rv = nghttp2_session_client_new(&connection.session, callbacks, &connection);
|
||||
|
||||
nghttp2_session_callbacks_del(callbacks);
|
||||
|
||||
if(rv != 0) {
|
||||
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);
|
||||
|
||||
@@ -582,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);
|
||||
@@ -607,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");
|
||||
}
|
||||
|
||||
@@ -704,13 +689,13 @@ int main(int argc, char **argv)
|
||||
act.sa_handler = SIG_IGN;
|
||||
sigaction(SIGPIPE, &act, 0);
|
||||
|
||||
OPENSSL_config(NULL);
|
||||
OpenSSL_add_all_algorithms();
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
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);
|
||||
|
||||
@@ -22,48 +22,48 @@
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /* !HAVE_CONFIG_H */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
#define MAKE_NV(K, V) \
|
||||
{ (uint8_t*)K, (uint8_t*)V, sizeof(K) - 1, sizeof(V) - 1, \
|
||||
NGHTTP2_NV_FLAG_NONE }
|
||||
#define MAKE_NV(K, V) \
|
||||
{ \
|
||||
(uint8_t *) K, (uint8_t *)V, sizeof(K) - 1, sizeof(V) - 1, \
|
||||
NGHTTP2_NV_FLAG_NONE \
|
||||
}
|
||||
|
||||
static void deflate(nghttp2_hd_deflater *deflater,
|
||||
nghttp2_hd_inflater *inflater,
|
||||
const nghttp2_nv * const nva, size_t nvlen);
|
||||
nghttp2_hd_inflater *inflater, const nghttp2_nv *const nva,
|
||||
size_t nvlen);
|
||||
|
||||
static int inflate_header_block(nghttp2_hd_inflater *inflater,
|
||||
uint8_t *in, size_t inlen, int final);
|
||||
static int inflate_header_block(nghttp2_hd_inflater *inflater, uint8_t *in,
|
||||
size_t inlen, int final);
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int main(int argc _U_, char **argv _U_) {
|
||||
int rv;
|
||||
nghttp2_hd_deflater *deflater;
|
||||
nghttp2_hd_inflater *inflater;
|
||||
/* Define 1st header set. This is looks like a HTTP request. */
|
||||
nghttp2_nv nva1[] = {
|
||||
MAKE_NV(":scheme", "https"),
|
||||
MAKE_NV(":authority", "example.org"),
|
||||
MAKE_NV(":path", "/"),
|
||||
MAKE_NV("user-agent", "libnghttp2"),
|
||||
MAKE_NV("accept-encoding", "gzip, deflate")
|
||||
};
|
||||
MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "example.org"),
|
||||
MAKE_NV(":path", "/"), MAKE_NV("user-agent", "libnghttp2"),
|
||||
MAKE_NV("accept-encoding", "gzip, deflate")};
|
||||
/* Define 2nd header set */
|
||||
nghttp2_nv nva2[] = {
|
||||
MAKE_NV(":scheme", "https"),
|
||||
MAKE_NV(":authority", "example.org"),
|
||||
MAKE_NV(":path", "/stylesheet/style.css"),
|
||||
MAKE_NV("user-agent", "libnghttp2"),
|
||||
MAKE_NV("accept-encoding", "gzip, deflate"),
|
||||
MAKE_NV("referer", "https://example.org")
|
||||
};
|
||||
nghttp2_nv nva2[] = {MAKE_NV(":scheme", "https"),
|
||||
MAKE_NV(":authority", "example.org"),
|
||||
MAKE_NV(":path", "/stylesheet/style.css"),
|
||||
MAKE_NV("user-agent", "libnghttp2"),
|
||||
MAKE_NV("accept-encoding", "gzip, deflate"),
|
||||
MAKE_NV("referer", "https://example.org")};
|
||||
|
||||
rv = nghttp2_hd_deflate_new(&deflater, 4096);
|
||||
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
fprintf(stderr, "nghttp2_hd_deflate_init failed with error: %s\n",
|
||||
nghttp2_strerror(rv));
|
||||
exit(EXIT_FAILURE);
|
||||
@@ -71,7 +71,7 @@ int main(int argc, char **argv)
|
||||
|
||||
rv = nghttp2_hd_inflate_new(&inflater);
|
||||
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
fprintf(stderr, "nghttp2_hd_inflate_init failed with error: %s\n",
|
||||
nghttp2_strerror(rv));
|
||||
exit(EXIT_FAILURE);
|
||||
@@ -91,9 +91,8 @@ int main(int argc, char **argv)
|
||||
}
|
||||
|
||||
static void deflate(nghttp2_hd_deflater *deflater,
|
||||
nghttp2_hd_inflater *inflater,
|
||||
const nghttp2_nv * const nva, size_t nvlen)
|
||||
{
|
||||
nghttp2_hd_inflater *inflater, const nghttp2_nv *const nva,
|
||||
size_t nvlen) {
|
||||
ssize_t rv;
|
||||
uint8_t *buf;
|
||||
size_t buflen;
|
||||
@@ -103,13 +102,13 @@ static void deflate(nghttp2_hd_deflater *deflater,
|
||||
|
||||
sum = 0;
|
||||
|
||||
for(i = 0; i < nvlen; ++i) {
|
||||
for (i = 0; i < nvlen; ++i) {
|
||||
sum += nva[i].namelen + nva[i].valuelen;
|
||||
}
|
||||
|
||||
printf("Input (%zu byte(s)):\n\n", sum);
|
||||
|
||||
for(i = 0; i < nvlen; ++i) {
|
||||
for (i = 0; i < nvlen; ++i) {
|
||||
fwrite(nva[i].name, nva[i].namelen, 1, stdout);
|
||||
printf(": ");
|
||||
fwrite(nva[i].value, nva[i].valuelen, 1, stdout);
|
||||
@@ -121,7 +120,7 @@ static void deflate(nghttp2_hd_deflater *deflater,
|
||||
|
||||
rv = nghttp2_hd_deflate_hd(deflater, buf, buflen, nva, nvlen);
|
||||
|
||||
if(rv < 0) {
|
||||
if (rv < 0) {
|
||||
fprintf(stderr, "nghttp2_hd_deflate_hd() failed with error: %s\n",
|
||||
nghttp2_strerror((int)rv));
|
||||
|
||||
@@ -132,17 +131,17 @@ static void deflate(nghttp2_hd_deflater *deflater,
|
||||
|
||||
outlen = rv;
|
||||
|
||||
printf("\nDeflate (%zu byte(s), ratio %.02f):\n\n",
|
||||
outlen, sum == 0 ? 0 : (double)outlen / sum);
|
||||
printf("\nDeflate (%zu byte(s), ratio %.02f):\n\n", outlen,
|
||||
sum == 0 ? 0 : (double)outlen / sum);
|
||||
|
||||
for(i = 0; i < outlen; ++i) {
|
||||
if((i & 0x0fu) == 0) {
|
||||
for (i = 0; i < outlen; ++i) {
|
||||
if ((i & 0x0fu) == 0) {
|
||||
printf("%08zX: ", i);
|
||||
}
|
||||
|
||||
printf("%02X ", buf[i]);
|
||||
|
||||
if(((i + 1) & 0x0fu) == 0) {
|
||||
if (((i + 1) & 0x0fu) == 0) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
@@ -153,7 +152,7 @@ static void deflate(nghttp2_hd_deflater *deflater,
|
||||
header data. */
|
||||
rv = inflate_header_block(inflater, buf, outlen, 1);
|
||||
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
free(buf);
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
@@ -165,20 +164,18 @@ static void deflate(nghttp2_hd_deflater *deflater,
|
||||
free(buf);
|
||||
}
|
||||
|
||||
int inflate_header_block(nghttp2_hd_inflater *inflater,
|
||||
uint8_t *in, size_t inlen, int final)
|
||||
{
|
||||
int inflate_header_block(nghttp2_hd_inflater *inflater, uint8_t *in,
|
||||
size_t inlen, int final) {
|
||||
ssize_t rv;
|
||||
|
||||
for(;;) {
|
||||
for (;;) {
|
||||
nghttp2_nv nv;
|
||||
int inflate_flags = 0;
|
||||
size_t proclen;
|
||||
|
||||
rv = nghttp2_hd_inflate_hd(inflater, &nv, &inflate_flags,
|
||||
in, inlen, final);
|
||||
rv = nghttp2_hd_inflate_hd(inflater, &nv, &inflate_flags, in, inlen, final);
|
||||
|
||||
if(rv < 0) {
|
||||
if (rv < 0) {
|
||||
fprintf(stderr, "inflate failed with error code %zd", rv);
|
||||
return -1;
|
||||
}
|
||||
@@ -188,20 +185,19 @@ int inflate_header_block(nghttp2_hd_inflater *inflater,
|
||||
in += proclen;
|
||||
inlen -= proclen;
|
||||
|
||||
if(inflate_flags & NGHTTP2_HD_INFLATE_EMIT) {
|
||||
if (inflate_flags & NGHTTP2_HD_INFLATE_EMIT) {
|
||||
fwrite(nv.name, nv.namelen, 1, stderr);
|
||||
fprintf(stderr, ": ");
|
||||
fwrite(nv.value, nv.valuelen, 1, stderr);
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
if(inflate_flags & NGHTTP2_HD_INFLATE_FINAL) {
|
||||
if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) {
|
||||
nghttp2_hd_inflate_end_headers(inflater);
|
||||
break;
|
||||
}
|
||||
|
||||
if((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 &&
|
||||
inlen == 0) {
|
||||
if ((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && inlen == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,12 +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 __sgi
|
||||
#include <string.h>
|
||||
#define errx(exitcode, format, args...) \
|
||||
{ \
|
||||
warnx(format, ##args); \
|
||||
exit(exitcode); \
|
||||
}
|
||||
#define warnx(format, args...) fprintf(stderr, format "\n", ##args)
|
||||
char *strndup(const char *s, size_t size);
|
||||
#endif
|
||||
|
||||
#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>
|
||||
#ifndef __sgi
|
||||
#include <err.h>
|
||||
#endif
|
||||
#include <signal.h>
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
@@ -43,14 +66,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 */
|
||||
@@ -70,9 +93,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));
|
||||
@@ -83,48 +105,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));
|
||||
@@ -132,11 +157,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);
|
||||
@@ -145,17 +169,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);
|
||||
@@ -165,13 +187,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");
|
||||
}
|
||||
@@ -179,11 +198,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;
|
||||
@@ -191,18 +208,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;
|
||||
@@ -213,15 +228,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);
|
||||
}
|
||||
@@ -232,14 +246,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;
|
||||
@@ -252,13 +265,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;
|
||||
@@ -268,19 +280,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;
|
||||
}
|
||||
}
|
||||
@@ -290,117 +299,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)
|
||||
{
|
||||
static void initialize_nghttp2_session(http2_session_data *session_data) {
|
||||
nghttp2_session_callbacks *callbacks;
|
||||
|
||||
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_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_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_stream_close_callback(
|
||||
callbacks, on_stream_close_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_header_callback
|
||||
(callbacks, on_header_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_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));
|
||||
}
|
||||
|
||||
@@ -409,12 +412,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;
|
||||
}
|
||||
@@ -425,26 +427,25 @@ static int session_send(http2_session_data *session_data)
|
||||
of bufferevent and feed them to nghttp2 library. This may invoke
|
||||
nghttp2 callbacks. It may also queues the frame in nghttp2 session
|
||||
context. To send them, we call session_send() in the end. */
|
||||
static void readcb(struct bufferevent *bev, void *ptr)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
||||
static void readcb(struct bufferevent *bev, void *ptr) {
|
||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||
ssize_t readlen;
|
||||
struct evbuffer *input = bufferevent_get_input(bev);
|
||||
size_t datalen = evbuffer_get_length(input);
|
||||
unsigned char *data = evbuffer_pullup(input, -1);
|
||||
|
||||
readlen = nghttp2_session_mem_recv(session_data->session, data, datalen);
|
||||
if(readlen < 0) {
|
||||
if (readlen < 0) {
|
||||
warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
if(evbuffer_drain(input, readlen) != 0) {
|
||||
if (evbuffer_drain(input, readlen) != 0) {
|
||||
warnx("Fatal error: evbuffer_drain failed");
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
if(session_send(session_data) != 0) {
|
||||
if (session_send(session_data) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
@@ -454,12 +455,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);
|
||||
}
|
||||
}
|
||||
@@ -469,10 +469,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");
|
||||
@@ -480,41 +479,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;
|
||||
@@ -522,8 +518,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;
|
||||
@@ -534,11 +529,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;
|
||||
@@ -561,11 +556,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);
|
||||
}
|
||||
@@ -574,10 +568,10 @@ int main(int argc, char **argv)
|
||||
act.sa_handler = SIG_IGN;
|
||||
sigaction(SIGPIPE, &act, NULL);
|
||||
|
||||
OPENSSL_config(NULL);
|
||||
OpenSSL_add_all_algorithms();
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
OpenSSL_add_all_algorithms();
|
||||
OPENSSL_config(NULL);
|
||||
|
||||
run(argv[1]);
|
||||
return 0;
|
||||
|
||||
@@ -22,17 +22,43 @@
|
||||
* 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 __sgi
|
||||
#define errx(exitcode, format, args...) \
|
||||
{ \
|
||||
warnx(format, ##args); \
|
||||
exit(exitcode); \
|
||||
}
|
||||
#define warn(format, args...) warnx(format ": %s", ##args, strerror(errno))
|
||||
#define warnx(format, args...) fprintf(stderr, format "\n", ##args)
|
||||
#endif
|
||||
|
||||
#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>
|
||||
#ifndef __sgi
|
||||
#include <err.h>
|
||||
#endif
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
@@ -47,11 +73,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;
|
||||
@@ -69,7 +97,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 {
|
||||
@@ -80,32 +107,40 @@ struct app_context {
|
||||
static unsigned char next_proto_list[256];
|
||||
static size_t next_proto_list_len;
|
||||
|
||||
static int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len,
|
||||
void *arg)
|
||||
{
|
||||
static int next_proto_cb(SSL *s _U_, const unsigned char **data,
|
||||
unsigned int *len, void *arg _U_) {
|
||||
*data = next_proto_list;
|
||||
*len = (unsigned int)next_proto_list_len;
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
|
||||
/* Create SSL_CTX. */
|
||||
static SSL_CTX* create_ssl_ctx(const char *key_file, const char *cert_file)
|
||||
{
|
||||
static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) {
|
||||
SSL_CTX *ssl_ctx;
|
||||
EC_KEY *ecdh;
|
||||
|
||||
ssl_ctx = SSL_CTX_new(SSLv23_server_method());
|
||||
if(!ssl_ctx) {
|
||||
if (!ssl_ctx) {
|
||||
errx(1, "Could not create SSL/TLS context: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
SSL_CTX_set_options(ssl_ctx,
|
||||
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_COMPRESSION |
|
||||
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
|
||||
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
|
||||
SSL_OP_NO_COMPRESSION |
|
||||
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
|
||||
|
||||
if(SSL_CTX_use_PrivateKey_file(ssl_ctx, key_file,
|
||||
SSL_FILETYPE_PEM) != 1) {
|
||||
ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
|
||||
if (!ecdh) {
|
||||
errx(1, "EC_KEY_new_by_curv_name failed: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh);
|
||||
EC_KEY_free(ecdh);
|
||||
|
||||
if (SSL_CTX_use_PrivateKey_file(ssl_ctx, key_file, SSL_FILETYPE_PEM) != 1) {
|
||||
errx(1, "Could not read private key file %s", key_file);
|
||||
}
|
||||
if(SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file) != 1) {
|
||||
if (SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file) != 1) {
|
||||
errx(1, "Could not read certificate file %s", cert_file);
|
||||
}
|
||||
|
||||
@@ -119,11 +154,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));
|
||||
}
|
||||
@@ -131,28 +165,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));
|
||||
@@ -163,20 +194,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;
|
||||
@@ -188,13 +217,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);
|
||||
@@ -203,17 +230,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;
|
||||
@@ -224,11 +250,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;
|
||||
}
|
||||
@@ -239,37 +264,34 @@ static int session_send(http2_session_data *session_data)
|
||||
function. Invocation of nghttp2_session_mem_recv() may make
|
||||
additional pending frames, so call session_send() at the end of the
|
||||
function. */
|
||||
static int session_recv(http2_session_data *session_data)
|
||||
{
|
||||
static int session_recv(http2_session_data *session_data) {
|
||||
ssize_t readlen;
|
||||
struct evbuffer *input = bufferevent_get_input(session_data->bev);
|
||||
size_t datalen = evbuffer_get_length(input);
|
||||
unsigned char *data = evbuffer_pullup(input, -1);
|
||||
|
||||
readlen = nghttp2_session_mem_recv(session_data->session, data, datalen);
|
||||
if(readlen < 0) {
|
||||
if (readlen < 0) {
|
||||
warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
|
||||
return -1;
|
||||
}
|
||||
if(evbuffer_drain(input, readlen) != 0) {
|
||||
if (evbuffer_drain(input, readlen) != 0) {
|
||||
warnx("Fatal error: evbuffer_drain failed");
|
||||
return -1;
|
||||
}
|
||||
if(session_send(session_data) != 0) {
|
||||
if (session_send(session_data) != 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t send_callback(nghttp2_session *session,
|
||||
const uint8_t *data, size_t length,
|
||||
int flags, void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
|
||||
size_t length, int flags _U_, void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
struct bufferevent *bev = session_data->bev;
|
||||
/* Avoid excessive buffering in server side. */
|
||||
if(evbuffer_get_length(bufferevent_get_output(session_data->bev)) >=
|
||||
OUTPUT_WOULDBLOCK_THRESHOLD) {
|
||||
if (evbuffer_get_length(bufferevent_get_output(session_data->bev)) >=
|
||||
OUTPUT_WOULDBLOCK_THRESHOLD) {
|
||||
return NGHTTP2_ERR_WOULDBLOCK;
|
||||
}
|
||||
bufferevent_write(bev, data, length);
|
||||
@@ -277,26 +299,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;
|
||||
@@ -306,16 +326,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;
|
||||
}
|
||||
@@ -331,33 +350,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;
|
||||
}
|
||||
@@ -365,25 +384,22 @@ static int send_response(nghttp2_session *session, int32_t stream_id,
|
||||
}
|
||||
|
||||
const char ERROR_HTML[] = "<html><head><title>404</title></head>"
|
||||
"<body><h1>404 Not Found</h1></body></html>";
|
||||
"<body><h1>404 Not Found</h1></body></html>";
|
||||
|
||||
static int error_reply(nghttp2_session *session,
|
||||
http2_stream_data *stream_data)
|
||||
{
|
||||
http2_stream_data *stream_data) {
|
||||
int rv;
|
||||
ssize_t writelen;
|
||||
int pipefd[2];
|
||||
nghttp2_nv hdrs[] = {
|
||||
MAKE_NV(":status", "404")
|
||||
};
|
||||
nghttp2_nv hdrs[] = {MAKE_NV(":status", "404")};
|
||||
|
||||
rv = pipe(pipefd);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
warn("Could not create pipe");
|
||||
rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
|
||||
stream_data->stream_id,
|
||||
NGHTTP2_INTERNAL_ERROR);
|
||||
if(rv != 0) {
|
||||
if (rv != 0) {
|
||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||
return -1;
|
||||
}
|
||||
@@ -393,15 +409,15 @@ static int error_reply(nghttp2_session *session,
|
||||
writelen = write(pipefd[1], ERROR_HTML, sizeof(ERROR_HTML) - 1);
|
||||
close(pipefd[1]);
|
||||
|
||||
if(writelen != sizeof(ERROR_HTML) - 1) {
|
||||
if (writelen != sizeof(ERROR_HTML) - 1) {
|
||||
close(pipefd[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
stream_data->fd = pipefd[0];
|
||||
|
||||
if(send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs),
|
||||
pipefd[0]) != 0) {
|
||||
if (send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs),
|
||||
pipefd[0]) != 0) {
|
||||
close(pipefd[0]);
|
||||
return -1;
|
||||
}
|
||||
@@ -411,27 +427,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;
|
||||
@@ -441,13 +456,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);
|
||||
@@ -458,52 +472,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;
|
||||
}
|
||||
@@ -511,20 +520,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);
|
||||
@@ -536,16 +544,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);
|
||||
@@ -553,25 +558,24 @@ static int on_stream_close_callback(nghttp2_session *session,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void initialize_nghttp2_session(http2_session_data *session_data)
|
||||
{
|
||||
static void initialize_nghttp2_session(http2_session_data *session_data) {
|
||||
nghttp2_session_callbacks *callbacks;
|
||||
|
||||
nghttp2_session_callbacks_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_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_stream_close_callback(
|
||||
callbacks, on_stream_close_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_header_callback
|
||||
(callbacks, on_header_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_callbacks_set_on_begin_headers_callback(
|
||||
callbacks, on_begin_headers_callback);
|
||||
|
||||
nghttp2_session_server_new(&session_data->session, callbacks, session_data);
|
||||
|
||||
@@ -580,16 +584,14 @@ static void initialize_nghttp2_session(http2_session_data *session_data)
|
||||
|
||||
/* 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;
|
||||
}
|
||||
@@ -598,10 +600,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;
|
||||
}
|
||||
@@ -615,88 +616,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;
|
||||
@@ -710,16 +683,15 @@ static void start_listen(struct event_base *evbase, const char *service,
|
||||
#endif /* AI_ADDRCONFIG */
|
||||
|
||||
rv = getaddrinfo(NULL, service, &hints, &res);
|
||||
if(rv != 0) {
|
||||
errx(1, NULL);
|
||||
if (rv != 0) {
|
||||
errx(1, "Could not resolve server address");
|
||||
}
|
||||
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;
|
||||
@@ -729,16 +701,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;
|
||||
@@ -754,11 +724,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);
|
||||
}
|
||||
@@ -767,10 +736,10 @@ int main(int argc, char **argv)
|
||||
act.sa_handler = SIG_IGN;
|
||||
sigaction(SIGPIPE, &act, NULL);
|
||||
|
||||
OPENSSL_config(NULL);
|
||||
OpenSSL_add_all_algorithms();
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
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')
|
||||
124
gennghttpxfun.py
Executable file
124
gennghttpxfun.py
Executable file
@@ -0,0 +1,124 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from gentokenlookup import gentokenlookup
|
||||
|
||||
OPTIONS = [
|
||||
"private-key-file",
|
||||
"private-key-passwd-file",
|
||||
"certificate-file",
|
||||
"dh-param-file",
|
||||
"subcert",
|
||||
"backend",
|
||||
"frontend",
|
||||
"workers",
|
||||
"http2-max-concurrent-streams",
|
||||
"log-level",
|
||||
"daemon",
|
||||
"http2-proxy",
|
||||
"http2-bridge",
|
||||
"client-proxy",
|
||||
"add-x-forwarded-for",
|
||||
"strip-incoming-x-forwarded-for",
|
||||
"no-via",
|
||||
"frontend-http2-read-timeout",
|
||||
"frontend-read-timeout",
|
||||
"frontend-write-timeout",
|
||||
"backend-read-timeout",
|
||||
"backend-write-timeout",
|
||||
"stream-read-timeout",
|
||||
"stream-write-timeout",
|
||||
"accesslog-file",
|
||||
"accesslog-syslog",
|
||||
"accesslog-format",
|
||||
"errorlog-file",
|
||||
"errorlog-syslog",
|
||||
"backend-keep-alive-timeout",
|
||||
"frontend-http2-window-bits",
|
||||
"backend-http2-window-bits",
|
||||
"frontend-http2-connection-window-bits",
|
||||
"backend-http2-connection-window-bits",
|
||||
"frontend-no-tls",
|
||||
"backend-no-tls",
|
||||
"backend-tls-sni-field",
|
||||
"pid-file",
|
||||
"user",
|
||||
"syslog-facility",
|
||||
"backlog",
|
||||
"ciphers",
|
||||
"client",
|
||||
"insecure",
|
||||
"cacert",
|
||||
"backend-ipv4",
|
||||
"backend-ipv6",
|
||||
"backend-http-proxy-uri",
|
||||
"read-rate",
|
||||
"read-burst",
|
||||
"write-rate",
|
||||
"write-burst",
|
||||
"worker-read-rate",
|
||||
"worker-read-burst",
|
||||
"worker-write-rate",
|
||||
"worker-write-burst",
|
||||
"npn-list",
|
||||
"tls-proto-list",
|
||||
"verify-client",
|
||||
"verify-client-cacert",
|
||||
"client-private-key-file",
|
||||
"client-cert-file",
|
||||
"frontend-http2-dump-request-header",
|
||||
"frontend-http2-dump-response-header",
|
||||
"http2-no-cookie-crumbling",
|
||||
"frontend-frame-debug",
|
||||
"padding",
|
||||
"altsvc",
|
||||
"add-request-header",
|
||||
"add-response-header",
|
||||
"worker-frontend-connections",
|
||||
"no-location-rewrite",
|
||||
"no-host-rewrite",
|
||||
"backend-http1-connections-per-host",
|
||||
"backend-http1-connections-per-frontend",
|
||||
"listener-disable-timeout",
|
||||
"tls-ticket-key-file",
|
||||
"rlimit-nofile",
|
||||
"backend-request-buffer",
|
||||
"backend-response-buffer",
|
||||
"no-server-push",
|
||||
"backend-http2-connections-per-worker",
|
||||
"fetch-ocsp-response-file",
|
||||
"ocsp-update-interval",
|
||||
"no-ocsp",
|
||||
"header-field-buffer",
|
||||
"max-header-fields",
|
||||
"include",
|
||||
"tls-ticket-key-cipher",
|
||||
"host-rewrite",
|
||||
"tls-session-cache-memcached",
|
||||
"tls-ticket-key-memcached",
|
||||
"tls-ticket-key-memcached-interval",
|
||||
"tls-ticket-key-memcached-max-retry",
|
||||
"tls-ticket-key-memcached-max-fail",
|
||||
"conf",
|
||||
]
|
||||
|
||||
LOGVARS = [
|
||||
"remote_addr",
|
||||
"time_local",
|
||||
"time_iso8601",
|
||||
"request",
|
||||
"status",
|
||||
"body_bytes_sent",
|
||||
"remote_port",
|
||||
"server_port",
|
||||
"request_time",
|
||||
"pid",
|
||||
"alpn",
|
||||
"ssl_cipher",
|
||||
"ssl_protocol",
|
||||
"ssl_session_id",
|
||||
"ssl_session_reused",
|
||||
]
|
||||
|
||||
if __name__ == '__main__':
|
||||
gentokenlookup(OPTIONS, 'SHRPX_OPTID', value_type='char', comp_fun='util::strieq_l')
|
||||
gentokenlookup(LOGVARS, 'SHRPX_LOGF', value_type='char', comp_fun='util::strieq_l', return_type='LogFragmentType', fail_value='SHRPX_LOGF_NONE')
|
||||
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, value_type, comp_fun, return_type, fail_value):
|
||||
print '''\
|
||||
{} lookup_token(const {} *name, size_t namelen) {{
|
||||
switch (namelen) {{'''.format(return_type, value_type)
|
||||
b = build_header(tokens)
|
||||
for size in sorted(b.keys()):
|
||||
ents = b[size]
|
||||
print '''\
|
||||
case {}:'''.format(size)
|
||||
print '''\
|
||||
switch (name[{}]) {{'''.format(size - 1)
|
||||
for c in sorted(ents.keys()):
|
||||
headers = sorted(ents[c])
|
||||
print '''\
|
||||
case '{}':'''.format(c)
|
||||
for k in headers:
|
||||
print '''\
|
||||
if ({}("{}", name, {})) {{
|
||||
return {};
|
||||
}}'''.format(comp_fun, k[:-1], size - 1, to_enum_hd(k, prefix))
|
||||
print '''\
|
||||
break;'''
|
||||
print '''\
|
||||
}
|
||||
break;'''
|
||||
print '''\
|
||||
}}
|
||||
return {};
|
||||
}}'''.format(fail_value)
|
||||
|
||||
def gentokenlookup(tokens, prefix, value_type='uint8_t', comp_fun='util::streq_l', return_type='int', fail_value='-1'):
|
||||
gen_enum(tokens, prefix)
|
||||
print ''
|
||||
gen_index_header(tokens, prefix, value_type, comp_fun, return_type, fail_value)
|
||||
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()
|
||||
192
help2rst.py
Executable file
192
help2rst.py
Executable file
@@ -0,0 +1,192 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# script to produce rst file from program's help output.
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from __future__ import print_function
|
||||
import sys
|
||||
import re
|
||||
import argparse
|
||||
|
||||
arg_indent = ' ' * 14
|
||||
|
||||
def help2man(infile):
|
||||
# We assume that first line is usage line like this:
|
||||
#
|
||||
# Usage: nghttp [OPTIONS]... URI...
|
||||
#
|
||||
# The second line is description of the command. Multiple lines
|
||||
# are permitted. The blank line signals the end of this section.
|
||||
# After that, we parses positional and optional arguments.
|
||||
#
|
||||
# The positional argument is enclosed with < and >:
|
||||
#
|
||||
# <PRIVATE_KEY>
|
||||
#
|
||||
# We may describe default behavior without any options by encoding
|
||||
# ( and ):
|
||||
#
|
||||
# (default mode)
|
||||
#
|
||||
# "Options:" is treated specially and produces "OPTIONS" section.
|
||||
# We allow subsection under OPTIONS. Lines not starting with (, <
|
||||
# and Options: are treated as subsection name and produces section
|
||||
# one level down:
|
||||
#
|
||||
# TLS/SSL:
|
||||
#
|
||||
# The above is an example of subsection.
|
||||
#
|
||||
# The description of arguments must be indented by len(arg_indent)
|
||||
# characters. The default value should be placed in separate line
|
||||
# and should be start with "Default: " after indentation.
|
||||
|
||||
line = infile.readline().strip()
|
||||
m = re.match(r'^Usage: (.*)', line)
|
||||
if not m:
|
||||
print('usage line is invalid. Expected following lines:')
|
||||
print('Usage: cmdname ...')
|
||||
sys.exit(1)
|
||||
synopsis = m.group(1).split(' ', 1)
|
||||
if len(synopsis) == 2:
|
||||
cmdname, args = synopsis
|
||||
else:
|
||||
cmdname, args = synopsis[0], ''
|
||||
|
||||
description = []
|
||||
for line in infile:
|
||||
line = line.strip()
|
||||
if not line:
|
||||
break
|
||||
description.append(line)
|
||||
|
||||
print('''
|
||||
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
|
||||
|
||||
.. program:: {cmdname}
|
||||
|
||||
{cmdname}(1)
|
||||
{cmdnameunderline}
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
|
||||
**{cmdname}** {args}
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
|
||||
{description}
|
||||
'''.format(cmdname=cmdname, args=args,
|
||||
cmdnameunderline='=' * (len(cmdname) + 3),
|
||||
synopsis=synopsis, description=format_text('\n'.join(description))))
|
||||
|
||||
in_arg = False
|
||||
in_footer = False
|
||||
|
||||
for line in infile:
|
||||
line = line.rstrip()
|
||||
|
||||
if not line.strip() and in_arg:
|
||||
print()
|
||||
continue
|
||||
if line.startswith(' ') and in_arg:
|
||||
if not line.startswith(arg_indent):
|
||||
sys.stderr.write('warning: argument description is not indented correctly. We need {} spaces as indentation.\n'.format(len(arg_indent)))
|
||||
print('{}'.format(format_arg_text(line[len(arg_indent):])))
|
||||
continue
|
||||
|
||||
if in_arg:
|
||||
print()
|
||||
in_arg = False
|
||||
|
||||
if line == '--':
|
||||
in_footer = True
|
||||
continue
|
||||
|
||||
if in_footer:
|
||||
print(line.strip())
|
||||
continue
|
||||
|
||||
if line == 'Options:':
|
||||
print('OPTIONS')
|
||||
print('-------')
|
||||
print()
|
||||
continue
|
||||
|
||||
if line.startswith(' <'):
|
||||
# positional argument
|
||||
m = re.match(r'^(?:\s+)([a-zA-Z0-9-_<>]+)(.*)', line)
|
||||
argname, rest = m.group(1), m.group(2)
|
||||
print('.. describe:: {}'.format(argname))
|
||||
print()
|
||||
print('{}'.format(format_arg_text(rest.strip())))
|
||||
in_arg = True
|
||||
continue
|
||||
|
||||
if line.startswith(' ('):
|
||||
# positional argument
|
||||
m = re.match(r'^(?:\s+)(\([a-zA-Z0-9-_<> ]+\))(.*)', line)
|
||||
argname, rest = m.group(1), m.group(2)
|
||||
print('.. describe:: {}'.format(argname))
|
||||
print()
|
||||
print('{}'.format(format_arg_text(rest.strip())))
|
||||
in_arg = True
|
||||
continue
|
||||
|
||||
if line.startswith(' -'):
|
||||
# optional argument
|
||||
m = re.match(
|
||||
r'^(?:\s+)(-\S+?(?:, -\S+?)*)($| .*)',
|
||||
line)
|
||||
argname, rest = m.group(1), m.group(2)
|
||||
print('.. option:: {}'.format(argname))
|
||||
print()
|
||||
rest = rest.strip()
|
||||
if len(rest):
|
||||
print('{}'.format(format_arg_text(rest)))
|
||||
in_arg = True
|
||||
continue
|
||||
|
||||
if not line.startswith(' ') and line.endswith(':'):
|
||||
# subsection
|
||||
subsec = line.strip()[:-1]
|
||||
print('{}'.format(subsec))
|
||||
print('{}'.format('~' * len(subsec)))
|
||||
print()
|
||||
continue
|
||||
|
||||
print(line.strip())
|
||||
|
||||
def format_text(text):
|
||||
# escape *
|
||||
if len(text) > len(arg_indent):
|
||||
text = text[:len(arg_indent) + 1] + re.sub(r'\*', r'\*', text[len(arg_indent) + 1:])
|
||||
else:
|
||||
text = re.sub(r'\*', r'\*', text)
|
||||
# markup option reference
|
||||
text = re.sub(r'(^|\s)(-[a-zA-Z0-9-]+)', r'\1:option:`\2`', text)
|
||||
# sphinx does not like markup like ':option:`-f`='. We need
|
||||
# backslash between ` and =.
|
||||
text = re.sub(r'(:option:`.*?`)(\S)', r'\1\\\2', text)
|
||||
# file path should be italic
|
||||
text = re.sub(r'(^|\s|\'|")(/[^\s\'"]*)', r'\1*\2*', text)
|
||||
return text
|
||||
|
||||
def format_arg_text(text):
|
||||
if text.strip().startswith('Default: '):
|
||||
return '\n ' + re.sub(r'^(\s*Default: )(.*)$', r'\1``\2``', text)
|
||||
return ' {}'.format(format_text(text))
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Produces rst document from help output.')
|
||||
parser.add_argument('-i', '--include', metavar='FILE',
|
||||
help='include content of <FILE> as verbatim. It should be ReST formatted text.')
|
||||
args = parser.parse_args()
|
||||
help2man(sys.stdin)
|
||||
if args.include:
|
||||
print()
|
||||
with open(args.include) as f:
|
||||
sys.stdout.write(f.read())
|
||||
2
integration-tests/.gitignore
vendored
Normal file
2
integration-tests/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# generated files
|
||||
config.go
|
||||
42
integration-tests/Makefile.am
Normal file
42
integration-tests/Makefile.am
Normal file
@@ -0,0 +1,42 @@
|
||||
# nghttp2 - HTTP/2 C Library
|
||||
|
||||
# Copyright (c) 2015 Tatsuhiro Tsujikawa
|
||||
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
EXTRA_DIST = \
|
||||
nghttpx_http1_test.go \
|
||||
nghttpx_http2_test.go \
|
||||
nghttpx_spdy_test.go \
|
||||
server_tester.go \
|
||||
server.key \
|
||||
server.crt \
|
||||
alt-server.key \
|
||||
alt-server.crt \
|
||||
setenv
|
||||
|
||||
itprep-local:
|
||||
go get -d -v github.com/bradfitz/http2
|
||||
go get -d -v github.com/tatsuhiro-t/go-nghttp2
|
||||
go get -d -v github.com/tatsuhiro-t/spdy
|
||||
go get -d -v golang.org/x/net/websocket
|
||||
|
||||
it-local:
|
||||
sh setenv go test -v
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user