mirror of
https://github.com/nghttp2/nghttp2.git
synced 2025-12-08 02:58:53 +08:00
Compare commits
1024 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 | ||
|
|
516a2f0efc |
55
.gitignore
vendored
55
.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,34 +20,21 @@ config.log
|
||||
config.status
|
||||
config.sub
|
||||
configure
|
||||
depcomp
|
||||
install-sh
|
||||
.deps/
|
||||
.libs
|
||||
lib/includes/nghttp2/nghttp2ver.h
|
||||
lib/libnghttp2.pc
|
||||
src/libnghttp2_asio.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
|
||||
doc/asio_http2.h.rst
|
||||
doc/libnghttp2_asio.rst
|
||||
doc/contribute.rst
|
||||
python/setup.py
|
||||
python/dist
|
||||
python/MANIFEST
|
||||
|
||||
# test logs generated by `make check`
|
||||
*.log
|
||||
*.trs
|
||||
|
||||
lib/MSVC_obj/
|
||||
_VC_ROOT/
|
||||
.depend.MSVC
|
||||
*.pyd
|
||||
*.egg-info/
|
||||
python/nghttp2.c
|
||||
|
||||
46
.travis.yml
46
.travis.yml
@@ -1,31 +1,31 @@
|
||||
language: cpp
|
||||
compiler:
|
||||
- clang
|
||||
#Disable gcc build for the moment...
|
||||
# - gcc
|
||||
- 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
|
||||
libev-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!
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# vim: ft=dockerfile:
|
||||
# Dockerfile to build nghttp2 android binary
|
||||
#
|
||||
# $ sudo docker build -t nghttp2-android - < Dockerfile.android
|
||||
@@ -9,7 +10,7 @@
|
||||
#
|
||||
# $ sudo docker run -v /path/to/dest:/out nghttp2-android cp /root/build/nghttp2/src/nghttpx /out
|
||||
|
||||
FROM ubuntu
|
||||
FROM ubuntu:vivid
|
||||
|
||||
MAINTAINER Tatsuhiro Tsujikawa
|
||||
|
||||
@@ -29,11 +30,12 @@ RUN apt-get install -y make binutils autoconf automake autotools-dev libtool \
|
||||
genisoimage libc6-i386 lib32stdc++6
|
||||
|
||||
WORKDIR /root/build
|
||||
RUN curl -L -O http://dl.google.com/android/ndk/android-ndk-r10c-linux-x86_64.bin
|
||||
RUN chmod a+x android-ndk-r10c-linux-x86_64.bin
|
||||
RUN ./android-ndk-r10c-linux-x86_64.bin
|
||||
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-r10c
|
||||
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 \
|
||||
@@ -57,20 +59,24 @@ RUN autoreconf -i && \
|
||||
make install
|
||||
|
||||
WORKDIR /root/build
|
||||
RUN curl -L -O https://www.openssl.org/source/openssl-1.0.1j.tar.gz
|
||||
RUN tar xf openssl-1.0.1j.tar.gz
|
||||
WORKDIR /root/build/openssl-1.0.1j
|
||||
RUN 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
|
||||
RUN curl -L -O https://gist.github.com/tatsuhiro-t/48c45f08950f587180ed/raw/80a8f003b5d1091eae497c5995bbaa68096e739b/libev-4.19-android.patch
|
||||
RUN tar xf libev-4.19.tar.gz
|
||||
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
|
||||
RUN ./configure \
|
||||
RUN patch -p1 < ../libev-4.19-android.patch && \
|
||||
./configure \
|
||||
--host=arm-linux-androideabi \
|
||||
--build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \
|
||||
--prefix=$PREFIX \
|
||||
@@ -80,6 +86,25 @@ RUN ./configure \
|
||||
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
|
||||
@@ -95,9 +120,9 @@ RUN autoreconf -i && \
|
||||
--disable-threads \
|
||||
LIBSPDYLAY_CFLAGS=-I$PREFIX/usr/local/include \
|
||||
LIBSPDYLAY_LIBS="-L$PREFIX/usr/local/lib -lspdylay" \
|
||||
CPPFLAGS="-I$PREFIX/include" \
|
||||
CPPFLAGS="-fPIE -I$PREFIX/include" \
|
||||
CXXFLAGS="-fno-strict-aliasing" \
|
||||
PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \
|
||||
LDFLAGS="-L$PREFIX/lib" && \
|
||||
LDFLAGS="-fPIE -pie -L$PREFIX/lib" && \
|
||||
make && \
|
||||
arm-linux-androideabi-strip src/nghttpx src/nghttpd src/nghttp
|
||||
|
||||
@@ -21,7 +21,12 @@
|
||||
# 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 integration-tests \
|
||||
doc contrib
|
||||
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
|
||||
|
||||
|
||||
916
README.rst
916
README.rst
File diff suppressed because it is too large
Load Diff
@@ -39,8 +39,9 @@ PATH=$TOOLCHAIN/bin:$PATH
|
||||
--without-libxml2 \
|
||||
--disable-python-bindings \
|
||||
--disable-examples \
|
||||
--enable-werror \
|
||||
CC=clang \
|
||||
CXX=clang++ \
|
||||
CPPFLAGS="-I$PREFIX/include" \
|
||||
CPPFLAGS="-fPIE -I$PREFIX/include" \
|
||||
PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \
|
||||
LDFLAGS="-L$PREFIX/lib"
|
||||
LDFLAGS="-fPIE -pie -L$PREFIX/lib"
|
||||
|
||||
211
configure.ac
211
configure.ac
@@ -25,14 +25,30 @@ 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.7.3], [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, 9)
|
||||
AC_SUBST(LT_CURRENT, 15)
|
||||
AC_SUBST(LT_REVISION, 0)
|
||||
AC_SUBST(LT_AGE, 4)
|
||||
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"`
|
||||
@@ -42,21 +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])
|
||||
# comment out for now since this requires automake 1.13 or higher and
|
||||
# travis has older one.
|
||||
# AM_EXTRA_RECURSIVE_TARGETS([it])
|
||||
|
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
|
||||
dnl Checks for command-line options
|
||||
AC_ARG_ENABLE([werror],
|
||||
[AS_HELP_STRING([--enable-werror],
|
||||
@@ -129,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
|
||||
|
||||
@@ -165,24 +169,6 @@ 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(
|
||||
@@ -200,21 +186,6 @@ std::vector<std::future<int>> v;
|
||||
[have_std_future=no
|
||||
AC_MSG_RESULT([no])])
|
||||
|
||||
# Check that thread_local is available.
|
||||
AC_MSG_CHECKING([whether thread_local is available])
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
|
||||
[[
|
||||
]],
|
||||
[[
|
||||
thread_local int n;
|
||||
]])],
|
||||
[AC_DEFINE([HAVE_THREAD_LOCAL], [1],
|
||||
[Define to 1 if you have the `thread_local`.])
|
||||
have_thread_local=yes
|
||||
AC_MSG_RESULT([yes])],
|
||||
[have_thread_local=no
|
||||
AC_MSG_RESULT([no])])
|
||||
|
||||
# Check that std::map::emplace is available for g++-4.7.
|
||||
AC_MSG_CHECKING([whether std::map::emplace is available])
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
|
||||
@@ -241,20 +212,6 @@ TESTLDADD=
|
||||
# Additional libraries required for programs under src directory.
|
||||
APPLDFLAGS=
|
||||
|
||||
LIBS_OLD=$LIBS
|
||||
# Search for dlsym function, which is used in tests. Linux needs -ldl,
|
||||
# but netbsd does not need it.
|
||||
AC_SEARCH_LIBS([dlsym], [dl])
|
||||
TESTLDADD="$LIBS $TESTLDADD"
|
||||
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`.])])
|
||||
APPLDFLAGS="$LIBS $APPLDFLAGS"
|
||||
LIBS=$LIBS_OLD
|
||||
|
||||
case "$host" in
|
||||
*android*)
|
||||
android_build=yes
|
||||
@@ -268,16 +225,10 @@ case "$host" in
|
||||
esac
|
||||
|
||||
# zlib
|
||||
if test "x$android_build" = "xyes"; then
|
||||
# Use zlib provided by NDK
|
||||
APPLDFLAGS="-lz $APPLDFLAGS"
|
||||
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
|
||||
@@ -343,7 +294,7 @@ fi
|
||||
# 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}" == "xyes"; then
|
||||
if test "x${have_jansson}" = "xyes"; then
|
||||
AC_DEFINE([HAVE_JANSSON], [1],
|
||||
[Define to 1 if you have `libjansson` library.])
|
||||
else
|
||||
@@ -373,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
|
||||
@@ -388,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.])
|
||||
@@ -480,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" &&
|
||||
@@ -513,23 +489,23 @@ AM_CONDITIONAL([ENABLE_FAILMALLOC], [ test "x${enable_failmalloc}" = "xyes" ])
|
||||
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
|
||||
@@ -537,8 +513,16 @@ AC_TYPE_UINT8_T
|
||||
AC_TYPE_UINT16_T
|
||||
AC_TYPE_UINT32_T
|
||||
AC_TYPE_UINT64_T
|
||||
AC_TYPE_INT8_T
|
||||
AC_TYPE_INT16_T
|
||||
AC_TYPE_INT32_T
|
||||
AC_TYPE_INT64_T
|
||||
AC_TYPE_OFF_T
|
||||
AC_TYPE_PID_T
|
||||
AC_TYPE_UID_T
|
||||
AC_CHECK_TYPES([ptrdiff_t])
|
||||
AC_C_BIGENDIAN
|
||||
AC_C_INLINE
|
||||
AC_SYS_LARGEFILE
|
||||
|
||||
AC_CHECK_MEMBER([struct tm.tm_gmtoff], [have_struct_tm_tm_gmtoff=yes],
|
||||
@@ -549,16 +533,44 @@ if test "x$have_struct_tm_tm_gmtoff" = "xyes"; then
|
||||
[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.
|
||||
if test "x$cross_compiling" != "xyes"; then
|
||||
AC_FUNC_MALLOC
|
||||
fi
|
||||
|
||||
# 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 \
|
||||
])
|
||||
|
||||
@@ -575,13 +587,6 @@ AM_CONDITIONAL([ENABLE_TINY_NGHTTPD],
|
||||
[ test "x${have_epoll}" = "xyes" &&
|
||||
test "x${have_timerfd_create}" = "xyes"])
|
||||
|
||||
dnl Windows library for winsock2
|
||||
case "${host}" in
|
||||
*mingw*)
|
||||
LIBS="$LIBS -lws2_32"
|
||||
;;
|
||||
esac
|
||||
|
||||
ac_save_CFLAGS=$CFLAGS
|
||||
CFLAGS=
|
||||
|
||||
@@ -627,11 +632,10 @@ if test "x$debug" != "xno"; then
|
||||
fi
|
||||
|
||||
enable_threads=yes
|
||||
# Some platform does not have working std::future or thread_local. We
|
||||
# disable threading for those platforms.
|
||||
# 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" ||
|
||||
test "x$have_thread_local" != "xyes"; then
|
||||
test "x$have_std_future" != "xyes"; then
|
||||
enable_threads=no
|
||||
AC_DEFINE([NOTHREADS], [1], [Define to 1 if you want to disable threads.])
|
||||
fi
|
||||
@@ -672,8 +676,11 @@ AC_CONFIG_FILES([
|
||||
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
|
||||
|
||||
@@ -712,6 +719,7 @@ AC_MSG_NOTICE([summary of build options:
|
||||
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}
|
||||
@@ -724,4 +732,5 @@ AC_MSG_NOTICE([summary of build options:
|
||||
Examples: ${enable_examples}
|
||||
Python bindings:${enable_python_bindings}
|
||||
Threading: ${enable_threads}
|
||||
Third-party: ${enable_third_party}
|
||||
])
|
||||
|
||||
2
contrib/.gitignore
vendored
2
contrib/.gitignore
vendored
@@ -1 +1,3 @@
|
||||
nghttpx-init
|
||||
nghttpx.service
|
||||
nghttpx-upstart.conf
|
||||
|
||||
@@ -21,19 +21,24 @@
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
EXTRA_DIST = nghttpx-init.in nghttpx-logrotate
|
||||
configfiles = nghttpx-init nghttpx.service nghttpx-upstart.conf
|
||||
|
||||
EXTRA_DIST = $(configfiles:%=%.in) nghttpx-logrotate tlsticketupdate.go
|
||||
|
||||
edit = sed -e 's|@bindir[@]|$(bindir)|g'
|
||||
|
||||
nghttpx-init: Makefile
|
||||
nghttpx-init: %: $(srcdir)/%.in
|
||||
rm -f $@ $@.tmp
|
||||
$(edit) $(srcdir)/$@.in > $@.tmp
|
||||
$(edit) $< > $@.tmp
|
||||
chmod +x $@.tmp
|
||||
mv $@.tmp $@
|
||||
|
||||
nghttpx-init: $(srcdir)/nghttpx-init.in
|
||||
nghttpx.service nghttpx-upstart.conf: %: $(srcdir)/%.in
|
||||
$(edit) $< > $@
|
||||
|
||||
all-local: nghttpx-init
|
||||
$(configfiles): Makefile
|
||||
|
||||
all-local: $(configfiles)
|
||||
|
||||
clean-local:
|
||||
-rm -f nghttpx-init nghttpx-init.tmp
|
||||
-rm -f nghttpx-init.tmp $(configfiles)
|
||||
|
||||
@@ -1,18 +1,11 @@
|
||||
/var/log/nghttpx/*.log {
|
||||
weekly
|
||||
missingok
|
||||
rotate 52
|
||||
compress
|
||||
delaycompress
|
||||
notifempty
|
||||
create 0640 www-data adm
|
||||
sharedscripts
|
||||
prerotate
|
||||
if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
|
||||
run-parts /etc/logrotate.d/httpd-prerotate; \
|
||||
fi \
|
||||
endscript
|
||||
postrotate
|
||||
[ -s /run/nghttpx.pid ] && kill -USR1 `cat /run/nghttpx.pid`
|
||||
endscript
|
||||
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
|
||||
|
||||
156
doc/Makefile.am
156
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 \
|
||||
@@ -41,24 +151,28 @@ EXTRA_DIST = \
|
||||
sources/python-apiref.rst \
|
||||
sources/building-android-binary.rst \
|
||||
sources/contribute.rst \
|
||||
_themes/sphinx_rtd_theme/footer.html \
|
||||
_themes/sphinx_rtd_theme/theme.conf \
|
||||
_themes/sphinx_rtd_theme/layout_old.html \
|
||||
_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
|
||||
#
|
||||
@@ -95,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."
|
||||
|
||||
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>
|
||||
|
||||
9
doc/_themes/sphinx_rtd_theme/footer.html
vendored
9
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,6 +28,9 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{%- 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>
|
||||
|
||||
|
||||
27
doc/_themes/sphinx_rtd_theme/layout.html
vendored
27
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&subset=latin,cyrillic' 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,14 +87,27 @@
|
||||
<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">
|
||||
{% block menu %}
|
||||
{% set toctree = toctree(maxdepth=2, collapse=False, includehidden=True) %}
|
||||
{% set toctree = toctree(maxdepth=4, collapse=False, includehidden=True) %}
|
||||
{% if toctree %}
|
||||
{{ toctree }}
|
||||
{% else %}
|
||||
|
||||
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,33 +0,0 @@
|
||||
API Reference
|
||||
=============
|
||||
|
||||
Includes
|
||||
--------
|
||||
|
||||
To use the public APIs, include ``nghttp2/nghttp2.h``::
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
The header files are also available online: :doc:`nghttp2.h` and
|
||||
:doc:`nghttp2ver.h`.
|
||||
|
||||
Remarks
|
||||
-------
|
||||
|
||||
Do not call `nghttp2_session_send()`, `nghttp2_session_mem_send()`,
|
||||
`nghttp2_session_recv()` or `nghttp2_session_mem_recv()` from the
|
||||
nghttp2 callback functions directly or indirectly. It will lead to the
|
||||
crash. You can submit requests or frames in the callbacks then call
|
||||
these functions outside the callbacks.
|
||||
|
||||
Currently, `nghttp2_session_send()` and `nghttp2_session_mem_send()`
|
||||
do not send client connection preface
|
||||
(:macro:`NGHTTP2_CLIENT_CONNECTION_PREFACE`). The applications are
|
||||
responsible to send it before sending any HTTP/2 frames using these
|
||||
functions if :type:`nghttp2_session` is configured as client.
|
||||
Similarly, `nghttp2_session_recv()` and `nghttp2_session_mem_recv()`
|
||||
do not consume client connection preface unless
|
||||
`nghttp2_option_set_recv_client_preface()` is used with nonzero option
|
||||
value. The applications are responsible to receive it before calling
|
||||
these functions if :type:`nghttp2_session` is configured as server and
|
||||
`nghttp2_option_set_recv_client_preface()` is not used.
|
||||
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
|
||||
235
doc/h2load.1
235
doc/h2load.1
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "H2LOAD" "1" "January 25, 2015" "0.7.3" "nghttp2"
|
||||
.TH "H2LOAD" "1" "August 30, 2015" "1.3.0" "nghttp2"
|
||||
.SH NAME
|
||||
h2load \- HTTP/2 benchmarking tool
|
||||
.
|
||||
@@ -44,9 +44,10 @@ 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.
|
||||
are used solely. Definition of a base URI overrides all
|
||||
scheme, host or port values.
|
||||
.UNINDENT
|
||||
.SH OPTIONS:
|
||||
.SH OPTIONS
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-n, \-\-requests=<N>
|
||||
@@ -70,15 +71,16 @@ Default: \fB1\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-i, \-\-input\-file=<FILE>
|
||||
Path of a file with multiple URIs are seperated by EOLs.
|
||||
.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 <FILE>, URIs will be read from stdin.
|
||||
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.
|
||||
are used solely. Definition of a base URI overrides all
|
||||
scheme, host or port values.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -93,6 +95,8 @@ Default: \fBauto\fP
|
||||
.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
|
||||
@@ -101,6 +105,8 @@ 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
|
||||
@@ -109,12 +115,97 @@ 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\-14
|
||||
Available protocols: spdy/2, spdy/3, spdy/3.1 and h2c
|
||||
.sp
|
||||
Default: \fBh2c\-14\fP
|
||||
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
|
||||
@@ -131,6 +222,132 @@ Display version information and exit.
|
||||
.B \-h, \-\-help
|
||||
Display this help and exit.
|
||||
.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
|
||||
|
||||
198
doc/h2load.1.rst
198
doc/h2load.1.rst
@@ -1,4 +1,8 @@
|
||||
|
||||
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
|
||||
|
||||
.. program:: h2load
|
||||
|
||||
h2load(1)
|
||||
=========
|
||||
|
||||
@@ -19,10 +23,11 @@ benchmarking tool for HTTP/2 and SPDY server
|
||||
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.
|
||||
are used solely. Definition of a base URI overrides all
|
||||
scheme, host or port values.
|
||||
|
||||
OPTIONS:
|
||||
--------
|
||||
OPTIONS
|
||||
-------
|
||||
|
||||
.. option:: -n, --requests=<N>
|
||||
|
||||
@@ -42,16 +47,17 @@ OPTIONS:
|
||||
|
||||
Default: ``1``
|
||||
|
||||
.. option:: -i, --input-file=<FILE>
|
||||
.. option:: -i, --input-file=<PATH>
|
||||
|
||||
Path of a file with multiple URIs are seperated by EOLs.
|
||||
Path of a file with multiple URIs are separated by EOLs.
|
||||
This option will disable URIs getting from command-line.
|
||||
If '-' is given as <FILE>, URIs will be read from stdin.
|
||||
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.
|
||||
are used solely. Definition of a base URI overrides all
|
||||
scheme, host or port values.
|
||||
|
||||
.. option:: -m, --max-concurrent-streams=(auto|<N>)
|
||||
|
||||
@@ -65,6 +71,8 @@ OPTIONS:
|
||||
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
|
||||
@@ -72,17 +80,96 @@ OPTIONS:
|
||||
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
|
||||
Available protocols: spdy/2, spdy/3, spdy/3.1 and h2c
|
||||
|
||||
Default: ``h2c-14``
|
||||
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
|
||||
|
||||
@@ -96,6 +183,99 @@ OPTIONS:
|
||||
|
||||
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
|
||||
--------
|
||||
|
||||
|
||||
@@ -1,3 +1,96 @@
|
||||
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
|
||||
--------
|
||||
|
||||
|
||||
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)
|
||||
|
||||
104
doc/nghttp.1
104
doc/nghttp.1
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTP" "1" "January 25, 2015" "0.7.3" "nghttp2"
|
||||
.TH "NGHTTP" "1" "August 30, 2015" "1.3.0" "nghttp2"
|
||||
.SH NAME
|
||||
nghttp \- HTTP/2 experimental client
|
||||
.
|
||||
@@ -41,7 +41,7 @@ HTTP/2 experimental client
|
||||
.B <URI>
|
||||
Specify URI to access.
|
||||
.UNINDENT
|
||||
.SH OPTIONS:
|
||||
.SH OPTIONS
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-v, \-\-verbose
|
||||
@@ -64,8 +64,9 @@ yet.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-t, \-\-timeout=<SEC>
|
||||
Timeout each request after <SEC> seconds.
|
||||
.B \-t, \-\-timeout=<DURATION>
|
||||
Timeout each request after <DURATION>. Set 0 to disable
|
||||
timeout.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -101,6 +102,14 @@ Add a header to the requests. Example: \fI\%\-H\fP\(aq:method: PUT\(aq
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.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
|
||||
.B \-\-cert=<CERT>
|
||||
Use the specified client certificate file. The file
|
||||
must be in PEM format.
|
||||
@@ -113,7 +122,7 @@ PEM format.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-d, \-\-data=<FILE>
|
||||
.B \-d, \-\-data=<PATH>
|
||||
Post FILE to server. If \(aq\-\(aq is given, data will be read
|
||||
from stdin.
|
||||
.UNINDENT
|
||||
@@ -127,7 +136,7 @@ requested twice. This option disables it too.
|
||||
.TP
|
||||
.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
|
||||
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
|
||||
@@ -158,8 +167,8 @@ Specify 0 to disable padding.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-r, \-\-har=<FILE>
|
||||
Output HTTP transactions <FILE> in HAR format. If \(aq\-\(aq
|
||||
.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
|
||||
@@ -184,8 +193,21 @@ Don\(aqt send dependency based priority hint to server.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-dep\-idle
|
||||
Use idle streams as anchor nodes to express priority.
|
||||
.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
|
||||
@@ -200,6 +222,68 @@ Display this help and exit.
|
||||
.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
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
|
||||
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
|
||||
|
||||
.. program:: nghttp
|
||||
|
||||
nghttp(1)
|
||||
=========
|
||||
|
||||
@@ -16,8 +20,8 @@ HTTP/2 experimental client
|
||||
|
||||
Specify URI to access.
|
||||
|
||||
OPTIONS:
|
||||
--------
|
||||
OPTIONS
|
||||
-------
|
||||
|
||||
.. option:: -v, --verbose
|
||||
|
||||
@@ -36,9 +40,10 @@ OPTIONS:
|
||||
'index.html' is used as a filename. Not implemented
|
||||
yet.
|
||||
|
||||
.. option:: -t, --timeout=<SEC>
|
||||
.. option:: -t, --timeout=<DURATION>
|
||||
|
||||
Timeout each request after <SEC> seconds.
|
||||
Timeout each request after <DURATION>. Set 0 to disable
|
||||
timeout.
|
||||
|
||||
.. option:: -w, --window-bits=<N>
|
||||
|
||||
@@ -67,6 +72,13 @@ OPTIONS:
|
||||
|
||||
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
|
||||
@@ -77,7 +89,7 @@ OPTIONS:
|
||||
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.
|
||||
@@ -115,9 +127,9 @@ OPTIONS:
|
||||
Add at most <N> bytes to a frame payload as padding.
|
||||
Specify 0 to disable padding.
|
||||
|
||||
.. option:: -r, --har=<FILE>
|
||||
.. option:: -r, --har=<PATH>
|
||||
|
||||
Output HTTP transactions <FILE> in HAR format. If '-'
|
||||
Output HTTP transactions <PATH> in HAR format. If '-'
|
||||
is given, data is written to stdout.
|
||||
|
||||
.. option:: --color
|
||||
@@ -136,9 +148,20 @@ OPTIONS:
|
||||
|
||||
Don't send dependency based priority hint to server.
|
||||
|
||||
.. option:: --dep-idle
|
||||
.. option:: --hexdump
|
||||
|
||||
Use idle streams as anchor nodes to express priority.
|
||||
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
|
||||
|
||||
@@ -149,9 +172,66 @@ OPTIONS:
|
||||
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
|
||||
--------
|
||||
|
||||
|
||||
@@ -1,3 +1,54 @@
|
||||
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
|
||||
--------
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTPD" "1" "January 25, 2015" "0.7.3" "nghttp2"
|
||||
.TH "NGHTTPD" "1" "August 30, 2015" "1.3.0" "nghttp2"
|
||||
.SH NAME
|
||||
nghttpd \- HTTP/2 experimental server
|
||||
.
|
||||
@@ -53,11 +53,17 @@ Set path to server\(aqs private key. Required unless
|
||||
Set path to server\(aqs certificate. Required unless
|
||||
\fI\%\-\-no\-tls\fP is specified.
|
||||
.UNINDENT
|
||||
.SH OPTIONS:
|
||||
.SH OPTIONS
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.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
|
||||
.B \-D, \-\-daemon
|
||||
Run in a background. If \fI\-D\fP is used, the current working
|
||||
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
|
||||
@@ -103,7 +109,7 @@ 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
|
||||
Example: \fI\%\-p\fP/=/foo.png \fI\%\-p\fP/doc=/bar.css
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -113,6 +119,14 @@ Specify 0 to disable padding.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.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.
|
||||
.sp
|
||||
@@ -138,6 +152,26 @@ rather than complete request is received.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-trailer=<HEADER>
|
||||
Add a trailer header to a response. <HEADER> must not
|
||||
include pseudo header field (header field name starting
|
||||
with \(aq:\(aq). The trailer is sent only if a response has
|
||||
body part. Example: \fI\%\-\-trailer\fP \(aqfoo: bar\(aq.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-hexdump
|
||||
Display the incoming traffic in hexadecimal (Canonical
|
||||
hex+ASCII display). If SSL/TLS is used, decrypted data
|
||||
are used.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-echo\-upload
|
||||
Send back uploaded content if method is POST or PUT.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-version
|
||||
Display version information and exit.
|
||||
.UNINDENT
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
|
||||
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
|
||||
|
||||
.. program:: nghttpd
|
||||
|
||||
nghttpd(1)
|
||||
==========
|
||||
|
||||
@@ -27,8 +31,13 @@ HTTP/2 experimental server
|
||||
Set path to server's certificate. Required unless
|
||||
:option:`--no-tls` is specified.
|
||||
|
||||
OPTIONS:
|
||||
--------
|
||||
OPTIONS
|
||||
-------
|
||||
|
||||
.. option:: -a, --address=<ADDR>
|
||||
|
||||
The address to bind to. If not specified the default IP
|
||||
address determined by getaddrinfo is used.
|
||||
|
||||
.. option:: -D, --daemon
|
||||
|
||||
@@ -78,6 +87,13 @@ OPTIONS:
|
||||
Add at most <N> bytes to a frame payload as padding.
|
||||
Specify 0 to disable padding.
|
||||
|
||||
.. option:: -m, --max-concurrent-streams=<N>
|
||||
|
||||
Set the maximum number of the concurrent streams in one
|
||||
HTTP/2 session.
|
||||
|
||||
Default: ``100``
|
||||
|
||||
.. option:: -n, --workers=<N>
|
||||
|
||||
Set the number of worker threads.
|
||||
@@ -99,6 +115,23 @@ OPTIONS:
|
||||
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.
|
||||
@@ -108,6 +141,7 @@ OPTIONS:
|
||||
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).
|
||||
|
||||
|
||||
469
doc/nghttpx.1
469
doc/nghttpx.1
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTPX" "1" "January 25, 2015" "0.7.3" "nghttp2"
|
||||
.TH "NGHTTPX" "1" "August 30, 2015" "1.3.0" "nghttp2"
|
||||
.SH NAME
|
||||
nghttpx \- HTTP/2 experimental proxy
|
||||
.
|
||||
@@ -46,28 +46,82 @@ Set path to server\(aqs private key. Required unless \fI\%\-p\fP,
|
||||
.TP
|
||||
.B <CERT>
|
||||
Set path to server\(aqs certificate. Required unless \fI\%\-p\fP,
|
||||
\fI\%\-\-client\fP or \fI\%\-\-frontend\-no\-tls\fP are given.
|
||||
\fI\%\-\-client\fP or \fI\%\-\-frontend\-no\-tls\fP are given. To make OCSP
|
||||
stapling work, this must be absolute path.
|
||||
.UNINDENT
|
||||
.SH OPTIONS:
|
||||
.SH OPTIONS
|
||||
.sp
|
||||
The options are categorized into several groups.
|
||||
.SS Connections:
|
||||
.SS Connections
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-b, \-\-backend=<HOST,PORT>
|
||||
Set backend host and port. For HTTP/1 backend, multiple
|
||||
backend addresses are accepted by repeating this option.
|
||||
HTTP/2 backend does not support multiple backend
|
||||
addresses and the first occurrence of this option is
|
||||
used.
|
||||
.B \-b, \-\-backend=(<HOST>,<PORT>|unix:<PATH>)[;<PATTERN>[:...]]
|
||||
Set backend host and port. The multiple backend
|
||||
addresses are accepted by repeating this option. UNIX
|
||||
domain socket can be specified by prefixing path name
|
||||
with "unix:" (e.g., unix:/var/run/backend.sock).
|
||||
.sp
|
||||
Optionally, if <PATTERN>s are given, the backend address
|
||||
is only used if request matches the pattern. If \fI\%\-s\fP or
|
||||
\fI\%\-p\fP is used, <PATTERN>s are ignored. The pattern
|
||||
matching is closely designed to ServeMux in net/http
|
||||
package of Go programming language. <PATTERN> consists
|
||||
of path, host + path or just host. The path must start
|
||||
with "\fI/\fP". If it ends with "\fI/\fP", it matches all request
|
||||
path in its subtree. To deal with the request to the
|
||||
directory without trailing slash, the path which ends
|
||||
with "\fI/\fP" also matches the request path which only lacks
|
||||
trailing \(aq\fI/\fP\(aq (e.g., path "\fI/foo/\fP" matches request path
|
||||
"\fI/foo\fP"). If it does not end with "\fI/\fP", it performs exact
|
||||
match against the request path. If host is given, it
|
||||
performs exact match against the request host. If host
|
||||
alone is given, "\fI/\fP" is appended to it, so that it
|
||||
matches all request paths under the host (e.g.,
|
||||
specifying "nghttp2.org" equals to "nghttp2.org/").
|
||||
.sp
|
||||
Patterns with host take precedence over patterns with
|
||||
just path. Then, longer patterns take precedence over
|
||||
shorter ones, breaking a tie by the order of the
|
||||
appearance in the configuration.
|
||||
.sp
|
||||
If <PATTERN> is omitted, "\fI/\fP" is used as pattern, which
|
||||
matches all request paths (catch\-all pattern). The
|
||||
catch\-all backend must be given.
|
||||
.sp
|
||||
When doing a match, nghttpx made some normalization to
|
||||
pattern, request host and path. For host part, they are
|
||||
converted to lower case. For path part, percent\-encoded
|
||||
unreserved characters defined in RFC 3986 are decoded,
|
||||
and any dot\-segments (".." and ".") are resolved and
|
||||
removed.
|
||||
.sp
|
||||
For example, \fI\%\-b\fP\(aq127.0.0.1,8080;nghttp2.org/httpbin/\(aq
|
||||
matches the request host "nghttp2.org" and the request
|
||||
path "\fI/httpbin/get\fP", but does not match the request host
|
||||
"nghttp2.org" and the request path "\fI/index.html\fP".
|
||||
.sp
|
||||
The multiple <PATTERN>s can be specified, delimiting
|
||||
them by ":". Specifying
|
||||
\fI\%\-b\fP\(aq127.0.0.1,8080;nghttp2.org:www.nghttp2.org\(aq has the
|
||||
same effect to specify \fI\%\-b\fP\(aq127.0.0.1,8080;nghttp2.org\(aq
|
||||
and \fI\%\-b\fP\(aq127.0.0.1,8080;www.nghttp2.org\(aq.
|
||||
.sp
|
||||
The backend addresses sharing same <PATTERN> are grouped
|
||||
together forming load balancing group.
|
||||
.sp
|
||||
Since ";" and ":" are used as delimiter, <PATTERN> must
|
||||
not contain these characters. Since ";" has special
|
||||
meaning in shell, the option value must be quoted.
|
||||
.sp
|
||||
Default: \fB127.0.0.1,80\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-f, \-\-frontend=<HOST,PORT>
|
||||
.B \-f, \-\-frontend=(<HOST>,<PORT>|unix:<PATH>)
|
||||
Set frontend host and port. If <HOST> is \(aq*\(aq, it
|
||||
assumes all addresses including both IPv4 and IPv6.
|
||||
assumes all addresses including both IPv4 and IPv6.
|
||||
UNIX domain socket can be specified by prefixing path
|
||||
name with "unix:" (e.g., unix:/var/run/nghttpx.sock)
|
||||
.sp
|
||||
Default: \fB*,3000\fP
|
||||
.UNINDENT
|
||||
@@ -104,7 +158,7 @@ timeouts when connecting and making CONNECT request can
|
||||
be specified by \fI\%\-\-backend\-read\-timeout\fP and
|
||||
\fI\%\-\-backend\-write\-timeout\fP options.
|
||||
.UNINDENT
|
||||
.SS Performance:
|
||||
.SS Performance
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-n, \-\-workers=<N>
|
||||
@@ -192,11 +246,25 @@ Default: \fB0\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-backend\-http2\-connections\-per\-worker=<N>
|
||||
Set maximum number of backend HTTP/2 physical
|
||||
connections per worker. If pattern is used in \fI\%\-b\fP
|
||||
option, this limit is applied to each pattern group (in
|
||||
other words, each pattern group can have maximum <N>
|
||||
HTTP/2 connections). The default value is 0, which
|
||||
means that the value is adjusted to the number of
|
||||
backend addresses. If pattern is used, this adjustment
|
||||
is done for each pattern group.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-backend\-http1\-connections\-per\-host=<N>
|
||||
Set maximum number of backend concurrent HTTP/1
|
||||
connections per host. This option is meaningful when \fI\%\-s\fP
|
||||
option is used. To limit the number of connections per
|
||||
frontend for default mode, use
|
||||
connections per origin host. This option is meaningful
|
||||
when \fI\%\-s\fP option is used. The origin host is determined
|
||||
by authority portion of requset URI (or :authority
|
||||
header field for HTTP/2). To limit the number of
|
||||
connections per frontend for default mode, use
|
||||
\fI\%\-\-backend\-http1\-connections\-per\-frontend\fP\&.
|
||||
.sp
|
||||
Default: \fB8\fP
|
||||
@@ -234,32 +302,32 @@ Set buffer size used to store backend response.
|
||||
.sp
|
||||
Default: \fB16K\fP
|
||||
.UNINDENT
|
||||
.SS Timeout:
|
||||
.SS Timeout
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-frontend\-http2\-read\-timeout=<SEC>
|
||||
.B \-\-frontend\-http2\-read\-timeout=<DURATION>
|
||||
Specify read timeout for HTTP/2 and SPDY frontend
|
||||
connection.
|
||||
.sp
|
||||
Default: \fB180\fP
|
||||
Default: \fB3m\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-frontend\-read\-timeout=<SEC>
|
||||
.B \-\-frontend\-read\-timeout=<DURATION>
|
||||
Specify read timeout for HTTP/1.1 frontend connection.
|
||||
.sp
|
||||
Default: \fB180\fP
|
||||
Default: \fB3m\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-frontend\-write\-timeout=<SEC>
|
||||
.B \-\-frontend\-write\-timeout=<DURATION>
|
||||
Specify write timeout for all frontend connections.
|
||||
.sp
|
||||
Default: \fB30\fP
|
||||
Default: \fB30s\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-stream\-read\-timeout=<SEC>
|
||||
.B \-\-stream\-read\-timeout=<DURATION>
|
||||
Specify read timeout for HTTP/2 and SPDY streams. 0
|
||||
means no timeout.
|
||||
.sp
|
||||
@@ -267,7 +335,7 @@ Default: \fB0\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-stream\-write\-timeout=<SEC>
|
||||
.B \-\-stream\-write\-timeout=<DURATION>
|
||||
Specify write timeout for HTTP/2 and SPDY streams. 0
|
||||
means no timeout.
|
||||
.sp
|
||||
@@ -275,35 +343,35 @@ Default: \fB0\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-backend\-read\-timeout=<SEC>
|
||||
.B \-\-backend\-read\-timeout=<DURATION>
|
||||
Specify read timeout for backend connection.
|
||||
.sp
|
||||
Default: \fB180\fP
|
||||
Default: \fB3m\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-backend\-write\-timeout=<SEC>
|
||||
.B \-\-backend\-write\-timeout=<DURATION>
|
||||
Specify write timeout for backend connection.
|
||||
.sp
|
||||
Default: \fB30\fP
|
||||
Default: \fB30s\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-backend\-keep\-alive\-timeout=<SEC>
|
||||
.B \-\-backend\-keep\-alive\-timeout=<DURATION>
|
||||
Specify keep\-alive timeout for backend connection.
|
||||
.sp
|
||||
Default: \fB600\fP
|
||||
Default: \fB2s\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-listener\-disable\-timeout=<SEC>
|
||||
.B \-\-listener\-disable\-timeout=<DURATION>
|
||||
After accepting connection failed, connection listener
|
||||
is disabled for a given time in seconds. Specifying 0
|
||||
is disabled for a given amount of time. Specifying 0
|
||||
disables this feature.
|
||||
.sp
|
||||
Default: \fB0\fP
|
||||
.UNINDENT
|
||||
.SS SSL/TLS:
|
||||
.SS SSL/TLS
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-ciphers=<SUITE>
|
||||
@@ -340,7 +408,8 @@ password protected it\(aqll be requested interactively.
|
||||
Specify additional certificate and private key file.
|
||||
nghttpx will choose certificates based on the hostname
|
||||
indicated by client using TLS SNI extension. This
|
||||
option can be used multiple times.
|
||||
option can be used multiple times. To make OCSP
|
||||
stapling work, <CERTPATH> must be absolute path.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -365,7 +434,7 @@ NPN. The parameter must be delimited by a single comma
|
||||
only and any white spaces are treated as a part of
|
||||
protocol string.
|
||||
.sp
|
||||
Default: \fBh2\-16,h2\-14,spdy/3.1,http/1.1\fP
|
||||
Default: \fBh2,h2\-16,h2\-14,spdy/3.1,http/1.1\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -406,35 +475,104 @@ Default: \fBTLSv1.2,TLSv1.1\fP
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-tls\-ticket\-key\-file=<PATH>
|
||||
Path to file that contains 48 bytes random data to
|
||||
construct TLS session ticket parameters. This options
|
||||
can be used repeatedly to specify multiple ticket
|
||||
parameters. If several files are given, only the first
|
||||
key is used to encrypt TLS session tickets. Other keys
|
||||
are accepted but server will issue new session ticket
|
||||
with first key. This allows session key rotation.
|
||||
Please note that key rotation does not occur
|
||||
automatically. User should rearrange files or change
|
||||
options values and restart nghttpx gracefully. If
|
||||
opening or reading given file fails, all loaded keys are
|
||||
discarded and it is treated as if none of this option is
|
||||
given. If this option is not given or an error occurred
|
||||
while opening or reading a file, key is generated
|
||||
automatically and renewed every 12hrs. At most 2 keys
|
||||
are stored in memory.
|
||||
Path to file that contains random data to construct TLS
|
||||
session ticket parameters. If aes\-128\-cbc is given in
|
||||
\fI\%\-\-tls\-ticket\-key\-cipher\fP, the file must contain exactly
|
||||
48 bytes. If aes\-256\-cbc is given in
|
||||
\fI\%\-\-tls\-ticket\-key\-cipher\fP, the file must contain exactly
|
||||
80 bytes. This options can be used repeatedly to
|
||||
specify multiple ticket parameters. If several files
|
||||
are given, only the first key is used to encrypt TLS
|
||||
session tickets. Other keys are accepted but server
|
||||
will issue new session ticket with first key. This
|
||||
allows session key rotation. Please note that key
|
||||
rotation does not occur automatically. User should
|
||||
rearrange files or change options values and restart
|
||||
nghttpx gracefully. If opening or reading given file
|
||||
fails, all loaded keys are discarded and it is treated
|
||||
as if none of this option is given. If this option is
|
||||
not given or an error occurred while opening or reading
|
||||
a file, key is generated every 1 hour internally and
|
||||
they are valid for 12 hours. This is recommended if
|
||||
ticket key sharing between nghttpx instances is not
|
||||
required.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-tls\-ctx\-per\-worker
|
||||
Create OpenSSL\(aqs SSL_CTX per worker, so that no internal
|
||||
locking is required. This may improve scalability with
|
||||
multi threaded configuration. If this option is
|
||||
enabled, session ID is no longer shared accross SSL_CTX
|
||||
objects, which means session ID generated by one worker
|
||||
is not acceptable by another worker. On the other hand,
|
||||
session ticket key is shared across all worker threads.
|
||||
.B \-\-tls\-ticket\-key\-memcached=<HOST>,<PORT>
|
||||
Specify address of memcached server to store session
|
||||
cache. This enables shared TLS ticket key between
|
||||
multiple nghttpx instances. nghttpx does not set TLS
|
||||
ticket key to memcached. The external ticket key
|
||||
generator is required. nghttpx just gets TLS ticket
|
||||
keys from memcached, and use them, possibly replacing
|
||||
current set of keys. It is up to extern TLS ticket key
|
||||
generator to rotate keys frequently. See "TLS SESSION
|
||||
TICKET RESUMPTION" section in manual page to know the
|
||||
data format in memcached entry.
|
||||
.UNINDENT
|
||||
.SS HTTP/2 and SPDY:
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-tls\-ticket\-key\-memcached\-interval=<DURATION>
|
||||
Set interval to get TLS ticket keys from memcached.
|
||||
.sp
|
||||
Default: \fB10m\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-tls\-ticket\-key\-memcached\-max\-retry=<N>
|
||||
Set maximum number of consecutive retries before
|
||||
abandoning TLS ticket key retrieval. If this number is
|
||||
reached, the attempt is considered as failure, and
|
||||
"failure" count is incremented by 1, which contributed
|
||||
to the value controlled
|
||||
\fI\%\-\-tls\-ticket\-key\-memcached\-max\-fail\fP option.
|
||||
.sp
|
||||
Default: \fB3\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-tls\-ticket\-key\-memcached\-max\-fail=<N>
|
||||
Set maximum number of consecutive failure before
|
||||
disabling TLS ticket until next scheduled key retrieval.
|
||||
.sp
|
||||
Default: \fB2\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-tls\-ticket\-key\-cipher=<CIPHER>
|
||||
Specify cipher to encrypt TLS session ticket. Specify
|
||||
either aes\-128\-cbc or aes\-256\-cbc. By default,
|
||||
aes\-128\-cbc is used.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-fetch\-ocsp\-response\-file=<PATH>
|
||||
Path to fetch\-ocsp\-response script file. It should be
|
||||
absolute path.
|
||||
.sp
|
||||
Default: \fB/usr/local/share/nghttp2/fetch\-ocsp\-response\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-ocsp\-update\-interval=<DURATION>
|
||||
Set interval to update OCSP response cache.
|
||||
.sp
|
||||
Default: \fB4h\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-no\-ocsp
|
||||
Disable OCSP stapling.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-tls\-session\-cache\-memcached=<HOST>,<PORT>
|
||||
Specify address of memcached server to store session
|
||||
cache. This enables shared session cache between
|
||||
multiple nghttpx instances.
|
||||
.UNINDENT
|
||||
.SS HTTP/2 and SPDY
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-c, \-\-http2\-max\-concurrent\-streams=<N>
|
||||
@@ -500,7 +638,14 @@ padding. Specify 0 to disable padding. This option is
|
||||
meant for debugging purpose and not intended to enhance
|
||||
protocol security.
|
||||
.UNINDENT
|
||||
.SS Mode:
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-no\-server\-push
|
||||
Disable HTTP/2 server push. Server push is only
|
||||
supported by default mode and HTTP/2 frontend. SPDY
|
||||
frontend does not support server push.
|
||||
.UNINDENT
|
||||
.SS Mode
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B (default mode)
|
||||
@@ -541,7 +686,7 @@ Like \fI\%\-\-client\fP option, but it also requires the request
|
||||
path from frontend must be an absolute URI, suitable for
|
||||
use as a forward proxy.
|
||||
.UNINDENT
|
||||
.SS Logging:
|
||||
.SS Logging
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-L, \-\-log\-level=<LEVEL>
|
||||
@@ -598,15 +743,28 @@ $pid: PID of the running process.
|
||||
$alpn: ALPN identifier of the protocol which generates
|
||||
the response. For HTTP/1, ALPN is always http/1.1,
|
||||
regardless of minor version.
|
||||
.IP \(bu 2
|
||||
$ssl_cipher: cipher used for SSL/TLS connection.
|
||||
.IP \(bu 2
|
||||
$ssl_protocol: protocol for SSL/TLS connection.
|
||||
.IP \(bu 2
|
||||
$ssl_session_id: session ID for SSL/TLS connection.
|
||||
.IP \(bu 2
|
||||
$ssl_session_reused: "r" if SSL/TLS session was
|
||||
reused. Otherwise, "."
|
||||
.UNINDENT
|
||||
.sp
|
||||
The variable can be enclosed by "{" and "}" for
|
||||
disambiguation (e.g., ${remote_addr}).
|
||||
.sp
|
||||
Default: \fB$remote_addr \- \- [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-errorlog\-file=<PATH>
|
||||
Set path to write error log. To reopen file, send USR1
|
||||
signal to nghttpx.
|
||||
signal to nghttpx. stderr will be redirected to the
|
||||
error log file unless \fI\%\-\-errorlog\-syslog\fP is used.
|
||||
.sp
|
||||
Default: \fB/dev/stderr\fP
|
||||
.UNINDENT
|
||||
@@ -623,7 +781,7 @@ Set syslog facility to <FACILITY>.
|
||||
.sp
|
||||
Default: \fBdaemon\fP
|
||||
.UNINDENT
|
||||
.SS HTTP:
|
||||
.SS HTTP
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-add\-x\-forwarded\-for
|
||||
@@ -652,13 +810,30 @@ altered regardless of this option.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-host\-rewrite
|
||||
Rewrite host and :authority header fields on
|
||||
\fI\%\-\-http2\-bridge\fP, \fI\%\-\-client\fP and default mode. For
|
||||
\fI\%\-\-http2\-proxy\fP and \fI\%\-\-client\-proxy\fP mode, these headers
|
||||
will not be altered regardless of this option.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-altsvc=<PROTOID,PORT[,HOST,[ORIGIN]]>
|
||||
Specify protocol ID, port, host and origin of
|
||||
alternative service. <HOST> and <ORIGIN> are optional.
|
||||
They are advertised in alt\-svc header field or HTTP/2
|
||||
ALTSVC frame. This option can be used multiple times to
|
||||
specify multiple alternative services. Example:
|
||||
\fI\%\-\-altsvc\fP=h2,443
|
||||
They are advertised in alt\-svc header field only in
|
||||
HTTP/1.1 frontend. This option can be used multiple
|
||||
times to specify multiple alternative services.
|
||||
Example: \fI\%\-\-altsvc\fP=h2,443
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-add\-request\-header=<HEADER>
|
||||
Specify additional header field to add to request header
|
||||
set. This option just appends header field and won\(aqt
|
||||
replace anything already set. This option can be used
|
||||
several times to specify multiple header fields.
|
||||
Example: \fI\%\-\-add\-request\-header\fP="foo: bar"
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -669,7 +844,24 @@ won\(aqt replace anything already set. This option can be
|
||||
used several times to specify multiple header fields.
|
||||
Example: \fI\%\-\-add\-response\-header\fP="foo: bar"
|
||||
.UNINDENT
|
||||
.SS Debug:
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-header\-field\-buffer=<SIZE>
|
||||
Set maximum buffer size for incoming HTTP header field
|
||||
list. This is the sum of header name and value in
|
||||
bytes.
|
||||
.sp
|
||||
Default: \fB64K\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-max\-header\-fields=<N>
|
||||
Set maximum number of incoming HTTP header fields, which
|
||||
appear in one request or response header field list.
|
||||
.sp
|
||||
Default: \fB100\fP
|
||||
.UNINDENT
|
||||
.SS Debug
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-frontend\-http2\-dump\-request\-header=<PATH>
|
||||
@@ -695,7 +887,7 @@ Print HTTP/2 frames in frontend to stderr. This option
|
||||
is not thread safe and MUST NOT be used with option
|
||||
\fI\%\-n\fP=N, where N >= 2.
|
||||
.UNINDENT
|
||||
.SS Process:
|
||||
.SS Process
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-D, \-\-daemon
|
||||
@@ -713,7 +905,7 @@ Set path to save PID of this program.
|
||||
Run this program as <USER>. This option is intended to
|
||||
be used to drop root privileges.
|
||||
.UNINDENT
|
||||
.SS Misc:
|
||||
.SS Misc
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-conf=<PATH>
|
||||
@@ -723,6 +915,14 @@ Default: \fB/etc/nghttpx/nghttpx.conf\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-include=<PATH>
|
||||
Load additional configurations from <PATH>. File <PATH>
|
||||
is read when configuration parser encountered this
|
||||
option. This option can be used multiple times, or even
|
||||
recursively.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-v, \-\-version
|
||||
Print version and exit.
|
||||
.UNINDENT
|
||||
@@ -734,6 +934,11 @@ Print this help and exit.
|
||||
.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 FILES
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -755,7 +960,7 @@ argument in the configuration file. Specify \fByes\fP as an argument
|
||||
ignored.
|
||||
.sp
|
||||
To specify private key and certificate file which are given as
|
||||
positional arguments in commnad\-line, use \fBprivate\-key\-file\fP and
|
||||
positional arguments in command\-line, use \fBprivate\-key\-file\fP and
|
||||
\fBcertificate\-file\fP\&.
|
||||
.sp
|
||||
\fI\%\-\-conf\fP option cannot be used in the configuration file and
|
||||
@@ -778,6 +983,124 @@ path with same command\-line arguments and environment variables.
|
||||
After new process comes up, sending SIGQUIT to the original process
|
||||
to perform hot swapping.
|
||||
.UNINDENT
|
||||
.SH SERVER PUSH
|
||||
.sp
|
||||
nghttpx supports HTTP/2 server push in default mode. nghttpx looks
|
||||
for Link header field (\fI\%RFC 5988\fP) in response headers from
|
||||
backend server and extracts URI\-reference with parameter
|
||||
\fBrel=preload\fP (see \fI\%preload\fP)
|
||||
and pushes those URIs to the frontend client. Here is a sample Link
|
||||
header field to initiate server push:
|
||||
.INDENT 0.0
|
||||
.INDENT 3.5
|
||||
.sp
|
||||
.nf
|
||||
.ft C
|
||||
Link: </fonts/font.woff>; rel=preload
|
||||
Link: </css/theme.css>; rel=preload
|
||||
.ft P
|
||||
.fi
|
||||
.UNINDENT
|
||||
.UNINDENT
|
||||
.sp
|
||||
Currently, the following restrictions are applied for server push:
|
||||
.INDENT 0.0
|
||||
.IP 1. 3
|
||||
URI\-reference must not contain authority. If it exists, it is not
|
||||
pushed. \fB/fonts/font.woff\fP and \fBcss/theme.css\fP are eligible to
|
||||
be pushed. \fBhttps://example.org/fonts/font.woff\fP and
|
||||
\fB//example.org/css/theme.css\fP are not.
|
||||
.IP 2. 3
|
||||
The associated stream must have method "GET" or "POST". The
|
||||
associated stream\(aqs status code must be 200.
|
||||
.UNINDENT
|
||||
.sp
|
||||
These limitations may be loosened in the future release.
|
||||
.SH UNIX DOMAIN SOCKET
|
||||
.sp
|
||||
nghttpx supports UNIX domain socket with a filename for both frontend
|
||||
and backend connections.
|
||||
.sp
|
||||
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.
|
||||
.SH OCSP STAPLING
|
||||
.sp
|
||||
OCSP query is done using external Python script
|
||||
\fBfetch\-ocsp\-response\fP, which has been originally developed in Perl
|
||||
as part of h2o project (\fI\%https://github.com/h2o/h2o\fP), and was
|
||||
translated into Python.
|
||||
.sp
|
||||
The script file is usually installed under
|
||||
\fB$(prefix)/share/nghttp2/\fP directory. The actual path to script can
|
||||
be customized using \fI\%\-\-fetch\-ocsp\-response\-file\fP option.
|
||||
.sp
|
||||
If OCSP query is failed, previous OCSP response, if any, is continued
|
||||
to be used.
|
||||
.SH TLS SESSION RESUMPTION
|
||||
.sp
|
||||
nghttpx supports TLS session resumption through both session ID and
|
||||
session ticket.
|
||||
.SS SESSION ID RESUMPTION
|
||||
.sp
|
||||
By default, session ID is shared by all worker threads.
|
||||
.sp
|
||||
If \fI\%\-\-tls\-session\-cache\-memcached\fP is given, nghttpx will
|
||||
insert serialized session data to memcached with
|
||||
\fBnghttpx:tls\-session\-cache:\fP + lowercased hex string of session ID
|
||||
as a memcached entry key, with expiry time 12 hours. Session timeout
|
||||
is set to 12 hours.
|
||||
.SS TLS SESSION TICKET RESUMPTION
|
||||
.sp
|
||||
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.
|
||||
.sp
|
||||
If \fI\%\-\-tls\-ticket\-key\-memcached\fP 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 \fBnghttpx:tls\-ticket\-key\fP\&. The data format stored in
|
||||
memcached is the binary format described below:
|
||||
.INDENT 0.0
|
||||
.INDENT 3.5
|
||||
.sp
|
||||
.nf
|
||||
.ft C
|
||||
+\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+
|
||||
| VERSION (4) |LEN (2)|KEY(48 or 80) ...
|
||||
+\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+
|
||||
^ |
|
||||
| |
|
||||
+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+
|
||||
(LEN, KEY) pair can be repeated
|
||||
.ft P
|
||||
.fi
|
||||
.UNINDENT
|
||||
.UNINDENT
|
||||
.sp
|
||||
All numbers in the above figure is bytes. All integer fields are
|
||||
network byte order.
|
||||
.sp
|
||||
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 \fI\%\-\-tls\-ticket\-key\-cipher\fP=aes\-128\-cbc is
|
||||
used, LEN must be 48. If
|
||||
\fI\%\-\-tls\-ticket\-key\-cipher\fP=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.
|
||||
.sp
|
||||
If \fI\%\-\-tls\-ticket\-key\-file\fP 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).
|
||||
.SH SEE ALSO
|
||||
.sp
|
||||
\fInghttp(1)\fP, \fInghttpd(1)\fP, \fIh2load(1)\fP
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
|
||||
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
|
||||
|
||||
.. program:: nghttpx
|
||||
|
||||
nghttpx(1)
|
||||
==========
|
||||
|
||||
@@ -21,31 +25,86 @@ A reverse proxy for HTTP/2, HTTP/1 and SPDY.
|
||||
.. describe:: <CERT>
|
||||
|
||||
Set path to server's certificate. Required unless :option:`-p`\,
|
||||
:option:`--client` or :option:`\--frontend-no-tls` are given.
|
||||
:option:`--client` or :option:`\--frontend-no-tls` are given. To make OCSP
|
||||
stapling work, this must be absolute path.
|
||||
|
||||
|
||||
OPTIONS:
|
||||
--------
|
||||
OPTIONS
|
||||
-------
|
||||
|
||||
The options are categorized into several groups.
|
||||
|
||||
Connections:
|
||||
~~~~~~~~~~~~
|
||||
Connections
|
||||
~~~~~~~~~~~
|
||||
|
||||
.. option:: -b, --backend=<HOST,PORT>
|
||||
.. option:: -b, --backend=(<HOST>,<PORT>|unix:<PATH>)[;<PATTERN>[:...]]
|
||||
|
||||
Set backend host and port. The multiple backend
|
||||
addresses are accepted by repeating this option. UNIX
|
||||
domain socket can be specified by prefixing path name
|
||||
with "unix:" (e.g., unix:/var/run/backend.sock).
|
||||
|
||||
Optionally, if <PATTERN>s are given, the backend address
|
||||
is only used if request matches the pattern. If :option:`-s` or
|
||||
:option:`-p` is used, <PATTERN>s are ignored. The pattern
|
||||
matching is closely designed to ServeMux in net/http
|
||||
package of Go programming language. <PATTERN> consists
|
||||
of path, host + path or just host. The path must start
|
||||
with "*/*". If it ends with "*/*", it matches all request
|
||||
path in its subtree. To deal with the request to the
|
||||
directory without trailing slash, the path which ends
|
||||
with "*/*" also matches the request path which only lacks
|
||||
trailing '*/*' (e.g., path "*/foo/*" matches request path
|
||||
"*/foo*"). If it does not end with "*/*", it performs exact
|
||||
match against the request path. If host is given, it
|
||||
performs exact match against the request host. If host
|
||||
alone is given, "*/*" is appended to it, so that it
|
||||
matches all request paths under the host (e.g.,
|
||||
specifying "nghttp2.org" equals to "nghttp2.org/").
|
||||
|
||||
Patterns with host take precedence over patterns with
|
||||
just path. Then, longer patterns take precedence over
|
||||
shorter ones, breaking a tie by the order of the
|
||||
appearance in the configuration.
|
||||
|
||||
If <PATTERN> is omitted, "*/*" is used as pattern, which
|
||||
matches all request paths (catch-all pattern). The
|
||||
catch-all backend must be given.
|
||||
|
||||
When doing a match, nghttpx made some normalization to
|
||||
pattern, request host and path. For host part, they are
|
||||
converted to lower case. For path part, percent-encoded
|
||||
unreserved characters defined in RFC 3986 are decoded,
|
||||
and any dot-segments (".." and ".") are resolved and
|
||||
removed.
|
||||
|
||||
For example, :option:`-b`\'127.0.0.1,8080;nghttp2.org/httpbin/'
|
||||
matches the request host "nghttp2.org" and the request
|
||||
path "*/httpbin/get*", but does not match the request host
|
||||
"nghttp2.org" and the request path "*/index.html*".
|
||||
|
||||
The multiple <PATTERN>s can be specified, delimiting
|
||||
them by ":". Specifying
|
||||
:option:`-b`\'127.0.0.1,8080;nghttp2.org:www.nghttp2.org' has the
|
||||
same effect to specify :option:`-b`\'127.0.0.1,8080;nghttp2.org'
|
||||
and :option:`-b`\'127.0.0.1,8080;www.nghttp2.org'.
|
||||
|
||||
The backend addresses sharing same <PATTERN> are grouped
|
||||
together forming load balancing group.
|
||||
|
||||
Since ";" and ":" are used as delimiter, <PATTERN> must
|
||||
not contain these characters. Since ";" has special
|
||||
meaning in shell, the option value must be quoted.
|
||||
|
||||
Set backend host and port. For HTTP/1 backend, multiple
|
||||
backend addresses are accepted by repeating this option.
|
||||
HTTP/2 backend does not support multiple backend
|
||||
addresses and the first occurrence of this option is
|
||||
used.
|
||||
|
||||
Default: ``127.0.0.1,80``
|
||||
|
||||
.. option:: -f, --frontend=<HOST,PORT>
|
||||
.. option:: -f, --frontend=(<HOST>,<PORT>|unix:<PATH>)
|
||||
|
||||
Set frontend host and port. If <HOST> is '\*', it
|
||||
assumes all addresses including both IPv4 and IPv6.
|
||||
assumes all addresses including both IPv4 and IPv6.
|
||||
UNIX domain socket can be specified by prefixing path
|
||||
name with "unix:" (e.g., unix:/var/run/nghttpx.sock)
|
||||
|
||||
Default: ``*,3000``
|
||||
|
||||
@@ -79,8 +138,8 @@ Connections:
|
||||
:option:`--backend-write-timeout` options.
|
||||
|
||||
|
||||
Performance:
|
||||
~~~~~~~~~~~~
|
||||
Performance
|
||||
~~~~~~~~~~~
|
||||
|
||||
.. option:: -n, --workers=<N>
|
||||
|
||||
@@ -157,12 +216,25 @@ Performance:
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --backend-http2-connections-per-worker=<N>
|
||||
|
||||
Set maximum number of backend HTTP/2 physical
|
||||
connections per worker. If pattern is used in :option:`-b`
|
||||
option, this limit is applied to each pattern group (in
|
||||
other words, each pattern group can have maximum <N>
|
||||
HTTP/2 connections). The default value is 0, which
|
||||
means that the value is adjusted to the number of
|
||||
backend addresses. If pattern is used, this adjustment
|
||||
is done for each pattern group.
|
||||
|
||||
.. option:: --backend-http1-connections-per-host=<N>
|
||||
|
||||
Set maximum number of backend concurrent HTTP/1
|
||||
connections per host. This option is meaningful when :option:`-s`
|
||||
option is used. To limit the number of connections per
|
||||
frontend for default mode, use
|
||||
connections per origin host. This option is meaningful
|
||||
when :option:`-s` option is used. The origin host is determined
|
||||
by authority portion of requset URI (or :authority
|
||||
header field for HTTP/2). To limit the number of
|
||||
connections per frontend for default mode, use
|
||||
:option:`--backend-http1-connections-per-frontend`\.
|
||||
|
||||
Default: ``8``
|
||||
@@ -197,71 +269,71 @@ Performance:
|
||||
Default: ``16K``
|
||||
|
||||
|
||||
Timeout:
|
||||
~~~~~~~~
|
||||
Timeout
|
||||
~~~~~~~
|
||||
|
||||
.. option:: --frontend-http2-read-timeout=<SEC>
|
||||
.. option:: --frontend-http2-read-timeout=<DURATION>
|
||||
|
||||
Specify read timeout for HTTP/2 and SPDY frontend
|
||||
connection.
|
||||
|
||||
Default: ``180``
|
||||
Default: ``3m``
|
||||
|
||||
.. option:: --frontend-read-timeout=<SEC>
|
||||
.. option:: --frontend-read-timeout=<DURATION>
|
||||
|
||||
Specify read timeout for HTTP/1.1 frontend connection.
|
||||
|
||||
Default: ``180``
|
||||
Default: ``3m``
|
||||
|
||||
.. option:: --frontend-write-timeout=<SEC>
|
||||
.. option:: --frontend-write-timeout=<DURATION>
|
||||
|
||||
Specify write timeout for all frontend connections.
|
||||
|
||||
Default: ``30``
|
||||
Default: ``30s``
|
||||
|
||||
.. option:: --stream-read-timeout=<SEC>
|
||||
.. option:: --stream-read-timeout=<DURATION>
|
||||
|
||||
Specify read timeout for HTTP/2 and SPDY streams. 0
|
||||
means no timeout.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --stream-write-timeout=<SEC>
|
||||
.. option:: --stream-write-timeout=<DURATION>
|
||||
|
||||
Specify write timeout for HTTP/2 and SPDY streams. 0
|
||||
means no timeout.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --backend-read-timeout=<SEC>
|
||||
.. option:: --backend-read-timeout=<DURATION>
|
||||
|
||||
Specify read timeout for backend connection.
|
||||
|
||||
Default: ``180``
|
||||
Default: ``3m``
|
||||
|
||||
.. option:: --backend-write-timeout=<SEC>
|
||||
.. option:: --backend-write-timeout=<DURATION>
|
||||
|
||||
Specify write timeout for backend connection.
|
||||
|
||||
Default: ``30``
|
||||
Default: ``30s``
|
||||
|
||||
.. option:: --backend-keep-alive-timeout=<SEC>
|
||||
.. option:: --backend-keep-alive-timeout=<DURATION>
|
||||
|
||||
Specify keep-alive timeout for backend connection.
|
||||
|
||||
Default: ``600``
|
||||
Default: ``2s``
|
||||
|
||||
.. option:: --listener-disable-timeout=<SEC>
|
||||
.. option:: --listener-disable-timeout=<DURATION>
|
||||
|
||||
After accepting connection failed, connection listener
|
||||
is disabled for a given time in seconds. Specifying 0
|
||||
is disabled for a given amount of time. Specifying 0
|
||||
disables this feature.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
|
||||
SSL/TLS:
|
||||
~~~~~~~~
|
||||
SSL/TLS
|
||||
~~~~~~~
|
||||
|
||||
.. option:: --ciphers=<SUITE>
|
||||
|
||||
@@ -294,7 +366,8 @@ SSL/TLS:
|
||||
Specify additional certificate and private key file.
|
||||
nghttpx will choose certificates based on the hostname
|
||||
indicated by client using TLS SNI extension. This
|
||||
option can be used multiple times.
|
||||
option can be used multiple times. To make OCSP
|
||||
stapling work, <CERTPATH> must be absolute path.
|
||||
|
||||
.. option:: --backend-tls-sni-field=<HOST>
|
||||
|
||||
@@ -316,7 +389,7 @@ SSL/TLS:
|
||||
only and any white spaces are treated as a part of
|
||||
protocol string.
|
||||
|
||||
Default: ``h2-16,h2-14,spdy/3.1,http/1.1``
|
||||
Default: ``h2,h2-16,h2-14,spdy/3.1,http/1.1``
|
||||
|
||||
.. option:: --verify-client
|
||||
|
||||
@@ -351,36 +424,97 @@ SSL/TLS:
|
||||
|
||||
.. option:: --tls-ticket-key-file=<PATH>
|
||||
|
||||
Path to file that contains 48 bytes random data to
|
||||
construct TLS session ticket parameters. This options
|
||||
can be used repeatedly to specify multiple ticket
|
||||
parameters. If several files are given, only the first
|
||||
key is used to encrypt TLS session tickets. Other keys
|
||||
are accepted but server will issue new session ticket
|
||||
with first key. This allows session key rotation.
|
||||
Please note that key rotation does not occur
|
||||
automatically. User should rearrange files or change
|
||||
options values and restart nghttpx gracefully. If
|
||||
opening or reading given file fails, all loaded keys are
|
||||
discarded and it is treated as if none of this option is
|
||||
given. If this option is not given or an error occurred
|
||||
while opening or reading a file, key is generated
|
||||
automatically and renewed every 12hrs. At most 2 keys
|
||||
are stored in memory.
|
||||
Path to file that contains random data to construct TLS
|
||||
session ticket parameters. If aes-128-cbc is given in
|
||||
:option:`--tls-ticket-key-cipher`\, the file must contain exactly
|
||||
48 bytes. If aes-256-cbc is given in
|
||||
:option:`--tls-ticket-key-cipher`\, the file must contain exactly
|
||||
80 bytes. This options can be used repeatedly to
|
||||
specify multiple ticket parameters. If several files
|
||||
are given, only the first key is used to encrypt TLS
|
||||
session tickets. Other keys are accepted but server
|
||||
will issue new session ticket with first key. This
|
||||
allows session key rotation. Please note that key
|
||||
rotation does not occur automatically. User should
|
||||
rearrange files or change options values and restart
|
||||
nghttpx gracefully. If opening or reading given file
|
||||
fails, all loaded keys are discarded and it is treated
|
||||
as if none of this option is given. If this option is
|
||||
not given or an error occurred while opening or reading
|
||||
a file, key is generated every 1 hour internally and
|
||||
they are valid for 12 hours. This is recommended if
|
||||
ticket key sharing between nghttpx instances is not
|
||||
required.
|
||||
|
||||
.. option:: --tls-ctx-per-worker
|
||||
.. option:: --tls-ticket-key-memcached=<HOST>,<PORT>
|
||||
|
||||
Create OpenSSL's SSL_CTX per worker, so that no internal
|
||||
locking is required. This may improve scalability with
|
||||
multi threaded configuration. If this option is
|
||||
enabled, session ID is no longer shared accross SSL_CTX
|
||||
objects, which means session ID generated by one worker
|
||||
is not acceptable by another worker. On the other hand,
|
||||
session ticket key is shared across all worker threads.
|
||||
Specify address of memcached server to store session
|
||||
cache. This enables shared TLS ticket key between
|
||||
multiple nghttpx instances. nghttpx does not set TLS
|
||||
ticket key to memcached. The external ticket key
|
||||
generator is required. nghttpx just gets TLS ticket
|
||||
keys from memcached, and use them, possibly replacing
|
||||
current set of keys. It is up to extern TLS ticket key
|
||||
generator to rotate keys frequently. See "TLS SESSION
|
||||
TICKET RESUMPTION" section in manual page to know the
|
||||
data format in memcached entry.
|
||||
|
||||
.. option:: --tls-ticket-key-memcached-interval=<DURATION>
|
||||
|
||||
Set interval to get TLS ticket keys from memcached.
|
||||
|
||||
Default: ``10m``
|
||||
|
||||
.. option:: --tls-ticket-key-memcached-max-retry=<N>
|
||||
|
||||
Set maximum number of consecutive retries before
|
||||
abandoning TLS ticket key retrieval. If this number is
|
||||
reached, the attempt is considered as failure, and
|
||||
"failure" count is incremented by 1, which contributed
|
||||
to the value controlled
|
||||
:option:`--tls-ticket-key-memcached-max-fail` option.
|
||||
|
||||
Default: ``3``
|
||||
|
||||
.. option:: --tls-ticket-key-memcached-max-fail=<N>
|
||||
|
||||
Set maximum number of consecutive failure before
|
||||
disabling TLS ticket until next scheduled key retrieval.
|
||||
|
||||
Default: ``2``
|
||||
|
||||
.. option:: --tls-ticket-key-cipher=<CIPHER>
|
||||
|
||||
Specify cipher to encrypt TLS session ticket. Specify
|
||||
either aes-128-cbc or aes-256-cbc. By default,
|
||||
aes-128-cbc is used.
|
||||
|
||||
.. option:: --fetch-ocsp-response-file=<PATH>
|
||||
|
||||
Path to fetch-ocsp-response script file. It should be
|
||||
absolute path.
|
||||
|
||||
Default: ``/usr/local/share/nghttp2/fetch-ocsp-response``
|
||||
|
||||
.. option:: --ocsp-update-interval=<DURATION>
|
||||
|
||||
Set interval to update OCSP response cache.
|
||||
|
||||
Default: ``4h``
|
||||
|
||||
.. option:: --no-ocsp
|
||||
|
||||
Disable OCSP stapling.
|
||||
|
||||
.. option:: --tls-session-cache-memcached=<HOST>,<PORT>
|
||||
|
||||
Specify address of memcached server to store session
|
||||
cache. This enables shared session cache between
|
||||
multiple nghttpx instances.
|
||||
|
||||
|
||||
HTTP/2 and SPDY:
|
||||
~~~~~~~~~~~~~~~~
|
||||
HTTP/2 and SPDY
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
.. option:: -c, --http2-max-concurrent-streams=<N>
|
||||
|
||||
@@ -438,9 +572,15 @@ HTTP/2 and SPDY:
|
||||
meant for debugging purpose and not intended to enhance
|
||||
protocol security.
|
||||
|
||||
.. option:: --no-server-push
|
||||
|
||||
Mode:
|
||||
~~~~~
|
||||
Disable HTTP/2 server push. Server push is only
|
||||
supported by default mode and HTTP/2 frontend. SPDY
|
||||
frontend does not support server push.
|
||||
|
||||
|
||||
Mode
|
||||
~~~~
|
||||
|
||||
.. describe:: (default mode)
|
||||
|
||||
@@ -479,8 +619,8 @@ Mode:
|
||||
use as a forward proxy.
|
||||
|
||||
|
||||
Logging:
|
||||
~~~~~~~~
|
||||
Logging
|
||||
~~~~~~~
|
||||
|
||||
.. option:: -L, --log-level=<LEVEL>
|
||||
|
||||
@@ -522,6 +662,14 @@ Logging:
|
||||
* $alpn: ALPN identifier of the protocol which generates
|
||||
the response. For HTTP/1, ALPN is always http/1.1,
|
||||
regardless of minor version.
|
||||
* $ssl_cipher: cipher used for SSL/TLS connection.
|
||||
* $ssl_protocol: protocol for SSL/TLS connection.
|
||||
* $ssl_session_id: session ID for SSL/TLS connection.
|
||||
* $ssl_session_reused: "r" if SSL/TLS session was
|
||||
reused. Otherwise, "."
|
||||
|
||||
The variable can be enclosed by "{" and "}" for
|
||||
disambiguation (e.g., ${remote_addr}).
|
||||
|
||||
|
||||
Default: ``$remote_addr - - [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"``
|
||||
@@ -529,7 +677,8 @@ Logging:
|
||||
.. option:: --errorlog-file=<PATH>
|
||||
|
||||
Set path to write error log. To reopen file, send USR1
|
||||
signal to nghttpx.
|
||||
signal to nghttpx. stderr will be redirected to the
|
||||
error log file unless :option:`--errorlog-syslog` is used.
|
||||
|
||||
Default: ``/dev/stderr``
|
||||
|
||||
@@ -545,8 +694,8 @@ Logging:
|
||||
Default: ``daemon``
|
||||
|
||||
|
||||
HTTP:
|
||||
~~~~~
|
||||
HTTP
|
||||
~~~~
|
||||
|
||||
.. option:: --add-x-forwarded-for
|
||||
|
||||
@@ -570,14 +719,29 @@ HTTP:
|
||||
:option:`--client-proxy` mode, location header field will not be
|
||||
altered regardless of this option.
|
||||
|
||||
.. option:: --host-rewrite
|
||||
|
||||
Rewrite host and :authority header fields on
|
||||
:option:`--http2-bridge`\, :option:`--client` and default mode. For
|
||||
:option:`--http2-proxy` and :option:`\--client-proxy` mode, these headers
|
||||
will not be altered regardless of this option.
|
||||
|
||||
.. option:: --altsvc=<PROTOID,PORT[,HOST,[ORIGIN]]>
|
||||
|
||||
Specify protocol ID, port, host and origin of
|
||||
alternative service. <HOST> and <ORIGIN> are optional.
|
||||
They are advertised in alt-svc header field or HTTP/2
|
||||
ALTSVC frame. This option can be used multiple times to
|
||||
specify multiple alternative services. Example:
|
||||
:option:`--altsvc`\=h2,443
|
||||
They are advertised in alt-svc header field only in
|
||||
HTTP/1.1 frontend. This option can be used multiple
|
||||
times to specify multiple alternative services.
|
||||
Example: :option:`--altsvc`\=h2,443
|
||||
|
||||
.. option:: --add-request-header=<HEADER>
|
||||
|
||||
Specify additional header field to add to request header
|
||||
set. This option just appends header field and won't
|
||||
replace anything already set. This option can be used
|
||||
several times to specify multiple header fields.
|
||||
Example: :option:`--add-request-header`\="foo: bar"
|
||||
|
||||
.. option:: --add-response-header=<HEADER>
|
||||
|
||||
@@ -587,9 +751,24 @@ HTTP:
|
||||
used several times to specify multiple header fields.
|
||||
Example: :option:`--add-response-header`\="foo: bar"
|
||||
|
||||
.. option:: --header-field-buffer=<SIZE>
|
||||
|
||||
Debug:
|
||||
~~~~~~
|
||||
Set maximum buffer size for incoming HTTP header field
|
||||
list. This is the sum of header name and value in
|
||||
bytes.
|
||||
|
||||
Default: ``64K``
|
||||
|
||||
.. option:: --max-header-fields=<N>
|
||||
|
||||
Set maximum number of incoming HTTP header fields, which
|
||||
appear in one request or response header field list.
|
||||
|
||||
Default: ``100``
|
||||
|
||||
|
||||
Debug
|
||||
~~~~~
|
||||
|
||||
.. option:: --frontend-http2-dump-request-header=<PATH>
|
||||
|
||||
@@ -614,8 +793,8 @@ Debug:
|
||||
:option:`-n`\=N, where N >= 2.
|
||||
|
||||
|
||||
Process:
|
||||
~~~~~~~~
|
||||
Process
|
||||
~~~~~~~
|
||||
|
||||
.. option:: -D, --daemon
|
||||
|
||||
@@ -632,8 +811,8 @@ Process:
|
||||
be used to drop root privileges.
|
||||
|
||||
|
||||
Misc:
|
||||
~~~~~
|
||||
Misc
|
||||
~~~~
|
||||
|
||||
.. option:: --conf=<PATH>
|
||||
|
||||
@@ -641,6 +820,13 @@ Misc:
|
||||
|
||||
Default: ``/etc/nghttpx/nghttpx.conf``
|
||||
|
||||
.. option:: --include=<PATH>
|
||||
|
||||
Load additional configurations from <PATH>. File <PATH>
|
||||
is read when configuration parser encountered this
|
||||
option. This option can be used multiple times, or even
|
||||
recursively.
|
||||
|
||||
.. option:: -v, --version
|
||||
|
||||
Print version and exit.
|
||||
@@ -650,9 +836,15 @@ Misc:
|
||||
Print this help and exit.
|
||||
|
||||
|
||||
|
||||
The <SIZE> argument is an integer and an optional unit (e.g., 10K is
|
||||
10 * 1024). Units are K, M and G (powers of 1024).
|
||||
|
||||
The <DURATION> argument is an integer and an optional unit (e.g., 1s
|
||||
is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms
|
||||
(hours, minutes, seconds and milliseconds, respectively). If a unit
|
||||
is omitted, a second is used as unit.
|
||||
|
||||
FILES
|
||||
-----
|
||||
|
||||
@@ -674,7 +866,7 @@ FILES
|
||||
ignored.
|
||||
|
||||
To specify private key and certificate file which are given as
|
||||
positional arguments in commnad-line, use ``private-key-file`` and
|
||||
positional arguments in command-line, use ``private-key-file`` and
|
||||
``certificate-file``.
|
||||
|
||||
:option:`--conf` option cannot be used in the configuration file and
|
||||
@@ -697,6 +889,122 @@ SIGUSR2
|
||||
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
|
||||
--------
|
||||
|
||||
|
||||
118
doc/nghttpx.h2r
118
doc/nghttpx.h2r
@@ -19,7 +19,7 @@ FILES
|
||||
ignored.
|
||||
|
||||
To specify private key and certificate file which are given as
|
||||
positional arguments in commnad-line, use ``private-key-file`` and
|
||||
positional arguments in command-line, use ``private-key-file`` and
|
||||
``certificate-file``.
|
||||
|
||||
:option:`--conf` option cannot be used in the configuration file and
|
||||
@@ -42,6 +42,122 @@ SIGUSR2
|
||||
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
|
||||
--------
|
||||
|
||||
|
||||
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.
|
||||
@@ -36,8 +36,9 @@ 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.
|
||||
@@ -95,10 +96,41 @@ patch, to configure libev, use the following script:
|
||||
|
||||
And run ``make install`` to build and install.
|
||||
|
||||
To configure zlib, use the following script:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
#!/bin/sh -e
|
||||
|
||||
if [ -z "$ANDROID_HOME" ]; then
|
||||
echo 'No $ANDROID_HOME specified.'
|
||||
exit 1
|
||||
fi
|
||||
PREFIX=$ANDROID_HOME/usr/local
|
||||
TOOLCHAIN=$ANDROID_HOME/toolchain
|
||||
PATH=$TOOLCHAIN/bin:$PATH
|
||||
|
||||
HOST=arm-linux-androideabi
|
||||
|
||||
CC=$HOST-gcc \
|
||||
AR=$HOST-ar \
|
||||
LD=$HOST-ld \
|
||||
RANLIB=$HOST-ranlib \
|
||||
STRIP=$HOST-strip \
|
||||
./configure \
|
||||
--prefix=$PREFIX \
|
||||
--libdir=$PREFIX/lib \
|
||||
--includedir=$PREFIX/include \
|
||||
--static
|
||||
|
||||
And run ``make install`` to build and install.
|
||||
|
||||
To configure spdylay, use the following script:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
#!/bin/sh -e
|
||||
|
||||
if [ -z "$ANDROID_HOME" ]; then
|
||||
echo 'No $ANDROID_HOME specified.'
|
||||
exit 1
|
||||
@@ -119,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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -28,11 +28,14 @@ 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>
|
||||
@@ -46,5 +49,5 @@ 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/2 https://tools.ietf.org/html/rfc7540
|
||||
* HPACK https://tools.ietf.org/html/rfc7541
|
||||
|
||||
@@ -4,7 +4,7 @@ libnghttp2_asio: High level HTTP/2 C++ library
|
||||
libnghttp2_asio is C++ library built on top of libnghttp2 and provides
|
||||
high level abstraction API to build HTTP/2 applications. It depends
|
||||
on Boost::ASIO library and OpenSSL. Currently libnghttp2_asio
|
||||
provides server side API.
|
||||
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
|
||||
@@ -14,42 +14,64 @@ are:
|
||||
* Boost::System
|
||||
* Boost::Thread
|
||||
|
||||
To use libnghttp2_asio, first include following header file:
|
||||
We have 3 header files for this library:
|
||||
|
||||
.. code-block:: cpp
|
||||
* :doc:`asio_http2_server.h`
|
||||
* :doc:`asio_http2_client.h`
|
||||
* :doc:`asio_http2.h`
|
||||
|
||||
#include <nghttp2/asio_http2.h>
|
||||
asio_http2.h is included from the other two files.
|
||||
|
||||
Also take a look at that header file :doc:`asio_http2.h`.
|
||||
To build a program with libnghttp2_asio, link to the following
|
||||
libraries::
|
||||
|
||||
-lnghttp2_asio -lboost_system
|
||||
|
||||
If ``boost::asio::ssl`` is used in application code, OpenSSL is also
|
||||
required in link line::
|
||||
|
||||
-lnghttp2_asio -lboost_system -lssl -lcrypto
|
||||
|
||||
Server API
|
||||
----------
|
||||
|
||||
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
|
||||
|
||||
#include <nghttp2/asio_http2.h>
|
||||
|
||||
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.listen("*", 3000, [](const std::shared_ptr<request> &req,
|
||||
const std::shared_ptr<response> &res) {
|
||||
res->write_head(200);
|
||||
res->end("hello, world");
|
||||
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.
|
||||
Then call ``nghttp2::asio_http2::server::http2::listen`` function with
|
||||
address and port to listen to and callback function, namely "request
|
||||
callback", invoked when request arrives.
|
||||
``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``
|
||||
@@ -58,9 +80,13 @@ status code, in the above example, which is 200. The second argument,
|
||||
which is omitted in the above example, is additional header fields to
|
||||
send.
|
||||
|
||||
``nghttp2::asio_http2::server::response::end`` sends responde body.
|
||||
``nghttp2::asio_http2::server::response::end`` sends response body.
|
||||
In the above example, we send string "hello, world".
|
||||
|
||||
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
|
||||
+++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
@@ -69,40 +95,47 @@ SSL/TLS.
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#include <nghttp2/asio_http2.h>
|
||||
#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.tls("server.key", "server.crt");
|
||||
|
||||
server.listen("*", 3000, [](const std::shared_ptr<request> &req,
|
||||
const std::shared_ptr<response> &res) {
|
||||
if (req->path() == "/" || req->path() == "/index.html") {
|
||||
res->write_head(200);
|
||||
res->end(file_reader("index.html"));
|
||||
} else {
|
||||
res->write_head(404);
|
||||
res->end("<html><head><title>404</title></head>"
|
||||
"<body>404 Not Found</body></html>");
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
Specifying path to private key file and certificate file in
|
||||
``nghttp2::asio_http2::server::http2::tls`` will enable SSL/TLS. Both
|
||||
files must be in PEM format.
|
||||
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 either "/" or "/index.html",
|
||||
we serve index.html file in the current working directory.
|
||||
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::read_cb`` and application pass
|
||||
its implementation to generate response body. For the convenience,
|
||||
libnghttp2_asio library provides ``nghttp2::asio_http2::file_reader``
|
||||
function to generate function to server static file.
|
||||
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
|
||||
+++++++++++
|
||||
@@ -111,44 +144,56 @@ Server push is also supported.
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#include <nghttp2/asio_http2.h>
|
||||
#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.tls("server.key", "server.crt");
|
||||
std::string style_css = "h1 { color: green; }";
|
||||
|
||||
server.listen("*", 3000, [](const std::shared_ptr<request> &req,
|
||||
const std::shared_ptr<response> &res) {
|
||||
if (req->path() == "/") {
|
||||
req->push("GET", "/my.css");
|
||||
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(file_reader("index.html"));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (req->path() == "/my.css") {
|
||||
res->write_head(200);
|
||||
res->end(file_reader("my.css"));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
res->write_head(404);
|
||||
res->end("<html><head><title>404</title></head>"
|
||||
"<body>404 Not Found</body></html>");
|
||||
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 "/", we push "/my.css". To push resource, call
|
||||
``nghttp2::asio_http2::server::request::push`` function with desired
|
||||
method and path. Later, the callback will be called with the pushed
|
||||
resource "/my.css".
|
||||
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
|
||||
++++++++++++++++++++++
|
||||
@@ -164,65 +209,225 @@ desired number of threads:
|
||||
// Use 4 native threads
|
||||
server.num_threads(4);
|
||||
|
||||
Run blocking tasks in background thread
|
||||
+++++++++++++++++++++++++++++++++++++++
|
||||
Client API
|
||||
----------
|
||||
|
||||
The request callback is called in the same thread where HTTP request
|
||||
is handled. And many connections shares the same thread, we cannot
|
||||
directly run blocking tasks in request callback.
|
||||
|
||||
To run blocking tasks, use
|
||||
``nghttp2::asio_http2::server::request::run_task``. The passed
|
||||
callback will be executed in the different thread from the thread
|
||||
where request callback was executed. So application can perform
|
||||
blocking task there. The example follows:
|
||||
To use client API, first include following header file:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#include <unistd.h>
|
||||
#include <nghttp2/asio_http2.h>
|
||||
#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::server;
|
||||
using namespace nghttp2::asio_http2::client;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
http2 server;
|
||||
boost::system::error_code ec;
|
||||
boost::asio::io_service io_service;
|
||||
|
||||
server.num_concurrent_tasks(16);
|
||||
// connect to localhost:3000
|
||||
session sess(io_service, "localhost", "3000");
|
||||
|
||||
server.listen("*", 3000, [](const std::shared_ptr<request> &req,
|
||||
const std::shared_ptr<response> &res) {
|
||||
req->run_task([res](channel &channel) {
|
||||
// executed in different thread than the thread where
|
||||
// request callback was executed.
|
||||
sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) {
|
||||
boost::system::error_code ec;
|
||||
|
||||
// using res directly here is not safe. Capturing it by
|
||||
// value is safe because it is std::shared_ptr.
|
||||
auto req = sess.submit(ec, "GET", "http://localhost:3000/");
|
||||
|
||||
sleep(1);
|
||||
req->on_response([](const response &res) {
|
||||
// print status code and response header fields.
|
||||
std::cerr << "HTTP/2 " << res.status_code() << std::endl;
|
||||
for (auto &kv : res.header()) {
|
||||
std::cerr << kv.first << ": " << kv.second.value << "\n";
|
||||
}
|
||||
std::cerr << std::endl;
|
||||
|
||||
channel.post([res]() {
|
||||
// executed in the same thread where request callback
|
||||
// was executed.
|
||||
res->write_head(200);
|
||||
res->end("hello, world");
|
||||
});
|
||||
});
|
||||
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();
|
||||
}
|
||||
|
||||
First we set the number of background threads which run tasks. By
|
||||
default it is set to 1. In this example, we set it to 16, so at most
|
||||
16 tasks can be executed concurrently without blocking handling new
|
||||
requests.
|
||||
``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.
|
||||
|
||||
We call ``req->run_task()`` to execute task in background thread. In
|
||||
the passed callback, we just simply sleeps 1 second. After sleep is
|
||||
over, we schedule another callback to send response to the client.
|
||||
Since the callback passed to ``req->run_task()`` is executed in the
|
||||
different thread from the thread where request callback is called,
|
||||
using ``req`` or ``res`` object directly there may cause undefined
|
||||
behaviour. To avoid this issue, we can use
|
||||
``nghttp2::asio_http2::channel::post`` by supplying a callback which
|
||||
in turn get called in the same thread where request callback was
|
||||
called.
|
||||
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
|
||||
@@ -59,11 +59,11 @@ 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
|
||||
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.
|
||||
The backend must be HTTP/1 proxy server. nghttpx supports multiple
|
||||
backend server addresses. It translates incoming requests to HTTP/1
|
||||
request to backend server. The backend server performs real proxy
|
||||
work for each request, for example, dispatching requests to the origin
|
||||
server and caching contents.
|
||||
|
||||
For example, to make nghttpx listen to encrypted HTTP/2 requests at
|
||||
port 8443, and a backend HTTP/1 proxy server is configured to listen
|
||||
@@ -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,7 +243,7 @@ Read/write rate limit
|
||||
---------------------
|
||||
|
||||
nghttpx supports transfer rate limiting on frontend connections. You
|
||||
can do rate limit per frontend connection for reading and writeing
|
||||
can do rate limit per frontend connection for reading and writing
|
||||
individually.
|
||||
|
||||
To perform rate limit for reading, use ``--read-rate`` and
|
||||
@@ -285,11 +293,11 @@ re-open log files, send USR1 signal to nghttpx process. It will
|
||||
re-open files specified by ``--accesslog-file`` and
|
||||
``--errorlog-file`` options.
|
||||
|
||||
Multiple HTTP/1 backend addresses
|
||||
---------------------------------
|
||||
Multiple backend addresses
|
||||
--------------------------
|
||||
|
||||
nghttpx supports multiple HTTP/1 backend addresses. To specify them,
|
||||
just use ``-b`` option repeatedly. For example, to use backend1:8080
|
||||
and backend2:8080, use command-line like this: ``-bbackend1,8080
|
||||
-bbackend2,8080``. Please note that HTTP/2 backend only supports 1
|
||||
backend address.
|
||||
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,25 +1,25 @@
|
||||
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 _U_, unsigned char **out,
|
||||
@@ -31,8 +31,8 @@ protocol the library supports::
|
||||
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) {
|
||||
SSL_CTX *ssl_ctx;
|
||||
@@ -49,8 +49,10 @@ The callback is set to the SSL_CTX object using
|
||||
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;
|
||||
@@ -59,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 */
|
||||
@@ -82,12 +84,12 @@ 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,
|
||||
const char *host, uint16_t port,
|
||||
@@ -110,11 +112,11 @@ the remote server::
|
||||
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) {
|
||||
@@ -142,11 +144,15 @@ underlying network socket::
|
||||
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.
|
||||
|
||||
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;
|
||||
@@ -175,18 +181,19 @@ finished successfully. We first initialize nghttp2 session object in
|
||||
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) {
|
||||
@@ -194,8 +201,7 @@ transmission of client connection header is done in
|
||||
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
|
||||
int rv;
|
||||
|
||||
bufferevent_write(session_data->bev, NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
|
||||
/* 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) {
|
||||
@@ -203,17 +209,17 @@ transmission of client connection header is done in
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
int32_t stream_id;
|
||||
@@ -237,17 +243,18 @@ request in ``submit_request()`` function::
|
||||
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;
|
||||
@@ -273,12 +280,13 @@ data is available to read in the bufferevent input buffer::
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
int rv;
|
||||
@@ -291,10 +299,10 @@ frames. The ``session_send()`` function is defined as follows::
|
||||
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 _U_, const uint8_t *data,
|
||||
size_t length, int flags _U_, void *user_data) {
|
||||
@@ -307,18 +315,18 @@ 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 _U_, void *ptr) {
|
||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||
@@ -330,25 +338,25 @@ all data written in the bufferevent output buffer have been sent::
|
||||
}
|
||||
|
||||
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 _U_,
|
||||
const nghttp2_frame *frame, const uint8_t *name,
|
||||
@@ -368,10 +376,11 @@ Each request header name/value pair is emitted via
|
||||
return 0;
|
||||
}
|
||||
|
||||
In this tutorial, 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 _U_,
|
||||
const nghttp2_frame *frame, void *user_data) {
|
||||
@@ -387,13 +396,16 @@ 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.
|
||||
|
||||
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,
|
||||
@@ -406,10 +418,10 @@ of data is received from the remote peer::
|
||||
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::
|
||||
@@ -432,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,118 +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.
|
||||
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,30 +1,31 @@
|
||||
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;
|
||||
@@ -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;
|
||||
@@ -90,18 +94,21 @@ 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``. 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
|
||||
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``.
|
||||
|
||||
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. We use
|
||||
libevent's ``struct evconnlistener`` for this purpose::
|
||||
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) {
|
||||
@@ -135,7 +142,7 @@ libevent's ``struct evconnlistener`` for this purpose::
|
||||
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 _U_, int fd,
|
||||
@@ -148,13 +155,13 @@ accepted::
|
||||
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 three
|
||||
callbacks for the bufferevent: ``readcb``, ``writecb`` and
|
||||
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 _U_, short events, void *ptr) {
|
||||
@@ -181,28 +188,23 @@ underlying network socket::
|
||||
delete_http2_session_data(session_data);
|
||||
}
|
||||
|
||||
For the ``BEV_EVENT_EOF``, ``BEV_EVENT_ERROR`` and
|
||||
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. Now we are ready to start the HTTP/2
|
||||
communication.
|
||||
``http2_session_data`` object and its associated bufferevent member.
|
||||
As a result, the underlying connection is closed.
|
||||
|
||||
We initialize a nghttp2 session object which is done in
|
||||
``initialize_nghttp2_session()``::
|
||||
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.
|
||||
|
||||
The ``initialize_nghttp2_session()`` function initializes the nghttp2
|
||||
session object and several callbacks::
|
||||
|
||||
static void initialize_nghttp2_session(http2_session_data *session_data) {
|
||||
nghttp2_option *option;
|
||||
nghttp2_session_callbacks *callbacks;
|
||||
|
||||
nghttp2_option_new(&option);
|
||||
|
||||
/* Tells nghttp2_session object that it handles client connection
|
||||
preface */
|
||||
nghttp2_option_set_recv_client_preface(option, 1);
|
||||
|
||||
nghttp2_session_callbacks_new(&callbacks);
|
||||
|
||||
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
||||
@@ -219,23 +221,18 @@ We initialize a nghttp2 session object which is done in
|
||||
nghttp2_session_callbacks_set_on_begin_headers_callback(
|
||||
callbacks, on_begin_headers_callback);
|
||||
|
||||
nghttp2_session_server_new2(&session_data->session, callbacks, session_data,
|
||||
option);
|
||||
nghttp2_session_server_new(&session_data->session, callbacks, session_data);
|
||||
|
||||
nghttp2_session_callbacks_del(callbacks);
|
||||
nghttp2_option_del(option);
|
||||
}
|
||||
|
||||
Since we are creating a server and uses options, the nghttp2 session
|
||||
object is created using `nghttp2_session_server_new2()` function. We
|
||||
registers five callbacks for nghttp2 session object. We'll talk about
|
||||
these callbacks later. Our server only speaks HTTP/2. In this case,
|
||||
we use `nghttp2_option_set_recv_client_preface()` to make
|
||||
:type:`nghttp2_session` object handle client connection preface, which
|
||||
saves some lines of application code.
|
||||
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) {
|
||||
nghttp2_settings_entry iv[1] = {
|
||||
@@ -251,11 +248,10 @@ a server connection header in ``send_server_connection_header()``::
|
||||
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.
|
||||
@@ -286,12 +282,14 @@ this pending data. To process the received data, we call the
|
||||
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()``.
|
||||
|
||||
The ``session_send()`` function is defined as follows::
|
||||
|
||||
static int session_send(http2_session_data *session_data) {
|
||||
int rv;
|
||||
@@ -304,7 +302,7 @@ 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::
|
||||
|
||||
@@ -324,17 +322,16 @@ 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::
|
||||
@@ -369,16 +366,18 @@ data in the bufferevent output buffer has been sent::
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
@@ -386,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
|
||||
@@ -408,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
|
||||
@@ -449,10 +450,10 @@ 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::
|
||||
@@ -482,15 +483,15 @@ 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) {
|
||||
@@ -507,12 +508,13 @@ Sending the content of the file is done in ``send_response()`` function::
|
||||
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 _U_,
|
||||
int32_t stream_id _U_, uint8_t *buf,
|
||||
@@ -532,11 +534,11 @@ function to read the contents of the file::
|
||||
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.
|
||||
@@ -558,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,7 +2,8 @@ client
|
||||
libevent-client
|
||||
libevent-server
|
||||
deflate
|
||||
asio-sv
|
||||
tiny-nghttpd
|
||||
asio-sv
|
||||
asio-sv2
|
||||
asio-sv3
|
||||
asio-cl
|
||||
asio-cl2
|
||||
|
||||
@@ -58,10 +58,21 @@ endif # ENABLE_TINY_NGHTTPD
|
||||
|
||||
if ENABLE_ASIO_LIB
|
||||
|
||||
noinst_PROGRAMS += asio-sv asio-sv2 asio-sv3
|
||||
noinst_PROGRAMS += asio-sv asio-sv2 asio-cl asio-cl2
|
||||
|
||||
ASIOCPPFLAGS = ${BOOST_CPPFLAGS} ${AM_CPPFLAGS}
|
||||
ASIOLDADD = $(top_builddir)/src/libnghttp2_asio.la @JEMALLOC_LIBS@
|
||||
# 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}
|
||||
@@ -71,9 +82,13 @@ asio_sv2_SOURCES = asio-sv2.cc
|
||||
asio_sv2_CPPFLAGS = ${ASIOCPPFLAGS}
|
||||
asio_sv2_LDADD = ${ASIOLDADD}
|
||||
|
||||
asio_sv3_SOURCES = asio-sv3.cc
|
||||
asio_sv3_CPPFLAGS = ${ASIOCPPFLAGS}
|
||||
asio_sv3_LDADD = ${ASIOLDADD}
|
||||
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
|
||||
|
||||
|
||||
96
examples/asio-cl.cc
Normal file
96
examples/asio-cl.cc
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <nghttp2/asio_http2_client.h>
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
using namespace nghttp2::asio_http2;
|
||||
using namespace nghttp2::asio_http2::client;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
try {
|
||||
if (argc < 2) {
|
||||
std::cerr << "Usage: asio-cl URI" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
boost::system::error_code ec;
|
||||
boost::asio::io_service io_service;
|
||||
|
||||
std::string uri = argv[1];
|
||||
std::string scheme, host, service;
|
||||
|
||||
if (host_service_from_uri(ec, scheme, host, service, uri)) {
|
||||
std::cerr << "error: bad URI: " << ec.message() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
boost::asio::ssl::context tls_ctx(boost::asio::ssl::context::sslv23);
|
||||
tls_ctx.set_default_verify_paths();
|
||||
// disabled to make development easier...
|
||||
// tls_ctx.set_verify_mode(boost::asio::ssl::verify_peer);
|
||||
configure_tls_context(ec, tls_ctx);
|
||||
|
||||
auto sess = scheme == "https" ? session(io_service, tls_ctx, host, service)
|
||||
: session(io_service, host, service);
|
||||
|
||||
sess.on_connect([&sess, &uri](tcp::resolver::iterator endpoint_it) {
|
||||
boost::system::error_code ec;
|
||||
auto req = sess.submit(ec, "GET", uri);
|
||||
|
||||
if (ec) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
req->on_response([&sess](const response &res) {
|
||||
std::cerr << "HTTP/2 " << res.status_code() << std::endl;
|
||||
for (auto &kv : res.header()) {
|
||||
std::cerr << kv.first << ": " << kv.second.value << "\n";
|
||||
}
|
||||
std::cerr << std::endl;
|
||||
|
||||
res.on_data([&sess](const uint8_t *data, std::size_t len) {
|
||||
std::cerr.write(reinterpret_cast<const char *>(data), len);
|
||||
std::cerr << std::endl;
|
||||
});
|
||||
});
|
||||
|
||||
req->on_close([&sess](uint32_t error_code) { sess.shutdown(); });
|
||||
});
|
||||
|
||||
sess.on_error([](const boost::system::error_code &ec) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
});
|
||||
|
||||
io_service.run();
|
||||
} catch (std::exception &e) {
|
||||
std::cerr << "exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
134
examples/asio-cl2.cc
Normal file
134
examples/asio-cl2.cc
Normal file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <nghttp2/asio_http2_client.h>
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
using namespace nghttp2::asio_http2;
|
||||
using namespace nghttp2::asio_http2::client;
|
||||
|
||||
void print_header(const header_map &h) {
|
||||
for (auto &kv : h) {
|
||||
std::cerr << kv.first << ": " << kv.second.value << "\n";
|
||||
}
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
|
||||
void print_header(const response &res) {
|
||||
std::cerr << "HTTP/2 " << res.status_code() << "\n";
|
||||
print_header(res.header());
|
||||
}
|
||||
|
||||
void print_header(const request &req) {
|
||||
auto &uri = req.uri();
|
||||
std::cerr << req.method() << " " << uri.scheme << "://" << uri.host
|
||||
<< uri.path;
|
||||
if (!uri.raw_query.empty()) {
|
||||
std::cerr << "?" << uri.raw_query;
|
||||
}
|
||||
std::cerr << " HTTP/2\n";
|
||||
print_header(req.header());
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
try {
|
||||
if (argc < 2) {
|
||||
std::cerr << "Usage: asio-cl URI" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
boost::system::error_code ec;
|
||||
boost::asio::io_service io_service;
|
||||
|
||||
std::string uri = argv[1];
|
||||
std::string scheme, host, service;
|
||||
|
||||
if (host_service_from_uri(ec, scheme, host, service, uri)) {
|
||||
std::cerr << "error: bad URI: " << ec.message() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
boost::asio::ssl::context tls_ctx(boost::asio::ssl::context::sslv23);
|
||||
tls_ctx.set_default_verify_paths();
|
||||
// disabled to make development easier...
|
||||
// tls_ctx.set_verify_mode(boost::asio::ssl::verify_peer);
|
||||
configure_tls_context(ec, tls_ctx);
|
||||
|
||||
auto sess = scheme == "https" ? session(io_service, tls_ctx, host, service)
|
||||
: session(io_service, host, service);
|
||||
|
||||
sess.on_connect([&sess, &uri](tcp::resolver::iterator endpoint_it) {
|
||||
std::cerr << "connected to " << (*endpoint_it).endpoint() << std::endl;
|
||||
boost::system::error_code ec;
|
||||
auto req = sess.submit(ec, "GET", uri, {{"cookie", {"foo=bar", true}}});
|
||||
if (ec) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
req->on_response([&sess, req](const response &res) {
|
||||
std::cerr << "response header was received" << std::endl;
|
||||
print_header(res);
|
||||
|
||||
res.on_data([&sess](const uint8_t *data, std::size_t len) {
|
||||
std::cerr.write(reinterpret_cast<const char *>(data), len);
|
||||
std::cerr << std::endl;
|
||||
});
|
||||
});
|
||||
|
||||
req->on_close([&sess](uint32_t error_code) {
|
||||
std::cerr << "request done with error_code=" << error_code << std::endl;
|
||||
});
|
||||
|
||||
req->on_push([](const request &push_req) {
|
||||
std::cerr << "push request was received" << std::endl;
|
||||
|
||||
print_header(push_req);
|
||||
|
||||
push_req.on_response([](const response &res) {
|
||||
std::cerr << "push response header was received" << std::endl;
|
||||
|
||||
res.on_data([](const uint8_t *data, std::size_t len) {
|
||||
std::cerr.write(reinterpret_cast<const char *>(data), len);
|
||||
std::cerr << std::endl;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
sess.on_error([](const boost::system::error_code &ec) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
});
|
||||
|
||||
io_service.run();
|
||||
} catch (std::exception &e) {
|
||||
std::cerr << "exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -37,7 +37,7 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <nghttp2/asio_http2.h>
|
||||
#include <nghttp2/asio_http2_server.h>
|
||||
|
||||
using namespace nghttp2::asio_http2;
|
||||
using namespace nghttp2::asio_http2::server;
|
||||
@@ -45,28 +45,102 @@ using namespace nghttp2::asio_http2::server;
|
||||
int main(int argc, char *argv[]) {
|
||||
try {
|
||||
// Check command line arguments.
|
||||
if (argc < 3) {
|
||||
std::cerr << "Usage: asio-sv <port> <threads> <private-key-file> "
|
||||
<< "<cert-file>\n";
|
||||
if (argc < 4) {
|
||||
std::cerr
|
||||
<< "Usage: asio-sv <address> <port> <threads> [<private-key-file> "
|
||||
<< "<cert-file>]\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint16_t port = std::stoi(argv[1]);
|
||||
std::size_t num_threads = std::stoi(argv[2]);
|
||||
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);
|
||||
|
||||
if (argc >= 5) {
|
||||
server.tls(argv[3], argv[4]);
|
||||
}
|
||||
|
||||
server.listen("*", port, [](const std::shared_ptr<request> &req,
|
||||
const std::shared_ptr<response> &res) {
|
||||
res->write_head(200, {header{"foo", "bar"}});
|
||||
res->end("hello, world");
|
||||
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";
|
||||
}
|
||||
|
||||
@@ -35,12 +35,16 @@
|
||||
//
|
||||
#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.h>
|
||||
#include <nghttp2/asio_http2_server.h>
|
||||
|
||||
using namespace nghttp2::asio_http2;
|
||||
using namespace nghttp2::asio_http2::server;
|
||||
@@ -48,30 +52,28 @@ using namespace nghttp2::asio_http2::server;
|
||||
int main(int argc, char *argv[]) {
|
||||
try {
|
||||
// Check command line arguments.
|
||||
if (argc < 4) {
|
||||
std::cerr << "Usage: asio-sv2 <port> <threads> <doc-root> "
|
||||
<< "<private-key-file> <cert-file>\n";
|
||||
if (argc < 5) {
|
||||
std::cerr << "Usage: asio-sv2 <address> <port> <threads> <doc-root> "
|
||||
<< "[<private-key-file> <cert-file>]\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint16_t port = std::stoi(argv[1]);
|
||||
std::size_t num_threads = std::stoi(argv[2]);
|
||||
std::string docroot = argv[3];
|
||||
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);
|
||||
|
||||
if (argc >= 6) {
|
||||
server.tls(argv[4], argv[5]);
|
||||
}
|
||||
|
||||
server.listen("*", port, [&docroot](const std::shared_ptr<request> &req,
|
||||
const std::shared_ptr<response> &res) {
|
||||
auto path = percent_decode(req->path());
|
||||
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();
|
||||
res.write_head(404);
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -82,23 +84,39 @@ int main(int argc, char *argv[]) {
|
||||
path = docroot + path;
|
||||
auto fd = open(path.c_str(), O_RDONLY);
|
||||
if (fd == -1) {
|
||||
res->write_head(404);
|
||||
res->end();
|
||||
res.write_head(404);
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
auto headers = std::vector<header>();
|
||||
auto header = header_map();
|
||||
|
||||
struct stat stbuf;
|
||||
if (stat(path.c_str(), &stbuf) == 0) {
|
||||
headers.push_back(
|
||||
header{"content-length", std::to_string(stbuf.st_size)});
|
||||
headers.push_back(
|
||||
header{"last-modified", http_date(stbuf.st_mtim.tv_sec)});
|
||||
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(headers));
|
||||
res->end(file_reader_from_fd(fd));
|
||||
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";
|
||||
}
|
||||
|
||||
@@ -1,142 +0,0 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2014 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
// We wrote this code based on the original code which has the
|
||||
// following license:
|
||||
//
|
||||
// main.cpp
|
||||
// ~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#include <unistd.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <deque>
|
||||
|
||||
#include <nghttp2/asio_http2.h>
|
||||
|
||||
using namespace nghttp2::asio_http2;
|
||||
using namespace nghttp2::asio_http2::server;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
try {
|
||||
// Check command line arguments.
|
||||
if (argc < 4) {
|
||||
std::cerr << "Usage: asio-sv3 <port> <threads> <tasks> "
|
||||
<< " <private-key-file> <cert-file>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint16_t port = std::stoi(argv[1]);
|
||||
std::size_t num_threads = std::stoi(argv[2]);
|
||||
std::size_t num_concurrent_tasks = std::stoi(argv[3]);
|
||||
|
||||
http2 server;
|
||||
|
||||
server.num_threads(num_threads);
|
||||
|
||||
if (argc >= 5) {
|
||||
server.tls(argv[4], argv[5]);
|
||||
}
|
||||
|
||||
server.num_concurrent_tasks(num_concurrent_tasks);
|
||||
|
||||
server.listen("*", port, [](const std::shared_ptr<request> &req,
|
||||
const std::shared_ptr<response> &res) {
|
||||
res->write_head(200);
|
||||
|
||||
auto msgq = std::make_shared<std::deque<std::string>>();
|
||||
|
||||
res->end([msgq](uint8_t * buf, std::size_t len)
|
||||
-> std::pair<ssize_t, bool> {
|
||||
if (msgq->empty()) {
|
||||
// if msgq is empty, tells the library that don't call
|
||||
// this callback until we call res->resume(). This is
|
||||
// done by returing std::make_pair(0, false).
|
||||
return std::make_pair(0, false);
|
||||
}
|
||||
auto msg = std::move(msgq->front());
|
||||
msgq->pop_front();
|
||||
|
||||
if (msg.empty()) {
|
||||
// The empty message signals the end of response in
|
||||
// this simple protocol.
|
||||
return std::make_pair(0, true);
|
||||
}
|
||||
|
||||
auto nwrite = std::min(len, msg.size());
|
||||
std::copy(std::begin(msg), std::begin(msg) + nwrite, buf);
|
||||
if (msg.size() > nwrite) {
|
||||
msgq->push_front(msg.substr(nwrite));
|
||||
}
|
||||
return std::make_pair(nwrite, false);
|
||||
});
|
||||
|
||||
req->run_task([res, msgq](channel &channel) {
|
||||
// executed in different thread from request callback
|
||||
// was called.
|
||||
|
||||
// Using res and msgq is not safe inside this callback.
|
||||
// But using them in callback passed to channel::post is
|
||||
// safe.
|
||||
|
||||
// We just emit simple message "message N\n" in every 1
|
||||
// second and 3 times in total.
|
||||
for (std::size_t i = 0; i < 3; ++i) {
|
||||
msgq->push_back("message " + std::to_string(i + 1) + "\n");
|
||||
|
||||
channel.post([res]() {
|
||||
// executed in same thread where
|
||||
// request callback was called.
|
||||
|
||||
// Tells library we have new message.
|
||||
res->resume();
|
||||
});
|
||||
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
// Send empty message to signal the end of response
|
||||
// body.
|
||||
msgq->push_back("");
|
||||
|
||||
channel.post([res]() {
|
||||
// executed in same thread where request
|
||||
// callback was called.
|
||||
res->resume();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
} catch (std::exception &e) {
|
||||
std::cerr << "exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -28,16 +28,26 @@
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /* !HAVE_CONFIG_H */
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <stdint.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>
|
||||
@@ -528,14 +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 */
|
||||
rv = SSL_write(ssl, NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
|
||||
if (rv <= 0) {
|
||||
dief("SSL_write failed: could not send connection preface",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
|
||||
/* Here make file descriptor non-block */
|
||||
make_non_block(fd);
|
||||
set_tcp_nodelay(fd);
|
||||
@@ -558,6 +560,8 @@ static void fetch_uri(const struct URI *uri) {
|
||||
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);
|
||||
|
||||
@@ -685,10 +689,10 @@ 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) {
|
||||
|
||||
@@ -22,16 +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 */
|
||||
#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>
|
||||
@@ -50,11 +69,11 @@
|
||||
#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 */
|
||||
@@ -94,7 +113,8 @@ static http2_stream_data *create_http2_stream_data(const char *uri,
|
||||
":%u", u->port);
|
||||
}
|
||||
|
||||
stream_data->pathlen = 0;
|
||||
/* 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;
|
||||
}
|
||||
@@ -102,19 +122,22 @@ static http2_stream_data *create_http2_stream_data(const char *uri,
|
||||
/* +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;
|
||||
}
|
||||
|
||||
@@ -258,8 +281,7 @@ static int on_data_chunk_recv_callback(nghttp2_session *session _U_,
|
||||
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,
|
||||
uint32_t error_code,
|
||||
void *user_data) {
|
||||
uint32_t error_code, void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
int rv;
|
||||
|
||||
@@ -345,8 +367,7 @@ static void send_client_connection_header(http2_session_data *session_data) {
|
||||
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
|
||||
int rv;
|
||||
|
||||
bufferevent_write(session_data->bev, NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
|
||||
/* 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) {
|
||||
@@ -547,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,21 +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 */
|
||||
#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>
|
||||
@@ -537,15 +559,8 @@ static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||
}
|
||||
|
||||
static void initialize_nghttp2_session(http2_session_data *session_data) {
|
||||
nghttp2_option *option;
|
||||
nghttp2_session_callbacks *callbacks;
|
||||
|
||||
nghttp2_option_new(&option);
|
||||
|
||||
/* Tells nghttp2_session object that it handles client connection
|
||||
preface */
|
||||
nghttp2_option_set_recv_client_preface(option, 1);
|
||||
|
||||
nghttp2_session_callbacks_new(&callbacks);
|
||||
|
||||
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
||||
@@ -562,11 +577,9 @@ static void initialize_nghttp2_session(http2_session_data *session_data) {
|
||||
nghttp2_session_callbacks_set_on_begin_headers_callback(
|
||||
callbacks, on_begin_headers_callback);
|
||||
|
||||
nghttp2_session_server_new2(&session_data->session, callbacks, session_data,
|
||||
option);
|
||||
nghttp2_session_server_new(&session_data->session, callbacks, session_data);
|
||||
|
||||
nghttp2_session_callbacks_del(callbacks);
|
||||
nghttp2_option_del(option);
|
||||
}
|
||||
|
||||
/* Send HTTP/2 client connection header, which includes 24 bytes
|
||||
@@ -671,7 +684,7 @@ static void start_listen(struct event_base *evbase, const char *service,
|
||||
|
||||
rv = getaddrinfo(NULL, service, &hints, &res);
|
||||
if (rv != 0) {
|
||||
errx(1, NULL);
|
||||
errx(1, "Could not resolve server address");
|
||||
}
|
||||
for (rp = res; rp; rp = rp->ai_next) {
|
||||
struct evconnlistener *listener;
|
||||
@@ -723,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;
|
||||
|
||||
@@ -30,18 +30,30 @@
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /* !HAVE_CONFIG_H */
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_SYS_SOCKET_H
|
||||
#include <sys/socket.h>
|
||||
#endif /* HAVE_SYS_SOCKET_H */
|
||||
#include <sys/stat.h>
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif /* HAVE_FCNTL_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>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif /* HAVE_UNISTD_H */
|
||||
#include <stdlib.h>
|
||||
#ifdef HAVE_TIME_H
|
||||
#include <time.h>
|
||||
#endif /* HAVE_TIME_H */
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
@@ -153,7 +165,6 @@ const char *docroot;
|
||||
size_t docrootlen;
|
||||
|
||||
nghttp2_session_callbacks *shared_callbacks;
|
||||
nghttp2_option *shared_option;
|
||||
|
||||
static int handle_accept(io_loop *loop, uint32_t events, void *ptr);
|
||||
static int handle_connection(io_loop *loop, uint32_t events, void *ptr);
|
||||
@@ -179,13 +190,57 @@ static char *io_buf_add_str(io_buf *buf, const void *src, size_t len) {
|
||||
return (char *)start;
|
||||
}
|
||||
|
||||
static int memseq(const uint8_t *a, size_t alen, const char *b) {
|
||||
const uint8_t *last = a + alen;
|
||||
static int memeq(const void *a, const void *b, size_t n) {
|
||||
return memcmp(a, b, n) == 0;
|
||||
}
|
||||
|
||||
for (; a != last && *b && *a == *b; ++a, ++b)
|
||||
;
|
||||
#define streq(A, B, N) ((sizeof((A)) - 1) == (N) && memeq((A), (B), (N)))
|
||||
|
||||
return a == last && *b == 0;
|
||||
typedef enum {
|
||||
NGHTTP2_TOKEN__AUTHORITY,
|
||||
NGHTTP2_TOKEN__METHOD,
|
||||
NGHTTP2_TOKEN__PATH,
|
||||
NGHTTP2_TOKEN__SCHEME,
|
||||
NGHTTP2_TOKEN_HOST
|
||||
} nghttp2_token;
|
||||
|
||||
/* Inspired by h2o header lookup. https://github.com/h2o/h2o */
|
||||
static int lookup_token(const uint8_t *name, size_t namelen) {
|
||||
switch (namelen) {
|
||||
case 5:
|
||||
switch (name[namelen - 1]) {
|
||||
case 'h':
|
||||
if (streq(":pat", name, 4)) {
|
||||
return NGHTTP2_TOKEN__PATH;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
switch (name[namelen - 1]) {
|
||||
case 'd':
|
||||
if (streq(":metho", name, 6)) {
|
||||
return NGHTTP2_TOKEN__METHOD;
|
||||
}
|
||||
break;
|
||||
case 'e':
|
||||
if (streq(":schem", name, 6)) {
|
||||
return NGHTTP2_TOKEN__SCHEME;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 10:
|
||||
switch (name[namelen - 1]) {
|
||||
case 'y':
|
||||
if (streq(":authorit", name, 9)) {
|
||||
return NGHTTP2_TOKEN__AUTHORITY;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static char *cpydig(char *buf, int n, size_t len) {
|
||||
@@ -344,8 +399,7 @@ static connection *connection_new(int fd) {
|
||||
|
||||
conn = malloc(sizeof(connection));
|
||||
|
||||
rv = nghttp2_session_server_new2(&conn->session, shared_callbacks, conn,
|
||||
shared_option);
|
||||
rv = nghttp2_session_server_new(&conn->session, shared_callbacks, conn);
|
||||
|
||||
if (rv != 0) {
|
||||
goto cleanup;
|
||||
@@ -614,19 +668,52 @@ static void stream_error(connection *conn, int32_t stream_id,
|
||||
error_code);
|
||||
}
|
||||
|
||||
static int send_data_callback(nghttp2_session *session _U_,
|
||||
nghttp2_frame *frame, const uint8_t *framehd,
|
||||
size_t length, nghttp2_data_source *source,
|
||||
void *user_data) {
|
||||
connection *conn = user_data;
|
||||
uint8_t *p = conn->buf.last;
|
||||
stream *strm = source->ptr;
|
||||
|
||||
/* We never use padding in this program */
|
||||
assert(frame->data.padlen == 0);
|
||||
|
||||
if ((size_t)io_buf_left(&conn->buf) < 9 + frame->hd.length) {
|
||||
return NGHTTP2_ERR_WOULDBLOCK;
|
||||
}
|
||||
|
||||
memcpy(p, framehd, 9);
|
||||
p += 9;
|
||||
|
||||
while (length) {
|
||||
ssize_t nread;
|
||||
while ((nread = read(strm->filefd, p, length)) == -1 && errno == EINTR)
|
||||
;
|
||||
if (nread == -1) {
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
length -= nread;
|
||||
p += nread;
|
||||
}
|
||||
|
||||
conn->buf.last = p;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t fd_read_callback(nghttp2_session *session _U_,
|
||||
int32_t stream_id _U_, uint8_t *buf,
|
||||
int32_t stream_id _U_, uint8_t *buf _U_,
|
||||
size_t length, uint32_t *data_flags,
|
||||
nghttp2_data_source *source,
|
||||
void *user_data _U_) {
|
||||
stream *strm = source->ptr;
|
||||
ssize_t nread;
|
||||
ssize_t nread =
|
||||
(int64_t)length < strm->fileleft ? (int64_t)length : strm->fileleft;
|
||||
|
||||
*data_flags |= NGHTTP2_DATA_FLAG_NO_COPY;
|
||||
|
||||
while ((nread = read(strm->filefd, buf, length)) == -1 && errno == EINTR)
|
||||
;
|
||||
if (nread == -1) {
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
strm->fileleft -= nread;
|
||||
if (nread == 0 || strm->fileleft == 0) {
|
||||
if (strm->fileleft != 0) {
|
||||
@@ -925,8 +1012,7 @@ 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 _U_,
|
||||
void *user_data) {
|
||||
connection *conn = user_data;
|
||||
void *user_data _U_) {
|
||||
stream *strm;
|
||||
|
||||
if (frame->hd.type != NGHTTP2_HEADERS ||
|
||||
@@ -940,74 +1026,42 @@ static int on_header_callback(nghttp2_session *session,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!nghttp2_check_header_name(name, namelen) ||
|
||||
!nghttp2_check_header_value(value, valuelen)) {
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
if (memseq(name, namelen, ":method")) {
|
||||
if (strm->method) {
|
||||
stream_error(conn, strm->stream_id, NGHTTP2_PROTOCOL_ERROR);
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
switch (lookup_token(name, namelen)) {
|
||||
case NGHTTP2_TOKEN__METHOD:
|
||||
strm->method = io_buf_add_str(&strm->scrbuf, value, valuelen);
|
||||
if (!strm->method) {
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
strm->methodlen = valuelen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (memseq(name, namelen, ":scheme")) {
|
||||
if (strm->scheme) {
|
||||
stream_error(conn, strm->stream_id, NGHTTP2_PROTOCOL_ERROR);
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
break;
|
||||
case NGHTTP2_TOKEN__SCHEME:
|
||||
strm->scheme = io_buf_add_str(&strm->scrbuf, value, valuelen);
|
||||
if (!strm->scheme) {
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
strm->schemelen = valuelen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (memseq(name, namelen, ":authority")) {
|
||||
if (strm->authority) {
|
||||
stream_error(conn, strm->stream_id, NGHTTP2_PROTOCOL_ERROR);
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
break;
|
||||
case NGHTTP2_TOKEN__AUTHORITY:
|
||||
strm->authority = io_buf_add_str(&strm->scrbuf, value, valuelen);
|
||||
if (!strm->authority) {
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
strm->authoritylen = valuelen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (memseq(name, namelen, ":path")) {
|
||||
if (strm->path) {
|
||||
stream_error(conn, strm->stream_id, NGHTTP2_PROTOCOL_ERROR);
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
break;
|
||||
case NGHTTP2_TOKEN__PATH:
|
||||
strm->path = io_buf_add_str(&strm->scrbuf, value, valuelen);
|
||||
if (!strm->path) {
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
strm->pathlen = valuelen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (name[0] == ':') {
|
||||
stream_error(conn, strm->stream_id, NGHTTP2_PROTOCOL_ERROR);
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
if (memseq(name, namelen, "host") && !strm->host) {
|
||||
break;
|
||||
case NGHTTP2_TOKEN_HOST:
|
||||
strm->host = io_buf_add_str(&strm->scrbuf, value, valuelen);
|
||||
if (!strm->host) {
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
strm->hostlen = valuelen;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -1029,12 +1083,6 @@ static int on_frame_recv_callback(nghttp2_session *session,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strm->method || !strm->scheme || !strm->path ||
|
||||
(!strm->authority && !strm->host)) {
|
||||
stream_error(conn, strm->stream_id, NGHTTP2_PROTOCOL_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strm->host) {
|
||||
strm->host = strm->authority;
|
||||
strm->hostlen = strm->authoritylen;
|
||||
@@ -1269,14 +1317,8 @@ int main(int argc, char **argv) {
|
||||
shared_callbacks, on_stream_close_callback);
|
||||
nghttp2_session_callbacks_set_on_frame_not_send_callback(
|
||||
shared_callbacks, on_frame_not_send_callback);
|
||||
|
||||
rv = nghttp2_option_new(&shared_option);
|
||||
if (rv != 0) {
|
||||
fprintf(stderr, "nghttp2_option_new: %s", nghttp2_strerror(rv));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
nghttp2_option_set_recv_client_preface(shared_option, 1);
|
||||
nghttp2_session_callbacks_set_send_data_callback(shared_callbacks,
|
||||
send_data_callback);
|
||||
|
||||
rv = io_loop_add(&loop, serv.fd, EPOLLIN, &serv);
|
||||
|
||||
@@ -1294,7 +1336,6 @@ int main(int argc, char **argv) {
|
||||
|
||||
io_loop_run(&loop, &serv);
|
||||
|
||||
nghttp2_option_del(shared_option);
|
||||
nghttp2_session_callbacks_del(shared_callbacks);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from gentokenlookup import gentokenlookup
|
||||
|
||||
HEADERS = [
|
||||
':authority',
|
||||
':method',
|
||||
@@ -21,6 +23,11 @@ HEADERS = [
|
||||
"content-length",
|
||||
"location",
|
||||
"trailer",
|
||||
"link",
|
||||
"accept-encoding",
|
||||
"accept-language",
|
||||
"cache-control",
|
||||
"user-agent",
|
||||
# disallowed h1 headers
|
||||
'connection',
|
||||
'keep-alive',
|
||||
@@ -29,70 +36,5 @@ HEADERS = [
|
||||
'upgrade'
|
||||
]
|
||||
|
||||
def to_enum_hd(k):
|
||||
res = 'HD_'
|
||||
for c in k.upper():
|
||||
if c == ':' or c == '-':
|
||||
res += '_'
|
||||
continue
|
||||
res += c
|
||||
return res
|
||||
|
||||
def build_header(headers):
|
||||
res = {}
|
||||
for k in headers:
|
||||
size = len(k)
|
||||
if size not in res:
|
||||
res[size] = {}
|
||||
ent = res[size]
|
||||
c = k[-1]
|
||||
if c not in ent:
|
||||
ent[c] = []
|
||||
ent[c].append(k)
|
||||
|
||||
return res
|
||||
|
||||
def gen_enum():
|
||||
print '''\
|
||||
enum {'''
|
||||
for k in sorted(HEADERS):
|
||||
print '''\
|
||||
{},'''.format(to_enum_hd(k))
|
||||
print '''\
|
||||
HD_MAXIDX,
|
||||
};'''
|
||||
|
||||
def gen_index_header():
|
||||
print '''\
|
||||
int lookup_token(const uint8_t *name, size_t namelen) {
|
||||
switch (namelen) {'''
|
||||
b = build_header(HEADERS)
|
||||
for size in sorted(b.keys()):
|
||||
ents = b[size]
|
||||
print '''\
|
||||
case {}:'''.format(size)
|
||||
print '''\
|
||||
switch (name[namelen - 1]) {'''
|
||||
for c in sorted(ents.keys()):
|
||||
headers = sorted(ents[c])
|
||||
print '''\
|
||||
case '{}':'''.format(c)
|
||||
for k in headers:
|
||||
print '''\
|
||||
if (util::streq("{}", name, {})) {{
|
||||
return {};
|
||||
}}'''.format(k[:-1], size - 1, to_enum_hd(k))
|
||||
print '''\
|
||||
break;'''
|
||||
print '''\
|
||||
}
|
||||
break;'''
|
||||
print '''\
|
||||
}
|
||||
return -1;
|
||||
}'''
|
||||
|
||||
if __name__ == '__main__':
|
||||
gen_enum()
|
||||
print ''
|
||||
gen_index_header()
|
||||
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')
|
||||
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)
|
||||
64
help2rst.py
64
help2rst.py
@@ -4,6 +4,7 @@
|
||||
# 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
|
||||
@@ -44,8 +45,8 @@ def help2man(infile):
|
||||
line = infile.readline().strip()
|
||||
m = re.match(r'^Usage: (.*)', line)
|
||||
if not m:
|
||||
print 'usage line is invalid. Expected following lines:'
|
||||
print 'Usage: cmdname ...'
|
||||
print('usage line is invalid. Expected following lines:')
|
||||
print('Usage: cmdname ...')
|
||||
sys.exit(1)
|
||||
synopsis = m.group(1).split(' ', 1)
|
||||
if len(synopsis) == 2:
|
||||
@@ -60,7 +61,11 @@ def help2man(infile):
|
||||
break
|
||||
description.append(line)
|
||||
|
||||
print '''
|
||||
print('''
|
||||
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
|
||||
|
||||
.. program:: {cmdname}
|
||||
|
||||
{cmdname}(1)
|
||||
{cmdnameunderline}
|
||||
|
||||
@@ -75,39 +80,48 @@ DESCRIPTION
|
||||
{description}
|
||||
'''.format(cmdname=cmdname, args=args,
|
||||
cmdnameunderline='=' * (len(cmdname) + 3),
|
||||
synopsis=synopsis, description=format_text('\n'.join(description)))
|
||||
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 ''
|
||||
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):]))
|
||||
print('{}'.format(format_arg_text(line[len(arg_indent):])))
|
||||
continue
|
||||
|
||||
if in_arg:
|
||||
print ''
|
||||
print()
|
||||
in_arg = False
|
||||
|
||||
if line == '--':
|
||||
in_footer = True
|
||||
continue
|
||||
|
||||
if in_footer:
|
||||
print(line.strip())
|
||||
continue
|
||||
|
||||
if line == 'Options:':
|
||||
print 'OPTIONS:'
|
||||
print '--------'
|
||||
print ''
|
||||
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()))
|
||||
print('.. describe:: {}'.format(argname))
|
||||
print()
|
||||
print('{}'.format(format_arg_text(rest.strip())))
|
||||
in_arg = True
|
||||
continue
|
||||
|
||||
@@ -115,9 +129,9 @@ DESCRIPTION
|
||||
# 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()))
|
||||
print('.. describe:: {}'.format(argname))
|
||||
print()
|
||||
print('{}'.format(format_arg_text(rest.strip())))
|
||||
in_arg = True
|
||||
continue
|
||||
|
||||
@@ -127,23 +141,23 @@ DESCRIPTION
|
||||
r'^(?:\s+)(-\S+?(?:, -\S+?)*)($| .*)',
|
||||
line)
|
||||
argname, rest = m.group(1), m.group(2)
|
||||
print '.. option:: {}'.format(argname)
|
||||
print ''
|
||||
print('.. option:: {}'.format(argname))
|
||||
print()
|
||||
rest = rest.strip()
|
||||
if len(rest):
|
||||
print '{}'.format(format_arg_text(rest))
|
||||
print('{}'.format(format_arg_text(rest)))
|
||||
in_arg = True
|
||||
continue
|
||||
|
||||
if not line.startswith(' ') and line.endswith(':'):
|
||||
# subsection
|
||||
subsec = line.strip()
|
||||
print '{}'.format(subsec)
|
||||
print '{}'.format('~' * len(subsec))
|
||||
print ''
|
||||
subsec = line.strip()[:-1]
|
||||
print('{}'.format(subsec))
|
||||
print('{}'.format('~' * len(subsec)))
|
||||
print()
|
||||
continue
|
||||
|
||||
print line.strip()
|
||||
print(line.strip())
|
||||
|
||||
def format_text(text):
|
||||
# escape *
|
||||
@@ -173,6 +187,6 @@ if __name__ == '__main__':
|
||||
args = parser.parse_args()
|
||||
help2man(sys.stdin)
|
||||
if args.include:
|
||||
print ''
|
||||
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
|
||||
@@ -32,12 +32,11 @@ EXTRA_DIST = \
|
||||
alt-server.crt \
|
||||
setenv
|
||||
|
||||
.PHONY: itprep it
|
||||
|
||||
itprep:
|
||||
itprep-local:
|
||||
go get -d -v github.com/bradfitz/http2
|
||||
go get -d -v github.com/tatsuhiro-t/go-nghttp2
|
||||
go get -d -v golang.org/x/net/spdy
|
||||
go get -d -v github.com/tatsuhiro-t/spdy
|
||||
go get -d -v golang.org/x/net/websocket
|
||||
|
||||
it:
|
||||
it-local:
|
||||
sh setenv go test -v
|
||||
|
||||
@@ -2,8 +2,10 @@ package nghttp2
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/bradfitz/http2/hpack"
|
||||
"golang.org/x/net/websocket"
|
||||
"io"
|
||||
"net/http"
|
||||
"syscall"
|
||||
@@ -50,6 +52,27 @@ func TestH1H1PlainGETClose(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H1InvalidMethod tests that server rejects invalid method with
|
||||
// 501 status code
|
||||
func TestH1H1InvalidMethod(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Errorf("server should not forward this request")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http1(requestParam{
|
||||
name: "TestH1H1InvalidMethod",
|
||||
method: "get",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http1() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.status, 501; got != want {
|
||||
t.Errorf("status = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H1MultipleRequestCL tests that server rejects request which
|
||||
// contains multiple Content-Length header fields.
|
||||
func TestH1H1MultipleRequestCL(t *testing.T) {
|
||||
@@ -139,6 +162,199 @@ func TestH1H1GracefulShutdown(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H1HostRewrite tests that server rewrites Host header field
|
||||
func TestH1H1HostRewrite(t *testing.T) {
|
||||
st := newServerTester([]string{"--host-rewrite"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("request-host", r.Host)
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http1(requestParam{
|
||||
name: "TestH1H1HostRewrite",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http1() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
if got, want := res.header.Get("request-host"), st.backendHost; got != want {
|
||||
t.Errorf("request-host: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H1HTTP10 tests that server can accept HTTP/1.0 request
|
||||
// without Host header field
|
||||
func TestH1H1HTTP10(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("request-host", r.Host)
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H1HTTP10\r\n\r\n"); err != nil {
|
||||
t.Fatalf("Error io.WriteString() = %v", err)
|
||||
}
|
||||
|
||||
resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Error http.ReadResponse() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := resp.StatusCode, 200; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
if got, want := resp.Header.Get("request-host"), st.backendHost; got != want {
|
||||
t.Errorf("request-host: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H1HTTP10NoHostRewrite tests that server generates host header
|
||||
// field using actual backend server even if --no-http-rewrite is
|
||||
// used.
|
||||
func TestH1H1HTTP10NoHostRewrite(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("request-host", r.Host)
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H1HTTP10NoHostRewrite\r\n\r\n"); err != nil {
|
||||
t.Fatalf("Error io.WriteString() = %v", err)
|
||||
}
|
||||
|
||||
resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Error http.ReadResponse() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := resp.StatusCode, 200; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
if got, want := resp.Header.Get("request-host"), st.backendHost; got != want {
|
||||
t.Errorf("request-host: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H1RequestTrailer tests request trailer part is forwarded to
|
||||
// backend.
|
||||
func TestH1H1RequestTrailer(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
buf := make([]byte, 4096)
|
||||
for {
|
||||
_, err := r.Body.Read(buf)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("r.Body.Read() = %v", err)
|
||||
}
|
||||
}
|
||||
if got, want := r.Trailer.Get("foo"), "bar"; got != want {
|
||||
t.Errorf("r.Trailer.Get(foo): %v; want %v", got, want)
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http1(requestParam{
|
||||
name: "TestH1H1RequestTrailer",
|
||||
body: []byte("1"),
|
||||
trailer: []hpack.HeaderField{
|
||||
pair("foo", "bar"),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http1() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("res.status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H1HeaderFieldBufferPath tests that request with request path
|
||||
// larger than configured buffer size is rejected.
|
||||
func TestH1H1HeaderFieldBufferPath(t *testing.T) {
|
||||
// The value 100 is chosen so that sum of header fields bytes
|
||||
// does not exceed it. We use > 100 bytes URI to exceed this
|
||||
// limit.
|
||||
st := newServerTester([]string{"--header-field-buffer=100"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatal("execution path should not be here")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http1(requestParam{
|
||||
name: "TestH1H1HeaderFieldBufferPath",
|
||||
path: "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http1() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 431; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H1HeaderFieldBuffer tests that request with header fields
|
||||
// larger than configured buffer size is rejected.
|
||||
func TestH1H1HeaderFieldBuffer(t *testing.T) {
|
||||
st := newServerTester([]string{"--header-field-buffer=10"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatal("execution path should not be here")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http1(requestParam{
|
||||
name: "TestH1H1HeaderFieldBuffer",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http1() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 431; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H1HeaderFields tests that request with header fields more
|
||||
// than configured number is rejected.
|
||||
func TestH1H1HeaderFields(t *testing.T) {
|
||||
st := newServerTester([]string{"--max-header-fields=1"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatal("execution path should not be here")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http1(requestParam{
|
||||
name: "TestH1H1HeaderFields",
|
||||
header: []hpack.HeaderField{
|
||||
// Add extra header field to ensure that
|
||||
// header field limit exceeds
|
||||
pair("Connection", "close"),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http1() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 431; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H1Websocket tests that HTTP Upgrade to WebSocket works.
|
||||
func TestH1H1Websocket(t *testing.T) {
|
||||
st := newServerTesterHandler(nil, t, websocket.Handler(func(ws *websocket.Conn) {
|
||||
io.Copy(ws, ws)
|
||||
}))
|
||||
defer st.Close()
|
||||
|
||||
content := []byte("hello world")
|
||||
res, err := st.websocket(requestParam{
|
||||
name: "TestH1H1Websocket",
|
||||
body: content,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.websocket() = %v", err)
|
||||
}
|
||||
if got, want := res.body, content; !bytes.Equal(got, want) {
|
||||
t.Errorf("echo: %q; want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H2ConnectFailure tests that server handles the situation that
|
||||
// connection attempt to HTTP/2 backend failed.
|
||||
func TestH1H2ConnectFailure(t *testing.T) {
|
||||
@@ -184,6 +400,57 @@ func TestH1H2NoHost(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H2HTTP10 tests that server can accept HTTP/1.0 request
|
||||
// without Host header field
|
||||
func TestH1H2HTTP10(t *testing.T) {
|
||||
st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("request-host", r.Host)
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H2HTTP10\r\n\r\n"); err != nil {
|
||||
t.Fatalf("Error io.WriteString() = %v", err)
|
||||
}
|
||||
|
||||
resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Error http.ReadResponse() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := resp.StatusCode, 200; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
if got, want := resp.Header.Get("request-host"), st.backendHost; got != want {
|
||||
t.Errorf("request-host: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H2HTTP10NoHostRewrite tests that server generates host header
|
||||
// field using actual backend server even if --no-http-rewrite is
|
||||
// used.
|
||||
func TestH1H2HTTP10NoHostRewrite(t *testing.T) {
|
||||
st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("request-host", r.Host)
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H2HTTP10NoHostRewrite\r\n\r\n"); err != nil {
|
||||
t.Fatalf("Error io.WriteString() = %v", err)
|
||||
}
|
||||
|
||||
resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Error http.ReadResponse() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := resp.StatusCode, 200; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
if got, want := resp.Header.Get("request-host"), st.backendHost; got != want {
|
||||
t.Errorf("request-host: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H2CrumbleCookie tests that Cookies are crumbled and assembled
|
||||
// when forwarding to HTTP/2 backend link. go-nghttp2 server
|
||||
// concatenates crumbled Cookies automatically, so this test is not
|
||||
@@ -209,3 +476,74 @@ func TestH1H2CrumbleCookie(t *testing.T) {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H2GenerateVia tests that server generates Via header field to and
|
||||
// from backend server.
|
||||
func TestH1H2GenerateVia(t *testing.T) {
|
||||
st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if got, want := r.Header.Get("Via"), "1.1 nghttpx"; got != want {
|
||||
t.Errorf("Via: %v; want %v", got, want)
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http1(requestParam{
|
||||
name: "TestH1H2GenerateVia",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http1() = %v", err)
|
||||
}
|
||||
if got, want := res.header.Get("Via"), "2 nghttpx"; got != want {
|
||||
t.Errorf("Via: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H2AppendVia tests that server adds value to existing Via
|
||||
// header field to and from backend server.
|
||||
func TestH1H2AppendVia(t *testing.T) {
|
||||
st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if got, want := r.Header.Get("Via"), "foo, 1.1 nghttpx"; got != want {
|
||||
t.Errorf("Via: %v; want %v", got, want)
|
||||
}
|
||||
w.Header().Add("Via", "bar")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http1(requestParam{
|
||||
name: "TestH1H2AppendVia",
|
||||
header: []hpack.HeaderField{
|
||||
pair("via", "foo"),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http1() = %v", err)
|
||||
}
|
||||
if got, want := res.header.Get("Via"), "bar, 2 nghttpx"; got != want {
|
||||
t.Errorf("Via: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H2NoVia tests that server does not add value to existing Via
|
||||
// header field to and from backend server.
|
||||
func TestH1H2NoVia(t *testing.T) {
|
||||
st := newServerTester([]string{"--http2-bridge", "--no-via"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if got, want := r.Header.Get("Via"), "foo"; got != want {
|
||||
t.Errorf("Via: %v; want %v", got, want)
|
||||
}
|
||||
w.Header().Add("Via", "bar")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http1(requestParam{
|
||||
name: "TestH1H2NoVia",
|
||||
header: []hpack.HeaderField{
|
||||
pair("via", "foo"),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http1() = %v", err)
|
||||
}
|
||||
if got, want := res.header.Get("Via"), "bar"; got != want {
|
||||
t.Errorf("Via: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,11 +8,12 @@ import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestH1H2PlainGET tests whether simple HTTP/2 GET request works.
|
||||
// TestH2H1PlainGET tests whether simple HTTP/2 GET request works.
|
||||
func TestH2H1PlainGET(t *testing.T) {
|
||||
st := newServerTester(nil, t, noopHandler)
|
||||
defer st.Close()
|
||||
@@ -121,6 +122,120 @@ func TestH2H1StripAddXff(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1GenerateVia tests that server generates Via header field to and
|
||||
// from backend server.
|
||||
func TestH2H1GenerateVia(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if got, want := r.Header.Get("Via"), "2 nghttpx"; got != want {
|
||||
t.Errorf("Via: %v; want %v", got, want)
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1GenerateVia",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.header.Get("Via"), "1.1 nghttpx"; got != want {
|
||||
t.Errorf("Via: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1AppendVia tests that server adds value to existing Via
|
||||
// header field to and from backend server.
|
||||
func TestH2H1AppendVia(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if got, want := r.Header.Get("Via"), "foo, 2 nghttpx"; got != want {
|
||||
t.Errorf("Via: %v; want %v", got, want)
|
||||
}
|
||||
w.Header().Add("Via", "bar")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1AppendVia",
|
||||
header: []hpack.HeaderField{
|
||||
pair("via", "foo"),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.header.Get("Via"), "bar, 1.1 nghttpx"; got != want {
|
||||
t.Errorf("Via: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1NoVia tests that server does not add value to existing Via
|
||||
// header field to and from backend server.
|
||||
func TestH2H1NoVia(t *testing.T) {
|
||||
st := newServerTester([]string{"--no-via"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if got, want := r.Header.Get("Via"), "foo"; got != want {
|
||||
t.Errorf("Via: %v; want %v", got, want)
|
||||
}
|
||||
w.Header().Add("Via", "bar")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1NoVia",
|
||||
header: []hpack.HeaderField{
|
||||
pair("via", "foo"),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.header.Get("Via"), "bar"; got != want {
|
||||
t.Errorf("Via: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1HostRewrite tests that server rewrites host header field
|
||||
func TestH2H1HostRewrite(t *testing.T) {
|
||||
st := newServerTester([]string{"--host-rewrite"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("request-host", r.Host)
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1HostRewrite",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
if got, want := res.header.Get("request-host"), st.backendHost; got != want {
|
||||
t.Errorf("request-host: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1NoHostRewrite tests that server does not rewrite host
|
||||
// header field
|
||||
func TestH2H1NoHostRewrite(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("request-host", r.Host)
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1NoHostRewrite",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
if got, want := res.header.Get("request-host"), st.frontendHost; got != want {
|
||||
t.Errorf("request-host: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1BadRequestCL tests that server rejects request whose
|
||||
// content-length header field value does not match its request body
|
||||
// size.
|
||||
@@ -241,9 +356,8 @@ func TestH2H1MultipleRequestCL(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
want := 400
|
||||
if got := res.status; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
if got, want := res.errCode, http2.ErrCodeProtocol; got != want {
|
||||
t.Errorf("res.errCode: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,9 +378,8 @@ func TestH2H1InvalidRequestCL(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
want := 400
|
||||
if got := res.status; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
if got, want := res.errCode, http2.ErrCodeProtocol; got != want {
|
||||
t.Errorf("res.errCode: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -291,6 +404,26 @@ func TestH2H1ConnectFailure(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1InvalidMethod tests that server rejects invalid method with
|
||||
// 501.
|
||||
func TestH2H1InvalidMethod(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Errorf("server should not forward this request")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1InvalidMethod",
|
||||
method: "get",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 501; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1AssembleCookies tests that crumbled cookies in HTTP/2
|
||||
// request is assembled into 1 when forwarding to HTTP/1 backend link.
|
||||
func TestH2H1AssembleCookies(t *testing.T) {
|
||||
@@ -359,6 +492,9 @@ func TestH2H1TEGzip(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1SNI tests server's TLS SNI extension feature. It must
|
||||
// choose appropriate certificate depending on the indicated
|
||||
// server_name from client.
|
||||
func TestH2H1SNI(t *testing.T) {
|
||||
st := newServerTesterTLSConfig([]string{"--subcert=" + testDir + "/alt-server.key:" + testDir + "/alt-server.crt"}, t, noopHandler, &tls.Config{
|
||||
ServerName: "alt-domain",
|
||||
@@ -374,6 +510,169 @@ func TestH2H1SNI(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1TLSXfp tests nghttpx sends x-forwarded-proto header field
|
||||
// with http value since :scheme is http, even if the frontend
|
||||
// connection is encrypted.
|
||||
func TestH2H1TLSXfp(t *testing.T) {
|
||||
st := newServerTesterTLS(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if got, want := r.Header.Get("x-forwarded-proto"), "http"; got != want {
|
||||
t.Errorf("x-forwarded-proto: want %v; got %v", want, got)
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1TLSXfp",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("res.status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1ServerPush tests server push using Link header field from
|
||||
// backend server.
|
||||
func TestH2H1ServerPush(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
// only resources marked as rel=preload are pushed
|
||||
if !strings.HasPrefix(r.URL.Path, "/css/") {
|
||||
w.Header().Add("Link", "</css/main.css>; rel=preload, </foo>, </css/theme.css>; rel=preload")
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1ServerPush",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("res.status: %v; want %v", got, want)
|
||||
}
|
||||
if got, want := len(res.pushResponse), 2; got != want {
|
||||
t.Fatalf("len(res.pushResponse): %v; want %v", got, want)
|
||||
}
|
||||
mainCSS := res.pushResponse[0]
|
||||
if got, want := mainCSS.status, 200; got != want {
|
||||
t.Errorf("mainCSS.status: %v; want %v", got, want)
|
||||
}
|
||||
themeCSS := res.pushResponse[1]
|
||||
if got, want := themeCSS.status, 200; got != want {
|
||||
t.Errorf("themeCSS.status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1RequestTrailer tests request trailer part is forwarded to
|
||||
// backend.
|
||||
func TestH2H1RequestTrailer(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
buf := make([]byte, 4096)
|
||||
for {
|
||||
_, err := r.Body.Read(buf)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("r.Body.Read() = %v", err)
|
||||
}
|
||||
}
|
||||
if got, want := r.Trailer.Get("foo"), "bar"; got != want {
|
||||
t.Errorf("r.Trailer.Get(foo): %v; want %v", got, want)
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1RequestTrailer",
|
||||
body: []byte("1"),
|
||||
trailer: []hpack.HeaderField{
|
||||
pair("foo", "bar"),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("res.status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1HeaderFieldBuffer tests that request with header fields
|
||||
// larger than configured buffer size is rejected.
|
||||
func TestH2H1HeaderFieldBuffer(t *testing.T) {
|
||||
st := newServerTester([]string{"--header-field-buffer=10"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatal("execution path should not be here")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1HeaderFieldBuffer",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 431; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1HeaderFields tests that request with header fields more
|
||||
// than configured number is rejected.
|
||||
func TestH2H1HeaderFields(t *testing.T) {
|
||||
st := newServerTester([]string{"--max-header-fields=1"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatal("execution path should not be here")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1HeaderFields",
|
||||
// we have at least 4 pseudo-header fields sent, and
|
||||
// that ensures that buffer limit exceeds.
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 431; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1Upgrade tests HTTP Upgrade to HTTP/2
|
||||
func TestH2H1Upgrade(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http1(requestParam{
|
||||
name: "TestH2H1Upgrade",
|
||||
header: []hpack.HeaderField{
|
||||
pair("Connection", "Upgrade, HTTP2-Settings"),
|
||||
pair("Upgrade", "h2c"),
|
||||
pair("HTTP2-Settings", "AAMAAABkAAQAAP__"),
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http1() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.status, 101; got != want {
|
||||
t.Errorf("res.status: %v; want %v", got, want)
|
||||
}
|
||||
|
||||
res, err = st.http2(requestParam{
|
||||
httpUpgrade: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("res.status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1GracefulShutdown tests graceful shutdown.
|
||||
func TestH2H1GracefulShutdown(t *testing.T) {
|
||||
st := newServerTester(nil, t, noopHandler)
|
||||
@@ -466,9 +765,8 @@ func TestH2H2MultipleResponseCL(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
want := 502
|
||||
if got := res.status; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
if got, want := res.errCode, http2.ErrCodeInternal; got != want {
|
||||
t.Errorf("res.errCode: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -487,9 +785,8 @@ func TestH2H2InvalidResponseCL(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
want := 502
|
||||
if got := res.status; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
if got, want := res.errCode, http2.ErrCodeInternal; got != want {
|
||||
t.Errorf("res.errCode: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -513,3 +810,68 @@ func TestH2H2ConnectFailure(t *testing.T) {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H2HostRewrite tests that server rewrites host header field
|
||||
func TestH2H2HostRewrite(t *testing.T) {
|
||||
st := newServerTester([]string{"--http2-bridge", "--host-rewrite"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("request-host", r.Host)
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H2HostRewrite",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
if got, want := res.header.Get("request-host"), st.backendHost; got != want {
|
||||
t.Errorf("request-host: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H2NoHostRewrite tests that server does not rewrite host
|
||||
// header field
|
||||
func TestH2H2NoHostRewrite(t *testing.T) {
|
||||
st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("request-host", r.Host)
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H2NoHostRewrite",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
if got, want := res.header.Get("request-host"), st.frontendHost; got != want {
|
||||
t.Errorf("request-host: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H2TLSXfp tests nghttpx sends x-forwarded-proto header field
|
||||
// with http value since :scheme is http, even if the frontend
|
||||
// connection is encrypted.
|
||||
func TestH2H2TLSXfp(t *testing.T) {
|
||||
st := newServerTesterTLS([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if got, want := r.Header.Get("x-forwarded-proto"), "http"; got != want {
|
||||
t.Errorf("x-forwarded-proto: want %v; got %v", want, got)
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H2TLSXfp",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("res.status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package nghttp2
|
||||
|
||||
import (
|
||||
"github.com/bradfitz/http2/hpack"
|
||||
"golang.org/x/net/spdy"
|
||||
"github.com/tatsuhiro-t/spdy"
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
@@ -99,6 +99,137 @@ func TestS3H1InvalidRequestCL(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestS3H1GenerateVia tests that server generates Via header field to and
|
||||
// from backend server.
|
||||
func TestS3H1GenerateVia(t *testing.T) {
|
||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if got, want := r.Header.Get("Via"), "1.1 nghttpx"; got != want {
|
||||
t.Errorf("Via: %v; want %v", got, want)
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.spdy(requestParam{
|
||||
name: "TestS3H1GenerateVia",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.spdy() = %v", err)
|
||||
}
|
||||
if got, want := res.header.Get("Via"), "1.1 nghttpx"; got != want {
|
||||
t.Errorf("Via: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestS3H1AppendVia tests that server adds value to existing Via
|
||||
// header field to and from backend server.
|
||||
func TestS3H1AppendVia(t *testing.T) {
|
||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if got, want := r.Header.Get("Via"), "foo, 1.1 nghttpx"; got != want {
|
||||
t.Errorf("Via: %v; want %v", got, want)
|
||||
}
|
||||
w.Header().Add("Via", "bar")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.spdy(requestParam{
|
||||
name: "TestS3H1AppendVia",
|
||||
header: []hpack.HeaderField{
|
||||
pair("via", "foo"),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.spdy() = %v", err)
|
||||
}
|
||||
if got, want := res.header.Get("Via"), "bar, 1.1 nghttpx"; got != want {
|
||||
t.Errorf("Via: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestS3H1NoVia tests that server does not add value to existing Via
|
||||
// header field to and from backend server.
|
||||
func TestS3H1NoVia(t *testing.T) {
|
||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--no-via"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if got, want := r.Header.Get("Via"), "foo"; got != want {
|
||||
t.Errorf("Via: %v; want %v", got, want)
|
||||
}
|
||||
w.Header().Add("Via", "bar")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.spdy(requestParam{
|
||||
name: "TestS3H1NoVia",
|
||||
header: []hpack.HeaderField{
|
||||
pair("via", "foo"),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.spdy() = %v", err)
|
||||
}
|
||||
if got, want := res.header.Get("Via"), "bar"; got != want {
|
||||
t.Errorf("Via: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestS3H1HeaderFieldBuffer tests that request with header fields
|
||||
// larger than configured buffer size is rejected.
|
||||
func TestS3H1HeaderFieldBuffer(t *testing.T) {
|
||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--header-field-buffer=10"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatal("execution path should not be here")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.spdy(requestParam{
|
||||
name: "TestS3H1HeaderFieldBuffer",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.spdy() = %v", err)
|
||||
}
|
||||
if got, want := res.spdyRstErrCode, spdy.InternalError; got != want {
|
||||
t.Errorf("res.spdyRstErrCode: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestS3H1HeaderFields tests that request with header fields more
|
||||
// than configured number is rejected.
|
||||
func TestS3H1HeaderFields(t *testing.T) {
|
||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--max-header-fields=1"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatal("execution path should not be here")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.spdy(requestParam{
|
||||
name: "TestS3H1HeaderFields",
|
||||
// we have at least 5 pseudo-header fields sent, and
|
||||
// that ensures that buffer limit exceeds.
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.spdy() = %v", err)
|
||||
}
|
||||
if got, want := res.spdyRstErrCode, spdy.InternalError; got != want {
|
||||
t.Errorf("res.spdyRstErrCode: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestS3H1InvalidMethod tests that server rejects invalid method with
|
||||
// 501.
|
||||
func TestS3H1InvalidMethod(t *testing.T) {
|
||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Errorf("server should not forward this request")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.spdy(requestParam{
|
||||
name: "TestS3H1InvalidMethod",
|
||||
method: "get",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.spdy() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 501; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestS3H2ConnectFailure tests that server handles the situation that
|
||||
// connection attempt to HTTP/2 backend failed.
|
||||
func TestS3H2ConnectFailure(t *testing.T) {
|
||||
|
||||
@@ -9,7 +9,8 @@ import (
|
||||
"github.com/bradfitz/http2"
|
||||
"github.com/bradfitz/http2/hpack"
|
||||
"github.com/tatsuhiro-t/go-nghttp2"
|
||||
"golang.org/x/net/spdy"
|
||||
"github.com/tatsuhiro-t/spdy"
|
||||
"golang.org/x/net/websocket"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
@@ -17,6 +18,7 @@ import (
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os/exec"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -42,6 +44,8 @@ type serverTester struct {
|
||||
url string // test frontend server URL
|
||||
t *testing.T
|
||||
ts *httptest.Server // backend server
|
||||
frontendHost string // frontend server host
|
||||
backendHost string // backend server host
|
||||
conn net.Conn // connection to frontend server
|
||||
h2PrefaceSent bool // HTTP/2 preface was sent in conn
|
||||
nextStreamID uint32 // next stream ID
|
||||
@@ -63,6 +67,10 @@ func newServerTester(args []string, t *testing.T, handler http.HandlerFunc) *ser
|
||||
return newServerTesterInternal(args, t, handler, false, nil)
|
||||
}
|
||||
|
||||
func newServerTesterHandler(args []string, t *testing.T, handler http.Handler) *serverTester {
|
||||
return newServerTesterInternal(args, t, handler, false, nil)
|
||||
}
|
||||
|
||||
// newServerTester creates test context for TLS frontend connection.
|
||||
func newServerTesterTLS(args []string, t *testing.T, handler http.HandlerFunc) *serverTester {
|
||||
return newServerTesterInternal(args, t, handler, true, nil)
|
||||
@@ -76,7 +84,7 @@ func newServerTesterTLSConfig(args []string, t *testing.T, handler http.HandlerF
|
||||
|
||||
// newServerTesterInternal creates test context. If frontendTLS is
|
||||
// true, set up TLS frontend connection.
|
||||
func newServerTesterInternal(args []string, t *testing.T, handler http.HandlerFunc, frontendTLS bool, clientConfig *tls.Config) *serverTester {
|
||||
func newServerTesterInternal(args []string, t *testing.T, handler http.Handler, frontendTLS bool, clientConfig *tls.Config) *serverTester {
|
||||
ts := httptest.NewUnstartedServer(handler)
|
||||
|
||||
backendTLS := false
|
||||
@@ -124,6 +132,8 @@ func newServerTesterInternal(args []string, t *testing.T, handler http.HandlerFu
|
||||
t: t,
|
||||
ts: ts,
|
||||
url: fmt.Sprintf("%v://%v", scheme, authority),
|
||||
frontendHost: fmt.Sprintf("127.0.0.1:%v", serverPort),
|
||||
backendHost: backendURL.Host,
|
||||
nextStreamID: 1,
|
||||
authority: authority,
|
||||
frCh: make(chan http2.Frame),
|
||||
@@ -242,14 +252,71 @@ func (st *serverTester) readSpdyFrame() (spdy.Frame, error) {
|
||||
}
|
||||
|
||||
type requestParam struct {
|
||||
name string // name for this request to identify the request in log easily
|
||||
streamID uint32 // stream ID, automatically assigned if 0
|
||||
method string // method, defaults to GET
|
||||
scheme string // scheme, defaults to http
|
||||
authority string // authority, defaults to backend server address
|
||||
path string // path, defaults to /
|
||||
header []hpack.HeaderField // additional request header fields
|
||||
body []byte // request body
|
||||
name string // name for this request to identify the request in log easily
|
||||
streamID uint32 // stream ID, automatically assigned if 0
|
||||
method string // method, defaults to GET
|
||||
scheme string // scheme, defaults to http
|
||||
authority string // authority, defaults to backend server address
|
||||
path string // path, defaults to /
|
||||
header []hpack.HeaderField // additional request header fields
|
||||
body []byte // request body
|
||||
trailer []hpack.HeaderField // trailer part
|
||||
httpUpgrade bool // true if upgraded to HTTP/2 through HTTP Upgrade
|
||||
}
|
||||
|
||||
// wrapper for request body to set trailer part
|
||||
type chunkedBodyReader struct {
|
||||
trailer []hpack.HeaderField
|
||||
trailerWritten bool
|
||||
body io.Reader
|
||||
req *http.Request
|
||||
}
|
||||
|
||||
func (cbr *chunkedBodyReader) Read(p []byte) (n int, err error) {
|
||||
// document says that we have to set http.Request.Trailer
|
||||
// after request was sent and before body returns EOF.
|
||||
if !cbr.trailerWritten {
|
||||
cbr.trailerWritten = true
|
||||
for _, h := range cbr.trailer {
|
||||
cbr.req.Trailer.Set(h.Name, h.Value)
|
||||
}
|
||||
}
|
||||
return cbr.body.Read(p)
|
||||
}
|
||||
|
||||
func (st *serverTester) websocket(rp requestParam) (*serverResponse, error) {
|
||||
urlstring := st.url + "/echo"
|
||||
|
||||
config, err := websocket.NewConfig(urlstring, st.url)
|
||||
if err != nil {
|
||||
st.t.Fatalf("websocket.NewConfig(%q, %q) returned error: %v", urlstring, st.url, err)
|
||||
}
|
||||
|
||||
config.Header.Add("Test-Case", rp.name)
|
||||
for _, h := range rp.header {
|
||||
config.Header.Add(h.Name, h.Value)
|
||||
}
|
||||
|
||||
ws, err := websocket.NewClient(config, st.conn)
|
||||
if err != nil {
|
||||
st.t.Fatalf("Error creating websocket client: %v", err)
|
||||
}
|
||||
|
||||
if _, err := ws.Write(rp.body); err != nil {
|
||||
st.t.Fatalf("ws.Write() returned error: %v", err)
|
||||
}
|
||||
|
||||
msg := make([]byte, 1024)
|
||||
var n int
|
||||
if n, err = ws.Read(msg); err != nil {
|
||||
st.t.Fatalf("ws.Read() returned error: %v", err)
|
||||
}
|
||||
|
||||
res := &serverResponse{
|
||||
body: msg[:n],
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (st *serverTester) http1(rp requestParam) (*serverResponse, error) {
|
||||
@@ -259,10 +326,30 @@ func (st *serverTester) http1(rp requestParam) (*serverResponse, error) {
|
||||
}
|
||||
|
||||
var body io.Reader
|
||||
var cbr *chunkedBodyReader
|
||||
if rp.body != nil {
|
||||
body = bytes.NewBuffer(rp.body)
|
||||
if len(rp.trailer) != 0 {
|
||||
cbr = &chunkedBodyReader{
|
||||
trailer: rp.trailer,
|
||||
body: body,
|
||||
}
|
||||
body = cbr
|
||||
}
|
||||
}
|
||||
req, err := http.NewRequest(method, st.url, body)
|
||||
|
||||
reqURL := st.url
|
||||
|
||||
if rp.path != "" {
|
||||
u, err := url.Parse(st.url)
|
||||
if err != nil {
|
||||
st.t.Fatalf("Error parsing URL from st.url %v: %v", st.url, err)
|
||||
}
|
||||
u.Path = rp.path
|
||||
reqURL = u.String()
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, reqURL, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -270,7 +357,15 @@ func (st *serverTester) http1(rp requestParam) (*serverResponse, error) {
|
||||
req.Header.Add(h.Name, h.Value)
|
||||
}
|
||||
req.Header.Add("Test-Case", rp.name)
|
||||
|
||||
if cbr != nil {
|
||||
cbr.req = req
|
||||
// this makes request use chunked encoding
|
||||
req.ContentLength = -1
|
||||
req.Trailer = make(http.Header)
|
||||
for _, h := range cbr.trailer {
|
||||
req.Trailer.Set(h.Name, "")
|
||||
}
|
||||
}
|
||||
if err := req.Write(st.conn); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -407,7 +502,6 @@ loop:
|
||||
}
|
||||
|
||||
func (st *serverTester) http2(rp requestParam) (*serverResponse, error) {
|
||||
res := &serverResponse{}
|
||||
st.headerBlkBuf.Reset()
|
||||
st.header = make(http.Header)
|
||||
|
||||
@@ -430,53 +524,77 @@ func (st *serverTester) http2(rp requestParam) (*serverResponse, error) {
|
||||
}
|
||||
}
|
||||
|
||||
method := "GET"
|
||||
if rp.method != "" {
|
||||
method = rp.method
|
||||
}
|
||||
_ = st.enc.WriteField(pair(":method", method))
|
||||
|
||||
scheme := "http"
|
||||
if rp.scheme != "" {
|
||||
scheme = rp.scheme
|
||||
}
|
||||
_ = st.enc.WriteField(pair(":scheme", scheme))
|
||||
|
||||
authority := st.authority
|
||||
if rp.authority != "" {
|
||||
authority = rp.authority
|
||||
}
|
||||
_ = st.enc.WriteField(pair(":authority", authority))
|
||||
|
||||
path := "/"
|
||||
if rp.path != "" {
|
||||
path = rp.path
|
||||
}
|
||||
_ = st.enc.WriteField(pair(":path", path))
|
||||
|
||||
_ = st.enc.WriteField(pair("test-case", rp.name))
|
||||
|
||||
for _, h := range rp.header {
|
||||
_ = st.enc.WriteField(h)
|
||||
res := &serverResponse{
|
||||
streamID: id,
|
||||
}
|
||||
|
||||
err := st.fr.WriteHeaders(http2.HeadersFrameParam{
|
||||
StreamID: id,
|
||||
EndStream: len(rp.body) == 0,
|
||||
EndHeaders: true,
|
||||
BlockFragment: st.headerBlkBuf.Bytes(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
streams := make(map[uint32]*serverResponse)
|
||||
streams[id] = res
|
||||
|
||||
if len(rp.body) != 0 {
|
||||
// TODO we assume rp.body fits in 1 frame
|
||||
if err := st.fr.WriteData(id, true, rp.body); err != nil {
|
||||
if !rp.httpUpgrade {
|
||||
method := "GET"
|
||||
if rp.method != "" {
|
||||
method = rp.method
|
||||
}
|
||||
_ = st.enc.WriteField(pair(":method", method))
|
||||
|
||||
scheme := "http"
|
||||
if rp.scheme != "" {
|
||||
scheme = rp.scheme
|
||||
}
|
||||
_ = st.enc.WriteField(pair(":scheme", scheme))
|
||||
|
||||
authority := st.authority
|
||||
if rp.authority != "" {
|
||||
authority = rp.authority
|
||||
}
|
||||
_ = st.enc.WriteField(pair(":authority", authority))
|
||||
|
||||
path := "/"
|
||||
if rp.path != "" {
|
||||
path = rp.path
|
||||
}
|
||||
_ = st.enc.WriteField(pair(":path", path))
|
||||
|
||||
_ = st.enc.WriteField(pair("test-case", rp.name))
|
||||
|
||||
for _, h := range rp.header {
|
||||
_ = st.enc.WriteField(h)
|
||||
}
|
||||
|
||||
err := st.fr.WriteHeaders(http2.HeadersFrameParam{
|
||||
StreamID: id,
|
||||
EndStream: len(rp.body) == 0 && len(rp.trailer) == 0,
|
||||
EndHeaders: true,
|
||||
BlockFragment: st.headerBlkBuf.Bytes(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if len(rp.body) != 0 {
|
||||
// TODO we assume rp.body fits in 1 frame
|
||||
if err := st.fr.WriteData(id, len(rp.trailer) == 0, rp.body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if len(rp.trailer) != 0 {
|
||||
st.headerBlkBuf.Reset()
|
||||
for _, h := range rp.trailer {
|
||||
_ = st.enc.WriteField(h)
|
||||
}
|
||||
err := st.fr.WriteHeaders(http2.HeadersFrameParam{
|
||||
StreamID: id,
|
||||
EndStream: true,
|
||||
EndHeaders: true,
|
||||
BlockFragment: st.headerBlkBuf.Bytes(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
loop:
|
||||
for {
|
||||
fr, err := st.readFrame()
|
||||
@@ -489,34 +607,53 @@ loop:
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
if f.FrameHeader.StreamID != id {
|
||||
sr, ok := streams[f.FrameHeader.StreamID]
|
||||
if !ok {
|
||||
st.header = make(http.Header)
|
||||
break
|
||||
}
|
||||
res.header = cloneHeader(st.header)
|
||||
sr.header = cloneHeader(st.header)
|
||||
var status int
|
||||
status, err = strconv.Atoi(res.header.Get(":status"))
|
||||
status, err = strconv.Atoi(sr.header.Get(":status"))
|
||||
if err != nil {
|
||||
return res, fmt.Errorf("Error parsing status code: %v", err)
|
||||
}
|
||||
res.status = status
|
||||
sr.status = status
|
||||
if f.StreamEnded() {
|
||||
break loop
|
||||
if streamEnded(res, streams, sr) {
|
||||
break loop
|
||||
}
|
||||
}
|
||||
case *http2.PushPromiseFrame:
|
||||
_, err := st.dec.Write(f.HeaderBlockFragment())
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
sr := &serverResponse{
|
||||
streamID: f.PromiseID,
|
||||
reqHeader: cloneHeader(st.header),
|
||||
}
|
||||
streams[sr.streamID] = sr
|
||||
case *http2.DataFrame:
|
||||
if f.FrameHeader.StreamID != id {
|
||||
sr, ok := streams[f.FrameHeader.StreamID]
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
res.body = append(res.body, f.Data()...)
|
||||
sr.body = append(sr.body, f.Data()...)
|
||||
if f.StreamEnded() {
|
||||
break loop
|
||||
if streamEnded(res, streams, sr) {
|
||||
break loop
|
||||
}
|
||||
}
|
||||
case *http2.RSTStreamFrame:
|
||||
if f.FrameHeader.StreamID != id {
|
||||
sr, ok := streams[f.FrameHeader.StreamID]
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
res.errCode = f.ErrCode
|
||||
break loop
|
||||
sr.errCode = f.ErrCode
|
||||
if streamEnded(res, streams, sr) {
|
||||
break loop
|
||||
}
|
||||
case *http2.GoAwayFrame:
|
||||
if f.ErrCode == http2.ErrCodeNo {
|
||||
break
|
||||
@@ -531,21 +668,46 @@ loop:
|
||||
if err := st.fr.WriteSettingsAck(); err != nil {
|
||||
return res, err
|
||||
}
|
||||
// TODO handle PUSH_PROMISE as well, since it alters HPACK context
|
||||
}
|
||||
}
|
||||
sort.Sort(ByStreamID(res.pushResponse))
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func streamEnded(mainSr *serverResponse, streams map[uint32]*serverResponse, sr *serverResponse) bool {
|
||||
delete(streams, sr.streamID)
|
||||
if mainSr.streamID != sr.streamID {
|
||||
mainSr.pushResponse = append(mainSr.pushResponse, sr)
|
||||
}
|
||||
return len(streams) == 0
|
||||
}
|
||||
|
||||
type serverResponse struct {
|
||||
status int // HTTP status code
|
||||
header http.Header // response header fields
|
||||
body []byte // response body
|
||||
streamID uint32 // stream ID in HTTP/2
|
||||
errCode http2.ErrCode // error code received in HTTP/2 RST_STREAM or GOAWAY
|
||||
connErr bool // true if HTTP/2 connection error
|
||||
spdyGoAwayErrCode spdy.GoAwayStatus // status code received in SPDY RST_STREAM
|
||||
spdyRstErrCode spdy.RstStreamStatus // status code received in SPDY GOAWAY
|
||||
connClose bool // Conection: close is included in response header in HTTP/1 test
|
||||
reqHeader http.Header // http request header, currently only sotres pushed request header
|
||||
pushResponse []*serverResponse // pushed response
|
||||
}
|
||||
|
||||
type ByStreamID []*serverResponse
|
||||
|
||||
func (b ByStreamID) Len() int {
|
||||
return len(b)
|
||||
}
|
||||
|
||||
func (b ByStreamID) Swap(i, j int) {
|
||||
b[i], b[j] = b[j], b[i]
|
||||
}
|
||||
|
||||
func (b ByStreamID) Less(i, j int) bool {
|
||||
return b[i].streamID < b[j].streamID
|
||||
}
|
||||
|
||||
func cloneHeader(h http.Header) http.Header {
|
||||
|
||||
3
lib/.gitignore
vendored
Normal file
3
lib/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# generated files
|
||||
includes/nghttp2/nghttp2ver.h
|
||||
libnghttp2.pc
|
||||
@@ -25,7 +25,8 @@ SUBDIRS = includes
|
||||
EXTRA_DIST = Makefile.msvc
|
||||
|
||||
AM_CFLAGS = $(WARNCFLAGS)
|
||||
AM_CPPFLAGS = -I$(srcdir)/includes -I$(builddir)/includes @DEFS@
|
||||
AM_CPPFLAGS = -I$(srcdir)/includes -I$(builddir)/includes -DBUILDING_NGHTTP2 \
|
||||
@DEFS@
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
pkgconfig_DATA = libnghttp2.pc
|
||||
@@ -45,7 +46,8 @@ OBJECTS = nghttp2_pq.c nghttp2_map.c nghttp2_queue.c \
|
||||
nghttp2_priority_spec.c \
|
||||
nghttp2_option.c \
|
||||
nghttp2_callbacks.c \
|
||||
nghttp2_mem.c
|
||||
nghttp2_mem.c \
|
||||
nghttp2_http.c
|
||||
|
||||
HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \
|
||||
nghttp2_frame.h \
|
||||
@@ -58,7 +60,8 @@ HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \
|
||||
nghttp2_priority_spec.h \
|
||||
nghttp2_option.h \
|
||||
nghttp2_callbacks.h \
|
||||
nghttp2_mem.h
|
||||
nghttp2_mem.h \
|
||||
nghttp2_http.h
|
||||
|
||||
libnghttp2_la_SOURCES = $(HFILES) $(OBJECTS)
|
||||
libnghttp2_la_LDFLAGS = -no-undefined \
|
||||
|
||||
@@ -2,81 +2,106 @@
|
||||
# GNU Makefile for nghttp2 / MSVC.
|
||||
#
|
||||
# By G. Vanem <gvanem@yahoo.no> 2013
|
||||
# Updated 3/2015 by Remo Eichenberger @remoe
|
||||
# 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
|
||||
THIS_MAKEFILE := $(lastword $(MAKEFILE_LIST))
|
||||
|
||||
_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)
|
||||
USE_CYTHON := 1
|
||||
#USE_CYTHON := 0
|
||||
|
||||
GENERATED = 'Generated by $(realpath Makefile.MSVC)'
|
||||
_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)'
|
||||
|
||||
OBJ_DIR := MSVC_obj
|
||||
#SUFFIX :=-vc90-mt-x86
|
||||
|
||||
#
|
||||
# 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
|
||||
TARGET_DIR ?= ../_VC_ROOT
|
||||
VC_ROOT := $(abspath $(TARGET_DIR))
|
||||
INSTALL_BIN := $(VC_ROOT)/bin
|
||||
INSTALL_LIB := $(VC_ROOT)/lib
|
||||
INSTALL_HDR := $(VC_ROOT)/include
|
||||
DLL_R := $(OBJ_DIR)/nghttp2$(SUFFIX).dll
|
||||
DLL_D := $(OBJ_DIR)/nghttp2d$(SUFFIX).dll
|
||||
LIB_R := $(OBJ_DIR)/nghttp2-static.lib
|
||||
LIB_D := $(OBJ_DIR)/nghttp2d-static.lib
|
||||
IMP_R := $(OBJ_DIR)/nghttp2.lib
|
||||
IMP_D := $(OBJ_DIR)/nghttp2d.lib
|
||||
|
||||
#
|
||||
# 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
|
||||
TARGETS := $(LIB_R) $(DLL_R) $(IMP_R) \
|
||||
$(LIB_D) $(DLL_D) $(IMP_D)
|
||||
|
||||
EXT_LIBS = $(ZLIB_ROOT)/zlib.lib ws2_32.lib
|
||||
EXT_LIBS =
|
||||
|
||||
OBJ_DIR = MSVC_obj
|
||||
|
||||
NGHTTP2_PDB_R = $(OBJ_DIR)/nghttp2.pdb
|
||||
NGHTTP2_PDB_D = $(OBJ_DIR)/nghttp2d.pdb
|
||||
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
|
||||
LD := link
|
||||
AR := lib
|
||||
#CC := icl
|
||||
#LD := xilink
|
||||
#AR := xilib
|
||||
RC := rc
|
||||
CFLAGS := -I./includes -Dssize_t=long -D_U_=""
|
||||
|
||||
CFLAGS_R = -nologo -MD -W3 -Zi -Fd./$(NGHTTP2_PDB_R)
|
||||
CFLAGS_D = -nologo -MDd -W3 -Zi -Fd./$(NGHTTP2_PDB_D) \
|
||||
CFLAGS_R := -nologo -MD -W3 -Z7 -DBUILDING_NGHTTP2
|
||||
CFLAGS_D := -nologo -MDd -W3 -Z7 -DBUILDING_NGHTTP2 \
|
||||
-Ot -D_DEBUG -GF -RTCs -RTCu # -RTCc -GS
|
||||
|
||||
LDFLAGS = -nologo -machine:i386 -map -debug -incremental:no # -verbose
|
||||
LDFLAGS := -nologo -MAP -debug -incremental:no -opt:ref,icf -MANIFEST # -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)))
|
||||
NGHTTP2_SRC := nghttp2_pq.c \
|
||||
nghttp2_map.c \
|
||||
nghttp2_queue.c \
|
||||
nghttp2_frame.c \
|
||||
nghttp2_buf.c \
|
||||
nghttp2_stream.c \
|
||||
nghttp2_outbound_item.c \
|
||||
nghttp2_session.c \
|
||||
nghttp2_submit.c \
|
||||
nghttp2_helper.c \
|
||||
nghttp2_npn.c \
|
||||
nghttp2_hd.c \
|
||||
nghttp2_hd_huffman.c \
|
||||
nghttp2_hd_huffman_data.c \
|
||||
nghttp2_version.c \
|
||||
nghttp2_priority_spec.c \
|
||||
nghttp2_option.c \
|
||||
nghttp2_callbacks.c \
|
||||
nghttp2_mem.c \
|
||||
nghttp2_http.c
|
||||
|
||||
all: intro $(OBJ_DIR) $(TARGETS)
|
||||
NGHTTP2_OBJ_R := $(addprefix $(OBJ_DIR)/r_, $(notdir $(NGHTTP2_SRC:.c=.obj)))
|
||||
NGHTTP2_OBJ_D := $(addprefix $(OBJ_DIR)/d_, $(notdir $(NGHTTP2_SRC:.c=.obj)))
|
||||
|
||||
.PHONY: all intro test_ver install copy_headers_and_libs \
|
||||
install_nghttp2_pyd_0 install_nghttp2_pyd_1 \
|
||||
build_nghttp2_pyd_0 build_nghttp2_pyd_1 \
|
||||
clean_nghttp2_pyd_0 clean_nghttp2_pyd_1
|
||||
|
||||
|
||||
all: intro $(OBJ_DIR) $(TARGETS) build_nghttp2_pyd_$(USE_CYTHON)
|
||||
@echo 'Welcome to NgHTTP2 (release + debug).'
|
||||
@echo 'Do a "make -f Makefile.MSVC install" at own risk!'
|
||||
|
||||
@@ -94,73 +119,88 @@ $(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)
|
||||
$(TARGETS) \
|
||||
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
|
||||
- mkdir -p $(INSTALL_HDR)/nghttp2 $(INSTALL_BIN) $(INSTALL_LIB)
|
||||
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)
|
||||
cp --update $(DLL_R) $(DLL_D) $(NGHTTP2_PDB_R) $(NGHTTP2_PDB_D) $(INSTALL_BIN)
|
||||
cp --update $(IMP_R) $(IMP_D) $(LIB_R) $(LIB_D) $(INSTALL_LIB)
|
||||
@echo
|
||||
|
||||
nghttp2.lib: $(NGHTTP2_OBJ_R)
|
||||
lib -nologo -out:$@ $^
|
||||
$(LIB_R): $(NGHTTP2_OBJ_R)
|
||||
$(AR) -nologo -out:$@ $^
|
||||
@echo
|
||||
|
||||
nghttp2d.lib: $(NGHTTP2_OBJ_D)
|
||||
lib -nologo -out:$@ $^
|
||||
$(LIB_D): $(NGHTTP2_OBJ_D)
|
||||
$(AR) -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)
|
||||
|
||||
$(IMP_R): $(DLL_R)
|
||||
|
||||
$(DLL_R): $(NGHTTP2_OBJ_R) $(OBJ_DIR)/r_nghttp2.res
|
||||
$(LD) $(LDFLAGS) -dll -out:$@ -implib:$(IMP_R) $(NGHTTP2_OBJ_R) -PDB:$(NGHTTP2_PDB_R) $(OBJ_DIR)/r_nghttp2.res $(EXT_LIBS)
|
||||
mt -nologo -manifest $@.manifest -outputresource:$@\;2
|
||||
@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)
|
||||
$(IMP_D): $(DLL_D)
|
||||
|
||||
$(DLL_D): $(NGHTTP2_OBJ_D) $(OBJ_DIR)/d_nghttp2.res
|
||||
$(LD) $(LDFLAGS) -dll -out:$@ -implib:$(IMP_D) $(NGHTTP2_OBJ_D) -PDB:$(NGHTTP2_PDB_D) $(OBJ_DIR)/d_nghttp2.res $(EXT_LIBS)
|
||||
mt -nologo -manifest $@.manifest -outputresource:$@\;2
|
||||
@echo
|
||||
|
||||
install_nghttp2_pyd_0: ;
|
||||
|
||||
install_nghttp2_pyd_1: $(addprefix ../python/, setup.py.in nghttp2.pyx)
|
||||
WIN_OBJDIR:=$(shell cygpath -w $(abspath $(OBJ_DIR)))
|
||||
WIN_OBJDIR:=$(subst \,/,$(WIN_OBJDIR))
|
||||
|
||||
../python/setup.py: ../python/setup.py.in $(THIS_MAKEFILE)
|
||||
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
|
||||
-e 's%@top_builddir@%$(WIN_OBJDIR)%' \
|
||||
-e 's/@PACKAGE_VERSION@/$(VERSION)/' setup.py.in >> setup.py ;
|
||||
|
||||
build_nghttp2_pyd_0: ;
|
||||
|
||||
build_nghttp2_pyd_1: $(addprefix ../python/, setup.py nghttp2.pyx)
|
||||
cd ../python ; \
|
||||
python setup.py build_ext -i -f bdist_wininst
|
||||
|
||||
install_nghttp2_pyd_0: ;
|
||||
|
||||
install_nghttp2_pyd_1: $(addprefix ../python/, setup.py nghttp2.pyx)
|
||||
cd ../python ; \
|
||||
pip install .
|
||||
|
||||
clean_nghttp2_pyd_0: ;
|
||||
|
||||
clean_nghttp2_pyd_1:
|
||||
cd ../python ; \
|
||||
rm -f setup.py nghttp2.c ; \
|
||||
rm -fR build/*
|
||||
rm -fR build dist
|
||||
|
||||
$(OBJ_DIR)/r_%.obj: %.c
|
||||
$(OBJ_DIR)/r_%.obj: %.c $(THIS_MAKEFILE)
|
||||
$(CC) $(CFLAGS_R) $(CFLAGS) -Fo$@ -c $<
|
||||
@echo
|
||||
|
||||
$(OBJ_DIR)/d_%.obj: %.c
|
||||
$(OBJ_DIR)/d_%.obj: %.c $(THIS_MAKEFILE)
|
||||
$(CC) $(CFLAGS_D) $(CFLAGS) -Fo$@ -c $<
|
||||
@echo
|
||||
|
||||
$(OBJ_DIR)/r_nghttp2.res: nghttp2.rc
|
||||
rc -nologo -D_RELEASE -Fo $@ $<
|
||||
$(OBJ_DIR)/r_nghttp2.res: $(OBJ_DIR)/nghttp2.rc $(THIS_MAKEFILE)
|
||||
$(RC) -nologo -D_RELEASE -Fo $@ $<
|
||||
@echo
|
||||
|
||||
$(OBJ_DIR)/d_nghttp2.res: nghttp2.rc
|
||||
rc -nologo -D_DEBUG -Fo $@ $<
|
||||
$(OBJ_DIR)/d_nghttp2.res: $(OBJ_DIR)/nghttp2.rc $(THIS_MAKEFILE)
|
||||
$(RC) -nologo -D_DEBUG -Fo $@ $<
|
||||
@echo
|
||||
|
||||
includes/nghttp2/nghttp2ver.h: includes/nghttp2/nghttp2ver.h.in
|
||||
includes/nghttp2/nghttp2ver.h: includes/nghttp2/nghttp2ver.h.in $(THIS_MAKEFILE)
|
||||
sed < includes/nghttp2/nghttp2ver.h.in \
|
||||
-e 's/@PACKAGE_VERSION@/$(VERSION)/g' \
|
||||
-e 's/@PACKAGE_VERSION_NUM@/($(VERSION_NUM))/g' > $@
|
||||
@@ -200,8 +240,6 @@ define RES_FILE
|
||||
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"
|
||||
@@ -213,38 +251,19 @@ endef
|
||||
|
||||
export RES_FILE
|
||||
|
||||
nghttp2.rc: Makefile.MSVC
|
||||
$(OBJ_DIR)/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
|
||||
rm -f $(OBJ_DIR)/* 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)
|
||||
- rm -rf $(OBJ_DIR)
|
||||
- rm -f .depend.MSVC
|
||||
|
||||
#
|
||||
# Use gcc to generated the dependencies. No MSVC specific args please!
|
||||
@@ -263,4 +282,4 @@ depend: includes/nghttp2/nghttp2ver.h
|
||||
sed -e $(REPLACE_D) .depend.tmp >> .depend.MSVC
|
||||
rm -f .depend.tmp
|
||||
|
||||
-include .depend.MSVC
|
||||
-include .depend.MSVC
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -320,7 +320,7 @@ int nghttp2_bufs_add(nghttp2_bufs *bufs, const void *data, size_t len) {
|
||||
}
|
||||
|
||||
buf->last = nghttp2_cpymem(buf->last, p, nwrite);
|
||||
p += len;
|
||||
p += nwrite;
|
||||
len -= nwrite;
|
||||
}
|
||||
|
||||
@@ -410,36 +410,46 @@ ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out) {
|
||||
len += nghttp2_buf_len(&chain->buf);
|
||||
}
|
||||
|
||||
if (!len) {
|
||||
if (len == 0) {
|
||||
res = NULL;
|
||||
} else {
|
||||
res = nghttp2_mem_malloc(bufs->mem, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (res == NULL) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
res = nghttp2_mem_malloc(bufs->mem, len);
|
||||
if (res == NULL) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
nghttp2_buf_wrap_init(&resbuf, res, len);
|
||||
|
||||
for (chain = bufs->head; chain; chain = chain->next) {
|
||||
buf = &chain->buf;
|
||||
|
||||
if (resbuf.last) {
|
||||
resbuf.last = nghttp2_cpymem(resbuf.last, buf->pos, nghttp2_buf_len(buf));
|
||||
}
|
||||
|
||||
nghttp2_buf_reset(buf);
|
||||
nghttp2_buf_shift_right(&chain->buf, bufs->offset);
|
||||
resbuf.last = nghttp2_cpymem(resbuf.last, buf->pos, nghttp2_buf_len(buf));
|
||||
}
|
||||
|
||||
bufs->cur = bufs->head;
|
||||
|
||||
*out = res;
|
||||
|
||||
return (ssize_t)len;
|
||||
}
|
||||
|
||||
size_t nghttp2_bufs_remove_copy(nghttp2_bufs *bufs, uint8_t *out) {
|
||||
size_t len;
|
||||
nghttp2_buf_chain *chain;
|
||||
nghttp2_buf *buf;
|
||||
nghttp2_buf resbuf;
|
||||
|
||||
len = nghttp2_bufs_len(bufs);
|
||||
|
||||
nghttp2_buf_wrap_init(&resbuf, out, len);
|
||||
|
||||
for (chain = bufs->head; chain; chain = chain->next) {
|
||||
buf = &chain->buf;
|
||||
resbuf.last = nghttp2_cpymem(resbuf.last, buf->pos, nghttp2_buf_len(buf));
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void nghttp2_bufs_reset(nghttp2_bufs *bufs) {
|
||||
nghttp2_buf_chain *chain, *ci;
|
||||
size_t k;
|
||||
|
||||
@@ -313,9 +313,8 @@ int nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b);
|
||||
* function allocates the contagious memory to store all data in
|
||||
* |bufs| and assigns it to |*out|.
|
||||
*
|
||||
* On successful return, nghttp2_bufs_len(bufs) returns 0, just like
|
||||
* after calling nghttp2_bufs_reset().
|
||||
|
||||
* The contents of |bufs| is left unchanged.
|
||||
*
|
||||
* This function returns the length of copied data and assigns the
|
||||
* pointer to copied data to |*out| if it succeeds, or one of the
|
||||
* following negative error codes:
|
||||
@@ -325,6 +324,17 @@ int nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b);
|
||||
*/
|
||||
ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out);
|
||||
|
||||
/*
|
||||
* Copies all data stored in |bufs| to |out|. This function assumes
|
||||
* that the buffer space pointed by |out| has at least
|
||||
* nghttp2_bufs(bufs) bytes.
|
||||
*
|
||||
* The contents of |bufs| is left unchanged.
|
||||
*
|
||||
* This function returns the length of copied data.
|
||||
*/
|
||||
size_t nghttp2_bufs_remove_copy(nghttp2_bufs *bufs, uint8_t *out);
|
||||
|
||||
/*
|
||||
* Resets |bufs| and makes the buffers empty.
|
||||
*/
|
||||
|
||||
@@ -121,3 +121,9 @@ void nghttp2_session_callbacks_set_on_begin_frame_callback(
|
||||
nghttp2_on_begin_frame_callback on_begin_frame_callback) {
|
||||
cbs->on_begin_frame_callback = on_begin_frame_callback;
|
||||
}
|
||||
|
||||
void nghttp2_session_callbacks_set_send_data_callback(
|
||||
nghttp2_session_callbacks *cbs,
|
||||
nghttp2_send_data_callback send_data_callback) {
|
||||
cbs->send_data_callback = send_data_callback;
|
||||
}
|
||||
|
||||
@@ -106,6 +106,7 @@ struct nghttp2_session_callbacks {
|
||||
* Sets callback function invoked when a frame header is received.
|
||||
*/
|
||||
nghttp2_on_begin_frame_callback on_begin_frame_callback;
|
||||
nghttp2_send_data_callback send_data_callback;
|
||||
};
|
||||
|
||||
#endif /* NGHTTP2_CALLBACKS_H */
|
||||
|
||||
@@ -739,7 +739,8 @@ int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, const nghttp2_nv *nva,
|
||||
nghttp2_nv *p;
|
||||
|
||||
for (i = 0; i < nvlen; ++i) {
|
||||
buflen += nva[i].namelen + nva[i].valuelen;
|
||||
/* + 2 for null-termination */
|
||||
buflen += nva[i].namelen + nva[i].valuelen + 2;
|
||||
}
|
||||
|
||||
if (nvlen == 0) {
|
||||
@@ -765,12 +766,14 @@ int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, const nghttp2_nv *nva,
|
||||
memcpy(data, nva[i].name, nva[i].namelen);
|
||||
p->name = data;
|
||||
p->namelen = nva[i].namelen;
|
||||
data[p->namelen] = '\0';
|
||||
nghttp2_downcase(p->name, p->namelen);
|
||||
data += nva[i].namelen;
|
||||
data += nva[i].namelen + 1;
|
||||
memcpy(data, nva[i].value, nva[i].valuelen);
|
||||
p->value = data;
|
||||
p->valuelen = nva[i].valuelen;
|
||||
data += nva[i].valuelen;
|
||||
data[p->valuelen] = '\0';
|
||||
data += nva[i].valuelen + 1;
|
||||
++p;
|
||||
}
|
||||
return 0;
|
||||
@@ -781,9 +784,6 @@ int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv) {
|
||||
for (i = 0; i < niv; ++i) {
|
||||
switch (iv[i].settings_id) {
|
||||
case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:
|
||||
if (iv[i].value > NGHTTP2_MAX_HEADER_TABLE_SIZE) {
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:
|
||||
break;
|
||||
@@ -810,7 +810,7 @@ int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void frame_set_pad(nghttp2_buf *buf, size_t padlen) {
|
||||
static void frame_set_pad(nghttp2_buf *buf, size_t padlen, int framehd_only) {
|
||||
size_t trail_padlen;
|
||||
size_t newlen;
|
||||
|
||||
@@ -825,6 +825,10 @@ static void frame_set_pad(nghttp2_buf *buf, size_t padlen) {
|
||||
newlen = (nghttp2_get_uint32(buf->pos) >> 8) + padlen;
|
||||
nghttp2_put_uint32be(buf->pos, (uint32_t)((newlen << 8) + buf->pos[3]));
|
||||
|
||||
if (framehd_only) {
|
||||
return;
|
||||
}
|
||||
|
||||
trail_padlen = padlen - 1;
|
||||
buf->pos[NGHTTP2_FRAME_HDLEN] = trail_padlen;
|
||||
|
||||
@@ -833,12 +837,10 @@ static void frame_set_pad(nghttp2_buf *buf, size_t padlen) {
|
||||
/* extend buffers trail_padlen bytes, since we ate previous padlen -
|
||||
trail_padlen byte(s) */
|
||||
buf->last += trail_padlen;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd,
|
||||
size_t padlen) {
|
||||
size_t padlen, int framehd_only) {
|
||||
nghttp2_buf *buf;
|
||||
|
||||
if (padlen == 0) {
|
||||
@@ -871,7 +873,7 @@ int nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd,
|
||||
|
||||
assert(nghttp2_buf_avail(buf) >= (ssize_t)padlen - 1);
|
||||
|
||||
frame_set_pad(buf, padlen);
|
||||
frame_set_pad(buf, padlen, framehd_only);
|
||||
|
||||
hd->length += padlen;
|
||||
hd->flags |= NGHTTP2_FLAG_PADDED;
|
||||
|
||||
@@ -55,10 +55,8 @@
|
||||
/* Number of inbound buffer */
|
||||
#define NGHTTP2_FRAMEBUF_MAX_NUM 5
|
||||
|
||||
/* The default length of DATA frame payload. This should be small enough
|
||||
* for the data payload and the header to fit into 1 TLS record */
|
||||
#define NGHTTP2_DATA_PAYLOADLEN \
|
||||
((NGHTTP2_MAX_FRAME_SIZE_MIN) - (NGHTTP2_FRAME_HDLEN))
|
||||
/* The default length of DATA frame payload. */
|
||||
#define NGHTTP2_DATA_PAYLOADLEN NGHTTP2_MAX_FRAME_SIZE_MIN
|
||||
|
||||
/* Maximum headers payload length, calculated in compressed form.
|
||||
This applies to transmission only. */
|
||||
@@ -67,9 +65,6 @@
|
||||
/* The number of bytes for each SETTINGS entry */
|
||||
#define NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH 6
|
||||
|
||||
/* The maximum header table size in SETTINGS_HEADER_TABLE_SIZE */
|
||||
#define NGHTTP2_MAX_HEADER_TABLE_SIZE ((1u << 31) - 1)
|
||||
|
||||
/* Length of priority related fields in HEADERS/PRIORITY frames */
|
||||
#define NGHTTP2_PRIORITY_SPECLEN 5
|
||||
|
||||
@@ -77,7 +72,7 @@
|
||||
#define NGHTTP2_MAX_PADLEN 256
|
||||
|
||||
/* Union of extension frame payload */
|
||||
typedef union { nghttp2_ext_altsvc altsvc; } nghttp2_ext_frame_payload;
|
||||
typedef union { int dummy; } nghttp2_ext_frame_payload;
|
||||
|
||||
void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd);
|
||||
|
||||
@@ -473,7 +468,9 @@ void nghttp2_nv_array_sort(nghttp2_nv *nva, size_t nvlen);
|
||||
/*
|
||||
* Copies name/value pairs from |nva|, which contains |nvlen| pairs,
|
||||
* to |*nva_ptr|, which is dynamically allocated so that all items can
|
||||
* be stored.
|
||||
* be stored. The resultant name and value in nghttp2_nv are
|
||||
* guaranteed to be NULL-terminated even if the input is not
|
||||
* null-terminated.
|
||||
*
|
||||
* The |*nva_ptr| must be freed using nghttp2_nv_array_del().
|
||||
*
|
||||
@@ -510,7 +507,8 @@ int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv);
|
||||
* Sets Pad Length field and flags and adjusts frame header position
|
||||
* of each buffers in |bufs|. The number of padding is given in the
|
||||
* |padlen| including Pad Length field. The |hd| is the frame header
|
||||
* for the serialized data.
|
||||
* for the serialized data. This function fills zeros padding region
|
||||
* unless framehd_only is nonzero.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
@@ -521,6 +519,6 @@ int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv);
|
||||
* The length of the resulting frame is too large.
|
||||
*/
|
||||
int nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd,
|
||||
size_t padlen);
|
||||
size_t padlen, int framehd_only);
|
||||
|
||||
#endif /* NGHTTP2_FRAME_H */
|
||||
|
||||
1041
lib/nghttp2_hd.c
1041
lib/nghttp2_hd.c
File diff suppressed because it is too large
Load Diff
112
lib/nghttp2_hd.h
112
lib/nghttp2_hd.h
@@ -49,7 +49,68 @@
|
||||
#define NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE (1 << 12)
|
||||
|
||||
/* Exported for unit test */
|
||||
extern const size_t NGHTTP2_STATIC_TABLE_LENGTH;
|
||||
#define NGHTTP2_STATIC_TABLE_LENGTH 61
|
||||
|
||||
/* Generated by genlibtokenlookup.py */
|
||||
typedef enum {
|
||||
NGHTTP2_TOKEN__AUTHORITY = 0,
|
||||
NGHTTP2_TOKEN__METHOD = 1,
|
||||
NGHTTP2_TOKEN__PATH = 3,
|
||||
NGHTTP2_TOKEN__SCHEME = 5,
|
||||
NGHTTP2_TOKEN__STATUS = 7,
|
||||
NGHTTP2_TOKEN_ACCEPT_CHARSET = 14,
|
||||
NGHTTP2_TOKEN_ACCEPT_ENCODING = 15,
|
||||
NGHTTP2_TOKEN_ACCEPT_LANGUAGE = 16,
|
||||
NGHTTP2_TOKEN_ACCEPT_RANGES = 17,
|
||||
NGHTTP2_TOKEN_ACCEPT = 18,
|
||||
NGHTTP2_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN = 19,
|
||||
NGHTTP2_TOKEN_AGE = 20,
|
||||
NGHTTP2_TOKEN_ALLOW = 21,
|
||||
NGHTTP2_TOKEN_AUTHORIZATION = 22,
|
||||
NGHTTP2_TOKEN_CACHE_CONTROL = 23,
|
||||
NGHTTP2_TOKEN_CONTENT_DISPOSITION = 24,
|
||||
NGHTTP2_TOKEN_CONTENT_ENCODING = 25,
|
||||
NGHTTP2_TOKEN_CONTENT_LANGUAGE = 26,
|
||||
NGHTTP2_TOKEN_CONTENT_LENGTH = 27,
|
||||
NGHTTP2_TOKEN_CONTENT_LOCATION = 28,
|
||||
NGHTTP2_TOKEN_CONTENT_RANGE = 29,
|
||||
NGHTTP2_TOKEN_CONTENT_TYPE = 30,
|
||||
NGHTTP2_TOKEN_COOKIE = 31,
|
||||
NGHTTP2_TOKEN_DATE = 32,
|
||||
NGHTTP2_TOKEN_ETAG = 33,
|
||||
NGHTTP2_TOKEN_EXPECT = 34,
|
||||
NGHTTP2_TOKEN_EXPIRES = 35,
|
||||
NGHTTP2_TOKEN_FROM = 36,
|
||||
NGHTTP2_TOKEN_HOST = 37,
|
||||
NGHTTP2_TOKEN_IF_MATCH = 38,
|
||||
NGHTTP2_TOKEN_IF_MODIFIED_SINCE = 39,
|
||||
NGHTTP2_TOKEN_IF_NONE_MATCH = 40,
|
||||
NGHTTP2_TOKEN_IF_RANGE = 41,
|
||||
NGHTTP2_TOKEN_IF_UNMODIFIED_SINCE = 42,
|
||||
NGHTTP2_TOKEN_LAST_MODIFIED = 43,
|
||||
NGHTTP2_TOKEN_LINK = 44,
|
||||
NGHTTP2_TOKEN_LOCATION = 45,
|
||||
NGHTTP2_TOKEN_MAX_FORWARDS = 46,
|
||||
NGHTTP2_TOKEN_PROXY_AUTHENTICATE = 47,
|
||||
NGHTTP2_TOKEN_PROXY_AUTHORIZATION = 48,
|
||||
NGHTTP2_TOKEN_RANGE = 49,
|
||||
NGHTTP2_TOKEN_REFERER = 50,
|
||||
NGHTTP2_TOKEN_REFRESH = 51,
|
||||
NGHTTP2_TOKEN_RETRY_AFTER = 52,
|
||||
NGHTTP2_TOKEN_SERVER = 53,
|
||||
NGHTTP2_TOKEN_SET_COOKIE = 54,
|
||||
NGHTTP2_TOKEN_STRICT_TRANSPORT_SECURITY = 55,
|
||||
NGHTTP2_TOKEN_TRANSFER_ENCODING = 56,
|
||||
NGHTTP2_TOKEN_USER_AGENT = 57,
|
||||
NGHTTP2_TOKEN_VARY = 58,
|
||||
NGHTTP2_TOKEN_VIA = 59,
|
||||
NGHTTP2_TOKEN_WWW_AUTHENTICATE = 60,
|
||||
NGHTTP2_TOKEN_TE,
|
||||
NGHTTP2_TOKEN_CONNECTION,
|
||||
NGHTTP2_TOKEN_KEEP_ALIVE,
|
||||
NGHTTP2_TOKEN_PROXY_CONNECTION,
|
||||
NGHTTP2_TOKEN_UPGRADE
|
||||
} nghttp2_token;
|
||||
|
||||
typedef enum {
|
||||
NGHTTP2_HD_FLAG_NONE = 0,
|
||||
@@ -67,18 +128,14 @@ typedef enum {
|
||||
|
||||
typedef struct {
|
||||
nghttp2_nv nv;
|
||||
uint32_t name_hash;
|
||||
uint32_t value_hash;
|
||||
/* nghttp2_token value for nv.name. It could be -1 if we have no
|
||||
token for that header field name. */
|
||||
int token;
|
||||
/* Reference count */
|
||||
uint8_t ref;
|
||||
uint8_t flags;
|
||||
} nghttp2_hd_entry;
|
||||
|
||||
typedef struct {
|
||||
nghttp2_hd_entry ent;
|
||||
size_t index;
|
||||
} nghttp2_hd_static_entry;
|
||||
|
||||
typedef struct {
|
||||
nghttp2_hd_entry **buffer;
|
||||
size_t mask;
|
||||
@@ -94,6 +151,8 @@ typedef enum {
|
||||
} nghttp2_hd_opcode;
|
||||
|
||||
typedef enum {
|
||||
NGHTTP2_HD_STATE_EXPECT_TABLE_SIZE,
|
||||
NGHTTP2_HD_STATE_INFLATE_START,
|
||||
NGHTTP2_HD_STATE_OPCODE,
|
||||
NGHTTP2_HD_STATE_READ_TABLE_SIZE,
|
||||
NGHTTP2_HD_STATE_READ_INDEX,
|
||||
@@ -107,6 +166,12 @@ typedef enum {
|
||||
NGHTTP2_HD_STATE_READ_VALUE
|
||||
} nghttp2_hd_inflate_state;
|
||||
|
||||
typedef enum {
|
||||
NGHTTP2_HD_WITH_INDEXING,
|
||||
NGHTTP2_HD_WITHOUT_INDEXING,
|
||||
NGHTTP2_HD_NEVER_INDEXING
|
||||
} nghttp2_hd_indexing_mode;
|
||||
|
||||
typedef struct {
|
||||
/* dynamic header table */
|
||||
nghttp2_hd_ringbuf hd_table;
|
||||
@@ -176,9 +241,8 @@ struct nghttp2_hd_inflater {
|
||||
* set in the |flags|, the content pointed by the |name| with length
|
||||
* |namelen| is copied. Likewise, if NGHTTP2_HD_FLAG_VALUE_ALLOC bit
|
||||
* set in the |flags|, the content pointed by the |value| with length
|
||||
* |valuelen| is copied. The |name_hash| and |value_hash| are hash
|
||||
* value for |name| and |value| respectively. The hash function is
|
||||
* defined in nghttp2_hd.c.
|
||||
* |valuelen| is copied. The |token| is enum number looked up by
|
||||
* |name|. It could be -1 if we don't have that enum value.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
@@ -188,8 +252,7 @@ struct nghttp2_hd_inflater {
|
||||
*/
|
||||
int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t flags, uint8_t *name,
|
||||
size_t namelen, uint8_t *value, size_t valuelen,
|
||||
uint32_t name_hash, uint32_t value_hash,
|
||||
nghttp2_mem *mem);
|
||||
int token, nghttp2_mem *mem);
|
||||
|
||||
void nghttp2_hd_entry_free(nghttp2_hd_entry *ent, nghttp2_mem *mem);
|
||||
|
||||
@@ -271,20 +334,32 @@ int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater, nghttp2_mem *mem);
|
||||
*/
|
||||
void nghttp2_hd_inflate_free(nghttp2_hd_inflater *inflater);
|
||||
|
||||
/*
|
||||
* Similar to nghttp2_hd_inflate_hd(), but this takes additional
|
||||
* output parameter |token|. On successful header emission, it
|
||||
* contains nghttp2_token value for nv_out->name. It could be -1 if
|
||||
* we don't have enum value for the name. Other than that return
|
||||
* values and semantics are the same as nghttp2_hd_inflate_hd().
|
||||
*/
|
||||
ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater,
|
||||
nghttp2_nv *nv_out, int *inflate_flags,
|
||||
int *token, uint8_t *in, size_t inlen,
|
||||
int in_final);
|
||||
|
||||
/* For unittesting purpose */
|
||||
int nghttp2_hd_emit_indname_block(nghttp2_bufs *bufs, size_t index,
|
||||
nghttp2_nv *nv, int inc_indexing);
|
||||
nghttp2_nv *nv, int indexing_mode);
|
||||
|
||||
/* For unittesting purpose */
|
||||
int nghttp2_hd_emit_newname_block(nghttp2_bufs *bufs, nghttp2_nv *nv,
|
||||
int inc_indexing);
|
||||
int indexing_mode);
|
||||
|
||||
/* For unittesting purpose */
|
||||
int nghttp2_hd_emit_table_size(nghttp2_bufs *bufs, size_t table_size);
|
||||
|
||||
/* For unittesting purpose */
|
||||
nghttp2_hd_entry *nghttp2_hd_table_get(nghttp2_hd_context *context,
|
||||
size_t index);
|
||||
NGHTTP2_EXTERN nghttp2_hd_entry *
|
||||
nghttp2_hd_table_get(nghttp2_hd_context *context, size_t index);
|
||||
|
||||
/* For unittesting purpose */
|
||||
ssize_t nghttp2_hd_decode_length(uint32_t *res, size_t *shift_ptr, int *final,
|
||||
@@ -324,8 +399,7 @@ void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx);
|
||||
* be initialized by nghttp2_hd_huff_decode_context_init(). The result
|
||||
* will be added to |dest|. This function may expand |dest| as
|
||||
* needed. The caller is responsible to release the memory of |dest|
|
||||
* by calling nghttp2_bufs_free() or export its content using
|
||||
* nghttp2_bufs_remove().
|
||||
* by calling nghttp2_bufs_free().
|
||||
*
|
||||
* The caller must set the |final| to nonzero if the given input is
|
||||
* the final block.
|
||||
|
||||
@@ -168,10 +168,27 @@ void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx) {
|
||||
ctx->accept = 1;
|
||||
}
|
||||
|
||||
/* Use macro to make the code simpler..., but error case is tricky.
|
||||
We spent most of the CPU in decoding, so we are doing this
|
||||
thing. */
|
||||
#define hd_huff_decode_sym_emit(bufs, sym, avail) \
|
||||
do { \
|
||||
if ((avail)) { \
|
||||
nghttp2_bufs_fast_addb((bufs), (sym)); \
|
||||
--(avail); \
|
||||
} else { \
|
||||
rv = nghttp2_bufs_addb((bufs), (sym)); \
|
||||
if (rv != 0) { \
|
||||
return rv; \
|
||||
} \
|
||||
(avail) = nghttp2_bufs_cur_avail((bufs)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx,
|
||||
nghttp2_bufs *bufs, const uint8_t *src,
|
||||
size_t srclen, int final) {
|
||||
size_t i, j;
|
||||
size_t i;
|
||||
int rv;
|
||||
size_t avail;
|
||||
|
||||
@@ -180,30 +197,28 @@ ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx,
|
||||
/* We use the decoding algorithm described in
|
||||
http://graphics.ics.uci.edu/pub/Prefix.pdf */
|
||||
for (i = 0; i < srclen; ++i) {
|
||||
uint8_t in = src[i] >> 4;
|
||||
for (j = 0; j < 2; ++j) {
|
||||
const nghttp2_huff_decode *t;
|
||||
const nghttp2_huff_decode *t;
|
||||
|
||||
t = &huff_decode_table[ctx->state][in];
|
||||
if (t->flags & NGHTTP2_HUFF_FAIL) {
|
||||
return NGHTTP2_ERR_HEADER_COMP;
|
||||
}
|
||||
if (t->flags & NGHTTP2_HUFF_SYM) {
|
||||
if (avail) {
|
||||
nghttp2_bufs_fast_addb(bufs, t->sym);
|
||||
--avail;
|
||||
} else {
|
||||
rv = nghttp2_bufs_addb(bufs, t->sym);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
avail = nghttp2_bufs_cur_avail(bufs);
|
||||
}
|
||||
}
|
||||
ctx->state = t->state;
|
||||
ctx->accept = (t->flags & NGHTTP2_HUFF_ACCEPTED) != 0;
|
||||
in = src[i] & 0xf;
|
||||
t = &huff_decode_table[ctx->state][src[i] >> 4];
|
||||
if (t->flags & NGHTTP2_HUFF_FAIL) {
|
||||
return NGHTTP2_ERR_HEADER_COMP;
|
||||
}
|
||||
if (t->flags & NGHTTP2_HUFF_SYM) {
|
||||
/* this is macro, and may return from this function on error */
|
||||
hd_huff_decode_sym_emit(bufs, t->sym, avail);
|
||||
}
|
||||
|
||||
t = &huff_decode_table[t->state][src[i] & 0xf];
|
||||
if (t->flags & NGHTTP2_HUFF_FAIL) {
|
||||
return NGHTTP2_ERR_HEADER_COMP;
|
||||
}
|
||||
if (t->flags & NGHTTP2_HUFF_SYM) {
|
||||
/* this is macro, and may return from this function on error */
|
||||
hd_huff_decode_sym_emit(bufs, t->sym, avail);
|
||||
}
|
||||
|
||||
ctx->state = t->state;
|
||||
ctx->accept = (t->flags & NGHTTP2_HUFF_ACCEPTED) != 0;
|
||||
}
|
||||
if (final && !ctx->accept) {
|
||||
return NGHTTP2_ERR_HEADER_COMP;
|
||||
|
||||
@@ -267,8 +267,6 @@ const char *nghttp2_strerror(int error_code) {
|
||||
return "Invalid stream state";
|
||||
case NGHTTP2_ERR_DEFERRED_DATA_EXIST:
|
||||
return "Another DATA frame has already been deferred";
|
||||
case NGHTTP2_ERR_SESSION_CLOSING:
|
||||
return "The current session is closing";
|
||||
case NGHTTP2_ERR_START_STREAM_NOT_ALLOWED:
|
||||
return "request HEADERS is not allowed";
|
||||
case NGHTTP2_ERR_GOAWAY_ALREADY_SENT:
|
||||
@@ -294,13 +292,23 @@ const char *nghttp2_strerror(int error_code) {
|
||||
case NGHTTP2_ERR_PUSH_DISABLED:
|
||||
return "Server push is disabled by peer";
|
||||
case NGHTTP2_ERR_DATA_EXIST:
|
||||
return "DATA frame already exists";
|
||||
return "DATA or HEADERS frame has already been submitted for the stream";
|
||||
case NGHTTP2_ERR_SESSION_CLOSING:
|
||||
return "The current session is closing";
|
||||
case NGHTTP2_ERR_HTTP_HEADER:
|
||||
return "Invalid HTTP header field was received";
|
||||
case NGHTTP2_ERR_HTTP_MESSAGING:
|
||||
return "Violation in HTTP messaging rule";
|
||||
case NGHTTP2_ERR_REFUSED_STREAM:
|
||||
return "Stream was refused";
|
||||
case NGHTTP2_ERR_INTERNAL:
|
||||
return "Internal error";
|
||||
case NGHTTP2_ERR_NOMEM:
|
||||
return "Out of memory";
|
||||
case NGHTTP2_ERR_CALLBACK_FAILURE:
|
||||
return "The user callback function failed";
|
||||
case NGHTTP2_ERR_BAD_PREFACE:
|
||||
return "Received bad connection preface";
|
||||
case NGHTTP2_ERR_BAD_CLIENT_MAGIC:
|
||||
return "Received bad client magic byte string";
|
||||
default:
|
||||
return "Unknown error code";
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user