mirror of
https://github.com/nghttp2/nghttp2.git
synced 2025-12-07 02:28:53 +08:00
Compare commits
673 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
895da9a7aa | ||
|
|
f9eaaa9da5 | ||
|
|
6680d8b792 | ||
|
|
0b32df94a5 | ||
|
|
913b95336b | ||
|
|
c44587a70c | ||
|
|
938fa9a1e2 | ||
|
|
206eab3b33 | ||
|
|
bb4ab7e333 | ||
|
|
f470d1cc06 | ||
|
|
3d5493c3f1 | ||
|
|
389a96483a | ||
|
|
bff59e09dc | ||
|
|
1e96a2d357 | ||
|
|
4a5d1b6708 | ||
|
|
b7fd0fdd96 | ||
|
|
1d88f02b10 | ||
|
|
a0643b86eb | ||
|
|
53270fac88 | ||
|
|
25db23e3c8 | ||
|
|
a54062bd5e | ||
|
|
c3283e1c6e | ||
|
|
25e2adbfe9 | ||
|
|
fec7ef61d3 | ||
|
|
2269b99b4f | ||
|
|
84f96a2fd5 | ||
|
|
50ec17c4af | ||
|
|
170f2a144a | ||
|
|
28fe3e7e89 | ||
|
|
2be6a94d70 | ||
|
|
e77f9d95d3 | ||
|
|
87a38bdf8d | ||
|
|
03bb58006b | ||
|
|
230b1f927f | ||
|
|
4ac8edfe27 | ||
|
|
ab824d1230 | ||
|
|
e67bb7ca62 | ||
|
|
da9e535c7a | ||
|
|
dbbc7296f5 | ||
|
|
ce1b11e3a0 | ||
|
|
b57b0db5bf | ||
|
|
03b32d92b7 | ||
|
|
604732b650 | ||
|
|
ec47dfb9b8 | ||
|
|
1c2fcc2a55 | ||
|
|
83ff3de9ae | ||
|
|
818c3bcadc | ||
|
|
cd845ae111 | ||
|
|
c6785abd1f | ||
|
|
ee4732a676 | ||
|
|
eacd6eeed2 | ||
|
|
4aaf111c58 | ||
|
|
991352d201 | ||
|
|
d22573086f | ||
|
|
1148584526 | ||
|
|
5005369f71 | ||
|
|
33601f1a51 | ||
|
|
b97c088e87 | ||
|
|
e93a1bdf6d | ||
|
|
7f12f0671c | ||
|
|
39fb5972d4 | ||
|
|
063e1d7606 | ||
|
|
57aee184ca | ||
|
|
4ae2850aa6 | ||
|
|
f586797f7d | ||
|
|
0d8c8ca033 | ||
|
|
5ea90ba6bd | ||
|
|
fd8067be94 | ||
|
|
ad34b45782 | ||
|
|
d0f96da2c0 | ||
|
|
da57989cf6 | ||
|
|
f086b7b67e | ||
|
|
471fd688b9 | ||
|
|
02adaac368 | ||
|
|
1bbef4be74 | ||
|
|
918f8cca36 | ||
|
|
22b39f0337 | ||
|
|
e282462e1f | ||
|
|
e4843201b0 | ||
|
|
753f6d4a3e | ||
|
|
aba2dbddc2 | ||
|
|
510dff4c39 | ||
|
|
e260006182 | ||
|
|
7d481db248 | ||
|
|
4f52f60b3c | ||
|
|
5afc25623e | ||
|
|
3ce1c1d39f | ||
|
|
21a3edfc60 | ||
|
|
026ab797eb | ||
|
|
79945c0c45 | ||
|
|
f8c1da7f3c | ||
|
|
a1bb48770c | ||
|
|
d05b77b36c | ||
|
|
ce53bd239e | ||
|
|
39287314d3 | ||
|
|
231db4a3e6 | ||
|
|
c30d252f94 | ||
|
|
6d46249b7b | ||
|
|
1b63e6d478 | ||
|
|
28defbfb4a | ||
|
|
75b98662be | ||
|
|
1df682140c | ||
|
|
7273c7d688 | ||
|
|
ef1595672c | ||
|
|
98c959291f | ||
|
|
2135c87f94 | ||
|
|
e1106e0f91 | ||
|
|
7b0113ac77 | ||
|
|
6f0c88580f | ||
|
|
18064d1626 | ||
|
|
3029e5c530 | ||
|
|
be77b47ab5 | ||
|
|
77958eab46 | ||
|
|
8f41accf41 | ||
|
|
e19d5efced | ||
|
|
69c3e2114a | ||
|
|
6dcfe1c3f8 | ||
|
|
d076d54f67 | ||
|
|
aba7e9e7f9 | ||
|
|
4ac7152f94 | ||
|
|
d348ea3384 | ||
|
|
b4709b9e8c | ||
|
|
21e1af2ae7 | ||
|
|
226a09b04a | ||
|
|
587f37a597 | ||
|
|
97f488a5c7 | ||
|
|
45e0d42c51 | ||
|
|
bddc4a0a04 | ||
|
|
a9338f1c0e | ||
|
|
4ad00200a2 | ||
|
|
6b38f7e0d2 | ||
|
|
4872c9b4c9 | ||
|
|
d8ca0a8d47 | ||
|
|
2222a898a1 | ||
|
|
9e685a2734 | ||
|
|
c86e839091 | ||
|
|
d722a09581 | ||
|
|
5967667e9e | ||
|
|
d044c58558 | ||
|
|
200217d8ea | ||
|
|
02bb2c3e83 | ||
|
|
64c754e2c5 | ||
|
|
ad65121496 | ||
|
|
3235b88253 | ||
|
|
5e3ab6da5a | ||
|
|
6f9c9f8518 | ||
|
|
65f2b16132 | ||
|
|
05a761b628 | ||
|
|
14f5240d8f | ||
|
|
baadec5ef4 | ||
|
|
d20229d9b9 | ||
|
|
e06af02573 | ||
|
|
5cd34920b7 | ||
|
|
e436caf0bb | ||
|
|
f0d41a5ac2 | ||
|
|
068529586d | ||
|
|
45cdb10c46 | ||
|
|
2170d958d5 | ||
|
|
9d78691936 | ||
|
|
1508c50a45 | ||
|
|
dd0a72579f | ||
|
|
e7bc269e97 | ||
|
|
e2557059e5 | ||
|
|
0c4e26fd24 | ||
|
|
3002674bac | ||
|
|
e1f7643c92 | ||
|
|
d70eb14ce0 | ||
|
|
31c19cbda4 | ||
|
|
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 | ||
|
|
90bfea77e0 | ||
|
|
cf0576253f | ||
|
|
59e3783f3f | ||
|
|
084e4487ed | ||
|
|
24897aa50d | ||
|
|
3e50ef439d | ||
|
|
87602e5d72 | ||
|
|
d0c27d5229 | ||
|
|
ebf214c8fc | ||
|
|
250ea53e4b | ||
|
|
01af6ea70c |
8
.gitignore
vendored
8
.gitignore
vendored
@@ -6,6 +6,7 @@
|
||||
*.lo
|
||||
*.m4
|
||||
*.o
|
||||
*.pyc
|
||||
.deps/
|
||||
.libs/
|
||||
INSTALL
|
||||
@@ -31,3 +32,10 @@ test-driver
|
||||
# test logs generated by `make check`
|
||||
*.log
|
||||
*.trs
|
||||
|
||||
lib/MSVC_obj/
|
||||
_VC_ROOT/
|
||||
.depend.MSVC
|
||||
*.pyd
|
||||
*.egg-info/
|
||||
python/nghttp2.c
|
||||
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "third-party/mruby"]
|
||||
path = third-party/mruby
|
||||
url = https://github.com/mruby/mruby
|
||||
49
.travis.yml
49
.travis.yml
@@ -1,37 +1,38 @@
|
||||
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
|
||||
- automake
|
||||
- autoconf
|
||||
- ./configure --enable-werror
|
||||
- git submodule update --init
|
||||
- ./configure --enable-werror --with-mruby
|
||||
script:
|
||||
- make
|
||||
- make check
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#
|
||||
# $ sudo docker run -v /path/to/dest:/out nghttp2-android cp /root/build/nghttp2/src/nghttpx /out
|
||||
|
||||
FROM ubuntu:trusty
|
||||
FROM ubuntu:vivid
|
||||
|
||||
MAINTAINER Tatsuhiro Tsujikawa
|
||||
|
||||
@@ -30,12 +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 && \
|
||||
chmod a+x android-ndk-r10c-linux-x86_64.bin && \
|
||||
./android-ndk-r10c-linux-x86_64.bin && \
|
||||
rm 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 \
|
||||
@@ -86,6 +86,25 @@ RUN patch -p1 < ../libev-4.19-android.patch && \
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
615
README.rst
615
README.rst
@@ -13,26 +13,18 @@ An HPACK encoder and decoder are available as a public API.
|
||||
|
||||
An experimental high level C++ library is also available.
|
||||
|
||||
We have Python bindings of this libary, but we do not have full
|
||||
We have Python bindings of this library, but we do not have full
|
||||
code coverage yet.
|
||||
|
||||
Development Status
|
||||
------------------
|
||||
|
||||
We started to implement h2-14
|
||||
(http://tools.ietf.org/html/draft-ietf-httpbis-http2-14), and header
|
||||
compression
|
||||
(http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-09).
|
||||
We have implemented `RFC 7540 <https://tools.ietf.org/html/rfc7540>`_
|
||||
HTTP/2 and `RFC 7541 <https://tools.ietf.org/html/rfc7541>`_ HPACK -
|
||||
Header Compression for HTTP/2
|
||||
|
||||
The nghttp2 code base was forked from the spdylay project.
|
||||
|
||||
=========================== =======
|
||||
HTTP/2 Features Support
|
||||
=========================== =======
|
||||
Core frames handling Yes
|
||||
Dependency Tree Yes
|
||||
Large header (CONTINUATION) Yes
|
||||
=========================== =======
|
||||
The nghttp2 code base was forked from the spdylay
|
||||
(https://github.com/tatsuhiro-t/spdylay) project.
|
||||
|
||||
Public Test Server
|
||||
------------------
|
||||
@@ -46,9 +38,9 @@ implementation.
|
||||
and ``http/1.1`` via ALPN/NPN and requires TLSv1.2 for HTTP/2
|
||||
connection.
|
||||
|
||||
* http://nghttp2.org/ (Upgrade / Direct)
|
||||
* http://nghttp2.org/ (HTTP Upgrade and HTTP/2 Direct)
|
||||
|
||||
``h2c-14`` and ``http/1.1``.
|
||||
``h2c`` and ``http/1.1``.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
@@ -75,11 +67,13 @@ required:
|
||||
* zlib >= 1.2.3
|
||||
|
||||
ALPN support requires OpenSSL >= 1.0.2 (released 22 January 2015).
|
||||
LibreSSL >= 2.2.0 can be used instead of OpenSSL, but OpenSSL has more
|
||||
features than LibreSSL at the time of this writing.
|
||||
|
||||
To enable the SPDY protocol in the application program ``nghttpx`` and
|
||||
``h2load``, the following package is required:
|
||||
|
||||
* spdylay >= 1.3.0
|
||||
* spdylay >= 1.3.2
|
||||
|
||||
To enable ``-a`` option (getting linked assets from the downloaded
|
||||
resource) in ``nghttp``, the following package is required:
|
||||
@@ -108,16 +102,31 @@ The Python bindings require the following packages:
|
||||
|
||||
* cython >= 0.19
|
||||
* python >= 2.7
|
||||
* python-setuptools
|
||||
|
||||
If you are using Ubuntu 14.04 LTS (trusty), run the following to install the needed packages::
|
||||
|
||||
sudo apt-get install make binutils autoconf automake autotools-dev libtool pkg-config \
|
||||
zlib1g-dev libcunit1-dev libssl-dev libxml2-dev libev-dev libevent-dev libjansson-dev \
|
||||
libjemalloc-dev cython python3.4-dev
|
||||
libjemalloc-dev cython python3.4-dev python-setuptools
|
||||
|
||||
spdylay is not packaged in Ubuntu, so you need to build it yourself:
|
||||
http://tatsuhiro-t.github.io/spdylay/
|
||||
|
||||
To enable mruby support for nghttpx, `mruby
|
||||
<https://github.com/mruby/mruby>`_ is required. We need to build
|
||||
mruby with C++ ABI explicitly turned on, and probably need other
|
||||
mrgems, mruby is manged by git submodule under third-party/mruby
|
||||
directory. Currently, mruby support for nghttpx is disabled by
|
||||
default. To enable mruby support, use ``--with-mruby`` configure
|
||||
option. Note that at the time of this writing, libmruby-dev and mruby
|
||||
packages in Debian/Ubuntu are not usable for nghttp2, since they do
|
||||
not enable C++ ABI. To build mruby, the following packages are
|
||||
required:
|
||||
|
||||
* ruby
|
||||
* bison
|
||||
|
||||
Building from git
|
||||
-----------------
|
||||
|
||||
@@ -132,6 +141,12 @@ used::
|
||||
|
||||
To compile the source code, gcc >= 4.8.3 or clang >= 3.4 is required.
|
||||
|
||||
.. note::
|
||||
|
||||
To enable mruby support in nghttpx, run ``git submodule update
|
||||
--init`` before running configure script, and use ``--with-mruby``
|
||||
configure option.
|
||||
|
||||
.. note::
|
||||
|
||||
Mac OS X users may need the ``--disable-threads`` configure option to
|
||||
@@ -187,7 +202,7 @@ https://nghttp2.org/documentation/
|
||||
Unit tests
|
||||
----------
|
||||
|
||||
Unit tests are done by simply running `make check`.
|
||||
Unit tests are done by simply running ``make check``.
|
||||
|
||||
Integration tests
|
||||
-----------------
|
||||
@@ -198,7 +213,8 @@ its testing framework. We depend on the following libraries:
|
||||
|
||||
* https://github.com/bradfitz/http2
|
||||
* https://github.com/tatsuhiro-t/go-nghttp2
|
||||
* https://golang.org/x/net/spdy
|
||||
* https://github.com/tatsuhiro-t/spdy
|
||||
* golang.org/x/net/websocket
|
||||
|
||||
To download the above packages, after settings ``GOPATH``, run the
|
||||
following command under ``integration-tests`` directory::
|
||||
@@ -212,6 +228,109 @@ To run the tests, run the following command under
|
||||
|
||||
Inside the tests, we use port 3009 to run the test subject server.
|
||||
|
||||
.. note::
|
||||
|
||||
github.com/tatsuhiro-t/spdy is a copy used to be available at
|
||||
golang.org/x/net/spdy, but it is now gone.
|
||||
|
||||
Migration from v0.7.15 or earlier
|
||||
---------------------------------
|
||||
|
||||
nghttp2 v1.0.0 introduced several backward incompatible changes. In
|
||||
this section, we describe these changes and how to migrate to v1.0.0.
|
||||
|
||||
ALPN protocol ID is now ``h2`` and ``h2c``
|
||||
++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
Previously we announced ``h2-14`` and ``h2c-14``. v1.0.0 implements
|
||||
final protocol version, and we changed ALPN ID to ``h2`` and ``h2c``.
|
||||
The macros ``NGHTTP2_PROTO_VERSION_ID``,
|
||||
``NGHTTP2_PROTO_VERSION_ID_LEN``,
|
||||
``NGHTTP2_CLEARTEXT_PROTO_VERSION_ID``, and
|
||||
``NGHTTP2_CLEARTEXT_PROTO_VERSION_ID_LEN`` have been updated to
|
||||
reflect this change.
|
||||
|
||||
Basically, existing applications do not have to do anything, just
|
||||
recompiling is enough for this change.
|
||||
|
||||
Use word "client magic" where we use "client connection preface"
|
||||
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
We use "client connection preface" to mean first 24 bytes of client
|
||||
connection preface. This is technically not correct, since client
|
||||
connection preface is composed of 24 bytes client magic byte string
|
||||
followed by SETTINGS frame. For clarification, we call "client magic"
|
||||
for this 24 bytes byte string and updated API.
|
||||
|
||||
* ``NGHTTP2_CLIENT_CONNECTION_PREFACE`` was replaced with
|
||||
``NGHTTP2_CLIENT_MAGIC``.
|
||||
* ``NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN`` was replaced with
|
||||
``NGHTTP2_CLIENT_MAGIC_LEN``.
|
||||
* ``NGHTTP2_BAD_PREFACE`` was renamed as ``NGHTTP2_BAD_CLIENT_MAGIC``
|
||||
|
||||
The alreay deprecated ``NGHTTP2_CLIENT_CONNECTION_HEADER`` and
|
||||
``NGHTTP2_CLIENT_CONNECTION_HEADER_LEN`` were removed.
|
||||
|
||||
If application uses these macros, just replace old ones with new ones.
|
||||
Since v1.0.0, client magic is sent by library (see next subsection),
|
||||
so client application may just remove these macro use.
|
||||
|
||||
Client magic is sent by library
|
||||
+++++++++++++++++++++++++++++++
|
||||
|
||||
Previously nghttp2 library did not send client magic, which is first
|
||||
24 bytes byte string of client connection preface, and client
|
||||
applications have to send it by themselves. Since v1.0.0, client
|
||||
magic is sent by library via first call of ``nghttp2_session_send()``
|
||||
or ``nghttp2_session_mem_send()``.
|
||||
|
||||
The client applications which send client magic must remove the
|
||||
relevant code.
|
||||
|
||||
Remove HTTP Alternative Services (Alt-Svc) related code
|
||||
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
Alt-Svc specification is not finalized yet. To make our API stable,
|
||||
we have decided to remove all Alt-Svc related API from nghttp2.
|
||||
|
||||
* ``NGHTTP2_EXT_ALTSVC`` was removed.
|
||||
* ``nghttp2_ext_altsvc`` was removed.
|
||||
|
||||
We have already removed the functionality of Alt-Svc in v0.7 series
|
||||
and they have been essentially noop. The application using these
|
||||
macro and struct, remove those lines.
|
||||
|
||||
Use nghttp2_error in nghttp2_on_invalid_frame_recv_callback
|
||||
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
Previously ``nghttp2_on_invalid_frame_recv_cb_called`` took the
|
||||
``error_code``, defined in ``nghttp2_error_code``, as parameter. But
|
||||
they are not detailed enough to debug. Therefore, we decided to use
|
||||
more detailed ``nghttp2_error`` values instead.
|
||||
|
||||
The application using this callback should update the callback
|
||||
signature. If it treats ``error_code`` as HTTP/2 error code, update
|
||||
the code so that it is treated as ``nghttp2_error``.
|
||||
|
||||
Receive client magic by default
|
||||
+++++++++++++++++++++++++++++++
|
||||
|
||||
Previously nghttp2 did not process client magic (24 bytes byte
|
||||
string). To make it deal with it, we had to use
|
||||
``nghttp2_option_set_recv_client_preface()``. Since v1.0.0, nghttp2
|
||||
processes client magic by default and
|
||||
``nghttp2_option_set_recv_client_preface()`` was removed.
|
||||
|
||||
Some application may want to disable this behaviour, so we added
|
||||
``nghttp2_option_set_no_recv_client_magic()`` to achieve this.
|
||||
|
||||
The application using ``nghttp2_option_set_recv_client_preface()``
|
||||
with nonzero value, just remove it.
|
||||
|
||||
The application using ``nghttp2_option_set_recv_client_preface()``
|
||||
with zero value or not using it must use
|
||||
``nghttp2_option_set_no_recv_client_magic()`` with nonzero value.
|
||||
|
||||
Client, Server and Proxy programs
|
||||
---------------------------------
|
||||
|
||||
@@ -227,140 +346,180 @@ It has verbose output mode for framing information. Here is sample
|
||||
output from ``nghttp`` client::
|
||||
|
||||
$ nghttp -nv https://nghttp2.org
|
||||
[ 0.033][NPN] server offers:
|
||||
* h2-14
|
||||
* spdy/3.1
|
||||
* http/1.1
|
||||
The negotiated protocol: h2-14
|
||||
[ 0.068] send SETTINGS frame <length=15, flags=0x00, stream_id=0>
|
||||
(niv=3)
|
||||
[SETTINGS_MAX_CONCURRENT_STREAMS(3):100]
|
||||
[SETTINGS_INITIAL_WINDOW_SIZE(4):65535]
|
||||
[SETTINGS_COMPRESS_DATA(5):1]
|
||||
[ 0.068] send HEADERS frame <length=46, flags=0x05, stream_id=1>
|
||||
; END_STREAM | END_HEADERS
|
||||
(padlen=0)
|
||||
; Open new stream
|
||||
:authority: nghttp2.org
|
||||
:method: GET
|
||||
:path: /
|
||||
:scheme: https
|
||||
accept: */*
|
||||
accept-encoding: gzip, deflate
|
||||
user-agent: nghttp2/0.4.0-DEV
|
||||
[ 0.068] recv SETTINGS frame <length=10, flags=0x00, stream_id=0>
|
||||
(niv=2)
|
||||
[SETTINGS_MAX_CONCURRENT_STREAMS(3):100]
|
||||
[SETTINGS_INITIAL_WINDOW_SIZE(4):65535]
|
||||
[ 0.068] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
|
||||
; ACK
|
||||
(niv=0)
|
||||
[ 0.079] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>
|
||||
; ACK
|
||||
(niv=0)
|
||||
[ 0.080] (stream_id=1, noind=0) :status: 200
|
||||
[ 0.080] (stream_id=1, noind=0) accept-ranges: bytes
|
||||
[ 0.080] (stream_id=1, noind=0) age: 15
|
||||
[ 0.080] (stream_id=1, noind=0) content-length: 40243
|
||||
[ 0.080] (stream_id=1, noind=0) content-type: text/html
|
||||
[ 0.080] (stream_id=1, noind=0) date: Wed, 14 May 2014 15:14:30 GMT
|
||||
[ 0.080] (stream_id=1, noind=0) etag: "535d0eea-9d33"
|
||||
[ 0.080] (stream_id=1, noind=0) last-modified: Sun, 27 Apr 2014 14:06:34 GMT
|
||||
[ 0.080] (stream_id=1, noind=0) server: nginx/1.4.6 (Ubuntu)
|
||||
[ 0.080] (stream_id=1, noind=0) x-varnish: 2114900538 2114900537
|
||||
[ 0.080] (stream_id=1, noind=0) via: 1.1 varnish, 1.1 nghttpx
|
||||
[ 0.080] (stream_id=1, noind=0) strict-transport-security: max-age=31536000
|
||||
[ 0.080] recv HEADERS frame <length=162, flags=0x04, stream_id=1>
|
||||
; END_HEADERS
|
||||
(padlen=0)
|
||||
; First response header
|
||||
[ 0.080] recv DATA frame <length=3786, flags=0x00, stream_id=1>
|
||||
[ 0.080] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.081] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.093] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.093] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.094] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.094] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.094] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.096] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.096] send WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
|
||||
(window_size_increment=36554)
|
||||
[ 0.096] send WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=1>
|
||||
(window_size_increment=36554)
|
||||
[ 0.108] recv DATA frame <length=3689, flags=0x00, stream_id=1>
|
||||
[ 0.108] recv DATA frame <length=0, flags=0x01, stream_id=1>
|
||||
; END_STREAM
|
||||
[ 0.108] send GOAWAY frame <length=8, flags=0x00, stream_id=0>
|
||||
(last_stream_id=0, error_code=NO_ERROR(0), opaque_data(0)=[])
|
||||
[ 0.190] Connected
|
||||
The negotiated protocol: h2
|
||||
[ 0.212] recv SETTINGS frame <length=12, flags=0x00, stream_id=0>
|
||||
(niv=2)
|
||||
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
|
||||
[SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
|
||||
[ 0.212] send SETTINGS frame <length=12, flags=0x00, stream_id=0>
|
||||
(niv=2)
|
||||
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
|
||||
[SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
|
||||
[ 0.212] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
|
||||
; ACK
|
||||
(niv=0)
|
||||
[ 0.212] send PRIORITY frame <length=5, flags=0x00, stream_id=3>
|
||||
(dep_stream_id=0, weight=201, exclusive=0)
|
||||
[ 0.212] send PRIORITY frame <length=5, flags=0x00, stream_id=5>
|
||||
(dep_stream_id=0, weight=101, exclusive=0)
|
||||
[ 0.212] send PRIORITY frame <length=5, flags=0x00, stream_id=7>
|
||||
(dep_stream_id=0, weight=1, exclusive=0)
|
||||
[ 0.212] send PRIORITY frame <length=5, flags=0x00, stream_id=9>
|
||||
(dep_stream_id=7, weight=1, exclusive=0)
|
||||
[ 0.212] send PRIORITY frame <length=5, flags=0x00, stream_id=11>
|
||||
(dep_stream_id=3, weight=1, exclusive=0)
|
||||
[ 0.212] send HEADERS frame <length=39, flags=0x25, stream_id=13>
|
||||
; END_STREAM | END_HEADERS | PRIORITY
|
||||
(padlen=0, dep_stream_id=11, weight=16, exclusive=0)
|
||||
; Open new stream
|
||||
:method: GET
|
||||
:path: /
|
||||
:scheme: https
|
||||
:authority: nghttp2.org
|
||||
accept: */*
|
||||
accept-encoding: gzip, deflate
|
||||
user-agent: nghttp2/1.0.1-DEV
|
||||
[ 0.221] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>
|
||||
; ACK
|
||||
(niv=0)
|
||||
[ 0.221] recv (stream_id=13) :method: GET
|
||||
[ 0.221] recv (stream_id=13) :scheme: https
|
||||
[ 0.221] recv (stream_id=13) :path: /stylesheets/screen.css
|
||||
[ 0.221] recv (stream_id=13) :authority: nghttp2.org
|
||||
[ 0.221] recv (stream_id=13) accept-encoding: gzip, deflate
|
||||
[ 0.222] recv (stream_id=13) user-agent: nghttp2/1.0.1-DEV
|
||||
[ 0.222] recv PUSH_PROMISE frame <length=50, flags=0x04, stream_id=13>
|
||||
; END_HEADERS
|
||||
(padlen=0, promised_stream_id=2)
|
||||
[ 0.222] recv (stream_id=13) :status: 200
|
||||
[ 0.222] recv (stream_id=13) date: Thu, 21 May 2015 16:38:14 GMT
|
||||
[ 0.222] recv (stream_id=13) content-type: text/html
|
||||
[ 0.222] recv (stream_id=13) last-modified: Fri, 15 May 2015 15:38:06 GMT
|
||||
[ 0.222] recv (stream_id=13) etag: W/"555612de-19f6"
|
||||
[ 0.222] recv (stream_id=13) link: </stylesheets/screen.css>; rel=preload; as=stylesheet
|
||||
[ 0.222] recv (stream_id=13) content-encoding: gzip
|
||||
[ 0.222] recv (stream_id=13) server: nghttpx nghttp2/1.0.1-DEV
|
||||
[ 0.222] recv (stream_id=13) via: 1.1 nghttpx
|
||||
[ 0.222] recv (stream_id=13) strict-transport-security: max-age=31536000
|
||||
[ 0.222] recv HEADERS frame <length=166, flags=0x04, stream_id=13>
|
||||
; END_HEADERS
|
||||
(padlen=0)
|
||||
; First response header
|
||||
[ 0.222] recv DATA frame <length=2601, flags=0x01, stream_id=13>
|
||||
; END_STREAM
|
||||
[ 0.222] recv (stream_id=2) :status: 200
|
||||
[ 0.222] recv (stream_id=2) date: Thu, 21 May 2015 16:38:14 GMT
|
||||
[ 0.222] recv (stream_id=2) content-type: text/css
|
||||
[ 0.222] recv (stream_id=2) last-modified: Fri, 15 May 2015 15:38:06 GMT
|
||||
[ 0.222] recv (stream_id=2) etag: W/"555612de-9845"
|
||||
[ 0.222] recv (stream_id=2) content-encoding: gzip
|
||||
[ 0.222] recv (stream_id=2) server: nghttpx nghttp2/1.0.1-DEV
|
||||
[ 0.222] recv (stream_id=2) via: 1.1 nghttpx
|
||||
[ 0.222] recv (stream_id=2) strict-transport-security: max-age=31536000
|
||||
[ 0.222] recv HEADERS frame <length=32, flags=0x04, stream_id=2>
|
||||
; END_HEADERS
|
||||
(padlen=0)
|
||||
; First push response header
|
||||
[ 0.228] recv DATA frame <length=8715, flags=0x01, stream_id=2>
|
||||
; END_STREAM
|
||||
[ 0.228] send GOAWAY frame <length=8, flags=0x00, stream_id=0>
|
||||
(last_stream_id=2, error_code=NO_ERROR(0x00), opaque_data(0)=[])
|
||||
|
||||
The HTTP Upgrade is performed like this::
|
||||
The HTTP Upgrade is performed like so::
|
||||
|
||||
$ nghttp -nvu http://nghttp2.org
|
||||
[ 0.013] HTTP Upgrade request
|
||||
[ 0.011] Connected
|
||||
[ 0.011] HTTP Upgrade request
|
||||
GET / HTTP/1.1
|
||||
Host: nghttp2.org
|
||||
Connection: Upgrade, HTTP2-Settings
|
||||
Upgrade: h2c-14
|
||||
HTTP2-Settings: AwAAAGQEAAD__wUAAAAB
|
||||
Upgrade: h2c
|
||||
HTTP2-Settings: AAMAAABkAAQAAP__
|
||||
Accept: */*
|
||||
User-Agent: nghttp2/0.4.0-DEV
|
||||
User-Agent: nghttp2/1.0.1-DEV
|
||||
|
||||
|
||||
[ 0.024] HTTP Upgrade response
|
||||
[ 0.018] HTTP Upgrade response
|
||||
HTTP/1.1 101 Switching Protocols
|
||||
Connection: Upgrade
|
||||
Upgrade: h2c-14
|
||||
Upgrade: h2c
|
||||
|
||||
|
||||
[ 0.024] HTTP Upgrade success
|
||||
[ 0.024] send SETTINGS frame <length=15, flags=0x00, stream_id=0>
|
||||
(niv=3)
|
||||
[SETTINGS_MAX_CONCURRENT_STREAMS(3):100]
|
||||
[SETTINGS_INITIAL_WINDOW_SIZE(4):65535]
|
||||
[SETTINGS_COMPRESS_DATA(5):1]
|
||||
[ 0.024] recv SETTINGS frame <length=10, flags=0x00, stream_id=0>
|
||||
(niv=2)
|
||||
[SETTINGS_MAX_CONCURRENT_STREAMS(3):100]
|
||||
[SETTINGS_INITIAL_WINDOW_SIZE(4):65535]
|
||||
[ 0.024] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
|
||||
; ACK
|
||||
(niv=0)
|
||||
[ 0.024] (stream_id=1, noind=0) :status: 200
|
||||
[ 0.024] (stream_id=1, noind=0) accept-ranges: bytes
|
||||
[ 0.024] (stream_id=1, noind=0) age: 10
|
||||
[ 0.024] (stream_id=1, noind=0) content-length: 40243
|
||||
[ 0.024] (stream_id=1, noind=0) content-type: text/html
|
||||
[ 0.024] (stream_id=1, noind=0) date: Wed, 14 May 2014 15:16:34 GMT
|
||||
[ 0.024] (stream_id=1, noind=0) etag: "535d0eea-9d33"
|
||||
[ 0.024] (stream_id=1, noind=0) last-modified: Sun, 27 Apr 2014 14:06:34 GMT
|
||||
[ 0.024] (stream_id=1, noind=0) server: nginx/1.4.6 (Ubuntu)
|
||||
[ 0.024] (stream_id=1, noind=0) x-varnish: 2114900541 2114900540
|
||||
[ 0.024] (stream_id=1, noind=0) via: 1.1 varnish, 1.1 nghttpx
|
||||
[ 0.024] recv HEADERS frame <length=148, flags=0x04, stream_id=1>
|
||||
; END_HEADERS
|
||||
(padlen=0)
|
||||
; First response header
|
||||
[ 0.024] recv DATA frame <length=3786, flags=0x00, stream_id=1>
|
||||
[ 0.025] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.031] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.031] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.032] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.032] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.033] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.033] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.033] send WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
|
||||
(window_size_increment=33164)
|
||||
[ 0.033] send WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=1>
|
||||
(window_size_increment=33164)
|
||||
[ 0.038] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.038] recv DATA frame <length=3689, flags=0x00, stream_id=1>
|
||||
[ 0.038] recv DATA frame <length=0, flags=0x01, stream_id=1>
|
||||
; END_STREAM
|
||||
[ 0.038] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>
|
||||
; ACK
|
||||
(niv=0)
|
||||
[ 0.038] send GOAWAY frame <length=8, flags=0x00, stream_id=0>
|
||||
(last_stream_id=0, error_code=NO_ERROR(0), opaque_data(0)=[])
|
||||
[ 0.018] HTTP Upgrade success
|
||||
[ 0.018] recv SETTINGS frame <length=12, flags=0x00, stream_id=0>
|
||||
(niv=2)
|
||||
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
|
||||
[SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
|
||||
[ 0.018] send SETTINGS frame <length=12, flags=0x00, stream_id=0>
|
||||
(niv=2)
|
||||
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
|
||||
[SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
|
||||
[ 0.018] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
|
||||
; ACK
|
||||
(niv=0)
|
||||
[ 0.018] send PRIORITY frame <length=5, flags=0x00, stream_id=3>
|
||||
(dep_stream_id=0, weight=201, exclusive=0)
|
||||
[ 0.018] send PRIORITY frame <length=5, flags=0x00, stream_id=5>
|
||||
(dep_stream_id=0, weight=101, exclusive=0)
|
||||
[ 0.018] send PRIORITY frame <length=5, flags=0x00, stream_id=7>
|
||||
(dep_stream_id=0, weight=1, exclusive=0)
|
||||
[ 0.018] send PRIORITY frame <length=5, flags=0x00, stream_id=9>
|
||||
(dep_stream_id=7, weight=1, exclusive=0)
|
||||
[ 0.018] send PRIORITY frame <length=5, flags=0x00, stream_id=11>
|
||||
(dep_stream_id=3, weight=1, exclusive=0)
|
||||
[ 0.018] send PRIORITY frame <length=5, flags=0x00, stream_id=1>
|
||||
(dep_stream_id=11, weight=16, exclusive=0)
|
||||
[ 0.019] recv (stream_id=1) :method: GET
|
||||
[ 0.019] recv (stream_id=1) :scheme: http
|
||||
[ 0.019] recv (stream_id=1) :path: /stylesheets/screen.css
|
||||
[ 0.019] recv (stream_id=1) host: nghttp2.org
|
||||
[ 0.019] recv (stream_id=1) user-agent: nghttp2/1.0.1-DEV
|
||||
[ 0.019] recv PUSH_PROMISE frame <length=49, flags=0x04, stream_id=1>
|
||||
; END_HEADERS
|
||||
(padlen=0, promised_stream_id=2)
|
||||
[ 0.019] recv (stream_id=1) :status: 200
|
||||
[ 0.019] recv (stream_id=1) date: Thu, 21 May 2015 16:39:16 GMT
|
||||
[ 0.019] recv (stream_id=1) content-type: text/html
|
||||
[ 0.019] recv (stream_id=1) content-length: 6646
|
||||
[ 0.019] recv (stream_id=1) last-modified: Fri, 15 May 2015 15:38:06 GMT
|
||||
[ 0.019] recv (stream_id=1) etag: "555612de-19f6"
|
||||
[ 0.019] recv (stream_id=1) link: </stylesheets/screen.css>; rel=preload; as=stylesheet
|
||||
[ 0.019] recv (stream_id=1) accept-ranges: bytes
|
||||
[ 0.019] recv (stream_id=1) server: nghttpx nghttp2/1.0.1-DEV
|
||||
[ 0.019] recv (stream_id=1) via: 1.1 nghttpx
|
||||
[ 0.019] recv HEADERS frame <length=157, flags=0x04, stream_id=1>
|
||||
; END_HEADERS
|
||||
(padlen=0)
|
||||
; First response header
|
||||
[ 0.019] recv DATA frame <length=6646, flags=0x01, stream_id=1>
|
||||
; END_STREAM
|
||||
[ 0.019] recv (stream_id=2) :status: 200
|
||||
[ 0.019] recv (stream_id=2) date: Thu, 21 May 2015 16:39:16 GMT
|
||||
[ 0.019] recv (stream_id=2) content-type: text/css
|
||||
[ 0.019] recv (stream_id=2) content-length: 38981
|
||||
[ 0.019] recv (stream_id=2) last-modified: Fri, 15 May 2015 15:38:06 GMT
|
||||
[ 0.019] recv (stream_id=2) etag: "555612de-9845"
|
||||
[ 0.019] recv (stream_id=2) accept-ranges: bytes
|
||||
[ 0.019] recv (stream_id=2) server: nghttpx nghttp2/1.0.1-DEV
|
||||
[ 0.019] recv (stream_id=2) via: 1.1 nghttpx
|
||||
[ 0.019] recv HEADERS frame <length=36, flags=0x04, stream_id=2>
|
||||
; END_HEADERS
|
||||
(padlen=0)
|
||||
; First push response header
|
||||
[ 0.026] recv DATA frame <length=16384, flags=0x00, stream_id=2>
|
||||
[ 0.027] recv DATA frame <length=7952, flags=0x00, stream_id=2>
|
||||
[ 0.027] send WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
|
||||
(window_size_increment=33343)
|
||||
[ 0.032] send WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=2>
|
||||
(window_size_increment=33707)
|
||||
[ 0.032] recv DATA frame <length=14645, flags=0x01, stream_id=2>
|
||||
; END_STREAM
|
||||
[ 0.032] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>
|
||||
; ACK
|
||||
(niv=0)
|
||||
[ 0.032] send GOAWAY frame <length=8, flags=0x00, stream_id=0>
|
||||
(last_stream_id=2, error_code=NO_ERROR(0x00), opaque_data(0)=[])
|
||||
|
||||
Using the ``-s`` option, ``nghttp`` prints out some timing information for
|
||||
requests, sorted by completion time::
|
||||
@@ -369,24 +528,26 @@ requests, sorted by completion time::
|
||||
***** Statistics *****
|
||||
|
||||
Request timing:
|
||||
complete: relative time from protocol handshake to stream close
|
||||
request: relative time from protocol handshake to request
|
||||
transmission. If '*' is shown, this was pushed by server.
|
||||
process: time for request and response
|
||||
code: HTTP status code
|
||||
size: number of bytes received as response body without
|
||||
inflation.
|
||||
URI: request URI
|
||||
responseEnd: the time when last byte of response was received
|
||||
relative to connectEnd
|
||||
requestStart: the time just before first byte of request was sent
|
||||
relative to connectEnd. If '*' is shown, this was
|
||||
pushed by server.
|
||||
process: responseEnd - requestStart
|
||||
code: HTTP status code
|
||||
size: number of bytes received as response body without
|
||||
inflation.
|
||||
URI: request URI
|
||||
|
||||
see http://www.w3.org/TR/resource-timing/#processing-model
|
||||
|
||||
sorted by 'complete'
|
||||
|
||||
complete request process code size request path
|
||||
+11.07ms +120us 10.95ms 200 9K /
|
||||
+16.77ms * +8.80ms 7.98ms 200 8K /stylesheets/screen.css
|
||||
+27.00ms +11.16ms 15.84ms 200 3K /javascripts/octopress.js
|
||||
+27.40ms +11.16ms 16.24ms 200 3K /javascripts/modernizr-2.0.js
|
||||
+76.14ms +11.17ms 64.97ms 200 171K /images/posts/with-pri-blog.png
|
||||
+88.52ms +11.17ms 77.36ms 200 174K /images/posts/without-pri-blog.png
|
||||
id responseEnd requestStart process code size request path
|
||||
13 +37.19ms +280us 36.91ms 200 2K /
|
||||
2 +72.65ms * +36.38ms 36.26ms 200 8K /stylesheets/screen.css
|
||||
17 +77.43ms +38.67ms 38.75ms 200 3K /javascripts/octopress.js
|
||||
15 +78.12ms +38.66ms 39.46ms 200 3K /javascripts/modernizr-2.0.js
|
||||
|
||||
Using the ``-r`` option, ``nghttp`` writes more detailed timing data to
|
||||
the given file in HAR format.
|
||||
@@ -408,58 +569,65 @@ Just like ``nghttp``, it has a verbose output mode for framing
|
||||
information. Here is sample output from ``nghttpd``::
|
||||
|
||||
$ nghttpd --no-tls -v 8080
|
||||
IPv4: listen on port 8080
|
||||
IPv6: listen on port 8080
|
||||
[id=1] [ 15.921] send SETTINGS frame <length=10, flags=0x00, stream_id=0>
|
||||
IPv4: listen 0.0.0.0:8080
|
||||
IPv6: listen :::8080
|
||||
[id=1] [ 1.521] send SETTINGS frame <length=6, flags=0x00, stream_id=0>
|
||||
(niv=1)
|
||||
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
|
||||
[id=1] [ 1.521] recv SETTINGS frame <length=12, flags=0x00, stream_id=0>
|
||||
(niv=2)
|
||||
[SETTINGS_MAX_CONCURRENT_STREAMS(3):100]
|
||||
[SETTINGS_COMPRESS_DATA(5):1]
|
||||
[id=1] [ 15.921] recv SETTINGS frame <length=15, flags=0x00, stream_id=0>
|
||||
(niv=3)
|
||||
[SETTINGS_MAX_CONCURRENT_STREAMS(3):100]
|
||||
[SETTINGS_INITIAL_WINDOW_SIZE(4):65535]
|
||||
[SETTINGS_COMPRESS_DATA(5):1]
|
||||
[id=1] [ 15.921] (stream_id=1, noind=0) :authority: localhost:8080
|
||||
[id=1] [ 15.921] (stream_id=1, noind=0) :method: GET
|
||||
[id=1] [ 15.921] (stream_id=1, noind=0) :path: /
|
||||
[id=1] [ 15.921] (stream_id=1, noind=0) :scheme: http
|
||||
[id=1] [ 15.921] (stream_id=1, noind=0) accept: */*
|
||||
[id=1] [ 15.921] (stream_id=1, noind=0) accept-encoding: gzip, deflate
|
||||
[id=1] [ 15.921] (stream_id=1, noind=0) user-agent: nghttp2/0.4.0-DEV
|
||||
[id=1] [ 15.921] recv HEADERS frame <length=48, flags=0x05, stream_id=1>
|
||||
; END_STREAM | END_HEADERS
|
||||
(padlen=0)
|
||||
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
|
||||
[SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
|
||||
[id=1] [ 1.521] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>
|
||||
; ACK
|
||||
(niv=0)
|
||||
[id=1] [ 1.521] recv PRIORITY frame <length=5, flags=0x00, stream_id=3>
|
||||
(dep_stream_id=0, weight=201, exclusive=0)
|
||||
[id=1] [ 1.521] recv PRIORITY frame <length=5, flags=0x00, stream_id=5>
|
||||
(dep_stream_id=0, weight=101, exclusive=0)
|
||||
[id=1] [ 1.521] recv PRIORITY frame <length=5, flags=0x00, stream_id=7>
|
||||
(dep_stream_id=0, weight=1, exclusive=0)
|
||||
[id=1] [ 1.521] recv PRIORITY frame <length=5, flags=0x00, stream_id=9>
|
||||
(dep_stream_id=7, weight=1, exclusive=0)
|
||||
[id=1] [ 1.521] recv PRIORITY frame <length=5, flags=0x00, stream_id=11>
|
||||
(dep_stream_id=3, weight=1, exclusive=0)
|
||||
[id=1] [ 1.521] recv (stream_id=13) :method: GET
|
||||
[id=1] [ 1.521] recv (stream_id=13) :path: /
|
||||
[id=1] [ 1.521] recv (stream_id=13) :scheme: http
|
||||
[id=1] [ 1.521] recv (stream_id=13) :authority: localhost:8080
|
||||
[id=1] [ 1.521] recv (stream_id=13) accept: */*
|
||||
[id=1] [ 1.521] recv (stream_id=13) accept-encoding: gzip, deflate
|
||||
[id=1] [ 1.521] recv (stream_id=13) user-agent: nghttp2/1.0.0-DEV
|
||||
[id=1] [ 1.521] recv HEADERS frame <length=41, flags=0x25, stream_id=13>
|
||||
; END_STREAM | END_HEADERS | PRIORITY
|
||||
(padlen=0, dep_stream_id=11, weight=16, exclusive=0)
|
||||
; Open new stream
|
||||
[id=1] [ 15.921] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>
|
||||
[id=1] [ 1.521] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
|
||||
; ACK
|
||||
(niv=0)
|
||||
[id=1] [ 15.921] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
|
||||
; ACK
|
||||
(niv=0)
|
||||
[id=1] [ 15.921] send HEADERS frame <length=82, flags=0x04, stream_id=1>
|
||||
[id=1] [ 1.521] send HEADERS frame <length=86, flags=0x04, stream_id=13>
|
||||
; END_HEADERS
|
||||
(padlen=0)
|
||||
; First response header
|
||||
:status: 200
|
||||
server: nghttpd nghttp2/1.0.0-DEV
|
||||
content-length: 10
|
||||
cache-control: max-age=3600
|
||||
content-length: 612
|
||||
date: Wed, 14 May 2014 15:19:03 GMT
|
||||
last-modified: Sat, 08 Mar 2014 16:04:06 GMT
|
||||
server: nghttpd nghttp2/0.4.0-DEV
|
||||
[id=1] [ 15.922] send DATA frame <length=381, flags=0x20, stream_id=1>
|
||||
; COMPRESSED
|
||||
[id=1] [ 15.922] send DATA frame <length=0, flags=0x01, stream_id=1>
|
||||
date: Fri, 15 May 2015 14:49:04 GMT
|
||||
last-modified: Tue, 30 Sep 2014 12:40:52 GMT
|
||||
[id=1] [ 1.522] send DATA frame <length=10, flags=0x01, stream_id=13>
|
||||
; END_STREAM
|
||||
[id=1] [ 15.922] stream_id=1 closed
|
||||
[id=1] [ 15.922] recv GOAWAY frame <length=8, flags=0x00, stream_id=0>
|
||||
(last_stream_id=0, error_code=NO_ERROR(0), opaque_data(0)=[])
|
||||
[id=1] [ 15.922] closed
|
||||
[id=1] [ 1.522] stream_id=13 closed
|
||||
[id=1] [ 1.522] recv GOAWAY frame <length=8, flags=0x00, stream_id=0>
|
||||
(last_stream_id=0, error_code=NO_ERROR(0x00), opaque_data(0)=[])
|
||||
[id=1] [ 1.522] closed
|
||||
|
||||
nghttpx - proxy
|
||||
+++++++++++++++
|
||||
|
||||
``nghttpx`` is a multi-threaded reverse proxy for ``h2-14``, SPDY and
|
||||
HTTP/1.1, and powers http://nghttp2.org and supports HTTP/2 server push.
|
||||
``nghttpx`` is a multi-threaded reverse proxy for HTTP/2, SPDY and
|
||||
HTTP/1.1, and powers http://nghttp2.org and supports HTTP/2 server
|
||||
push.
|
||||
|
||||
``nghttpx`` implements `important performance-oriented features
|
||||
<https://istlsfastyet.com/#server-performance>`_ in TLS, such as
|
||||
@@ -480,8 +648,8 @@ default mode HTTP/2, SPDY, HTTP/1.1 (TLS) HTTP/1.1 Reverse proxy
|
||||
================== ============================ ============== =============
|
||||
|
||||
The interesting mode at the moment is the default mode. It works like
|
||||
a reverse proxy and listens for ``h2-14``, SPDY and HTTP/1.1 and can
|
||||
be deployed as a SSL/TLS terminator for existing web server.
|
||||
a reverse proxy and listens for HTTP/2, SPDY and HTTP/1.1 and can be
|
||||
deployed as a SSL/TLS terminator for existing web server.
|
||||
|
||||
The default mode, ``--http2-proxy`` and ``--http2-bridge`` modes use
|
||||
SSL/TLS in the frontend connection by default. To disable SSL/TLS,
|
||||
@@ -490,7 +658,7 @@ disabled in the frontend and incoming HTTP/1.1 connections can be
|
||||
upgraded to HTTP/2 through HTTP Upgrade.
|
||||
|
||||
The ``--http2-bridge``, ``--client`` and ``--client-proxy`` modes use
|
||||
SSL/TLS in the backend connection by deafult. To disable SSL/TLS, use
|
||||
SSL/TLS in the backend connection by default. To disable SSL/TLS, use
|
||||
the ``--backend-no-tls`` option.
|
||||
|
||||
``nghttpx`` supports a configuration file. See the ``--conf`` option and
|
||||
@@ -582,6 +750,7 @@ follows::
|
||||
spawning thread #0: 100 concurrent clients, 100000 total requests
|
||||
Protocol: TLSv1.2
|
||||
Cipher: ECDHE-RSA-AES128-GCM-SHA256
|
||||
Server Temp Key: ECDH P-256 256 bits
|
||||
progress: 10% done
|
||||
progress: 20% done
|
||||
progress: 30% done
|
||||
@@ -593,12 +762,14 @@ follows::
|
||||
progress: 90% done
|
||||
progress: 100% done
|
||||
|
||||
finished in 7.10s, 14092 req/s, 55.67MB/s
|
||||
finished in 771.26ms, 129658 req/s, 4.71MB/s
|
||||
requests: 100000 total, 100000 started, 100000 done, 100000 succeeded, 0 failed, 0 errored
|
||||
status codes: 100000 2xx, 0 3xx, 0 4xx, 0 5xx
|
||||
traffic: 414200800 bytes total, 2723100 bytes headers, 409600000 bytes data
|
||||
traffic: 3812300 bytes total, 1009900 bytes headers, 1000000 bytes data
|
||||
min max mean sd +/- sd
|
||||
time for request: 283.86ms 1.46s 659.70ms 150.87ms 84.68%
|
||||
time for request: 25.12ms 124.55ms 51.07ms 15.36ms 84.87%
|
||||
time for connect: 208.94ms 254.67ms 241.38ms 7.95ms 63.00%
|
||||
time to 1st byte: 209.11ms 254.80ms 241.51ms 7.94ms 63.00%
|
||||
|
||||
The above example issued total 100,000 requests, using 100 concurrent
|
||||
clients (in other words, 100 HTTP/2 sessions), and a maximum of 100 streams
|
||||
@@ -785,7 +956,7 @@ max_deflate_size
|
||||
The maximum header table size the encoder uses. This can be smaller
|
||||
than ``max_size``. In this case, the encoder only uses up to first
|
||||
``max_deflate_size`` buffer. Since the header table size is still
|
||||
``max_size``, the encoder has to keep track of entries ouside the
|
||||
``max_size``, the encoder has to keep track of entries outside the
|
||||
``max_deflate_size`` but inside the ``max_size`` and make sure
|
||||
that they are no longer referenced.
|
||||
|
||||
@@ -1068,6 +1239,8 @@ an HTTP/2 server looks like this:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <nghttp2/asio_http2_server.h>
|
||||
|
||||
using namespace nghttp2::asio_http2;
|
||||
@@ -1108,32 +1281,32 @@ Here is sample code to use the client API:
|
||||
session sess(io_service, "localhost", "3000");
|
||||
|
||||
sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) {
|
||||
boost::system::error_code ec;
|
||||
boost::system::error_code ec;
|
||||
|
||||
auto req = sess.submit(ec, "GET", "http://localhost:3000/");
|
||||
auto req = sess.submit(ec, "GET", "http://localhost:3000/");
|
||||
|
||||
req->on_response([](const response &res) {
|
||||
// print status code and response header fields.
|
||||
std::cerr << "HTTP/2 " << res.status_code() << std::endl;
|
||||
for (auto &kv : res.header()) {
|
||||
std::cerr << kv.first << ": " << kv.second.value << "\n";
|
||||
}
|
||||
std::cerr << std::endl;
|
||||
req->on_response([](const response &res) {
|
||||
// print status code and response header fields.
|
||||
std::cerr << "HTTP/2 " << res.status_code() << std::endl;
|
||||
for (auto &kv : res.header()) {
|
||||
std::cerr << kv.first << ": " << kv.second.value << "\n";
|
||||
}
|
||||
std::cerr << std::endl;
|
||||
|
||||
res.on_data([](const uint8_t *data, std::size_t len) {
|
||||
std::cerr.write(reinterpret_cast<const char *>(data), len);
|
||||
std::cerr << std::endl;
|
||||
});
|
||||
});
|
||||
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();
|
||||
});
|
||||
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;
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
});
|
||||
|
||||
io_service.run();
|
||||
@@ -1273,7 +1446,7 @@ original creator(s) or those who have been assigned copyright by the
|
||||
original author(s).
|
||||
|
||||
By submitting a patch to the nghttp2 project, you (or your employer, as
|
||||
the case may be) agree to assign the copyright of your submission to us.
|
||||
the case may be) agree to assign the copyright of your submission to us.
|
||||
.. the above really needs to be reworded to pass legal muster.
|
||||
We will credit you for your
|
||||
changes as far as possible, to give credit but also to keep a trace
|
||||
|
||||
@@ -27,9 +27,9 @@ 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
|
||||
PREFIX="$ANDROID_HOME"/usr/local
|
||||
TOOLCHAIN="$ANDROID_HOME"/toolchain
|
||||
PATH="$TOOLCHAIN"/bin:"$PATH"
|
||||
|
||||
./configure \
|
||||
--disable-shared \
|
||||
@@ -40,8 +40,8 @@ PATH=$TOOLCHAIN/bin:$PATH
|
||||
--disable-python-bindings \
|
||||
--disable-examples \
|
||||
--enable-werror \
|
||||
CC=clang \
|
||||
CXX=clang++ \
|
||||
CC="$TOOLCHAIN"/bin/clang \
|
||||
CXX="$TOOLCHAIN"/bin/clang++ \
|
||||
CPPFLAGS="-fPIE -I$PREFIX/include" \
|
||||
PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \
|
||||
LDFLAGS="-fPIE -pie -L$PREFIX/lib"
|
||||
|
||||
209
configure.ac
209
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.12], [t-tujikawa@users.sourceforge.net])
|
||||
AC_INIT([nghttp2], [1.3.4], [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, 13)
|
||||
AC_SUBST(LT_REVISION, 1)
|
||||
AC_SUBST(LT_AGE, 8)
|
||||
AC_SUBST(LT_CURRENT, 15)
|
||||
AC_SUBST(LT_REVISION, 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],
|
||||
@@ -118,6 +119,11 @@ AC_ARG_WITH([spdylay],
|
||||
[Use spdylay [default=check]])],
|
||||
[request_spdylay=$withval], [request_spdylay=check])
|
||||
|
||||
AC_ARG_WITH([mruby],
|
||||
[AS_HELP_STRING([--with-mruby],
|
||||
[Use mruby [default=no]])],
|
||||
[request_mruby=$withval], [request_mruby=no])
|
||||
|
||||
AC_ARG_WITH([cython],
|
||||
[AS_HELP_STRING([--with-cython=PATH],
|
||||
[Use cython in given PATH])],
|
||||
@@ -129,14 +135,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
|
||||
|
||||
@@ -157,32 +166,16 @@ fi
|
||||
#
|
||||
if test "x$GCC" = "xyes" -o "x$CC" = "xclang" ; then
|
||||
AC_DEFINE([_U_], [__attribute__((unused))], [Hint to the compiler that a function parameters is not used])
|
||||
AC_DEFINE([NGHTTP2_NORETURN], [__attribute__((noreturn))], [Hint to the compiler that a function never return])
|
||||
else
|
||||
AC_DEFINE([_U_], , [Hint to the compiler that a function parameters is not used])
|
||||
AC_DEFINE([_U_], , [Hint to the compiler that a function parameters is not use AC_DEFINE([NGHTTP2_NORETURN], , [Hint to the compiler that a function never return])
|
||||
d])
|
||||
fi
|
||||
|
||||
AX_CXX_COMPILE_STDCXX_11([noext], [optional])
|
||||
|
||||
AC_LANG_PUSH(C++)
|
||||
|
||||
# Check that std::chrono::steady_clock is available. In particular,
|
||||
# gcc 4.6 does not have one, but has monotonic_clock which is the old
|
||||
# name existed in the pre-standard draft. If steady_clock is not
|
||||
# available, don't define HAVE_STEADY_CLOCK and replace steady_clock
|
||||
# with monotonic_clock.
|
||||
AC_MSG_CHECKING([whether std::chrono::steady_clock is available])
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
|
||||
[[
|
||||
#include <chrono>
|
||||
]],
|
||||
[[
|
||||
auto tp = std::chrono::steady_clock::now();
|
||||
]])],
|
||||
[AC_DEFINE([HAVE_STEADY_CLOCK], [1],
|
||||
[Define to 1 if you have the `std::chrono::steady_clock`.])
|
||||
AC_MSG_RESULT([yes])],
|
||||
[AC_MSG_RESULT([no])])
|
||||
|
||||
# Check that std::future is available.
|
||||
AC_MSG_CHECKING([whether std::future is available])
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
|
||||
@@ -226,20 +219,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
|
||||
@@ -253,16 +232,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
|
||||
@@ -328,7 +301,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
|
||||
@@ -358,9 +331,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
|
||||
@@ -390,6 +377,20 @@ fi
|
||||
|
||||
AM_CONDITIONAL([HAVE_SPDYLAY], [ test "x${have_spdylay}" = "xyes" ])
|
||||
|
||||
# mruby (for src/nghttpx)
|
||||
have_mruby=no
|
||||
if test "x${request_mruby}" = "xyes"; then
|
||||
# We are going to build mruby
|
||||
have_mruby=yes
|
||||
AC_DEFINE([HAVE_MRUBY], [1], [Define to 1 if you have `mruby` library.])
|
||||
LIBMRUBY_LIBS="-lmruby -lm"
|
||||
LIBMRUBY_CFLAGS=
|
||||
AC_SUBST([LIBMRUBY_LIBS])
|
||||
AC_SUBST([LIBMRUBY_CFLAGS])
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL([HAVE_MRUBY], [test "x${have_mruby}" = "xyes"])
|
||||
|
||||
# Check Boost Asio library
|
||||
have_asio_lib=no
|
||||
|
||||
@@ -465,6 +466,18 @@ 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_hpack_tools}" = "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" &&
|
||||
@@ -498,12 +511,19 @@ 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 \
|
||||
])
|
||||
@@ -515,8 +535,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],
|
||||
@@ -531,16 +559,40 @@ fi
|
||||
# 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 \
|
||||
])
|
||||
|
||||
@@ -588,6 +640,21 @@ if test "x$werror" != "xno"; then
|
||||
AX_CHECK_COMPILE_FLAG([-Wdiv-by-zero], [CFLAGS="$CFLAGS -Wdiv-by-zero"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wshorten-64-to-32], [CFLAGS="$CFLAGS -Wshorten-64-to-32"])
|
||||
|
||||
AX_CHECK_COMPILE_FLAG([-Wconversion], [CFLAGS="$CFLAGS -Wconversion"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wextended-offsetof], [CFLAGS="$CFLAGS -Wextended-offsetof"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wformat-nonliteral], [CFLAGS="$CFLAGS -Wformat-nonliteral"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wlanguage-extension-token], [CFLAGS="$CFLAGS -Wlanguage-extension-token"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wmissing-field-initializers], [CFLAGS="$CFLAGS -Wmissing-field-initializers"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wmissing-noreturn], [CFLAGS="$CFLAGS -Wmissing-noreturn"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wmissing-variable-declarations], [CFLAGS="$CFLAGS -Wmissing-variable-declarations"])
|
||||
# Not used because we cannot change public structs
|
||||
# AX_CHECK_COMPILE_FLAG([-Wpadded], [CFLAGS="$CFLAGS -Wpadded"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wsign-conversion], [CFLAGS="$CFLAGS -Wsign-conversion"])
|
||||
# Not used because this basically disallows default case
|
||||
# AX_CHECK_COMPILE_FLAG([-Wswitch-enum], [CFLAGS="$CFLAGS -Wswitch-enum"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wunreachable-code-break], [CFLAGS="$CFLAGS -Wunreachable-code-break"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wunused-macros], [CFLAGS="$CFLAGS -Wunused-macros"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wunused-parameter], [CFLAGS="$CFLAGS -Wunused-parameter"])
|
||||
# Only work with Clang for the moment
|
||||
AX_CHECK_COMPILE_FLAG([-Wheader-guard], [CFLAGS="$CFLAGS -Wheader-guard"])
|
||||
fi
|
||||
@@ -650,6 +717,7 @@ AC_CONFIG_FILES([
|
||||
doc/asio_http2_client.h.rst
|
||||
doc/contribute.rst
|
||||
contrib/Makefile
|
||||
script/Makefile
|
||||
])
|
||||
AC_OUTPUT
|
||||
|
||||
@@ -686,8 +754,10 @@ AC_MSG_NOTICE([summary of build options:
|
||||
Libev: ${have_libev}
|
||||
Libevent(SSL): ${have_libevent_openssl}
|
||||
Spdylay: ${have_spdylay}
|
||||
MRuby: ${have_mruby}
|
||||
Jansson: ${have_jansson}
|
||||
Jemalloc: ${have_jemalloc}
|
||||
Zlib: ${have_zlib}
|
||||
Boost CPPFLAGS: ${BOOST_CPPFLAGS}
|
||||
Boost LDFLAGS: ${BOOST_LDFLAGS}
|
||||
Boost::ASIO: ${BOOST_ASIO_LIB}
|
||||
@@ -700,4 +770,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)
|
||||
|
||||
73
contrib/nghttpx-init.in
Normal file → Executable file
73
contrib/nghttpx-init.in
Normal file → Executable file
@@ -20,7 +20,7 @@ NAME=nghttpx
|
||||
# Depending on the configuration, binary may be located under @sbindir@
|
||||
DAEMON=@bindir@/$NAME
|
||||
PIDFILE=/var/run/$NAME.pid
|
||||
DAEMON_ARGS="--conf /etc/nghttpx/nghttpx.conf --pid-file=$PIDFILE"
|
||||
DAEMON_ARGS="--conf /etc/nghttpx/nghttpx.conf --pid-file=$PIDFILE --daemon"
|
||||
SCRIPTNAME=/etc/init.d/$NAME
|
||||
|
||||
# Exit if the package is not installed
|
||||
@@ -83,19 +83,6 @@ do_stop()
|
||||
return "$RETVAL"
|
||||
}
|
||||
|
||||
#
|
||||
# Function that sends a SIGHUP to the daemon/service
|
||||
#
|
||||
do_reload() {
|
||||
#
|
||||
# If the daemon can reload its configuration without
|
||||
# restarting (for example, when it is sent a SIGHUP),
|
||||
# then implement that here.
|
||||
#
|
||||
start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME
|
||||
return 0
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
|
||||
@@ -114,34 +101,38 @@ case "$1" in
|
||||
esac
|
||||
;;
|
||||
status)
|
||||
status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
|
||||
;;
|
||||
status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
|
||||
;;
|
||||
upgrade)
|
||||
log_daemon_msg "Upgrade $DESC" "$NAME"
|
||||
pid=`pidofproc -p $PIDFILE $NAME`
|
||||
case "$?" in
|
||||
0) echo "Sending USR2 signal to $pid"
|
||||
kill -USR2 $pid
|
||||
echo "Waiting for new binary..."
|
||||
sleep 5
|
||||
echo "Sending QUIT signal to $pid"
|
||||
kill -QUIT $pid
|
||||
log_end_msg 0
|
||||
;;
|
||||
*) echo "pidofproc() failed"
|
||||
log_end_msg 1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
#reload|force-reload)
|
||||
#
|
||||
# If do_reload() is not implemented then leave this commented out
|
||||
# and leave 'force-reload' as an alias for 'restart'.
|
||||
#
|
||||
#log_daemon_msg "Reloading $DESC" "$NAME"
|
||||
#do_reload
|
||||
#log_end_msg $?
|
||||
#;;
|
||||
log_daemon_msg "Upgrading $DESC" "$NAME"
|
||||
oldpid=`pidofproc -p $PIDFILE $NAME`
|
||||
case "$?" in
|
||||
0)
|
||||
log_progress_msg "Sending SIGUSR2 to $oldpid..."
|
||||
kill -USR2 $oldpid
|
||||
log_progress_msg "Waiting for new binary..."
|
||||
for i in 1 2 3 4 5 ; do
|
||||
sleep 1
|
||||
newpid=`pidofproc -p $PIDFILE $NAME`
|
||||
if [ "$newpid" != "$oldpid" ] ; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [ "$newpid" != "$oldpid" ] ; then
|
||||
log_progress_msg "Sending SIGQUIT to $oldpid..."
|
||||
kill -QUIT $oldpid
|
||||
log_end_msg 0
|
||||
else
|
||||
log_progress_msg "New binary failed to start"
|
||||
log_end_msg 1
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
log_progress_msg "pidofproc() failed"
|
||||
log_end_msg 1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
restart|force-reload)
|
||||
#
|
||||
# If the "reload" option is implemented then remove the
|
||||
|
||||
@@ -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
|
||||
[ -s /var/run/nghttpx.pid ] && kill -USR1 `cat /var/run/nghttpx.pid` 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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,7 +24,6 @@
|
||||
man_MANS = nghttp.1 nghttpd.1 nghttpx.1 h2load.1
|
||||
|
||||
APIDOCS= \
|
||||
apiref.rst \
|
||||
macros.rst \
|
||||
enums.rst \
|
||||
types.rst \
|
||||
@@ -46,10 +45,11 @@ APIDOCS= \
|
||||
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_option_set_recv_client_preface.rst \
|
||||
nghttp2_pack_settings_payload.rst \
|
||||
nghttp2_priority_spec_check_default.rst \
|
||||
nghttp2_priority_spec_default_init.rst \
|
||||
@@ -79,6 +79,7 @@ APIDOCS= \
|
||||
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 \
|
||||
@@ -86,6 +87,7 @@ APIDOCS= \
|
||||
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 \
|
||||
@@ -107,8 +109,14 @@ APIDOCS= \
|
||||
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_altsvc.rst \
|
||||
nghttp2_submit_data.rst \
|
||||
nghttp2_submit_goaway.rst \
|
||||
nghttp2_submit_headers.rst \
|
||||
@@ -143,6 +151,9 @@ EXTRA_DIST = \
|
||||
sources/python-apiref.rst \
|
||||
sources/building-android-binary.rst \
|
||||
sources/contribute.rst \
|
||||
_exts/sphinxcontrib/LICENSE.rubydomain \
|
||||
_exts/sphinxcontrib/__init__.py \
|
||||
_exts/sphinxcontrib/rubydomain.py \
|
||||
_themes/sphinx_rtd_theme/__init__.py \
|
||||
_themes/sphinx_rtd_theme/breadcrumbs.html \
|
||||
_themes/sphinx_rtd_theme/footer.html \
|
||||
@@ -201,13 +212,17 @@ 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 \
|
||||
$@ macros.rst enums.rst types.rst . $^
|
||||
apiref.rst macros.rst enums.rst types.rst . $^
|
||||
|
||||
$(APIDOCS): apiref.rst
|
||||
|
||||
clean-local:
|
||||
-rm $(APIDOCS)
|
||||
-rm -f apiref.rst
|
||||
-rm -f $(APIDOCS)
|
||||
-rm -rf $(BUILDDIR)/*
|
||||
|
||||
html-local: apiref.rst
|
||||
|
||||
28
doc/_exts/sphinxcontrib/LICENSE.rubydomain
Normal file
28
doc/_exts/sphinxcontrib/LICENSE.rubydomain
Normal file
@@ -0,0 +1,28 @@
|
||||
If not otherwise noted, the extensions in this package are licensed
|
||||
under the following license.
|
||||
|
||||
Copyright (c) 2010 by the contributors (see AUTHORS file).
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
14
doc/_exts/sphinxcontrib/__init__.py
Normal file
14
doc/_exts/sphinxcontrib/__init__.py
Normal file
@@ -0,0 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
sphinxcontrib
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
This package is a namespace package that contains all extensions
|
||||
distributed in the ``sphinx-contrib`` distribution.
|
||||
|
||||
:copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
__import__('pkg_resources').declare_namespace(__name__)
|
||||
|
||||
695
doc/_exts/sphinxcontrib/rubydomain.py
Normal file
695
doc/_exts/sphinxcontrib/rubydomain.py
Normal file
@@ -0,0 +1,695 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
sphinx.domains.ruby
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The Ruby domain.
|
||||
|
||||
:copyright: Copyright 2010 by SHIBUKAWA Yoshiki
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.parsers.rst import directives
|
||||
|
||||
from sphinx import addnodes
|
||||
from sphinx.roles import XRefRole
|
||||
from sphinx.locale import l_, _
|
||||
from sphinx.domains import Domain, ObjType, Index
|
||||
from sphinx.directives import ObjectDescription
|
||||
from sphinx.util.nodes import make_refnode
|
||||
from sphinx.util.compat import Directive
|
||||
from sphinx.util.docfields import Field, GroupedField, TypedField
|
||||
|
||||
|
||||
# REs for Ruby signatures
|
||||
rb_sig_re = re.compile(
|
||||
r'''^ ([\w.]*\.)? # class name(s)
|
||||
(\$?\w+\??!?) \s* # thing name
|
||||
(?: \((.*)\) # optional: arguments
|
||||
(?:\s* -> \s* (.*))? # return annotation
|
||||
)? $ # and nothing more
|
||||
''', re.VERBOSE)
|
||||
|
||||
rb_paramlist_re = re.compile(r'([\[\],])') # split at '[', ']' and ','
|
||||
|
||||
separators = {
|
||||
'method':'#', 'attr_reader':'#', 'attr_writer':'#', 'attr_accessor':'#',
|
||||
'function':'.', 'classmethod':'.', 'class':'::', 'module':'::',
|
||||
'global':'', 'const':'::'}
|
||||
|
||||
rb_separator = re.compile(r"(?:\w+)?(?:::)?(?:\.)?(?:#)?")
|
||||
|
||||
|
||||
def _iteritems(d):
|
||||
|
||||
for k in d:
|
||||
yield k, d[k]
|
||||
|
||||
|
||||
def ruby_rsplit(fullname):
|
||||
items = [item for item in rb_separator.findall(fullname)]
|
||||
return ''.join(items[:-2]), items[-1]
|
||||
|
||||
|
||||
class RubyObject(ObjectDescription):
|
||||
"""
|
||||
Description of a general Ruby object.
|
||||
"""
|
||||
option_spec = {
|
||||
'noindex': directives.flag,
|
||||
'module': directives.unchanged,
|
||||
}
|
||||
|
||||
doc_field_types = [
|
||||
TypedField('parameter', label=l_('Parameters'),
|
||||
names=('param', 'parameter', 'arg', 'argument'),
|
||||
typerolename='obj', typenames=('paramtype', 'type')),
|
||||
TypedField('variable', label=l_('Variables'), rolename='obj',
|
||||
names=('var', 'ivar', 'cvar'),
|
||||
typerolename='obj', typenames=('vartype',)),
|
||||
GroupedField('exceptions', label=l_('Raises'), rolename='exc',
|
||||
names=('raises', 'raise', 'exception', 'except'),
|
||||
can_collapse=True),
|
||||
Field('returnvalue', label=l_('Returns'), has_arg=False,
|
||||
names=('returns', 'return')),
|
||||
Field('returntype', label=l_('Return type'), has_arg=False,
|
||||
names=('rtype',)),
|
||||
]
|
||||
|
||||
def get_signature_prefix(self, sig):
|
||||
"""
|
||||
May return a prefix to put before the object name in the signature.
|
||||
"""
|
||||
return ''
|
||||
|
||||
def needs_arglist(self):
|
||||
"""
|
||||
May return true if an empty argument list is to be generated even if
|
||||
the document contains none.
|
||||
"""
|
||||
return False
|
||||
|
||||
def handle_signature(self, sig, signode):
|
||||
"""
|
||||
Transform a Ruby signature into RST nodes.
|
||||
Returns (fully qualified name of the thing, classname if any).
|
||||
|
||||
If inside a class, the current class name is handled intelligently:
|
||||
* it is stripped from the displayed name if present
|
||||
* it is added to the full name (return value) if not present
|
||||
"""
|
||||
m = rb_sig_re.match(sig)
|
||||
if m is None:
|
||||
raise ValueError
|
||||
name_prefix, name, arglist, retann = m.groups()
|
||||
if not name_prefix:
|
||||
name_prefix = ""
|
||||
# determine module and class name (if applicable), as well as full name
|
||||
modname = self.options.get(
|
||||
'module', self.env.temp_data.get('rb:module'))
|
||||
classname = self.env.temp_data.get('rb:class')
|
||||
if self.objtype == 'global':
|
||||
add_module = False
|
||||
modname = None
|
||||
classname = None
|
||||
fullname = name
|
||||
elif classname:
|
||||
add_module = False
|
||||
if name_prefix and name_prefix.startswith(classname):
|
||||
fullname = name_prefix + name
|
||||
# class name is given again in the signature
|
||||
name_prefix = name_prefix[len(classname):].lstrip('.')
|
||||
else:
|
||||
separator = separators[self.objtype]
|
||||
fullname = classname + separator + name_prefix + name
|
||||
else:
|
||||
add_module = True
|
||||
if name_prefix:
|
||||
classname = name_prefix.rstrip('.')
|
||||
fullname = name_prefix + name
|
||||
else:
|
||||
classname = ''
|
||||
fullname = name
|
||||
|
||||
signode['module'] = modname
|
||||
signode['class'] = self.class_name = classname
|
||||
signode['fullname'] = fullname
|
||||
|
||||
sig_prefix = self.get_signature_prefix(sig)
|
||||
if sig_prefix:
|
||||
signode += addnodes.desc_annotation(sig_prefix, sig_prefix)
|
||||
|
||||
if name_prefix:
|
||||
signode += addnodes.desc_addname(name_prefix, name_prefix)
|
||||
# exceptions are a special case, since they are documented in the
|
||||
# 'exceptions' module.
|
||||
elif add_module and self.env.config.add_module_names:
|
||||
if self.objtype == 'global':
|
||||
nodetext = ''
|
||||
signode += addnodes.desc_addname(nodetext, nodetext)
|
||||
else:
|
||||
modname = self.options.get(
|
||||
'module', self.env.temp_data.get('rb:module'))
|
||||
if modname and modname != 'exceptions':
|
||||
nodetext = modname + separators[self.objtype]
|
||||
signode += addnodes.desc_addname(nodetext, nodetext)
|
||||
|
||||
signode += addnodes.desc_name(name, name)
|
||||
if not arglist:
|
||||
if self.needs_arglist():
|
||||
# for callables, add an empty parameter list
|
||||
signode += addnodes.desc_parameterlist()
|
||||
if retann:
|
||||
signode += addnodes.desc_returns(retann, retann)
|
||||
return fullname, name_prefix
|
||||
signode += addnodes.desc_parameterlist()
|
||||
|
||||
stack = [signode[-1]]
|
||||
for token in rb_paramlist_re.split(arglist):
|
||||
if token == '[':
|
||||
opt = addnodes.desc_optional()
|
||||
stack[-1] += opt
|
||||
stack.append(opt)
|
||||
elif token == ']':
|
||||
try:
|
||||
stack.pop()
|
||||
except IndexError:
|
||||
raise ValueError
|
||||
elif not token or token == ',' or token.isspace():
|
||||
pass
|
||||
else:
|
||||
token = token.strip()
|
||||
stack[-1] += addnodes.desc_parameter(token, token)
|
||||
if len(stack) != 1:
|
||||
raise ValueError
|
||||
if retann:
|
||||
signode += addnodes.desc_returns(retann, retann)
|
||||
return fullname, name_prefix
|
||||
|
||||
def get_index_text(self, modname, name):
|
||||
"""
|
||||
Return the text for the index entry of the object.
|
||||
"""
|
||||
raise NotImplementedError('must be implemented in subclasses')
|
||||
|
||||
def _is_class_member(self):
|
||||
return self.objtype.endswith('method') or self.objtype.startswith('attr')
|
||||
|
||||
def add_target_and_index(self, name_cls, sig, signode):
|
||||
if self.objtype == 'global':
|
||||
modname = ''
|
||||
else:
|
||||
modname = self.options.get(
|
||||
'module', self.env.temp_data.get('rb:module'))
|
||||
separator = separators[self.objtype]
|
||||
if self._is_class_member():
|
||||
if signode['class']:
|
||||
prefix = modname and modname + '::' or ''
|
||||
else:
|
||||
prefix = modname and modname + separator or ''
|
||||
else:
|
||||
prefix = modname and modname + separator or ''
|
||||
fullname = prefix + name_cls[0]
|
||||
# note target
|
||||
if fullname not in self.state.document.ids:
|
||||
signode['names'].append(fullname)
|
||||
signode['ids'].append(fullname)
|
||||
signode['first'] = (not self.names)
|
||||
self.state.document.note_explicit_target(signode)
|
||||
objects = self.env.domaindata['rb']['objects']
|
||||
if fullname in objects:
|
||||
self.env.warn(
|
||||
self.env.docname,
|
||||
'duplicate object description of %s, ' % fullname +
|
||||
'other instance in ' +
|
||||
self.env.doc2path(objects[fullname][0]),
|
||||
self.lineno)
|
||||
objects[fullname] = (self.env.docname, self.objtype)
|
||||
|
||||
indextext = self.get_index_text(modname, name_cls)
|
||||
if indextext:
|
||||
self.indexnode['entries'].append(('single', indextext,
|
||||
fullname, fullname))
|
||||
|
||||
def before_content(self):
|
||||
# needed for automatic qualification of members (reset in subclasses)
|
||||
self.clsname_set = False
|
||||
|
||||
def after_content(self):
|
||||
if self.clsname_set:
|
||||
self.env.temp_data['rb:class'] = None
|
||||
|
||||
|
||||
class RubyModulelevel(RubyObject):
|
||||
"""
|
||||
Description of an object on module level (functions, data).
|
||||
"""
|
||||
|
||||
def needs_arglist(self):
|
||||
return self.objtype == 'function'
|
||||
|
||||
def get_index_text(self, modname, name_cls):
|
||||
if self.objtype == 'function':
|
||||
if not modname:
|
||||
return _('%s() (global function)') % name_cls[0]
|
||||
return _('%s() (module function in %s)') % (name_cls[0], modname)
|
||||
else:
|
||||
return ''
|
||||
|
||||
|
||||
class RubyGloballevel(RubyObject):
|
||||
"""
|
||||
Description of an object on module level (functions, data).
|
||||
"""
|
||||
|
||||
def get_index_text(self, modname, name_cls):
|
||||
if self.objtype == 'global':
|
||||
return _('%s (global variable)') % name_cls[0]
|
||||
else:
|
||||
return ''
|
||||
|
||||
|
||||
class RubyEverywhere(RubyObject):
|
||||
"""
|
||||
Description of a class member (methods, attributes).
|
||||
"""
|
||||
|
||||
def needs_arglist(self):
|
||||
return self.objtype == 'method'
|
||||
|
||||
def get_index_text(self, modname, name_cls):
|
||||
name, cls = name_cls
|
||||
add_modules = self.env.config.add_module_names
|
||||
if self.objtype == 'method':
|
||||
try:
|
||||
clsname, methname = ruby_rsplit(name)
|
||||
except ValueError:
|
||||
if modname:
|
||||
return _('%s() (in module %s)') % (name, modname)
|
||||
else:
|
||||
return '%s()' % name
|
||||
if modname and add_modules:
|
||||
return _('%s() (%s::%s method)') % (methname, modname,
|
||||
clsname)
|
||||
else:
|
||||
return _('%s() (%s method)') % (methname, clsname)
|
||||
else:
|
||||
return ''
|
||||
|
||||
|
||||
class RubyClasslike(RubyObject):
|
||||
"""
|
||||
Description of a class-like object (classes, exceptions).
|
||||
"""
|
||||
|
||||
def get_signature_prefix(self, sig):
|
||||
return self.objtype + ' '
|
||||
|
||||
def get_index_text(self, modname, name_cls):
|
||||
if self.objtype == 'class':
|
||||
if not modname:
|
||||
return _('%s (class)') % name_cls[0]
|
||||
return _('%s (class in %s)') % (name_cls[0], modname)
|
||||
elif self.objtype == 'exception':
|
||||
return name_cls[0]
|
||||
else:
|
||||
return ''
|
||||
|
||||
def before_content(self):
|
||||
RubyObject.before_content(self)
|
||||
if self.names:
|
||||
self.env.temp_data['rb:class'] = self.names[0][0]
|
||||
self.clsname_set = True
|
||||
|
||||
|
||||
class RubyClassmember(RubyObject):
|
||||
"""
|
||||
Description of a class member (methods, attributes).
|
||||
"""
|
||||
|
||||
def needs_arglist(self):
|
||||
return self.objtype.endswith('method')
|
||||
|
||||
def get_signature_prefix(self, sig):
|
||||
if self.objtype == 'classmethod':
|
||||
return "classmethod %s." % self.class_name
|
||||
elif self.objtype == 'attr_reader':
|
||||
return "attribute [R] "
|
||||
elif self.objtype == 'attr_writer':
|
||||
return "attribute [W] "
|
||||
elif self.objtype == 'attr_accessor':
|
||||
return "attribute [R/W] "
|
||||
return ''
|
||||
|
||||
def get_index_text(self, modname, name_cls):
|
||||
name, cls = name_cls
|
||||
add_modules = self.env.config.add_module_names
|
||||
if self.objtype == 'classmethod':
|
||||
try:
|
||||
clsname, methname = ruby_rsplit(name)
|
||||
except ValueError:
|
||||
return '%s()' % name
|
||||
if modname:
|
||||
return _('%s() (%s.%s class method)') % (methname, modname,
|
||||
clsname)
|
||||
else:
|
||||
return _('%s() (%s class method)') % (methname, clsname)
|
||||
elif self.objtype.startswith('attr'):
|
||||
try:
|
||||
clsname, attrname = ruby_rsplit(name)
|
||||
except ValueError:
|
||||
return name
|
||||
if modname and add_modules:
|
||||
return _('%s (%s.%s attribute)') % (attrname, modname, clsname)
|
||||
else:
|
||||
return _('%s (%s attribute)') % (attrname, clsname)
|
||||
else:
|
||||
return ''
|
||||
|
||||
def before_content(self):
|
||||
RubyObject.before_content(self)
|
||||
lastname = self.names and self.names[-1][1]
|
||||
if lastname and not self.env.temp_data.get('rb:class'):
|
||||
self.env.temp_data['rb:class'] = lastname.strip('.')
|
||||
self.clsname_set = True
|
||||
|
||||
|
||||
class RubyModule(Directive):
|
||||
"""
|
||||
Directive to mark description of a new module.
|
||||
"""
|
||||
|
||||
has_content = False
|
||||
required_arguments = 1
|
||||
optional_arguments = 0
|
||||
final_argument_whitespace = False
|
||||
option_spec = {
|
||||
'platform': lambda x: x,
|
||||
'synopsis': lambda x: x,
|
||||
'noindex': directives.flag,
|
||||
'deprecated': directives.flag,
|
||||
}
|
||||
|
||||
def run(self):
|
||||
env = self.state.document.settings.env
|
||||
modname = self.arguments[0].strip()
|
||||
noindex = 'noindex' in self.options
|
||||
env.temp_data['rb:module'] = modname
|
||||
env.domaindata['rb']['modules'][modname] = \
|
||||
(env.docname, self.options.get('synopsis', ''),
|
||||
self.options.get('platform', ''), 'deprecated' in self.options)
|
||||
targetnode = nodes.target('', '', ids=['module-' + modname], ismod=True)
|
||||
self.state.document.note_explicit_target(targetnode)
|
||||
ret = [targetnode]
|
||||
# XXX this behavior of the module directive is a mess...
|
||||
if 'platform' in self.options:
|
||||
platform = self.options['platform']
|
||||
node = nodes.paragraph()
|
||||
node += nodes.emphasis('', _('Platforms: '))
|
||||
node += nodes.Text(platform, platform)
|
||||
ret.append(node)
|
||||
# the synopsis isn't printed; in fact, it is only used in the
|
||||
# modindex currently
|
||||
if not noindex:
|
||||
indextext = _('%s (module)') % modname
|
||||
inode = addnodes.index(entries=[('single', indextext,
|
||||
'module-' + modname, modname)])
|
||||
ret.append(inode)
|
||||
return ret
|
||||
|
||||
|
||||
class RubyCurrentModule(Directive):
|
||||
"""
|
||||
This directive is just to tell Sphinx that we're documenting
|
||||
stuff in module foo, but links to module foo won't lead here.
|
||||
"""
|
||||
|
||||
has_content = False
|
||||
required_arguments = 1
|
||||
optional_arguments = 0
|
||||
final_argument_whitespace = False
|
||||
option_spec = {}
|
||||
|
||||
def run(self):
|
||||
env = self.state.document.settings.env
|
||||
modname = self.arguments[0].strip()
|
||||
if modname == 'None':
|
||||
env.temp_data['rb:module'] = None
|
||||
else:
|
||||
env.temp_data['rb:module'] = modname
|
||||
return []
|
||||
|
||||
|
||||
class RubyXRefRole(XRefRole):
|
||||
def process_link(self, env, refnode, has_explicit_title, title, target):
|
||||
if not has_explicit_title:
|
||||
title = title.lstrip('.') # only has a meaning for the target
|
||||
title = title.lstrip('#')
|
||||
if title.startswith("::"):
|
||||
title = title[2:]
|
||||
target = target.lstrip('~') # only has a meaning for the title
|
||||
# if the first character is a tilde, don't display the module/class
|
||||
# parts of the contents
|
||||
if title[0:1] == '~':
|
||||
m = re.search(r"(?:\.)?(?:#)?(?:::)?(.*)\Z", title)
|
||||
if m:
|
||||
title = m.group(1)
|
||||
if not title.startswith("$"):
|
||||
refnode['rb:module'] = env.temp_data.get('rb:module')
|
||||
refnode['rb:class'] = env.temp_data.get('rb:class')
|
||||
# if the first character is a dot, search more specific namespaces first
|
||||
# else search builtins first
|
||||
if target[0:1] == '.':
|
||||
target = target[1:]
|
||||
refnode['refspecific'] = True
|
||||
return title, target
|
||||
|
||||
|
||||
class RubyModuleIndex(Index):
|
||||
"""
|
||||
Index subclass to provide the Ruby module index.
|
||||
"""
|
||||
|
||||
name = 'modindex'
|
||||
localname = l_('Ruby Module Index')
|
||||
shortname = l_('modules')
|
||||
|
||||
def generate(self, docnames=None):
|
||||
content = {}
|
||||
# list of prefixes to ignore
|
||||
ignores = self.domain.env.config['modindex_common_prefix']
|
||||
ignores = sorted(ignores, key=len, reverse=True)
|
||||
# list of all modules, sorted by module name
|
||||
modules = sorted(_iteritems(self.domain.data['modules']),
|
||||
key=lambda x: x[0].lower())
|
||||
# sort out collapsable modules
|
||||
prev_modname = ''
|
||||
num_toplevels = 0
|
||||
for modname, (docname, synopsis, platforms, deprecated) in modules:
|
||||
if docnames and docname not in docnames:
|
||||
continue
|
||||
|
||||
for ignore in ignores:
|
||||
if modname.startswith(ignore):
|
||||
modname = modname[len(ignore):]
|
||||
stripped = ignore
|
||||
break
|
||||
else:
|
||||
stripped = ''
|
||||
|
||||
# we stripped the whole module name?
|
||||
if not modname:
|
||||
modname, stripped = stripped, ''
|
||||
|
||||
entries = content.setdefault(modname[0].lower(), [])
|
||||
|
||||
package = modname.split('::')[0]
|
||||
if package != modname:
|
||||
# it's a submodule
|
||||
if prev_modname == package:
|
||||
# first submodule - make parent a group head
|
||||
entries[-1][1] = 1
|
||||
elif not prev_modname.startswith(package):
|
||||
# submodule without parent in list, add dummy entry
|
||||
entries.append([stripped + package, 1, '', '', '', '', ''])
|
||||
subtype = 2
|
||||
else:
|
||||
num_toplevels += 1
|
||||
subtype = 0
|
||||
|
||||
qualifier = deprecated and _('Deprecated') or ''
|
||||
entries.append([stripped + modname, subtype, docname,
|
||||
'module-' + stripped + modname, platforms,
|
||||
qualifier, synopsis])
|
||||
prev_modname = modname
|
||||
|
||||
# apply heuristics when to collapse modindex at page load:
|
||||
# only collapse if number of toplevel modules is larger than
|
||||
# number of submodules
|
||||
collapse = len(modules) - num_toplevels < num_toplevels
|
||||
|
||||
# sort by first letter
|
||||
content = sorted(_iteritems(content))
|
||||
|
||||
return content, collapse
|
||||
|
||||
|
||||
class RubyDomain(Domain):
|
||||
"""Ruby language domain."""
|
||||
name = 'rb'
|
||||
label = 'Ruby'
|
||||
object_types = {
|
||||
'function': ObjType(l_('function'), 'func', 'obj'),
|
||||
'global': ObjType(l_('global variable'), 'global', 'obj'),
|
||||
'method': ObjType(l_('method'), 'meth', 'obj'),
|
||||
'class': ObjType(l_('class'), 'class', 'obj'),
|
||||
'exception': ObjType(l_('exception'), 'exc', 'obj'),
|
||||
'classmethod': ObjType(l_('class method'), 'meth', 'obj'),
|
||||
'attr_reader': ObjType(l_('attribute'), 'attr', 'obj'),
|
||||
'attr_writer': ObjType(l_('attribute'), 'attr', 'obj'),
|
||||
'attr_accessor': ObjType(l_('attribute'), 'attr', 'obj'),
|
||||
'const': ObjType(l_('const'), 'const', 'obj'),
|
||||
'module': ObjType(l_('module'), 'mod', 'obj'),
|
||||
}
|
||||
|
||||
directives = {
|
||||
'function': RubyModulelevel,
|
||||
'global': RubyGloballevel,
|
||||
'method': RubyEverywhere,
|
||||
'const': RubyEverywhere,
|
||||
'class': RubyClasslike,
|
||||
'exception': RubyClasslike,
|
||||
'classmethod': RubyClassmember,
|
||||
'attr_reader': RubyClassmember,
|
||||
'attr_writer': RubyClassmember,
|
||||
'attr_accessor': RubyClassmember,
|
||||
'module': RubyModule,
|
||||
'currentmodule': RubyCurrentModule,
|
||||
}
|
||||
|
||||
roles = {
|
||||
'func': RubyXRefRole(fix_parens=False),
|
||||
'global':RubyXRefRole(),
|
||||
'class': RubyXRefRole(),
|
||||
'exc': RubyXRefRole(),
|
||||
'meth': RubyXRefRole(fix_parens=False),
|
||||
'attr': RubyXRefRole(),
|
||||
'const': RubyXRefRole(),
|
||||
'mod': RubyXRefRole(),
|
||||
'obj': RubyXRefRole(),
|
||||
}
|
||||
initial_data = {
|
||||
'objects': {}, # fullname -> docname, objtype
|
||||
'modules': {}, # modname -> docname, synopsis, platform, deprecated
|
||||
}
|
||||
indices = [
|
||||
RubyModuleIndex,
|
||||
]
|
||||
|
||||
def clear_doc(self, docname):
|
||||
for fullname, (fn, _) in list(self.data['objects'].items()):
|
||||
if fn == docname:
|
||||
del self.data['objects'][fullname]
|
||||
for modname, (fn, _, _, _) in list(self.data['modules'].items()):
|
||||
if fn == docname:
|
||||
del self.data['modules'][modname]
|
||||
|
||||
def find_obj(self, env, modname, classname, name, type, searchorder=0):
|
||||
"""
|
||||
Find a Ruby object for "name", perhaps using the given module and/or
|
||||
classname.
|
||||
"""
|
||||
# skip parens
|
||||
if name[-2:] == '()':
|
||||
name = name[:-2]
|
||||
|
||||
if not name:
|
||||
return None, None
|
||||
|
||||
objects = self.data['objects']
|
||||
|
||||
newname = None
|
||||
if searchorder == 1:
|
||||
if modname and classname and \
|
||||
modname + '::' + classname + '#' + name in objects:
|
||||
newname = modname + '::' + classname + '#' + name
|
||||
elif modname and classname and \
|
||||
modname + '::' + classname + '.' + name in objects:
|
||||
newname = modname + '::' + classname + '.' + name
|
||||
elif modname and modname + '::' + name in objects:
|
||||
newname = modname + '::' + name
|
||||
elif modname and modname + '#' + name in objects:
|
||||
newname = modname + '#' + name
|
||||
elif modname and modname + '.' + name in objects:
|
||||
newname = modname + '.' + name
|
||||
elif classname and classname + '.' + name in objects:
|
||||
newname = classname + '.' + name
|
||||
elif classname and classname + '#' + name in objects:
|
||||
newname = classname + '#' + name
|
||||
elif name in objects:
|
||||
newname = name
|
||||
else:
|
||||
if name in objects:
|
||||
newname = name
|
||||
elif classname and classname + '.' + name in objects:
|
||||
newname = classname + '.' + name
|
||||
elif classname and classname + '#' + name in objects:
|
||||
newname = classname + '#' + name
|
||||
elif modname and modname + '::' + name in objects:
|
||||
newname = modname + '::' + name
|
||||
elif modname and modname + '#' + name in objects:
|
||||
newname = modname + '#' + name
|
||||
elif modname and modname + '.' + name in objects:
|
||||
newname = modname + '.' + name
|
||||
elif modname and classname and \
|
||||
modname + '::' + classname + '#' + name in objects:
|
||||
newname = modname + '::' + classname + '#' + name
|
||||
elif modname and classname and \
|
||||
modname + '::' + classname + '.' + name in objects:
|
||||
newname = modname + '::' + classname + '.' + name
|
||||
# special case: object methods
|
||||
elif type in ('func', 'meth') and '.' not in name and \
|
||||
'object.' + name in objects:
|
||||
newname = 'object.' + name
|
||||
if newname is None:
|
||||
return None, None
|
||||
return newname, objects[newname]
|
||||
|
||||
def resolve_xref(self, env, fromdocname, builder,
|
||||
typ, target, node, contnode):
|
||||
if (typ == 'mod' or
|
||||
typ == 'obj' and target in self.data['modules']):
|
||||
docname, synopsis, platform, deprecated = \
|
||||
self.data['modules'].get(target, ('','','', ''))
|
||||
if not docname:
|
||||
return None
|
||||
else:
|
||||
title = '%s%s%s' % ((platform and '(%s) ' % platform),
|
||||
synopsis,
|
||||
(deprecated and ' (deprecated)' or ''))
|
||||
return make_refnode(builder, fromdocname, docname,
|
||||
'module-' + target, contnode, title)
|
||||
else:
|
||||
modname = node.get('rb:module')
|
||||
clsname = node.get('rb:class')
|
||||
searchorder = node.hasattr('refspecific') and 1 or 0
|
||||
name, obj = self.find_obj(env, modname, clsname,
|
||||
target, typ, searchorder)
|
||||
if not obj:
|
||||
return None
|
||||
else:
|
||||
return make_refnode(builder, fromdocname, obj[0], name,
|
||||
contnode, name)
|
||||
|
||||
def get_objects(self):
|
||||
for modname, info in _iteritems(self.data['modules']):
|
||||
yield (modname, modname, 'module', info[0], 'module-' + modname, 0)
|
||||
for refname, (docname, type) in _iteritems(self.data['objects']):
|
||||
yield (refname, refname, type, docname, refname, 1)
|
||||
|
||||
|
||||
def setup(app):
|
||||
app.add_domain(RubyDomain)
|
||||
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, 7)
|
||||
VERSION = (0, 1, 8)
|
||||
|
||||
__version__ = ".".join(str(v) for v in VERSION)
|
||||
__version_full__ = __version__
|
||||
|
||||
18
doc/_themes/sphinx_rtd_theme/breadcrumbs.html
vendored
18
doc/_themes/sphinx_rtd_theme/breadcrumbs.html
vendored
@@ -6,14 +6,16 @@
|
||||
{% endfor %}
|
||||
<li>{{ title }}</li>
|
||||
<li class="wy-breadcrumbs-aside">
|
||||
{% 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>
|
||||
{% 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>
|
||||
|
||||
4
doc/_themes/sphinx_rtd_theme/footer.html
vendored
4
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 %}
|
||||
|
||||
2
doc/_themes/sphinx_rtd_theme/layout.html
vendored
2
doc/_themes/sphinx_rtd_theme/layout.html
vendored
@@ -107,7 +107,7 @@
|
||||
|
||||
<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 %}
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
101
doc/_themes/sphinx_rtd_theme/static/js/theme.js
vendored
101
doc/_themes/sphinx_rtd_theme/static/js/theme.js
vendored
@@ -1,50 +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',
|
||||
winScroll = false,
|
||||
linkScroll = false,
|
||||
winPosition = 0,
|
||||
enable = function () {
|
||||
navBar.addClass(stickyNavCssClass);
|
||||
win.on('scroll', function() { // set flag on scroll event
|
||||
winScroll = true;
|
||||
init();
|
||||
reset();
|
||||
win.on('hashchange', reset);
|
||||
|
||||
// Set scrolling
|
||||
win.on('scroll', function () {
|
||||
if (!linkScroll) {
|
||||
winScroll = true;
|
||||
}
|
||||
});
|
||||
// use setInterval to only handle a subset of scroll events so we don't kill scroll performance
|
||||
setInterval(function() {
|
||||
setInterval(function () {
|
||||
if (winScroll) {
|
||||
winScroll = false;
|
||||
navBar.scrollTop(win.scrollTop());
|
||||
var newWinPosition = win.scrollTop(),
|
||||
navPosition = navBar.scrollTop(),
|
||||
newNavPosition = navPosition + (newWinPosition - winPosition);
|
||||
navBar.scrollTop(newNavPosition);
|
||||
winPosition = newWinPosition;
|
||||
}
|
||||
}, 100);
|
||||
}, 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
|
||||
};
|
||||
}($));
|
||||
|
||||
@@ -8,7 +8,7 @@ _h2load()
|
||||
_get_comp_words_by_ref cur prev
|
||||
case $cur in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W '--threads --connection-window-bits --input-file --help --requests --data --verbose --version --window-bits --clients --no-tls-proto --header --max-concurrent-streams ' -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W '--threads --connection-window-bits --input-file --npn-list --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
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from __future__ import print_function
|
||||
import subprocess
|
||||
from StringIO import StringIO
|
||||
import io
|
||||
import re
|
||||
import sys
|
||||
import os.path
|
||||
@@ -14,10 +17,10 @@ class Option:
|
||||
def get_all_options(cmd):
|
||||
opt_pattern = re.compile(r' (?:(-.), )?(--[^\s\[=]+)(\[)?')
|
||||
proc = subprocess.Popen([cmd, "--help"], stdout=subprocess.PIPE)
|
||||
stdoutdata, stderrdata = proc.communicate()
|
||||
stdoutdata, _ = proc.communicate()
|
||||
cur_option = None
|
||||
opts = {}
|
||||
for line in StringIO(stdoutdata):
|
||||
for line in io.StringIO(stdoutdata.decode('utf-8')):
|
||||
match = opt_pattern.match(line)
|
||||
if not match:
|
||||
continue
|
||||
@@ -45,7 +48,7 @@ _{name}()
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W '\
|
||||
''')
|
||||
for opt in opts.itervalues():
|
||||
for opt in opts.values():
|
||||
out.write(opt.long_opt)
|
||||
out.write(' ')
|
||||
|
||||
@@ -66,8 +69,8 @@ complete -F _{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"
|
||||
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])
|
||||
|
||||
@@ -8,7 +8,7 @@ _nghttp()
|
||||
_get_comp_words_by_ref cur prev
|
||||
case $cur in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W '--no-push --verbose --no-dep --get-assets --har --header-table-size --multiply --padding --hexdump --continuation --connection-window-bits --peer-max-concurrent-streams --timeout --data --no-content-length --version --color --cert --upgrade --remote-name --trailer --weight --help --key --null-out --window-bits --stat --header ' -- "$cur" ) )
|
||||
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
|
||||
|
||||
@@ -8,7 +8,7 @@ _nghttpd()
|
||||
_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 --daemon --verify-client --workers --no-tls --color --early-response --dh-param-file ' -- "$cur" ) )
|
||||
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
|
||||
|
||||
@@ -8,7 +8,7 @@ _nghttpx()
|
||||
_get_comp_words_by_ref cur prev
|
||||
case $cur in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W '--frontend-http2-connection-window-bits --worker-read-rate --frontend-no-tls --frontend-http2-dump-request-header --daemon --write-rate --altsvc --frontend-http2-dump-response-header --backend-http1-connections-per-frontend --tls-ticket-key-file --ciphers --verify-client-cacert --backend-keep-alive-timeout --strip-incoming-x-forwarded-for --errorlog-file --private-key-passwd-file --version --backlog --backend-http-proxy-uri --add-response-header --backend-write-timeout --backend-request-buffer --add-x-forwarded-for --write-burst --backend-http2-connection-window-bits --insecure --rlimit-nofile --backend-http2-window-bits --tls-proto-list --no-location-rewrite --padding --conf --accesslog-syslog --backend-http2-connections-per-worker --http2-max-concurrent-streams --client-proxy --worker-frontend-connections --ocsp-update-interval --cacert --frontend-read-timeout --worker-write-burst --npn-list --syslog-facility --backend-http1-connections-per-host --no-server-push --client --http2-bridge --fetch-ocsp-response-file --no-via --user --stream-write-timeout --no-ocsp --backend-response-buffer --http2-no-cookie-crumbling --backend-read-timeout --stream-read-timeout --workers --worker-read-burst --dh-param-file --errorlog-syslog --frontend --accesslog-file --http2-proxy --frontend-http2-read-timeout --accesslog-format --frontend-http2-window-bits --backend-no-tls --client-private-key-file --pid-file --client-cert-file --no-host-rewrite --log-level --worker-write-rate --help --backend-tls-sni-field --subcert --frontend-frame-debug --frontend-write-timeout --verify-client --read-rate --read-burst --backend-ipv4 --listener-disable-timeout --backend-ipv6 --backend ' -- "$cur" ) )
|
||||
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 --response-phase-file --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 --request-phase-file --backend-read-timeout --frontend --accesslog-file --http2-proxy --max-header-fields --backend-no-tls --client-private-key-file --client-cert-file --accept-proxy-protocol --add-response-header --read-rate ' -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
_filedir
|
||||
|
||||
@@ -41,6 +41,8 @@ import sys, os
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
sys.path.append(os.path.abspath('_exts'))
|
||||
|
||||
# -- General configuration -----------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
@@ -48,7 +50,7 @@ import sys, os
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = []
|
||||
extensions = ['sphinxcontrib.rubydomain']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['@top_srcdir@/_templates']
|
||||
|
||||
187
doc/h2load.1
187
doc/h2load.1
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "H2LOAD" "1" "April 19, 2015" "0.7.12" "nghttp2"
|
||||
.TH "H2LOAD" "1" "September 25, 2015" "1.3.4" "nghttp2"
|
||||
.SH NAME
|
||||
h2load \- HTTP/2 benchmarking tool
|
||||
.
|
||||
@@ -44,7 +44,8 @@ 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
|
||||
.INDENT 0.0
|
||||
@@ -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,21 +115,113 @@ Add/Override a header to the requests.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-p, \-\-no\-tls\-proto=<PROTOID>
|
||||
Specify ALPN identifier of the protocol to be used when
|
||||
accessing http URI without SSL/TLS.
|
||||
Available protocols: spdy/2, spdy/3, spdy/3.1 and h2c\-14
|
||||
.sp
|
||||
Default: \fBh2c\-14\fP
|
||||
.B \-\-ciphers=<SUITE>
|
||||
Set allowed cipher list. The format of the string is
|
||||
described in OpenSSL ciphers(1).
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-d, \-\-data=<FILE>
|
||||
.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, h2c and
|
||||
http/1.1
|
||||
.sp
|
||||
Default: \fBh2c\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-d, \-\-data=<PATH>
|
||||
Post FILE to server. The request method is changed to
|
||||
POST.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-r, \-\-rate=<N>
|
||||
Specifies the fixed rate at which connections are
|
||||
created. The rate must be a positive integer,
|
||||
representing the number of connections to be made per
|
||||
second. When the rate is 0, the program will run as it
|
||||
normally does, creating connections at whatever variable
|
||||
rate it wants. The default value for this option is 0.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-C, \-\-num\-conns=<N>
|
||||
Specifies the total number of connections to create.
|
||||
The total number of connections must be a positive
|
||||
integer. On each connection, \fI\%\-m\fP requests are made. The
|
||||
test stops once as soon as the <N> connections have
|
||||
either completed or failed. When the number of
|
||||
connections is 0, the program will run as it normally
|
||||
does, creating as many connections as it needs in order
|
||||
to make the \fI\%\-n\fP requests specified. The default value
|
||||
for this option is 0. The \fI\%\-n\fP option is not required if
|
||||
the \fI\%\-C\fP option is being used.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-T, \-\-connection\-active\-timeout=<N>
|
||||
Specifies the maximum time that h2load is willing to
|
||||
keep a connection open, regardless of the activity on
|
||||
said connection. <N> must be a positive integer,
|
||||
specifying the number of seconds to wait. When no
|
||||
timeout value is set (either active or inactive), h2load
|
||||
will keep a connection open indefinitely, waiting for a
|
||||
response.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-N, \-\-connection\-inactivity\-timeout=<N>
|
||||
Specifies the amount of time that h2load is willing to
|
||||
wait to see activity on a given connection. <N> must be
|
||||
a positive integer, specifying the number of seconds to
|
||||
wait. When no timeout value is set (either active or
|
||||
inactive), h2load will keep a connection open
|
||||
indefinitely, waiting for a response.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-timing\-script\-file=<PATH>
|
||||
Path of a file containing one or more lines separated by
|
||||
EOLs. Each script line is composed of two tab\-separated
|
||||
fields. The first field represents the time offset from
|
||||
the start of execution, expressed as a positive value of
|
||||
milliseconds with microsecond resolution. The second
|
||||
field represents the URI. This option will disable URIs
|
||||
getting from command\-line. If \(aq\-\(aq is given as <PATH>,
|
||||
script lines will be read from stdin. Script lines are
|
||||
used in order for each client. If \fI\%\-n\fP is given, it must be
|
||||
less than or equal to the number of script lines, larger
|
||||
values are clamped to the number of script lines. If \fI\%\-n\fP
|
||||
is not given, the number of requests will default to the
|
||||
number of script lines. The scheme, host and port defined
|
||||
in the first URI are used solely. Values contained in
|
||||
other URIs, if present, are ignored. Definition of a
|
||||
base URI overrides all scheme, host or port values.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-B, \-\-base\-uri=<URI>
|
||||
Specify URI from which the scheme, host and port will be
|
||||
used for all requests. The base URI overrides all
|
||||
values defined either at the command line or inside
|
||||
input files.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-npn\-list=<LIST>
|
||||
Comma delimited list of ALPN protocol identifier sorted
|
||||
in the order of preference. That means most desirable
|
||||
protocol comes first. This is used in both ALPN and
|
||||
NPN. The parameter must be delimited by a single comma
|
||||
only and any white spaces are treated as a part of
|
||||
protocol string.
|
||||
.sp
|
||||
Default: \fBh2,h2\-16,h2\-14,spdy/3.1,spdy/3,spdy/2,http/1.1\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-v, \-\-verbose
|
||||
Output debug information.
|
||||
.UNINDENT
|
||||
@@ -162,9 +260,14 @@ The number of requests failed, including HTTP level failures
|
||||
.TP
|
||||
.B errored
|
||||
The number of requests failed, except for HTTP level failures.
|
||||
status code. This is the subset of the number reported in
|
||||
\fBfailed\fP and most likely the network level failures or stream
|
||||
was reset by RST_STREAM.
|
||||
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
|
||||
@@ -201,13 +304,63 @@ The maximum time taken for request and response.
|
||||
The mean time taken for request and response.
|
||||
.TP
|
||||
.B sd
|
||||
The standard deviation of the time for request and response.
|
||||
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
|
||||
|
||||
158
doc/h2load.1.rst
158
doc/h2load.1.rst
@@ -1,4 +1,6 @@
|
||||
|
||||
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
|
||||
|
||||
.. program:: h2load
|
||||
|
||||
h2load(1)
|
||||
@@ -21,7 +23,8 @@ 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
|
||||
-------
|
||||
@@ -44,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>)
|
||||
|
||||
@@ -67,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
|
||||
@@ -74,23 +80,109 @@ 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, h2c and
|
||||
http/1.1
|
||||
|
||||
Default: ``h2c-14``
|
||||
Default: ``h2c``
|
||||
|
||||
.. option:: -d, --data=<FILE>
|
||||
.. 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 a positive value of
|
||||
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:: --npn-list=<LIST>
|
||||
|
||||
Comma delimited list of ALPN protocol identifier sorted
|
||||
in the order of preference. That means most desirable
|
||||
protocol comes first. This is used in both ALPN and
|
||||
NPN. The parameter must be delimited by a single comma
|
||||
only and any white spaces are treated as a part of
|
||||
protocol string.
|
||||
|
||||
Default: ``h2,h2-16,h2-14,spdy/3.1,spdy/3,spdy/2,http/1.1``
|
||||
|
||||
.. option:: -v, --verbose
|
||||
|
||||
Output debug information.
|
||||
@@ -121,9 +213,13 @@ requests
|
||||
(non-successful HTTP status code).
|
||||
errored
|
||||
The number of requests failed, except for HTTP level failures.
|
||||
status code. This is the subset of the number reported in
|
||||
``failed`` and most likely the network level failures or stream
|
||||
was reset by RST_STREAM.
|
||||
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.
|
||||
@@ -149,11 +245,49 @@ time for request
|
||||
mean
|
||||
The mean time taken for request and response.
|
||||
sd
|
||||
The standard deviation of the time for request and response.
|
||||
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
|
||||
--------
|
||||
|
||||
|
||||
@@ -16,9 +16,13 @@ requests
|
||||
(non-successful HTTP status code).
|
||||
errored
|
||||
The number of requests failed, except for HTTP level failures.
|
||||
status code. This is the subset of the number reported in
|
||||
``failed`` and most likely the network level failures or stream
|
||||
was reset by RST_STREAM.
|
||||
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.
|
||||
@@ -44,11 +48,49 @@ time for request
|
||||
mean
|
||||
The mean time taken for request and response.
|
||||
sd
|
||||
The standard deviation of the time for request and response.
|
||||
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,4 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# nghttp2 - HTTP/2 C Library
|
||||
|
||||
# Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
@@ -23,6 +24,8 @@
|
||||
# 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, os.path
|
||||
|
||||
@@ -222,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):
|
||||
|
||||
24
doc/nghttp.1
24
doc/nghttp.1
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTP" "1" "April 19, 2015" "0.7.12" "nghttp2"
|
||||
.TH "NGHTTP" "1" "September 25, 2015" "1.3.4" "nghttp2"
|
||||
.SH NAME
|
||||
nghttp \- HTTP/2 experimental client
|
||||
.
|
||||
@@ -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
|
||||
@@ -121,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
|
||||
@@ -166,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
|
||||
@@ -204,6 +205,12 @@ Disable server push.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-max\-concurrent\-streams=<N>
|
||||
The number of concurrent pushed streams this client
|
||||
accepts.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-version
|
||||
Display version information and exit.
|
||||
.UNINDENT
|
||||
@@ -215,6 +222,11 @@ 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
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
|
||||
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
|
||||
|
||||
.. program:: nghttp
|
||||
|
||||
nghttp(1)
|
||||
@@ -38,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>
|
||||
|
||||
@@ -86,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.
|
||||
@@ -124,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
|
||||
@@ -155,6 +158,11 @@ OPTIONS
|
||||
|
||||
Disable server push.
|
||||
|
||||
.. option:: --max-concurrent-streams=<N>
|
||||
|
||||
The number of concurrent pushed streams this client
|
||||
accepts.
|
||||
|
||||
.. option:: --version
|
||||
|
||||
Display version information and exit.
|
||||
@@ -168,6 +176,11 @@ OPTIONS
|
||||
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
|
||||
-------------------------
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTPD" "1" "April 19, 2015" "0.7.12" "nghttp2"
|
||||
.TH "NGHTTPD" "1" "September 25, 2015" "1.3.4" "nghttp2"
|
||||
.SH NAME
|
||||
nghttpd \- HTTP/2 experimental server
|
||||
.
|
||||
@@ -119,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
|
||||
@@ -159,6 +167,11 @@ 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,6 @@
|
||||
|
||||
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
|
||||
|
||||
.. program:: nghttpd
|
||||
|
||||
nghttpd(1)
|
||||
@@ -85,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.
|
||||
@@ -119,6 +128,10 @@ OPTIONS
|
||||
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.
|
||||
|
||||
618
doc/nghttpx.1
618
doc/nghttpx.1
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTPX" "1" "April 19, 2015" "0.7.12" "nghttp2"
|
||||
.TH "NGHTTPX" "1" "September 25, 2015" "1.3.4" "nghttp2"
|
||||
.SH NAME
|
||||
nghttpx \- HTTP/2 experimental proxy
|
||||
.
|
||||
@@ -55,17 +55,69 @@ The options are categorized into several groups.
|
||||
.SS Connections
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-b, \-\-backend=<HOST,PORT>
|
||||
.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)
|
||||
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.
|
||||
UNIX domain socket can be specified by prefixing path
|
||||
@@ -106,6 +158,11 @@ timeouts when connecting and making CONNECT request can
|
||||
be specified by \fI\%\-\-backend\-read\-timeout\fP and
|
||||
\fI\%\-\-backend\-write\-timeout\fP options.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-accept\-proxy\-protocol
|
||||
Accept PROXY protocol version 1 on frontend connection.
|
||||
.UNINDENT
|
||||
.SS Performance
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -195,17 +252,24 @@ Default: \fB0\fP
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-backend\-http2\-connections\-per\-worker=<N>
|
||||
Set maximum number of HTTP/2 connections per worker.
|
||||
The default value is 0, which means the number of
|
||||
backend addresses specified by \fI\%\-b\fP option.
|
||||
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
|
||||
@@ -416,22 +480,75 @@ 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\-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
|
||||
.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
|
||||
@@ -453,6 +570,13 @@ Default: \fB4h\fP
|
||||
.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
|
||||
@@ -624,8 +748,20 @@ $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
|
||||
@@ -679,8 +815,8 @@ altered regardless of this option.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-no\-host\-rewrite
|
||||
Don\(aqt rewrite host and :authority header fields on
|
||||
.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.
|
||||
@@ -690,10 +826,19 @@ will not be altered regardless of this option.
|
||||
.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
|
||||
@@ -704,6 +849,23 @@ 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
|
||||
.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
|
||||
@@ -748,6 +910,21 @@ 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 Scripting
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-request\-phase\-file=<PATH>
|
||||
Set mruby script file which will be executed when
|
||||
request header fields are completely received from
|
||||
frontend. This hook is called request phase hook.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-response\-phase\-file=<PATH>
|
||||
Set mruby script file which will be executed when
|
||||
response header fields are completely received from
|
||||
backend. This hook is called response phase hook.
|
||||
.UNINDENT
|
||||
.SS Misc
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -758,6 +935,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
|
||||
@@ -795,7 +980,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
|
||||
@@ -818,6 +1003,21 @@ 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
|
||||
.sp
|
||||
\fBNOTE:\fP
|
||||
.INDENT 0.0
|
||||
.INDENT 3.5
|
||||
nghttpx consists of 2 processes: one process for processing these
|
||||
signals, and another one for processing requests. The former spawns
|
||||
the latter. The former is called master process, and the latter is
|
||||
called worker process. The above signal must be sent to the master
|
||||
process. If the worker process receives one of them, it is ignored.
|
||||
This behaviour of worker process may change in the future release.
|
||||
In other words, in the future release, worker process may terminate
|
||||
upon the reception of these signals. Therefore these signals should
|
||||
not be sent to the worker process.
|
||||
.UNINDENT
|
||||
.UNINDENT
|
||||
.SH SERVER PUSH
|
||||
.sp
|
||||
nghttpx supports HTTP/2 server push in default mode. nghttpx looks
|
||||
@@ -838,19 +1038,14 @@ Link: </css/theme.css>; rel=preload
|
||||
.UNINDENT
|
||||
.UNINDENT
|
||||
.sp
|
||||
Currently, the following restrictions are applied for server push:
|
||||
Currently, the following restriction is 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.
|
||||
This limitation may be loosened in the future release.
|
||||
.SH UNIX DOMAIN SOCKET
|
||||
.sp
|
||||
nghttpx supports UNIX domain socket with a filename for both frontend
|
||||
@@ -864,13 +1059,350 @@ 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 perl script \fBfetch\-ocsp\-response\fP,
|
||||
which has been developed as part of h2o project
|
||||
(\fI\%https://github.com/h2o/h2o\fP).
|
||||
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 MRUBY SCRIPTING
|
||||
.sp
|
||||
\fBWARNING:\fP
|
||||
.INDENT 0.0
|
||||
.INDENT 3.5
|
||||
The current mruby extension API is experimental and not frozen. The
|
||||
API is subject to change in the future release.
|
||||
.UNINDENT
|
||||
.UNINDENT
|
||||
.sp
|
||||
nghttpx allows users to extend its capability using mruby scripts.
|
||||
nghttpx has 2 hook points to execute mruby script: request phase and
|
||||
response phase. The request phase hook is invoked after all request
|
||||
header fields are received from client. The response phase hook is
|
||||
invoked after all response header fields are received from backend
|
||||
server. These hooks allows users to modify header fields, or common
|
||||
HTTP variables, like authority or request path, and even return custom
|
||||
response without forwarding request to backend servers.
|
||||
.sp
|
||||
To set request phase hook, use \fI\%\-\-request\-phase\-file\fP option.
|
||||
To set response phase hook, use \fI\%\-\-response\-phase\-file\fP
|
||||
option.
|
||||
.sp
|
||||
For request and response phase hook, user calls \fI\%Nghttpx.run\fP
|
||||
with block. The \fI\%Nghttpx::Env\fP is passed to the block.
|
||||
User can can access \fI\%Nghttpx::Request\fP and
|
||||
\fI\%Nghttpx::Response\fP objects via \fI\%Nghttpx::Env#req\fP
|
||||
and \fI\%Nghttpx::Env#resp\fP respectively.
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B classmethod .Nghttpx.run(&block)
|
||||
Run request or response phase hook with given \fIblock\fP\&.
|
||||
\fI\%Nghttpx::Env\fP object is passed to the given block.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B Nghttpx::REQUEST_PHASE
|
||||
Constant to represent request phase.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B Nghttpx::RESPONSE_PHASE
|
||||
Constant to represent response phase.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B class Nghttpx::Env
|
||||
Object to represent current request specific context.
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B attribute [R] req
|
||||
Return \fI\%Request\fP object.
|
||||
.UNINDENT
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B attribute [R] resp
|
||||
Return \fI\%Response\fP object.
|
||||
.UNINDENT
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B attribute [R] ctx
|
||||
Return Ruby hash object. It persists until request finishes.
|
||||
So values set in request phase hoo can be retrieved in
|
||||
response phase hook.
|
||||
.UNINDENT
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B attribute [R] phase
|
||||
Return the current phase.
|
||||
.UNINDENT
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B attribute [R] remote_addr
|
||||
Return IP address of a remote client.
|
||||
.UNINDENT
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B class Nghttpx::Request
|
||||
Object to represent request from client. The modification to
|
||||
Request object is allowed only in request phase hook.
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B attribute [R] http_version_major
|
||||
Return HTTP major version.
|
||||
.UNINDENT
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B attribute [R] http_version_minor
|
||||
Return HTTP minor version.
|
||||
.UNINDENT
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B attribute [R/W] method
|
||||
HTTP method. On assignment, copy of given value is assigned.
|
||||
We don\(aqt accept arbitrary method name. We will document them
|
||||
later, but well known methods, like GET, PUT and POST, are all
|
||||
supported.
|
||||
.UNINDENT
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B attribute [R/W] authority
|
||||
Authority (i.e., example.org), including optional port
|
||||
component . On assignment, copy of given value is assigned.
|
||||
.UNINDENT
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B attribute [R/W] scheme
|
||||
Scheme (i.e., http, https). On assignment, copy of given
|
||||
value is assigned.
|
||||
.UNINDENT
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B attribute [R/W] path
|
||||
Request path, including query component (i.e., /index.html).
|
||||
On assignment, copy of given value is assigned. The path does
|
||||
not include authority component of URI.
|
||||
.UNINDENT
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B attribute [R] headers
|
||||
Return Ruby hash containing copy of request header fields.
|
||||
Changing values in returned hash does not change request
|
||||
header fields actually used in request processing. Use
|
||||
\fI\%Nghttpx::Request#add_header\fP or
|
||||
\fI\%Nghttpx::Request#set_header\fP to change request
|
||||
header fields.
|
||||
.UNINDENT
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B add_header(key, value)
|
||||
Add header entry associated with key. The value can be single
|
||||
string or array of string. It does not replace any existing
|
||||
values associated with key.
|
||||
.UNINDENT
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B set_header(key, value)
|
||||
Set header entry associated with key. The value can be single
|
||||
string or array of string. It replaces any existing values
|
||||
associated with key.
|
||||
.UNINDENT
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B clear_headers()
|
||||
Clear all existing request header fields.
|
||||
.UNINDENT
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B push uri
|
||||
Initiate to push resource identified by \fIuri\fP\&. Only HTTP/2
|
||||
protocol supports this feature. For the other protocols, this
|
||||
method is noop. \fIuri\fP can be absolute URI, absolute path or
|
||||
relative path to the current request. For absolute or
|
||||
relative path, scheme and authority are inherited from the
|
||||
current request. Currently, method is always GET. nghttpx
|
||||
will issue request to backend servers to fulfill this request.
|
||||
The request and response phase hooks will be called for pushed
|
||||
resource as well.
|
||||
.UNINDENT
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B class Nghttpx::Response
|
||||
Object to represent response from backend server.
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B attribute [R] http_version_major
|
||||
Return HTTP major version.
|
||||
.UNINDENT
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B attribute [R] http_version_minor
|
||||
Return HTTP minor version.
|
||||
.UNINDENT
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B attribute [R/W] status
|
||||
HTTP status code. It must be in the range [200, 999],
|
||||
inclusive. The non\-final status code is not supported in
|
||||
mruby scripting at the moment.
|
||||
.UNINDENT
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B attribute [R] headers
|
||||
Return Ruby hash containing copy of response header fields.
|
||||
Changing values in returned hash does not change response
|
||||
header fields actually used in response processing. Use
|
||||
\fI\%Nghttpx::Response#add_header\fP or
|
||||
\fI\%Nghttpx::Response#set_header\fP to change response
|
||||
header fields.
|
||||
.UNINDENT
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B add_header(key, value)
|
||||
Add header entry associated with key. The value can be single
|
||||
string or array of string. It does not replace any existing
|
||||
values associated with key.
|
||||
.UNINDENT
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B set_header(key, value)
|
||||
Set header entry associated with key. The value can be single
|
||||
string or array of string. It replaces any existing values
|
||||
associated with key.
|
||||
.UNINDENT
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B clear_headers()
|
||||
Clear all existing response header fields.
|
||||
.UNINDENT
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B return(body)
|
||||
Return custom response \fIbody\fP to a client. When this method
|
||||
is called in request phase hook, the request is not forwarded
|
||||
to the backend, and response phase hook for this request will
|
||||
not be invoked. When this method is called in resonse phase
|
||||
hook, response from backend server is canceled and discarded.
|
||||
The status code and response header fields should be set
|
||||
before using this method. To set status code, use :rb:meth To
|
||||
set response header fields, use
|
||||
\fI\%Nghttpx::Response#status\fP\&. If status code is not
|
||||
set, 200 is used. \fI\%Nghttpx::Response#add_header\fP and
|
||||
\fI\%Nghttpx::Response#set_header\fP\&. When this method is
|
||||
invoked in response phase hook, the response headers are
|
||||
filled with the ones received from backend server. To send
|
||||
completely custom header fields, first call
|
||||
\fI\%Nghttpx::Response#clear_headers\fP to erase all
|
||||
existing header fields, and then add required header fields.
|
||||
It is an error to call this method twice for a given request.
|
||||
.UNINDENT
|
||||
.UNINDENT
|
||||
.SS MRUBY EXAMPLES
|
||||
.sp
|
||||
Modify requet path:
|
||||
.INDENT 0.0
|
||||
.INDENT 3.5
|
||||
.sp
|
||||
.nf
|
||||
.ft C
|
||||
Nghttpx.run do |env|
|
||||
env.req.path = "/apps#{env.req.path}"
|
||||
end
|
||||
.ft P
|
||||
.fi
|
||||
.UNINDENT
|
||||
.UNINDENT
|
||||
.sp
|
||||
Note that the file containing the above script must be set with
|
||||
\fI\%\-\-request\-phase\-file\fP option since we modify request path.
|
||||
.sp
|
||||
Restrict permission of viewing a content to a specific client
|
||||
addresses:
|
||||
.INDENT 0.0
|
||||
.INDENT 3.5
|
||||
.sp
|
||||
.nf
|
||||
.ft C
|
||||
Nghttpx.run do |env|
|
||||
allowed_clients = ["127.0.0.1", "::1"]
|
||||
|
||||
if env.req.path.start_with?("/log/") &&
|
||||
!allowed_clients.include?(env.remote_addr) then
|
||||
env.resp.status = 404
|
||||
env.resp.return "permission denied"
|
||||
end
|
||||
end
|
||||
.ft P
|
||||
.fi
|
||||
.UNINDENT
|
||||
.UNINDENT
|
||||
.SH SEE ALSO
|
||||
.sp
|
||||
\fInghttp(1)\fP, \fInghttpd(1)\fP, \fIh2load(1)\fP
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
|
||||
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
|
||||
|
||||
.. program:: nghttpx
|
||||
|
||||
nghttpx(1)
|
||||
@@ -35,16 +37,69 @@ The options are categorized into several groups.
|
||||
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)
|
||||
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.
|
||||
|
||||
|
||||
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.
|
||||
@@ -82,6 +137,10 @@ Connections
|
||||
be specified by :option:`--backend-read-timeout` and
|
||||
:option:`--backend-write-timeout` options.
|
||||
|
||||
.. option:: --accept-proxy-protocol
|
||||
|
||||
Accept PROXY protocol version 1 on frontend connection.
|
||||
|
||||
|
||||
Performance
|
||||
~~~~~~~~~~~
|
||||
@@ -163,16 +222,23 @@ Performance
|
||||
|
||||
.. option:: --backend-http2-connections-per-worker=<N>
|
||||
|
||||
Set maximum number of HTTP/2 connections per worker.
|
||||
The default value is 0, which means the number of
|
||||
backend addresses specified by :option:`-b` option.
|
||||
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``
|
||||
@@ -362,22 +428,70 @@ 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-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.
|
||||
|
||||
.. 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>
|
||||
|
||||
@@ -396,6 +510,12 @@ SSL/TLS
|
||||
|
||||
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
|
||||
~~~~~~~~~~~~~~~
|
||||
@@ -546,6 +666,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"``
|
||||
@@ -595,9 +723,9 @@ HTTP
|
||||
:option:`--client-proxy` mode, location header field will not be
|
||||
altered regardless of this option.
|
||||
|
||||
.. option:: --no-host-rewrite
|
||||
.. option:: --host-rewrite
|
||||
|
||||
Don't rewrite host and :authority header fields on
|
||||
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.
|
||||
@@ -606,10 +734,18 @@ HTTP
|
||||
|
||||
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>
|
||||
|
||||
@@ -619,6 +755,21 @@ HTTP
|
||||
used several times to specify multiple header fields.
|
||||
Example: :option:`--add-response-header`\="foo: bar"
|
||||
|
||||
.. option:: --header-field-buffer=<SIZE>
|
||||
|
||||
Set maximum buffer size for incoming HTTP header field
|
||||
list. This is the sum of header name and value in
|
||||
bytes.
|
||||
|
||||
Default: ``64K``
|
||||
|
||||
.. option:: --max-header-fields=<N>
|
||||
|
||||
Set maximum number of incoming HTTP header fields, which
|
||||
appear in one request or response header field list.
|
||||
|
||||
Default: ``100``
|
||||
|
||||
|
||||
Debug
|
||||
~~~~~
|
||||
@@ -664,6 +815,22 @@ Process
|
||||
be used to drop root privileges.
|
||||
|
||||
|
||||
Scripting
|
||||
~~~~~~~~~
|
||||
|
||||
.. option:: --request-phase-file=<PATH>
|
||||
|
||||
Set mruby script file which will be executed when
|
||||
request header fields are completely received from
|
||||
frontend. This hook is called request phase hook.
|
||||
|
||||
.. option:: --response-phase-file=<PATH>
|
||||
|
||||
Set mruby script file which will be executed when
|
||||
response header fields are completely received from
|
||||
backend. This hook is called response phase hook.
|
||||
|
||||
|
||||
Misc
|
||||
~~~~
|
||||
|
||||
@@ -673,6 +840,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.
|
||||
@@ -712,7 +886,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
|
||||
@@ -735,6 +909,18 @@ SIGUSR2
|
||||
After new process comes up, sending SIGQUIT to the original process
|
||||
to perform hot swapping.
|
||||
|
||||
.. note::
|
||||
|
||||
nghttpx consists of 2 processes: one process for processing these
|
||||
signals, and another one for processing requests. The former spawns
|
||||
the latter. The former is called master process, and the latter is
|
||||
called worker process. The above signal must be sent to the master
|
||||
process. If the worker process receives one of them, it is ignored.
|
||||
This behaviour of worker process may change in the future release.
|
||||
In other words, in the future release, worker process may terminate
|
||||
upon the reception of these signals. Therefore these signals should
|
||||
not be sent to the worker process.
|
||||
|
||||
SERVER PUSH
|
||||
-----------
|
||||
|
||||
@@ -752,17 +938,12 @@ header field to initiate server push:
|
||||
Link: </fonts/font.woff>; rel=preload
|
||||
Link: </css/theme.css>; rel=preload
|
||||
|
||||
Currently, the following restrictions are applied for server push:
|
||||
Currently, the following restriction is 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
|
||||
1. 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.
|
||||
This limitation may be loosened in the future release.
|
||||
|
||||
UNIX DOMAIN SOCKET
|
||||
------------------
|
||||
@@ -780,14 +961,310 @@ delete the socket and continues to use it.
|
||||
OCSP STAPLING
|
||||
-------------
|
||||
|
||||
OCSP query is done using external perl script ``fetch-ocsp-response``,
|
||||
which has been developed as part of h2o project
|
||||
(https://github.com/h2o/h2o).
|
||||
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).
|
||||
|
||||
MRUBY SCRIPTING
|
||||
---------------
|
||||
|
||||
.. warning::
|
||||
|
||||
The current mruby extension API is experimental and not frozen. The
|
||||
API is subject to change in the future release.
|
||||
|
||||
nghttpx allows users to extend its capability using mruby scripts.
|
||||
nghttpx has 2 hook points to execute mruby script: request phase and
|
||||
response phase. The request phase hook is invoked after all request
|
||||
header fields are received from client. The response phase hook is
|
||||
invoked after all response header fields are received from backend
|
||||
server. These hooks allows users to modify header fields, or common
|
||||
HTTP variables, like authority or request path, and even return custom
|
||||
response without forwarding request to backend servers.
|
||||
|
||||
To set request phase hook, use :option:`--request-phase-file` option.
|
||||
To set response phase hook, use :option:`--response-phase-file`
|
||||
option.
|
||||
|
||||
For request and response phase hook, user calls :rb:meth:`Nghttpx.run`
|
||||
with block. The :rb:class:`Nghttpx::Env` is passed to the block.
|
||||
User can can access :rb:class:`Nghttpx::Request` and
|
||||
:rb:class:`Nghttpx::Response` objects via :rb:attr:`Nghttpx::Env#req`
|
||||
and :rb:attr:`Nghttpx::Env#resp` respectively.
|
||||
|
||||
.. rb:module:: Nghttpx
|
||||
|
||||
.. rb:classmethod:: run(&block)
|
||||
|
||||
Run request or response phase hook with given *block*.
|
||||
:rb:class:`Nghttpx::Env` object is passed to the given block.
|
||||
|
||||
.. rb:const:: REQUEST_PHASE
|
||||
|
||||
Constant to represent request phase.
|
||||
|
||||
.. rb:const:: RESPONSE_PHASE
|
||||
|
||||
Constant to represent response phase.
|
||||
|
||||
.. rb:class:: Env
|
||||
|
||||
Object to represent current request specific context.
|
||||
|
||||
.. rb:attr_reader:: req
|
||||
|
||||
Return :rb:class:`Request` object.
|
||||
|
||||
.. rb:attr_reader:: resp
|
||||
|
||||
Return :rb:class:`Response` object.
|
||||
|
||||
.. rb:attr_reader:: ctx
|
||||
|
||||
Return Ruby hash object. It persists until request finishes.
|
||||
So values set in request phase hoo can be retrieved in
|
||||
response phase hook.
|
||||
|
||||
.. rb:attr_reader:: phase
|
||||
|
||||
Return the current phase.
|
||||
|
||||
.. rb:attr_reader:: remote_addr
|
||||
|
||||
Return IP address of a remote client.
|
||||
|
||||
.. rb:class:: Request
|
||||
|
||||
Object to represent request from client. The modification to
|
||||
Request object is allowed only in request phase hook.
|
||||
|
||||
.. rb:attr_reader:: http_version_major
|
||||
|
||||
Return HTTP major version.
|
||||
|
||||
.. rb:attr_reader:: http_version_minor
|
||||
|
||||
Return HTTP minor version.
|
||||
|
||||
.. rb:attr_accessor:: method
|
||||
|
||||
HTTP method. On assignment, copy of given value is assigned.
|
||||
We don't accept arbitrary method name. We will document them
|
||||
later, but well known methods, like GET, PUT and POST, are all
|
||||
supported.
|
||||
|
||||
.. rb:attr_accessor:: authority
|
||||
|
||||
Authority (i.e., example.org), including optional port
|
||||
component . On assignment, copy of given value is assigned.
|
||||
|
||||
.. rb:attr_accessor:: scheme
|
||||
|
||||
Scheme (i.e., http, https). On assignment, copy of given
|
||||
value is assigned.
|
||||
|
||||
.. rb:attr_accessor:: path
|
||||
|
||||
Request path, including query component (i.e., /index.html).
|
||||
On assignment, copy of given value is assigned. The path does
|
||||
not include authority component of URI.
|
||||
|
||||
.. rb:attr_reader:: headers
|
||||
|
||||
Return Ruby hash containing copy of request header fields.
|
||||
Changing values in returned hash does not change request
|
||||
header fields actually used in request processing. Use
|
||||
:rb:meth:`Nghttpx::Request#add_header` or
|
||||
:rb:meth:`Nghttpx::Request#set_header` to change request
|
||||
header fields.
|
||||
|
||||
.. rb:method:: add_header(key, value)
|
||||
|
||||
Add header entry associated with key. The value can be single
|
||||
string or array of string. It does not replace any existing
|
||||
values associated with key.
|
||||
|
||||
.. rb:method:: set_header(key, value)
|
||||
|
||||
Set header entry associated with key. The value can be single
|
||||
string or array of string. It replaces any existing values
|
||||
associated with key.
|
||||
|
||||
.. rb:method:: clear_headers
|
||||
|
||||
Clear all existing request header fields.
|
||||
|
||||
.. rb:method:: push uri
|
||||
|
||||
Initiate to push resource identified by *uri*. Only HTTP/2
|
||||
protocol supports this feature. For the other protocols, this
|
||||
method is noop. *uri* can be absolute URI, absolute path or
|
||||
relative path to the current request. For absolute or
|
||||
relative path, scheme and authority are inherited from the
|
||||
current request. Currently, method is always GET. nghttpx
|
||||
will issue request to backend servers to fulfill this request.
|
||||
The request and response phase hooks will be called for pushed
|
||||
resource as well.
|
||||
|
||||
.. rb:class:: Response
|
||||
|
||||
Object to represent response from backend server.
|
||||
|
||||
.. rb:attr_reader:: http_version_major
|
||||
|
||||
Return HTTP major version.
|
||||
|
||||
.. rb:attr_reader:: http_version_minor
|
||||
|
||||
Return HTTP minor version.
|
||||
|
||||
.. rb:attr_accessor:: status
|
||||
|
||||
HTTP status code. It must be in the range [200, 999],
|
||||
inclusive. The non-final status code is not supported in
|
||||
mruby scripting at the moment.
|
||||
|
||||
.. rb:attr_reader:: headers
|
||||
|
||||
Return Ruby hash containing copy of response header fields.
|
||||
Changing values in returned hash does not change response
|
||||
header fields actually used in response processing. Use
|
||||
:rb:meth:`Nghttpx::Response#add_header` or
|
||||
:rb:meth:`Nghttpx::Response#set_header` to change response
|
||||
header fields.
|
||||
|
||||
.. rb:method:: add_header(key, value)
|
||||
|
||||
Add header entry associated with key. The value can be single
|
||||
string or array of string. It does not replace any existing
|
||||
values associated with key.
|
||||
|
||||
.. rb:method:: set_header(key, value)
|
||||
|
||||
Set header entry associated with key. The value can be single
|
||||
string or array of string. It replaces any existing values
|
||||
associated with key.
|
||||
|
||||
.. rb:method:: clear_headers
|
||||
|
||||
Clear all existing response header fields.
|
||||
|
||||
.. rb:method:: return(body)
|
||||
|
||||
Return custom response *body* to a client. When this method
|
||||
is called in request phase hook, the request is not forwarded
|
||||
to the backend, and response phase hook for this request will
|
||||
not be invoked. When this method is called in resonse phase
|
||||
hook, response from backend server is canceled and discarded.
|
||||
The status code and response header fields should be set
|
||||
before using this method. To set status code, use :rb:meth To
|
||||
set response header fields, use
|
||||
:rb:attr:`Nghttpx::Response#status`. If status code is not
|
||||
set, 200 is used. :rb:meth:`Nghttpx::Response#add_header` and
|
||||
:rb:meth:`Nghttpx::Response#set_header`. When this method is
|
||||
invoked in response phase hook, the response headers are
|
||||
filled with the ones received from backend server. To send
|
||||
completely custom header fields, first call
|
||||
:rb:meth:`Nghttpx::Response#clear_headers` to erase all
|
||||
existing header fields, and then add required header fields.
|
||||
It is an error to call this method twice for a given request.
|
||||
|
||||
MRUBY EXAMPLES
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Modify requet path:
|
||||
|
||||
.. code-block:: ruby
|
||||
|
||||
Nghttpx.run do |env|
|
||||
env.req.path = "/apps#{env.req.path}"
|
||||
end
|
||||
|
||||
Note that the file containing the above script must be set with
|
||||
:option:`--request-phase-file` option since we modify request path.
|
||||
|
||||
Restrict permission of viewing a content to a specific client
|
||||
addresses:
|
||||
|
||||
.. code-block:: ruby
|
||||
|
||||
Nghttpx.run do |env|
|
||||
allowed_clients = ["127.0.0.1", "::1"]
|
||||
|
||||
if env.req.path.start_with?("/log/") &&
|
||||
!allowed_clients.include?(env.remote_addr) then
|
||||
env.resp.status = 404
|
||||
env.resp.return "permission denied"
|
||||
end
|
||||
end
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
|
||||
|
||||
327
doc/nghttpx.h2r
327
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,18 @@ SIGUSR2
|
||||
After new process comes up, sending SIGQUIT to the original process
|
||||
to perform hot swapping.
|
||||
|
||||
.. note::
|
||||
|
||||
nghttpx consists of 2 processes: one process for processing these
|
||||
signals, and another one for processing requests. The former spawns
|
||||
the latter. The former is called master process, and the latter is
|
||||
called worker process. The above signal must be sent to the master
|
||||
process. If the worker process receives one of them, it is ignored.
|
||||
This behaviour of worker process may change in the future release.
|
||||
In other words, in the future release, worker process may terminate
|
||||
upon the reception of these signals. Therefore these signals should
|
||||
not be sent to the worker process.
|
||||
|
||||
SERVER PUSH
|
||||
-----------
|
||||
|
||||
@@ -59,17 +71,12 @@ header field to initiate server push:
|
||||
Link: </fonts/font.woff>; rel=preload
|
||||
Link: </css/theme.css>; rel=preload
|
||||
|
||||
Currently, the following restrictions are applied for server push:
|
||||
Currently, the following restriction is 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
|
||||
1. 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.
|
||||
This limitation may be loosened in the future release.
|
||||
|
||||
UNIX DOMAIN SOCKET
|
||||
------------------
|
||||
@@ -87,14 +94,310 @@ delete the socket and continues to use it.
|
||||
OCSP STAPLING
|
||||
-------------
|
||||
|
||||
OCSP query is done using external perl script ``fetch-ocsp-response``,
|
||||
which has been developed as part of h2o project
|
||||
(https://github.com/h2o/h2o).
|
||||
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).
|
||||
|
||||
MRUBY SCRIPTING
|
||||
---------------
|
||||
|
||||
.. warning::
|
||||
|
||||
The current mruby extension API is experimental and not frozen. The
|
||||
API is subject to change in the future release.
|
||||
|
||||
nghttpx allows users to extend its capability using mruby scripts.
|
||||
nghttpx has 2 hook points to execute mruby script: request phase and
|
||||
response phase. The request phase hook is invoked after all request
|
||||
header fields are received from client. The response phase hook is
|
||||
invoked after all response header fields are received from backend
|
||||
server. These hooks allows users to modify header fields, or common
|
||||
HTTP variables, like authority or request path, and even return custom
|
||||
response without forwarding request to backend servers.
|
||||
|
||||
To set request phase hook, use :option:`--request-phase-file` option.
|
||||
To set response phase hook, use :option:`--response-phase-file`
|
||||
option.
|
||||
|
||||
For request and response phase hook, user calls :rb:meth:`Nghttpx.run`
|
||||
with block. The :rb:class:`Nghttpx::Env` is passed to the block.
|
||||
User can can access :rb:class:`Nghttpx::Request` and
|
||||
:rb:class:`Nghttpx::Response` objects via :rb:attr:`Nghttpx::Env#req`
|
||||
and :rb:attr:`Nghttpx::Env#resp` respectively.
|
||||
|
||||
.. rb:module:: Nghttpx
|
||||
|
||||
.. rb:classmethod:: run(&block)
|
||||
|
||||
Run request or response phase hook with given *block*.
|
||||
:rb:class:`Nghttpx::Env` object is passed to the given block.
|
||||
|
||||
.. rb:const:: REQUEST_PHASE
|
||||
|
||||
Constant to represent request phase.
|
||||
|
||||
.. rb:const:: RESPONSE_PHASE
|
||||
|
||||
Constant to represent response phase.
|
||||
|
||||
.. rb:class:: Env
|
||||
|
||||
Object to represent current request specific context.
|
||||
|
||||
.. rb:attr_reader:: req
|
||||
|
||||
Return :rb:class:`Request` object.
|
||||
|
||||
.. rb:attr_reader:: resp
|
||||
|
||||
Return :rb:class:`Response` object.
|
||||
|
||||
.. rb:attr_reader:: ctx
|
||||
|
||||
Return Ruby hash object. It persists until request finishes.
|
||||
So values set in request phase hoo can be retrieved in
|
||||
response phase hook.
|
||||
|
||||
.. rb:attr_reader:: phase
|
||||
|
||||
Return the current phase.
|
||||
|
||||
.. rb:attr_reader:: remote_addr
|
||||
|
||||
Return IP address of a remote client.
|
||||
|
||||
.. rb:class:: Request
|
||||
|
||||
Object to represent request from client. The modification to
|
||||
Request object is allowed only in request phase hook.
|
||||
|
||||
.. rb:attr_reader:: http_version_major
|
||||
|
||||
Return HTTP major version.
|
||||
|
||||
.. rb:attr_reader:: http_version_minor
|
||||
|
||||
Return HTTP minor version.
|
||||
|
||||
.. rb:attr_accessor:: method
|
||||
|
||||
HTTP method. On assignment, copy of given value is assigned.
|
||||
We don't accept arbitrary method name. We will document them
|
||||
later, but well known methods, like GET, PUT and POST, are all
|
||||
supported.
|
||||
|
||||
.. rb:attr_accessor:: authority
|
||||
|
||||
Authority (i.e., example.org), including optional port
|
||||
component . On assignment, copy of given value is assigned.
|
||||
|
||||
.. rb:attr_accessor:: scheme
|
||||
|
||||
Scheme (i.e., http, https). On assignment, copy of given
|
||||
value is assigned.
|
||||
|
||||
.. rb:attr_accessor:: path
|
||||
|
||||
Request path, including query component (i.e., /index.html).
|
||||
On assignment, copy of given value is assigned. The path does
|
||||
not include authority component of URI.
|
||||
|
||||
.. rb:attr_reader:: headers
|
||||
|
||||
Return Ruby hash containing copy of request header fields.
|
||||
Changing values in returned hash does not change request
|
||||
header fields actually used in request processing. Use
|
||||
:rb:meth:`Nghttpx::Request#add_header` or
|
||||
:rb:meth:`Nghttpx::Request#set_header` to change request
|
||||
header fields.
|
||||
|
||||
.. rb:method:: add_header(key, value)
|
||||
|
||||
Add header entry associated with key. The value can be single
|
||||
string or array of string. It does not replace any existing
|
||||
values associated with key.
|
||||
|
||||
.. rb:method:: set_header(key, value)
|
||||
|
||||
Set header entry associated with key. The value can be single
|
||||
string or array of string. It replaces any existing values
|
||||
associated with key.
|
||||
|
||||
.. rb:method:: clear_headers
|
||||
|
||||
Clear all existing request header fields.
|
||||
|
||||
.. rb:method:: push uri
|
||||
|
||||
Initiate to push resource identified by *uri*. Only HTTP/2
|
||||
protocol supports this feature. For the other protocols, this
|
||||
method is noop. *uri* can be absolute URI, absolute path or
|
||||
relative path to the current request. For absolute or
|
||||
relative path, scheme and authority are inherited from the
|
||||
current request. Currently, method is always GET. nghttpx
|
||||
will issue request to backend servers to fulfill this request.
|
||||
The request and response phase hooks will be called for pushed
|
||||
resource as well.
|
||||
|
||||
.. rb:class:: Response
|
||||
|
||||
Object to represent response from backend server.
|
||||
|
||||
.. rb:attr_reader:: http_version_major
|
||||
|
||||
Return HTTP major version.
|
||||
|
||||
.. rb:attr_reader:: http_version_minor
|
||||
|
||||
Return HTTP minor version.
|
||||
|
||||
.. rb:attr_accessor:: status
|
||||
|
||||
HTTP status code. It must be in the range [200, 999],
|
||||
inclusive. The non-final status code is not supported in
|
||||
mruby scripting at the moment.
|
||||
|
||||
.. rb:attr_reader:: headers
|
||||
|
||||
Return Ruby hash containing copy of response header fields.
|
||||
Changing values in returned hash does not change response
|
||||
header fields actually used in response processing. Use
|
||||
:rb:meth:`Nghttpx::Response#add_header` or
|
||||
:rb:meth:`Nghttpx::Response#set_header` to change response
|
||||
header fields.
|
||||
|
||||
.. rb:method:: add_header(key, value)
|
||||
|
||||
Add header entry associated with key. The value can be single
|
||||
string or array of string. It does not replace any existing
|
||||
values associated with key.
|
||||
|
||||
.. rb:method:: set_header(key, value)
|
||||
|
||||
Set header entry associated with key. The value can be single
|
||||
string or array of string. It replaces any existing values
|
||||
associated with key.
|
||||
|
||||
.. rb:method:: clear_headers
|
||||
|
||||
Clear all existing response header fields.
|
||||
|
||||
.. rb:method:: return(body)
|
||||
|
||||
Return custom response *body* to a client. When this method
|
||||
is called in request phase hook, the request is not forwarded
|
||||
to the backend, and response phase hook for this request will
|
||||
not be invoked. When this method is called in resonse phase
|
||||
hook, response from backend server is canceled and discarded.
|
||||
The status code and response header fields should be set
|
||||
before using this method. To set status code, use :rb:meth To
|
||||
set response header fields, use
|
||||
:rb:attr:`Nghttpx::Response#status`. If status code is not
|
||||
set, 200 is used. :rb:meth:`Nghttpx::Response#add_header` and
|
||||
:rb:meth:`Nghttpx::Response#set_header`. When this method is
|
||||
invoked in response phase hook, the response headers are
|
||||
filled with the ones received from backend server. To send
|
||||
completely custom header fields, first call
|
||||
:rb:meth:`Nghttpx::Response#clear_headers` to erase all
|
||||
existing header fields, and then add required header fields.
|
||||
It is an error to call this method twice for a given request.
|
||||
|
||||
MRUBY EXAMPLES
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Modify requet path:
|
||||
|
||||
.. code-block:: ruby
|
||||
|
||||
Nghttpx.run do |env|
|
||||
env.req.path = "/apps#{env.req.path}"
|
||||
end
|
||||
|
||||
Note that the file containing the above script must be set with
|
||||
:option:`--request-phase-file` option since we modify request path.
|
||||
|
||||
Restrict permission of viewing a content to a specific client
|
||||
addresses:
|
||||
|
||||
.. code-block:: ruby
|
||||
|
||||
Nghttpx.run do |env|
|
||||
allowed_clients = ["127.0.0.1", "::1"]
|
||||
|
||||
if env.req.path.start_with?("/log/") &&
|
||||
!allowed_clients.include?(env.remote_addr) then
|
||||
env.resp.status = 404
|
||||
env.resp.return "permission denied"
|
||||
end
|
||||
end
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
|
||||
|
||||
@@ -20,17 +20,15 @@ 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.
|
||||
`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:
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -65,7 +66,8 @@ To configure OpenSSL, use the following script:
|
||||
export CROSS_COMPILE=$TOOLCHAIN/bin/arm-linux-androideabi-
|
||||
./Configure --prefix=$PREFIX android
|
||||
|
||||
And run ``make install`` to build and install.
|
||||
And run ``make install_sw`` to build and install without
|
||||
documentation.
|
||||
|
||||
We cannot compile libev without modification. Apply `this patch
|
||||
<https://gist.github.com/tatsuhiro-t/48c45f08950f587180ed>`_ before
|
||||
@@ -95,10 +97,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 +152,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.
|
||||
|
||||
@@ -49,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
|
||||
|
||||
@@ -80,7 +80,7 @@ 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
|
||||
@@ -277,7 +277,7 @@ response header fields and response body to the console screen:
|
||||
``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 paramter. After this callback call, use
|
||||
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.
|
||||
|
||||
@@ -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
|
||||
@@ -243,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
|
||||
|
||||
@@ -267,7 +267,7 @@ HTTP/2 servers
|
||||
(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_DEFERRD`). When data arrived, call
|
||||
: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;
|
||||
|
||||
@@ -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;
|
||||
@@ -262,7 +269,7 @@ data is available to read in the bufferevent input buffer::
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
if (evbuffer_drain(input, readlen) != 0) {
|
||||
if (evbuffer_drain(input, (size_t)readlen) != 0) {
|
||||
warnx("Fatal error: evbuffer_drain failed");
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
@@ -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,34 +299,34 @@ 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) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
struct bufferevent *bev = session_data->bev;
|
||||
bufferevent_write(bev, data, length);
|
||||
return length;
|
||||
return (ssize_t)length;
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -125,7 +132,7 @@ libevent's ``struct evconnlistener`` for this purpose::
|
||||
struct evconnlistener *listener;
|
||||
listener = evconnlistener_new_bind(
|
||||
evbase, acceptcb, app_ctx, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
|
||||
16, rp->ai_addr, rp->ai_addrlen);
|
||||
16, rp->ai_addr, (int)rp->ai_addrlen);
|
||||
if (listener) {
|
||||
freeaddrinfo(res);
|
||||
|
||||
@@ -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.
|
||||
@@ -276,7 +272,7 @@ this pending data. To process the received data, we call the
|
||||
warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
|
||||
return -1;
|
||||
}
|
||||
if (evbuffer_drain(input, readlen) != 0) {
|
||||
if (evbuffer_drain(input, (size_t)readlen) != 0) {
|
||||
warnx("Fatal error: evbuffer_drain failed");
|
||||
return -1;
|
||||
}
|
||||
@@ -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::
|
||||
|
||||
@@ -318,23 +316,22 @@ follows::
|
||||
return NGHTTP2_ERR_WOULDBLOCK;
|
||||
}
|
||||
bufferevent_write(bev, data, length);
|
||||
return length;
|
||||
return (ssize_t)length;
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
@@ -35,8 +35,12 @@
|
||||
//
|
||||
#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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -114,6 +124,7 @@ static char *strcopy(const char *s, size_t len) {
|
||||
/*
|
||||
* Prints error message |msg| and exit.
|
||||
*/
|
||||
NGHTTP2_NORETURN
|
||||
static void die(const char *msg) {
|
||||
fprintf(stderr, "FATAL: %s\n", msg);
|
||||
exit(EXIT_FAILURE);
|
||||
@@ -123,6 +134,7 @@ static void die(const char *msg) {
|
||||
* Prints error containing the function name |func| and message |msg|
|
||||
* and exit.
|
||||
*/
|
||||
NGHTTP2_NORETURN
|
||||
static void dief(const char *func, const char *msg) {
|
||||
fprintf(stderr, "FATAL: %s: %s\n", func, msg);
|
||||
exit(EXIT_FAILURE);
|
||||
@@ -132,6 +144,7 @@ static void dief(const char *func, const char *msg) {
|
||||
* Prints error containing the function name |func| and error code
|
||||
* |error_code| and exit.
|
||||
*/
|
||||
NGHTTP2_NORETURN
|
||||
static void diec(const char *func, int error_code) {
|
||||
fprintf(stderr, "FATAL: %s: error_code=%d, msg=%s\n", func, error_code,
|
||||
nghttp2_strerror(error_code));
|
||||
@@ -528,14 +541,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);
|
||||
@@ -655,10 +660,10 @@ static int parse_uri(struct URI *res, const char *uri) {
|
||||
return -1;
|
||||
}
|
||||
offset = i;
|
||||
res->port = port;
|
||||
res->port = (uint16_t)port;
|
||||
}
|
||||
}
|
||||
res->hostportlen = uri + offset + ipv6addr - res->host;
|
||||
res->hostportlen = (size_t)(uri + offset + ipv6addr - res->host);
|
||||
for (i = offset; i < len; ++i) {
|
||||
if (uri[i] == '#') {
|
||||
break;
|
||||
@@ -687,10 +692,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) {
|
||||
|
||||
@@ -129,10 +129,10 @@ static void deflate(nghttp2_hd_deflater *deflater,
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
outlen = rv;
|
||||
outlen = (size_t)rv;
|
||||
|
||||
printf("\nDeflate (%zu byte(s), ratio %.02f):\n\n", outlen,
|
||||
sum == 0 ? 0 : (double)outlen / sum);
|
||||
sum == 0 ? 0 : (double)outlen / (double)sum);
|
||||
|
||||
for (i = 0; i < outlen; ++i) {
|
||||
if ((i & 0x0fu) == 0) {
|
||||
@@ -180,7 +180,7 @@ int inflate_header_block(nghttp2_hd_inflater *inflater, uint8_t *in,
|
||||
return -1;
|
||||
}
|
||||
|
||||
proclen = rv;
|
||||
proclen = (size_t)rv;
|
||||
|
||||
in += proclen;
|
||||
inlen -= proclen;
|
||||
|
||||
@@ -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 */
|
||||
@@ -90,31 +109,35 @@ static http2_stream_data *create_http2_stream_data(const char *uri,
|
||||
u->field_data[UF_HOST].len);
|
||||
if (u->field_set & (1 << UF_PORT)) {
|
||||
stream_data->authoritylen +=
|
||||
snprintf(stream_data->authority + u->field_data[UF_HOST].len, extra,
|
||||
":%u", u->port);
|
||||
(size_t)snprintf(stream_data->authority + u->field_data[UF_HOST].len,
|
||||
extra, ":%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;
|
||||
}
|
||||
if (u->field_set & (1 << UF_QUERY)) {
|
||||
/* +1 for '?' character */
|
||||
stream_data->pathlen += u->field_data[UF_QUERY].len + 1;
|
||||
stream_data->pathlen += (size_t)(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;
|
||||
}
|
||||
|
||||
@@ -180,7 +203,7 @@ static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
struct bufferevent *bev = session_data->bev;
|
||||
bufferevent_write(bev, data, length);
|
||||
return length;
|
||||
return (ssize_t)length;
|
||||
}
|
||||
|
||||
/* nghttp2_on_header_callback: Called when nghttp2 library emits
|
||||
@@ -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) {
|
||||
@@ -419,7 +440,7 @@ static void readcb(struct bufferevent *bev, void *ptr) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
if (evbuffer_drain(input, readlen) != 0) {
|
||||
if (evbuffer_drain(input, (size_t)readlen) != 0) {
|
||||
warnx("Fatal error: evbuffer_drain failed");
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
@@ -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>
|
||||
@@ -198,7 +220,8 @@ static http2_session_data *create_http2_session_data(app_context *app_ctx,
|
||||
session_data->bev = bufferevent_openssl_socket_new(
|
||||
app_ctx->evbase, fd, ssl, BUFFEREVENT_SSL_ACCEPTING,
|
||||
BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
|
||||
rv = getnameinfo(addr, addrlen, host, sizeof(host), NULL, 0, NI_NUMERICHOST);
|
||||
rv = getnameinfo(addr, (socklen_t)addrlen, host, sizeof(host), NULL, 0,
|
||||
NI_NUMERICHOST);
|
||||
if (rv != 0) {
|
||||
session_data->client_addr = strdup("(unknown)");
|
||||
} else {
|
||||
@@ -253,7 +276,7 @@ static int session_recv(http2_session_data *session_data) {
|
||||
warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
|
||||
return -1;
|
||||
}
|
||||
if (evbuffer_drain(input, readlen) != 0) {
|
||||
if (evbuffer_drain(input, (size_t)readlen) != 0) {
|
||||
warnx("Fatal error: evbuffer_drain failed");
|
||||
return -1;
|
||||
}
|
||||
@@ -273,7 +296,7 @@ static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
|
||||
return NGHTTP2_ERR_WOULDBLOCK;
|
||||
}
|
||||
bufferevent_write(bev, data, length);
|
||||
return length;
|
||||
return (ssize_t)length;
|
||||
}
|
||||
|
||||
/* Returns nonzero if the string |s| ends with the substring |sub| */
|
||||
@@ -289,13 +312,13 @@ static int ends_with(const char *s, const char *sub) {
|
||||
/* Returns int value of hex string character |c| */
|
||||
static uint8_t hex_to_uint(uint8_t c) {
|
||||
if ('0' <= c && c <= '9') {
|
||||
return c - '0';
|
||||
return (uint8_t)(c - '0');
|
||||
}
|
||||
if ('A' <= c && c <= 'F') {
|
||||
return c - 'A' + 10;
|
||||
return (uint8_t)(c - 'A' + 10);
|
||||
}
|
||||
if ('a' <= c && c <= 'f') {
|
||||
return c - 'a' + 10;
|
||||
return (uint8_t)(c - 'a' + 10);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -313,10 +336,11 @@ static char *percent_decode(const uint8_t *value, size_t valuelen) {
|
||||
for (i = 0, j = 0; i < valuelen - 2;) {
|
||||
if (value[i] != '%' || !isxdigit(value[i + 1]) ||
|
||||
!isxdigit(value[i + 2])) {
|
||||
res[j++] = value[i++];
|
||||
res[j++] = (char)value[i++];
|
||||
continue;
|
||||
}
|
||||
res[j++] = (hex_to_uint(value[i + 1]) << 4) + hex_to_uint(value[i + 2]);
|
||||
res[j++] =
|
||||
(char)((hex_to_uint(value[i + 1]) << 4) + hex_to_uint(value[i + 2]));
|
||||
i += 3;
|
||||
}
|
||||
memcpy(&res[j], &value[i], 2);
|
||||
@@ -361,8 +385,8 @@ static int send_response(nghttp2_session *session, int32_t stream_id,
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char ERROR_HTML[] = "<html><head><title>404</title></head>"
|
||||
"<body><h1>404 Not Found</h1></body></html>";
|
||||
static const char ERROR_HTML[] = "<html><head><title>404</title></head>"
|
||||
"<body><h1>404 Not Found</h1></body></html>";
|
||||
|
||||
static int error_reply(nghttp2_session *session,
|
||||
http2_stream_data *stream_data) {
|
||||
@@ -537,15 +561,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 +579,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,13 +686,13 @@ 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;
|
||||
listener = evconnlistener_new_bind(
|
||||
evbase, acceptcb, app_ctx, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
|
||||
16, rp->ai_addr, rp->ai_addrlen);
|
||||
16, rp->ai_addr, (int)rp->ai_addrlen);
|
||||
if (listener) {
|
||||
freeaddrinfo(res);
|
||||
|
||||
@@ -723,10 +738,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>
|
||||
@@ -69,9 +81,9 @@
|
||||
#define array_size(a) (sizeof((a)) / sizeof((a)[0]))
|
||||
|
||||
/* Returns the length of remaning data in buffer */
|
||||
#define io_buf_len(iobuf) ((iobuf)->last - (iobuf)->pos)
|
||||
#define io_buf_len(iobuf) ((size_t)((iobuf)->last - (iobuf)->pos))
|
||||
/* Returns the space buffer can still accept */
|
||||
#define io_buf_left(iobuf) ((iobuf)->end - (iobuf)->last)
|
||||
#define io_buf_left(iobuf) ((size_t)((iobuf)->end - (iobuf)->last))
|
||||
|
||||
typedef struct {
|
||||
/* beginning of buffer */
|
||||
@@ -148,12 +160,11 @@ typedef struct {
|
||||
} timer;
|
||||
|
||||
/* document root */
|
||||
const char *docroot;
|
||||
static const char *docroot;
|
||||
/* length of docroot */
|
||||
size_t docrootlen;
|
||||
static size_t docrootlen;
|
||||
|
||||
nghttp2_session_callbacks *shared_callbacks;
|
||||
nghttp2_option *shared_option;
|
||||
static nghttp2_session_callbacks *shared_callbacks;
|
||||
|
||||
static int handle_accept(io_loop *loop, uint32_t events, void *ptr);
|
||||
static int handle_connection(io_loop *loop, uint32_t events, void *ptr);
|
||||
@@ -190,7 +201,7 @@ typedef enum {
|
||||
NGHTTP2_TOKEN__METHOD,
|
||||
NGHTTP2_TOKEN__PATH,
|
||||
NGHTTP2_TOKEN__SCHEME,
|
||||
NGHTTP2_TOKEN_HOST,
|
||||
NGHTTP2_TOKEN_HOST
|
||||
} nghttp2_token;
|
||||
|
||||
/* Inspired by h2o header lookup. https://github.com/h2o/h2o */
|
||||
@@ -237,7 +248,7 @@ static char *cpydig(char *buf, int n, size_t len) {
|
||||
|
||||
p = buf + len - 1;
|
||||
do {
|
||||
*p-- = (n % 10) + '0';
|
||||
*p-- = (char)((n % 10) + '0');
|
||||
n /= 10;
|
||||
} while (p >= buf);
|
||||
|
||||
@@ -278,11 +289,11 @@ static size_t http_date(char *buf, time_t t) {
|
||||
memcpy(p, " GMT", 4);
|
||||
p += 4;
|
||||
|
||||
return p - buf;
|
||||
return (size_t)(p - buf);
|
||||
}
|
||||
|
||||
static char date[29];
|
||||
static char datelen;
|
||||
static size_t datelen;
|
||||
|
||||
static void update_date() { datelen = http_date(date, time(NULL)); }
|
||||
|
||||
@@ -308,7 +319,7 @@ static size_t utos(char *buf, size_t len, uint64_t n) {
|
||||
|
||||
buf += nwrite - 1;
|
||||
do {
|
||||
*buf-- = (n % 10) + '0';
|
||||
*buf-- = (char)((n % 10) + '0');
|
||||
n /= 10;
|
||||
} while (n);
|
||||
|
||||
@@ -388,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;
|
||||
@@ -684,7 +694,7 @@ static int send_data_callback(nghttp2_session *session _U_,
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
length -= nread;
|
||||
length -= (size_t)nread;
|
||||
p += nread;
|
||||
}
|
||||
|
||||
@@ -699,7 +709,8 @@ static ssize_t fd_read_callback(nghttp2_session *session _U_,
|
||||
nghttp2_data_source *source,
|
||||
void *user_data _U_) {
|
||||
stream *strm = source->ptr;
|
||||
ssize_t nread = (int64_t)length < strm->fileleft ? length : strm->fileleft;
|
||||
ssize_t nread =
|
||||
(int64_t)length < strm->fileleft ? (int64_t)length : strm->fileleft;
|
||||
|
||||
*data_flags |= NGHTTP2_DATA_FLAG_NO_COPY;
|
||||
|
||||
@@ -719,7 +730,7 @@ static ssize_t resbuf_read_callback(nghttp2_session *session _U_,
|
||||
nghttp2_data_source *source,
|
||||
void *user_data _U_) {
|
||||
stream *strm = source->ptr;
|
||||
size_t left = strm->res_end - strm->res_begin;
|
||||
size_t left = (size_t)(strm->res_end - strm->res_begin);
|
||||
size_t nwrite = length < left ? length : left;
|
||||
|
||||
memcpy(buf, strm->res_begin, nwrite);
|
||||
@@ -729,7 +740,7 @@ static ssize_t resbuf_read_callback(nghttp2_session *session _U_,
|
||||
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
||||
}
|
||||
|
||||
return nwrite;
|
||||
return (ssize_t)nwrite;
|
||||
}
|
||||
|
||||
static int hex_digit(char c) {
|
||||
@@ -739,14 +750,14 @@ static int hex_digit(char c) {
|
||||
|
||||
static unsigned int hex_to_uint(char c) {
|
||||
if (c <= '9') {
|
||||
return c - '0';
|
||||
return (unsigned int)(c - '0');
|
||||
}
|
||||
|
||||
if (c <= 'F') {
|
||||
return c - 'A' + 10;
|
||||
return (unsigned int)(c - 'A' + 10);
|
||||
}
|
||||
|
||||
return c - 'a' + 10;
|
||||
return (unsigned int)(c - 'a' + 10);
|
||||
}
|
||||
|
||||
static void percent_decode(io_buf *buf, const char *s) {
|
||||
@@ -756,12 +767,13 @@ static void percent_decode(io_buf *buf, const char *s) {
|
||||
}
|
||||
|
||||
if (*s == '%' && hex_digit(*(s + 1)) && hex_digit(*(s + 2))) {
|
||||
*buf->last++ = (hex_to_uint(*(s + 1)) << 4) + hex_to_uint(*(s + 2));
|
||||
*buf->last++ =
|
||||
(uint8_t)((hex_to_uint(*(s + 1)) << 4) + hex_to_uint(*(s + 2)));
|
||||
s += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
*buf->last++ = *s;
|
||||
*buf->last++ = (uint8_t)*s;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -796,7 +808,7 @@ static int make_path(io_buf *pathbuf, const char *req, size_t reqlen _U_) {
|
||||
|
||||
*pathbuf->last++ = '\0';
|
||||
|
||||
if (!check_path((const char *)p, pathbuf->last - 1 - p)) {
|
||||
if (!check_path((const char *)p, (size_t)(pathbuf->last - 1 - p))) {
|
||||
|
||||
return -1;
|
||||
}
|
||||
@@ -947,7 +959,8 @@ static int process_request(stream *strm, connection *conn) {
|
||||
strm->fileleft = stbuf.st_size;
|
||||
|
||||
lastmodlen = http_date(lastmod, stbuf.st_mtim.tv_sec);
|
||||
contentlengthlen = utos(contentlength, sizeof(contentlength), stbuf.st_size);
|
||||
contentlengthlen =
|
||||
utos(contentlength, sizeof(contentlength), (uint64_t)stbuf.st_size);
|
||||
|
||||
nva[nvlen].value = (uint8_t *)contentlength;
|
||||
nva[nvlen].valuelen = contentlengthlen;
|
||||
@@ -1138,7 +1151,7 @@ static int do_read(connection *conn) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
nproc = nghttp2_session_mem_recv(conn->session, buf, nread);
|
||||
nproc = nghttp2_session_mem_recv(conn->session, buf, (size_t)nread);
|
||||
|
||||
if (nproc < 0) {
|
||||
return -1;
|
||||
@@ -1193,13 +1206,13 @@ static int do_write(connection *conn) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (io_buf_left(&conn->buf) < n) {
|
||||
if (io_buf_left(&conn->buf) < (size_t)n) {
|
||||
conn->cache = b;
|
||||
conn->cachelen = n;
|
||||
conn->cachelen = (size_t)n;
|
||||
break;
|
||||
}
|
||||
|
||||
io_buf_add(&conn->buf, b, n);
|
||||
io_buf_add(&conn->buf, b, (size_t)n);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1309,14 +1322,6 @@ int main(int argc, char **argv) {
|
||||
nghttp2_session_callbacks_set_send_data_callback(shared_callbacks,
|
||||
send_data_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);
|
||||
|
||||
rv = io_loop_add(&loop, serv.fd, EPOLLIN, &serv);
|
||||
|
||||
if (rv != 0) {
|
||||
@@ -1333,7 +1338,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',
|
||||
@@ -26,6 +28,7 @@ HEADERS = [
|
||||
"accept-language",
|
||||
"cache-control",
|
||||
"user-agent",
|
||||
"date",
|
||||
# disallowed h1 headers
|
||||
'connection',
|
||||
'keep-alive',
|
||||
@@ -34,70 +37,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[{}]) {{'''.format(size - 1)
|
||||
for c in sorted(ents.keys()):
|
||||
headers = sorted(ents[c])
|
||||
print '''\
|
||||
case '{}':'''.format(c)
|
||||
for k in headers:
|
||||
print '''\
|
||||
if (util::streq_l("{}", name, {})) {{
|
||||
return {};
|
||||
}}'''.format(k[:-1], size - 1, to_enum_hd(k))
|
||||
print '''\
|
||||
break;'''
|
||||
print '''\
|
||||
}
|
||||
break;'''
|
||||
print '''\
|
||||
}
|
||||
return -1;
|
||||
}'''
|
||||
|
||||
if __name__ == '__main__':
|
||||
gen_enum()
|
||||
print ''
|
||||
gen_index_header()
|
||||
gentokenlookup(HEADERS, 'HD')
|
||||
|
||||
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')
|
||||
127
gennghttpxfun.py
Executable file
127
gennghttpxfun.py
Executable file
@@ -0,0 +1,127 @@
|
||||
#!/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",
|
||||
"request-phase-file",
|
||||
"response-phase-file",
|
||||
"accept-proxy-protocol",
|
||||
"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)
|
||||
53
help2rst.py
53
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,9 @@ def help2man(infile):
|
||||
break
|
||||
description.append(line)
|
||||
|
||||
print '''
|
||||
print('''
|
||||
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
|
||||
|
||||
.. program:: {cmdname}
|
||||
|
||||
{cmdname}(1)
|
||||
@@ -77,7 +80,7 @@ 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
|
||||
@@ -86,16 +89,16 @@ DESCRIPTION
|
||||
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 == '--':
|
||||
@@ -103,22 +106,22 @@ DESCRIPTION
|
||||
continue
|
||||
|
||||
if in_footer:
|
||||
print line.strip()
|
||||
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
|
||||
|
||||
@@ -126,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
|
||||
|
||||
@@ -138,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()[:-1]
|
||||
print '{}'.format(subsec)
|
||||
print '{}'.format('~' * len(subsec))
|
||||
print ''
|
||||
print('{}'.format(subsec))
|
||||
print('{}'.format('~' * len(subsec)))
|
||||
print()
|
||||
continue
|
||||
|
||||
print line.strip()
|
||||
print(line.strip())
|
||||
|
||||
def format_text(text):
|
||||
# escape *
|
||||
@@ -184,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())
|
||||
|
||||
@@ -30,14 +30,16 @@ EXTRA_DIST = \
|
||||
server.crt \
|
||||
alt-server.key \
|
||||
alt-server.crt \
|
||||
setenv
|
||||
setenv \
|
||||
req-set-header.rb \
|
||||
resp-set-header.rb \
|
||||
return.rb
|
||||
|
||||
.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,12 +2,15 @@ package nghttp2
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/bradfitz/http2/hpack"
|
||||
"golang.org/x/net/websocket"
|
||||
"io"
|
||||
"net/http"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TestH1H1PlainGET tests whether simple HTTP/1 GET request works.
|
||||
@@ -50,6 +53,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) {
|
||||
@@ -117,6 +141,7 @@ func TestH1H1GracefulShutdown(t *testing.T) {
|
||||
}
|
||||
|
||||
st.cmd.Process.Signal(syscall.SIGQUIT)
|
||||
time.Sleep(150 * time.Millisecond)
|
||||
|
||||
res, err = st.http1(requestParam{
|
||||
name: "TestH1H1GracefulShutdown-2",
|
||||
@@ -141,7 +166,7 @@ func TestH1H1GracefulShutdown(t *testing.T) {
|
||||
|
||||
// TestH1H1HostRewrite tests that server rewrites Host header field
|
||||
func TestH1H1HostRewrite(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
st := newServerTester([]string{"--host-rewrite"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("request-host", r.Host)
|
||||
})
|
||||
defer st.Close()
|
||||
@@ -189,7 +214,7 @@ func TestH1H1HTTP10(t *testing.T) {
|
||||
// field using actual backend server even if --no-http-rewrite is
|
||||
// used.
|
||||
func TestH1H1HTTP10NoHostRewrite(t *testing.T) {
|
||||
st := newServerTester([]string{"--no-host-rewrite"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("request-host", r.Host)
|
||||
})
|
||||
defer st.Close()
|
||||
@@ -246,6 +271,206 @@ func TestH1H1RequestTrailer(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H1ReqPhaseSetHeader tests mruby request phase hook
|
||||
// modifies request header fields.
|
||||
func TestH1H1ReqPhaseSetHeader(t *testing.T) {
|
||||
st := newServerTester([]string{"--request-phase-file=" + testDir + "/req-set-header.rb"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if got, want := r.Header.Get("User-Agent"), "mruby"; got != want {
|
||||
t.Errorf("User-Agent = %v; want %v", got, want)
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http1(requestParam{
|
||||
name: "TestH1H1ReqPhaseSetHeader",
|
||||
})
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H1ReqPhaseReturn tests mruby request phase hook returns
|
||||
// custom response.
|
||||
func TestH1H1ReqPhaseReturn(t *testing.T) {
|
||||
st := newServerTester([]string{"--request-phase-file=" + testDir + "/return.rb"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatalf("request should not be forwarded")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http1(requestParam{
|
||||
name: "TestH1H1ReqPhaseReturn",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http1() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.status, 404; got != want {
|
||||
t.Errorf("status = %v; want %v", got, want)
|
||||
}
|
||||
|
||||
hdtests := []struct {
|
||||
k, v string
|
||||
}{
|
||||
{"content-length", "11"},
|
||||
{"from", "mruby"},
|
||||
}
|
||||
for _, tt := range hdtests {
|
||||
if got, want := res.header.Get(tt.k), tt.v; got != want {
|
||||
t.Errorf("%v = %v; want %v", tt.k, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
if got, want := string(res.body), "Hello World"; got != want {
|
||||
t.Errorf("body = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H1RespPhaseSetHeader tests mruby response phase hook modifies
|
||||
// response header fields.
|
||||
func TestH1H1RespPhaseSetHeader(t *testing.T) {
|
||||
st := newServerTester([]string{"--response-phase-file=" + testDir + "/resp-set-header.rb"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http1(requestParam{
|
||||
name: "TestH1H1RespPhaseSetHeader",
|
||||
})
|
||||
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("alpha"), "bravo"; got != want {
|
||||
t.Errorf("alpha = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H1RespPhaseReturn tests mruby response phase hook returns
|
||||
// custom response.
|
||||
func TestH1H1RespPhaseReturn(t *testing.T) {
|
||||
st := newServerTester([]string{"--response-phase-file=" + testDir + "/return.rb"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http1(requestParam{
|
||||
name: "TestH1H1RespPhaseReturn",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http1() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.status, 404; got != want {
|
||||
t.Errorf("status = %v; want %v", got, want)
|
||||
}
|
||||
|
||||
hdtests := []struct {
|
||||
k, v string
|
||||
}{
|
||||
{"content-length", "11"},
|
||||
{"from", "mruby"},
|
||||
}
|
||||
for _, tt := range hdtests {
|
||||
if got, want := res.header.Get(tt.k), tt.v; got != want {
|
||||
t.Errorf("%v = %v; want %v", tt.k, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
if got, want := string(res.body), "Hello World"; got != want {
|
||||
t.Errorf("body = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H2ConnectFailure tests that server handles the situation that
|
||||
// connection attempt to HTTP/2 backend failed.
|
||||
func TestH1H2ConnectFailure(t *testing.T) {
|
||||
@@ -320,7 +545,7 @@ func TestH1H2HTTP10(t *testing.T) {
|
||||
// field using actual backend server even if --no-http-rewrite is
|
||||
// used.
|
||||
func TestH1H2HTTP10NoHostRewrite(t *testing.T) {
|
||||
st := newServerTester([]string{"--http2-bridge", "--no-host-rewrite"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("request-host", r.Host)
|
||||
})
|
||||
defer st.Close()
|
||||
@@ -438,3 +663,73 @@ func TestH1H2NoVia(t *testing.T) {
|
||||
t.Errorf("Via: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H2ReqPhaseReturn tests mruby request phase hook returns
|
||||
// custom response.
|
||||
func TestH1H2ReqPhaseReturn(t *testing.T) {
|
||||
st := newServerTester([]string{"--http2-bridge", "--request-phase-file=" + testDir + "/return.rb"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatalf("request should not be forwarded")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http1(requestParam{
|
||||
name: "TestH1H2ReqPhaseReturn",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http1() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.status, 404; got != want {
|
||||
t.Errorf("status = %v; want %v", got, want)
|
||||
}
|
||||
|
||||
hdtests := []struct {
|
||||
k, v string
|
||||
}{
|
||||
{"content-length", "11"},
|
||||
{"from", "mruby"},
|
||||
}
|
||||
for _, tt := range hdtests {
|
||||
if got, want := res.header.Get(tt.k), tt.v; got != want {
|
||||
t.Errorf("%v = %v; want %v", tt.k, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
if got, want := string(res.body), "Hello World"; got != want {
|
||||
t.Errorf("body = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H2RespPhaseReturn tests mruby response phase hook returns
|
||||
// custom response.
|
||||
func TestH1H2RespPhaseReturn(t *testing.T) {
|
||||
st := newServerTester([]string{"--http2-bridge", "--response-phase-file=" + testDir + "/return.rb"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http1(requestParam{
|
||||
name: "TestH1H2RespPhaseReturn",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http1() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.status, 404; got != want {
|
||||
t.Errorf("status = %v; want %v", got, want)
|
||||
}
|
||||
|
||||
hdtests := []struct {
|
||||
k, v string
|
||||
}{
|
||||
{"content-length", "11"},
|
||||
{"from", "mruby"},
|
||||
}
|
||||
for _, tt := range hdtests {
|
||||
if got, want := res.header.Get(tt.k), tt.v; got != want {
|
||||
t.Errorf("%v = %v; want %v", tt.k, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
if got, want := string(res.body), "Hello World"; got != want {
|
||||
t.Errorf("body = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TestH2H1PlainGET tests whether simple HTTP/2 GET request works.
|
||||
@@ -195,7 +196,7 @@ func TestH2H1NoVia(t *testing.T) {
|
||||
|
||||
// TestH2H1HostRewrite tests that server rewrites host header field
|
||||
func TestH2H1HostRewrite(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
st := newServerTester([]string{"--host-rewrite"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("request-host", r.Host)
|
||||
})
|
||||
defer st.Close()
|
||||
@@ -217,7 +218,7 @@ func TestH2H1HostRewrite(t *testing.T) {
|
||||
// TestH2H1NoHostRewrite tests that server does not rewrite host
|
||||
// header field
|
||||
func TestH2H1NoHostRewrite(t *testing.T) {
|
||||
st := newServerTester([]string{"--no-host-rewrite"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("request-host", r.Host)
|
||||
})
|
||||
defer st.Close()
|
||||
@@ -404,6 +405,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) {
|
||||
@@ -490,6 +511,28 @@ 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) {
|
||||
@@ -558,6 +601,160 @@ func TestH2H1RequestTrailer(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1ReqPhaseSetHeader tests mruby request phase hook
|
||||
// modifies request header fields.
|
||||
func TestH2H1ReqPhaseSetHeader(t *testing.T) {
|
||||
st := newServerTester([]string{"--request-phase-file=" + testDir + "/req-set-header.rb"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if got, want := r.Header.Get("User-Agent"), "mruby"; got != want {
|
||||
t.Errorf("User-Agent = %v; want %v", got, want)
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1ReqPhaseSetHeader",
|
||||
})
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1ReqPhaseReturn tests mruby request phase hook returns
|
||||
// custom response.
|
||||
func TestH2H1ReqPhaseReturn(t *testing.T) {
|
||||
st := newServerTester([]string{"--request-phase-file=" + testDir + "/return.rb"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatalf("request should not be forwarded")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1ReqPhaseReturn",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.status, 404; got != want {
|
||||
t.Errorf("status = %v; want %v", got, want)
|
||||
}
|
||||
|
||||
hdtests := []struct {
|
||||
k, v string
|
||||
}{
|
||||
{"content-length", "11"},
|
||||
{"from", "mruby"},
|
||||
}
|
||||
for _, tt := range hdtests {
|
||||
if got, want := res.header.Get(tt.k), tt.v; got != want {
|
||||
t.Errorf("%v = %v; want %v", tt.k, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
if got, want := string(res.body), "Hello World"; got != want {
|
||||
t.Errorf("body = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1RespPhaseSetHeader tests mruby response phase hook modifies
|
||||
// response header fields.
|
||||
func TestH2H1RespPhaseSetHeader(t *testing.T) {
|
||||
st := newServerTester([]string{"--response-phase-file=" + testDir + "/resp-set-header.rb"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1RespPhaseSetHeader",
|
||||
})
|
||||
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("alpha"), "bravo"; got != want {
|
||||
t.Errorf("alpha = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1RespPhaseReturn tests mruby response phase hook returns
|
||||
// custom response.
|
||||
func TestH2H1RespPhaseReturn(t *testing.T) {
|
||||
st := newServerTester([]string{"--response-phase-file=" + testDir + "/return.rb"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1RespPhaseReturn",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.status, 404; got != want {
|
||||
t.Errorf("status = %v; want %v", got, want)
|
||||
}
|
||||
|
||||
hdtests := []struct {
|
||||
k, v string
|
||||
}{
|
||||
{"content-length", "11"},
|
||||
{"from", "mruby"},
|
||||
}
|
||||
for _, tt := range hdtests {
|
||||
if got, want := res.header.Get(tt.k), tt.v; got != want {
|
||||
t.Errorf("%v = %v; want %v", tt.k, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
if got, want := string(res.body), "Hello World"; got != want {
|
||||
t.Errorf("body = %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) {})
|
||||
@@ -567,7 +764,7 @@ func TestH2H1Upgrade(t *testing.T) {
|
||||
name: "TestH2H1Upgrade",
|
||||
header: []hpack.HeaderField{
|
||||
pair("Connection", "Upgrade, HTTP2-Settings"),
|
||||
pair("Upgrade", "h2c-14"),
|
||||
pair("Upgrade", "h2c"),
|
||||
pair("HTTP2-Settings", "AAMAAABkAAQAAP__"),
|
||||
},
|
||||
})
|
||||
@@ -591,6 +788,362 @@ func TestH2H1Upgrade(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1ProxyProtocolV1TCP4 tests PROXY protocol version 1
|
||||
// containing TCP4 entry is accepted and X-Forwarded-For contains
|
||||
// advertised src address.
|
||||
func TestH2H1ProxyProtocolV1TCP4(t *testing.T) {
|
||||
st := newServerTester([]string{"--accept-proxy-protocol", "--add-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if got, want := r.Header.Get("X-Forwarded-For"), "192.168.0.2"; got != want {
|
||||
t.Errorf("X-Forwarded-For: %v; want %v", got, want)
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
st.conn.Write([]byte("PROXY TCP4 192.168.0.2 192.168.0.100 12345 8080\r\n"))
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1ProxyProtocolV1TCP4",
|
||||
})
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1ProxyProtocolV1TCP6 tests PROXY protocol version 1
|
||||
// containing TCP6 entry is accepted and X-Forwarded-For contains
|
||||
// advertised src address.
|
||||
func TestH2H1ProxyProtocolV1TCP6(t *testing.T) {
|
||||
st := newServerTester([]string{"--accept-proxy-protocol", "--add-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if got, want := r.Header.Get("X-Forwarded-For"), "2001:0db8:85a3:0000:0000:8a2e:0370:7334"; got != want {
|
||||
t.Errorf("X-Forwarded-For: %v; want %v", got, want)
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
st.conn.Write([]byte("PROXY TCP6 2001:0db8:85a3:0000:0000:8a2e:0370:7334 ::1 12345 8080\r\n"))
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1ProxyProtocolV1TCP6",
|
||||
})
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1ProxyProtocolV1Unknown tests PROXY protocol version 1
|
||||
// containing UNKNOWN entry is accepted.
|
||||
func TestH2H1ProxyProtocolV1Unknown(t *testing.T) {
|
||||
st := newServerTester([]string{"--accept-proxy-protocol", "--add-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if got, notWant := r.Header.Get("X-Forwarded-For"), "192.168.0.2"; got == notWant {
|
||||
t.Errorf("X-Forwarded-For: %v")
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
st.conn.Write([]byte("PROXY UNKNOWN 192.168.0.2 192.168.0.100 12345 8080\r\n"))
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1ProxyProtocolV1Unknown",
|
||||
})
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1ProxyProtocolV1JustUnknown tests PROXY protocol version 1
|
||||
// containing only "PROXY UNKNOWN" is accepted.
|
||||
func TestH2H1ProxyProtocolV1JustUnknown(t *testing.T) {
|
||||
st := newServerTester([]string{"--accept-proxy-protocol", "--add-x-forwarded-for"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
st.conn.Write([]byte("PROXY UNKNOWN\r\n"))
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1ProxyProtocolV1JustUnknown",
|
||||
})
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1ProxyProtocolV1TooLongLine tests PROXY protocol version 1
|
||||
// line longer than 107 bytes must be rejected
|
||||
func TestH2H1ProxyProtocolV1TooLongLine(t *testing.T) {
|
||||
st := newServerTester([]string{"--accept-proxy-protocol", "--add-x-forwarded-for"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
st.conn.Write([]byte("PROXY UNKNOWN ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 65535 655350\r\n"))
|
||||
|
||||
_, err := st.http2(requestParam{
|
||||
name: "TestH2H1ProxyProtocolV1TooLongLine",
|
||||
})
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf("connection was not terminated")
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1ProxyProtocolV1BadLineEnd tests that PROXY protocol version
|
||||
// 1 line ending without \r\n should be rejected.
|
||||
func TestH2H1ProxyProtocolV1BadLineEnd(t *testing.T) {
|
||||
st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
st.conn.Write([]byte("PROXY TCP6 ::1 ::1 12345 8080\r \n"))
|
||||
|
||||
_, err := st.http2(requestParam{
|
||||
name: "TestH2H1ProxyProtocolV1BadLineEnd",
|
||||
})
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf("connection was not terminated")
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1ProxyProtocolV1NoEnd tests that PROXY protocol version 1
|
||||
// line containing no \r\n should be rejected.
|
||||
func TestH2H1ProxyProtocolV1NoEnd(t *testing.T) {
|
||||
st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
st.conn.Write([]byte("PROXY TCP6 ::1 ::1 12345 8080"))
|
||||
|
||||
_, err := st.http2(requestParam{
|
||||
name: "TestH2H1ProxyProtocolV1NoEnd",
|
||||
})
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf("connection was not terminated")
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1ProxyProtocolV1EmbeddedNULL tests that PROXY protocol
|
||||
// version 1 line containing NULL character should be rejected.
|
||||
func TestH2H1ProxyProtocolV1EmbeddedNULL(t *testing.T) {
|
||||
st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
b := []byte("PROXY TCP6 ::1*foo ::1 12345 8080\r\n")
|
||||
b[14] = 0
|
||||
st.conn.Write(b)
|
||||
|
||||
_, err := st.http2(requestParam{
|
||||
name: "TestH2H1ProxyProtocolV1EmbeddedNULL",
|
||||
})
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf("connection was not terminated")
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1ProxyProtocolV1MissingSrcPort tests that PROXY protocol
|
||||
// version 1 line without src port should be rejected.
|
||||
func TestH2H1ProxyProtocolV1MissingSrcPort(t *testing.T) {
|
||||
st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
st.conn.Write([]byte("PROXY TCP6 ::1 ::1 8080\r\n"))
|
||||
|
||||
_, err := st.http2(requestParam{
|
||||
name: "TestH2H1ProxyProtocolV1MissingSrcPort",
|
||||
})
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf("connection was not terminated")
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1ProxyProtocolV1MissingDstPort tests that PROXY protocol
|
||||
// version 1 line without dst port should be rejected.
|
||||
func TestH2H1ProxyProtocolV1MissingDstPort(t *testing.T) {
|
||||
st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
st.conn.Write([]byte("PROXY TCP6 ::1 ::1 12345 \r\n"))
|
||||
|
||||
_, err := st.http2(requestParam{
|
||||
name: "TestH2H1ProxyProtocolV1MissingDstPort",
|
||||
})
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf("connection was not terminated")
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1ProxyProtocolV1InvalidSrcPort tests that PROXY protocol
|
||||
// containing invalid src port should be rejected.
|
||||
func TestH2H1ProxyProtocolV1InvalidSrcPort(t *testing.T) {
|
||||
st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
st.conn.Write([]byte("PROXY TCP6 ::1 ::1 123x 8080\r\n"))
|
||||
|
||||
_, err := st.http2(requestParam{
|
||||
name: "TestH2H1ProxyProtocolV1InvalidSrcPort",
|
||||
})
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf("connection was not terminated")
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1ProxyProtocolV1InvalidDstPort tests that PROXY protocol
|
||||
// containing invalid dst port should be rejected.
|
||||
func TestH2H1ProxyProtocolV1InvalidDstPort(t *testing.T) {
|
||||
st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
st.conn.Write([]byte("PROXY TCP6 ::1 ::1 123456 80x\r\n"))
|
||||
|
||||
_, err := st.http2(requestParam{
|
||||
name: "TestH2H1ProxyProtocolV1InvalidDstPort",
|
||||
})
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf("connection was not terminated")
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1ProxyProtocolV1LeadingZeroPort tests that PROXY protocol
|
||||
// version 1 line with non zero port with leading zero should be
|
||||
// rejected.
|
||||
func TestH2H1ProxyProtocolV1LeadingZeroPort(t *testing.T) {
|
||||
st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
st.conn.Write([]byte("PROXY TCP6 ::1 ::1 03000 8080\r\n"))
|
||||
|
||||
_, err := st.http2(requestParam{
|
||||
name: "TestH2H1ProxyProtocolV1LeadingZeroPort",
|
||||
})
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf("connection was not terminated")
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1ProxyProtocolV1TooLargeSrcPort tests that PROXY protocol
|
||||
// containing too large src port should be rejected.
|
||||
func TestH2H1ProxyProtocolV1TooLargeSrcPort(t *testing.T) {
|
||||
st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
st.conn.Write([]byte("PROXY TCP6 ::1 ::1 65536 8080\r\n"))
|
||||
|
||||
_, err := st.http2(requestParam{
|
||||
name: "TestH2H1ProxyProtocolV1TooLargeSrcPort",
|
||||
})
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf("connection was not terminated")
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1ProxyProtocolV1TooLargeDstPort tests that PROXY protocol
|
||||
// containing too large dst port should be rejected.
|
||||
func TestH2H1ProxyProtocolV1TooLargeDstPort(t *testing.T) {
|
||||
st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
st.conn.Write([]byte("PROXY TCP6 ::1 ::1 12345 65536\r\n"))
|
||||
|
||||
_, err := st.http2(requestParam{
|
||||
name: "TestH2H1ProxyProtocolV1TooLargeDstPort",
|
||||
})
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf("connection was not terminated")
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1ProxyProtocolV1InvalidSrcAddr tests that PROXY protocol
|
||||
// containing invalid src addr should be rejected.
|
||||
func TestH2H1ProxyProtocolV1InvalidSrcAddr(t *testing.T) {
|
||||
st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
st.conn.Write([]byte("PROXY TCP6 192.168.0.1 ::1 12345 8080\r\n"))
|
||||
|
||||
_, err := st.http2(requestParam{
|
||||
name: "TestH2H1ProxyProtocolV1InvalidSrcAddr",
|
||||
})
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf("connection was not terminated")
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1ProxyProtocolV1InvalidDstAddr tests that PROXY protocol
|
||||
// containing invalid dst addr should be rejected.
|
||||
func TestH2H1ProxyProtocolV1InvalidDstAddr(t *testing.T) {
|
||||
st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
st.conn.Write([]byte("PROXY TCP6 ::1 192.168.0.1 12345 8080\r\n"))
|
||||
|
||||
_, err := st.http2(requestParam{
|
||||
name: "TestH2H1ProxyProtocolV1InvalidDstAddr",
|
||||
})
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf("connection was not terminated")
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1ProxyProtocolV1InvalidProtoFamily tests that PROXY protocol
|
||||
// containing invalid protocol family should be rejected.
|
||||
func TestH2H1ProxyProtocolV1InvalidProtoFamily(t *testing.T) {
|
||||
st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
st.conn.Write([]byte("PROXY UNIX ::1 ::1 12345 8080\r\n"))
|
||||
|
||||
_, err := st.http2(requestParam{
|
||||
name: "TestH2H1ProxyProtocolV1InvalidProtoFamily",
|
||||
})
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf("connection was not terminated")
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1ProxyProtocolV1InvalidID tests that PROXY protocol
|
||||
// containing invalid PROXY protocol version 1 ID should be rejected.
|
||||
func TestH2H1ProxyProtocolV1InvalidID(t *testing.T) {
|
||||
st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
st.conn.Write([]byte("PR0XY TCP6 ::1 ::1 12345 8080\r\n"))
|
||||
|
||||
_, err := st.http2(requestParam{
|
||||
name: "TestH2H1ProxyProtocolV1InvalidID",
|
||||
})
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf("connection was not terminated")
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1GracefulShutdown tests graceful shutdown.
|
||||
func TestH2H1GracefulShutdown(t *testing.T) {
|
||||
st := newServerTester(nil, t, noopHandler)
|
||||
@@ -623,6 +1176,7 @@ func TestH2H1GracefulShutdown(t *testing.T) {
|
||||
|
||||
// send SIGQUIT signal to nghttpx to perform graceful shutdown
|
||||
st.cmd.Process.Signal(syscall.SIGQUIT)
|
||||
time.Sleep(150 * time.Millisecond)
|
||||
|
||||
// after signal, finish request body
|
||||
if err := st.fr.WriteData(1, true, nil); err != nil {
|
||||
@@ -731,7 +1285,7 @@ func TestH2H2ConnectFailure(t *testing.T) {
|
||||
|
||||
// TestH2H2HostRewrite tests that server rewrites host header field
|
||||
func TestH2H2HostRewrite(t *testing.T) {
|
||||
st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
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()
|
||||
@@ -753,7 +1307,7 @@ func TestH2H2HostRewrite(t *testing.T) {
|
||||
// TestH2H2NoHostRewrite tests that server does not rewrite host
|
||||
// header field
|
||||
func TestH2H2NoHostRewrite(t *testing.T) {
|
||||
st := newServerTester([]string{"--http2-bridge", "--no-host-rewrite"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("request-host", r.Host)
|
||||
})
|
||||
defer st.Close()
|
||||
@@ -771,3 +1325,95 @@ func TestH2H2NoHostRewrite(t *testing.T) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H2ReqPhaseReturn tests mruby request phase hook returns
|
||||
// custom response.
|
||||
func TestH2H2ReqPhaseReturn(t *testing.T) {
|
||||
st := newServerTester([]string{"--http2-bridge", "--request-phase-file=" + testDir + "/return.rb"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatalf("request should not be forwarded")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H2ReqPhaseReturn",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.status, 404; got != want {
|
||||
t.Errorf("status = %v; want %v", got, want)
|
||||
}
|
||||
|
||||
hdtests := []struct {
|
||||
k, v string
|
||||
}{
|
||||
{"content-length", "11"},
|
||||
{"from", "mruby"},
|
||||
}
|
||||
for _, tt := range hdtests {
|
||||
if got, want := res.header.Get(tt.k), tt.v; got != want {
|
||||
t.Errorf("%v = %v; want %v", tt.k, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
if got, want := string(res.body), "Hello World"; got != want {
|
||||
t.Errorf("body = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H2RespPhaseReturn tests mruby response phase hook returns
|
||||
// custom response.
|
||||
func TestH2H2RespPhaseReturn(t *testing.T) {
|
||||
st := newServerTester([]string{"--http2-bridge", "--response-phase-file=" + testDir + "/return.rb"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H2RespPhaseReturn",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.status, 404; got != want {
|
||||
t.Errorf("status = %v; want %v", got, want)
|
||||
}
|
||||
|
||||
hdtests := []struct {
|
||||
k, v string
|
||||
}{
|
||||
{"content-length", "11"},
|
||||
{"from", "mruby"},
|
||||
}
|
||||
for _, tt := range hdtests {
|
||||
if got, want := res.header.Get(tt.k), tt.v; got != want {
|
||||
t.Errorf("%v = %v; want %v", tt.k, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
if got, want := string(res.body), "Hello World"; got != want {
|
||||
t.Errorf("body = %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"
|
||||
)
|
||||
@@ -170,6 +170,180 @@ func TestS3H1NoVia(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
// TestS3H1ReqPhaseSetHeader tests mruby request phase hook
|
||||
// modifies request header fields.
|
||||
func TestS3H1ReqPhaseSetHeader(t *testing.T) {
|
||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--request-phase-file=" + testDir + "/req-set-header.rb"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if got, want := r.Header.Get("User-Agent"), "mruby"; got != want {
|
||||
t.Errorf("User-Agent = %v; want %v", got, want)
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.spdy(requestParam{
|
||||
name: "TestS3H1ReqPhaseSetHeader",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.spdy() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("status = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestS3H1ReqPhaseReturn tests mruby request phase hook returns
|
||||
// custom response.
|
||||
func TestS3H1ReqPhaseReturn(t *testing.T) {
|
||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--request-phase-file=" + testDir + "/return.rb"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatalf("request should not be forwarded")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.spdy(requestParam{
|
||||
name: "TestS3H1ReqPhaseReturn",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.spdy() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.status, 404; got != want {
|
||||
t.Errorf("status = %v; want %v", got, want)
|
||||
}
|
||||
|
||||
hdtests := []struct {
|
||||
k, v string
|
||||
}{
|
||||
{"content-length", "11"},
|
||||
{"from", "mruby"},
|
||||
}
|
||||
for _, tt := range hdtests {
|
||||
if got, want := res.header.Get(tt.k), tt.v; got != want {
|
||||
t.Errorf("%v = %v; want %v", tt.k, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
if got, want := string(res.body), "Hello World"; got != want {
|
||||
t.Errorf("body = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestS3H1RespPhaseSetHeader tests mruby response phase hook modifies
|
||||
// response header fields.
|
||||
func TestS3H1RespPhaseSetHeader(t *testing.T) {
|
||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--response-phase-file=" + testDir + "/resp-set-header.rb"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.spdy(requestParam{
|
||||
name: "TestS3H1RespPhaseSetHeader",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.spdy() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("status = %v; want %v", got, want)
|
||||
}
|
||||
|
||||
if got, want := res.header.Get("alpha"), "bravo"; got != want {
|
||||
t.Errorf("alpha = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestS3H1RespPhaseReturn tests mruby response phase hook returns
|
||||
// custom response.
|
||||
func TestS3H1RespPhaseReturn(t *testing.T) {
|
||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--response-phase-file=" + testDir + "/return.rb"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.spdy(requestParam{
|
||||
name: "TestS3H1RespPhaseReturn",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.spdy() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.status, 404; got != want {
|
||||
t.Errorf("status = %v; want %v", got, want)
|
||||
}
|
||||
|
||||
hdtests := []struct {
|
||||
k, v string
|
||||
}{
|
||||
{"content-length", "11"},
|
||||
{"from", "mruby"},
|
||||
}
|
||||
for _, tt := range hdtests {
|
||||
if got, want := res.header.Get(tt.k), tt.v; got != want {
|
||||
t.Errorf("%v = %v; want %v", tt.k, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
if got, want := string(res.body), "Hello World"; got != want {
|
||||
t.Errorf("body = %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) {
|
||||
@@ -190,3 +364,73 @@ func TestS3H2ConnectFailure(t *testing.T) {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestS3H2ReqPhaseReturn tests mruby request phase hook returns
|
||||
// custom response.
|
||||
func TestS3H2ReqPhaseReturn(t *testing.T) {
|
||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--http2-bridge", "--request-phase-file=" + testDir + "/return.rb"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatalf("request should not be forwarded")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.spdy(requestParam{
|
||||
name: "TestS3H2ReqPhaseReturn",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.spdy() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.status, 404; got != want {
|
||||
t.Errorf("status = %v; want %v", got, want)
|
||||
}
|
||||
|
||||
hdtests := []struct {
|
||||
k, v string
|
||||
}{
|
||||
{"content-length", "11"},
|
||||
{"from", "mruby"},
|
||||
}
|
||||
for _, tt := range hdtests {
|
||||
if got, want := res.header.Get(tt.k), tt.v; got != want {
|
||||
t.Errorf("%v = %v; want %v", tt.k, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
if got, want := string(res.body), "Hello World"; got != want {
|
||||
t.Errorf("body = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestS3H2RespPhaseReturn tests mruby response phase hook returns
|
||||
// custom response.
|
||||
func TestS3H2RespPhaseReturn(t *testing.T) {
|
||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--http2-bridge", "--response-phase-file=" + testDir + "/return.rb"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.spdy(requestParam{
|
||||
name: "TestS3H2RespPhaseReturn",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.spdy() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.status, 404; got != want {
|
||||
t.Errorf("status = %v; want %v", got, want)
|
||||
}
|
||||
|
||||
hdtests := []struct {
|
||||
k, v string
|
||||
}{
|
||||
{"content-length", "11"},
|
||||
{"from", "mruby"},
|
||||
}
|
||||
for _, tt := range hdtests {
|
||||
if got, want := res.header.Get(tt.k), tt.v; got != want {
|
||||
t.Errorf("%v = %v; want %v", tt.k, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
if got, want := string(res.body), "Hello World"; got != want {
|
||||
t.Errorf("body = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
3
integration-tests/req-set-header.rb
Normal file
3
integration-tests/req-set-header.rb
Normal file
@@ -0,0 +1,3 @@
|
||||
Nghttpx.run do |env|
|
||||
env.req.set_header "User-Agent", "mruby"
|
||||
end
|
||||
3
integration-tests/resp-set-header.rb
Normal file
3
integration-tests/resp-set-header.rb
Normal file
@@ -0,0 +1,3 @@
|
||||
Nghttpx.run do |env|
|
||||
env.resp.set_header "Alpha", "bravo"
|
||||
end
|
||||
8
integration-tests/return.rb
Normal file
8
integration-tests/return.rb
Normal file
@@ -0,0 +1,8 @@
|
||||
Nghttpx.run do |env|
|
||||
resp = env.resp
|
||||
|
||||
resp.clear_headers
|
||||
resp.status = 404
|
||||
resp.add_header "from", "mruby"
|
||||
resp.return "Hello World"
|
||||
end
|
||||
@@ -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"
|
||||
@@ -20,6 +21,7 @@ import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@@ -66,6 +68,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)
|
||||
@@ -79,7 +85,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
|
||||
@@ -198,8 +204,20 @@ func (st *serverTester) Close() {
|
||||
st.conn.Close()
|
||||
}
|
||||
if st.cmd != nil {
|
||||
st.cmd.Process.Kill()
|
||||
st.cmd.Wait()
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
st.cmd.Wait()
|
||||
done <- struct{}{}
|
||||
}()
|
||||
|
||||
st.cmd.Process.Signal(syscall.SIGQUIT)
|
||||
|
||||
select {
|
||||
case <-done:
|
||||
case <-time.After(10 * time.Second):
|
||||
st.cmd.Process.Kill()
|
||||
<-done
|
||||
}
|
||||
}
|
||||
if st.ts != nil {
|
||||
st.ts.Close()
|
||||
@@ -279,6 +297,41 @@ func (cbr *chunkedBodyReader) Read(p []byte) (n int, err error) {
|
||||
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) {
|
||||
method := "GET"
|
||||
if rp.method != "" {
|
||||
@@ -297,7 +350,19 @@ func (st *serverTester) http1(rp requestParam) (*serverResponse, error) {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -10,50 +10,68 @@
|
||||
# Choose your weapons:
|
||||
# Set 'USE_CYTHON=1' to build and install the 'nghttp2.pyd' Python extension.
|
||||
#
|
||||
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 =
|
||||
|
||||
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 -Dssize_t=long -D_U_=""
|
||||
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:x64 -map -debug -incremental:no # -verbose
|
||||
LDFLAGS := -nologo -MAP -debug -incremental:no -opt:ref,icf -MANIFEST # -verbose
|
||||
|
||||
NGHTTP2_SRC = nghttp2_pq.c \
|
||||
|
||||
NGHTTP2_SRC := nghttp2_pq.c \
|
||||
nghttp2_map.c \
|
||||
nghttp2_queue.c \
|
||||
nghttp2_frame.c \
|
||||
@@ -74,10 +92,16 @@ NGHTTP2_SRC = nghttp2_pq.c \
|
||||
nghttp2_mem.c \
|
||||
nghttp2_http.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_OBJ_R := $(addprefix $(OBJ_DIR)/r_, $(notdir $(NGHTTP2_SRC:.c=.obj)))
|
||||
NGHTTP2_OBJ_D := $(addprefix $(OBJ_DIR)/d_, $(notdir $(NGHTTP2_SRC:.c=.obj)))
|
||||
|
||||
all: intro $(OBJ_DIR) $(TARGETS)
|
||||
.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!'
|
||||
|
||||
@@ -95,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
|
||||
link $(LDFLAGS) -dll -out:nghttp2.dll -implib:nghttp2_imp.lib \
|
||||
$(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
|
||||
link $(LDFLAGS) -dll -out:nghttp2d.dll -implib:nghttp2d_imp.lib \
|
||||
$(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' > $@
|
||||
@@ -201,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"
|
||||
@@ -214,20 +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" >> $@
|
||||
|
||||
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!
|
||||
|
||||
@@ -36,7 +36,14 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#if defined(_MSC_VER) && (_MSC_VER < 1800)
|
||||
/* MSVC < 2013 does not have inttypes.h because it is not C99
|
||||
compliant. See compiler macros and version number in
|
||||
https://sourceforge.net/p/predef/wiki/Compilers/ */
|
||||
#include <stdint.h>
|
||||
#else /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */
|
||||
#include <inttypes.h>
|
||||
#endif /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <nghttp2/nghttp2ver.h>
|
||||
@@ -44,8 +51,12 @@ extern "C" {
|
||||
#ifdef NGHTTP2_STATICLIB
|
||||
#define NGHTTP2_EXTERN
|
||||
#elif defined(WIN32)
|
||||
#ifdef BUILDING_NGHTTP2
|
||||
#define NGHTTP2_EXTERN __declspec(dllexport)
|
||||
#else /* !defined(WIN32) */
|
||||
#else /* !BUILDING_NGHTTP2 */
|
||||
#define NGHTTP2_EXTERN __declspec(dllimport)
|
||||
#endif /* !BUILDING_NGHTTP2 */
|
||||
#else /* !defined(WIN32) */
|
||||
#define NGHTTP2_EXTERN
|
||||
#endif /* !defined(WIN32) */
|
||||
|
||||
@@ -55,13 +66,13 @@ extern "C" {
|
||||
* The protocol version identification string of this library
|
||||
* supports. This identifier is used if HTTP/2 is used over TLS.
|
||||
*/
|
||||
#define NGHTTP2_PROTO_VERSION_ID "h2-14"
|
||||
#define NGHTTP2_PROTO_VERSION_ID "h2"
|
||||
/**
|
||||
* @macro
|
||||
*
|
||||
* The length of :macro:`NGHTTP2_PROTO_VERSION_ID`.
|
||||
*/
|
||||
#define NGHTTP2_PROTO_VERSION_ID_LEN 5
|
||||
#define NGHTTP2_PROTO_VERSION_ID_LEN 2
|
||||
|
||||
/**
|
||||
* @macro
|
||||
@@ -72,7 +83,7 @@ extern "C" {
|
||||
* extension <https://tools.ietf.org/html/rfc7301>`_. This is useful
|
||||
* to process incoming ALPN tokens in wire format.
|
||||
*/
|
||||
#define NGHTTP2_PROTO_ALPN "\x5h2-14"
|
||||
#define NGHTTP2_PROTO_ALPN "\x2h2"
|
||||
|
||||
/**
|
||||
* @macro
|
||||
@@ -88,14 +99,14 @@ extern "C" {
|
||||
* supports. This identifier is used if HTTP/2 is used over cleartext
|
||||
* TCP.
|
||||
*/
|
||||
#define NGHTTP2_CLEARTEXT_PROTO_VERSION_ID "h2c-14"
|
||||
#define NGHTTP2_CLEARTEXT_PROTO_VERSION_ID "h2c"
|
||||
|
||||
/**
|
||||
* @macro
|
||||
*
|
||||
* The length of :macro:`NGHTTP2_CLEARTEXT_PROTO_VERSION_ID`.
|
||||
*/
|
||||
#define NGHTTP2_CLEARTEXT_PROTO_VERSION_ID_LEN 6
|
||||
#define NGHTTP2_CLEARTEXT_PROTO_VERSION_ID_LEN 3
|
||||
|
||||
struct nghttp2_session;
|
||||
/**
|
||||
@@ -194,32 +205,17 @@ typedef struct {
|
||||
/**
|
||||
* @macro
|
||||
*
|
||||
* The client connection preface.
|
||||
* The client magic string, which is the first 24 bytes byte string of
|
||||
* client connection preface.
|
||||
*/
|
||||
#define NGHTTP2_CLIENT_CONNECTION_PREFACE "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
|
||||
#define NGHTTP2_CLIENT_MAGIC "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
|
||||
|
||||
/**
|
||||
* @macro
|
||||
*
|
||||
* The length of :macro:`NGHTTP2_CLIENT_CONNECTION_PREFACE`.
|
||||
* The length of :macro:`NGHTTP2_CLIENT_MAGIC`.
|
||||
*/
|
||||
#define NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN 24
|
||||
|
||||
/**
|
||||
* @macro
|
||||
*
|
||||
* The client connection header. This macro is obsoleted by
|
||||
* NGHTTP2_CLIENT_CONNECTION_PREFACE.
|
||||
*/
|
||||
#define NGHTTP2_CLIENT_CONNECTION_HEADER NGHTTP2_CLIENT_CONNECTION_PREFACE
|
||||
|
||||
/**
|
||||
* @macro
|
||||
*
|
||||
* The length of :macro:`NGHTTP2_CLIENT_CONNECTION_HEADER`.
|
||||
*/
|
||||
#define NGHTTP2_CLIENT_CONNECTION_HEADER_LEN \
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN
|
||||
#define NGHTTP2_CLIENT_MAGIC_LEN 24
|
||||
|
||||
/**
|
||||
* @enum
|
||||
@@ -241,8 +237,9 @@ typedef enum {
|
||||
*/
|
||||
NGHTTP2_ERR_UNSUPPORTED_VERSION = -503,
|
||||
/**
|
||||
* Used as a return value from :type:`nghttp2_send_callback` and
|
||||
* :type:`nghttp2_recv_callback` to indicate that the operation
|
||||
* Used as a return value from :type:`nghttp2_send_callback`,
|
||||
* :type:`nghttp2_recv_callback` and
|
||||
* :type:`nghttp2_send_data_callback` to indicate that the operation
|
||||
* would block.
|
||||
*/
|
||||
NGHTTP2_ERR_WOULDBLOCK = -504,
|
||||
@@ -353,8 +350,10 @@ typedef enum {
|
||||
*/
|
||||
NGHTTP2_ERR_PUSH_DISABLED = -528,
|
||||
/**
|
||||
* DATA frame for a given stream has been already submitted and has
|
||||
* not been fully processed yet.
|
||||
* DATA or HEADERS frame for a given stream has been already
|
||||
* submitted and has not been fully processed yet. Application
|
||||
* should wait for the transmission of the previously submitted
|
||||
* frame before submitting another.
|
||||
*/
|
||||
NGHTTP2_ERR_DATA_EXIST = -529,
|
||||
/**
|
||||
@@ -367,6 +366,18 @@ typedef enum {
|
||||
* closed.
|
||||
*/
|
||||
NGHTTP2_ERR_HTTP_HEADER = -531,
|
||||
/**
|
||||
* Violation in HTTP messaging rule.
|
||||
*/
|
||||
NGHTTP2_ERR_HTTP_MESSAGING = -532,
|
||||
/**
|
||||
* Stream was refused.
|
||||
*/
|
||||
NGHTTP2_ERR_REFUSED_STREAM = -533,
|
||||
/**
|
||||
* Unexpected internal error, but recovered.
|
||||
*/
|
||||
NGHTTP2_ERR_INTERNAL = -534,
|
||||
/**
|
||||
* The errors < :enum:`NGHTTP2_ERR_FATAL` mean that the library is
|
||||
* under unexpected condition and processing was terminated (e.g.,
|
||||
@@ -385,10 +396,10 @@ typedef enum {
|
||||
*/
|
||||
NGHTTP2_ERR_CALLBACK_FAILURE = -902,
|
||||
/**
|
||||
* Invalid connection preface was received and further processing is
|
||||
* not possible.
|
||||
* Invalid client magic (see :macro:`NGHTTP2_CLIENT_MAGIC`) was
|
||||
* received and further processing is not possible.
|
||||
*/
|
||||
NGHTTP2_ERR_BAD_PREFACE = -903
|
||||
NGHTTP2_ERR_BAD_CLIENT_MAGIC = -903
|
||||
} nghttp2_error;
|
||||
|
||||
/**
|
||||
@@ -495,21 +506,6 @@ typedef enum {
|
||||
NGHTTP2_CONTINUATION = 0x09
|
||||
} nghttp2_frame_type;
|
||||
|
||||
/**
|
||||
* @enum
|
||||
*
|
||||
* The extension frame types.
|
||||
*
|
||||
* TODO: The assigned frame types were carried from draft-12, and now
|
||||
* actually TBD.
|
||||
*/
|
||||
typedef enum {
|
||||
/**
|
||||
* The ALTSVC extension frame.
|
||||
*/
|
||||
NGHTTP2_EXT_ALTSVC = 0x0a
|
||||
} nghttp2_ext_frame_type;
|
||||
|
||||
/**
|
||||
* @enum
|
||||
*
|
||||
@@ -1078,52 +1074,12 @@ typedef struct {
|
||||
* The pointer to extension payload. The exact pointer type is
|
||||
* determined by hd.type.
|
||||
*
|
||||
* If hd.type == :enum:`NGHTTP2_EXT_ALTSVC`, it is a pointer to
|
||||
* :type:`nghttp2_ext_altsvc`.
|
||||
* Currently, no extension is supported. This is a place holder for
|
||||
* the future extensions.
|
||||
*/
|
||||
void *payload;
|
||||
} nghttp2_extension;
|
||||
|
||||
/**
|
||||
* @struct
|
||||
*
|
||||
* The ALTSVC extension frame payload. It has following members:
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* Protocol ID
|
||||
*/
|
||||
uint8_t *protocol_id;
|
||||
/**
|
||||
* Host
|
||||
*/
|
||||
uint8_t *host;
|
||||
/**
|
||||
* Origin
|
||||
*/
|
||||
uint8_t *origin;
|
||||
/**
|
||||
* The length of |protocol_id|
|
||||
*/
|
||||
size_t protocol_id_len;
|
||||
/**
|
||||
* The length of |host|
|
||||
*/
|
||||
size_t host_len;
|
||||
/**
|
||||
* The length of |origin|
|
||||
*/
|
||||
size_t origin_len;
|
||||
/**
|
||||
* Max-Age
|
||||
*/
|
||||
uint32_t max_age;
|
||||
/**
|
||||
* Port
|
||||
*/
|
||||
uint16_t port;
|
||||
} nghttp2_ext_altsvc;
|
||||
|
||||
/**
|
||||
* @union
|
||||
*
|
||||
@@ -1198,6 +1154,15 @@ typedef union {
|
||||
*
|
||||
* To set this callback to :type:`nghttp2_session_callbacks`, use
|
||||
* `nghttp2_session_callbacks_set_send_callback()`.
|
||||
*
|
||||
* .. note::
|
||||
*
|
||||
* The |length| may be very small. If that is the case, and
|
||||
* application disables Nagle algorithm (``TCP_NODELAY``), then just
|
||||
* writing |data| to the network stack leads to very small packet,
|
||||
* and it is very inefficient. An application should be responsible
|
||||
* to buffer up small chunks of data as necessary to avoid this
|
||||
* situation.
|
||||
*/
|
||||
typedef ssize_t (*nghttp2_send_callback)(nghttp2_session *session,
|
||||
const uint8_t *data, size_t length,
|
||||
@@ -1275,10 +1240,10 @@ typedef ssize_t (*nghttp2_recv_callback)(nghttp2_session *session, uint8_t *buf,
|
||||
/**
|
||||
* @functypedef
|
||||
*
|
||||
* Callback function invoked by `nghttp2_session_recv()` when a frame
|
||||
* is received. The |user_data| pointer is the third argument passed
|
||||
* in to the call to `nghttp2_session_client_new()` or
|
||||
* `nghttp2_session_server_new()`.
|
||||
* Callback function invoked by `nghttp2_session_recv()` and
|
||||
* `nghttp2_session_mem_recv()` when a frame is received. The
|
||||
* |user_data| pointer is the third argument passed in to the call to
|
||||
* `nghttp2_session_client_new()` or `nghttp2_session_server_new()`.
|
||||
*
|
||||
* If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen``
|
||||
* member of their data structure are always ``NULL`` and 0
|
||||
@@ -1313,14 +1278,14 @@ typedef int (*nghttp2_on_frame_recv_callback)(nghttp2_session *session,
|
||||
/**
|
||||
* @functypedef
|
||||
*
|
||||
* Callback function invoked by `nghttp2_session_recv()` when an
|
||||
* invalid non-DATA frame is received. The |error_code| indicates the
|
||||
* error. It is usually one of the :enum:`nghttp2_error_code` but
|
||||
* that is not guaranteed. When this callback function is invoked,
|
||||
* the library automatically submits either RST_STREAM or GOAWAY
|
||||
* frame. The |user_data| pointer is the third argument passed in to
|
||||
* the call to `nghttp2_session_client_new()` or
|
||||
* `nghttp2_session_server_new()`.
|
||||
* Callback function invoked by `nghttp2_session_recv()` and
|
||||
* `nghttp2_session_mem_recv()` when an invalid non-DATA frame is
|
||||
* received. The error is indicated by the |lib_error_code|, which is
|
||||
* one of the values defined in :type:`nghttp2_error`. When this
|
||||
* callback function is invoked, the library automatically submits
|
||||
* either RST_STREAM or GOAWAY frame. The |user_data| pointer is the
|
||||
* third argument passed in to the call to
|
||||
* `nghttp2_session_client_new()` or `nghttp2_session_server_new()`.
|
||||
*
|
||||
* If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen``
|
||||
* member of their data structure are always ``NULL`` and 0
|
||||
@@ -1328,14 +1293,14 @@ typedef int (*nghttp2_on_frame_recv_callback)(nghttp2_session *session,
|
||||
*
|
||||
* The implementation of this function must return 0 if it succeeds.
|
||||
* If nonzero is returned, it is treated as fatal error and
|
||||
* `nghttp2_session_recv()` and `nghttp2_session_send()` functions
|
||||
* `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions
|
||||
* immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
|
||||
*
|
||||
* To set this callback to :type:`nghttp2_session_callbacks`, use
|
||||
* `nghttp2_session_callbacks_set_on_invalid_frame_recv_callback()`.
|
||||
*/
|
||||
typedef int (*nghttp2_on_invalid_frame_recv_callback)(
|
||||
nghttp2_session *session, const nghttp2_frame *frame, uint32_t error_code,
|
||||
nghttp2_session *session, const nghttp2_frame *frame, int lib_error_code,
|
||||
void *user_data);
|
||||
|
||||
/**
|
||||
@@ -1361,7 +1326,7 @@ typedef int (*nghttp2_on_invalid_frame_recv_callback)(
|
||||
* region included in the input bytes.
|
||||
*
|
||||
* The implementation of this function must return 0 if it succeeds.
|
||||
* If nonzero is returned, it is treated as fatal error and
|
||||
* If nonzero is returned, it is treated as fatal error, and
|
||||
* `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions
|
||||
* immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
|
||||
*
|
||||
@@ -1384,7 +1349,7 @@ typedef int (*nghttp2_on_data_chunk_recv_callback)(nghttp2_session *session,
|
||||
*
|
||||
* The implementation of this function must return 0 if it succeeds.
|
||||
* If nonzero is returned, it is treated as fatal error and
|
||||
* `nghttp2_session_recv()` and `nghttp2_session_send()` functions
|
||||
* `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions
|
||||
* immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
|
||||
*
|
||||
* To set this callback to :type:`nghttp2_session_callbacks`, use
|
||||
@@ -1403,7 +1368,7 @@ typedef int (*nghttp2_before_frame_send_callback)(nghttp2_session *session,
|
||||
*
|
||||
* The implementation of this function must return 0 if it succeeds.
|
||||
* If nonzero is returned, it is treated as fatal error and
|
||||
* `nghttp2_session_recv()` and `nghttp2_session_send()` functions
|
||||
* `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions
|
||||
* immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
|
||||
*
|
||||
* To set this callback to :type:`nghttp2_session_callbacks`, use
|
||||
@@ -1425,7 +1390,7 @@ typedef int (*nghttp2_on_frame_send_callback)(nghttp2_session *session,
|
||||
*
|
||||
* The implementation of this function must return 0 if it succeeds.
|
||||
* If nonzero is returned, it is treated as fatal error and
|
||||
* `nghttp2_session_recv()` and `nghttp2_session_send()` functions
|
||||
* `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions
|
||||
* immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
|
||||
*
|
||||
* `nghttp2_session_get_stream_user_data()` can be used to get
|
||||
@@ -1455,8 +1420,9 @@ typedef int (*nghttp2_on_frame_not_send_callback)(nghttp2_session *session,
|
||||
*
|
||||
* The implementation of this function must return 0 if it succeeds.
|
||||
* If nonzero is returned, it is treated as fatal error and
|
||||
* `nghttp2_session_recv()` and `nghttp2_session_send()` functions
|
||||
* immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
|
||||
* `nghttp2_session_recv()`, `nghttp2_session_mem_recv()`,
|
||||
* `nghttp2_session_send()`, and `nghttp2_session_mem_send()`
|
||||
* functions immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
|
||||
*
|
||||
* To set this callback to :type:`nghttp2_session_callbacks`, use
|
||||
* `nghttp2_session_callbacks_set_on_stream_close_callback()`.
|
||||
@@ -1498,13 +1464,26 @@ typedef int (*nghttp2_on_stream_close_callback)(nghttp2_session *session,
|
||||
* frame with ``frame->headers.cat == NGHTTP2_HCAT_HEADERS``
|
||||
* containing final response headers (non-1xx status code). The
|
||||
* trailer headers also has ``frame->headers.cat ==
|
||||
* NGHTTP2_HCAT_HEADERS`` which does not containg any status code.
|
||||
* NGHTTP2_HCAT_HEADERS`` which does not contain any status code.
|
||||
*
|
||||
* The implementation of this function must return 0 if it succeeds or
|
||||
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. If nonzero value other than
|
||||
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned, it is treated as
|
||||
* if :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned. If
|
||||
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned,
|
||||
* Returning :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close
|
||||
* the stream (promised stream if frame is PUSH_PROMISE) by issuing
|
||||
* RST_STREAM with :enum:`NGHTTP2_INTERNAL_ERROR`. In this case,
|
||||
* :type:`nghttp2_on_header_callback` and
|
||||
* :type:`nghttp2_on_frame_recv_callback` will not be invoked. If a
|
||||
* different error code is desirable, use
|
||||
* `nghttp2_submit_rst_stream()` with a desired error code and then
|
||||
* return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. Again, use
|
||||
* ``frame->push_promise.promised_stream_id`` as stream_id parameter
|
||||
* in `nghttp2_submit_rst_stream()` if frame is PUSH_PROMISE.
|
||||
*
|
||||
* The implementation of this function must return 0 if it succeeds.
|
||||
* It can return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` to
|
||||
* reset the stream (promised stream if frame is PUSH_PROMISE). For
|
||||
* critical errors, it must return
|
||||
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. If the other value is
|
||||
* returned, it is treated as if :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`
|
||||
* is returned. If :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned,
|
||||
* `nghttp2_session_mem_recv()` function will immediately return
|
||||
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
|
||||
*
|
||||
@@ -1546,7 +1525,7 @@ typedef int (*nghttp2_on_begin_headers_callback)(nghttp2_session *session,
|
||||
* used, nghttp2 library does perform validation against the |name|
|
||||
* and the |value| using `nghttp2_check_header_name()` and
|
||||
* `nghttp2_check_header_value()`. In addition to this, nghttp2
|
||||
* performs vaidation based on HTTP Messaging rule, which is briefly
|
||||
* performs validation based on HTTP Messaging rule, which is briefly
|
||||
* explained in :ref:`http-messaging` section.
|
||||
*
|
||||
* If the application uses `nghttp2_session_mem_recv()`, it can return
|
||||
@@ -1559,12 +1538,15 @@ typedef int (*nghttp2_on_begin_headers_callback)(nghttp2_session *session,
|
||||
* included in the input bytes.
|
||||
*
|
||||
* Returning :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close
|
||||
* the stream by issuing RST_STREAM with
|
||||
* :enum:`NGHTTP2_INTERNAL_ERROR`. In this case,
|
||||
* the stream (promised stream if frame is PUSH_PROMISE) by issuing
|
||||
* RST_STREAM with :enum:`NGHTTP2_INTERNAL_ERROR`. In this case,
|
||||
* :type:`nghttp2_on_header_callback` and
|
||||
* :type:`nghttp2_on_frame_recv_callback` will not be invoked. If a
|
||||
* different error code is desirable, use
|
||||
* `nghttp2_submit_rst_stream()` with a desired error code and then
|
||||
* return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`.
|
||||
* return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. Again, use
|
||||
* ``frame->push_promise.promised_stream_id`` as stream_id parameter
|
||||
* in `nghttp2_submit_rst_stream()` if frame is PUSH_PROMISE.
|
||||
*
|
||||
* The implementation of this function must return 0 if it succeeds.
|
||||
* It may return :enum:`NGHTTP2_ERR_PAUSE` or
|
||||
@@ -1596,8 +1578,8 @@ typedef int (*nghttp2_on_header_callback)(nghttp2_session *session,
|
||||
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. Returning
|
||||
* ``frame->hd.length`` means no padding is added. Returning
|
||||
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` will make
|
||||
* `nghttp2_session_send()` function immediately return
|
||||
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
|
||||
* `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions
|
||||
* immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
|
||||
*
|
||||
* To set this callback to :type:`nghttp2_session_callbacks`, use
|
||||
* `nghttp2_session_callbacks_set_select_padding_callback()`.
|
||||
@@ -1726,8 +1708,8 @@ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_recv_callback(
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* Sets callback function invoked by `nghttp2_session_recv()` when a
|
||||
* frame is received.
|
||||
* Sets callback function invoked by `nghttp2_session_recv()` and
|
||||
* `nghttp2_session_mem_recv()` when a frame is received.
|
||||
*/
|
||||
NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_recv_callback(
|
||||
nghttp2_session_callbacks *cbs,
|
||||
@@ -1736,8 +1718,9 @@ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_recv_callback(
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* Sets callback function invoked by `nghttp2_session_recv()` when an
|
||||
* invalid non-DATA frame is received.
|
||||
* Sets callback function invoked by `nghttp2_session_recv()` and
|
||||
* `nghttp2_session_mem_recv()` when an invalid non-DATA frame is
|
||||
* received.
|
||||
*/
|
||||
NGHTTP2_EXTERN void
|
||||
nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(
|
||||
@@ -2017,35 +2000,56 @@ nghttp2_option_set_peer_max_concurrent_streams(nghttp2_option *option,
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* By default, nghttp2 library only handles HTTP/2 frames and does not
|
||||
* recognize first 24 bytes of client connection preface. This design
|
||||
* choice is done due to the fact that server may want to detect the
|
||||
* application protocol based on first few bytes on clear text
|
||||
* communication. But for simple servers which only speak HTTP/2, it
|
||||
* is easier for developers if nghttp2 library takes care of client
|
||||
* connection preface.
|
||||
* By default, nghttp2 library, if configured as server, requires
|
||||
* first 24 bytes of client magic byte string (MAGIC). In most cases,
|
||||
* this will simplify the implementation of server. But sometimes
|
||||
* server may want to detect the application protocol based on first
|
||||
* few bytes on clear text communication.
|
||||
*
|
||||
* If this option is used with nonzero |val|, nghttp2 library checks
|
||||
* first 24 bytes client connection preface. If it is not a valid
|
||||
* one, `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` will
|
||||
* return error :enum:`NGHTTP2_ERR_BAD_PREFACE`, which is fatal error.
|
||||
* If this option is used with nonzero |val|, nghttp2 library does not
|
||||
* handle MAGIC. It still checks following SETTINGS frame. This
|
||||
* means that applications should deal with MAGIC by themselves.
|
||||
*
|
||||
* If this option is not used or used with zero value, if MAGIC does
|
||||
* not match :macro:`NGHTTP2_CLIENT_MAGIC`, `nghttp2_session_recv()`
|
||||
* and `nghttp2_session_mem_recv()` will return error
|
||||
* :enum:`NGHTTP2_ERR_BAD_CLIENT_MAGIC`, which is fatal error.
|
||||
*/
|
||||
NGHTTP2_EXTERN void
|
||||
nghttp2_option_set_recv_client_preface(nghttp2_option *option, int val);
|
||||
nghttp2_option_set_no_recv_client_magic(nghttp2_option *option, int val);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* By default, nghttp2 library enforces subset of HTTP Messaging rules
|
||||
* described in `HTTP/2 specification, section 8
|
||||
* <https://tools.ietf.org/html/draft-ietf-httpbis-http2-17#section-8>`_.
|
||||
* See :ref:`http-messaging` section for details. For those
|
||||
* applications who use nghttp2 library as non-HTTP use, give nonzero
|
||||
* to |val| to disable this enforcement.
|
||||
* <https://tools.ietf.org/html/rfc7540#section-8>`_. See
|
||||
* :ref:`http-messaging` section for details. For those applications
|
||||
* who use nghttp2 library as non-HTTP use, give nonzero to |val| to
|
||||
* disable this enforcement.
|
||||
*/
|
||||
NGHTTP2_EXTERN void nghttp2_option_set_no_http_messaging(nghttp2_option *option,
|
||||
int val);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* RFC 7540 does not enforce any limit on the number of incoming
|
||||
* reserved streams (in RFC 7540 terms, streams in reserved (remote)
|
||||
* state). This only affects client side, since only server can push
|
||||
* streams. Malicious server can push arbitrary number of streams,
|
||||
* and make client's memory exhausted. This option can set the
|
||||
* maximum number of such incoming streams to avoid possible memory
|
||||
* exhaustion. If this option is set, and pushed streams are
|
||||
* automatically closed on reception, without calling user provided
|
||||
* callback, if they exceed the given limit. The default value is
|
||||
* 200. If session is configured as server side, this option has no
|
||||
* effect. Server can control the number of streams to push.
|
||||
*/
|
||||
NGHTTP2_EXTERN void
|
||||
nghttp2_option_set_max_reserved_remote_streams(nghttp2_option *option,
|
||||
uint32_t val);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
@@ -2286,6 +2290,15 @@ NGHTTP2_EXTERN int nghttp2_session_send(nghttp2_session *session);
|
||||
*
|
||||
* :enum:`NGHTTP2_ERR_NOMEM`
|
||||
* Out of memory.
|
||||
*
|
||||
* .. note::
|
||||
*
|
||||
* This function may produce very small byte string. If that is the
|
||||
* case, and application disables Nagle algorithm (``TCP_NODELAY``),
|
||||
* then writing this small chunk leads to very small packet, and it
|
||||
* is very inefficient. An application should be responsible to
|
||||
* buffer up small chunks of data as necessary to avoid this
|
||||
* situation.
|
||||
*/
|
||||
NGHTTP2_EXTERN ssize_t nghttp2_session_mem_send(nghttp2_session *session,
|
||||
const uint8_t **data_ptr);
|
||||
@@ -2348,10 +2361,11 @@ NGHTTP2_EXTERN ssize_t nghttp2_session_mem_send(nghttp2_session *session,
|
||||
* Out of memory.
|
||||
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`
|
||||
* The callback function failed.
|
||||
* :enum:`NGHTTP2_ERR_BAD_PREFACE`
|
||||
* Invalid client preface was detected. This error only returns
|
||||
* :enum:`NGHTTP2_ERR_BAD_CLIENT_MAGIC`
|
||||
* Invalid client magic was detected. This error only returns
|
||||
* when |session| was configured as server and
|
||||
* `nghttp2_option_set_recv_client_preface()` is used.
|
||||
* `nghttp2_option_set_no_recv_client_magic()` is not used with
|
||||
* nonzero value.
|
||||
*/
|
||||
NGHTTP2_EXTERN int nghttp2_session_recv(nghttp2_session *session);
|
||||
|
||||
@@ -2383,10 +2397,11 @@ NGHTTP2_EXTERN int nghttp2_session_recv(nghttp2_session *session);
|
||||
* Out of memory.
|
||||
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`
|
||||
* The callback function failed.
|
||||
* :enum:`NGHTTP2_ERR_BAD_PREFACE`
|
||||
* Invalid client preface was detected. This error only returns
|
||||
* :enum:`NGHTTP2_ERR_BAD_CLIENT_MAGIC`
|
||||
* Invalid client magic was detected. This error only returns
|
||||
* when |session| was configured as server and
|
||||
* `nghttp2_option_set_recv_client_preface()` is used.
|
||||
* `nghttp2_option_set_no_recv_client_magic()` is not used with
|
||||
* nonzero value.
|
||||
*/
|
||||
NGHTTP2_EXTERN ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
|
||||
const uint8_t *in,
|
||||
@@ -2930,13 +2945,13 @@ nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec);
|
||||
* If |data_prd| is not ``NULL``, it provides data which will be sent
|
||||
* in subsequent DATA frames. In this case, a method that allows
|
||||
* request message bodies
|
||||
* (http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9) must
|
||||
* be specified with ``:method`` key in |nva| (e.g. ``POST``). This
|
||||
* function does not take ownership of the |data_prd|. The function
|
||||
* copies the members of the |data_prd|. If |data_prd| is ``NULL``,
|
||||
* HEADERS have END_STREAM set. The |stream_user_data| is data
|
||||
* associated to the stream opened by this request and can be an
|
||||
* arbitrary pointer, which can be retrieved later by
|
||||
* (https://tools.ietf.org/html/rfc7231#section-4) must be specified
|
||||
* with ``:method`` key in |nva| (e.g. ``POST``). This function does
|
||||
* not take ownership of the |data_prd|. The function copies the
|
||||
* members of the |data_prd|. If |data_prd| is ``NULL``, HEADERS have
|
||||
* END_STREAM set. The |stream_user_data| is data associated to the
|
||||
* stream opened by this request and can be an arbitrary pointer,
|
||||
* which can be retrieved later by
|
||||
* `nghttp2_session_get_stream_user_data()`.
|
||||
*
|
||||
* This function returns assigned stream ID if it succeeds, or one of
|
||||
@@ -3006,6 +3021,11 @@ NGHTTP2_EXTERN int32_t
|
||||
* Out of memory.
|
||||
* :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
|
||||
* The |stream_id| is 0.
|
||||
* :enum:`NGHTTP2_ERR_DATA_EXIST`
|
||||
* DATA or HEADERS has been already submitted and not fully
|
||||
* processed yet. Normally, this does not happen, but when
|
||||
* application wrongly calls `nghttp2_submit_response()` twice,
|
||||
* this may happen.
|
||||
*
|
||||
* .. warning::
|
||||
*
|
||||
@@ -3115,7 +3135,8 @@ NGHTTP2_EXTERN int nghttp2_submit_trailer(nghttp2_session *session,
|
||||
*
|
||||
* This function is low-level in a sense that the application code can
|
||||
* specify flags directly. For usual HTTP request,
|
||||
* `nghttp2_submit_request()` is useful.
|
||||
* `nghttp2_submit_request()` is useful. Likewise, for HTTP response,
|
||||
* prefer `nghttp2_submit_response()`.
|
||||
*
|
||||
* This function returns newly assigned stream ID if it succeeds and
|
||||
* |stream_id| is -1. Otherwise, this function returns 0 if it
|
||||
@@ -3128,6 +3149,10 @@ NGHTTP2_EXTERN int nghttp2_submit_trailer(nghttp2_session *session,
|
||||
* reached.
|
||||
* :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
|
||||
* The |stream_id| is 0.
|
||||
* :enum:`NGHTTP2_ERR_DATA_EXIST`
|
||||
* DATA or HEADERS has been already submitted and not fully
|
||||
* processed yet. This happens if stream denoted by |stream_id|
|
||||
* is in reserved state.
|
||||
*
|
||||
* .. warning::
|
||||
*
|
||||
@@ -3162,7 +3187,8 @@ NGHTTP2_EXTERN int32_t
|
||||
* :enum:`NGHTTP2_ERR_NOMEM`
|
||||
* Out of memory.
|
||||
* :enum:`NGHTTP2_ERR_DATA_EXIST`
|
||||
* DATA has been already submitted and not fully processed yet.
|
||||
* DATA or HEADERS has been already submitted and not fully
|
||||
* processed yet.
|
||||
* :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
|
||||
* The |stream_id| is 0.
|
||||
* :enum:`NGHTTP2_ERR_STREAM_CLOSED`
|
||||
@@ -3170,14 +3196,19 @@ NGHTTP2_EXTERN int32_t
|
||||
*
|
||||
* .. note::
|
||||
*
|
||||
* Currently, only one data is allowed for a stream at a time.
|
||||
* Submitting data more than once before first data is finished
|
||||
* results in :enum:`NGHTTP2_ERR_DATA_EXIST` error code. The
|
||||
* earliest callback which tells that previous data is done is
|
||||
* :type:`nghttp2_on_frame_send_callback`. In side that callback,
|
||||
* new data can be submitted using `nghttp2_submit_data()`. Of
|
||||
* course, all data except for last one must not have
|
||||
* :enum:`NGHTTP2_FLAG_END_STREAM` flag set in |flags|.
|
||||
* Currently, only one DATA or HEADERS is allowed for a stream at a
|
||||
* time. Submitting these frames more than once before first DATA
|
||||
* or HEADERS is finished results in :enum:`NGHTTP2_ERR_DATA_EXIST`
|
||||
* error code. The earliest callback which tells that previous
|
||||
* frame is done is :type:`nghttp2_on_frame_send_callback`. In side
|
||||
* that callback, new data can be submitted using
|
||||
* `nghttp2_submit_data()`. Of course, all data except for last one
|
||||
* must not have :enum:`NGHTTP2_FLAG_END_STREAM` flag set in
|
||||
* |flags|. This sounds a bit complicated, and we recommend to use
|
||||
* `nghttp2_submit_request()` and `nghttp2_submit_response()` to
|
||||
* avoid this cascading issue. The experience shows that for HTTP
|
||||
* use, these two functions are enough to implement both client and
|
||||
* server.
|
||||
*/
|
||||
NGHTTP2_EXTERN int nghttp2_submit_data(nghttp2_session *session, uint8_t flags,
|
||||
int32_t stream_id,
|
||||
@@ -3266,10 +3297,6 @@ NGHTTP2_EXTERN int nghttp2_submit_rst_stream(nghttp2_session *session,
|
||||
* :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
|
||||
* The |iv| contains invalid value (e.g., initial window size
|
||||
* strictly greater than (1 << 31) - 1.
|
||||
* :enum:`NGHTTP2_ERR_TOO_MANY_INFLIGHT_SETTINGS`
|
||||
* There is already another in-flight SETTINGS. Note that the
|
||||
* current implementation only allows 1 in-flight SETTINGS frame
|
||||
* without ACK flag set.
|
||||
* :enum:`NGHTTP2_ERR_NOMEM`
|
||||
* Out of memory.
|
||||
*/
|
||||
@@ -3469,20 +3496,6 @@ NGHTTP2_EXTERN int nghttp2_submit_window_update(nghttp2_session *session,
|
||||
int32_t stream_id,
|
||||
int32_t window_size_increment);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* This function previously submits ALTSVC frame with given
|
||||
* parameters, but is deprecated and will be removed in a future
|
||||
* release. This function does nothing and just return 0.
|
||||
*/
|
||||
NGHTTP2_EXTERN int
|
||||
nghttp2_submit_altsvc(nghttp2_session *session, uint8_t flags,
|
||||
int32_t stream_id, uint32_t max_age, uint16_t port,
|
||||
const uint8_t *protocol_id, size_t protocol_id_len,
|
||||
const uint8_t *host, size_t host_len,
|
||||
const uint8_t *origin, size_t origin_len);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
@@ -3501,14 +3514,14 @@ NGHTTP2_EXTERN int nghttp2_nv_compare_name(const nghttp2_nv *lhs,
|
||||
* A helper function for dealing with NPN in client side or ALPN in
|
||||
* server side. The |in| contains peer's protocol list in preferable
|
||||
* order. The format of |in| is length-prefixed and not
|
||||
* null-terminated. For example, ``HTTP-draft-04/2.0`` and
|
||||
* null-terminated. For example, ``h2`` and
|
||||
* ``http/1.1`` stored in |in| like this::
|
||||
*
|
||||
* in[0] = 17
|
||||
* in[1..17] = "HTTP-draft-04/2.0"
|
||||
* in[18] = 8
|
||||
* in[19..26] = "http/1.1"
|
||||
* inlen = 27
|
||||
* in[0] = 2
|
||||
* in[1..2] = "h2"
|
||||
* in[3] = 8
|
||||
* in[4..11] = "http/1.1"
|
||||
* inlen = 12
|
||||
*
|
||||
* The selection algorithm is as follows:
|
||||
*
|
||||
@@ -3522,12 +3535,10 @@ NGHTTP2_EXTERN int nghttp2_nv_compare_name(const nghttp2_nv *lhs,
|
||||
* non-overlap case). In this case, |out| and |outlen| are left
|
||||
* untouched.
|
||||
*
|
||||
* Selecting ``HTTP-draft-04/2.0`` means that ``HTTP-draft-04/2.0`` is
|
||||
* written into |*out| and its length (which is 17) is assigned to
|
||||
* |*outlen|.
|
||||
* Selecting ``h2`` means that ``h2`` is written into |*out| and its
|
||||
* length (which is 2) is assigned to |*outlen|.
|
||||
*
|
||||
* For ALPN, refer to
|
||||
* https://tools.ietf.org/html/draft-ietf-tls-applayerprotoneg-05
|
||||
* For ALPN, refer to https://tools.ietf.org/html/rfc7301
|
||||
*
|
||||
* See http://technotes.googlecode.com/git/nextprotoneg.html for more
|
||||
* details about NPN.
|
||||
@@ -3543,7 +3554,10 @@ NGHTTP2_EXTERN int nghttp2_nv_compare_name(const nghttp2_nv *lhs,
|
||||
* {
|
||||
* int rv;
|
||||
* rv = nghttp2_select_next_protocol(out, outlen, in, inlen);
|
||||
* if(rv == 1) {
|
||||
* if (rv == -1) {
|
||||
* return SSL_TLSEXT_ERR_NOACK;
|
||||
* }
|
||||
* if (rv == 1) {
|
||||
* ((MyType*)arg)->http2_selected = 1;
|
||||
* }
|
||||
* return SSL_TLSEXT_ERR_OK;
|
||||
@@ -3574,7 +3588,7 @@ NGHTTP2_EXTERN nghttp2_info *nghttp2_version(int least_version);
|
||||
* Returns nonzero if the :type:`nghttp2_error` library error code
|
||||
* |lib_error| is fatal.
|
||||
*/
|
||||
NGHTTP2_EXTERN int nghttp2_is_fatal(int lib_error);
|
||||
NGHTTP2_EXTERN int nghttp2_is_fatal(int lib_error_code);
|
||||
|
||||
/**
|
||||
* @function
|
||||
@@ -3778,11 +3792,22 @@ NGHTTP2_EXTERN void nghttp2_hd_inflate_del(nghttp2_hd_inflater *inflater);
|
||||
* The |settings_hd_table_bufsize_max| should be the value transmitted
|
||||
* in SETTINGS_HEADER_TABLE_SIZE.
|
||||
*
|
||||
* This function must not be called while header block is being
|
||||
* inflated. In other words, this function must be called after
|
||||
* initialization of |inflater|, but before calling
|
||||
* `nghttp2_hd_inflate_hd()`, or after
|
||||
* `nghttp2_hd_inflate_end_headers()`. Otherwise,
|
||||
* `NGHTTP2_ERR_INVALID_STATE` was returned.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* :enum:`NGHTTP2_ERR_NOMEM`
|
||||
* Out of memory.
|
||||
* :enum:`NGHTTP2_ERR_INVALID_STATE`
|
||||
* The function is called while header block is being inflated.
|
||||
* Probably, application missed to call
|
||||
* `nghttp2_hd_inflate_end_headers()`.
|
||||
*/
|
||||
NGHTTP2_EXTERN int
|
||||
nghttp2_hd_inflate_change_table_size(nghttp2_hd_inflater *inflater,
|
||||
@@ -3901,6 +3926,134 @@ NGHTTP2_EXTERN ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater,
|
||||
NGHTTP2_EXTERN int
|
||||
nghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater);
|
||||
|
||||
struct nghttp2_stream;
|
||||
|
||||
/**
|
||||
* @struct
|
||||
*
|
||||
* The structure to represent HTTP/2 stream. The details of this
|
||||
* structure are intentionally hidden from the public API.
|
||||
*/
|
||||
typedef struct nghttp2_stream nghttp2_stream;
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* Returns pointer to :type:`nghttp2_stream` object denoted by
|
||||
* |stream_id|. If stream was not found, returns NULL.
|
||||
*
|
||||
* Returns imaginary root stream (see
|
||||
* `nghttp2_session_get_root_stream()`) if 0 is given in |stream_id|.
|
||||
*
|
||||
* Unless |stream_id| == 0, the returned pointer is valid until next
|
||||
* call of `nghttp2_session_send()`, `nghttp2_session_mem_send()`,
|
||||
* `nghttp2_session_recv()`, and `nghttp2_session_mem_recv()`.
|
||||
*/
|
||||
nghttp2_stream *nghttp2_session_find_stream(nghttp2_session *session,
|
||||
int32_t stream_id);
|
||||
|
||||
/**
|
||||
* @enum
|
||||
*
|
||||
* State of stream as described in RFC 7540.
|
||||
*/
|
||||
typedef enum {
|
||||
/**
|
||||
* idle state.
|
||||
*/
|
||||
NGHTTP2_STREAM_STATE_IDLE = 1,
|
||||
/**
|
||||
* open state.
|
||||
*/
|
||||
NGHTTP2_STREAM_STATE_OPEN,
|
||||
/**
|
||||
* reserved (local) state.
|
||||
*/
|
||||
NGHTTP2_STREAM_STATE_RESERVED_LOCAL,
|
||||
/**
|
||||
* reserved (remote) state.
|
||||
*/
|
||||
NGHTTP2_STREAM_STATE_RESERVED_REMOTE,
|
||||
/**
|
||||
* half closed (local) state.
|
||||
*/
|
||||
NGHTTP2_STREAM_STATE_HALF_CLOSED_LOCAL,
|
||||
/**
|
||||
* half closed (remote) state.
|
||||
*/
|
||||
NGHTTP2_STREAM_STATE_HALF_CLOSED_REMOTE,
|
||||
/**
|
||||
* closed state.
|
||||
*/
|
||||
NGHTTP2_STREAM_STATE_CLOSED
|
||||
} nghttp2_stream_proto_state;
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* Returns state of |stream|. The root stream retrieved by
|
||||
* `nghttp2_session_get_root_stream()` will have stream state
|
||||
* :enum:`NGHTTP2_STREAM_STATE_IDLE`.
|
||||
*/
|
||||
nghttp2_stream_proto_state nghttp2_stream_get_state(nghttp2_stream *stream);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* Returns root of dependency tree, which is imaginary stream with
|
||||
* stream ID 0. The returned pointer is valid until |session| is
|
||||
* freed by `nghttp2_session_del()`.
|
||||
*/
|
||||
nghttp2_stream *nghttp2_session_get_root_stream(nghttp2_session *session);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* Returns the parent stream of |stream| in dependency tree. Returns
|
||||
* NULL if there is no such stream.
|
||||
*/
|
||||
nghttp2_stream *nghttp2_stream_get_parent(nghttp2_stream *stream);
|
||||
|
||||
int32_t nghttp2_stream_get_stream_id(nghttp2_stream *stream);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* Returns the next sibling stream of |stream| in dependency tree.
|
||||
* Returns NULL if there is no such stream.
|
||||
*/
|
||||
nghttp2_stream *nghttp2_stream_get_next_sibling(nghttp2_stream *stream);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* Returns the previous sibling stream of |stream| in dependency tree.
|
||||
* Returns NULL if there is no such stream.
|
||||
*/
|
||||
nghttp2_stream *nghttp2_stream_get_previous_sibling(nghttp2_stream *stream);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* Returns the first child stream of |stream| in dependency tree.
|
||||
* Returns NULL if there is no such stream.
|
||||
*/
|
||||
nghttp2_stream *nghttp2_stream_get_first_child(nghttp2_stream *stream);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* Returns dependency weight to the parent stream of |stream|.
|
||||
*/
|
||||
int32_t nghttp2_stream_get_weight(nghttp2_stream *stream);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* Returns the sum of the weight for |stream|'s children.
|
||||
*/
|
||||
int32_t nghttp2_stream_get_sum_dependency_weight(nghttp2_stream *stream);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -244,9 +244,9 @@ void nghttp2_bufs_seek_last_present(nghttp2_bufs *bufs) {
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t nghttp2_bufs_len(nghttp2_bufs *bufs) {
|
||||
size_t nghttp2_bufs_len(nghttp2_bufs *bufs) {
|
||||
nghttp2_buf_chain *ci;
|
||||
ssize_t len;
|
||||
size_t len;
|
||||
|
||||
len = 0;
|
||||
for (ci = bufs->head; ci; ci = ci->next) {
|
||||
@@ -256,10 +256,10 @@ ssize_t nghttp2_bufs_len(nghttp2_bufs *bufs) {
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t bufs_avail(nghttp2_bufs *bufs) {
|
||||
return (ssize_t)(nghttp2_buf_avail(&bufs->cur->buf) +
|
||||
(bufs->chunk_length - bufs->offset) *
|
||||
(bufs->max_chunk - bufs->chunk_used));
|
||||
static size_t bufs_avail(nghttp2_bufs *bufs) {
|
||||
return nghttp2_buf_avail(&bufs->cur->buf) +
|
||||
(bufs->chunk_length - bufs->offset) *
|
||||
(bufs->max_chunk - bufs->chunk_used);
|
||||
}
|
||||
|
||||
static int bufs_alloc_chain(nghttp2_bufs *bufs) {
|
||||
@@ -301,7 +301,7 @@ int nghttp2_bufs_add(nghttp2_bufs *bufs, const void *data, size_t len) {
|
||||
nghttp2_buf *buf;
|
||||
const uint8_t *p;
|
||||
|
||||
if (bufs_avail(bufs) < (ssize_t)len) {
|
||||
if (bufs_avail(bufs) < len) {
|
||||
return NGHTTP2_ERR_BUFFER_ERROR;
|
||||
}
|
||||
|
||||
@@ -310,7 +310,7 @@ int nghttp2_bufs_add(nghttp2_bufs *bufs, const void *data, size_t len) {
|
||||
while (len) {
|
||||
buf = &bufs->cur->buf;
|
||||
|
||||
nwrite = nghttp2_min((size_t)nghttp2_buf_avail(buf), len);
|
||||
nwrite = nghttp2_min(nghttp2_buf_avail(buf), len);
|
||||
if (nwrite == 0) {
|
||||
rv = bufs_alloc_chain(bufs);
|
||||
if (rv != 0) {
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -432,6 +432,24 @@ ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out) {
|
||||
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;
|
||||
|
||||
@@ -51,13 +51,13 @@ typedef struct {
|
||||
uint8_t *mark;
|
||||
} nghttp2_buf;
|
||||
|
||||
#define nghttp2_buf_len(BUF) ((ssize_t)((BUF)->last - (BUF)->pos))
|
||||
#define nghttp2_buf_avail(BUF) ((ssize_t)((BUF)->end - (BUF)->last))
|
||||
#define nghttp2_buf_mark_avail(BUF) ((ssize_t)((BUF)->mark - (BUF)->last))
|
||||
#define nghttp2_buf_cap(BUF) ((ssize_t)((BUF)->end - (BUF)->begin))
|
||||
#define nghttp2_buf_len(BUF) ((size_t)((BUF)->last - (BUF)->pos))
|
||||
#define nghttp2_buf_avail(BUF) ((size_t)((BUF)->end - (BUF)->last))
|
||||
#define nghttp2_buf_mark_avail(BUF) ((size_t)((BUF)->mark - (BUF)->last))
|
||||
#define nghttp2_buf_cap(BUF) ((size_t)((BUF)->end - (BUF)->begin))
|
||||
|
||||
#define nghttp2_buf_pos_offset(BUF) ((ssize_t)((BUF)->pos - (BUF)->begin))
|
||||
#define nghttp2_buf_last_offset(BUF) ((ssize_t)((BUF)->last - (BUF)->begin))
|
||||
#define nghttp2_buf_pos_offset(BUF) ((size_t)((BUF)->pos - (BUF)->begin))
|
||||
#define nghttp2_buf_last_offset(BUF) ((size_t)((BUF)->last - (BUF)->begin))
|
||||
|
||||
#define nghttp2_buf_shift_right(BUF, AMT) \
|
||||
do { \
|
||||
@@ -300,12 +300,15 @@ int nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b);
|
||||
|
||||
#define nghttp2_bufs_fast_orb(BUFS, B) \
|
||||
do { \
|
||||
*(BUFS)->cur->buf.last++ |= B; \
|
||||
uint8_t **p = &(BUFS)->cur->buf.last; \
|
||||
**p = (uint8_t)(**p | (B)); \
|
||||
++(*p); \
|
||||
} while (0)
|
||||
|
||||
#define nghttp2_bufs_fast_orb_hold(BUFS, B) \
|
||||
do { \
|
||||
*(BUFS)->cur->buf.last |= B; \
|
||||
uint8_t *p = (BUFS)->cur->buf.last; \
|
||||
*p = (uint8_t)(*p | (B)); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
@@ -324,6 +327,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.
|
||||
*/
|
||||
@@ -369,6 +383,6 @@ int nghttp2_bufs_next_present(nghttp2_bufs *bufs);
|
||||
/*
|
||||
* Returns the buffer length of |bufs|.
|
||||
*/
|
||||
ssize_t nghttp2_bufs_len(nghttp2_bufs *bufs);
|
||||
size_t nghttp2_bufs_len(nghttp2_bufs *bufs);
|
||||
|
||||
#endif /* NGHTTP2_BUF_H */
|
||||
|
||||
@@ -37,7 +37,7 @@ void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd) {
|
||||
nghttp2_put_uint32be(&buf[0], (uint32_t)(hd->length << 8));
|
||||
buf[3] = hd->type;
|
||||
buf[4] = hd->flags;
|
||||
nghttp2_put_uint32be(&buf[5], hd->stream_id);
|
||||
nghttp2_put_uint32be(&buf[5], (uint32_t)hd->stream_id);
|
||||
/* ignore hd->reserved for now */
|
||||
}
|
||||
|
||||
@@ -219,7 +219,7 @@ static int frame_pack_headers_shared(nghttp2_bufs *bufs,
|
||||
CONTINUATION frame is involved. Remove END_HEADERS flag from the
|
||||
first frame. */
|
||||
if (bufs->head != bufs->cur) {
|
||||
hd.flags &= ~NGHTTP2_FLAG_END_HEADERS;
|
||||
hd.flags = (uint8_t)(hd.flags & ~NGHTTP2_FLAG_END_HEADERS);
|
||||
}
|
||||
|
||||
buf->pos -= NGHTTP2_FRAME_HDLEN;
|
||||
@@ -300,11 +300,11 @@ int nghttp2_frame_pack_headers(nghttp2_bufs *bufs, nghttp2_headers *frame,
|
||||
|
||||
void nghttp2_frame_pack_priority_spec(uint8_t *buf,
|
||||
const nghttp2_priority_spec *pri_spec) {
|
||||
nghttp2_put_uint32be(buf, pri_spec->stream_id);
|
||||
nghttp2_put_uint32be(buf, (uint32_t)pri_spec->stream_id);
|
||||
if (pri_spec->exclusive) {
|
||||
buf[0] |= 0x80;
|
||||
}
|
||||
buf[4] = pri_spec->weight - 1;
|
||||
buf[4] = (uint8_t)(pri_spec->weight - 1);
|
||||
}
|
||||
|
||||
void nghttp2_frame_unpack_priority_spec(nghttp2_priority_spec *pri_spec,
|
||||
@@ -398,7 +398,7 @@ int nghttp2_frame_pack_settings(nghttp2_bufs *bufs, nghttp2_settings *frame) {
|
||||
|
||||
buf = &bufs->head->buf;
|
||||
|
||||
if (nghttp2_buf_avail(buf) < (ssize_t)frame->hd.length) {
|
||||
if (nghttp2_buf_avail(buf) < frame->hd.length) {
|
||||
return NGHTTP2_ERR_FRAME_SIZE_ERROR;
|
||||
}
|
||||
|
||||
@@ -417,7 +417,7 @@ size_t nghttp2_frame_pack_settings_payload(uint8_t *buf,
|
||||
size_t niv) {
|
||||
size_t i;
|
||||
for (i = 0; i < niv; ++i, buf += NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) {
|
||||
nghttp2_put_uint16be(buf, iv[i].settings_id);
|
||||
nghttp2_put_uint16be(buf, (uint16_t)iv[i].settings_id);
|
||||
nghttp2_put_uint32be(buf + 2, iv[i].value);
|
||||
}
|
||||
return NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH * niv;
|
||||
@@ -507,7 +507,7 @@ int nghttp2_frame_pack_push_promise(nghttp2_bufs *bufs,
|
||||
return rv;
|
||||
}
|
||||
|
||||
nghttp2_put_uint32be(buf->pos, frame->promised_stream_id);
|
||||
nghttp2_put_uint32be(buf->pos, (uint32_t)frame->promised_stream_id);
|
||||
|
||||
frame->padlen = 0;
|
||||
frame->hd.length = nghttp2_bufs_len(bufs);
|
||||
@@ -562,7 +562,7 @@ int nghttp2_frame_pack_goaway(nghttp2_bufs *bufs, nghttp2_goaway *frame) {
|
||||
|
||||
nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
|
||||
|
||||
nghttp2_put_uint32be(buf->last, frame->last_stream_id);
|
||||
nghttp2_put_uint32be(buf->last, (uint32_t)frame->last_stream_id);
|
||||
buf->last += 4;
|
||||
|
||||
nghttp2_put_uint32be(buf->last, frame->error_code);
|
||||
@@ -639,7 +639,7 @@ int nghttp2_frame_pack_window_update(nghttp2_bufs *bufs,
|
||||
|
||||
nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
|
||||
|
||||
nghttp2_put_uint32be(buf->last, frame->window_size_increment);
|
||||
nghttp2_put_uint32be(buf->last, (uint32_t)frame->window_size_increment);
|
||||
buf->last += 4;
|
||||
|
||||
return 0;
|
||||
@@ -784,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;
|
||||
@@ -833,7 +830,7 @@ static void frame_set_pad(nghttp2_buf *buf, size_t padlen, int framehd_only) {
|
||||
}
|
||||
|
||||
trail_padlen = padlen - 1;
|
||||
buf->pos[NGHTTP2_FRAME_HDLEN] = trail_padlen;
|
||||
buf->pos[NGHTTP2_FRAME_HDLEN] = (uint8_t)trail_padlen;
|
||||
|
||||
/* zero out padding */
|
||||
memset(buf->last, 0, trail_padlen);
|
||||
@@ -874,7 +871,7 @@ int nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd,
|
||||
|
||||
buf = &bufs->head->buf;
|
||||
|
||||
assert(nghttp2_buf_avail(buf) >= (ssize_t)padlen - 1);
|
||||
assert(nghttp2_buf_avail(buf) >= padlen - 1);
|
||||
|
||||
frame_set_pad(buf, padlen, framehd_only);
|
||||
|
||||
|
||||
@@ -65,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
|
||||
|
||||
@@ -75,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);
|
||||
|
||||
|
||||
479
lib/nghttp2_hd.c
479
lib/nghttp2_hd.c
@@ -32,10 +32,10 @@
|
||||
#include "nghttp2_int.h"
|
||||
|
||||
/* Make scalar initialization form of nghttp2_hd_entry */
|
||||
#define MAKE_STATIC_ENT(N, V, T) \
|
||||
#define MAKE_STATIC_ENT(N, V, T, H) \
|
||||
{ \
|
||||
{ (uint8_t *)(N), (uint8_t *)(V), sizeof((N)) - 1, sizeof((V)) - 1, 0 } \
|
||||
, (T), 1, NGHTTP2_HD_FLAG_NONE \
|
||||
, NULL, 0, (H), (T), 1, NGHTTP2_HD_FLAG_NONE \
|
||||
}
|
||||
|
||||
/* Generated by mkstatictbl.py */
|
||||
@@ -43,67 +43,67 @@
|
||||
first enum value if same header names are repeated (e.g.,
|
||||
:status). */
|
||||
static nghttp2_hd_entry static_table[] = {
|
||||
MAKE_STATIC_ENT(":authority", "", 0),
|
||||
MAKE_STATIC_ENT(":method", "GET", 1),
|
||||
MAKE_STATIC_ENT(":method", "POST", 1),
|
||||
MAKE_STATIC_ENT(":path", "/", 3),
|
||||
MAKE_STATIC_ENT(":path", "/index.html", 3),
|
||||
MAKE_STATIC_ENT(":scheme", "http", 5),
|
||||
MAKE_STATIC_ENT(":scheme", "https", 5),
|
||||
MAKE_STATIC_ENT(":status", "200", 7),
|
||||
MAKE_STATIC_ENT(":status", "204", 7),
|
||||
MAKE_STATIC_ENT(":status", "206", 7),
|
||||
MAKE_STATIC_ENT(":status", "304", 7),
|
||||
MAKE_STATIC_ENT(":status", "400", 7),
|
||||
MAKE_STATIC_ENT(":status", "404", 7),
|
||||
MAKE_STATIC_ENT(":status", "500", 7),
|
||||
MAKE_STATIC_ENT("accept-charset", "", 14),
|
||||
MAKE_STATIC_ENT("accept-encoding", "gzip, deflate", 15),
|
||||
MAKE_STATIC_ENT("accept-language", "", 16),
|
||||
MAKE_STATIC_ENT("accept-ranges", "", 17),
|
||||
MAKE_STATIC_ENT("accept", "", 18),
|
||||
MAKE_STATIC_ENT("access-control-allow-origin", "", 19),
|
||||
MAKE_STATIC_ENT("age", "", 20),
|
||||
MAKE_STATIC_ENT("allow", "", 21),
|
||||
MAKE_STATIC_ENT("authorization", "", 22),
|
||||
MAKE_STATIC_ENT("cache-control", "", 23),
|
||||
MAKE_STATIC_ENT("content-disposition", "", 24),
|
||||
MAKE_STATIC_ENT("content-encoding", "", 25),
|
||||
MAKE_STATIC_ENT("content-language", "", 26),
|
||||
MAKE_STATIC_ENT("content-length", "", 27),
|
||||
MAKE_STATIC_ENT("content-location", "", 28),
|
||||
MAKE_STATIC_ENT("content-range", "", 29),
|
||||
MAKE_STATIC_ENT("content-type", "", 30),
|
||||
MAKE_STATIC_ENT("cookie", "", 31),
|
||||
MAKE_STATIC_ENT("date", "", 32),
|
||||
MAKE_STATIC_ENT("etag", "", 33),
|
||||
MAKE_STATIC_ENT("expect", "", 34),
|
||||
MAKE_STATIC_ENT("expires", "", 35),
|
||||
MAKE_STATIC_ENT("from", "", 36),
|
||||
MAKE_STATIC_ENT("host", "", 37),
|
||||
MAKE_STATIC_ENT("if-match", "", 38),
|
||||
MAKE_STATIC_ENT("if-modified-since", "", 39),
|
||||
MAKE_STATIC_ENT("if-none-match", "", 40),
|
||||
MAKE_STATIC_ENT("if-range", "", 41),
|
||||
MAKE_STATIC_ENT("if-unmodified-since", "", 42),
|
||||
MAKE_STATIC_ENT("last-modified", "", 43),
|
||||
MAKE_STATIC_ENT("link", "", 44),
|
||||
MAKE_STATIC_ENT("location", "", 45),
|
||||
MAKE_STATIC_ENT("max-forwards", "", 46),
|
||||
MAKE_STATIC_ENT("proxy-authenticate", "", 47),
|
||||
MAKE_STATIC_ENT("proxy-authorization", "", 48),
|
||||
MAKE_STATIC_ENT("range", "", 49),
|
||||
MAKE_STATIC_ENT("referer", "", 50),
|
||||
MAKE_STATIC_ENT("refresh", "", 51),
|
||||
MAKE_STATIC_ENT("retry-after", "", 52),
|
||||
MAKE_STATIC_ENT("server", "", 53),
|
||||
MAKE_STATIC_ENT("set-cookie", "", 54),
|
||||
MAKE_STATIC_ENT("strict-transport-security", "", 55),
|
||||
MAKE_STATIC_ENT("transfer-encoding", "", 56),
|
||||
MAKE_STATIC_ENT("user-agent", "", 57),
|
||||
MAKE_STATIC_ENT("vary", "", 58),
|
||||
MAKE_STATIC_ENT("via", "", 59),
|
||||
MAKE_STATIC_ENT("www-authenticate", "", 60),
|
||||
MAKE_STATIC_ENT(":authority", "", 0, 3153725150u),
|
||||
MAKE_STATIC_ENT(":method", "GET", 1, 695666056u),
|
||||
MAKE_STATIC_ENT(":method", "POST", 1, 695666056u),
|
||||
MAKE_STATIC_ENT(":path", "/", 3, 3292848686u),
|
||||
MAKE_STATIC_ENT(":path", "/index.html", 3, 3292848686u),
|
||||
MAKE_STATIC_ENT(":scheme", "http", 5, 2510477674u),
|
||||
MAKE_STATIC_ENT(":scheme", "https", 5, 2510477674u),
|
||||
MAKE_STATIC_ENT(":status", "200", 7, 4000288983u),
|
||||
MAKE_STATIC_ENT(":status", "204", 7, 4000288983u),
|
||||
MAKE_STATIC_ENT(":status", "206", 7, 4000288983u),
|
||||
MAKE_STATIC_ENT(":status", "304", 7, 4000288983u),
|
||||
MAKE_STATIC_ENT(":status", "400", 7, 4000288983u),
|
||||
MAKE_STATIC_ENT(":status", "404", 7, 4000288983u),
|
||||
MAKE_STATIC_ENT(":status", "500", 7, 4000288983u),
|
||||
MAKE_STATIC_ENT("accept-charset", "", 14, 3664010344u),
|
||||
MAKE_STATIC_ENT("accept-encoding", "gzip, deflate", 15, 3379649177u),
|
||||
MAKE_STATIC_ENT("accept-language", "", 16, 1979086614u),
|
||||
MAKE_STATIC_ENT("accept-ranges", "", 17, 1713753958u),
|
||||
MAKE_STATIC_ENT("accept", "", 18, 136609321u),
|
||||
MAKE_STATIC_ENT("access-control-allow-origin", "", 19, 2710797292u),
|
||||
MAKE_STATIC_ENT("age", "", 20, 742476188u),
|
||||
MAKE_STATIC_ENT("allow", "", 21, 2930878514u),
|
||||
MAKE_STATIC_ENT("authorization", "", 22, 2436257726u),
|
||||
MAKE_STATIC_ENT("cache-control", "", 23, 1355326669u),
|
||||
MAKE_STATIC_ENT("content-disposition", "", 24, 3889184348u),
|
||||
MAKE_STATIC_ENT("content-encoding", "", 25, 65203592u),
|
||||
MAKE_STATIC_ENT("content-language", "", 26, 24973587u),
|
||||
MAKE_STATIC_ENT("content-length", "", 27, 1308181789u),
|
||||
MAKE_STATIC_ENT("content-location", "", 28, 2302364718u),
|
||||
MAKE_STATIC_ENT("content-range", "", 29, 3555523146u),
|
||||
MAKE_STATIC_ENT("content-type", "", 30, 4244048277u),
|
||||
MAKE_STATIC_ENT("cookie", "", 31, 2007449791u),
|
||||
MAKE_STATIC_ENT("date", "", 32, 3564297305u),
|
||||
MAKE_STATIC_ENT("etag", "", 33, 113792960u),
|
||||
MAKE_STATIC_ENT("expect", "", 34, 2530896728u),
|
||||
MAKE_STATIC_ENT("expires", "", 35, 1049544579u),
|
||||
MAKE_STATIC_ENT("from", "", 36, 2513272949u),
|
||||
MAKE_STATIC_ENT("host", "", 37, 2952701295u),
|
||||
MAKE_STATIC_ENT("if-match", "", 38, 3597694698u),
|
||||
MAKE_STATIC_ENT("if-modified-since", "", 39, 2213050793u),
|
||||
MAKE_STATIC_ENT("if-none-match", "", 40, 2536202615u),
|
||||
MAKE_STATIC_ENT("if-range", "", 41, 2340978238u),
|
||||
MAKE_STATIC_ENT("if-unmodified-since", "", 42, 3794814858u),
|
||||
MAKE_STATIC_ENT("last-modified", "", 43, 3226950251u),
|
||||
MAKE_STATIC_ENT("link", "", 44, 232457833u),
|
||||
MAKE_STATIC_ENT("location", "", 45, 200649126u),
|
||||
MAKE_STATIC_ENT("max-forwards", "", 46, 1826162134u),
|
||||
MAKE_STATIC_ENT("proxy-authenticate", "", 47, 2709445359u),
|
||||
MAKE_STATIC_ENT("proxy-authorization", "", 48, 2686392507u),
|
||||
MAKE_STATIC_ENT("range", "", 49, 4208725202u),
|
||||
MAKE_STATIC_ENT("referer", "", 50, 3969579366u),
|
||||
MAKE_STATIC_ENT("refresh", "", 51, 3572655668u),
|
||||
MAKE_STATIC_ENT("retry-after", "", 52, 3336180598u),
|
||||
MAKE_STATIC_ENT("server", "", 53, 1085029842u),
|
||||
MAKE_STATIC_ENT("set-cookie", "", 54, 1848371000u),
|
||||
MAKE_STATIC_ENT("strict-transport-security", "", 55, 4138147361u),
|
||||
MAKE_STATIC_ENT("transfer-encoding", "", 56, 3719590988u),
|
||||
MAKE_STATIC_ENT("user-agent", "", 57, 606444526u),
|
||||
MAKE_STATIC_ENT("vary", "", 58, 1085005381u),
|
||||
MAKE_STATIC_ENT("via", "", 59, 1762798611u),
|
||||
MAKE_STATIC_ENT("www-authenticate", "", 60, 779865858u),
|
||||
};
|
||||
|
||||
static int memeq(const void *s1, const void *s2, size_t n) {
|
||||
@@ -114,7 +114,7 @@ static int memeq(const void *s1, const void *s2, size_t n) {
|
||||
* This function was generated by genlibtokenlookup.py. Inspired by
|
||||
* h2o header lookup. https://github.com/h2o/h2o
|
||||
*/
|
||||
static inline int lookup_token(const uint8_t *name, size_t namelen) {
|
||||
static int lookup_token(const uint8_t *name, size_t namelen) {
|
||||
switch (namelen) {
|
||||
case 2:
|
||||
switch (name[1]) {
|
||||
@@ -506,7 +506,7 @@ int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t flags, uint8_t *name,
|
||||
if ((flags & NGHTTP2_HD_FLAG_NAME_ALLOC) &&
|
||||
(flags & NGHTTP2_HD_FLAG_NAME_GIFT) == 0) {
|
||||
if (namelen == 0) {
|
||||
flags &= ~NGHTTP2_HD_FLAG_NAME_ALLOC;
|
||||
flags = (uint8_t)(flags & ~NGHTTP2_HD_FLAG_NAME_ALLOC);
|
||||
ent->nv.name = (uint8_t *)"";
|
||||
} else {
|
||||
/* copy including terminating NULL byte */
|
||||
@@ -522,7 +522,7 @@ int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t flags, uint8_t *name,
|
||||
if ((flags & NGHTTP2_HD_FLAG_VALUE_ALLOC) &&
|
||||
(flags & NGHTTP2_HD_FLAG_VALUE_GIFT) == 0) {
|
||||
if (valuelen == 0) {
|
||||
flags &= ~NGHTTP2_HD_FLAG_VALUE_ALLOC;
|
||||
flags = (uint8_t)(flags & ~NGHTTP2_HD_FLAG_VALUE_ALLOC);
|
||||
ent->nv.value = (uint8_t *)"";
|
||||
} else {
|
||||
/* copy including terminating NULL byte */
|
||||
@@ -540,11 +540,14 @@ int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t flags, uint8_t *name,
|
||||
ent->token = token;
|
||||
ent->ref = 1;
|
||||
ent->flags = flags;
|
||||
ent->next = NULL;
|
||||
ent->hash = 0;
|
||||
|
||||
return 0;
|
||||
|
||||
fail2:
|
||||
if (flags & NGHTTP2_HD_FLAG_NAME_ALLOC) {
|
||||
if ((flags & NGHTTP2_HD_FLAG_NAME_ALLOC) &&
|
||||
(flags & NGHTTP2_HD_FLAG_NAME_GIFT) == 0) {
|
||||
nghttp2_mem_free(mem, ent->nv.name);
|
||||
}
|
||||
fail:
|
||||
@@ -561,6 +564,97 @@ void nghttp2_hd_entry_free(nghttp2_hd_entry *ent, nghttp2_mem *mem) {
|
||||
}
|
||||
}
|
||||
|
||||
static int name_eq(const nghttp2_nv *a, const nghttp2_nv *b) {
|
||||
return a->namelen == b->namelen && memeq(a->name, b->name, a->namelen);
|
||||
}
|
||||
|
||||
static int value_eq(const nghttp2_nv *a, const nghttp2_nv *b) {
|
||||
return a->valuelen == b->valuelen && memeq(a->value, b->value, a->valuelen);
|
||||
}
|
||||
|
||||
static uint32_t name_hash(const nghttp2_nv *nv) {
|
||||
/* 32 bit FNV-1a: http://isthe.com/chongo/tech/comp/fnv/ */
|
||||
uint32_t h = 2166136261u;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < nv->namelen; ++i) {
|
||||
h ^= nv->name[i];
|
||||
h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24);
|
||||
}
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
static void hd_map_init(nghttp2_hd_map *map) {
|
||||
memset(map, 0, sizeof(nghttp2_hd_map));
|
||||
}
|
||||
|
||||
static void hd_map_insert(nghttp2_hd_map *map, nghttp2_hd_entry *ent) {
|
||||
nghttp2_hd_entry **bucket;
|
||||
|
||||
bucket = &map->table[ent->hash & (HD_MAP_SIZE - 1)];
|
||||
|
||||
if (*bucket == NULL) {
|
||||
*bucket = ent;
|
||||
return;
|
||||
}
|
||||
|
||||
/* lower index is linked near the root */
|
||||
ent->next = *bucket;
|
||||
*bucket = ent;
|
||||
}
|
||||
|
||||
static nghttp2_hd_entry *hd_map_find(nghttp2_hd_map *map, int *exact_match,
|
||||
const nghttp2_nv *nv, int token,
|
||||
uint32_t hash) {
|
||||
nghttp2_hd_entry *p;
|
||||
nghttp2_hd_entry *res = NULL;
|
||||
|
||||
*exact_match = 0;
|
||||
|
||||
for (p = map->table[hash & (HD_MAP_SIZE - 1)]; p; p = p->next) {
|
||||
if (hash != p->hash || token != p->token ||
|
||||
(token == -1 && !name_eq(&p->nv, nv))) {
|
||||
continue;
|
||||
}
|
||||
if (!res) {
|
||||
res = p;
|
||||
}
|
||||
if (value_eq(&p->nv, nv)) {
|
||||
res = p;
|
||||
*exact_match = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void hd_map_remove(nghttp2_hd_map *map, nghttp2_hd_entry *ent) {
|
||||
nghttp2_hd_entry **bucket;
|
||||
nghttp2_hd_entry *p;
|
||||
|
||||
bucket = &map->table[ent->hash & (HD_MAP_SIZE - 1)];
|
||||
|
||||
if (*bucket == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (*bucket == ent) {
|
||||
*bucket = ent->next;
|
||||
ent->next = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
for (p = *bucket; p; p = p->next) {
|
||||
if (p->next == ent) {
|
||||
p->next = ent->next;
|
||||
ent->next = NULL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int hd_ringbuf_init(nghttp2_hd_ringbuf *ringbuf, size_t bufsize,
|
||||
nghttp2_mem *mem) {
|
||||
size_t size;
|
||||
@@ -655,6 +749,8 @@ static int hd_context_init(nghttp2_hd_context *context, nghttp2_mem *mem) {
|
||||
}
|
||||
|
||||
context->hd_table_bufsize = 0;
|
||||
context->next_seq = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -676,6 +772,8 @@ int nghttp2_hd_deflate_init2(nghttp2_hd_deflater *deflater,
|
||||
return rv;
|
||||
}
|
||||
|
||||
hd_map_init(&deflater->map);
|
||||
|
||||
if (deflate_hd_table_bufsize_max < NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE) {
|
||||
deflater->notify_table_size_change = 1;
|
||||
deflater->ctx.hd_table_bufsize_max = deflate_hd_table_bufsize_max;
|
||||
@@ -703,7 +801,7 @@ int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater, nghttp2_mem *mem) {
|
||||
inflater->nv_keep = NULL;
|
||||
|
||||
inflater->opcode = NGHTTP2_HD_OPCODE_NONE;
|
||||
inflater->state = NGHTTP2_HD_STATE_OPCODE;
|
||||
inflater->state = NGHTTP2_HD_STATE_INFLATE_START;
|
||||
|
||||
rv = nghttp2_bufs_init3(&inflater->nvbufs, NGHTTP2_HD_MAX_NV / 8, 8, 1, 0,
|
||||
mem);
|
||||
@@ -779,7 +877,7 @@ static int emit_literal_header(nghttp2_nv *nv_out, int *token_out,
|
||||
}
|
||||
|
||||
static size_t count_encoded_length(size_t n, size_t prefix) {
|
||||
size_t k = (1 << prefix) - 1;
|
||||
size_t k = (size_t)((1 << prefix) - 1);
|
||||
size_t len = 0;
|
||||
|
||||
if (n < k) {
|
||||
@@ -796,21 +894,23 @@ static size_t count_encoded_length(size_t n, size_t prefix) {
|
||||
}
|
||||
|
||||
static size_t encode_length(uint8_t *buf, size_t n, size_t prefix) {
|
||||
size_t k = (1 << prefix) - 1;
|
||||
size_t k = (size_t)((1 << prefix) - 1);
|
||||
uint8_t *begin = buf;
|
||||
|
||||
*buf &= ~k;
|
||||
*buf = (uint8_t)(*buf & ~k);
|
||||
|
||||
if (n < k) {
|
||||
*buf |= n;
|
||||
*buf = (uint8_t)(*buf | n);
|
||||
return 1;
|
||||
}
|
||||
|
||||
*buf++ |= k;
|
||||
*buf = (uint8_t)(*buf | k);
|
||||
++buf;
|
||||
|
||||
n -= k;
|
||||
|
||||
for (; n >= 128; n >>= 7) {
|
||||
*buf++ = (1 << 7) | (n & 0x7f);
|
||||
*buf++ = (uint8_t)((1 << 7) | (n & 0x7f));
|
||||
}
|
||||
|
||||
*buf++ = (uint8_t)n;
|
||||
@@ -838,7 +938,7 @@ static size_t encode_length(uint8_t *buf, size_t n, size_t prefix) {
|
||||
static ssize_t decode_length(uint32_t *res, size_t *shift_ptr, int *final,
|
||||
uint32_t initial, size_t shift, uint8_t *in,
|
||||
uint8_t *last, size_t prefix) {
|
||||
uint32_t k = (1 << prefix) - 1;
|
||||
uint32_t k = (uint8_t)((1 << prefix) - 1);
|
||||
uint32_t n = initial;
|
||||
uint8_t *start = in;
|
||||
|
||||
@@ -1080,10 +1180,10 @@ static int emit_newname_block(nghttp2_bufs *bufs, const nghttp2_nv *nv,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static nghttp2_hd_entry *add_hd_table_incremental(nghttp2_hd_context *context,
|
||||
const nghttp2_nv *nv,
|
||||
int token,
|
||||
uint8_t entry_flags) {
|
||||
static nghttp2_hd_entry *
|
||||
add_hd_table_incremental(nghttp2_hd_context *context, const nghttp2_nv *nv,
|
||||
int token, uint8_t entry_flags, nghttp2_hd_map *map,
|
||||
uint32_t hash) {
|
||||
int rv;
|
||||
nghttp2_hd_entry *new_ent;
|
||||
size_t room;
|
||||
@@ -1104,6 +1204,9 @@ static nghttp2_hd_entry *add_hd_table_incremental(nghttp2_hd_context *context,
|
||||
ent->nv.name, ent->nv.value));
|
||||
|
||||
hd_ringbuf_pop_back(&context->hd_table);
|
||||
if (map) {
|
||||
hd_map_remove(map, ent);
|
||||
}
|
||||
if (--ent->ref == 0) {
|
||||
nghttp2_hd_entry_free(ent, mem);
|
||||
nghttp2_mem_free(mem, ent);
|
||||
@@ -1151,23 +1254,21 @@ static nghttp2_hd_entry *add_hd_table_incremental(nghttp2_hd_context *context,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
new_ent->seq = context->next_seq++;
|
||||
new_ent->hash = hash;
|
||||
|
||||
DEBUGF(fprintf(stderr, "deflatehd: indexed at %zu\n",
|
||||
context->hd_table.len + NGHTTP2_STATIC_TABLE_LENGTH));
|
||||
|
||||
if (map) {
|
||||
hd_map_insert(map, new_ent);
|
||||
}
|
||||
|
||||
context->hd_table_bufsize += room;
|
||||
}
|
||||
return new_ent;
|
||||
}
|
||||
|
||||
static int name_eq(const nghttp2_nv *a, const nghttp2_nv *b) {
|
||||
return a->namelen == b->namelen &&
|
||||
a->name[a->namelen - 1] == b->name[a->namelen - 1] &&
|
||||
memeq(a->name, b->name, a->namelen);
|
||||
}
|
||||
|
||||
static int value_eq(const nghttp2_nv *a, const nghttp2_nv *b) {
|
||||
return a->valuelen == b->valuelen &&
|
||||
a->value[a->valuelen - 1] == b->value[a->valuelen - 1] &&
|
||||
memeq(a->value, b->value, a->valuelen);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
ssize_t index;
|
||||
/* Nonzero if both name and value are matched. */
|
||||
@@ -1197,9 +1298,11 @@ static search_result search_static_table(const nghttp2_nv *nv, int token,
|
||||
|
||||
static search_result search_hd_table(nghttp2_hd_context *context,
|
||||
const nghttp2_nv *nv, int token,
|
||||
int indexing_mode) {
|
||||
int indexing_mode, nghttp2_hd_map *map,
|
||||
uint32_t hash) {
|
||||
search_result res = {-1, 0};
|
||||
size_t i;
|
||||
nghttp2_hd_entry *ent;
|
||||
int exact_match;
|
||||
|
||||
if (token >= 0 && token <= NGHTTP2_TOKEN_WWW_AUTHENTICATE) {
|
||||
res = search_static_table(nv, token, indexing_mode);
|
||||
@@ -1208,27 +1311,27 @@ static search_result search_hd_table(nghttp2_hd_context *context,
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < context->hd_table.len; ++i) {
|
||||
nghttp2_hd_entry *ent = hd_ringbuf_get(&context->hd_table, i);
|
||||
if (ent->token != token || (token == -1 && !name_eq(&ent->nv, nv))) {
|
||||
continue;
|
||||
}
|
||||
exact_match = 0;
|
||||
ent = hd_map_find(map, &exact_match, nv, token, hash);
|
||||
if (ent == NULL) {
|
||||
return res;
|
||||
}
|
||||
|
||||
if (res.index == -1) {
|
||||
res.index = (ssize_t)(i + NGHTTP2_STATIC_TABLE_LENGTH);
|
||||
}
|
||||
if (res.index != -1 && !exact_match) {
|
||||
return res;
|
||||
}
|
||||
|
||||
if (indexing_mode != NGHTTP2_HD_NEVER_INDEXING && value_eq(&ent->nv, nv)) {
|
||||
res.index = (ssize_t)(i + NGHTTP2_STATIC_TABLE_LENGTH);
|
||||
res.name_value_match = 1;
|
||||
return res;
|
||||
}
|
||||
res.index = context->next_seq - 1 - ent->seq + NGHTTP2_STATIC_TABLE_LENGTH;
|
||||
|
||||
if (exact_match) {
|
||||
res.name_value_match = 1;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void hd_context_shrink_table_size(nghttp2_hd_context *context) {
|
||||
static void hd_context_shrink_table_size(nghttp2_hd_context *context,
|
||||
nghttp2_hd_map *map) {
|
||||
nghttp2_mem *mem;
|
||||
|
||||
mem = context->mem;
|
||||
@@ -1239,6 +1342,9 @@ static void hd_context_shrink_table_size(nghttp2_hd_context *context) {
|
||||
nghttp2_hd_entry *ent = hd_ringbuf_get(&context->hd_table, idx);
|
||||
context->hd_table_bufsize -= entry_room(ent->nv.namelen, ent->nv.valuelen);
|
||||
hd_ringbuf_pop_back(&context->hd_table);
|
||||
if (map) {
|
||||
hd_map_remove(map, ent);
|
||||
}
|
||||
if (--ent->ref == 0) {
|
||||
nghttp2_hd_entry_free(ent, mem);
|
||||
nghttp2_mem_free(mem, ent);
|
||||
@@ -1258,15 +1364,24 @@ int nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater,
|
||||
|
||||
deflater->notify_table_size_change = 1;
|
||||
|
||||
hd_context_shrink_table_size(&deflater->ctx);
|
||||
hd_context_shrink_table_size(&deflater->ctx, &deflater->map);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nghttp2_hd_inflate_change_table_size(nghttp2_hd_inflater *inflater,
|
||||
size_t settings_hd_table_bufsize_max) {
|
||||
switch (inflater->state) {
|
||||
case NGHTTP2_HD_STATE_EXPECT_TABLE_SIZE:
|
||||
case NGHTTP2_HD_STATE_INFLATE_START:
|
||||
break;
|
||||
default:
|
||||
return NGHTTP2_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
inflater->state = NGHTTP2_HD_STATE_EXPECT_TABLE_SIZE;
|
||||
inflater->settings_hd_table_bufsize_max = settings_hd_table_bufsize_max;
|
||||
inflater->ctx.hd_table_bufsize_max = settings_hd_table_bufsize_max;
|
||||
hd_context_shrink_table_size(&inflater->ctx);
|
||||
hd_context_shrink_table_size(&inflater->ctx, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1311,12 +1426,18 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs,
|
||||
int indexing_mode;
|
||||
int token;
|
||||
nghttp2_mem *mem;
|
||||
uint32_t hash;
|
||||
|
||||
DEBUGF(fprintf(stderr, "deflatehd: deflating %s: %s\n", nv->name, nv->value));
|
||||
|
||||
mem = deflater->ctx.mem;
|
||||
|
||||
token = lookup_token(nv->name, nv->namelen);
|
||||
if (token == -1 || token > NGHTTP2_TOKEN_WWW_AUTHENTICATE) {
|
||||
hash = name_hash(nv);
|
||||
} else {
|
||||
hash = static_table[token].hash;
|
||||
}
|
||||
|
||||
/* Don't index authorization header field since it may contain low
|
||||
entropy secret data (e.g., id/password). Also cookie header
|
||||
@@ -1329,7 +1450,8 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs,
|
||||
? NGHTTP2_HD_NEVER_INDEXING
|
||||
: hd_deflate_decide_indexing(deflater, nv, token);
|
||||
|
||||
res = search_hd_table(&deflater->ctx, nv, token, indexing_mode);
|
||||
res = search_hd_table(&deflater->ctx, nv, token, indexing_mode,
|
||||
&deflater->map, hash);
|
||||
|
||||
idx = res.index;
|
||||
|
||||
@@ -1337,7 +1459,7 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs,
|
||||
|
||||
DEBUGF(fprintf(stderr, "deflatehd: name/value match index=%zd\n", idx));
|
||||
|
||||
rv = emit_indexed_block(bufs, idx);
|
||||
rv = emit_indexed_block(bufs, (size_t)idx);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
@@ -1354,13 +1476,16 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs,
|
||||
if (idx != -1 && idx < (ssize_t)NGHTTP2_STATIC_TABLE_LENGTH) {
|
||||
nghttp2_nv nv_indname;
|
||||
nv_indname = *nv;
|
||||
nv_indname.name = nghttp2_hd_table_get(&deflater->ctx, idx)->nv.name;
|
||||
nv_indname.name =
|
||||
nghttp2_hd_table_get(&deflater->ctx, (size_t)idx)->nv.name;
|
||||
new_ent = add_hd_table_incremental(&deflater->ctx, &nv_indname, token,
|
||||
NGHTTP2_HD_FLAG_VALUE_ALLOC);
|
||||
NGHTTP2_HD_FLAG_VALUE_ALLOC,
|
||||
&deflater->map, hash);
|
||||
} else {
|
||||
new_ent = add_hd_table_incremental(&deflater->ctx, nv, token,
|
||||
NGHTTP2_HD_FLAG_NAME_ALLOC |
|
||||
NGHTTP2_HD_FLAG_VALUE_ALLOC);
|
||||
NGHTTP2_HD_FLAG_VALUE_ALLOC,
|
||||
&deflater->map, hash);
|
||||
}
|
||||
if (!new_ent) {
|
||||
return NGHTTP2_ERR_HEADER_COMP;
|
||||
@@ -1373,7 +1498,7 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs,
|
||||
if (idx == -1) {
|
||||
rv = emit_newname_block(bufs, nv, indexing_mode);
|
||||
} else {
|
||||
rv = emit_indname_block(bufs, idx, nv, indexing_mode);
|
||||
rv = emit_indname_block(bufs, (size_t)idx, nv, indexing_mode);
|
||||
}
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
@@ -1609,7 +1734,7 @@ static ssize_t hd_inflate_read_huff(nghttp2_hd_inflater *inflater,
|
||||
final = 1;
|
||||
}
|
||||
readlen = nghttp2_hd_huff_decode(&inflater->huff_decode_ctx, bufs, in,
|
||||
last - in, final);
|
||||
(size_t)(last - in), final);
|
||||
|
||||
if (readlen < 0) {
|
||||
DEBUGF(fprintf(stderr, "inflatehd: huffman decoding failed\n"));
|
||||
@@ -1683,7 +1808,7 @@ static int hd_inflate_remove_bufs(nghttp2_hd_inflater *inflater, nghttp2_nv *nv,
|
||||
|
||||
nghttp2_bufs_reset(&inflater->nvbufs);
|
||||
|
||||
buflen = rv;
|
||||
buflen = (size_t)rv;
|
||||
|
||||
if (value_only) {
|
||||
/* we don't use this value, so no need to NULL-terminate */
|
||||
@@ -1730,6 +1855,47 @@ static int hd_inflate_remove_bufs(nghttp2_hd_inflater *inflater, nghttp2_nv *nv,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hd_inflate_remove_bufs_with_name(nghttp2_hd_inflater *inflater,
|
||||
nghttp2_nv *nv,
|
||||
nghttp2_hd_entry *ent_name) {
|
||||
#ifndef NDEBUG
|
||||
size_t rv;
|
||||
#endif
|
||||
size_t buflen;
|
||||
uint8_t *buf;
|
||||
nghttp2_mem *mem;
|
||||
|
||||
mem = inflater->ctx.mem;
|
||||
|
||||
/* Allocate buffer including name in ent_name, plus terminating
|
||||
NULL. */
|
||||
buflen = ent_name->nv.namelen + 1 + nghttp2_bufs_len(&inflater->nvbufs);
|
||||
|
||||
buf = nghttp2_mem_malloc(mem, buflen);
|
||||
if (buf == NULL) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
/* Copy including terminal NULL */
|
||||
memcpy(buf, ent_name->nv.name, ent_name->nv.namelen + 1);
|
||||
#ifndef NDEBUG
|
||||
rv =
|
||||
#endif
|
||||
nghttp2_bufs_remove_copy(&inflater->nvbufs,
|
||||
buf + ent_name->nv.namelen + 1);
|
||||
assert(ent_name->nv.namelen + 1 + rv == buflen);
|
||||
|
||||
nghttp2_bufs_reset(&inflater->nvbufs);
|
||||
|
||||
nv->name = buf;
|
||||
nv->namelen = ent_name->nv.namelen;
|
||||
|
||||
nv->value = buf + nv->namelen + 1;
|
||||
nv->valuelen = buflen - nv->namelen - 2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Finalize literal header representation - new name- reception. If
|
||||
* header is emitted, |*nv_out| is filled with that value and 0 is
|
||||
@@ -1769,8 +1935,9 @@ static int hd_inflate_commit_newname(nghttp2_hd_inflater *inflater,
|
||||
management. */
|
||||
ent_flags = NGHTTP2_HD_FLAG_NAME_ALLOC | NGHTTP2_HD_FLAG_NAME_GIFT;
|
||||
|
||||
new_ent = add_hd_table_incremental(
|
||||
&inflater->ctx, &nv, lookup_token(nv.name, nv.namelen), ent_flags);
|
||||
new_ent = add_hd_table_incremental(&inflater->ctx, &nv,
|
||||
lookup_token(nv.name, nv.namelen),
|
||||
ent_flags, NULL, 0);
|
||||
|
||||
if (new_ent) {
|
||||
emit_indexed_header(nv_out, token_out, new_ent);
|
||||
@@ -1813,11 +1980,6 @@ static int hd_inflate_commit_indname(nghttp2_hd_inflater *inflater,
|
||||
|
||||
mem = inflater->ctx.mem;
|
||||
|
||||
rv = hd_inflate_remove_bufs(inflater, &nv, 1 /* value only */);
|
||||
if (rv != 0) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
if (inflater->no_index) {
|
||||
nv.flags = NGHTTP2_NV_FLAG_NO_INDEX;
|
||||
} else {
|
||||
@@ -1826,31 +1988,33 @@ static int hd_inflate_commit_indname(nghttp2_hd_inflater *inflater,
|
||||
|
||||
ent_name = nghttp2_hd_table_get(&inflater->ctx, inflater->index);
|
||||
|
||||
nv.name = ent_name->nv.name;
|
||||
nv.namelen = ent_name->nv.namelen;
|
||||
|
||||
if (inflater->index_required) {
|
||||
nghttp2_hd_entry *new_ent;
|
||||
uint8_t ent_flags;
|
||||
int static_name;
|
||||
|
||||
ent_flags = NGHTTP2_HD_FLAG_VALUE_ALLOC | NGHTTP2_HD_FLAG_VALUE_GIFT;
|
||||
static_name = inflater->index < NGHTTP2_STATIC_TABLE_LENGTH;
|
||||
if (inflater->index < NGHTTP2_STATIC_TABLE_LENGTH) {
|
||||
/* We don't copy name in static table */
|
||||
rv = hd_inflate_remove_bufs(inflater, &nv, 1 /* value only */);
|
||||
if (rv != 0) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
nv.name = ent_name->nv.name;
|
||||
nv.namelen = ent_name->nv.namelen;
|
||||
|
||||
if (!static_name) {
|
||||
ent_flags |= NGHTTP2_HD_FLAG_NAME_ALLOC;
|
||||
/* For entry in static table, we must not touch ref, because it
|
||||
is shared by threads */
|
||||
++ent_name->ref;
|
||||
ent_flags = NGHTTP2_HD_FLAG_VALUE_ALLOC | NGHTTP2_HD_FLAG_VALUE_GIFT;
|
||||
} else {
|
||||
rv = hd_inflate_remove_bufs_with_name(inflater, &nv, ent_name);
|
||||
if (rv != 0) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
/* nv->name and nv->value are in the same buffer. */
|
||||
ent_flags = NGHTTP2_HD_FLAG_NAME_ALLOC | NGHTTP2_HD_FLAG_NAME_GIFT;
|
||||
}
|
||||
|
||||
new_ent = add_hd_table_incremental(&inflater->ctx, &nv, ent_name->token,
|
||||
ent_flags);
|
||||
ent_flags, NULL, 0);
|
||||
|
||||
if (!static_name && --ent_name->ref == 0) {
|
||||
nghttp2_hd_entry_free(ent_name, mem);
|
||||
nghttp2_mem_free(mem, ent_name);
|
||||
}
|
||||
/* At this point, ent_name might be deleted. */
|
||||
|
||||
if (new_ent) {
|
||||
emit_indexed_header(nv_out, token_out, new_ent);
|
||||
@@ -1860,11 +2024,23 @@ static int hd_inflate_commit_indname(nghttp2_hd_inflater *inflater,
|
||||
return 0;
|
||||
}
|
||||
|
||||
nghttp2_mem_free(mem, nv.value);
|
||||
if (inflater->index < NGHTTP2_STATIC_TABLE_LENGTH) {
|
||||
nghttp2_mem_free(mem, nv.value);
|
||||
} else {
|
||||
nghttp2_mem_free(mem, nv.name);
|
||||
}
|
||||
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
rv = hd_inflate_remove_bufs(inflater, &nv, 1 /* value only */);
|
||||
if (rv != 0) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
nv.name = ent_name->nv.name;
|
||||
nv.namelen = ent_name->nv.namelen;
|
||||
|
||||
emit_literal_header(nv_out, token_out, &nv);
|
||||
|
||||
if (nv.value != inflater->nvbufs.head->buf.pos) {
|
||||
@@ -1904,9 +2080,25 @@ ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater,
|
||||
for (; in != last || busy;) {
|
||||
busy = 0;
|
||||
switch (inflater->state) {
|
||||
case NGHTTP2_HD_STATE_EXPECT_TABLE_SIZE:
|
||||
if ((*in & 0xe0u) != 0x20u) {
|
||||
DEBUGF(fprintf(stderr, "inflatehd: header table size change was "
|
||||
"expected, but saw 0x%02x as first byte",
|
||||
*in));
|
||||
rv = NGHTTP2_ERR_HEADER_COMP;
|
||||
goto fail;
|
||||
}
|
||||
/* fall through */
|
||||
case NGHTTP2_HD_STATE_INFLATE_START:
|
||||
case NGHTTP2_HD_STATE_OPCODE:
|
||||
if ((*in & 0xe0u) == 0x20u) {
|
||||
DEBUGF(fprintf(stderr, "inflatehd: header table size change\n"));
|
||||
if (inflater->state == NGHTTP2_HD_STATE_OPCODE) {
|
||||
DEBUGF(fprintf(stderr, "inflatehd: header table size change must "
|
||||
"appear at the head of header block\n"));
|
||||
rv = NGHTTP2_ERR_HEADER_COMP;
|
||||
goto fail;
|
||||
}
|
||||
inflater->opcode = NGHTTP2_HD_OPCODE_INDEXED;
|
||||
inflater->state = NGHTTP2_HD_STATE_READ_TABLE_SIZE;
|
||||
} else if (*in & 0x80u) {
|
||||
@@ -1949,8 +2141,8 @@ ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater,
|
||||
}
|
||||
DEBUGF(fprintf(stderr, "inflatehd: table_size=%zu\n", inflater->left));
|
||||
inflater->ctx.hd_table_bufsize_max = inflater->left;
|
||||
hd_context_shrink_table_size(&inflater->ctx);
|
||||
inflater->state = NGHTTP2_HD_STATE_OPCODE;
|
||||
hd_context_shrink_table_size(&inflater->ctx, NULL);
|
||||
inflater->state = NGHTTP2_HD_STATE_INFLATE_START;
|
||||
break;
|
||||
case NGHTTP2_HD_STATE_READ_INDEX: {
|
||||
size_t prefixlen;
|
||||
@@ -2235,6 +2427,7 @@ fail:
|
||||
|
||||
int nghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater) {
|
||||
hd_inflate_keep_free(inflater);
|
||||
inflater->state = NGHTTP2_HD_STATE_INFLATE_START;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ typedef enum {
|
||||
NGHTTP2_TOKEN_CONNECTION,
|
||||
NGHTTP2_TOKEN_KEEP_ALIVE,
|
||||
NGHTTP2_TOKEN_PROXY_CONNECTION,
|
||||
NGHTTP2_TOKEN_UPGRADE,
|
||||
NGHTTP2_TOKEN_UPGRADE
|
||||
} nghttp2_token;
|
||||
|
||||
typedef enum {
|
||||
@@ -126,15 +126,25 @@ typedef enum {
|
||||
NGHTTP2_HD_FLAG_VALUE_GIFT = 1 << 3
|
||||
} nghttp2_hd_flags;
|
||||
|
||||
typedef struct {
|
||||
struct nghttp2_hd_entry;
|
||||
typedef struct nghttp2_hd_entry nghttp2_hd_entry;
|
||||
|
||||
struct nghttp2_hd_entry {
|
||||
nghttp2_nv nv;
|
||||
/* The next entry which shares same bucket in hash table. */
|
||||
nghttp2_hd_entry *next;
|
||||
/* The sequence number. We will increment it by one whenever we
|
||||
store nghttp2_hd_entry to dynamic header table. */
|
||||
uint32_t seq;
|
||||
/* The hash value for header name (nv.name). */
|
||||
uint32_t 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 **buffer;
|
||||
@@ -151,6 +161,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,
|
||||
@@ -181,14 +193,21 @@ typedef struct {
|
||||
size_t hd_table_bufsize;
|
||||
/* The effective header table size. */
|
||||
size_t hd_table_bufsize_max;
|
||||
/* Next sequence number for nghttp2_hd_entry */
|
||||
uint32_t next_seq;
|
||||
/* If inflate/deflate error occurred, this value is set to 1 and
|
||||
further invocation of inflate/deflate will fail with
|
||||
NGHTTP2_ERR_HEADER_COMP. */
|
||||
uint8_t bad;
|
||||
} nghttp2_hd_context;
|
||||
|
||||
#define HD_MAP_SIZE 128
|
||||
|
||||
typedef struct { nghttp2_hd_entry *table[HD_MAP_SIZE]; } nghttp2_hd_map;
|
||||
|
||||
struct nghttp2_hd_deflater {
|
||||
nghttp2_hd_context ctx;
|
||||
nghttp2_hd_map map;
|
||||
/* The upper limit of the header table size the deflater accepts. */
|
||||
size_t deflate_hd_table_bufsize_max;
|
||||
/* Minimum header table size notified in the next context update */
|
||||
@@ -356,8 +375,8 @@ int nghttp2_hd_emit_newname_block(nghttp2_bufs *bufs, nghttp2_nv *nv,
|
||||
int nghttp2_hd_emit_table_size(nghttp2_bufs *bufs, size_t table_size);
|
||||
|
||||
/* For unittesting purpose */
|
||||
nghttp2_hd_entry *nghttp2_hd_table_get(nghttp2_hd_context *context,
|
||||
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,
|
||||
|
||||
@@ -48,17 +48,17 @@ static ssize_t huff_encode_sym(nghttp2_bufs *bufs, size_t *avail_ptr,
|
||||
|
||||
/* We assume that sym->nbits <= 32 */
|
||||
if (rembits > nbits) {
|
||||
nghttp2_bufs_fast_orb_hold(bufs, code << (rembits - nbits));
|
||||
nghttp2_bufs_fast_orb_hold(bufs, (uint8_t)(code << (rembits - nbits)));
|
||||
return (ssize_t)(rembits - nbits);
|
||||
}
|
||||
|
||||
if (rembits == nbits) {
|
||||
nghttp2_bufs_fast_orb(bufs, code);
|
||||
nghttp2_bufs_fast_orb(bufs, (uint8_t)code);
|
||||
--*avail_ptr;
|
||||
return 8;
|
||||
}
|
||||
|
||||
nghttp2_bufs_fast_orb(bufs, code >> (nbits - rembits));
|
||||
nghttp2_bufs_fast_orb(bufs, (uint8_t)(code >> (nbits - rembits)));
|
||||
--*avail_ptr;
|
||||
|
||||
nbits -= rembits;
|
||||
@@ -80,34 +80,34 @@ static ssize_t huff_encode_sym(nghttp2_bufs *bufs, size_t *avail_ptr,
|
||||
|
||||
/* fast path, since most code is less than 8 */
|
||||
if (nbits < 8) {
|
||||
nghttp2_bufs_fast_addb_hold(bufs, code);
|
||||
nghttp2_bufs_fast_addb_hold(bufs, (uint8_t)code);
|
||||
*avail_ptr = nghttp2_bufs_cur_avail(bufs);
|
||||
return (ssize_t)(8 - nbits);
|
||||
}
|
||||
|
||||
/* handle longer code path */
|
||||
if (nbits > 24) {
|
||||
nghttp2_bufs_fast_addb(bufs, code >> 24);
|
||||
nghttp2_bufs_fast_addb(bufs, (uint8_t)(code >> 24));
|
||||
nbits -= 8;
|
||||
}
|
||||
|
||||
if (nbits > 16) {
|
||||
nghttp2_bufs_fast_addb(bufs, code >> 16);
|
||||
nghttp2_bufs_fast_addb(bufs, (uint8_t)(code >> 16));
|
||||
nbits -= 8;
|
||||
}
|
||||
|
||||
if (nbits > 8) {
|
||||
nghttp2_bufs_fast_addb(bufs, code >> 8);
|
||||
nghttp2_bufs_fast_addb(bufs, (uint8_t)(code >> 8));
|
||||
nbits -= 8;
|
||||
}
|
||||
|
||||
if (nbits == 8) {
|
||||
nghttp2_bufs_fast_addb(bufs, code);
|
||||
nghttp2_bufs_fast_addb(bufs, (uint8_t)code);
|
||||
*avail_ptr = nghttp2_bufs_cur_avail(bufs);
|
||||
return 8;
|
||||
}
|
||||
|
||||
nghttp2_bufs_fast_addb_hold(bufs, code);
|
||||
nghttp2_bufs_fast_addb_hold(bufs, (uint8_t)code);
|
||||
*avail_ptr = nghttp2_bufs_cur_avail(bufs);
|
||||
return (ssize_t)(8 - nbits);
|
||||
}
|
||||
@@ -145,7 +145,7 @@ int nghttp2_hd_huff_encode(nghttp2_bufs *bufs, const uint8_t *src,
|
||||
avail = nghttp2_bufs_cur_avail(bufs);
|
||||
}
|
||||
}
|
||||
rembits = huff_encode_sym(bufs, &avail, rembits, sym);
|
||||
rembits = huff_encode_sym(bufs, &avail, (size_t)rembits, sym);
|
||||
if (rembits < 0) {
|
||||
return (int)rembits;
|
||||
}
|
||||
@@ -157,7 +157,7 @@ int nghttp2_hd_huff_encode(nghttp2_bufs *bufs, const uint8_t *src,
|
||||
const nghttp2_huff_sym *sym = &huff_sym_table[256];
|
||||
assert(avail);
|
||||
/* Caution we no longer adjust avail here */
|
||||
nghttp2_bufs_fast_orb(bufs, sym->code >> (sym->nbits - rembits));
|
||||
nghttp2_bufs_fast_orb(bufs, (uint8_t)(sym->code >> (sym->nbits - rembits)));
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -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;
|
||||
|
||||
@@ -71,4 +71,7 @@ typedef struct {
|
||||
uint32_t code;
|
||||
} nghttp2_huff_sym;
|
||||
|
||||
extern const nghttp2_huff_sym huff_sym_table[];
|
||||
extern const nghttp2_huff_decode huff_decode_table[][16];
|
||||
|
||||
#endif /* NGHTTP2_HD_HUFFMAN_H */
|
||||
|
||||
@@ -67,7 +67,7 @@ void *nghttp2_memdup(const void *src, size_t n, nghttp2_mem *mem) {
|
||||
}
|
||||
|
||||
/* Generated by gendowncasetbl.py */
|
||||
static const int DOWNCASE_TBL[] = {
|
||||
static const uint8_t DOWNCASE_TBL[] = {
|
||||
0 /* NUL */, 1 /* SOH */, 2 /* STX */, 3 /* ETX */,
|
||||
4 /* EOT */, 5 /* ENQ */, 6 /* ACK */, 7 /* BEL */,
|
||||
8 /* BS */, 9 /* HT */, 10 /* LF */, 11 /* VT */,
|
||||
@@ -292,17 +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";
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
#include "nghttp2_mem.h"
|
||||
@@ -39,6 +40,9 @@
|
||||
|
||||
#define lstreq(A, B, N) ((sizeof((A)) - 1) == (N) && memcmp((A), (B), (N)) == 0)
|
||||
|
||||
#define nghttp2_struct_of(ptr, type, member) \
|
||||
((type *)(void *)((char *)(ptr) - offsetof(type, member)))
|
||||
|
||||
/*
|
||||
* Copies 2 byte unsigned integer |n| in host byte order to |buf| in
|
||||
* network byte order.
|
||||
|
||||
@@ -31,8 +31,8 @@
|
||||
#include "nghttp2_hd.h"
|
||||
#include "nghttp2_helper.h"
|
||||
|
||||
static char downcase(char c) {
|
||||
return 'A' <= c && c <= 'Z' ? (c - 'A' + 'a') : c;
|
||||
static uint8_t downcase(uint8_t c) {
|
||||
return 'A' <= c && c <= 'Z' ? (uint8_t)(c - 'A' + 'a') : c;
|
||||
}
|
||||
|
||||
static int memieq(const void *a, const void *b, size_t n) {
|
||||
@@ -90,7 +90,7 @@ static int check_pseudo_header(nghttp2_stream *stream, const nghttp2_nv *nv,
|
||||
if (lws(nv->value, nv->valuelen)) {
|
||||
return 0;
|
||||
}
|
||||
stream->http_flags |= flag;
|
||||
stream->http_flags = (uint16_t)(stream->http_flags | flag);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -243,7 +243,7 @@ static int http_response_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
|
||||
if (nv->valuelen != 3) {
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
stream->status_code = parse_uint(nv->value, nv->valuelen);
|
||||
stream->status_code = (int16_t)parse_uint(nv->value, nv->valuelen);
|
||||
if (stream->status_code == -1) {
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
@@ -301,7 +301,7 @@ int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
|
||||
}
|
||||
/* header field name must be lower-cased without exception */
|
||||
for (i = 0; i < nv->namelen; ++i) {
|
||||
char c = nv->name[i];
|
||||
uint8_t c = nv->name[i];
|
||||
if ('A' <= c && c <= 'Z') {
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
@@ -368,14 +368,16 @@ int nghttp2_http_on_response_headers(nghttp2_stream *stream) {
|
||||
|
||||
if (stream->status_code / 100 == 1) {
|
||||
/* non-final response */
|
||||
stream->http_flags = (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_ALL) |
|
||||
NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE;
|
||||
stream->http_flags =
|
||||
(uint16_t)((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_ALL) |
|
||||
NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE);
|
||||
stream->content_length = -1;
|
||||
stream->status_code = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
stream->http_flags &= ~NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE;
|
||||
stream->http_flags =
|
||||
(uint16_t)(stream->http_flags & ~NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE);
|
||||
|
||||
if (!expect_response_body(stream)) {
|
||||
stream->content_length = 0;
|
||||
@@ -409,7 +411,7 @@ int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream) {
|
||||
}
|
||||
|
||||
int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n) {
|
||||
stream->recv_content_length += n;
|
||||
stream->recv_content_length += (int64_t)n;
|
||||
|
||||
if ((stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) ||
|
||||
(stream->content_length != -1 &&
|
||||
|
||||
@@ -39,7 +39,8 @@
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
typedef int (*nghttp2_compar)(const void *lhs, const void *rhs);
|
||||
/* "less" function, return nonzero if |lhs| is less than |rhs|. */
|
||||
typedef int (*nghttp2_less)(const void *lhs, const void *rhs);
|
||||
|
||||
/* Internal error code. They must be in the range [-499, -100],
|
||||
inclusive. */
|
||||
|
||||
@@ -49,7 +49,7 @@ void nghttp2_map_free(nghttp2_map *map) {
|
||||
void nghttp2_map_each_free(nghttp2_map *map,
|
||||
int (*func)(nghttp2_map_entry *entry, void *ptr),
|
||||
void *ptr) {
|
||||
size_t i;
|
||||
uint32_t i;
|
||||
for (i = 0; i < map->tablelen; ++i) {
|
||||
nghttp2_map_entry *entry;
|
||||
for (entry = map->table[i]; entry;) {
|
||||
@@ -65,7 +65,7 @@ int nghttp2_map_each(nghttp2_map *map,
|
||||
int (*func)(nghttp2_map_entry *entry, void *ptr),
|
||||
void *ptr) {
|
||||
int rv;
|
||||
size_t i;
|
||||
uint32_t i;
|
||||
for (i = 0; i < map->tablelen; ++i) {
|
||||
nghttp2_map_entry *entry;
|
||||
for (entry = map->table[i]; entry; entry = entry->next) {
|
||||
@@ -85,15 +85,16 @@ void nghttp2_map_entry_init(nghttp2_map_entry *entry, key_type key) {
|
||||
|
||||
/* Same hash function in android HashMap source code. */
|
||||
/* The |mod| must be power of 2 */
|
||||
static int32_t hash(int32_t h, size_t mod) {
|
||||
static uint32_t hash(int32_t key, uint32_t mod) {
|
||||
uint32_t h = (uint32_t)key;
|
||||
h ^= (h >> 20) ^ (h >> 12);
|
||||
h ^= (h >> 7) ^ (h >> 4);
|
||||
return h & (mod - 1);
|
||||
}
|
||||
|
||||
static int insert(nghttp2_map_entry **table, size_t tablelen,
|
||||
static int insert(nghttp2_map_entry **table, uint32_t tablelen,
|
||||
nghttp2_map_entry *entry) {
|
||||
int32_t h = hash(entry->key, tablelen);
|
||||
uint32_t h = hash(entry->key, tablelen);
|
||||
if (table[h] == NULL) {
|
||||
table[h] = entry;
|
||||
} else {
|
||||
@@ -111,8 +112,8 @@ static int insert(nghttp2_map_entry **table, size_t tablelen,
|
||||
}
|
||||
|
||||
/* new_tablelen must be power of 2 */
|
||||
static int resize(nghttp2_map *map, size_t new_tablelen) {
|
||||
size_t i;
|
||||
static int resize(nghttp2_map *map, uint32_t new_tablelen) {
|
||||
uint32_t i;
|
||||
nghttp2_map_entry **new_table;
|
||||
|
||||
new_table =
|
||||
@@ -156,7 +157,7 @@ int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_entry *new_entry) {
|
||||
}
|
||||
|
||||
nghttp2_map_entry *nghttp2_map_find(nghttp2_map *map, key_type key) {
|
||||
int32_t h;
|
||||
uint32_t h;
|
||||
nghttp2_map_entry *entry;
|
||||
h = hash(key, map->tablelen);
|
||||
for (entry = map->table[h]; entry; entry = entry->next) {
|
||||
@@ -168,7 +169,7 @@ nghttp2_map_entry *nghttp2_map_find(nghttp2_map *map, key_type key) {
|
||||
}
|
||||
|
||||
int nghttp2_map_remove(nghttp2_map *map, key_type key) {
|
||||
int32_t h;
|
||||
uint32_t h;
|
||||
nghttp2_map_entry *entry, *prev;
|
||||
h = hash(key, map->tablelen);
|
||||
prev = NULL;
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
/* Implementation of unordered map */
|
||||
|
||||
typedef uint32_t key_type;
|
||||
typedef int32_t key_type;
|
||||
|
||||
typedef struct nghttp2_map_entry {
|
||||
struct nghttp2_map_entry *next;
|
||||
@@ -49,8 +49,8 @@ typedef struct nghttp2_map_entry {
|
||||
typedef struct {
|
||||
nghttp2_map_entry **table;
|
||||
nghttp2_mem *mem;
|
||||
size_t tablelen;
|
||||
size_t size;
|
||||
uint32_t tablelen;
|
||||
} nghttp2_map;
|
||||
|
||||
/*
|
||||
|
||||
@@ -30,7 +30,7 @@ static int select_next_protocol(unsigned char **out, unsigned char *outlen,
|
||||
const unsigned char *in, unsigned int inlen,
|
||||
const char *key, unsigned int keylen) {
|
||||
unsigned int i;
|
||||
for (i = 0; i + keylen <= inlen; i += in [i] + 1) {
|
||||
for (i = 0; i + keylen <= inlen; i += (unsigned int)(in [i] + 1)) {
|
||||
if (memcmp(&in[i], key, keylen) == 0) {
|
||||
*out = (unsigned char *)&in[i + 1];
|
||||
*outlen = in[i];
|
||||
|
||||
@@ -47,12 +47,18 @@ void nghttp2_option_set_peer_max_concurrent_streams(nghttp2_option *option,
|
||||
option->peer_max_concurrent_streams = val;
|
||||
}
|
||||
|
||||
void nghttp2_option_set_recv_client_preface(nghttp2_option *option, int val) {
|
||||
option->opt_set_mask |= NGHTTP2_OPT_RECV_CLIENT_PREFACE;
|
||||
option->recv_client_preface = val;
|
||||
void nghttp2_option_set_no_recv_client_magic(nghttp2_option *option, int val) {
|
||||
option->opt_set_mask |= NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC;
|
||||
option->no_recv_client_magic = val;
|
||||
}
|
||||
|
||||
void nghttp2_option_set_no_http_messaging(nghttp2_option *option, int val) {
|
||||
option->opt_set_mask |= NGHTTP2_OPT_NO_HTTP_MESSAGING;
|
||||
option->no_http_messaging = val;
|
||||
}
|
||||
|
||||
void nghttp2_option_set_max_reserved_remote_streams(nghttp2_option *option,
|
||||
uint32_t val) {
|
||||
option->opt_set_mask |= NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS;
|
||||
option->max_reserved_remote_streams = val;
|
||||
}
|
||||
|
||||
@@ -57,8 +57,9 @@ typedef enum {
|
||||
* SETTINGS_MAX_CONCURRENT_STREAMS from the remote endpoint.
|
||||
*/
|
||||
NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS = 1 << 1,
|
||||
NGHTTP2_OPT_RECV_CLIENT_PREFACE = 1 << 2,
|
||||
NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC = 1 << 2,
|
||||
NGHTTP2_OPT_NO_HTTP_MESSAGING = 1 << 3,
|
||||
NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS = 1 << 4
|
||||
} nghttp2_option_flag;
|
||||
|
||||
/**
|
||||
@@ -74,18 +75,22 @@ struct nghttp2_option {
|
||||
* NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS
|
||||
*/
|
||||
uint32_t peer_max_concurrent_streams;
|
||||
/**
|
||||
* NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS
|
||||
*/
|
||||
uint32_t max_reserved_remote_streams;
|
||||
/**
|
||||
* NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE
|
||||
*/
|
||||
uint8_t no_auto_window_update;
|
||||
int no_auto_window_update;
|
||||
/**
|
||||
* NGHTTP2_OPT_RECV_CLIENT_PREFACE
|
||||
* NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC
|
||||
*/
|
||||
uint8_t recv_client_preface;
|
||||
int no_recv_client_magic;
|
||||
/**
|
||||
* NGHTTP2_OPT_NO_HTTP_MESSAGING
|
||||
*/
|
||||
uint8_t no_http_messaging;
|
||||
int no_http_messaging;
|
||||
};
|
||||
|
||||
#endif /* NGHTTP2_OPTION_H */
|
||||
|
||||
@@ -25,6 +25,15 @@
|
||||
#include "nghttp2_outbound_item.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
void nghttp2_outbound_item_init(nghttp2_outbound_item *item) {
|
||||
item->cycle = 0;
|
||||
item->qnext = NULL;
|
||||
item->queued = 0;
|
||||
|
||||
memset(&item->aux_data, 0, sizeof(nghttp2_aux_data));
|
||||
}
|
||||
|
||||
void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem) {
|
||||
nghttp2_frame *frame;
|
||||
@@ -65,3 +74,32 @@ void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void nghttp2_outbound_queue_init(nghttp2_outbound_queue *q) {
|
||||
q->head = q->tail = NULL;
|
||||
q->n = 0;
|
||||
}
|
||||
|
||||
void nghttp2_outbound_queue_push(nghttp2_outbound_queue *q,
|
||||
nghttp2_outbound_item *item) {
|
||||
if (q->tail) {
|
||||
q->tail = q->tail->qnext = item;
|
||||
} else {
|
||||
q->head = q->tail = item;
|
||||
}
|
||||
++q->n;
|
||||
}
|
||||
|
||||
void nghttp2_outbound_queue_pop(nghttp2_outbound_queue *q) {
|
||||
nghttp2_outbound_item *item;
|
||||
if (!q->head) {
|
||||
return;
|
||||
}
|
||||
item = q->head;
|
||||
q->head = q->head->qnext;
|
||||
item->qnext = NULL;
|
||||
if (!q->head) {
|
||||
q->tail = NULL;
|
||||
}
|
||||
--q->n;
|
||||
}
|
||||
|
||||
@@ -33,13 +33,6 @@
|
||||
#include "nghttp2_frame.h"
|
||||
#include "nghttp2_mem.h"
|
||||
|
||||
/* A bit higher priority for non-DATA frames */
|
||||
#define NGHTTP2_OB_EX_CYCLE 2
|
||||
/* Even more higher priority for SETTINGS frame */
|
||||
#define NGHTTP2_OB_SETTINGS_CYCLE 1
|
||||
/* Highest priority for PING frame */
|
||||
#define NGHTTP2_OB_PING_CYCLE 0
|
||||
|
||||
/* struct used for HEADERS and PUSH_PROMISE frame */
|
||||
typedef struct {
|
||||
nghttp2_data_provider data_prd;
|
||||
@@ -88,7 +81,7 @@ typedef enum {
|
||||
/* indicates that this GOAWAY is just a notification for graceful
|
||||
shutdown. No nghttp2_session.goaway_flags should be updated on
|
||||
the reaction to this frame. */
|
||||
NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE = 0x2,
|
||||
NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE = 0x2
|
||||
} nghttp2_goaway_aux_flag;
|
||||
|
||||
/* struct used for GOAWAY frame */
|
||||
@@ -104,10 +97,12 @@ typedef union {
|
||||
nghttp2_goaway_aux_data goaway;
|
||||
} nghttp2_aux_data;
|
||||
|
||||
typedef struct {
|
||||
struct nghttp2_outbound_item;
|
||||
typedef struct nghttp2_outbound_item nghttp2_outbound_item;
|
||||
|
||||
struct nghttp2_outbound_item {
|
||||
nghttp2_frame frame;
|
||||
nghttp2_aux_data aux_data;
|
||||
int64_t seq;
|
||||
/* The priority used in priority comparion. Smaller is served
|
||||
ealier. For PING, SETTINGS and non-DATA frames (excluding
|
||||
response HEADERS frame) have dedicated cycle value defined above.
|
||||
@@ -116,9 +111,18 @@ typedef struct {
|
||||
that the amount of transmission is distributed across streams
|
||||
proportional to effective weight (inside a tree). */
|
||||
uint64_t cycle;
|
||||
/* nonzero if this object is queued. */
|
||||
nghttp2_outbound_item *qnext;
|
||||
/* nonzero if this object is queued, except for DATA or HEADERS
|
||||
which are attached to stream as item. */
|
||||
uint8_t queued;
|
||||
} nghttp2_outbound_item;
|
||||
};
|
||||
|
||||
/*
|
||||
* Initializes |item|. No memory allocation is done in this function.
|
||||
* Don't call nghttp2_outbound_item_free() until frame member is
|
||||
* initialized.
|
||||
*/
|
||||
void nghttp2_outbound_item_init(nghttp2_outbound_item *item);
|
||||
|
||||
/*
|
||||
* Deallocates resource for |item|. If |item| is NULL, this function
|
||||
@@ -126,4 +130,29 @@ typedef struct {
|
||||
*/
|
||||
void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem);
|
||||
|
||||
/*
|
||||
* queue for nghttp2_outbound_item.
|
||||
*/
|
||||
typedef struct {
|
||||
nghttp2_outbound_item *head, *tail;
|
||||
/* number of items in this queue. */
|
||||
size_t n;
|
||||
} nghttp2_outbound_queue;
|
||||
|
||||
void nghttp2_outbound_queue_init(nghttp2_outbound_queue *q);
|
||||
|
||||
/* Pushes |item| into |q| */
|
||||
void nghttp2_outbound_queue_push(nghttp2_outbound_queue *q,
|
||||
nghttp2_outbound_item *item);
|
||||
|
||||
/* Pops |item| at the top from |q|. If |q| is empty, nothing
|
||||
happens. */
|
||||
void nghttp2_outbound_queue_pop(nghttp2_outbound_queue *q);
|
||||
|
||||
/* Returns the top item. */
|
||||
#define nghttp2_outbound_queue_top(Q) ((Q)->head)
|
||||
|
||||
/* Returns the size of the queue */
|
||||
#define nghttp2_outbound_queue_size(Q) ((Q)->n)
|
||||
|
||||
#endif /* NGHTTP2_OUTBOUND_ITEM_H */
|
||||
|
||||
@@ -24,15 +24,17 @@
|
||||
*/
|
||||
#include "nghttp2_pq.h"
|
||||
|
||||
int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_compar compar, nghttp2_mem *mem) {
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "nghttp2_helper.h"
|
||||
|
||||
int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_less less, nghttp2_mem *mem) {
|
||||
pq->mem = mem;
|
||||
pq->capacity = 128;
|
||||
pq->q = nghttp2_mem_malloc(mem, pq->capacity * sizeof(void *));
|
||||
if (pq->q == NULL) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
pq->capacity = 0;
|
||||
pq->q = NULL;
|
||||
pq->length = 0;
|
||||
pq->compar = compar;
|
||||
pq->less = less;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -42,41 +44,48 @@ void nghttp2_pq_free(nghttp2_pq *pq) {
|
||||
}
|
||||
|
||||
static void swap(nghttp2_pq *pq, size_t i, size_t j) {
|
||||
void *t = pq->q[i];
|
||||
nghttp2_pq_entry *t = pq->q[i];
|
||||
pq->q[i] = pq->q[j];
|
||||
pq->q[i]->index = i;
|
||||
pq->q[j] = t;
|
||||
pq->q[j]->index = j;
|
||||
}
|
||||
|
||||
static void bubble_up(nghttp2_pq *pq, size_t index) {
|
||||
if (index == 0) {
|
||||
return;
|
||||
} else {
|
||||
size_t parent = (index - 1) / 2;
|
||||
if (pq->compar(pq->q[parent], pq->q[index]) > 0) {
|
||||
swap(pq, parent, index);
|
||||
bubble_up(pq, parent);
|
||||
size_t parent;
|
||||
while (index != 0) {
|
||||
parent = (index - 1) / 2;
|
||||
if (!pq->less(pq->q[index], pq->q[parent])) {
|
||||
return;
|
||||
}
|
||||
swap(pq, parent, index);
|
||||
index = parent;
|
||||
}
|
||||
}
|
||||
|
||||
int nghttp2_pq_push(nghttp2_pq *pq, void *item) {
|
||||
int nghttp2_pq_push(nghttp2_pq *pq, nghttp2_pq_entry *item) {
|
||||
if (pq->capacity <= pq->length) {
|
||||
void *nq;
|
||||
size_t ncapacity;
|
||||
|
||||
ncapacity = nghttp2_max(4, (pq->capacity * 2));
|
||||
|
||||
nq = nghttp2_mem_realloc(pq->mem, pq->q,
|
||||
(pq->capacity * 2) * sizeof(void *));
|
||||
ncapacity * sizeof(nghttp2_pq_entry *));
|
||||
if (nq == NULL) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
pq->capacity *= 2;
|
||||
pq->capacity = ncapacity;
|
||||
pq->q = nq;
|
||||
}
|
||||
pq->q[pq->length] = item;
|
||||
item->index = pq->length;
|
||||
++pq->length;
|
||||
bubble_up(pq, pq->length - 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *nghttp2_pq_top(nghttp2_pq *pq) {
|
||||
nghttp2_pq_entry *nghttp2_pq_top(nghttp2_pq *pq) {
|
||||
if (pq->length == 0) {
|
||||
return NULL;
|
||||
} else {
|
||||
@@ -85,32 +94,59 @@ void *nghttp2_pq_top(nghttp2_pq *pq) {
|
||||
}
|
||||
|
||||
static void bubble_down(nghttp2_pq *pq, size_t index) {
|
||||
size_t lchild = index * 2 + 1;
|
||||
size_t minindex = index;
|
||||
size_t i, j;
|
||||
for (i = 0; i < 2; ++i) {
|
||||
j = lchild + i;
|
||||
if (j >= pq->length) {
|
||||
break;
|
||||
size_t i, j, minindex;
|
||||
for (;;) {
|
||||
j = index * 2 + 1;
|
||||
minindex = index;
|
||||
for (i = 0; i < 2; ++i, ++j) {
|
||||
if (j >= pq->length) {
|
||||
break;
|
||||
}
|
||||
if (pq->less(pq->q[j], pq->q[minindex])) {
|
||||
minindex = j;
|
||||
}
|
||||
}
|
||||
if (pq->compar(pq->q[minindex], pq->q[j]) > 0) {
|
||||
minindex = j;
|
||||
if (minindex == index) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (minindex != index) {
|
||||
swap(pq, index, minindex);
|
||||
bubble_down(pq, minindex);
|
||||
index = minindex;
|
||||
}
|
||||
}
|
||||
|
||||
void nghttp2_pq_pop(nghttp2_pq *pq) {
|
||||
if (pq->length > 0) {
|
||||
pq->q[0] = pq->q[pq->length - 1];
|
||||
pq->q[0]->index = 0;
|
||||
--pq->length;
|
||||
bubble_down(pq, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void nghttp2_pq_remove(nghttp2_pq *pq, nghttp2_pq_entry *item) {
|
||||
assert(pq->q[item->index] == item);
|
||||
|
||||
if (item->index == 0) {
|
||||
nghttp2_pq_pop(pq);
|
||||
return;
|
||||
}
|
||||
|
||||
if (item->index == pq->length - 1) {
|
||||
--pq->length;
|
||||
return;
|
||||
}
|
||||
|
||||
pq->q[item->index] = pq->q[pq->length - 1];
|
||||
pq->q[item->index]->index = item->index;
|
||||
--pq->length;
|
||||
|
||||
if (pq->less(item, pq->q[item->index])) {
|
||||
bubble_down(pq, item->index);
|
||||
} else {
|
||||
bubble_up(pq, item->index);
|
||||
}
|
||||
}
|
||||
|
||||
int nghttp2_pq_empty(nghttp2_pq *pq) { return pq->length == 0; }
|
||||
|
||||
size_t nghttp2_pq_size(nghttp2_pq *pq) { return pq->length; }
|
||||
|
||||
@@ -35,9 +35,11 @@
|
||||
|
||||
/* Implementation of priority queue */
|
||||
|
||||
typedef struct { size_t index; } nghttp2_pq_entry;
|
||||
|
||||
typedef struct {
|
||||
/* The pointer to the pointer to the item stored */
|
||||
void **q;
|
||||
nghttp2_pq_entry **q;
|
||||
/* Memory allocator */
|
||||
nghttp2_mem *mem;
|
||||
/* The number of items sotred */
|
||||
@@ -45,8 +47,8 @@ typedef struct {
|
||||
/* The maximum number of items this pq can store. This is
|
||||
automatically extended when length is reached to this value. */
|
||||
size_t capacity;
|
||||
/* The compare function between items */
|
||||
nghttp2_compar compar;
|
||||
/* The less function between items */
|
||||
nghttp2_less less;
|
||||
} nghttp2_pq;
|
||||
|
||||
/*
|
||||
@@ -58,7 +60,7 @@ typedef struct {
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_compar cmp, nghttp2_mem *mem);
|
||||
int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_less less, nghttp2_mem *mem);
|
||||
|
||||
/*
|
||||
* Deallocates any resources allocated for |pq|. The stored items are
|
||||
@@ -75,13 +77,13 @@ void nghttp2_pq_free(nghttp2_pq *pq);
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
int nghttp2_pq_push(nghttp2_pq *pq, void *item);
|
||||
int nghttp2_pq_push(nghttp2_pq *pq, nghttp2_pq_entry *item);
|
||||
|
||||
/*
|
||||
* Returns item at the top of the queue |pq|. If the queue is empty,
|
||||
* this function returns NULL.
|
||||
*/
|
||||
void *nghttp2_pq_top(nghttp2_pq *pq);
|
||||
nghttp2_pq_entry *nghttp2_pq_top(nghttp2_pq *pq);
|
||||
|
||||
/*
|
||||
* Pops item at the top of the queue |pq|. The popped item is not
|
||||
@@ -99,7 +101,7 @@ int nghttp2_pq_empty(nghttp2_pq *pq);
|
||||
*/
|
||||
size_t nghttp2_pq_size(nghttp2_pq *pq);
|
||||
|
||||
typedef int (*nghttp2_pq_item_cb)(void *item, void *arg);
|
||||
typedef int (*nghttp2_pq_item_cb)(nghttp2_pq_entry *item, void *arg);
|
||||
|
||||
/*
|
||||
* Updates each item in |pq| using function |fun| and re-construct
|
||||
@@ -118,4 +120,9 @@ void nghttp2_pq_update(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg);
|
||||
*/
|
||||
int nghttp2_pq_each(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg);
|
||||
|
||||
/*
|
||||
* Removes |item| from priority queue.
|
||||
*/
|
||||
void nghttp2_pq_remove(nghttp2_pq *pq, nghttp2_pq_entry *item);
|
||||
|
||||
#endif /* NGHTTP2_PQ_H */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -30,7 +30,6 @@
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
#include "nghttp2_pq.h"
|
||||
#include "nghttp2_map.h"
|
||||
#include "nghttp2_frame.h"
|
||||
#include "nghttp2_hd.h"
|
||||
@@ -41,19 +40,25 @@
|
||||
#include "nghttp2_callbacks.h"
|
||||
#include "nghttp2_mem.h"
|
||||
|
||||
/* The global variable for tests where we want to disable strict
|
||||
preface handling. */
|
||||
/* Specify NGHTTP2_EXTERN, so that we can test using Win build dll. */
|
||||
NGHTTP2_EXTERN extern int nghttp2_enable_strict_preface;
|
||||
|
||||
/*
|
||||
* Option flags.
|
||||
*/
|
||||
typedef enum {
|
||||
NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE = 1 << 0,
|
||||
NGHTTP2_OPTMASK_RECV_CLIENT_PREFACE = 1 << 1,
|
||||
NGHTTP2_OPTMASK_NO_HTTP_MESSAGING = 1 << 2,
|
||||
NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC = 1 << 1,
|
||||
NGHTTP2_OPTMASK_NO_HTTP_MESSAGING = 1 << 2
|
||||
} nghttp2_optmask;
|
||||
|
||||
typedef enum {
|
||||
NGHTTP2_OB_POP_ITEM,
|
||||
NGHTTP2_OB_SEND_DATA,
|
||||
NGHTTP2_OB_SEND_NO_COPY
|
||||
NGHTTP2_OB_SEND_NO_COPY,
|
||||
NGHTTP2_OB_SEND_CLIENT_MAGIC
|
||||
} nghttp2_outbound_state;
|
||||
|
||||
typedef struct {
|
||||
@@ -66,10 +71,13 @@ typedef struct {
|
||||
nghttp2_session_recv(). */
|
||||
#define NGHTTP2_INBOUND_BUFFER_LENGTH 16384
|
||||
|
||||
/* The default maximum number of incoming reserved streams */
|
||||
#define NGHTTP2_MAX_INCOMING_RESERVED_STREAMS 200
|
||||
|
||||
/* Internal state when receiving incoming frame */
|
||||
typedef enum {
|
||||
/* Receiving frame header */
|
||||
NGHTTP2_IB_READ_CLIENT_PREFACE,
|
||||
NGHTTP2_IB_READ_CLIENT_MAGIC,
|
||||
NGHTTP2_IB_READ_FIRST_SETTINGS,
|
||||
NGHTTP2_IB_READ_HEAD,
|
||||
NGHTTP2_IB_READ_NBYTE,
|
||||
@@ -84,7 +92,7 @@ typedef enum {
|
||||
NGHTTP2_IB_READ_PAD_DATA,
|
||||
NGHTTP2_IB_READ_DATA,
|
||||
NGHTTP2_IB_IGN_DATA,
|
||||
NGHTTP2_IB_IGN_ALL,
|
||||
NGHTTP2_IB_IGN_ALL
|
||||
} nghttp2_inbound_state;
|
||||
|
||||
#define NGHTTP2_INBOUND_NUM_IV 7
|
||||
@@ -136,19 +144,32 @@ typedef enum {
|
||||
/* Flag means GOAWAY was sent */
|
||||
NGHTTP2_GOAWAY_SENT = 0x4,
|
||||
/* Flag means GOAWAY was received */
|
||||
NGHTTP2_GOAWAY_RECV = 0x8,
|
||||
NGHTTP2_GOAWAY_RECV = 0x8
|
||||
} nghttp2_goaway_flag;
|
||||
|
||||
/* nghttp2_inflight_settings stores the SETTINGS entries which local
|
||||
endpoint has sent to the remote endpoint, and has not received ACK
|
||||
yet. */
|
||||
struct nghttp2_inflight_settings {
|
||||
struct nghttp2_inflight_settings *next;
|
||||
nghttp2_settings_entry *iv;
|
||||
size_t niv;
|
||||
};
|
||||
|
||||
typedef struct nghttp2_inflight_settings nghttp2_inflight_settings;
|
||||
|
||||
struct nghttp2_session {
|
||||
nghttp2_map /* <nghttp2_stream*> */ streams;
|
||||
nghttp2_stream_roots roots;
|
||||
/* Queue for outbound frames other than stream-creating HEADERS and
|
||||
DATA */
|
||||
nghttp2_pq /* <nghttp2_outbound_item*> */ ob_pq;
|
||||
/* Queue for outbound stream-creating HEADERS frame */
|
||||
nghttp2_pq /* <nghttp2_outbound_item*> */ ob_ss_pq;
|
||||
/* QUeue for DATA frame */
|
||||
nghttp2_pq /* <nghttp2_outbound_item*> */ ob_da_pq;
|
||||
/* root of dependency tree*/
|
||||
nghttp2_stream root;
|
||||
/* Queue for outbound urgent frames (PING and SETTINGS) */
|
||||
nghttp2_outbound_queue ob_urgent;
|
||||
/* Queue for non-DATA frames */
|
||||
nghttp2_outbound_queue ob_reg;
|
||||
/* Queue for outbound stream-creating HEADERS (request or push
|
||||
response) frame, which are subject to
|
||||
SETTINGS_MAX_CONCURRENT_STREAMS limit. */
|
||||
nghttp2_outbound_queue ob_syn;
|
||||
nghttp2_active_outbound_item aob;
|
||||
nghttp2_inbound_frame iframe;
|
||||
nghttp2_hd_deflater hd_deflater;
|
||||
@@ -156,22 +177,8 @@ struct nghttp2_session {
|
||||
nghttp2_session_callbacks callbacks;
|
||||
/* Memory allocator */
|
||||
nghttp2_mem mem;
|
||||
/* Sequence number of outbound frame to maintain the order of
|
||||
enqueue if priority is equal. */
|
||||
int64_t next_seq;
|
||||
/* Reset count of nghttp2_outbound_item's weight. We decrements
|
||||
weight each time DATA is sent to simulate resource sharing. We
|
||||
use priority queue and larger weight has the precedence. If
|
||||
weight is reached to lowest weight, it resets to its initial
|
||||
weight. If this happens, other items which have the lower weight
|
||||
currently but same initial weight cannot send DATA until item
|
||||
having large weight is decreased. To avoid this, we use this
|
||||
cycle variable. Initally, this is set to 1. If weight gets
|
||||
lowest weight, and if item's cycle == last_cycle, we increments
|
||||
last_cycle and assigns it to item's cycle. Otherwise, just
|
||||
assign last_cycle. In priority queue comparator, we first
|
||||
compare items' cycle value. Lower cycle value has the
|
||||
precedence. */
|
||||
/* Base value when we schedule next DATA frame write. This is
|
||||
updated when one frame was written. */
|
||||
uint64_t last_cycle;
|
||||
void *user_data;
|
||||
/* Points to the latest closed stream. NULL if there is no closed
|
||||
@@ -186,18 +193,28 @@ struct nghttp2_session {
|
||||
/* Points to the oldest idle stream. NULL if there is no idle
|
||||
stream. Only used when session is initialized as erver. */
|
||||
nghttp2_stream *idle_stream_tail;
|
||||
/* In-flight SETTINGS values. NULL does not necessarily mean there
|
||||
is no in-flight SETTINGS. */
|
||||
nghttp2_settings_entry *inflight_iv;
|
||||
/* The number of entries in |inflight_iv|. -1 if there is no
|
||||
in-flight SETTINGS. */
|
||||
ssize_t inflight_niv;
|
||||
/* Queue of In-flight SETTINGS values. SETTINGS bearing ACK is not
|
||||
considered as in-flight. */
|
||||
nghttp2_inflight_settings *inflight_settings_head;
|
||||
/* The number of outgoing streams. This will be capped by
|
||||
remote_settings.max_concurrent_streams. */
|
||||
size_t num_outgoing_streams;
|
||||
/* The number of incoming streams. This will be capped by
|
||||
local_settings.max_concurrent_streams. */
|
||||
size_t num_incoming_streams;
|
||||
/* The number of incoming reserved streams. This is the number of
|
||||
streams in reserved (remote) state. RFC 7540 does not limit this
|
||||
number. nghttp2 offers
|
||||
nghttp2_option_set_max_reserved_remote_streams() to achieve this.
|
||||
If it is used, num_incoming_streams is capped by
|
||||
max_incoming_reserved_streams. Client application should
|
||||
consider to set this because without that server can send
|
||||
arbitrary number of PUSH_PROMISE, and exhaust client's memory. */
|
||||
size_t num_incoming_reserved_streams;
|
||||
/* The maximum number of incoming reserved streams (reserved
|
||||
(remote) state). RST_STREAM will be sent for the pushed stream
|
||||
which exceeds this limit. */
|
||||
size_t max_incoming_reserved_streams;
|
||||
/* The number of closed streams still kept in |streams| hash. The
|
||||
closed streams can be accessed through single linked list
|
||||
|closed_stream_head|. The current implementation only keeps
|
||||
@@ -291,14 +308,6 @@ typedef struct {
|
||||
int nghttp2_session_is_my_stream_id(nghttp2_session *session,
|
||||
int32_t stream_id);
|
||||
|
||||
/*
|
||||
* Initializes |item|. No memory allocation is done in this function.
|
||||
* Don't call nghttp2_outbound_item_free() until frame member is
|
||||
* initialized.
|
||||
*/
|
||||
void nghttp2_session_outbound_item_init(nghttp2_session *session,
|
||||
nghttp2_outbound_item *item);
|
||||
|
||||
/*
|
||||
* Adds |item| to the outbound queue in |session|. When this function
|
||||
* succeeds, it takes ownership of |item|. So caller must not free it
|
||||
@@ -445,26 +454,43 @@ int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id,
|
||||
* Deletes |stream| from memory. After this function returns, stream
|
||||
* cannot be accessed.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
void nghttp2_session_destroy_stream(nghttp2_session *session,
|
||||
nghttp2_stream *stream);
|
||||
int nghttp2_session_destroy_stream(nghttp2_session *session,
|
||||
nghttp2_stream *stream);
|
||||
|
||||
/*
|
||||
* Tries to keep incoming closed stream |stream|. Due to the
|
||||
* limitation of maximum number of streams in memory, |stream| is not
|
||||
* closed and just deleted from memory (see
|
||||
* nghttp2_session_destroy_stream).
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
void nghttp2_session_keep_closed_stream(nghttp2_session *session,
|
||||
nghttp2_stream *stream);
|
||||
int nghttp2_session_keep_closed_stream(nghttp2_session *session,
|
||||
nghttp2_stream *stream);
|
||||
|
||||
/*
|
||||
* Appends |stream| to linked list |session->idle_stream_head|. We
|
||||
* apply fixed limit for list size. To fit into that limit, one or
|
||||
* more oldest streams are removed from list as necessary.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
void nghttp2_session_keep_idle_stream(nghttp2_session *session,
|
||||
nghttp2_stream *stream);
|
||||
int nghttp2_session_keep_idle_stream(nghttp2_session *session,
|
||||
nghttp2_stream *stream);
|
||||
|
||||
/*
|
||||
* Detaches |stream| from idle streams linked list.
|
||||
@@ -478,15 +504,27 @@ void nghttp2_session_detach_idle_stream(nghttp2_session *session,
|
||||
* stream. If |offset| is nonzero, it is decreased from the maximum
|
||||
* number of allowed stream when comparing number of active and closed
|
||||
* stream and the maximum number.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
void nghttp2_session_adjust_closed_stream(nghttp2_session *session,
|
||||
ssize_t offset);
|
||||
int nghttp2_session_adjust_closed_stream(nghttp2_session *session,
|
||||
size_t offset);
|
||||
|
||||
/*
|
||||
* Deletes idle stream to ensure that number of idle streams is in
|
||||
* certain limit.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
void nghttp2_session_adjust_idle_stream(nghttp2_session *session);
|
||||
int nghttp2_session_adjust_idle_stream(nghttp2_session *session);
|
||||
|
||||
/*
|
||||
* If further receptions and transmissions over the stream |stream_id|
|
||||
@@ -708,27 +746,21 @@ int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs,
|
||||
nghttp2_stream *stream);
|
||||
|
||||
/*
|
||||
* Returns top of outbound frame queue. This function returns NULL if
|
||||
* queue is empty.
|
||||
*/
|
||||
nghttp2_outbound_item *nghttp2_session_get_ob_pq_top(nghttp2_session *session);
|
||||
|
||||
/*
|
||||
* Pops and returns next item to send. If there is no such item,
|
||||
* Pops and returns next item to send. If there is no such item,
|
||||
* returns NULL. This function takes into account max concurrent
|
||||
* streams. That means if session->ob_pq is empty but
|
||||
* session->ob_ss_pq has item and max concurrent streams is reached,
|
||||
* then this function returns NULL.
|
||||
* streams. That means if session->ob_syn has item and max concurrent
|
||||
* streams is reached, the even if other queues contain items, then
|
||||
* this function returns NULL.
|
||||
*/
|
||||
nghttp2_outbound_item *
|
||||
nghttp2_session_pop_next_ob_item(nghttp2_session *session);
|
||||
|
||||
/*
|
||||
* Returns next item to send. If there is no such item, this function
|
||||
* Returns next item to send. If there is no such item, this function
|
||||
* returns NULL. This function takes into account max concurrent
|
||||
* streams. That means if session->ob_pq is empty but
|
||||
* session->ob_ss_pq has item and max concurrent streams is reached,
|
||||
* then this function returns NULL.
|
||||
* streams. That means if session->ob_syn has item and max concurrent
|
||||
* streams is reached, the even if other queues contain items, then
|
||||
* this function returns NULL.
|
||||
*/
|
||||
nghttp2_outbound_item *
|
||||
nghttp2_session_get_next_ob_item(nghttp2_session *session);
|
||||
@@ -755,7 +787,8 @@ int nghttp2_session_update_local_settings(nghttp2_session *session,
|
||||
|
||||
/*
|
||||
* Re-prioritize |stream|. The new priority specification is
|
||||
* |pri_spec|.
|
||||
* |pri_spec|. Caller must ensure that stream->hd.stream_id !=
|
||||
* pri_spec->stream_id.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
|
||||
1041
lib/nghttp2_stream.c
1041
lib/nghttp2_stream.c
File diff suppressed because it is too large
Load Diff
@@ -35,11 +35,6 @@
|
||||
#include "nghttp2_pq.h"
|
||||
#include "nghttp2_int.h"
|
||||
|
||||
/*
|
||||
* Maximum number of streams in one dependency tree.
|
||||
*/
|
||||
#define NGHTTP2_MAX_DEP_TREE_LENGTH 100
|
||||
|
||||
/*
|
||||
* If local peer is stream initiator:
|
||||
* NGHTTP2_STREAM_OPENING : upon sending request HEADERS
|
||||
@@ -133,27 +128,18 @@ typedef enum {
|
||||
/* "http" or "https" scheme */
|
||||
NGHTTP2_HTTP_FLAG_SCHEME_HTTP = 1 << 12,
|
||||
/* set if final response is expected */
|
||||
NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE = 1 << 13,
|
||||
NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE = 1 << 13
|
||||
} nghttp2_http_flag;
|
||||
|
||||
typedef enum {
|
||||
NGHTTP2_STREAM_DPRI_NONE = 0,
|
||||
NGHTTP2_STREAM_DPRI_NO_ITEM = 0x01,
|
||||
NGHTTP2_STREAM_DPRI_TOP = 0x02,
|
||||
NGHTTP2_STREAM_DPRI_REST = 0x04
|
||||
} nghttp2_stream_dpri;
|
||||
|
||||
struct nghttp2_stream_roots;
|
||||
|
||||
typedef struct nghttp2_stream_roots nghttp2_stream_roots;
|
||||
|
||||
struct nghttp2_stream;
|
||||
|
||||
typedef struct nghttp2_stream nghttp2_stream;
|
||||
|
||||
struct nghttp2_stream {
|
||||
/* Intrusive Map */
|
||||
nghttp2_map_entry map_entry;
|
||||
/* Entry for dep_prev->obq */
|
||||
nghttp2_pq_entry pq_entry;
|
||||
/* Priority Queue storing direct descendant (nghttp2_stream). Only
|
||||
streams which itself has some data to send, or has a descendant
|
||||
which has some data to sent. */
|
||||
nghttp2_pq obq;
|
||||
/* Content-Length of request/response body. -1 if unknown. */
|
||||
int64_t content_length;
|
||||
/* Received body so far */
|
||||
@@ -167,28 +153,17 @@ struct nghttp2_stream {
|
||||
dep_prev and sib_prev are NULL. */
|
||||
nghttp2_stream *dep_prev, *dep_next;
|
||||
nghttp2_stream *sib_prev, *sib_next;
|
||||
/* pointers to track dependency tree root streams. This is
|
||||
doubly-linked list and first element is pointed by
|
||||
roots->head. */
|
||||
nghttp2_stream *root_prev, *root_next;
|
||||
/* When stream is kept after closure, it may be kept in doubly
|
||||
linked list pointed by nghttp2_session closed_stream_head.
|
||||
closed_next points to the next stream object if it is the element
|
||||
of the list. */
|
||||
nghttp2_stream *closed_prev, *closed_next;
|
||||
/* pointer to roots, which tracks dependency tree roots */
|
||||
nghttp2_stream_roots *roots;
|
||||
/* The arbitrary data provided by user for this stream. */
|
||||
void *stream_user_data;
|
||||
/* Item to send */
|
||||
nghttp2_outbound_item *item;
|
||||
/* stream ID */
|
||||
int32_t stream_id;
|
||||
/* categorized priority of this stream. Only stream bearing
|
||||
NGHTTP2_STREAM_DPRI_TOP can send item. */
|
||||
nghttp2_stream_dpri dpri;
|
||||
/* the number of streams in subtree */
|
||||
size_t num_substreams;
|
||||
/* Current remote window size. This value is computed against the
|
||||
current initial window size of remote endpoint. */
|
||||
int32_t remote_window_size;
|
||||
@@ -209,14 +184,8 @@ struct nghttp2_stream {
|
||||
int32_t local_window_size;
|
||||
/* weight of this stream */
|
||||
int32_t weight;
|
||||
/* effective weight of this stream in belonging dependency tree */
|
||||
int32_t effective_weight;
|
||||
/* sum of weight (not effective_weight) of direct descendants */
|
||||
/* sum of weight of direct descendants */
|
||||
int32_t sum_dep_weight;
|
||||
/* sum of weight of direct descendants which have at least one
|
||||
descendant with dpri == NGHTTP2_STREAM_DPRI_TOP. We use this
|
||||
value to calculate effective weight. */
|
||||
int32_t sum_norest_weight;
|
||||
nghttp2_stream_state state;
|
||||
/* status code from remote server */
|
||||
int16_t status_code;
|
||||
@@ -226,14 +195,24 @@ struct nghttp2_stream {
|
||||
uint8_t flags;
|
||||
/* Bitwise OR of zero or more nghttp2_shut_flag values */
|
||||
uint8_t shut_flags;
|
||||
/* Nonzero if this stream has been queued to stream pointed by
|
||||
dep_prev. We maintain the invariant that if a stream is queued,
|
||||
then its ancestors, except for root, are also queued. This
|
||||
invariant may break in fatal error condition. */
|
||||
uint8_t queued;
|
||||
/* Base last_cycle for direct descendent streams. */
|
||||
uint64_t descendant_last_cycle;
|
||||
/* Next scheduled time to sent item */
|
||||
uint64_t cycle;
|
||||
/* Last written length of frame payload */
|
||||
size_t last_writelen;
|
||||
};
|
||||
|
||||
void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
|
||||
uint8_t flags, nghttp2_stream_state initial_state,
|
||||
int32_t weight, nghttp2_stream_roots *roots,
|
||||
int32_t remote_initial_window_size,
|
||||
int32_t weight, int32_t remote_initial_window_size,
|
||||
int32_t local_initial_window_size,
|
||||
void *stream_user_data);
|
||||
void *stream_user_data, nghttp2_mem *mem);
|
||||
|
||||
void nghttp2_stream_free(nghttp2_stream *stream);
|
||||
|
||||
@@ -256,8 +235,7 @@ void nghttp2_stream_shutdown(nghttp2_stream *stream, nghttp2_shut_flag flag);
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
int nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags,
|
||||
nghttp2_session *session);
|
||||
int nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags);
|
||||
|
||||
/*
|
||||
* Put back deferred data in this stream to active state. The |flags|
|
||||
@@ -266,9 +244,14 @@ int nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags,
|
||||
* NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL and given masks are
|
||||
* cleared if they are set. So even if this function is called, if
|
||||
* one of flag is still set, data does not become active.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
int nghttp2_stream_resume_deferred_item(nghttp2_stream *stream, uint8_t flags,
|
||||
nghttp2_session *session);
|
||||
int nghttp2_stream_resume_deferred_item(nghttp2_stream *stream, uint8_t flags);
|
||||
|
||||
/*
|
||||
* Returns nonzero if item is deferred by whatever reason.
|
||||
@@ -312,61 +295,47 @@ int nghttp2_stream_update_local_initial_window_size(
|
||||
void nghttp2_stream_promise_fulfilled(nghttp2_stream *stream);
|
||||
|
||||
/*
|
||||
* Returns the stream positioned in root of the dependency tree the
|
||||
* |stream| belongs to.
|
||||
* Returns nonzero if |target| is an ancestor of |stream|.
|
||||
*/
|
||||
nghttp2_stream *nghttp2_stream_get_dep_root(nghttp2_stream *stream);
|
||||
|
||||
/*
|
||||
* Returns nonzero if |target| is found in subtree of |stream|.
|
||||
*/
|
||||
int nghttp2_stream_dep_subtree_find(nghttp2_stream *stream,
|
||||
nghttp2_stream *target);
|
||||
int nghttp2_stream_dep_find_ancestor(nghttp2_stream *stream,
|
||||
nghttp2_stream *target);
|
||||
|
||||
/*
|
||||
* Computes distributed weight of a stream of the |weight| under the
|
||||
* |stream| if |stream| is removed from a dependency tree. The result
|
||||
* is computed using stream->weight rather than
|
||||
* stream->effective_weight.
|
||||
* |stream| if |stream| is removed from a dependency tree.
|
||||
*/
|
||||
int32_t nghttp2_stream_dep_distributed_weight(nghttp2_stream *stream,
|
||||
int32_t weight);
|
||||
|
||||
/*
|
||||
* Computes effective weight of a stream of the |weight| under the
|
||||
* |stream|. The result is computed using stream->effective_weight
|
||||
* rather than stream->weight. This function is used to determine
|
||||
* weight in dependency tree.
|
||||
*/
|
||||
int32_t nghttp2_stream_dep_distributed_effective_weight(nghttp2_stream *stream,
|
||||
int32_t weight);
|
||||
|
||||
/*
|
||||
* Makes the |stream| depend on the |dep_stream|. This dependency is
|
||||
* exclusive. All existing direct descendants of |dep_stream| become
|
||||
* the descendants of the |stream|. This function assumes
|
||||
* |stream->data| is NULL and no dpri members are changed in this
|
||||
* dependency tree.
|
||||
* |stream->item| is NULL.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
void nghttp2_stream_dep_insert(nghttp2_stream *dep_stream,
|
||||
nghttp2_stream *stream);
|
||||
int nghttp2_stream_dep_insert(nghttp2_stream *dep_stream,
|
||||
nghttp2_stream *stream);
|
||||
|
||||
/*
|
||||
* Makes the |stream| depend on the |dep_stream|. This dependency is
|
||||
* not exclusive. This function assumes |stream->data| is NULL and no
|
||||
* dpri members are changed in this dependency tree.
|
||||
* not exclusive. This function assumes |stream->item| is NULL.
|
||||
*/
|
||||
void nghttp2_stream_dep_add(nghttp2_stream *dep_stream, nghttp2_stream *stream);
|
||||
|
||||
/*
|
||||
* Removes the |stream| from the current dependency tree. This
|
||||
* function assumes |stream->data| is NULL.
|
||||
* function assumes |stream->item| is NULL.
|
||||
*/
|
||||
void nghttp2_stream_dep_remove(nghttp2_stream *stream);
|
||||
int nghttp2_stream_dep_remove(nghttp2_stream *stream);
|
||||
|
||||
/*
|
||||
* Attaches |item| to |stream|. Updates dpri members in this
|
||||
* dependency tree.
|
||||
* Attaches |item| to |stream|.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
@@ -375,13 +344,11 @@ void nghttp2_stream_dep_remove(nghttp2_stream *stream);
|
||||
* Out of memory
|
||||
*/
|
||||
int nghttp2_stream_attach_item(nghttp2_stream *stream,
|
||||
nghttp2_outbound_item *item,
|
||||
nghttp2_session *session);
|
||||
nghttp2_outbound_item *item);
|
||||
|
||||
/*
|
||||
* Detaches |stream->item|. Updates dpri members in this dependency
|
||||
* tree. This function does not free |stream->item|. The caller must
|
||||
* free it.
|
||||
* Detaches |stream->item|. This function does not free
|
||||
* |stream->item|. The caller must free it.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
@@ -389,12 +356,11 @@ int nghttp2_stream_attach_item(nghttp2_stream *stream,
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
int nghttp2_stream_detach_item(nghttp2_stream *stream,
|
||||
nghttp2_session *session);
|
||||
int nghttp2_stream_detach_item(nghttp2_stream *stream);
|
||||
|
||||
/*
|
||||
* Makes the |stream| depend on the |dep_stream|. This dependency is
|
||||
* exclusive. Updates dpri members in this dependency tree.
|
||||
* exclusive.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
@@ -403,12 +369,11 @@ int nghttp2_stream_detach_item(nghttp2_stream *stream,
|
||||
* Out of memory
|
||||
*/
|
||||
int nghttp2_stream_dep_insert_subtree(nghttp2_stream *dep_stream,
|
||||
nghttp2_stream *stream,
|
||||
nghttp2_session *session);
|
||||
nghttp2_stream *stream);
|
||||
|
||||
/*
|
||||
* Makes the |stream| depend on the |dep_stream|. This dependency is
|
||||
* not exclusive. Updates dpri members in this dependency tree.
|
||||
* not exclusive.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
@@ -417,13 +382,11 @@ int nghttp2_stream_dep_insert_subtree(nghttp2_stream *dep_stream,
|
||||
* Out of memory
|
||||
*/
|
||||
int nghttp2_stream_dep_add_subtree(nghttp2_stream *dep_stream,
|
||||
nghttp2_stream *stream,
|
||||
nghttp2_session *session);
|
||||
nghttp2_stream *stream);
|
||||
|
||||
/*
|
||||
* Removes subtree whose root stream is |stream|. Removing subtree
|
||||
* does not change dpri values. The effective_weight of streams in
|
||||
* removed subtree is not updated.
|
||||
* Removes subtree whose root stream is |stream|. The
|
||||
* effective_weight of streams in removed subtree is not updated.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
@@ -433,54 +396,21 @@ int nghttp2_stream_dep_add_subtree(nghttp2_stream *dep_stream,
|
||||
*/
|
||||
void nghttp2_stream_dep_remove_subtree(nghttp2_stream *stream);
|
||||
|
||||
/*
|
||||
* Makes the |stream| as root. Updates dpri members in this
|
||||
* dependency tree.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
int nghttp2_stream_dep_make_root(nghttp2_stream *stream,
|
||||
nghttp2_session *session);
|
||||
|
||||
/*
|
||||
* Makes the |stream| as root and all existing root streams become
|
||||
* direct children of |stream|.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
int
|
||||
nghttp2_stream_dep_all_your_stream_are_belong_to_us(nghttp2_stream *stream,
|
||||
nghttp2_session *session);
|
||||
|
||||
/*
|
||||
* Returns nonzero if |stream| is in any dependency tree.
|
||||
*/
|
||||
int nghttp2_stream_in_dep_tree(nghttp2_stream *stream);
|
||||
|
||||
struct nghttp2_stream_roots {
|
||||
nghttp2_stream *head;
|
||||
/*
|
||||
* Schedules transmission of |stream|'s item, assuming stream->item is
|
||||
* attached, and stream->last_writelen was updated.
|
||||
*/
|
||||
void nghttp2_stream_reschedule(nghttp2_stream *stream);
|
||||
|
||||
int32_t num_streams;
|
||||
};
|
||||
|
||||
void nghttp2_stream_roots_init(nghttp2_stream_roots *roots);
|
||||
|
||||
void nghttp2_stream_roots_free(nghttp2_stream_roots *roots);
|
||||
|
||||
void nghttp2_stream_roots_add(nghttp2_stream_roots *roots,
|
||||
nghttp2_stream *stream);
|
||||
|
||||
void nghttp2_stream_roots_remove(nghttp2_stream_roots *roots,
|
||||
nghttp2_stream *stream);
|
||||
|
||||
void nghttp2_stream_roots_remove_all(nghttp2_stream_roots *roots);
|
||||
/*
|
||||
* Returns a stream which has highest priority.
|
||||
*/
|
||||
nghttp2_outbound_item *
|
||||
nghttp2_stream_next_outbound_item(nghttp2_stream *stream);
|
||||
|
||||
#endif /* NGHTTP2_STREAM */
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user