mirror of
https://github.com/nghttp2/nghttp2.git
synced 2025-12-07 10:38:53 +08:00
Compare commits
473 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
08ec5b3fc0 | ||
|
|
8c491d5917 | ||
|
|
4219fe7822 | ||
|
|
d4eb2b2c75 | ||
|
|
8ea26fddfd | ||
|
|
98add63cdf | ||
|
|
7b90404072 | ||
|
|
de0543f684 | ||
|
|
46e3be7b5b | ||
|
|
7c675b1505 | ||
|
|
d287ea986f | ||
|
|
399328cb49 | ||
|
|
d46e50b112 | ||
|
|
0f87cedc2d | ||
|
|
d34095cf49 | ||
|
|
6039bacb1b | ||
|
|
4877f72a75 | ||
|
|
274b3a2296 | ||
|
|
93013f4205 | ||
|
|
5789987ca3 | ||
|
|
a0524ef05d | ||
|
|
0e3ae63965 | ||
|
|
3e14261ebf | ||
|
|
446de923f3 | ||
|
|
c5860fc6f4 | ||
|
|
6b714030dd | ||
|
|
8483225839 | ||
|
|
585af93828 | ||
|
|
41e266181e | ||
|
|
6b7e6166f9 | ||
|
|
2a4f347dbc | ||
|
|
bff5a28d12 | ||
|
|
5c31c130bd | ||
|
|
b9d6fff962 | ||
|
|
9ffbc45ba6 | ||
|
|
961f41d7ce | ||
|
|
928d3e5f3f | ||
|
|
42eeebc7f6 | ||
|
|
991baf9e69 | ||
|
|
6ad63a06b0 | ||
|
|
60c2fe5a2e | ||
|
|
41b5077330 | ||
|
|
d10dc4bb9b | ||
|
|
c4b487be05 | ||
|
|
c976a0fcd1 | ||
|
|
5b5034f19c | ||
|
|
dd9e829ee1 | ||
|
|
80a7523b49 | ||
|
|
4c55a2340b | ||
|
|
2f2a535113 | ||
|
|
505a300d93 | ||
|
|
4236fa603f | ||
|
|
196d7da53f | ||
|
|
6824319fe6 | ||
|
|
66d00954a5 | ||
|
|
76eb3193ab | ||
|
|
66f5438dc9 | ||
|
|
45164b6761 | ||
|
|
ceefddd332 | ||
|
|
a043230371 | ||
|
|
c2f49fa478 | ||
|
|
fe17a20f84 | ||
|
|
773b108eeb | ||
|
|
36a9cbf41f | ||
|
|
f8182333b4 | ||
|
|
c3265de625 | ||
|
|
b24bd3d8cb | ||
|
|
fa21b95274 | ||
|
|
5dccc88a7c | ||
|
|
2cadd38b6b | ||
|
|
d7cfe464a2 | ||
|
|
018e9eaf6d | ||
|
|
94ca9705ef | ||
|
|
d6f810d91a | ||
|
|
d8cf29c202 | ||
|
|
bf46539d10 | ||
|
|
2ed47cdd19 | ||
|
|
a3f79232c6 | ||
|
|
421bf85808 | ||
|
|
05a847b6b8 | ||
|
|
1e3afe0646 | ||
|
|
38788d707b | ||
|
|
44c0d32a1b | ||
|
|
36aae8c310 | ||
|
|
34ac90443f | ||
|
|
992c14e354 | ||
|
|
529fc937dc | ||
|
|
42c174e803 | ||
|
|
05b8d8c5b7 | ||
|
|
a1c937a007 | ||
|
|
8baec366f0 | ||
|
|
c64bb62ffe | ||
|
|
6020b727d8 | ||
|
|
7f04968950 | ||
|
|
ca1f43dfca | ||
|
|
9efb62f40b | ||
|
|
f1c7d3edfd | ||
|
|
7ff38eeb2e | ||
|
|
e4ce595ebb | ||
|
|
8accf3898a | ||
|
|
690a1622aa | ||
|
|
aaef798566 | ||
|
|
0753fcd6e5 | ||
|
|
a14029744c | ||
|
|
7e46d0b142 | ||
|
|
726e6c087d | ||
|
|
ff0eaf8399 | ||
|
|
7fb7575f78 | ||
|
|
44642af227 | ||
|
|
e7d052100c | ||
|
|
fc2170e488 | ||
|
|
0cda2282dd | ||
|
|
566baab307 | ||
|
|
6753b6d61e | ||
|
|
35817876fe | ||
|
|
7d753d779e | ||
|
|
f6f908a541 | ||
|
|
b0c1986a46 | ||
|
|
9671eaa850 | ||
|
|
062b42918c | ||
|
|
2fa28e790d | ||
|
|
76eab3faa0 | ||
|
|
70ea774f23 | ||
|
|
e15d302985 | ||
|
|
b2196f215a | ||
|
|
8afd75ca47 | ||
|
|
0676535caf | ||
|
|
dd741a58ae | ||
|
|
26304546c4 | ||
|
|
838fb33892 | ||
|
|
7f802b623d | ||
|
|
c2426bc732 | ||
|
|
08d0cdfd31 | ||
|
|
1fd44b1567 | ||
|
|
a2a9f15307 | ||
|
|
291c27c940 | ||
|
|
f86c11f07d | ||
|
|
dbd0f032ce | ||
|
|
6a2e6b744f | ||
|
|
4de8db523f | ||
|
|
cf5ac0f363 | ||
|
|
185ebd7b79 | ||
|
|
38153e0f6e | ||
|
|
f36f3ae1fa | ||
|
|
94d6320376 | ||
|
|
f3a0ab6552 | ||
|
|
568b744374 | ||
|
|
00e687506e | ||
|
|
1c2c5bdd05 | ||
|
|
30acb41561 | ||
|
|
3715a89655 | ||
|
|
ef090d425e | ||
|
|
e1bb06d8ab | ||
|
|
d1793e3b5a | ||
|
|
1bdf664f4d | ||
|
|
05b8901d69 | ||
|
|
1c0d617742 | ||
|
|
b161dfe573 | ||
|
|
c6d019da5f | ||
|
|
b773d63b92 | ||
|
|
66b6787fbe | ||
|
|
f2a498e3c4 | ||
|
|
1a2bccd71c | ||
|
|
8417275368 | ||
|
|
1646352f3c | ||
|
|
c98cf045d6 | ||
|
|
933b9636e5 | ||
|
|
d10bce3dc8 | ||
|
|
814c7e68e0 | ||
|
|
c5c58ccd78 | ||
|
|
1468bcd7b4 | ||
|
|
8b533e19bb | ||
|
|
a55a080b9c | ||
|
|
df32a534fc | ||
|
|
e583a25a8b | ||
|
|
4430b06c71 | ||
|
|
6051ff63e0 | ||
|
|
da2376effd | ||
|
|
0c4ae3dea5 | ||
|
|
e457c9a414 | ||
|
|
997f9233bc | ||
|
|
49781da8f0 | ||
|
|
7f9de007d0 | ||
|
|
c506386997 | ||
|
|
3144f7de72 | ||
|
|
c84a190ac7 | ||
|
|
a26a597453 | ||
|
|
20a689ef44 | ||
|
|
62928ddbcd | ||
|
|
04ef26473a | ||
|
|
7897f5b94b | ||
|
|
9302e3edf4 | ||
|
|
ab93a700ce | ||
|
|
2fc1dd77d2 | ||
|
|
e45c523dc7 | ||
|
|
b3846d6c27 | ||
|
|
dec115cff3 | ||
|
|
fe84ec5e8b | ||
|
|
b39aa43537 | ||
|
|
2216fd2bc1 | ||
|
|
83d9ee1fd1 | ||
|
|
4424cf5b65 | ||
|
|
00a83a44b4 | ||
|
|
83952ef0af | ||
|
|
3dbd2d31bd | ||
|
|
f755ea3894 | ||
|
|
50c4aa061f | ||
|
|
6657966334 | ||
|
|
512aa8942a | ||
|
|
b371331297 | ||
|
|
489b4f307c | ||
|
|
b157d4ebb2 | ||
|
|
9845729709 | ||
|
|
dbfc02cba0 | ||
|
|
d1a1e882bf | ||
|
|
799778af69 | ||
|
|
d45f5a51e4 | ||
|
|
e479abc4ab | ||
|
|
37706c2930 | ||
|
|
bb856fe4c5 | ||
|
|
f4c0a243e7 | ||
|
|
ba9ea1831c | ||
|
|
c7126663df | ||
|
|
aa1339d9c0 | ||
|
|
f87aaf1fbf | ||
|
|
685dabc2d6 | ||
|
|
4cafdb7670 | ||
|
|
a8889971db | ||
|
|
1fbaae837c | ||
|
|
e8c294b701 | ||
|
|
9c30211da9 | ||
|
|
58d3b5b4a0 | ||
|
|
442572c1f4 | ||
|
|
011e3b325d | ||
|
|
a473641e3f | ||
|
|
6f4f252907 | ||
|
|
0d2bbead9d | ||
|
|
16475f6613 | ||
|
|
0bdacd3e77 | ||
|
|
57a50f981b | ||
|
|
115d7133a0 | ||
|
|
eb94603c51 | ||
|
|
7d4a6aa179 | ||
|
|
354de30874 | ||
|
|
c462093555 | ||
|
|
364772f508 | ||
|
|
ae0100a9ab | ||
|
|
756e2b3e9b | ||
|
|
57d02f5c57 | ||
|
|
73b999a662 | ||
|
|
4401f697e5 | ||
|
|
f360b5c1e3 | ||
|
|
07fdaaba45 | ||
|
|
7fa62c9904 | ||
|
|
3e2714810a | ||
|
|
68866f53b0 | ||
|
|
ad8e9a4741 | ||
|
|
3c5d8f446b | ||
|
|
2e425e3cb6 | ||
|
|
1b00bc1929 | ||
|
|
6b28e033de | ||
|
|
eec8870ac1 | ||
|
|
d151759f8a | ||
|
|
807d39abe3 | ||
|
|
7b81136bb3 | ||
|
|
75d7e5abe0 | ||
|
|
4d47c31ebe | ||
|
|
9e723b6b1d | ||
|
|
7aff00496a | ||
|
|
0efdeab1db | ||
|
|
b01d0c88fe | ||
|
|
502b552b68 | ||
|
|
8c90e5314d | ||
|
|
af960f1982 | ||
|
|
8b4291edcb | ||
|
|
45a47936e0 | ||
|
|
7c09d5eb8d | ||
|
|
88f0bc70c4 | ||
|
|
b14cfaf308 | ||
|
|
8dd8d68b83 | ||
|
|
c55d7343ca | ||
|
|
4dea318b5b | ||
|
|
795a22a320 | ||
|
|
b4b2ddad3b | ||
|
|
6ff67ae869 | ||
|
|
33879219ff | ||
|
|
4956bdc4da | ||
|
|
b165775811 | ||
|
|
90746cdd0e | ||
|
|
f93a2b71a1 | ||
|
|
2dd6353e24 | ||
|
|
208abd8cc5 | ||
|
|
4cda09beff | ||
|
|
9a2d36fc6c | ||
|
|
ab6663c785 | ||
|
|
54851ef7a6 | ||
|
|
f8f9b36acd | ||
|
|
6774fa6e07 | ||
|
|
7baf6f781e | ||
|
|
2349a03882 | ||
|
|
3904550d5d | ||
|
|
be3ee91e90 | ||
|
|
d4f87ce29f | ||
|
|
1216d7d912 | ||
|
|
f3b247e4c8 | ||
|
|
7c75d9db98 | ||
|
|
b2fb888363 | ||
|
|
a4d729d36b | ||
|
|
83200f3080 | ||
|
|
a14c614c10 | ||
|
|
a68c4c1e3c | ||
|
|
82f90f9030 | ||
|
|
b707cfe986 | ||
|
|
d37fc8f3a6 | ||
|
|
9f5f724147 | ||
|
|
e2bbc94616 | ||
|
|
928b49a916 | ||
|
|
56c2fd6c5b | ||
|
|
267f877255 | ||
|
|
36e216d24a | ||
|
|
73d231b1bb | ||
|
|
cabb7c73cd | ||
|
|
3a37ed97f4 | ||
|
|
0f14c93fa4 | ||
|
|
f321ee5a61 | ||
|
|
e9eae3fb61 | ||
|
|
17de036d85 | ||
|
|
a91e0de06c | ||
|
|
bbc34904c1 | ||
|
|
f1049a66e2 | ||
|
|
5a497b9f30 | ||
|
|
b4ad0a30af | ||
|
|
1816738b3c | ||
|
|
4b0b036d3b | ||
|
|
0a0618baac | ||
|
|
e03f36eeeb | ||
|
|
6b1ef95d3f | ||
|
|
147bc45658 | ||
|
|
00555dc7bb | ||
|
|
d1a4002b22 | ||
|
|
8ddad1a53d | ||
|
|
96e66b1a81 | ||
|
|
19429abd07 | ||
|
|
243a8135a6 | ||
|
|
3167aa4081 | ||
|
|
10a4926648 | ||
|
|
67f50770f5 | ||
|
|
93daa98608 | ||
|
|
63675f0a47 | ||
|
|
f8765be817 | ||
|
|
f0c7839f25 | ||
|
|
6a39de0ae5 | ||
|
|
402ebb277f | ||
|
|
4e68ca8233 | ||
|
|
95b3e2f140 | ||
|
|
f412ae442b | ||
|
|
01313c1241 | ||
|
|
99afea05b9 | ||
|
|
b99b83812b | ||
|
|
0b48448270 | ||
|
|
2a56a3d9ea | ||
|
|
1883bdaf1d | ||
|
|
82cd23e40b | ||
|
|
d56e167c54 | ||
|
|
3e5765831f | ||
|
|
b9c6162cd5 | ||
|
|
68510f1282 | ||
|
|
4b58b25c19 | ||
|
|
516a2f0efc | ||
|
|
fbd9bcb00e | ||
|
|
682db00ba9 | ||
|
|
434e80dc7b | ||
|
|
5c93a113f3 | ||
|
|
6b4b7bef23 | ||
|
|
8d5c893929 | ||
|
|
b943fbb002 | ||
|
|
9a89db575a | ||
|
|
0fcfe16dc5 | ||
|
|
990f9ed4de | ||
|
|
0f4b0966ef | ||
|
|
ecfd593076 | ||
|
|
3c6b75fb2b | ||
|
|
d1f06b09cd | ||
|
|
3a94ad709c | ||
|
|
7ea8037ee1 | ||
|
|
064bfcc9d2 | ||
|
|
a0028ea8ef | ||
|
|
d174ffeb00 | ||
|
|
5a93bedf72 | ||
|
|
4a0dba08b9 | ||
|
|
b685747643 | ||
|
|
76a97b9718 | ||
|
|
ac1cc56fbb | ||
|
|
f604cbae70 | ||
|
|
91ae5291cc | ||
|
|
5770c6bd04 | ||
|
|
7492f628aa | ||
|
|
2f788aa214 | ||
|
|
cee22df073 | ||
|
|
e219d6a94f | ||
|
|
41e72064e0 | ||
|
|
f302f1a830 | ||
|
|
c0fc726955 | ||
|
|
041d9863c2 | ||
|
|
3c5ca63b6f | ||
|
|
53a41c953d | ||
|
|
c39f2b2c91 | ||
|
|
603b0ae501 | ||
|
|
9938a4e952 | ||
|
|
8997e4369d | ||
|
|
5a6d6ccbd4 | ||
|
|
426fbda799 | ||
|
|
16e91746d9 | ||
|
|
b9a9a23b1e | ||
|
|
8059380fb0 | ||
|
|
039d9db5a3 | ||
|
|
b9f41e34ef | ||
|
|
ca7288ae38 | ||
|
|
a789008f17 | ||
|
|
53142222fe | ||
|
|
f1bec6f05c | ||
|
|
506177e1bd | ||
|
|
91151f1f56 | ||
|
|
daec7c16d3 | ||
|
|
6e446934d4 | ||
|
|
9ab71305d1 | ||
|
|
ae1aac26a7 | ||
|
|
8eb2160890 | ||
|
|
a440bdf15e | ||
|
|
8004ea9726 | ||
|
|
5436c95788 | ||
|
|
467565589c | ||
|
|
8f45bf7b9e | ||
|
|
09939cf6bc | ||
|
|
e8053ac931 | ||
|
|
fff785178d | ||
|
|
af24f8394e | ||
|
|
62b9e4bb56 | ||
|
|
441f1cc282 | ||
|
|
f92110c54c | ||
|
|
1cb6d5cb6d | ||
|
|
3817798905 | ||
|
|
13a14ecda8 | ||
|
|
2b458666ba | ||
|
|
06a8d684a6 | ||
|
|
18d42b411b | ||
|
|
cbd878bbd5 | ||
|
|
49eeed8420 | ||
|
|
5f36d91afd | ||
|
|
a08ce38bcf | ||
|
|
5d204fc3aa | ||
|
|
84ead30e58 | ||
|
|
b11e1afc91 | ||
|
|
c3aa02f003 | ||
|
|
d142830109 | ||
|
|
22e41bab3f | ||
|
|
b0078a2379 | ||
|
|
50109bb307 | ||
|
|
aa1c8d1fa4 | ||
|
|
1de20c1232 | ||
|
|
8fe093de1d | ||
|
|
f004361ef2 | ||
|
|
d6db38a318 | ||
|
|
c88a5291b7 | ||
|
|
0d614cf103 | ||
|
|
29d6cfae80 | ||
|
|
c48a6e73e8 | ||
|
|
956c11388c | ||
|
|
5e8eb926f2 | ||
|
|
1e4f8f27fd | ||
|
|
0ea041e8cb | ||
|
|
e048deb64c | ||
|
|
8ece08e1a3 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -42,6 +42,8 @@ doc/tutorial-hpack.rst
|
||||
doc/python-apiref.rst
|
||||
doc/building-android-binary.rst
|
||||
doc/asio_http2.h.rst
|
||||
doc/asio_http2_server.h.rst
|
||||
doc/asio_http2_client.h.rst
|
||||
doc/libnghttp2_asio.rst
|
||||
doc/contribute.rst
|
||||
python/setup.py
|
||||
|
||||
2
COPYING
2
COPYING
@@ -1,6 +1,6 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2012, 2014 Tatsuhiro Tsujikawa
|
||||
Copyright (c) 2012, 2014, 2015 Tatsuhiro Tsujikawa
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
||||
@@ -20,7 +20,8 @@
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
SUBDIRS = lib third-party src examples python tests doc contrib
|
||||
SUBDIRS = lib third-party src examples python tests integration-tests \
|
||||
doc contrib
|
||||
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
|
||||
187
README.rst
187
README.rst
@@ -42,9 +42,9 @@ implementation.
|
||||
|
||||
* https://nghttp2.org/ (TLS + ALPN/NPN)
|
||||
|
||||
NPN offer ``h2-14``, ``spdy/3.1`` and ``http/1.1``.
|
||||
|
||||
This endpoint requires TLSv1.2 for HTTP/2 connection.
|
||||
This endpoint supports ``h2``, ``h2-16``, ``h2-14``, ``spdy/3.1``
|
||||
and ``http/1.1`` via ALPN/NPN and requires TLSv1.2 for HTTP/2
|
||||
connection.
|
||||
|
||||
* http://nghttp2.org/ (Upgrade / Direct)
|
||||
|
||||
@@ -110,25 +110,9 @@ The Python bindings require the following packages:
|
||||
* python >= 2.7
|
||||
|
||||
If you are using Ubuntu 14.04 LTS, you need the following packages
|
||||
installed:
|
||||
installed::
|
||||
|
||||
* 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
|
||||
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
|
||||
|
||||
spdylay is not packaged in Ubuntu, so you need to build it yourself:
|
||||
http://tatsuhiro-t.github.io/spdylay/
|
||||
@@ -172,6 +156,34 @@ The generated documents will not be installed with ``make install``.
|
||||
The online documentation is available at
|
||||
https://nghttp2.org/documentation/
|
||||
|
||||
Unit tests
|
||||
----------
|
||||
|
||||
Unit tests are done by simply running `make check`.
|
||||
|
||||
Integration tests
|
||||
-----------------
|
||||
|
||||
We have the integration tests for nghttpx proxy server. The tests are
|
||||
written in `Go programming language <http://golang.org/>`_ and uses
|
||||
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
|
||||
|
||||
To download the above packages, after settings ``GOPATH``, run the
|
||||
following command under ``integration-tests`` directory::
|
||||
|
||||
$ make itprep
|
||||
|
||||
To run the tests, run the following command under
|
||||
``integration-tests`` directory::
|
||||
|
||||
$ make it
|
||||
|
||||
Inside the tests, we use port 3009 to run test subject server.
|
||||
|
||||
Client, Server and Proxy programs
|
||||
---------------------------------
|
||||
|
||||
@@ -186,7 +198,7 @@ with prior knowledge, HTTP Upgrade and NPN/ALPN TLS extension.
|
||||
It has verbose output mode for framing information. Here is sample
|
||||
output from ``nghttp`` client::
|
||||
|
||||
$ src/nghttp -nv https://nghttp2.org
|
||||
$ nghttp -nv https://nghttp2.org
|
||||
[ 0.033][NPN] server offers:
|
||||
* h2-14
|
||||
* spdy/3.1
|
||||
@@ -255,7 +267,7 @@ output from ``nghttp`` client::
|
||||
|
||||
The HTTP Upgrade is performed like this::
|
||||
|
||||
$ src/nghttp -nvu http://nghttp2.org
|
||||
$ nghttp -nvu http://nghttp2.org
|
||||
[ 0.013] HTTP Upgrade request
|
||||
GET / HTTP/1.1
|
||||
Host: nghttp2.org
|
||||
@@ -322,6 +334,35 @@ The HTTP Upgrade is performed like this::
|
||||
[ 0.038] send GOAWAY frame <length=8, flags=0x00, stream_id=0>
|
||||
(last_stream_id=0, error_code=NO_ERROR(0), opaque_data(0)=[])
|
||||
|
||||
With ``-s`` option, ``nghttp`` prints out some timing information for
|
||||
requests, sorted by completion time::
|
||||
|
||||
$ nghttp -nas https://nghttp2.org/
|
||||
***** 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
|
||||
|
||||
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
|
||||
|
||||
With ``-r`` option, ``nghttp`` writes more detailed timing data to
|
||||
given file in HAR format.
|
||||
|
||||
nghttpd - server
|
||||
++++++++++++++++
|
||||
|
||||
@@ -338,7 +379,7 @@ HTTP/2 connection. No HTTP Upgrade is supported.
|
||||
Just like ``nghttp``, it has verbose output mode for framing
|
||||
information. Here is sample output from ``nghttpd`` server::
|
||||
|
||||
$ src/nghttpd --no-tls -v 8080
|
||||
$ 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>
|
||||
@@ -390,7 +431,8 @@ nghttpx - proxy
|
||||
+++++++++++++++
|
||||
|
||||
``nghttpx`` is a multi-threaded reverse proxy for ``h2-14``, SPDY and
|
||||
HTTP/1.1 and powers nghttp2.org site. It has several operation modes:
|
||||
HTTP/1.1 and powers nghttp2.org site and supports HTTP/2 server push.
|
||||
It has several operation modes:
|
||||
|
||||
================== ============================ ============== =============
|
||||
Mode option Frontend Backend Note
|
||||
@@ -419,8 +461,6 @@ SSL/TLS in the backend connection by deafult. To disable SSL/TLS, use
|
||||
``nghttpx`` supports configuration file. See ``--conf`` option and
|
||||
sample configuration file ``nghttpx.conf.sample``.
|
||||
|
||||
``nghttpx`` does not support server push.
|
||||
|
||||
In the default mode, (without any of ``--http2-proxy``,
|
||||
``--http2-bridge``, ``--client-proxy`` and ``--client`` options),
|
||||
``nghttpx`` works as reverse proxy to the backend server::
|
||||
@@ -434,7 +474,7 @@ SPDY proxy)::
|
||||
Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/1.1) --> Proxy
|
||||
[secure proxy] (e.g., Squid, ATS)
|
||||
|
||||
The ``Client`` in the above is needs to be configured to use
|
||||
The ``Client`` in the above needs to be configured to use
|
||||
``nghttpx`` as secure proxy.
|
||||
|
||||
At the time of this writing, Chrome is the only browser which supports
|
||||
@@ -502,8 +542,11 @@ library. The UI of ``h2load`` is heavily inspired by ``weighttp``
|
||||
(https://github.com/lighttpd/weighttp). The typical usage is as
|
||||
follows::
|
||||
|
||||
$ src/h2load -n1000 -c10 -m10 https://127.0.0.1:8443/
|
||||
$ h2load -n100000 -c100 -m100 https://localhost:8443/
|
||||
starting benchmark...
|
||||
spawning thread #0: 100 concurrent clients, 100000 total requests
|
||||
Protocol: TLSv1.2
|
||||
Cipher: ECDHE-RSA-AES128-GCM-SHA256
|
||||
progress: 10% done
|
||||
progress: 20% done
|
||||
progress: 30% done
|
||||
@@ -515,15 +558,17 @@ follows::
|
||||
progress: 90% done
|
||||
progress: 100% done
|
||||
|
||||
finished in 0 sec, 152 millisec and 152 microsec, 6572 req/s, 749 kbytes/s
|
||||
requests: 1000 total, 1000 started, 1000 done, 0 succeeded, 1000 failed, 0 errored
|
||||
status codes: 0 2xx, 0 3xx, 1000 4xx, 0 5xx
|
||||
traffic: 141100 bytes total, 840 bytes headers, 116000 bytes data
|
||||
finished in 7.10s, 14092 req/s, 55.67MB/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
|
||||
min max mean sd +/- sd
|
||||
time for request: 283.86ms 1.46s 659.70ms 150.87ms 84.68%
|
||||
|
||||
The above example issued total 1000 requests, using 10 concurrent
|
||||
clients (thus 10 HTTP/2 sessions), and maximum 10 streams per client.
|
||||
With ``-t`` option, ``h2load`` will use multiple native threads to
|
||||
avoid saturating single core on client side.
|
||||
The above example issued total 100000 requests, using 100 concurrent
|
||||
clients (in other words, 100 HTTP/2 sessions), and maximum 100 streams
|
||||
per client. With ``-t`` option, ``h2load`` will use multiple native
|
||||
threads to avoid saturating single core on client side.
|
||||
|
||||
.. warning::
|
||||
|
||||
@@ -972,7 +1017,7 @@ libnghttp2_asio: High level HTTP/2 C++ library
|
||||
libnghttp2_asio is C++ library built on top of libnghttp2 and provides
|
||||
high level abstraction API to build HTTP/2 applications. It depends
|
||||
on Boost::ASIO library and OpenSSL. Currently libnghttp2_asio
|
||||
provides server side API.
|
||||
provides client and server API.
|
||||
|
||||
libnghttp2_asio is not built by default. Use ``--enable-asio-lib``
|
||||
configure flag to build libnghttp2_asio. The required Boost libraries
|
||||
@@ -988,19 +1033,75 @@ HTTP/2 server looks like this:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#include <nghttp2/asio_http2.h>
|
||||
#include <nghttp2/asio_http2_server.h>
|
||||
|
||||
using namespace nghttp2::asio_http2;
|
||||
using namespace nghttp2::asio_http2::server;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
boost::system::error_code ec;
|
||||
http2 server;
|
||||
|
||||
server.listen("*", 3000, [](const std::shared_ptr<request> &req,
|
||||
const std::shared_ptr<response> &res) {
|
||||
res->write_head(200);
|
||||
res->end("hello, world");
|
||||
server.handle("/", [](const request &req, const response &res) {
|
||||
res.write_head(200);
|
||||
res.end("hello, world\n");
|
||||
});
|
||||
|
||||
if (server.listen_and_serve(ec, "localhost", "3000")) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
Here is the sample code for client API use:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <nghttp2/asio_http2_client.h>
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
using namespace nghttp2::asio_http2;
|
||||
using namespace nghttp2::asio_http2::client;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
boost::system::error_code ec;
|
||||
boost::asio::io_service io_service;
|
||||
|
||||
// connect to localhost:3000
|
||||
session sess(io_service, "localhost", "3000");
|
||||
|
||||
sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) {
|
||||
boost::system::error_code ec;
|
||||
|
||||
auto req = sess.submit(ec, "GET", "http://localhost:3000/");
|
||||
|
||||
req->on_response([](const response &res) {
|
||||
// print status code and response header fields.
|
||||
std::cerr << "HTTP/2 " << res.status_code() << std::endl;
|
||||
for (auto &kv : res.header()) {
|
||||
std::cerr << kv.first << ": " << kv.second.value << "\n";
|
||||
}
|
||||
std::cerr << std::endl;
|
||||
|
||||
res.on_data([](const uint8_t *data, std::size_t len) {
|
||||
std::cerr.write(reinterpret_cast<const char *>(data), len);
|
||||
std::cerr << std::endl;
|
||||
});
|
||||
});
|
||||
|
||||
req->on_close([&sess](uint32_t error_code) {
|
||||
// shutdown session after first request was done.
|
||||
sess.shutdown();
|
||||
});
|
||||
});
|
||||
|
||||
sess.on_error([](const boost::system::error_code &ec) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
});
|
||||
|
||||
io_service.run();
|
||||
}
|
||||
|
||||
For more details, see the documentation of libnghttp2_asio.
|
||||
|
||||
@@ -39,6 +39,7 @@ PATH=$TOOLCHAIN/bin:$PATH
|
||||
--without-libxml2 \
|
||||
--disable-python-bindings \
|
||||
--disable-examples \
|
||||
--enable-werror \
|
||||
CC=clang \
|
||||
CXX=clang++ \
|
||||
CPPFLAGS="-I$PREFIX/include" \
|
||||
|
||||
54
configure.ac
54
configure.ac
@@ -1,6 +1,6 @@
|
||||
dnl nghttp2 - HTTP/2 C Library
|
||||
|
||||
dnl Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa
|
||||
dnl Copyright (c) 2012, 2013, 2014, 2015 Tatsuhiro Tsujikawa
|
||||
|
||||
dnl Permission is hereby granted, free of charge, to any person obtaining
|
||||
dnl a copy of this software and associated documentation files (the
|
||||
@@ -25,14 +25,14 @@ 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.1], [t-tujikawa@users.sourceforge.net])
|
||||
AC_INIT([nghttp2], [0.7.6], [t-tujikawa@users.sourceforge.net])
|
||||
LT_PREREQ([2.2.6])
|
||||
LT_INIT()
|
||||
dnl See versioning rule:
|
||||
dnl http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
||||
AC_SUBST(LT_CURRENT, 8)
|
||||
AC_SUBST(LT_REVISION, 1)
|
||||
AC_SUBST(LT_AGE, 3)
|
||||
AC_SUBST(LT_CURRENT, 12)
|
||||
AC_SUBST(LT_REVISION, 0)
|
||||
AC_SUBST(LT_AGE, 7)
|
||||
|
||||
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"`
|
||||
@@ -49,6 +49,9 @@ 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])])
|
||||
|
||||
@@ -197,12 +200,28 @@ std::vector<std::future<int>> v;
|
||||
[have_std_future=no
|
||||
AC_MSG_RESULT([no])])
|
||||
|
||||
# Check that std::map::emplace is available for g++-4.7.
|
||||
AC_MSG_CHECKING([whether std::map::emplace is available])
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
|
||||
[[
|
||||
#include <map>
|
||||
]],
|
||||
[[
|
||||
std::map<int, int>().emplace(1, 2);
|
||||
]])],
|
||||
[AC_DEFINE([HAVE_STD_MAP_EMPLACE], [1],
|
||||
[Define to 1 if you have the `std::map::emplace`.])
|
||||
have_std_map_emplace=yes
|
||||
AC_MSG_RESULT([yes])],
|
||||
[have_std_map_emplace=no
|
||||
AC_MSG_RESULT([no])])
|
||||
|
||||
AC_LANG_POP()
|
||||
|
||||
# Checks for libraries.
|
||||
|
||||
# Additional libraries required for tests.
|
||||
TESTLDFLAGS=
|
||||
TESTLDADD=
|
||||
|
||||
# Additional libraries required for programs under src directory.
|
||||
APPLDFLAGS=
|
||||
@@ -211,7 +230,7 @@ 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])
|
||||
TESTLDFLAGS="$LIBS $TESTLDFLAGS"
|
||||
TESTLDADD="$LIBS $TESTLDADD"
|
||||
LIBS=$LIBS_OLD
|
||||
|
||||
LIBS_OLD=$LIBS
|
||||
@@ -354,7 +373,7 @@ fi
|
||||
# spdylay (for src/nghttpx and src/h2load)
|
||||
have_spdylay=no
|
||||
if test "x${request_spdylay}" != "xno"; then
|
||||
PKG_CHECK_MODULES([LIBSPDYLAY], [libspdylay >= 1.3.0],
|
||||
PKG_CHECK_MODULES([LIBSPDYLAY], [libspdylay >= 1.3.2],
|
||||
[have_spdylay=yes], [have_spdylay=no])
|
||||
if test "x${have_spdylay}" = "xyes"; then
|
||||
AC_DEFINE([HAVE_SPDYLAY], [1], [Define to 1 if you have `spdylay` library.])
|
||||
@@ -515,6 +534,10 @@ if test "x$have_struct_tm_tm_gmtoff" = "xyes"; then
|
||||
[Define to 1 if you have `struct tm.tm_gmtoff` member.])
|
||||
fi
|
||||
|
||||
# Check size of pointer to decide we need 8 bytes alignment
|
||||
# adjustment.
|
||||
AC_CHECK_SIZEOF([int *])
|
||||
|
||||
# Checks for library functions.
|
||||
if test "x$cross_compiling" != "xyes"; then
|
||||
AC_FUNC_MALLOC
|
||||
@@ -592,13 +615,16 @@ if test "x$debug" != "xno"; then
|
||||
AC_DEFINE([DEBUGBUILD], [1], [Define to 1 to enable debug output.])
|
||||
fi
|
||||
|
||||
enable_threads=yes
|
||||
# Some platform does not have working std::future. We disable
|
||||
# threading for those platforms to exclude std::future use.
|
||||
if test "x$threads" != "xyes" || test "x$have_std_future" != "xyes"; then
|
||||
# threading for those platforms.
|
||||
if test "x$threads" != "xyes" ||
|
||||
test "x$have_std_future" != "xyes"; then
|
||||
enable_threads=no
|
||||
AC_DEFINE([NOTHREADS], [1], [Define to 1 if you want to disable threads.])
|
||||
fi
|
||||
|
||||
AC_SUBST([TESTLDFLAGS])
|
||||
AC_SUBST([TESTLDADD])
|
||||
AC_SUBST([APPLDFLAGS])
|
||||
|
||||
AC_CONFIG_FILES([
|
||||
@@ -616,6 +642,9 @@ AC_CONFIG_FILES([
|
||||
examples/Makefile
|
||||
python/Makefile
|
||||
python/setup.py
|
||||
integration-tests/Makefile
|
||||
integration-tests/config.go
|
||||
integration-tests/setenv
|
||||
doc/Makefile
|
||||
doc/conf.py
|
||||
doc/index.rst
|
||||
@@ -631,6 +660,8 @@ AC_CONFIG_FILES([
|
||||
doc/nghttp2.h.rst
|
||||
doc/nghttp2ver.h.rst
|
||||
doc/asio_http2.h.rst
|
||||
doc/asio_http2_server.h.rst
|
||||
doc/asio_http2_client.h.rst
|
||||
doc/contribute.rst
|
||||
contrib/Makefile
|
||||
])
|
||||
@@ -682,4 +713,5 @@ AC_MSG_NOTICE([summary of build options:
|
||||
Libnghttp2_asio:${enable_asio_lib}
|
||||
Examples: ${enable_examples}
|
||||
Python bindings:${enable_python_bindings}
|
||||
Threading: ${enable_threads}
|
||||
])
|
||||
|
||||
@@ -41,24 +41,28 @@ EXTRA_DIST = \
|
||||
sources/python-apiref.rst \
|
||||
sources/building-android-binary.rst \
|
||||
sources/contribute.rst \
|
||||
_themes/sphinx_rtd_theme/footer.html \
|
||||
_themes/sphinx_rtd_theme/theme.conf \
|
||||
_themes/sphinx_rtd_theme/layout_old.html \
|
||||
_themes/sphinx_rtd_theme/__init__.py \
|
||||
_themes/sphinx_rtd_theme/layout.html \
|
||||
_themes/sphinx_rtd_theme/search.html \
|
||||
_themes/sphinx_rtd_theme/breadcrumbs.html \
|
||||
_themes/sphinx_rtd_theme/versions.html \
|
||||
_themes/sphinx_rtd_theme/footer.html \
|
||||
_themes/sphinx_rtd_theme/layout.html \
|
||||
_themes/sphinx_rtd_theme/layout_old.html \
|
||||
_themes/sphinx_rtd_theme/search.html \
|
||||
_themes/sphinx_rtd_theme/searchbox.html \
|
||||
_themes/sphinx_rtd_theme/static/fonts/FontAwesome.otf \
|
||||
_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg \
|
||||
_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.woff \
|
||||
_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.eot \
|
||||
_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.ttf \
|
||||
_themes/sphinx_rtd_theme/static/js/theme.js \
|
||||
_themes/sphinx_rtd_theme/static/css/theme.css \
|
||||
_themes/sphinx_rtd_theme/static/css/badge_only.css \
|
||||
$(man_MANS)
|
||||
_themes/sphinx_rtd_theme/static/css/theme.css \
|
||||
_themes/sphinx_rtd_theme/static/fonts/FontAwesome.otf \
|
||||
_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.eot \
|
||||
_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg \
|
||||
_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.ttf \
|
||||
_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.woff \
|
||||
_themes/sphinx_rtd_theme/static/js/theme.js \
|
||||
_themes/sphinx_rtd_theme/theme.conf \
|
||||
_themes/sphinx_rtd_theme/versions.html \
|
||||
$(man_MANS) \
|
||||
bash_completion/nghttp \
|
||||
bash_completion/nghttpd \
|
||||
bash_completion/nghttpx \
|
||||
bash_completion/h2load
|
||||
|
||||
# Makefile for Sphinx documentation
|
||||
#
|
||||
@@ -104,7 +108,7 @@ clean-local:
|
||||
-rm apiref.rst
|
||||
-rm -rf $(BUILDDIR)/*
|
||||
|
||||
html: apiref.rst
|
||||
html-local: apiref.rst
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||
|
||||
@@ -31,3 +31,70 @@ do not consume client connection preface unless
|
||||
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.
|
||||
|
||||
HTTP Messaging
|
||||
--------------
|
||||
|
||||
By default, nghttp2 library checks HTTP messaging rules described in
|
||||
`HTTP/2 specification, section 8
|
||||
<https://tools.ietf.org/html/draft-ietf-httpbis-http2-17#section-8>`_.
|
||||
Everything described in that section is not validated however. We
|
||||
briefly describe what the library does in this area. In the following
|
||||
description, without loss of generality we omit CONTINUATION frame
|
||||
since they must follow HEADERS frame and are processed atomically. In
|
||||
other words, they are just one big HEADERS frame. To disable these
|
||||
validations, use `nghttp2_option_set_no_http_messaging()`.
|
||||
|
||||
For HTTP request, including those carried by PUSH_PROMISE, HTTP
|
||||
message starts with one HEADERS frame containing request headers. It
|
||||
is followed by zero or more DATA frames containing request body, which
|
||||
is followed by zero or one HEADERS containing trailer headers. The
|
||||
request headers must include ":scheme", ":method" and ":path" pseudo
|
||||
header fields unless ":method" is not "CONNECT". ":authority" is
|
||||
optional, but nghttp2 requires either ":authority" or "Host" header
|
||||
field must be present. If ":method" is "CONNECT", the request headers
|
||||
must include ":method" and ":authority" and must omit ":scheme" and
|
||||
":path".
|
||||
|
||||
For HTTP response, HTTP message starts with zero or more HEADERS
|
||||
frames containing non-final response (status code 1xx). They are
|
||||
followed by one HEADERS frame containing final response headers
|
||||
(non-1xx). It is followed by zero or more DATA frames containing
|
||||
response body, which is followed by zero or one HEADERS containing
|
||||
trailer headers. The non-final and final response headers must
|
||||
contain ":status" pseudo header field containing 3 digits only.
|
||||
|
||||
All request and response headers must include exactly one valid value
|
||||
for each pseudo header field. Additionally nghttp2 requires all
|
||||
request headers must not include more than one "Host" header field.
|
||||
|
||||
HTTP/2 prohibits connection-specific header fields. The following
|
||||
header fields must not appear: "Connection", "Keep-Alive",
|
||||
"Proxy-Connection", "Transfer-Encoding" and "Upgrade". Additionally,
|
||||
"TE" header field must not include any value other than "trailers".
|
||||
|
||||
Each header field name and value must obey the field-name and
|
||||
field-value production rules described in `RFC 7230, section
|
||||
3.2. <https://tools.ietf.org/html/rfc7230#section-3.2>`_.
|
||||
Additionally, all field name must be lower cased. While the pseudo
|
||||
header fields must satisfy these rules, we just ignore illegal regular
|
||||
headers (this means that these header fields are not passed to
|
||||
application callback). This is because these illegal header fields
|
||||
are floating around in existing internet and resetting stream just
|
||||
because of this may break many web sites. This is especially true if
|
||||
we forward to or translate from HTTP/1 traffic.
|
||||
|
||||
With the above validations, nghttp2 library guarantees that header
|
||||
field name passed to `nghttp2_on_header_callback()` is not empty.
|
||||
Also required pseudo headers are all present and not empty.
|
||||
|
||||
nghttp2 enforces "Content-Length" validation as well. All request or
|
||||
response headers must not contain more than one "Content-Length"
|
||||
header field. If "Content-Length" header field is present, it must be
|
||||
parsed as 64 bit signed integer. The sum of data length in the
|
||||
following DATA frames must match with the number in "Content-Length"
|
||||
header field if it is present (this does not include padding bytes).
|
||||
|
||||
Any deviation results in stream error of type PROTOCOL_ERROR. If
|
||||
error is found in PUSH_PROMISE frame, stream error is raised against
|
||||
promised stream.
|
||||
|
||||
5
doc/asio_http2_client.h.rst.in
Normal file
5
doc/asio_http2_client.h.rst.in
Normal file
@@ -0,0 +1,5 @@
|
||||
asio_http2_client.h
|
||||
===================
|
||||
|
||||
.. literalinclude:: @top_srcdir@/src/includes/nghttp2/asio_http2_client.h
|
||||
:language: cpp
|
||||
5
doc/asio_http2_server.h.rst.in
Normal file
5
doc/asio_http2_server.h.rst.in
Normal file
@@ -0,0 +1,5 @@
|
||||
asio_http2_server.h
|
||||
===================
|
||||
|
||||
.. literalinclude:: @top_srcdir@/src/includes/nghttp2/asio_http2_server.h
|
||||
:language: cpp
|
||||
19
doc/bash_completion/h2load
Normal file
19
doc/bash_completion/h2load
Normal file
@@ -0,0 +1,19 @@
|
||||
_h2load()
|
||||
{
|
||||
local cur prev split=false
|
||||
COMPREPLY=()
|
||||
COMP_WORDBREAKS=${COMP_WORDBREAKS//=}
|
||||
|
||||
cmd=${COMP_WORDS[0]}
|
||||
_get_comp_words_by_ref cur prev
|
||||
case $cur in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W '--threads --connection-window-bits --input-file --help --requests --verbose --version --window-bits --clients --no-tls-proto --header --max-concurrent-streams ' -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
_filedir
|
||||
return 0
|
||||
esac
|
||||
return 0
|
||||
}
|
||||
complete -F _h2load h2load
|
||||
74
doc/bash_completion/make_bash_completion.py
Executable file
74
doc/bash_completion/make_bash_completion.py
Executable file
@@ -0,0 +1,74 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import subprocess
|
||||
from StringIO import StringIO
|
||||
import re
|
||||
import sys
|
||||
import os.path
|
||||
|
||||
class Option:
|
||||
def __init__(self, long_opt, short_opt):
|
||||
self.long_opt = long_opt
|
||||
self.short_opt = short_opt
|
||||
|
||||
def get_all_options(cmd):
|
||||
opt_pattern = re.compile(r' (?:(-.), )?(--[^\s\[=]+)(\[)?')
|
||||
proc = subprocess.Popen([cmd, "--help"], stdout=subprocess.PIPE)
|
||||
stdoutdata, stderrdata = proc.communicate()
|
||||
cur_option = None
|
||||
opts = {}
|
||||
for line in StringIO(stdoutdata):
|
||||
match = opt_pattern.match(line)
|
||||
if not match:
|
||||
continue
|
||||
long_opt = match.group(2)
|
||||
short_opt = match.group(1)
|
||||
opts[long_opt] = Option(long_opt, short_opt)
|
||||
|
||||
return opts
|
||||
|
||||
def output_case(out, name, opts):
|
||||
out.write('''\
|
||||
_{name}()
|
||||
{{
|
||||
local cur prev split=false
|
||||
COMPREPLY=()
|
||||
COMP_WORDBREAKS=${{COMP_WORDBREAKS//=}}
|
||||
|
||||
cmd=${{COMP_WORDS[0]}}
|
||||
_get_comp_words_by_ref cur prev
|
||||
'''.format(name=name))
|
||||
|
||||
# Complete option name.
|
||||
out.write('''\
|
||||
case $cur in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W '\
|
||||
''')
|
||||
for opt in opts.itervalues():
|
||||
out.write(opt.long_opt)
|
||||
out.write(' ')
|
||||
|
||||
out.write('''\
|
||||
' -- "$cur" ) )
|
||||
;;
|
||||
''')
|
||||
# If no option found for completion then complete with files.
|
||||
out.write('''\
|
||||
*)
|
||||
_filedir
|
||||
return 0
|
||||
esac
|
||||
return 0
|
||||
}}
|
||||
complete -F _{name} {name}
|
||||
'''.format(name=name))
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) < 2:
|
||||
print "Generates bash_completion using `/path/to/cmd --help'"
|
||||
print "Usage: make_bash_completion.py /path/to/cmd"
|
||||
exit(1)
|
||||
name = os.path.basename(sys.argv[1])
|
||||
opts = get_all_options(sys.argv[1])
|
||||
output_case(sys.stdout, name, opts)
|
||||
19
doc/bash_completion/nghttp
Normal file
19
doc/bash_completion/nghttp
Normal file
@@ -0,0 +1,19 @@
|
||||
_nghttp()
|
||||
{
|
||||
local cur prev split=false
|
||||
COMPREPLY=()
|
||||
COMP_WORDBREAKS=${COMP_WORDBREAKS//=}
|
||||
|
||||
cmd=${COMP_WORDS[0]}
|
||||
_get_comp_words_by_ref cur prev
|
||||
case $cur in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W '--verbose --no-dep --get-assets --har --header-table-size --multiply --padding --dep-idle --continuation --connection-window-bits --peer-max-concurrent-streams --timeout --data --no-content-length --version --color --cert --upgrade --remote-name --weight --help --key --null-out --window-bits --stat --header ' -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
_filedir
|
||||
return 0
|
||||
esac
|
||||
return 0
|
||||
}
|
||||
complete -F _nghttp nghttp
|
||||
19
doc/bash_completion/nghttpd
Normal file
19
doc/bash_completion/nghttpd
Normal file
@@ -0,0 +1,19 @@
|
||||
_nghttpd()
|
||||
{
|
||||
local cur prev split=false
|
||||
COMPREPLY=()
|
||||
COMP_WORDBREAKS=${COMP_WORDBREAKS//=}
|
||||
|
||||
cmd=${COMP_WORDS[0]}
|
||||
_get_comp_words_by_ref cur prev
|
||||
case $cur in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W '--error-gzip --push --header-table-size --htdocs --padding --verbose --version --help --daemon --verify-client --workers --no-tls --color --early-response --dh-param-file ' -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
_filedir
|
||||
return 0
|
||||
esac
|
||||
return 0
|
||||
}
|
||||
complete -F _nghttpd nghttpd
|
||||
19
doc/bash_completion/nghttpx
Normal file
19
doc/bash_completion/nghttpx
Normal file
@@ -0,0 +1,19 @@
|
||||
_nghttpx()
|
||||
{
|
||||
local cur prev split=false
|
||||
COMPREPLY=()
|
||||
COMP_WORDBREAKS=${COMP_WORDBREAKS//=}
|
||||
|
||||
cmd=${COMP_WORDS[0]}
|
||||
_get_comp_words_by_ref cur prev
|
||||
case $cur in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W '--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 --accesslog-syslog --conf --http2-max-concurrent-streams --client-proxy --worker-frontend-connections --cacert --frontend-read-timeout --worker-write-burst --npn-list --syslog-facility --backend-http1-connections-per-host --no-server-push --client --http2-bridge --no-via --user --stream-write-timeout --backend-response-buffer --http2-no-cookie-crumbling --backend-read-timeout --stream-read-timeout --workers --worker-read-burst --tls-ctx-per-worker --dh-param-file --errorlog-syslog --frontend --accesslog-file --http2-proxy --read-burst --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 --frontend-http2-read-timeout --backend-ipv4 --listener-disable-timeout --backend-ipv6 --backend ' -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
_filedir
|
||||
return 0
|
||||
esac
|
||||
return 0
|
||||
}
|
||||
complete -F _nghttpx nghttpx
|
||||
129
doc/h2load.1
129
doc/h2load.1
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "H2LOAD" "1" "January 11, 2015" "0.7.1" "nghttp2"
|
||||
.TH "H2LOAD" "1" "February 27, 2015" "0.7.5" "nghttp2"
|
||||
.SH NAME
|
||||
h2load \- HTTP/2 benchmarking tool
|
||||
.
|
||||
@@ -39,15 +39,14 @@ benchmarking tool for HTTP/2 and SPDY server
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B <URI>
|
||||
Specify URI to access. Multiple URIs can be
|
||||
specified. URIs are used in this order for each
|
||||
client. All URIs are used, then first URI is
|
||||
used and then 2nd URI, and so on. The scheme,
|
||||
host and port in the subsequent URIs, if present,
|
||||
are ignored. Those in the first URI are used
|
||||
solely.
|
||||
Specify URI to access. Multiple URIs can be specified.
|
||||
URIs are used in this order for each client. All URIs
|
||||
are used, then first URI is used and then 2nd URI, and
|
||||
so on. The scheme, host and port in the subsequent
|
||||
URIs, if present, are ignored. Those in the first URI
|
||||
are used solely.
|
||||
.UNINDENT
|
||||
.SH OPTIONS:
|
||||
.SH OPTIONS
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-n, \-\-requests=<N>
|
||||
@@ -72,38 +71,36 @@ Default: \fB1\fP
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-i, \-\-input\-file=<FILE>
|
||||
Path of a file with multiple URIs are seperated
|
||||
by EOLs. This option will disable URIs getting
|
||||
from command\-line. If \(aq\-\(aq is given as <FILE>,
|
||||
URIs will be read from stdin. URIs are used in
|
||||
this order for each client. All URIs are used,
|
||||
then first URI is used and then 2nd URI, and so
|
||||
on. The scheme, host and port in the subsequent
|
||||
URIs, if present, are ignored. Those in the
|
||||
first URI are used solely.
|
||||
Path of a file with multiple URIs are seperated by EOLs.
|
||||
This option will disable URIs getting from command\-line.
|
||||
If \(aq\-\(aq is given as <FILE>, URIs will be read from stdin.
|
||||
URIs are used in this order for each client. All URIs
|
||||
are used, then first URI is used and then 2nd URI, and
|
||||
so on. The scheme, host and port in the subsequent
|
||||
URIs, if present, are ignored. Those in the first URI
|
||||
are used solely.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-m, \-\-max\-concurrent\-streams=(auto|<N>)
|
||||
Max concurrent streams to issue per session. If
|
||||
"auto" is given, the number of given URIs is
|
||||
used.
|
||||
Max concurrent streams to issue per session. If "auto"
|
||||
is given, the number of given URIs is used.
|
||||
.sp
|
||||
Default: \fBauto\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-w, \-\-window\-bits=<N>
|
||||
Sets the stream level initial window size to
|
||||
(2**<N>)\-1. For SPDY, 2**<N> is used instead.
|
||||
Sets the stream level initial window size to (2**<N>)\-1.
|
||||
For SPDY, 2**<N> is used instead.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-W, \-\-connection\-window\-bits=<N>
|
||||
Sets the connection level initial window size to
|
||||
(2**<N>)\-1. For SPDY, if <N> is strictly less
|
||||
than 16, this option is ignored. Otherwise
|
||||
2**<N> is used for SPDY.
|
||||
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.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -113,10 +110,9 @@ Add/Override a header to the requests.
|
||||
.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
|
||||
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
|
||||
.UNINDENT
|
||||
@@ -135,6 +131,77 @@ Display version information and exit.
|
||||
.B \-h, \-\-help
|
||||
Display this help and exit.
|
||||
.UNINDENT
|
||||
.SH OUTPUT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B requests
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B total
|
||||
The number of requests h2load was instructed to make.
|
||||
.TP
|
||||
.B started
|
||||
The number of requests h2load has started.
|
||||
.TP
|
||||
.B done
|
||||
The number of requests completed.
|
||||
.TP
|
||||
.B succeeded
|
||||
The number of requests completed successfully. Only HTTP status
|
||||
code 2xx or3xx are considered as success.
|
||||
.TP
|
||||
.B failed
|
||||
The number of requests failed, including HTTP level failures
|
||||
(non\-successful HTTP status code).
|
||||
.TP
|
||||
.B errored
|
||||
The number of requests failed, except for HTTP level failures.
|
||||
status code. This is the subset of the number reported in
|
||||
\fBfailed\fP and most likely the network level failures or stream
|
||||
was reset by RST_STREAM.
|
||||
.UNINDENT
|
||||
.TP
|
||||
.B status codes
|
||||
The number of status code h2load received.
|
||||
.TP
|
||||
.B traffic
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B total
|
||||
The number of bytes received from the server "on the wire". If
|
||||
requests were made via TLS, this value is the number of decrpyted
|
||||
bytes.
|
||||
.TP
|
||||
.B headers
|
||||
The number of response header bytes from the server without
|
||||
decompression. For HTTP/2, this is the sum of the payload of
|
||||
HEADERS frame. For SPDY, this is the sum of the payload of
|
||||
SYN_REPLY frame.
|
||||
.TP
|
||||
.B data
|
||||
The number of response body bytes received from the server.
|
||||
.UNINDENT
|
||||
.TP
|
||||
.B time for request
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B min
|
||||
The minimum time taken for request and response.
|
||||
.TP
|
||||
.B max
|
||||
The maximum time taken for request and response.
|
||||
.TP
|
||||
.B mean
|
||||
The mean time taken for request and response.
|
||||
.TP
|
||||
.B sd
|
||||
The standard deviation of the time 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
|
||||
.UNINDENT
|
||||
.SH SEE ALSO
|
||||
.sp
|
||||
\fInghttp(1)\fP, \fInghttpd(1)\fP, \fInghttpx(1)\fP
|
||||
|
||||
109
doc/h2load.1.rst
109
doc/h2load.1.rst
@@ -14,16 +14,15 @@ benchmarking tool for HTTP/2 and SPDY server
|
||||
|
||||
.. describe:: <URI>
|
||||
|
||||
Specify URI to access. Multiple URIs can be
|
||||
specified. URIs are used in this order for each
|
||||
client. All URIs are used, then first URI is
|
||||
used and then 2nd URI, and so on. The scheme,
|
||||
host and port in the subsequent URIs, if present,
|
||||
are ignored. Those in the first URI are used
|
||||
solely.
|
||||
Specify URI to access. Multiple URIs can be specified.
|
||||
URIs are used in this order for each client. All URIs
|
||||
are used, then first URI is used and then 2nd URI, and
|
||||
so on. The scheme, host and port in the subsequent
|
||||
URIs, if present, are ignored. Those in the first URI
|
||||
are used solely.
|
||||
|
||||
OPTIONS:
|
||||
--------
|
||||
OPTIONS
|
||||
-------
|
||||
|
||||
.. option:: -n, --requests=<N>
|
||||
|
||||
@@ -45,35 +44,33 @@ OPTIONS:
|
||||
|
||||
.. option:: -i, --input-file=<FILE>
|
||||
|
||||
Path of a file with multiple URIs are seperated
|
||||
by EOLs. This option will disable URIs getting
|
||||
from command-line. If '-' is given as <FILE>,
|
||||
URIs will be read from stdin. URIs are used in
|
||||
this order for each client. All URIs are used,
|
||||
then first URI is used and then 2nd URI, and so
|
||||
on. The scheme, host and port in the subsequent
|
||||
URIs, if present, are ignored. Those in the
|
||||
first URI are used solely.
|
||||
Path of a file with multiple URIs are seperated by EOLs.
|
||||
This option will disable URIs getting from command-line.
|
||||
If '-' is given as <FILE>, URIs will be read from stdin.
|
||||
URIs are used in this order for each client. All URIs
|
||||
are used, then first URI is used and then 2nd URI, and
|
||||
so on. The scheme, host and port in the subsequent
|
||||
URIs, if present, are ignored. Those in the first URI
|
||||
are used solely.
|
||||
|
||||
.. option:: -m, --max-concurrent-streams=(auto|<N>)
|
||||
|
||||
Max concurrent streams to issue per session. If
|
||||
"auto" is given, the number of given URIs is
|
||||
used.
|
||||
Max concurrent streams to issue per session. If "auto"
|
||||
is given, the number of given URIs is used.
|
||||
|
||||
Default: ``auto``
|
||||
|
||||
.. option:: -w, --window-bits=<N>
|
||||
|
||||
Sets the stream level initial window size to
|
||||
(2**<N>)-1. For SPDY, 2\*\*<N> is used instead.
|
||||
Sets the stream level initial window size to (2\*\*<N>)-1.
|
||||
For SPDY, 2**<N> is used instead.
|
||||
|
||||
.. option:: -W, --connection-window-bits=<N>
|
||||
|
||||
Sets the connection level initial window size to
|
||||
(2**<N>)-1. For SPDY, if <N> is strictly less
|
||||
than 16, this option is ignored. Otherwise
|
||||
2**<N> is used for SPDY.
|
||||
Sets the connection level initial window size to
|
||||
(2**<N>)-1. For SPDY, if <N> is strictly less than 16,
|
||||
this option is ignored. Otherwise 2\*\*<N> is used for
|
||||
SPDY.
|
||||
|
||||
.. option:: -H, --header=<HEADER>
|
||||
|
||||
@@ -81,10 +78,9 @@ OPTIONS:
|
||||
|
||||
.. 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
|
||||
Specify ALPN identifier of the protocol to be used when
|
||||
accessing http URI without SSL/TLS.
|
||||
Available protocols: spdy/2, spdy/3, spdy/3.1 and h2c-14
|
||||
|
||||
Default: ``h2c-14``
|
||||
|
||||
@@ -100,6 +96,57 @@ OPTIONS:
|
||||
|
||||
Display this help and exit.
|
||||
|
||||
OUTPUT
|
||||
------
|
||||
|
||||
requests
|
||||
total
|
||||
The number of requests h2load was instructed to make.
|
||||
started
|
||||
The number of requests h2load has started.
|
||||
done
|
||||
The number of requests completed.
|
||||
succeeded
|
||||
The number of requests completed successfully. Only HTTP status
|
||||
code 2xx or3xx are considered as success.
|
||||
failed
|
||||
The number of requests failed, including HTTP level failures
|
||||
(non-successful HTTP status code).
|
||||
errored
|
||||
The number of requests failed, except for HTTP level failures.
|
||||
status code. This is the subset of the number reported in
|
||||
``failed`` and most likely the network level failures or stream
|
||||
was reset by RST_STREAM.
|
||||
|
||||
status codes
|
||||
The number of status code h2load received.
|
||||
|
||||
traffic
|
||||
total
|
||||
The number of bytes received from the server "on the wire". If
|
||||
requests were made via TLS, this value is the number of decrpyted
|
||||
bytes.
|
||||
headers
|
||||
The number of response header bytes from the server without
|
||||
decompression. For HTTP/2, this is the sum of the payload of
|
||||
HEADERS frame. For SPDY, this is the sum of the payload of
|
||||
SYN_REPLY frame.
|
||||
data
|
||||
The number of response body bytes received from the server.
|
||||
|
||||
time for request
|
||||
min
|
||||
The minimum time taken for request and response.
|
||||
max
|
||||
The maximum time taken for request and response.
|
||||
mean
|
||||
The mean time taken for request and response.
|
||||
sd
|
||||
The standard deviation of the time for request and response.
|
||||
+/- sd
|
||||
The fraction of the number of requests within standard deviation
|
||||
range (mean +/- sd) against total number of successful requests.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
|
||||
|
||||
@@ -1,3 +1,54 @@
|
||||
OUTPUT
|
||||
------
|
||||
|
||||
requests
|
||||
total
|
||||
The number of requests h2load was instructed to make.
|
||||
started
|
||||
The number of requests h2load has started.
|
||||
done
|
||||
The number of requests completed.
|
||||
succeeded
|
||||
The number of requests completed successfully. Only HTTP status
|
||||
code 2xx or3xx are considered as success.
|
||||
failed
|
||||
The number of requests failed, including HTTP level failures
|
||||
(non-successful HTTP status code).
|
||||
errored
|
||||
The number of requests failed, except for HTTP level failures.
|
||||
status code. This is the subset of the number reported in
|
||||
``failed`` and most likely the network level failures or stream
|
||||
was reset by RST_STREAM.
|
||||
|
||||
status codes
|
||||
The number of status code h2load received.
|
||||
|
||||
traffic
|
||||
total
|
||||
The number of bytes received from the server "on the wire". If
|
||||
requests were made via TLS, this value is the number of decrpyted
|
||||
bytes.
|
||||
headers
|
||||
The number of response header bytes from the server without
|
||||
decompression. For HTTP/2, this is the sum of the payload of
|
||||
HEADERS frame. For SPDY, this is the sum of the payload of
|
||||
SYN_REPLY frame.
|
||||
data
|
||||
The number of response body bytes received from the server.
|
||||
|
||||
time for request
|
||||
min
|
||||
The minimum time taken for request and response.
|
||||
max
|
||||
The maximum time taken for request and response.
|
||||
mean
|
||||
The mean time taken for request and response.
|
||||
sd
|
||||
The standard deviation of the time for request and response.
|
||||
+/- sd
|
||||
The fraction of the number of requests within standard deviation
|
||||
range (mean +/- sd) against total number of successful requests.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
|
||||
|
||||
100
doc/nghttp.1
100
doc/nghttp.1
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTP" "1" "January 11, 2015" "0.7.1" "nghttp2"
|
||||
.TH "NGHTTP" "1" "February 27, 2015" "0.7.5" "nghttp2"
|
||||
.SH NAME
|
||||
nghttp \- HTTP/2 experimental client
|
||||
.
|
||||
@@ -41,14 +41,13 @@ HTTP/2 experimental client
|
||||
.B <URI>
|
||||
Specify URI to access.
|
||||
.UNINDENT
|
||||
.SH OPTIONS:
|
||||
.SH OPTIONS
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-v, \-\-verbose
|
||||
Print debug information such as reception and
|
||||
transmission of frames and name/value pairs.
|
||||
Specifying this option multiple times increases
|
||||
verbosity.
|
||||
Print debug information such as reception and
|
||||
transmission of frames and name/value pairs. Specifying
|
||||
this option multiple times increases verbosity.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -58,39 +57,37 @@ Discard downloaded data.
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-O, \-\-remote\-name
|
||||
Save download data in the current directory. The
|
||||
filename is dereived from URI. If URI ends with
|
||||
\(aq\fI/\fP\(aq, \(aqindex.html\(aq is used as a filename. Not
|
||||
implemented yet.
|
||||
Save download data in the current directory. The
|
||||
filename is dereived from URI. If URI ends with \(aq\fI/\fP\(aq,
|
||||
\(aqindex.html\(aq is used as a filename. Not implemented
|
||||
yet.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-t, \-\-timeout=<N>
|
||||
Timeout each request after <N> seconds.
|
||||
.B \-t, \-\-timeout=<SEC>
|
||||
Timeout each request after <SEC> seconds.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-w, \-\-window\-bits=<N>
|
||||
Sets the stream level initial window size to
|
||||
2**<N>\-1.
|
||||
Sets the stream level initial window size to 2**<N>\-1.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-W, \-\-connection\-window\-bits=<N>
|
||||
Sets the connection level initial window size to
|
||||
Sets the connection level initial window size to
|
||||
2**<N>\-1.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-a, \-\-get\-assets
|
||||
Download assets such as stylesheets, images and
|
||||
script files linked from the downloaded resource.
|
||||
Only links whose origins are the same with the
|
||||
linking resource will be downloaded. nghttp
|
||||
prioritizes resources using HTTP/2 dependency
|
||||
based priority. The priority order, from highest
|
||||
to lowest, is html itself, css, javascript and
|
||||
images.
|
||||
Download assets such as stylesheets, images and script
|
||||
files linked from the downloaded resource. Only links
|
||||
whose origins are the same with the linking resource
|
||||
will be downloaded. nghttp prioritizes resources using
|
||||
HTTP/2 dependency based priority. The priority order,
|
||||
from highest to lowest, is html itself, css, javascript
|
||||
and images.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -100,74 +97,70 @@ Print statistics.
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-H, \-\-header=<HEADER>
|
||||
Add a header to the requests. Example:
|
||||
\fI\%\-H\fP\(aq:method: PUT\(aq
|
||||
Add a header to the requests. Example: \fI\%\-H\fP\(aq:method: PUT\(aq
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-cert=<CERT>
|
||||
Use the specified client certificate file. The
|
||||
file must be in PEM format.
|
||||
Use the specified client certificate file. The file
|
||||
must be in PEM format.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-key=<KEY>
|
||||
Use the client private key file. The file must
|
||||
be in PEM format.
|
||||
Use the client private key file. The file must be in
|
||||
PEM format.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-d, \-\-data=<FILE>
|
||||
Post FILE to server. If \(aq\-\(aq is given, data will
|
||||
be read from stdin.
|
||||
Post FILE to server. If \(aq\-\(aq is given, data will be read
|
||||
from stdin.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-m, \-\-multiply=<N>
|
||||
Request each URI <N> times. By default, same URI
|
||||
is not requested twice. This option disables it
|
||||
too.
|
||||
Request each URI <N> times. By default, same URI is not
|
||||
requested twice. This option disables it too.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-u, \-\-upgrade
|
||||
Perform HTTP Upgrade for HTTP/2. This option is
|
||||
ignored if the request URI has https scheme. If
|
||||
\fI\-d\fP is used, the HTTP upgrade request is performed
|
||||
with OPTIONS method.
|
||||
Perform HTTP Upgrade for HTTP/2. This option is ignored
|
||||
if the request URI has https scheme. If \fI\-d\fP is used, the
|
||||
HTTP upgrade request is performed with OPTIONS method.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-p, \-\-weight=<WEIGHT>
|
||||
Sets priority group weight. The valid value
|
||||
range is [1, 256], inclusive.
|
||||
Sets priority group weight. The valid value range is
|
||||
[1, 256], inclusive.
|
||||
.sp
|
||||
Default: \fB16\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-M, \-\-peer\-max\-concurrent\-streams=<N>
|
||||
Use <N> as SETTINGS_MAX_CONCURRENT_STREAMS value
|
||||
of remote endpoint as if it is received in
|
||||
SETTINGS frame. The default is large enough as
|
||||
it is seen as unlimited.
|
||||
Use <N> as SETTINGS_MAX_CONCURRENT_STREAMS value of
|
||||
remote endpoint as if it is received in SETTINGS frame.
|
||||
The default is large enough as it is seen as unlimited.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-c, \-\-header\-table\-size=<N>
|
||||
.B \-c, \-\-header\-table\-size=<SIZE>
|
||||
Specify decoder header table size.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-b, \-\-padding=<N>
|
||||
Add at most <N> bytes to a frame payload as
|
||||
padding. Specify 0 to disable padding.
|
||||
Add at most <N> bytes to a frame payload as padding.
|
||||
Specify 0 to disable padding.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-r, \-\-har=<FILE>
|
||||
Output HTTP transactions <FILE> in HAR format.
|
||||
If \(aq\-\(aq is given, data is written to stdout.
|
||||
Output HTTP transactions <FILE> in HAR format. If \(aq\-\(aq
|
||||
is given, data is written to stdout.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -187,14 +180,12 @@ Don\(aqt send content\-length header field.
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-no\-dep
|
||||
Don\(aqt send dependency based priority hint to
|
||||
server.
|
||||
Don\(aqt send dependency based priority hint to server.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-dep\-idle
|
||||
Use idle streams as anchor nodes to express
|
||||
priority.
|
||||
Use idle streams as anchor nodes to express priority.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -206,6 +197,9 @@ Display version information and exit.
|
||||
.B \-h, \-\-help
|
||||
Display this help and exit.
|
||||
.UNINDENT
|
||||
.sp
|
||||
The <SIZE> argument is an integer and an optional unit (e.g., 10K is
|
||||
10 * 1024). Units are K, M and G (powers of 1024).
|
||||
.SH SEE ALSO
|
||||
.sp
|
||||
\fInghttpd(1)\fP, \fInghttpx(1)\fP, \fIh2load(1)\fP
|
||||
|
||||
101
doc/nghttp.1.rst
101
doc/nghttp.1.rst
@@ -16,15 +16,14 @@ HTTP/2 experimental client
|
||||
|
||||
Specify URI to access.
|
||||
|
||||
OPTIONS:
|
||||
--------
|
||||
OPTIONS
|
||||
-------
|
||||
|
||||
.. option:: -v, --verbose
|
||||
|
||||
Print debug information such as reception and
|
||||
transmission of frames and name/value pairs.
|
||||
Specifying this option multiple times increases
|
||||
verbosity.
|
||||
Print debug information such as reception and
|
||||
transmission of frames and name/value pairs. Specifying
|
||||
this option multiple times increases verbosity.
|
||||
|
||||
.. option:: -n, --null-out
|
||||
|
||||
@@ -32,35 +31,33 @@ OPTIONS:
|
||||
|
||||
.. option:: -O, --remote-name
|
||||
|
||||
Save download data in the current directory. The
|
||||
filename is dereived from URI. If URI ends with
|
||||
'*/*', 'index.html' is used as a filename. Not
|
||||
implemented yet.
|
||||
Save download data in the current directory. The
|
||||
filename is dereived from URI. If URI ends with '*/*',
|
||||
'index.html' is used as a filename. Not implemented
|
||||
yet.
|
||||
|
||||
.. option:: -t, --timeout=<N>
|
||||
.. option:: -t, --timeout=<SEC>
|
||||
|
||||
Timeout each request after <N> seconds.
|
||||
Timeout each request after <SEC> seconds.
|
||||
|
||||
.. option:: -w, --window-bits=<N>
|
||||
|
||||
Sets the stream level initial window size to
|
||||
2\*\*<N>-1.
|
||||
Sets the stream level initial window size to 2\*\*<N>-1.
|
||||
|
||||
.. option:: -W, --connection-window-bits=<N>
|
||||
|
||||
Sets the connection level initial window size to
|
||||
Sets the connection level initial window size to
|
||||
2\*\*<N>-1.
|
||||
|
||||
.. option:: -a, --get-assets
|
||||
|
||||
Download assets such as stylesheets, images and
|
||||
script files linked from the downloaded resource.
|
||||
Only links whose origins are the same with the
|
||||
linking resource will be downloaded. nghttp
|
||||
prioritizes resources using HTTP/2 dependency
|
||||
based priority. The priority order, from highest
|
||||
to lowest, is html itself, css, javascript and
|
||||
images.
|
||||
Download assets such as stylesheets, images and script
|
||||
files linked from the downloaded resource. Only links
|
||||
whose origins are the same with the linking resource
|
||||
will be downloaded. nghttp prioritizes resources using
|
||||
HTTP/2 dependency based priority. The priority order,
|
||||
from highest to lowest, is html itself, css, javascript
|
||||
and images.
|
||||
|
||||
.. option:: -s, --stat
|
||||
|
||||
@@ -68,64 +65,60 @@ OPTIONS:
|
||||
|
||||
.. option:: -H, --header=<HEADER>
|
||||
|
||||
Add a header to the requests. Example:
|
||||
:option:`-H`\':method: PUT'
|
||||
Add a header to the requests. Example: :option:`-H`\':method: PUT'
|
||||
|
||||
.. option:: --cert=<CERT>
|
||||
|
||||
Use the specified client certificate file. The
|
||||
file must be in PEM format.
|
||||
Use the specified client certificate file. The file
|
||||
must be in PEM format.
|
||||
|
||||
.. option:: --key=<KEY>
|
||||
|
||||
Use the client private key file. The file must
|
||||
be in PEM format.
|
||||
Use the client private key file. The file must be in
|
||||
PEM format.
|
||||
|
||||
.. option:: -d, --data=<FILE>
|
||||
|
||||
Post FILE to server. If '-' is given, data will
|
||||
be read from stdin.
|
||||
Post FILE to server. If '-' is given, data will be read
|
||||
from stdin.
|
||||
|
||||
.. option:: -m, --multiply=<N>
|
||||
|
||||
Request each URI <N> times. By default, same URI
|
||||
is not requested twice. This option disables it
|
||||
too.
|
||||
Request each URI <N> times. By default, same URI is not
|
||||
requested twice. This option disables it too.
|
||||
|
||||
.. option:: -u, --upgrade
|
||||
|
||||
Perform HTTP Upgrade for HTTP/2. This option is
|
||||
ignored if the request URI has https scheme. If
|
||||
:option:`-d` is used, the HTTP upgrade request is performed
|
||||
with OPTIONS method.
|
||||
Perform HTTP Upgrade for HTTP/2. This option is ignored
|
||||
if the request URI has https scheme. If :option:`-d` is used, the
|
||||
HTTP upgrade request is performed with OPTIONS method.
|
||||
|
||||
.. option:: -p, --weight=<WEIGHT>
|
||||
|
||||
Sets priority group weight. The valid value
|
||||
range is [1, 256], inclusive.
|
||||
Sets priority group weight. The valid value range is
|
||||
[1, 256], inclusive.
|
||||
|
||||
Default: ``16``
|
||||
|
||||
.. option:: -M, --peer-max-concurrent-streams=<N>
|
||||
|
||||
Use <N> as SETTINGS_MAX_CONCURRENT_STREAMS value
|
||||
of remote endpoint as if it is received in
|
||||
SETTINGS frame. The default is large enough as
|
||||
it is seen as unlimited.
|
||||
Use <N> as SETTINGS_MAX_CONCURRENT_STREAMS value of
|
||||
remote endpoint as if it is received in SETTINGS frame.
|
||||
The default is large enough as it is seen as unlimited.
|
||||
|
||||
.. option:: -c, --header-table-size=<N>
|
||||
.. option:: -c, --header-table-size=<SIZE>
|
||||
|
||||
Specify decoder header table size.
|
||||
|
||||
.. option:: -b, --padding=<N>
|
||||
|
||||
Add at most <N> bytes to a frame payload as
|
||||
padding. Specify 0 to disable padding.
|
||||
Add at most <N> bytes to a frame payload as padding.
|
||||
Specify 0 to disable padding.
|
||||
|
||||
.. option:: -r, --har=<FILE>
|
||||
|
||||
Output HTTP transactions <FILE> in HAR format.
|
||||
If '-' is given, data is written to stdout.
|
||||
Output HTTP transactions <FILE> in HAR format. If '-'
|
||||
is given, data is written to stdout.
|
||||
|
||||
.. option:: --color
|
||||
|
||||
@@ -141,13 +134,11 @@ OPTIONS:
|
||||
|
||||
.. option:: --no-dep
|
||||
|
||||
Don't send dependency based priority hint to
|
||||
server.
|
||||
Don't send dependency based priority hint to server.
|
||||
|
||||
.. option:: --dep-idle
|
||||
|
||||
Use idle streams as anchor nodes to express
|
||||
priority.
|
||||
Use idle streams as anchor nodes to express priority.
|
||||
|
||||
.. option:: --version
|
||||
|
||||
@@ -157,6 +148,10 @@ OPTIONS:
|
||||
|
||||
Display this help and exit.
|
||||
|
||||
|
||||
The <SIZE> argument is an integer and an optional unit (e.g., 10K is
|
||||
10 * 1024). Units are K, M and G (powers of 1024).
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTPD" "1" "January 11, 2015" "0.7.1" "nghttp2"
|
||||
.TH "NGHTTPD" "1" "February 27, 2015" "0.7.5" "nghttp2"
|
||||
.SH NAME
|
||||
nghttpd \- HTTP/2 experimental server
|
||||
.
|
||||
@@ -44,45 +44,48 @@ Specify listening port number.
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B <PRIVATE_KEY>
|
||||
Set path to server\(aqs private key. Required
|
||||
unless \fI\%\-\-no\-tls\fP is specified.
|
||||
Set path to server\(aqs private key. Required unless
|
||||
\fI\%\-\-no\-tls\fP is specified.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B <CERT>
|
||||
Set path to server\(aqs certificate. Required
|
||||
unless \fI\%\-\-no\-tls\fP is specified.
|
||||
Set path to server\(aqs certificate. Required unless
|
||||
\fI\%\-\-no\-tls\fP is specified.
|
||||
.UNINDENT
|
||||
.SH OPTIONS
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-a, \-\-address=<ADDR>
|
||||
The address to bind to. If not specified the default IP
|
||||
address determined by getaddrinfo is used.
|
||||
.UNINDENT
|
||||
.SH OPTIONS:
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-D, \-\-daemon
|
||||
Run in a background. If \fI\-D\fP is used, the current
|
||||
working directory is changed to \(aq\fI/\fP\(aq. Therefore
|
||||
if this option is used, \fI\%\-d\fP option must be
|
||||
specified.
|
||||
Run in a background. If \fI\-D\fP is used, the current working
|
||||
directory is changed to \(aq\fI/\fP\(aq. Therefore if this option
|
||||
is used, \fI\%\-d\fP option must be specified.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-V, \-\-verify\-client
|
||||
The server sends a client certificate request.
|
||||
If the client did not return a certificate, the
|
||||
handshake is terminated. Currently, this option
|
||||
just requests a client certificate and does not
|
||||
verify it.
|
||||
The server sends a client certificate request. If the
|
||||
client did not return a certificate, the handshake is
|
||||
terminated. Currently, this option just requests a
|
||||
client certificate and does not verify it.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-d, \-\-htdocs=<PATH>
|
||||
Specify document root. If this option is not
|
||||
specified, the document root is the current
|
||||
working directory.
|
||||
Specify document root. If this option is not specified,
|
||||
the document root is the current working directory.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-v, \-\-verbose
|
||||
Print debug information such as reception/
|
||||
transmission of frames and name/value pairs.
|
||||
Print debug information such as reception/ transmission
|
||||
of frames and name/value pairs.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -91,7 +94,7 @@ Disable SSL/TLS.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-c, \-\-header\-table\-size=<N>
|
||||
.B \-c, \-\-header\-table\-size=<SIZE>
|
||||
Specify decoder header table size.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
@@ -102,22 +105,21 @@ Force colored log output.
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-p, \-\-push=<PATH>=<PUSH_PATH,...>
|
||||
Push resources <PUSH_PATH>s when <PATH> is
|
||||
requested. This option can be used repeatedly to
|
||||
specify multiple push configurations. <PATH> and
|
||||
<PUSH_PATH>s are relative to document root. See
|
||||
\fI\%\-\-htdocs\fP option. Example: \fI\-p\fP/=/foo.png
|
||||
\fI\-p\fP/doc=/bar.css
|
||||
Push resources <PUSH_PATH>s when <PATH> is requested.
|
||||
This option can be used repeatedly to specify multiple
|
||||
push configurations. <PATH> and <PUSH_PATH>s are
|
||||
relative to document root. See \fI\%\-\-htdocs\fP option.
|
||||
Example: \fI\-p\fP/=/foo.png \fI\-p\fP/doc=/bar.css
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-b, \-\-padding=<N>
|
||||
Add at most <N> bytes to a frame payload as
|
||||
padding. Specify 0 to disable padding.
|
||||
Add at most <N> bytes to a frame payload as padding.
|
||||
Specify 0 to disable padding.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-n, \-\-workers=<CORE>
|
||||
.B \-n, \-\-workers=<N>
|
||||
Set the number of worker threads.
|
||||
.sp
|
||||
Default: \fB1\fP
|
||||
@@ -130,16 +132,15 @@ Make error response gzipped.
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-dh\-param\-file=<PATH>
|
||||
Path to file that contains DH parameters in PEM
|
||||
format. Without this option, DHE cipher suites
|
||||
are not available.
|
||||
Path to file that contains DH parameters in PEM format.
|
||||
Without this option, DHE cipher suites are not
|
||||
available.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-early\-response
|
||||
Start sending response when request HEADERS is
|
||||
received, rather than complete request is
|
||||
received.
|
||||
Start sending response when request HEADERS is received,
|
||||
rather than complete request is received.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -151,6 +152,9 @@ Display version information and exit.
|
||||
.B \-h, \-\-help
|
||||
Display this help and exit.
|
||||
.UNINDENT
|
||||
.sp
|
||||
The <SIZE> argument is an integer and an optional unit (e.g., 10K is
|
||||
10 * 1024). Units are K, M and G (powers of 1024).
|
||||
.SH SEE ALSO
|
||||
.sp
|
||||
\fInghttp(1)\fP, \fInghttpx(1)\fP, \fIh2load(1)\fP
|
||||
|
||||
@@ -18,48 +18,51 @@ HTTP/2 experimental server
|
||||
|
||||
.. describe:: <PRIVATE_KEY>
|
||||
|
||||
Set path to server's private key. Required
|
||||
unless :option:`--no-tls` is specified.
|
||||
|
||||
Set path to server's private key. Required unless
|
||||
:option:`--no-tls` is specified.
|
||||
|
||||
.. describe:: <CERT>
|
||||
|
||||
Set path to server's certificate. Required
|
||||
unless :option:`--no-tls` is specified.
|
||||
Set path to server's certificate. Required unless
|
||||
:option:`--no-tls` is specified.
|
||||
|
||||
OPTIONS:
|
||||
--------
|
||||
OPTIONS
|
||||
-------
|
||||
|
||||
.. option:: -a, --address=<ADDR>
|
||||
|
||||
The address to bind to. If not specified the default IP
|
||||
address determined by getaddrinfo is used.
|
||||
|
||||
.. option:: -D, --daemon
|
||||
|
||||
Run in a background. If :option:`-D` is used, the current
|
||||
working directory is changed to '*/*'. Therefore
|
||||
if this option is used, :option:`-d` option must be
|
||||
specified.
|
||||
Run in a background. If :option:`-D` is used, the current working
|
||||
directory is changed to '*/*'. Therefore if this option
|
||||
is used, :option:`-d` option must be specified.
|
||||
|
||||
.. option:: -V, --verify-client
|
||||
|
||||
The server sends a client certificate request.
|
||||
If the client did not return a certificate, the
|
||||
handshake is terminated. Currently, this option
|
||||
just requests a client certificate and does not
|
||||
verify it.
|
||||
The server sends a client certificate request. If the
|
||||
client did not return a certificate, the handshake is
|
||||
terminated. Currently, this option just requests a
|
||||
client certificate and does not verify it.
|
||||
|
||||
.. option:: -d, --htdocs=<PATH>
|
||||
|
||||
Specify document root. If this option is not
|
||||
specified, the document root is the current
|
||||
working directory.
|
||||
Specify document root. If this option is not specified,
|
||||
the document root is the current working directory.
|
||||
|
||||
.. option:: -v, --verbose
|
||||
|
||||
Print debug information such as reception/
|
||||
transmission of frames and name/value pairs.
|
||||
Print debug information such as reception/ transmission
|
||||
of frames and name/value pairs.
|
||||
|
||||
.. option:: --no-tls
|
||||
|
||||
Disable SSL/TLS.
|
||||
|
||||
.. option:: -c, --header-table-size=<N>
|
||||
.. option:: -c, --header-table-size=<SIZE>
|
||||
|
||||
Specify decoder header table size.
|
||||
|
||||
@@ -69,19 +72,18 @@ OPTIONS:
|
||||
|
||||
.. option:: -p, --push=<PATH>=<PUSH_PATH,...>
|
||||
|
||||
Push resources <PUSH_PATH>s when <PATH> is
|
||||
requested. This option can be used repeatedly to
|
||||
specify multiple push configurations. <PATH> and
|
||||
<PUSH_PATH>s are relative to document root. See
|
||||
:option:`--htdocs` option. Example: :option:`\-p`/=/foo.png
|
||||
:option:`-p`\/doc=/bar.css
|
||||
Push resources <PUSH_PATH>s when <PATH> is requested.
|
||||
This option can be used repeatedly to specify multiple
|
||||
push configurations. <PATH> and <PUSH_PATH>s are
|
||||
relative to document root. See :option:`--htdocs` option.
|
||||
Example: :option:`-p`\/=/foo.png :option:`-p`\/doc=/bar.css
|
||||
|
||||
.. option:: -b, --padding=<N>
|
||||
|
||||
Add at most <N> bytes to a frame payload as
|
||||
padding. Specify 0 to disable padding.
|
||||
Add at most <N> bytes to a frame payload as padding.
|
||||
Specify 0 to disable padding.
|
||||
|
||||
.. option:: -n, --workers=<CORE>
|
||||
.. option:: -n, --workers=<N>
|
||||
|
||||
Set the number of worker threads.
|
||||
|
||||
@@ -93,15 +95,14 @@ OPTIONS:
|
||||
|
||||
.. option:: --dh-param-file=<PATH>
|
||||
|
||||
Path to file that contains DH parameters in PEM
|
||||
format. Without this option, DHE cipher suites
|
||||
are not available.
|
||||
Path to file that contains DH parameters in PEM format.
|
||||
Without this option, DHE cipher suites are not
|
||||
available.
|
||||
|
||||
.. option:: --early-response
|
||||
|
||||
Start sending response when request HEADERS is
|
||||
received, rather than complete request is
|
||||
received.
|
||||
Start sending response when request HEADERS is received,
|
||||
rather than complete request is received.
|
||||
|
||||
.. option:: --version
|
||||
|
||||
@@ -111,6 +112,10 @@ OPTIONS:
|
||||
|
||||
Display this help and exit.
|
||||
|
||||
|
||||
The <SIZE> argument is an integer and an optional unit (e.g., 10K is
|
||||
10 * 1024). Units are K, M and G (powers of 1024).
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
|
||||
|
||||
644
doc/nghttpx.1
644
doc/nghttpx.1
File diff suppressed because it is too large
Load Diff
@@ -14,48 +14,50 @@ A reverse proxy for HTTP/2, HTTP/1 and SPDY.
|
||||
|
||||
.. describe:: <PRIVATE_KEY>
|
||||
|
||||
Set path to server's private key. Required
|
||||
unless :option:`-p`\, :option:`--client` or :option:`\--frontend-no-tls` are
|
||||
given.
|
||||
|
||||
Set path to server's private key. Required unless :option:`-p`\,
|
||||
:option:`--client` or :option:`\--frontend-no-tls` are given.
|
||||
|
||||
.. describe:: <CERT>
|
||||
|
||||
Set path to server's certificate. Required
|
||||
unless :option:`-p`\, :option:`--client` or :option:`\--frontend-no-tls` are
|
||||
given.
|
||||
Set path to server's certificate. Required unless :option:`-p`\,
|
||||
:option:`--client` or :option:`\--frontend-no-tls` are given.
|
||||
|
||||
OPTIONS:
|
||||
--------
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
|
||||
The options are categorized into several groups.
|
||||
|
||||
Connections:
|
||||
~~~~~~~~~~~~
|
||||
Connections
|
||||
~~~~~~~~~~~
|
||||
|
||||
.. option:: -b, --backend=<HOST,PORT>
|
||||
|
||||
Set backend host and port. For HTTP/1 backend,
|
||||
multiple backend addresses are accepted by
|
||||
repeating this option. HTTP/2 backend does not
|
||||
support multiple backend addresses and the first
|
||||
occurrence of this option is used.
|
||||
Set backend host and port. For HTTP/1 backend, multiple
|
||||
backend addresses are accepted by repeating this option.
|
||||
HTTP/2 backend does not support multiple backend
|
||||
addresses and the first occurrence of this option is
|
||||
used. UNIX domain socket can be specified by prefixing
|
||||
path name with "unix:" (e.g.,
|
||||
unix:/var/run/backend.sock)
|
||||
|
||||
Default: ``127.0.0.1,80``
|
||||
|
||||
.. option:: -f, --frontend=<HOST,PORT>
|
||||
|
||||
Set frontend host and port. If <HOST> is '\*', it
|
||||
assumes all addresses including both IPv4 and
|
||||
IPv6.
|
||||
Set frontend host and port. If <HOST> is '\*', it
|
||||
assumes all addresses including both IPv4 and IPv6.
|
||||
UNIX domain socket can be specified by prefixing path
|
||||
name with "unix:" (e.g., unix:/var/run/nghttpx.sock)
|
||||
|
||||
Default: ``*,3000``
|
||||
|
||||
.. option:: --backlog=<NUM>
|
||||
.. option:: --backlog=<N>
|
||||
|
||||
Set listen backlog size. If :option:`-1` is given,
|
||||
libevent will choose suitable value.
|
||||
Set listen backlog size.
|
||||
|
||||
Default: ``128``
|
||||
Default: ``512``
|
||||
|
||||
.. option:: --backend-ipv4
|
||||
|
||||
@@ -67,263 +69,258 @@ Connections:
|
||||
|
||||
.. option:: --backend-http-proxy-uri=<URI>
|
||||
|
||||
Specify proxy URI in the form
|
||||
http://[<USER>:<PASS>@]<PROXY>:<PORT>. If a
|
||||
proxy requires authentication, specify <USER> and
|
||||
<PASS>. Note that they must be properly
|
||||
percent-encoded. This proxy is used when the
|
||||
backend connection is HTTP/2. First, make a
|
||||
CONNECT request to the proxy and it connects to
|
||||
the backend on behalf of nghttpx. This forms
|
||||
tunnel. After that, nghttpx performs SSL/TLS
|
||||
handshake with the downstream through the tunnel.
|
||||
The timeouts when connecting and making CONNECT
|
||||
request can be specified by
|
||||
:option:`--backend-read-timeout` and
|
||||
Specify proxy URI in the form
|
||||
http://[<USER>:<PASS>@]<PROXY>:<PORT>. If a proxy
|
||||
requires authentication, specify <USER> and <PASS>.
|
||||
Note that they must be properly percent-encoded. This
|
||||
proxy is used when the backend connection is HTTP/2.
|
||||
First, make a CONNECT request to the proxy and it
|
||||
connects to the backend on behalf of nghttpx. This
|
||||
forms tunnel. After that, nghttpx performs SSL/TLS
|
||||
handshake with the downstream through the tunnel. The
|
||||
timeouts when connecting and making CONNECT request can
|
||||
be specified by :option:`--backend-read-timeout` and
|
||||
:option:`--backend-write-timeout` options.
|
||||
|
||||
|
||||
Performance:
|
||||
~~~~~~~~~~~~
|
||||
Performance
|
||||
~~~~~~~~~~~
|
||||
|
||||
.. option:: -n, --workers=<CORES>
|
||||
.. option:: -n, --workers=<N>
|
||||
|
||||
Set the number of worker threads.
|
||||
|
||||
Default: ``1``
|
||||
|
||||
.. option:: --read-rate=<RATE>
|
||||
.. option:: --read-rate=<SIZE>
|
||||
|
||||
Set maximum average read rate on frontend
|
||||
connection. Setting 0 to this option means read
|
||||
rate is unlimited.
|
||||
Set maximum average read rate on frontend connection.
|
||||
Setting 0 to this option means read rate is unlimited.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --read-burst=<SIZE>
|
||||
|
||||
Set maximum read burst size on frontend
|
||||
connection. Setting 0 to this option means read
|
||||
burst size is unlimited.
|
||||
Set maximum read burst size on frontend connection.
|
||||
Setting 0 to this option means read burst size is
|
||||
unlimited.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --write-rate=<RATE>
|
||||
.. option:: --write-rate=<SIZE>
|
||||
|
||||
Set maximum average write rate on frontend
|
||||
connection. Setting 0 to this option means write
|
||||
rate is unlimited.
|
||||
Set maximum average write rate on frontend connection.
|
||||
Setting 0 to this option means write rate is unlimited.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --write-burst=<SIZE>
|
||||
|
||||
Set maximum write burst size on frontend
|
||||
connection. Setting 0 to this option means write
|
||||
burst size is unlimited.
|
||||
Set maximum write burst size on frontend connection.
|
||||
Setting 0 to this option means write burst size is
|
||||
unlimited.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --worker-read-rate=<RATE>
|
||||
.. option:: --worker-read-rate=<SIZE>
|
||||
|
||||
Set maximum average read rate on frontend
|
||||
connection per worker. Setting 0 to this option
|
||||
means read rate is unlimited. Not implemented
|
||||
yet.
|
||||
Set maximum average read rate on frontend connection per
|
||||
worker. Setting 0 to this option means read rate is
|
||||
unlimited. Not implemented yet.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --worker-read-burst=<SIZE>
|
||||
|
||||
Set maximum read burst size on frontend
|
||||
connection per worker. Setting 0 to this option
|
||||
means read burst size is unlimited. Not
|
||||
implemented yet.
|
||||
Set maximum read burst size on frontend connection per
|
||||
worker. Setting 0 to this option means read burst size
|
||||
is unlimited. Not implemented yet.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --worker-write-rate=<RATE>
|
||||
.. option:: --worker-write-rate=<SIZE>
|
||||
|
||||
Set maximum average write rate on frontend
|
||||
connection per worker. Setting 0 to this option
|
||||
means write rate is unlimited. Not implemented
|
||||
yet.
|
||||
Set maximum average write rate on frontend connection
|
||||
per worker. Setting 0 to this option means write rate
|
||||
is unlimited. Not implemented yet.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --worker-write-burst=<SIZE>
|
||||
|
||||
Set maximum write burst size on frontend
|
||||
connection per worker. Setting 0 to this option
|
||||
means write burst size is unlimited. Not
|
||||
implemented yet.
|
||||
Set maximum write burst size on frontend connection per
|
||||
worker. Setting 0 to this option means write burst size
|
||||
is unlimited. Not implemented yet.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --worker-frontend-connections=<NUM>
|
||||
.. option:: --worker-frontend-connections=<N>
|
||||
|
||||
Set maximum number of simultaneous connections
|
||||
frontend accepts. Setting 0 means unlimited.
|
||||
Set maximum number of simultaneous connections frontend
|
||||
accepts. Setting 0 means unlimited.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --backend-http1-connections-per-host=<NUM>
|
||||
.. 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
|
||||
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
|
||||
:option:`--backend-http1-connections-per-frontend`\.
|
||||
|
||||
Default: ``8``
|
||||
|
||||
.. option:: --backend-http1-connections-per-frontend=<NUM>
|
||||
.. option:: --backend-http1-connections-per-frontend=<N>
|
||||
|
||||
Set maximum number of backend concurrent HTTP/1
|
||||
connections per frontend. This option is only
|
||||
used for default mode. 0 means unlimited. To
|
||||
limit the number of connections per host for
|
||||
HTTP/2 or SPDY proxy mode (-s option), use
|
||||
:option:`--backend-http1-connections-per-host`\.
|
||||
Set maximum number of backend concurrent HTTP/1
|
||||
connections per frontend. This option is only used for
|
||||
default mode. 0 means unlimited. To limit the number
|
||||
of connections per host for HTTP/2 or SPDY proxy mode
|
||||
(-s option), use :option:`--backend-http1-connections-per-host`\.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --rlimit-nofile=<N>
|
||||
|
||||
Set maximum number of open files (RLIMIT_NOFILE)
|
||||
to <N>. If 0 is given, nghttpx does not set the
|
||||
limit.
|
||||
Set maximum number of open files (RLIMIT_NOFILE) to <N>.
|
||||
If 0 is given, nghttpx does not set the limit.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --backend-request-buffer=<SIZE>
|
||||
|
||||
Timeout:
|
||||
~~~~~~~~
|
||||
Set buffer size used to store backend request.
|
||||
|
||||
.. option:: --frontend-http2-read-timeout=<SEC>
|
||||
Default: ``16K``
|
||||
|
||||
Specify read timeout for HTTP/2 and SPDY frontend
|
||||
.. option:: --backend-response-buffer=<SIZE>
|
||||
|
||||
Set buffer size used to store backend response.
|
||||
|
||||
Default: ``16K``
|
||||
|
||||
|
||||
Timeout
|
||||
~~~~~~~
|
||||
|
||||
.. option:: --frontend-http2-read-timeout=<DURATION>
|
||||
|
||||
Specify read timeout for HTTP/2 and SPDY frontend
|
||||
connection.
|
||||
|
||||
Default: ``180``
|
||||
Default: ``180s``
|
||||
|
||||
.. option:: --frontend-read-timeout=<SEC>
|
||||
.. option:: --frontend-read-timeout=<DURATION>
|
||||
|
||||
Specify read timeout for HTTP/1.1 frontend
|
||||
connection.
|
||||
Specify read timeout for HTTP/1.1 frontend connection.
|
||||
|
||||
Default: ``180``
|
||||
Default: ``180s``
|
||||
|
||||
.. option:: --frontend-write-timeout=<SEC>
|
||||
.. option:: --frontend-write-timeout=<DURATION>
|
||||
|
||||
Specify write timeout for all frontend
|
||||
connections.
|
||||
Specify write timeout for all frontend connections.
|
||||
|
||||
Default: ``30``
|
||||
Default: ``30s``
|
||||
|
||||
.. option:: --stream-read-timeout=<SEC>
|
||||
.. option:: --stream-read-timeout=<DURATION>
|
||||
|
||||
Specify read timeout for HTTP/2 and SPDY streams.
|
||||
0 means no timeout.
|
||||
Specify read timeout for HTTP/2 and SPDY streams. 0
|
||||
means no timeout.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --stream-write-timeout=<SEC>
|
||||
.. option:: --stream-write-timeout=<DURATION>
|
||||
|
||||
Specify write timeout for HTTP/2 and SPDY
|
||||
streams. 0 means no timeout.
|
||||
Specify write timeout for HTTP/2 and SPDY streams. 0
|
||||
means no timeout.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --backend-read-timeout=<SEC>
|
||||
.. option:: --backend-read-timeout=<DURATION>
|
||||
|
||||
Specify read timeout for backend connection.
|
||||
|
||||
Default: ``180``
|
||||
Default: ``180s``
|
||||
|
||||
.. option:: --backend-write-timeout=<SEC>
|
||||
.. option:: --backend-write-timeout=<DURATION>
|
||||
|
||||
Specify write timeout for backend connection.
|
||||
|
||||
Default: ``30``
|
||||
Default: ``30s``
|
||||
|
||||
.. option:: --backend-keep-alive-timeout=<SEC>
|
||||
.. option:: --backend-keep-alive-timeout=<DURATION>
|
||||
|
||||
Specify keep-alive timeout for backend
|
||||
connection.
|
||||
Specify keep-alive timeout for backend connection.
|
||||
|
||||
Default: ``600``
|
||||
Default: ``2s``
|
||||
|
||||
.. option:: --listener-disable-timeout=<SEC>
|
||||
.. option:: --listener-disable-timeout=<DURATION>
|
||||
|
||||
After accepting connection failed, connection
|
||||
listener is disabled for a given time in seconds.
|
||||
Specifying 0 disables this feature.
|
||||
After accepting connection failed, connection listener
|
||||
is disabled for a given amount of time. Specifying 0
|
||||
disables this feature.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
|
||||
SSL/TLS:
|
||||
~~~~~~~~
|
||||
SSL/TLS
|
||||
~~~~~~~
|
||||
|
||||
.. option:: --ciphers=<SUITE>
|
||||
|
||||
Set allowed cipher list. The format of the
|
||||
string is described in OpenSSL ciphers(1).
|
||||
Set allowed cipher list. The format of the string is
|
||||
described in OpenSSL ciphers(1).
|
||||
|
||||
.. option:: -k, --insecure
|
||||
|
||||
Don't verify backend server's certificate if :option:`-p`\,
|
||||
:option:`--client` or :option:`\--http2-bridge` are given and
|
||||
Don't verify backend server's certificate if :option:`-p`\,
|
||||
:option:`--client` or :option:`\--http2-bridge` are given and
|
||||
:option:`--backend-no-tls` is not given.
|
||||
|
||||
.. option:: --cacert=<PATH>
|
||||
|
||||
Set path to trusted CA certificate file if :option:`-p`\,
|
||||
:option:`--client` or :option:`\--http2-bridge` are given and
|
||||
:option:`--backend-no-tls` is not given. The file must be
|
||||
in PEM format. It can contain multiple
|
||||
certificates. If the linked OpenSSL is
|
||||
configured to load system wide certificates, they
|
||||
are loaded at startup regardless of this option.
|
||||
Set path to trusted CA certificate file if :option:`-p`\, :option:`--client`
|
||||
or :option:`--http2-bridge` are given and :option:`\--backend-no-tls` is not
|
||||
given. The file must be in PEM format. It can contain
|
||||
multiple certificates. If the linked OpenSSL is
|
||||
configured to load system wide certificates, they are
|
||||
loaded at startup regardless of this option.
|
||||
|
||||
.. option:: --private-key-passwd-file=<FILEPATH>
|
||||
.. option:: --private-key-passwd-file=<PATH>
|
||||
|
||||
Path to file that contains password for the
|
||||
server's private key. If none is given and the
|
||||
private key is password protected it'll be
|
||||
requested interactively.
|
||||
Path to file that contains password for the server's
|
||||
private key. If none is given and the private key is
|
||||
password protected it'll be requested interactively.
|
||||
|
||||
.. option:: --subcert=<KEYPATH>:<CERTPATH>
|
||||
|
||||
Specify additional certificate and private key
|
||||
file. nghttpx will choose certificates based on
|
||||
the hostname indicated by client using TLS SNI
|
||||
extension. This option can be used multiple
|
||||
times.
|
||||
Specify additional certificate and private key file.
|
||||
nghttpx will choose certificates based on the hostname
|
||||
indicated by client using TLS SNI extension. This
|
||||
option can be used multiple times.
|
||||
|
||||
.. option:: --backend-tls-sni-field=<HOST>
|
||||
|
||||
Explicitly set the content of the TLS SNI
|
||||
extension. This will default to the backend HOST
|
||||
name.
|
||||
Explicitly set the content of the TLS SNI extension.
|
||||
This will default to the backend HOST name.
|
||||
|
||||
.. option:: --dh-param-file=<PATH>
|
||||
|
||||
Path to file that contains DH parameters in PEM
|
||||
format. Without this option, DHE cipher suites
|
||||
are not available.
|
||||
Path to file that contains DH parameters in PEM format.
|
||||
Without this option, DHE cipher suites are not
|
||||
available.
|
||||
|
||||
.. option:: --npn-list=<LIST>
|
||||
|
||||
Comma delimited list of ALPN protocol identifier
|
||||
sorted in the order of preference. That means
|
||||
most desirable protocol comes first. This is
|
||||
used in both ALPN and NPN. The parameter must be
|
||||
delimited by a single comma only and any white
|
||||
spaces are treated as a part of protocol string.
|
||||
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-16,h2-14,spdy/3.1,http/1.1``
|
||||
Default: ``h2,h2-16,h2-14,spdy/3.1,http/1.1``
|
||||
|
||||
.. option:: --verify-client
|
||||
|
||||
@@ -331,78 +328,84 @@ SSL/TLS:
|
||||
|
||||
.. option:: --verify-client-cacert=<PATH>
|
||||
|
||||
Path to file that contains CA certificates to
|
||||
verify client certificate. The file must be in
|
||||
PEM format. It can contain multiple
|
||||
certificates.
|
||||
Path to file that contains CA certificates to verify
|
||||
client certificate. The file must be in PEM format. It
|
||||
can contain multiple certificates.
|
||||
|
||||
.. option:: --client-private-key-file=<PATH>
|
||||
|
||||
Path to file that contains client private key
|
||||
used in backend client authentication.
|
||||
Path to file that contains client private key used in
|
||||
backend client authentication.
|
||||
|
||||
.. option:: --client-cert-file=<PATH>
|
||||
|
||||
Path to file that contains client certificate
|
||||
used in backend client authentication.
|
||||
Path to file that contains client certificate used in
|
||||
backend client authentication.
|
||||
|
||||
.. option:: --tls-proto-list=<LIST>
|
||||
|
||||
Comma delimited list of SSL/TLS protocol to be
|
||||
enabled. The following protocols are available:
|
||||
TLSv1.2, TLSv1.1 and TLSv1.0. The name matching
|
||||
is done in case-insensitive manner. The
|
||||
parameter must be delimited by a single comma
|
||||
only and any white spaces are treated as a part
|
||||
of protocol string.
|
||||
Comma delimited list of SSL/TLS protocol to be enabled.
|
||||
The following protocols are available: TLSv1.2, TLSv1.1
|
||||
and TLSv1.0. The name matching is done in
|
||||
case-insensitive manner. The parameter must be
|
||||
delimited by a single comma only and any white spaces
|
||||
are treated as a part of protocol string.
|
||||
|
||||
Default: ``TLSv1.2,TLSv1.1``
|
||||
|
||||
.. option:: --tls-ticket-key-file=<FILE>
|
||||
.. 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 48 bytes random data to
|
||||
construct TLS session ticket parameters. This options
|
||||
can be used repeatedly to specify multiple ticket
|
||||
parameters. If several files are given, only the first
|
||||
key is used to encrypt TLS session tickets. Other keys
|
||||
are accepted but server will issue new session ticket
|
||||
with first key. This allows session key rotation.
|
||||
Please note that key rotation does not occur
|
||||
automatically. User should rearrange files or change
|
||||
options values and restart nghttpx gracefully. If
|
||||
opening or reading given file fails, all loaded keys are
|
||||
discarded and it is treated as if none of this option is
|
||||
given. If this option is not given or an error occurred
|
||||
while opening or reading a file, key is generated
|
||||
automatically and renewed every 12hrs. At most 2 keys
|
||||
are stored in memory.
|
||||
|
||||
.. option:: --tls-ctx-per-worker
|
||||
|
||||
Create OpenSSL's SSL_CTX per worker, so that no internal
|
||||
locking is required. This may improve scalability with
|
||||
multi threaded configuration. If this option is
|
||||
enabled, session ID is no longer shared accross SSL_CTX
|
||||
objects, which means session ID generated by one worker
|
||||
is not acceptable by another worker. On the other hand,
|
||||
session ticket key is shared across all worker threads.
|
||||
|
||||
|
||||
HTTP/2 and SPDY:
|
||||
~~~~~~~~~~~~~~~~
|
||||
HTTP/2 and SPDY
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
.. option:: -c, --http2-max-concurrent-streams=<NUM>
|
||||
.. option:: -c, --http2-max-concurrent-streams=<N>
|
||||
|
||||
Set the maximum number of the concurrent streams
|
||||
in one HTTP/2 and SPDY session.
|
||||
Set the maximum number of the concurrent streams in one
|
||||
HTTP/2 and SPDY session.
|
||||
|
||||
Default: ``100``
|
||||
|
||||
.. option:: --frontend-http2-window-bits=<N>
|
||||
|
||||
Sets the per-stream initial window size of HTTP/2
|
||||
SPDY frontend connection. For HTTP/2, the size
|
||||
is 2**<N>-1. For SPDY, the size is 2\*\*<N>.
|
||||
Sets the per-stream initial window size of HTTP/2 SPDY
|
||||
frontend connection. For HTTP/2, the size is 2\*\*<N>-1.
|
||||
For SPDY, the size is 2\*\*<N>.
|
||||
|
||||
Default: ``16``
|
||||
|
||||
.. option:: --frontend-http2-connection-window-bits=<N>
|
||||
|
||||
Sets the per-connection window size of HTTP/2 and
|
||||
SPDY frontend connection. For HTTP/2, the size
|
||||
is 2**<N>-1. For SPDY, the size is 2\*\*<N>.
|
||||
Sets the per-connection window size of HTTP/2 and SPDY
|
||||
frontend connection. For HTTP/2, the size is
|
||||
2**<N>-1. For SPDY, the size is 2\*\*<N>.
|
||||
|
||||
Default: ``16``
|
||||
|
||||
@@ -412,15 +415,15 @@ HTTP/2 and SPDY:
|
||||
|
||||
.. option:: --backend-http2-window-bits=<N>
|
||||
|
||||
Sets the initial window size of HTTP/2 backend
|
||||
connection to 2**<N>-1.
|
||||
Sets the initial window size of HTTP/2 backend
|
||||
connection to 2\*\*<N>-1.
|
||||
|
||||
Default: ``16``
|
||||
|
||||
.. option:: --backend-http2-connection-window-bits=<N>
|
||||
|
||||
Sets the per-connection window size of HTTP/2
|
||||
backend connection to 2\*\*<N>-1.
|
||||
Sets the per-connection window size of HTTP/2 backend
|
||||
connection to 2\*\*<N>-1.
|
||||
|
||||
Default: ``16``
|
||||
|
||||
@@ -434,22 +437,29 @@ HTTP/2 and SPDY:
|
||||
|
||||
.. option:: --padding=<N>
|
||||
|
||||
Add at most <N> bytes to a HTTP/2 frame payload
|
||||
as padding. Specify 0 to disable padding. This
|
||||
option is meant for debugging purpose and not
|
||||
intended to enhance protocol security.
|
||||
Add at most <N> bytes to a HTTP/2 frame payload as
|
||||
padding. Specify 0 to disable padding. This option is
|
||||
meant for debugging purpose and not intended to enhance
|
||||
protocol security.
|
||||
|
||||
.. option:: --no-server-push
|
||||
|
||||
Disable HTTP/2 server push. Server push is only
|
||||
supported by default mode and HTTP/2 frontend. SPDY
|
||||
frontend does not support server push.
|
||||
|
||||
|
||||
Mode:
|
||||
~~~~~
|
||||
Mode
|
||||
~~~~
|
||||
|
||||
.. describe:: (default mode)
|
||||
|
||||
Accept HTTP/2, SPDY and HTTP/1.1 over SSL/TLS.
|
||||
If :option:`--frontend-no-tls` is used, accept HTTP/2 and
|
||||
HTTP/1.1. The incoming HTTP/1.1 connection can
|
||||
be upgraded to HTTP/2 through HTTP Upgrade. The
|
||||
protocol to the backend is HTTP/1.1.
|
||||
|
||||
Accept HTTP/2, SPDY and HTTP/1.1 over SSL/TLS. If
|
||||
:option:`--frontend-no-tls` is used, accept HTTP/2 and HTTP/1.1.
|
||||
The incoming HTTP/1.1 connection can be upgraded to
|
||||
HTTP/2 through HTTP Upgrade. The protocol to the
|
||||
backend is HTTP/1.1.
|
||||
|
||||
.. option:: -s, --http2-proxy
|
||||
|
||||
@@ -457,89 +467,86 @@ Mode:
|
||||
|
||||
.. option:: --http2-bridge
|
||||
|
||||
Like default mode, but communicate with the
|
||||
backend in HTTP/2 over SSL/TLS. Thus the
|
||||
incoming all connections are converted to HTTP/2
|
||||
connection and relayed to the backend. See
|
||||
:option:`--backend-http-proxy-uri` option if you are behind
|
||||
the proxy and want to connect to the outside
|
||||
Like default mode, but communicate with the backend in
|
||||
HTTP/2 over SSL/TLS. Thus the incoming all connections
|
||||
are converted to HTTP/2 connection and relayed to the
|
||||
backend. See :option:`--backend-http-proxy-uri` option if you are
|
||||
behind the proxy and want to connect to the outside
|
||||
HTTP/2 proxy.
|
||||
|
||||
.. option:: --client
|
||||
|
||||
Accept HTTP/2 and HTTP/1.1 without SSL/TLS. The
|
||||
incoming HTTP/1.1 connection can be upgraded to
|
||||
HTTP/2 connection through HTTP Upgrade. The
|
||||
protocol to the backend is HTTP/2. To use
|
||||
nghttpx as a forward proxy, use :option:`-p` option
|
||||
instead.
|
||||
Accept HTTP/2 and HTTP/1.1 without SSL/TLS. The
|
||||
incoming HTTP/1.1 connection can be upgraded to HTTP/2
|
||||
connection through HTTP Upgrade. The protocol to the
|
||||
backend is HTTP/2. To use nghttpx as a forward proxy,
|
||||
use :option:`-p` option instead.
|
||||
|
||||
.. option:: -p, --client-proxy
|
||||
|
||||
Like :option:`--client` option, but it also requires the
|
||||
request path from frontend must be an absolute
|
||||
URI, suitable for use as a forward proxy.
|
||||
Like :option:`--client` option, but it also requires the request
|
||||
path from frontend must be an absolute URI, suitable for
|
||||
use as a forward proxy.
|
||||
|
||||
|
||||
Logging:
|
||||
~~~~~~~~
|
||||
Logging
|
||||
~~~~~~~
|
||||
|
||||
.. option:: -L, --log-level=<LEVEL>
|
||||
|
||||
Set the severity level of log output. <LEVEL>
|
||||
must be one of INFO, NOTICE, WARN, ERROR and
|
||||
FATAL.
|
||||
Set the severity level of log output. <LEVEL> must be
|
||||
one of INFO, NOTICE, WARN, ERROR and FATAL.
|
||||
|
||||
Default: ``NOTICE``
|
||||
|
||||
.. option:: --accesslog-file=<PATH>
|
||||
|
||||
Set path to write access log. To reopen file,
|
||||
send USR1 signal to nghttpx.
|
||||
Set path to write access log. To reopen file, send USR1
|
||||
signal to nghttpx.
|
||||
|
||||
.. option:: --accesslog-syslog
|
||||
|
||||
Send access log to syslog. If this option is
|
||||
used, :option:`--accesslog-file` option is ignored.
|
||||
Send access log to syslog. If this option is used,
|
||||
:option:`--accesslog-file` option is ignored.
|
||||
|
||||
.. option:: --accesslog-format=<FORMAT>
|
||||
|
||||
Specify format string for access log. The
|
||||
default format is combined format. The following
|
||||
variables are available:
|
||||
Specify format string for access log. The default
|
||||
format is combined format. The following variables are
|
||||
available:
|
||||
|
||||
* $remote_addr: client IP address.
|
||||
* $time_local: local time in Common Log format.
|
||||
* $time_iso8601: local time in ISO 8601 format.
|
||||
* $request: HTTP request line.
|
||||
* $status: HTTP response status code.
|
||||
* $body_bytes_sent: the number of bytes sent to
|
||||
client as response body.
|
||||
* $http_<VAR>: value of HTTP request header <VAR>
|
||||
where '_' in <VAR> is replaced with '-'.
|
||||
* $body_bytes_sent: the number of bytes sent to client
|
||||
as response body.
|
||||
* $http_<VAR>: value of HTTP request header <VAR> where
|
||||
'_' in <VAR> is replaced with '-'.
|
||||
* $remote_port: client port.
|
||||
* $server_port: server port.
|
||||
* $request_time: request processing time in
|
||||
seconds with milliseconds resolution.
|
||||
* $request_time: request processing time in seconds with
|
||||
milliseconds resolution.
|
||||
* $pid: PID of the running process.
|
||||
* $alpn: ALPN identifier of the protocol which
|
||||
generates the response. For HTTP/1, ALPN is
|
||||
always http/1.1, regardless of minor version.
|
||||
* $alpn: ALPN identifier of the protocol which generates
|
||||
the response. For HTTP/1, ALPN is always http/1.1,
|
||||
regardless of minor version.
|
||||
|
||||
|
||||
Default: ``$remote_addr - - [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"``
|
||||
|
||||
.. option:: --errorlog-file=<PATH>
|
||||
|
||||
Set path to write error log. To reopen file,
|
||||
send USR1 signal to nghttpx.
|
||||
Set path to write error log. To reopen file, send USR1
|
||||
signal to nghttpx.
|
||||
|
||||
Default: ``/dev/stderr``
|
||||
|
||||
.. option:: --errorlog-syslog
|
||||
|
||||
Send error log to syslog. If this option is
|
||||
used, :option:`--errorlog-file` option is ignored.
|
||||
Send error log to syslog. If this option is used,
|
||||
:option:`--errorlog-file` option is ignored.
|
||||
|
||||
.. option:: --syslog-facility=<FACILITY>
|
||||
|
||||
@@ -548,78 +555,89 @@ Logging:
|
||||
Default: ``daemon``
|
||||
|
||||
|
||||
Misc:
|
||||
~~~~~
|
||||
HTTP
|
||||
~~~~
|
||||
|
||||
.. option:: --add-x-forwarded-for
|
||||
|
||||
Append X-Forwarded-For header field to the
|
||||
downstream request.
|
||||
Append X-Forwarded-For header field to the downstream
|
||||
request.
|
||||
|
||||
.. option:: --strip-incoming-x-forwarded-for
|
||||
|
||||
Strip X-Forwarded-For header field from inbound
|
||||
client requests.
|
||||
Strip X-Forwarded-For header field from inbound client
|
||||
requests.
|
||||
|
||||
.. option:: --no-via
|
||||
|
||||
Don't append to Via header field. If Via header
|
||||
field is received, it is left unaltered.
|
||||
Don't append to Via header field. If Via header field
|
||||
is received, it is left unaltered.
|
||||
|
||||
.. option:: --no-location-rewrite
|
||||
|
||||
Don't rewrite location header field on
|
||||
:option:`--http2-bridge`\, :option:`--client` and default mode. For
|
||||
:option:`--http2-proxy` and :option:`\--client-proxy` mode, location
|
||||
header field will not be altered regardless of
|
||||
this option.
|
||||
Don't rewrite location header field on :option:`--http2-bridge`\,
|
||||
:option:`--client` and default mode. For :option:`\--http2-proxy` and
|
||||
:option:`--client-proxy` mode, location header field will not be
|
||||
altered regardless of this option.
|
||||
|
||||
.. option:: --no-host-rewrite
|
||||
|
||||
Don't rewrite host and :authority header fields on
|
||||
:option:`--http2-bridge`\, :option:`--client` and default mode. For
|
||||
:option:`--http2-proxy` and :option:`\--client-proxy` mode, these headers
|
||||
will not be altered regardless of this option.
|
||||
|
||||
.. option:: --altsvc=<PROTOID,PORT[,HOST,[ORIGIN]]>
|
||||
|
||||
Specify protocol ID, port, host and origin of
|
||||
alternative service. <HOST> and <ORIGIN> are
|
||||
optional. They are advertised in alt-svc header
|
||||
field or HTTP/2 ALTSVC frame. This option can be
|
||||
used multiple times to specify multiple
|
||||
alternative services. Example: :option:`--altsvc`\=h2,443
|
||||
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
|
||||
|
||||
.. option:: --add-response-header=<HEADER>
|
||||
|
||||
Specify additional header field to add to
|
||||
response header set. This option just appends
|
||||
header field and won't replace anything already
|
||||
set. This option can be used several times to
|
||||
specify multiple header fields.
|
||||
Specify additional header field to add to response
|
||||
header set. This option just appends header field and
|
||||
won't replace anything already set. This option can be
|
||||
used several times to specify multiple header fields.
|
||||
Example: :option:`--add-response-header`\="foo: bar"
|
||||
|
||||
|
||||
Debug
|
||||
~~~~~
|
||||
|
||||
.. option:: --frontend-http2-dump-request-header=<PATH>
|
||||
|
||||
Dumps request headers received by HTTP/2 frontend
|
||||
to the file denoted in <PATH>. The output is
|
||||
done in HTTP/1 header field format and each
|
||||
header block is followed by an empty line. This
|
||||
option is not thread safe and MUST NOT be used
|
||||
with option :option:`-n`\<N>, where <N> >= 2.
|
||||
Dumps request headers received by HTTP/2 frontend to the
|
||||
file denoted in <PATH>. The output is done in HTTP/1
|
||||
header field format and each header block is followed by
|
||||
an empty line. This option is not thread safe and MUST
|
||||
NOT be used with option :option:`-n`\<N>, where <N> >= 2.
|
||||
|
||||
.. option:: --frontend-http2-dump-response-header=<PATH>
|
||||
|
||||
Dumps response headers sent from HTTP/2 frontend
|
||||
to the file denoted in <PATH>. The output is
|
||||
done in HTTP/1 header field format and each
|
||||
header block is followed by an empty line. This
|
||||
option is not thread safe and MUST NOT be used
|
||||
with option :option:`-n`\<N>, where <N> >= 2.
|
||||
Dumps response headers sent from HTTP/2 frontend to the
|
||||
file denoted in <PATH>. The output is done in HTTP/1
|
||||
header field format and each header block is followed by
|
||||
an empty line. This option is not thread safe and MUST
|
||||
NOT be used with option :option:`-n`\<N>, where <N> >= 2.
|
||||
|
||||
.. option:: -o, --frontend-frame-debug
|
||||
|
||||
Print HTTP/2 frames in frontend to stderr. This
|
||||
option is not thread safe and MUST NOT be used
|
||||
with option :option:`-n`\=N, where N >= 2.
|
||||
Print HTTP/2 frames in frontend to stderr. This option
|
||||
is not thread safe and MUST NOT be used with option
|
||||
:option:`-n`\=N, where N >= 2.
|
||||
|
||||
|
||||
Process
|
||||
~~~~~~~
|
||||
|
||||
.. option:: -D, --daemon
|
||||
|
||||
Run in a background. If :option:`-D` is used, the current
|
||||
working directory is changed to '*/*'.
|
||||
Run in a background. If :option:`-D` is used, the current working
|
||||
directory is changed to '*/*'.
|
||||
|
||||
.. option:: --pid-file=<PATH>
|
||||
|
||||
@@ -627,8 +645,12 @@ Misc:
|
||||
|
||||
.. option:: --user=<USER>
|
||||
|
||||
Run this program as <USER>. This option is
|
||||
intended to be used to drop root privileges.
|
||||
Run this program as <USER>. This option is intended to
|
||||
be used to drop root privileges.
|
||||
|
||||
|
||||
Misc
|
||||
~~~~
|
||||
|
||||
.. option:: --conf=<PATH>
|
||||
|
||||
@@ -644,6 +666,14 @@ Misc:
|
||||
|
||||
Print this help and exit.
|
||||
|
||||
|
||||
The <SIZE> argument is an integer and an optional unit (e.g., 10K is
|
||||
10 * 1024). Units are K, M and G (powers of 1024).
|
||||
|
||||
The <DURATION> argument is an integer and an optional unit (e.g., 1s
|
||||
is 1 second and 500ms is 500 milliseconds). Units are s or ms. If
|
||||
a unit is omitted, a second is used as unit.
|
||||
|
||||
FILES
|
||||
-----
|
||||
|
||||
@@ -688,6 +718,48 @@ SIGUSR2
|
||||
After new process comes up, sending SIGQUIT to the original process
|
||||
to perform hot swapping.
|
||||
|
||||
SERVER PUSH
|
||||
-----------
|
||||
|
||||
nghttpx supports HTTP/2 server push in default mode. nghttpx looks
|
||||
for Link header field (`RFC 5988
|
||||
<http://tools.ietf.org/html/rfc5988>`_) in response headers from
|
||||
backend server and extracts URI-reference with parameter
|
||||
``rel=preload`` (see `preload
|
||||
<http://w3c.github.io/preload/#interoperability-with-http-link-header>`_)
|
||||
and pushes those URIs to the frontend client. Here is a sample Link
|
||||
header field to initiate server push:
|
||||
|
||||
.. code-block:: http
|
||||
|
||||
Link: </fonts/font.woff>; rel=preload
|
||||
Link: </css/theme.css>; rel=preload
|
||||
|
||||
Currently, the following restrictions are applied for server push:
|
||||
|
||||
1. URI-reference must not contain authority. If it exists, it is not
|
||||
pushed. ``/fonts/font.woff`` and ``css/theme.css`` are eligible to
|
||||
be pushed. ``https://example.org/fonts/font.woff`` and
|
||||
``//example.org/css/theme.css`` are not.
|
||||
|
||||
2. The associated stream must have method "GET" or "POST". The
|
||||
associated stream's status code must be 200.
|
||||
|
||||
These limitations may be loosened in the future release.
|
||||
|
||||
UNIX DOMAIN SOCKET
|
||||
------------------
|
||||
|
||||
nghttpx supports UNIX domain socket with a filename for both frontend
|
||||
and backend connections.
|
||||
|
||||
Please note that current nghttpx implementation does not delete a
|
||||
socket with a filename. And on start up, if nghttpx detects that the
|
||||
specified socket already exists in the file system, nghttpx first
|
||||
deletes it. However, if SIGUSR2 is used to execute new binary and
|
||||
both old and new configurations use same filename, new binary does not
|
||||
delete the socket and continues to use it.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
|
||||
|
||||
@@ -42,6 +42,48 @@ SIGUSR2
|
||||
After new process comes up, sending SIGQUIT to the original process
|
||||
to perform hot swapping.
|
||||
|
||||
SERVER PUSH
|
||||
-----------
|
||||
|
||||
nghttpx supports HTTP/2 server push in default mode. nghttpx looks
|
||||
for Link header field (`RFC 5988
|
||||
<http://tools.ietf.org/html/rfc5988>`_) in response headers from
|
||||
backend server and extracts URI-reference with parameter
|
||||
``rel=preload`` (see `preload
|
||||
<http://w3c.github.io/preload/#interoperability-with-http-link-header>`_)
|
||||
and pushes those URIs to the frontend client. Here is a sample Link
|
||||
header field to initiate server push:
|
||||
|
||||
.. code-block:: http
|
||||
|
||||
Link: </fonts/font.woff>; rel=preload
|
||||
Link: </css/theme.css>; rel=preload
|
||||
|
||||
Currently, the following restrictions are applied for server push:
|
||||
|
||||
1. URI-reference must not contain authority. If it exists, it is not
|
||||
pushed. ``/fonts/font.woff`` and ``css/theme.css`` are eligible to
|
||||
be pushed. ``https://example.org/fonts/font.woff`` and
|
||||
``//example.org/css/theme.css`` are not.
|
||||
|
||||
2. The associated stream must have method "GET" or "POST". The
|
||||
associated stream's status code must be 200.
|
||||
|
||||
These limitations may be loosened in the future release.
|
||||
|
||||
UNIX DOMAIN SOCKET
|
||||
------------------
|
||||
|
||||
nghttpx supports UNIX domain socket with a filename for both frontend
|
||||
and backend connections.
|
||||
|
||||
Please note that current nghttpx implementation does not delete a
|
||||
socket with a filename. And on start up, if nghttpx detects that the
|
||||
specified socket already exists in the file system, nghttpx first
|
||||
deletes it. However, if SIGUSR2 is used to execute new binary and
|
||||
both old and new configurations use same filename, new binary does not
|
||||
delete the socket and continues to use it.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
|
||||
|
||||
@@ -33,6 +33,8 @@ Contents:
|
||||
python-apiref
|
||||
nghttp2.h
|
||||
nghttp2ver.h
|
||||
asio_http2_server.h
|
||||
asio_http2_client.h
|
||||
asio_http2.h
|
||||
Source <https://github.com/tatsuhiro-t/nghttp2>
|
||||
Issues <https://github.com/tatsuhiro-t/nghttp2/issues>
|
||||
|
||||
@@ -4,7 +4,7 @@ libnghttp2_asio: High level HTTP/2 C++ library
|
||||
libnghttp2_asio is C++ library built on top of libnghttp2 and provides
|
||||
high level abstraction API to build HTTP/2 applications. It depends
|
||||
on Boost::ASIO library and OpenSSL. Currently libnghttp2_asio
|
||||
provides server side API.
|
||||
provides server and client side API.
|
||||
|
||||
libnghttp2_asio is not built by default. Use ``--enable-asio-lib``
|
||||
configure flag to build libnghttp2_asio. The required Boost libraries
|
||||
@@ -14,42 +14,64 @@ are:
|
||||
* Boost::System
|
||||
* Boost::Thread
|
||||
|
||||
To use libnghttp2_asio, first include following header file:
|
||||
We have 3 header files for this library:
|
||||
|
||||
.. code-block:: cpp
|
||||
* :doc:`asio_http2_server.h`
|
||||
* :doc:`asio_http2_client.h`
|
||||
* :doc:`asio_http2.h`
|
||||
|
||||
#include <nghttp2/asio_http2.h>
|
||||
asio_http2.h is included from the other two files.
|
||||
|
||||
Also take a look at that header file :doc:`asio_http2.h`.
|
||||
To build a program with libnghttp2_asio, link to the following
|
||||
libraries::
|
||||
|
||||
-lnghttp2_asio -lboost_system
|
||||
|
||||
If ``boost::asio::ssl`` is used in application code, OpenSSL is also
|
||||
required in link line::
|
||||
|
||||
-lnghttp2_asio -lboost_system -lssl -lcrypto
|
||||
|
||||
Server API
|
||||
----------
|
||||
|
||||
To use server API, first include following header file:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#include <nghttp2/asio_http2_server.h>
|
||||
|
||||
Also take a look at that header file :doc:`asio_http2_server.h`.
|
||||
|
||||
Server API is designed to build HTTP/2 server very easily to utilize
|
||||
C++11 anonymous function and closure. The bare minimum example of
|
||||
HTTP/2 server looks like this:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#include <nghttp2/asio_http2.h>
|
||||
|
||||
using namespace nghttp2::asio_http2;
|
||||
using namespace nghttp2::asio_http2::server;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
boost::system::error_code ec;
|
||||
http2 server;
|
||||
|
||||
server.listen("*", 3000, [](const std::shared_ptr<request> &req,
|
||||
const std::shared_ptr<response> &res) {
|
||||
res->write_head(200);
|
||||
res->end("hello, world");
|
||||
server.handle("/", [](const request &req, const response &res) {
|
||||
res.write_head(200);
|
||||
res.end("hello, world\n");
|
||||
});
|
||||
|
||||
if (server.listen_and_serve(ec, "localhost", "3000")) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
First we instantiate ``nghttp2::asio_http2::server::http2`` object.
|
||||
Then call ``nghttp2::asio_http2::server::http2::listen`` function with
|
||||
address and port to listen to and callback function, namely "request
|
||||
callback", invoked when request arrives.
|
||||
``nghttp2::asio_http2::server::http2::handle`` function registers
|
||||
pattern and its handler function. In this example, we register "/" as
|
||||
pattern, which matches all requests. Then call
|
||||
``nghttp2::asio_http2::server::http2::listen_and_serve`` function with
|
||||
address and port to listen to.
|
||||
|
||||
The ``req`` and ``res`` represent HTTP request and response
|
||||
respectively. ``nghttp2::asio_http2_::server::response::write_head``
|
||||
@@ -61,6 +83,10 @@ send.
|
||||
``nghttp2::asio_http2::server::response::end`` sends responde body.
|
||||
In the above example, we send string "hello, world".
|
||||
|
||||
The life time of req and res object ends after the callback set by
|
||||
``nghttp2::asio_http2::server::response::on_close`` function.
|
||||
Application must not use those objects after this call.
|
||||
|
||||
Serving static files and enabling SSL/TLS
|
||||
+++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
@@ -69,40 +95,47 @@ SSL/TLS.
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#include <nghttp2/asio_http2.h>
|
||||
#include <nghttp2/asio_http2_server.h>
|
||||
|
||||
using namespace nghttp2::asio_http2;
|
||||
using namespace nghttp2::asio_http2::server;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
boost::system::error_code ec;
|
||||
boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23);
|
||||
|
||||
tls.use_private_key_file("server.key", boost::asio::ssl::context::pem);
|
||||
tls.use_certificate_chain_file("server.crt");
|
||||
|
||||
configure_tls_context_easy(ec, tls);
|
||||
|
||||
http2 server;
|
||||
|
||||
server.tls("server.key", "server.crt");
|
||||
|
||||
server.listen("*", 3000, [](const std::shared_ptr<request> &req,
|
||||
const std::shared_ptr<response> &res) {
|
||||
if (req->path() == "/" || req->path() == "/index.html") {
|
||||
res->write_head(200);
|
||||
res->end(file_reader("index.html"));
|
||||
} else {
|
||||
res->write_head(404);
|
||||
res->end("<html><head><title>404</title></head>"
|
||||
"<body>404 Not Found</body></html>");
|
||||
}
|
||||
server.handle("/index.html", [](const request &req, const response &res) {
|
||||
res.write_head(200);
|
||||
res.end(file_generator("index.html"));
|
||||
});
|
||||
|
||||
if (server.listen_and_serve(ec, tls, "localhost", "3000")) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
Specifying path to private key file and certificate file in
|
||||
``nghttp2::asio_http2::server::http2::tls`` will enable SSL/TLS. Both
|
||||
files must be in PEM format.
|
||||
We first create ``boost::asio::ssl::context`` object and set path to
|
||||
private key file and certificate file.
|
||||
``nghttp2::asio_http2::server::configure_tls_context_easy`` function
|
||||
configures SSL/TLS context object for HTTP/2 server use, including NPN
|
||||
callbacks.
|
||||
|
||||
In the above example, if request path is either "/" or "/index.html",
|
||||
we serve index.html file in the current working directory.
|
||||
In the above example, if request path is "/index.html", we serve
|
||||
index.html file in the current working directory.
|
||||
``nghttp2::asio_http2::server::response::end`` has overload to take
|
||||
function of type ``nghttp2::asio_http2::read_cb`` and application pass
|
||||
its implementation to generate response body. For the convenience,
|
||||
libnghttp2_asio library provides ``nghttp2::asio_http2::file_reader``
|
||||
function to generate function to server static file.
|
||||
function of type ``nghttp2::asio_http2::generator_cb`` and application
|
||||
pass its implementation to generate response body. For the
|
||||
convenience, libnghttp2_asio library provides
|
||||
``nghttp2::asio_http2::file_generator`` function to generate function
|
||||
to server static file. If other resource is requested, server
|
||||
automatically responds with 404 status code.
|
||||
|
||||
Server push
|
||||
+++++++++++
|
||||
@@ -111,44 +144,56 @@ Server push is also supported.
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#include <nghttp2/asio_http2.h>
|
||||
#include <nghttp2/asio_http2_server.h>
|
||||
|
||||
using namespace nghttp2::asio_http2;
|
||||
using namespace nghttp2::asio_http2::server;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
boost::system::error_code ec;
|
||||
boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23);
|
||||
|
||||
tls.use_private_key_file("server.key", boost::asio::ssl::context::pem);
|
||||
tls.use_certificate_chain_file("server.crt");
|
||||
|
||||
configure_tls_context_easy(ec, tls);
|
||||
|
||||
http2 server;
|
||||
|
||||
server.tls("server.key", "server.crt");
|
||||
std::string style_css = "h1 { color: green; }";
|
||||
|
||||
server.listen("*", 3000, [](const std::shared_ptr<request> &req,
|
||||
const std::shared_ptr<response> &res) {
|
||||
if (req->path() == "/") {
|
||||
req->push("GET", "/my.css");
|
||||
server.handle("/", [&style_css](const request &req, const response &res) {
|
||||
boost::system::error_code ec;
|
||||
auto push = res.push(ec, "GET", "/style.css");
|
||||
push->write_head(200);
|
||||
push->end(style_css);
|
||||
|
||||
res->write_head(200);
|
||||
res->end(file_reader("index.html"));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (req->path() == "/my.css") {
|
||||
res->write_head(200);
|
||||
res->end(file_reader("my.css"));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
res->write_head(404);
|
||||
res->end("<html><head><title>404</title></head>"
|
||||
"<body>404 Not Found</body></html>");
|
||||
res.write_head(200);
|
||||
res.end(R"(
|
||||
<!DOCTYPE html><html lang="en">
|
||||
<title>HTTP/2 FTW</title><body>
|
||||
<link href="/style.css" rel="stylesheet" type="text/css">
|
||||
<h1>This should be green</h1>
|
||||
</body></html>
|
||||
)");
|
||||
});
|
||||
|
||||
server.handle("/style.css",
|
||||
[&style_css](const request &req, const response &res) {
|
||||
res.write_head(200);
|
||||
res.end(style_css);
|
||||
});
|
||||
|
||||
if (server.listen_and_serve(ec, tls, "localhost", "3000")) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
When client requested "/", we push "/my.css". To push resource, call
|
||||
``nghttp2::asio_http2::server::request::push`` function with desired
|
||||
method and path. Later, the callback will be called with the pushed
|
||||
resource "/my.css".
|
||||
When client requested any resource other than "/style.css", we push
|
||||
"/style.css". To push resource, call
|
||||
``nghttp2::asio_http2::server::response::push`` function with desired
|
||||
method and path. It returns another response object and use its
|
||||
functions to send push response.
|
||||
|
||||
Enable multi-threading
|
||||
++++++++++++++++++++++
|
||||
@@ -164,65 +209,225 @@ desired number of threads:
|
||||
// Use 4 native threads
|
||||
server.num_threads(4);
|
||||
|
||||
Run blocking tasks in background thread
|
||||
+++++++++++++++++++++++++++++++++++++++
|
||||
Client API
|
||||
----------
|
||||
|
||||
The request callback is called in the same thread where HTTP request
|
||||
is handled. And many connections shares the same thread, we cannot
|
||||
directly run blocking tasks in request callback.
|
||||
|
||||
To run blocking tasks, use
|
||||
``nghttp2::asio_http2::server::request::run_task``. The passed
|
||||
callback will be executed in the different thread from the thread
|
||||
where request callback was executed. So application can perform
|
||||
blocking task there. The example follows:
|
||||
To use client API, first include following header file:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#include <unistd.h>
|
||||
#include <nghttp2/asio_http2.h>
|
||||
#include <nghttp2/asio_http2_client.h>
|
||||
|
||||
Also take a look at that header file :doc:`asio_http2_client.h`.
|
||||
|
||||
Here is the sample client code to access HTTP/2 server and print out
|
||||
response header fields and response body to the console screen:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <nghttp2/asio_http2_client.h>
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
using namespace nghttp2::asio_http2;
|
||||
using namespace nghttp2::asio_http2::server;
|
||||
using namespace nghttp2::asio_http2::client;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
http2 server;
|
||||
boost::system::error_code ec;
|
||||
boost::asio::io_service io_service;
|
||||
|
||||
server.num_concurrent_tasks(16);
|
||||
// connect to localhost:3000
|
||||
session sess(io_service, "localhost", "3000");
|
||||
|
||||
server.listen("*", 3000, [](const std::shared_ptr<request> &req,
|
||||
const std::shared_ptr<response> &res) {
|
||||
req->run_task([res](channel &channel) {
|
||||
// executed in different thread than the thread where
|
||||
// request callback was executed.
|
||||
sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) {
|
||||
boost::system::error_code ec;
|
||||
|
||||
// using res directly here is not safe. Capturing it by
|
||||
// value is safe because it is std::shared_ptr.
|
||||
auto req = sess.submit(ec, "GET", "http://localhost:3000/");
|
||||
|
||||
sleep(1);
|
||||
req->on_response([](const response &res) {
|
||||
// print status code and response header fields.
|
||||
std::cerr << "HTTP/2 " << res.status_code() << std::endl;
|
||||
for (auto &kv : res.header()) {
|
||||
std::cerr << kv.first << ": " << kv.second.value << "\n";
|
||||
}
|
||||
std::cerr << std::endl;
|
||||
|
||||
channel.post([res]() {
|
||||
// executed in the same thread where request callback
|
||||
// was executed.
|
||||
res->write_head(200);
|
||||
res->end("hello, world");
|
||||
});
|
||||
});
|
||||
res.on_data([](const uint8_t *data, std::size_t len) {
|
||||
std::cerr.write(reinterpret_cast<const char *>(data), len);
|
||||
std::cerr << std::endl;
|
||||
});
|
||||
});
|
||||
|
||||
req->on_close([&sess](uint32_t error_code) {
|
||||
// shutdown session after first request was done.
|
||||
sess.shutdown();
|
||||
});
|
||||
});
|
||||
|
||||
sess.on_error([](const boost::system::error_code &ec) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
});
|
||||
|
||||
io_service.run();
|
||||
}
|
||||
|
||||
First we set the number of background threads which run tasks. By
|
||||
default it is set to 1. In this example, we set it to 16, so at most
|
||||
16 tasks can be executed concurrently without blocking handling new
|
||||
requests.
|
||||
``nghttp2::asio_http2::client::session`` object takes
|
||||
``boost::asio::io_service`` object and remote server address. When
|
||||
connection is made, the callback function passed to
|
||||
``nghttp2::asio_http2::client::on_connect`` is invoked with connected
|
||||
address as its paramter. After this callback call, use
|
||||
``nghttp2::asio_http2::session::submit`` to send request to the
|
||||
server. You can submit multiple requests at once without waiting for
|
||||
the completion of previous request.
|
||||
|
||||
We call ``req->run_task()`` to execute task in background thread. In
|
||||
the passed callback, we just simply sleeps 1 second. After sleep is
|
||||
over, we schedule another callback to send response to the client.
|
||||
Since the callback passed to ``req->run_task()`` is executed in the
|
||||
different thread from the thread where request callback is called,
|
||||
using ``req`` or ``res`` object directly there may cause undefined
|
||||
behaviour. To avoid this issue, we can use
|
||||
``nghttp2::asio_http2::channel::post`` by supplying a callback which
|
||||
in turn get called in the same thread where request callback was
|
||||
called.
|
||||
The life time of req and res object ends after the callback set by
|
||||
``nghttp2::asio_http2::server::request::on_close`` function.
|
||||
Application must not use those objects after this call.
|
||||
|
||||
Normally, client does not stop even after all requests are done unless
|
||||
connection is lost. To stop client, call
|
||||
``nghttp2::asio_http2::server::session::shutdown()``.
|
||||
|
||||
Recieve server push and enable SSL/TLS
|
||||
++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <nghttp2/asio_http2_client.h>
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
using namespace nghttp2::asio_http2;
|
||||
using namespace nghttp2::asio_http2::client;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
boost::system::error_code ec;
|
||||
boost::asio::io_service io_service;
|
||||
|
||||
boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23);
|
||||
tls.set_default_verify_paths();
|
||||
// disabled to make development easier...
|
||||
// tls_ctx.set_verify_mode(boost::asio::ssl::verify_peer);
|
||||
configure_tls_context(ec, tls);
|
||||
|
||||
// connect to localhost:3000
|
||||
session sess(io_service, tls, "localhost", "3000");
|
||||
|
||||
sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) {
|
||||
boost::system::error_code ec;
|
||||
|
||||
auto req = sess.submit(ec, "GET", "http://localhost:3000/");
|
||||
|
||||
req->on_response([&sess](const response &res) {
|
||||
std::cerr << "response received!" << std::endl;
|
||||
res.on_data([&sess](const uint8_t *data, std::size_t len) {
|
||||
std::cerr.write(reinterpret_cast<const char *>(data), len);
|
||||
std::cerr << std::endl;
|
||||
});
|
||||
});
|
||||
|
||||
req->on_push([](const request &push) {
|
||||
std::cerr << "push request received!" << std::endl;
|
||||
push.on_response([](const response &res) {
|
||||
std::cerr << "push response received!" << std::endl;
|
||||
res.on_data([](const uint8_t *data, std::size_t len) {
|
||||
std::cerr.write(reinterpret_cast<const char *>(data), len);
|
||||
std::cerr << std::endl;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
sess.on_error([](const boost::system::error_code &ec) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
});
|
||||
|
||||
io_service.run();
|
||||
}
|
||||
|
||||
The above sample code demonstrates how to enable SSL/TLS and receive
|
||||
server push. Currently,
|
||||
``nghttp2::asio_http2::client::configure_tls_context`` function setups
|
||||
NPN callbacks for SSL/TLS context for HTTP/2 use.
|
||||
|
||||
To receive server push, use
|
||||
``nghttp2::asio_http2::client::request::on_push`` function to set
|
||||
callback function which is invoked when server push request is
|
||||
arrived. The callback function takes
|
||||
``nghttp2::asio_http2::client::request`` object, which contains the
|
||||
pushed request. To get server push response, set callback using
|
||||
``nghttp2::asio_http2::client::request::on_response``.
|
||||
|
||||
As stated in the previous section, client does not stop automatically
|
||||
as long as HTTP/2 session is fine and connection is alive. We don't
|
||||
call ``nghttp2::asio_http2::client::session::shutdown`` in this
|
||||
example, so the program does not terminate after all responses are
|
||||
received. Hit Ctrl-C to terminate the program.
|
||||
|
||||
Multiple concurrent requests
|
||||
++++++++++++++++++++++++++++
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <nghttp2/asio_http2_client.h>
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
using namespace nghttp2::asio_http2;
|
||||
using namespace nghttp2::asio_http2::client;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
boost::system::error_code ec;
|
||||
boost::asio::io_service io_service;
|
||||
|
||||
// connect to localhost:3000
|
||||
session sess(io_service, "localhost", "3000");
|
||||
|
||||
sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) {
|
||||
boost::system::error_code ec;
|
||||
|
||||
auto printer = [](const response &res) {
|
||||
res.on_data([](const uint8_t *data, std::size_t len) {
|
||||
std::cerr.write(reinterpret_cast<const char *>(data), len);
|
||||
std::cerr << std::endl;
|
||||
});
|
||||
};
|
||||
|
||||
std::size_t num = 3;
|
||||
auto count = std::make_shared<int>(num);
|
||||
|
||||
for (std::size_t i = 0; i < num; ++i) {
|
||||
auto req = sess.submit(ec, "GET",
|
||||
"http://localhost:3000/" + std::to_string(i + 1));
|
||||
|
||||
req->on_response(printer);
|
||||
req->on_close([&sess, count](uint32_t error_code) {
|
||||
if (--*count == 0) {
|
||||
// shutdown session after |num| requests were done.
|
||||
sess.shutdown();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
sess.on_error([](const boost::system::error_code &ec) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
});
|
||||
|
||||
io_service.run();
|
||||
}
|
||||
|
||||
Here is the sample to send 3 requests at once. Depending on the
|
||||
server settings, these requests are processed out-of-order. In this
|
||||
example, we have a trick to shutdown session after all requests were
|
||||
done. We made ``count`` object which is shared pointer to int and is
|
||||
initialized to 3. On each request closure (the invocation of the
|
||||
callback set by ``nghttp2::asio_http2::client::request::on_close``),
|
||||
we decrement the count. If count becomes 0, we are sure that all
|
||||
requests have been done and initiate shutdown.
|
||||
|
||||
@@ -59,11 +59,11 @@ SPDY protocols and it works as so called SPDY proxy.
|
||||
With ``--frontend-no-tls`` option, SSL/TLS is turned off in frontend
|
||||
connection, so the connection gets insecure.
|
||||
|
||||
The backend must be HTTP/1 proxy server. nghttpx only supports
|
||||
multiple backend server addresses. It translates incoming requests to
|
||||
HTTP/1 request to backend server. The backend server performs real
|
||||
proxy work for each request, for example, dispatching requests to the
|
||||
origin server and caching contents.
|
||||
The backend must be HTTP/1 proxy server. nghttpx supports multiple
|
||||
backend server addresses. It translates incoming requests to HTTP/1
|
||||
request to backend server. The backend server performs real proxy
|
||||
work for each request, for example, dispatching requests to the origin
|
||||
server and caching contents.
|
||||
|
||||
For example, to make nghttpx listen to encrypted HTTP/2 requests at
|
||||
port 8443, and a backend HTTP/1 proxy server is configured to listen
|
||||
@@ -124,7 +124,9 @@ HTTP/1 frontend connection can be upgraded to HTTP/2 using HTTP
|
||||
Upgrade. To disable SSL/TLS in backend connection, use
|
||||
``--backend-no-tls`` option.
|
||||
|
||||
The backend connection is created one per worker (thread).
|
||||
By default, the number of backend HTTP/2 connections per worker
|
||||
(thread) is determined by number of ``-b`` option. To adjust this
|
||||
value, use ``--backend-http2-connections-per-worker`` option.
|
||||
|
||||
The backend server is supporsed to be a HTTP/2 web server (e.g.,
|
||||
nghttpd). The one use-case of this mode is utilize existing HTTP/1
|
||||
@@ -156,7 +158,9 @@ HTTP/1 frontend connection can be upgraded to HTTP/2 using HTTP
|
||||
Upgrade. To disable SSL/TLS in backend connection, use
|
||||
``--backend-no-tls`` option.
|
||||
|
||||
The backend connection is created one per worker (thread).
|
||||
By default, the number of backend HTTP/2 connections per worker
|
||||
(thread) is determined by number of ``-b`` option. To adjust this
|
||||
value, use ``--backend-http2-connections-per-worker`` option.
|
||||
|
||||
The backend server must be a HTTP/2 proxy. You can use nghttpx in
|
||||
`HTTP/2 proxy mode`_ as backend server. The one use-case of this mode
|
||||
@@ -196,10 +200,14 @@ With ``--frontend-no-tls`` option, SSL/TLS is turned off in frontend
|
||||
connection, so the connection gets insecure. To disable SSL/TLS in
|
||||
backend connection, use ``--backend-no-tls`` option.
|
||||
|
||||
By default, the number of backend HTTP/2 connections per worker
|
||||
(thread) is determined by number of ``-b`` option. To adjust this
|
||||
value, use ``--backend-http2-connections-per-worker`` option.
|
||||
|
||||
The backend server is supporsed to be a HTTP/2 web server or HTTP/2
|
||||
proxy. If backend server is HTTP/2 proxy, use
|
||||
``--no-location-rewrite`` option to disable rewriting location header
|
||||
field.
|
||||
``--no-location-rewrite`` and ``--no-host-rewrite`` options to disable
|
||||
rewriting location, host and :authority header field.
|
||||
|
||||
The use-case of this mode is aggregate the incoming connections to one
|
||||
HTTP/2 connection. One backend HTTP/2 connection is created per
|
||||
@@ -285,11 +293,11 @@ re-open log files, send USR1 signal to nghttpx process. It will
|
||||
re-open files specified by ``--accesslog-file`` and
|
||||
``--errorlog-file`` options.
|
||||
|
||||
Multiple HTTP/1 backend addresses
|
||||
---------------------------------
|
||||
Multiple backend addresses
|
||||
--------------------------
|
||||
|
||||
nghttpx supports multiple HTTP/1 backend addresses. To specify them,
|
||||
just use ``-b`` option repeatedly. For example, to use backend1:8080
|
||||
and backend2:8080, use command-line like this: ``-bbackend1,8080
|
||||
-bbackend2,8080``. Please note that HTTP/2 backend only supports 1
|
||||
backend address.
|
||||
nghttpx supports multiple backend addresses. To specify them, just
|
||||
use ``-b`` option repeatedly. For example, to use backend1:8080 and
|
||||
backend2:8080, use command-line like this: ``-bbackend1,8080
|
||||
-bbackend2,8080``. For HTTP/2 backend, see also
|
||||
``--backend-http2-connections-per-worker`` option.
|
||||
|
||||
5
examples/.gitignore
vendored
5
examples/.gitignore
vendored
@@ -2,7 +2,8 @@ client
|
||||
libevent-client
|
||||
libevent-server
|
||||
deflate
|
||||
asio-sv
|
||||
tiny-nghttpd
|
||||
asio-sv
|
||||
asio-sv2
|
||||
asio-sv3
|
||||
asio-cl
|
||||
asio-cl2
|
||||
|
||||
@@ -33,12 +33,10 @@ AM_CPPFLAGS = \
|
||||
@LIBEVENT_OPENSSL_CFLAGS@ \
|
||||
@OPENSSL_CFLAGS@ \
|
||||
@DEFS@
|
||||
AM_LDFLAGS = \
|
||||
LDADD = $(top_builddir)/lib/libnghttp2.la \
|
||||
$(top_builddir)/third-party/libhttp-parser.la \
|
||||
@LIBEVENT_OPENSSL_LIBS@ \
|
||||
@OPENSSL_LIBS@
|
||||
LDADD = \
|
||||
$(top_builddir)/lib/libnghttp2.la \
|
||||
$(top_builddir)/third-party/libhttp-parser.la
|
||||
|
||||
noinst_PROGRAMS = client libevent-client libevent-server deflate
|
||||
|
||||
@@ -60,26 +58,34 @@ endif # ENABLE_TINY_NGHTTPD
|
||||
|
||||
if ENABLE_ASIO_LIB
|
||||
|
||||
noinst_PROGRAMS += asio-sv asio-sv2 asio-sv3
|
||||
noinst_PROGRAMS += asio-sv asio-sv2 asio-cl asio-cl2
|
||||
|
||||
ASIOCPPFLAGS = ${BOOST_CPPFLAGS} ${AM_CPPFLAGS}
|
||||
ASIOLDFLAGS = @JEMALLOC_LIBS@
|
||||
ASIOLDADD = $(top_builddir)/src/libnghttp2_asio.la
|
||||
ASIOLDADD = $(top_builddir)/lib/libnghttp2.la \
|
||||
$(top_builddir)/src/libnghttp2_asio.la @JEMALLOC_LIBS@ \
|
||||
$(top_builddir)/third-party/libhttp-parser.la \
|
||||
${BOOST_LDFLAGS} \
|
||||
${BOOST_ASIO_LIB} \
|
||||
${BOOST_THREAD_LIB} \
|
||||
${BOOST_SYSTEM_LIB} \
|
||||
@OPENSSL_LIBS@ \
|
||||
@APPLDFLAGS@
|
||||
|
||||
asio_sv_SOURCES = asio-sv.cc
|
||||
asio_sv_CPPFLAGS = ${ASIOCPPFLAGS}
|
||||
asio_sv_LDFLAGS = ${ASIOLDFLAGS}
|
||||
asio_sv_LDADD = ${ASIOLDADD}
|
||||
|
||||
asio_sv2_SOURCES = asio-sv2.cc
|
||||
asio_sv2_CPPFLAGS = ${ASIOCPPFLAGS}
|
||||
asio_sv2_LDFLAGS = ${ASIOLDFLAGS}
|
||||
asio_sv2_LDADD = ${ASIOLDADD}
|
||||
|
||||
asio_sv3_SOURCES = asio-sv3.cc
|
||||
asio_sv3_CPPFLAGS = ${ASIOCPPFLAGS}
|
||||
asio_sv3_LDFLAGS = ${ASIOLDFLAGS}
|
||||
asio_sv3_LDADD = ${ASIOLDADD}
|
||||
asio_cl_SOURCES = asio-cl.cc
|
||||
asio_cl_CPPFLAGS = ${ASIOCPPFLAGS}
|
||||
asio_cl_LDADD = ${ASIOLDADD}
|
||||
|
||||
asio_cl2_SOURCES = asio-cl2.cc
|
||||
asio_cl2_CPPFLAGS = ${ASIOCPPFLAGS}
|
||||
asio_cl2_LDADD = ${ASIOLDADD}
|
||||
|
||||
endif # ENABLE_ASIO_LIB
|
||||
|
||||
|
||||
96
examples/asio-cl.cc
Normal file
96
examples/asio-cl.cc
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <nghttp2/asio_http2_client.h>
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
using namespace nghttp2::asio_http2;
|
||||
using namespace nghttp2::asio_http2::client;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
try {
|
||||
if (argc < 2) {
|
||||
std::cerr << "Usage: asio-cl URI" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
boost::system::error_code ec;
|
||||
boost::asio::io_service io_service;
|
||||
|
||||
std::string uri = argv[1];
|
||||
std::string scheme, host, service;
|
||||
|
||||
if (host_service_from_uri(ec, scheme, host, service, uri)) {
|
||||
std::cerr << "error: bad URI: " << ec.message() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
boost::asio::ssl::context tls_ctx(boost::asio::ssl::context::sslv23);
|
||||
tls_ctx.set_default_verify_paths();
|
||||
// disabled to make development easier...
|
||||
// tls_ctx.set_verify_mode(boost::asio::ssl::verify_peer);
|
||||
configure_tls_context(ec, tls_ctx);
|
||||
|
||||
auto sess = scheme == "https" ? session(io_service, tls_ctx, host, service)
|
||||
: session(io_service, host, service);
|
||||
|
||||
sess.on_connect([&sess, &uri](tcp::resolver::iterator endpoint_it) {
|
||||
boost::system::error_code ec;
|
||||
auto req = sess.submit(ec, "GET", uri);
|
||||
|
||||
if (ec) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
req->on_response([&sess](const response &res) {
|
||||
std::cerr << "HTTP/2 " << res.status_code() << std::endl;
|
||||
for (auto &kv : res.header()) {
|
||||
std::cerr << kv.first << ": " << kv.second.value << "\n";
|
||||
}
|
||||
std::cerr << std::endl;
|
||||
|
||||
res.on_data([&sess](const uint8_t *data, std::size_t len) {
|
||||
std::cerr.write(reinterpret_cast<const char *>(data), len);
|
||||
std::cerr << std::endl;
|
||||
});
|
||||
});
|
||||
|
||||
req->on_close([&sess](uint32_t error_code) { sess.shutdown(); });
|
||||
});
|
||||
|
||||
sess.on_error([](const boost::system::error_code &ec) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
});
|
||||
|
||||
io_service.run();
|
||||
} catch (std::exception &e) {
|
||||
std::cerr << "exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
134
examples/asio-cl2.cc
Normal file
134
examples/asio-cl2.cc
Normal file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <nghttp2/asio_http2_client.h>
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
using namespace nghttp2::asio_http2;
|
||||
using namespace nghttp2::asio_http2::client;
|
||||
|
||||
void print_header(const header_map &h) {
|
||||
for (auto &kv : h) {
|
||||
std::cerr << kv.first << ": " << kv.second.value << "\n";
|
||||
}
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
|
||||
void print_header(const response &res) {
|
||||
std::cerr << "HTTP/2 " << res.status_code() << "\n";
|
||||
print_header(res.header());
|
||||
}
|
||||
|
||||
void print_header(const request &req) {
|
||||
auto &uri = req.uri();
|
||||
std::cerr << req.method() << " " << uri.scheme << "://" << uri.host
|
||||
<< uri.path;
|
||||
if (!uri.raw_query.empty()) {
|
||||
std::cerr << "?" << uri.raw_query;
|
||||
}
|
||||
std::cerr << " HTTP/2\n";
|
||||
print_header(req.header());
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
try {
|
||||
if (argc < 2) {
|
||||
std::cerr << "Usage: asio-cl URI" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
boost::system::error_code ec;
|
||||
boost::asio::io_service io_service;
|
||||
|
||||
std::string uri = argv[1];
|
||||
std::string scheme, host, service;
|
||||
|
||||
if (host_service_from_uri(ec, scheme, host, service, uri)) {
|
||||
std::cerr << "error: bad URI: " << ec.message() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
boost::asio::ssl::context tls_ctx(boost::asio::ssl::context::sslv23);
|
||||
tls_ctx.set_default_verify_paths();
|
||||
// disabled to make development easier...
|
||||
// tls_ctx.set_verify_mode(boost::asio::ssl::verify_peer);
|
||||
configure_tls_context(ec, tls_ctx);
|
||||
|
||||
auto sess = scheme == "https" ? session(io_service, tls_ctx, host, service)
|
||||
: session(io_service, host, service);
|
||||
|
||||
sess.on_connect([&sess, &uri](tcp::resolver::iterator endpoint_it) {
|
||||
std::cerr << "connected to " << (*endpoint_it).endpoint() << std::endl;
|
||||
boost::system::error_code ec;
|
||||
auto req = sess.submit(ec, "GET", uri, {{"cookie", {"foo=bar", true}}});
|
||||
if (ec) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
req->on_response([&sess, req](const response &res) {
|
||||
std::cerr << "response header was received" << std::endl;
|
||||
print_header(res);
|
||||
|
||||
res.on_data([&sess](const uint8_t *data, std::size_t len) {
|
||||
std::cerr.write(reinterpret_cast<const char *>(data), len);
|
||||
std::cerr << std::endl;
|
||||
});
|
||||
});
|
||||
|
||||
req->on_close([&sess](uint32_t error_code) {
|
||||
std::cerr << "request done with error_code=" << error_code << std::endl;
|
||||
});
|
||||
|
||||
req->on_push([](const request &push_req) {
|
||||
std::cerr << "push request was received" << std::endl;
|
||||
|
||||
print_header(push_req);
|
||||
|
||||
push_req.on_response([](const response &res) {
|
||||
std::cerr << "push response header was received" << std::endl;
|
||||
|
||||
res.on_data([](const uint8_t *data, std::size_t len) {
|
||||
std::cerr.write(reinterpret_cast<const char *>(data), len);
|
||||
std::cerr << std::endl;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
sess.on_error([](const boost::system::error_code &ec) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
});
|
||||
|
||||
io_service.run();
|
||||
} catch (std::exception &e) {
|
||||
std::cerr << "exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -37,7 +37,7 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <nghttp2/asio_http2.h>
|
||||
#include <nghttp2/asio_http2_server.h>
|
||||
|
||||
using namespace nghttp2::asio_http2;
|
||||
using namespace nghttp2::asio_http2::server;
|
||||
@@ -45,28 +45,102 @@ using namespace nghttp2::asio_http2::server;
|
||||
int main(int argc, char *argv[]) {
|
||||
try {
|
||||
// Check command line arguments.
|
||||
if (argc < 3) {
|
||||
std::cerr << "Usage: asio-sv <port> <threads> <private-key-file> "
|
||||
<< "<cert-file>\n";
|
||||
if (argc < 4) {
|
||||
std::cerr
|
||||
<< "Usage: asio-sv <address> <port> <threads> [<private-key-file> "
|
||||
<< "<cert-file>]\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint16_t port = std::stoi(argv[1]);
|
||||
std::size_t num_threads = std::stoi(argv[2]);
|
||||
boost::system::error_code ec;
|
||||
|
||||
std::string addr = argv[1];
|
||||
std::string port = argv[2];
|
||||
std::size_t num_threads = std::stoi(argv[3]);
|
||||
|
||||
http2 server;
|
||||
|
||||
server.num_threads(num_threads);
|
||||
|
||||
if (argc >= 5) {
|
||||
server.tls(argv[3], argv[4]);
|
||||
}
|
||||
|
||||
server.listen("*", port, [](const std::shared_ptr<request> &req,
|
||||
const std::shared_ptr<response> &res) {
|
||||
res->write_head(200, {header{"foo", "bar"}});
|
||||
res->end("hello, world");
|
||||
server.handle("/", [](const request &req, const response &res) {
|
||||
res.write_head(200, {{"foo", {"bar"}}});
|
||||
res.end("hello, world\n");
|
||||
});
|
||||
server.handle("/secret/", [](const request &req, const response &res) {
|
||||
res.write_head(200);
|
||||
res.end("under construction!\n");
|
||||
});
|
||||
server.handle("/push", [](const request &req, const response &res) {
|
||||
boost::system::error_code ec;
|
||||
auto push = res.push(ec, "GET", "/push/1");
|
||||
if (!ec) {
|
||||
push->write_head(200);
|
||||
push->end("server push FTW!\n");
|
||||
}
|
||||
|
||||
res.write_head(200);
|
||||
res.end("you'll receive server push!\n");
|
||||
});
|
||||
server.handle("/delay", [](const request &req, const response &res) {
|
||||
res.write_head(200);
|
||||
|
||||
auto timer = std::make_shared<boost::asio::deadline_timer>(
|
||||
res.io_service(), boost::posix_time::seconds(3));
|
||||
auto closed = std::make_shared<bool>();
|
||||
|
||||
res.on_close([timer, closed](uint32_t error_code) {
|
||||
timer->cancel();
|
||||
*closed = true;
|
||||
});
|
||||
|
||||
timer->async_wait([&res, closed](const boost::system::error_code &ec) {
|
||||
if (ec || *closed) {
|
||||
return;
|
||||
}
|
||||
|
||||
res.end("finally!\n");
|
||||
});
|
||||
});
|
||||
server.handle("/trailer", [](const request &req, const response &res) {
|
||||
// send trailer part.
|
||||
res.write_head(200, {{"trailers", {"digest"}}});
|
||||
|
||||
std::string body = "nghttp2 FTW!\n";
|
||||
auto left = std::make_shared<size_t>(body.size());
|
||||
|
||||
res.end([&res, body, left](uint8_t *dst, std::size_t len,
|
||||
uint32_t *data_flags) {
|
||||
auto n = std::min(len, *left);
|
||||
std::copy_n(body.c_str() + (body.size() - *left), n, dst);
|
||||
*left -= n;
|
||||
if (*left == 0) {
|
||||
*data_flags |=
|
||||
NGHTTP2_DATA_FLAG_EOF | NGHTTP2_DATA_FLAG_NO_END_STREAM;
|
||||
// RFC 3230 Instance Digests in HTTP. The digest value is
|
||||
// SHA-256 message digest of body.
|
||||
res.write_trailer(
|
||||
{{"digest",
|
||||
{"SHA-256=qqXqskW7F3ueBSvmZRCiSwl2ym4HRO0M/pvQCBlSDis="}}});
|
||||
}
|
||||
return n;
|
||||
});
|
||||
});
|
||||
|
||||
if (argc >= 6) {
|
||||
boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23);
|
||||
tls.use_private_key_file(argv[4], boost::asio::ssl::context::pem);
|
||||
tls.use_certificate_chain_file(argv[5]);
|
||||
|
||||
configure_tls_context_easy(ec, tls);
|
||||
|
||||
if (server.listen_and_serve(ec, tls, addr, port)) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
}
|
||||
} else {
|
||||
if (server.listen_and_serve(ec, addr, port)) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
}
|
||||
}
|
||||
} catch (std::exception &e) {
|
||||
std::cerr << "exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <nghttp2/asio_http2.h>
|
||||
#include <nghttp2/asio_http2_server.h>
|
||||
|
||||
using namespace nghttp2::asio_http2;
|
||||
using namespace nghttp2::asio_http2::server;
|
||||
@@ -48,30 +48,28 @@ using namespace nghttp2::asio_http2::server;
|
||||
int main(int argc, char *argv[]) {
|
||||
try {
|
||||
// Check command line arguments.
|
||||
if (argc < 4) {
|
||||
std::cerr << "Usage: asio-sv2 <port> <threads> <doc-root> "
|
||||
<< "<private-key-file> <cert-file>\n";
|
||||
if (argc < 5) {
|
||||
std::cerr << "Usage: asio-sv2 <address> <port> <threads> <doc-root> "
|
||||
<< "[<private-key-file> <cert-file>]\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint16_t port = std::stoi(argv[1]);
|
||||
std::size_t num_threads = std::stoi(argv[2]);
|
||||
std::string docroot = argv[3];
|
||||
boost::system::error_code ec;
|
||||
|
||||
std::string addr = argv[1];
|
||||
std::string port = argv[2];
|
||||
std::size_t num_threads = std::stoi(argv[3]);
|
||||
std::string docroot = argv[4];
|
||||
|
||||
http2 server;
|
||||
|
||||
server.num_threads(num_threads);
|
||||
|
||||
if (argc >= 6) {
|
||||
server.tls(argv[4], argv[5]);
|
||||
}
|
||||
|
||||
server.listen("*", port, [&docroot](const std::shared_ptr<request> &req,
|
||||
const std::shared_ptr<response> &res) {
|
||||
auto path = percent_decode(req->path());
|
||||
server.handle("/", [&docroot](const request &req, const response &res) {
|
||||
auto path = percent_decode(req.uri().path);
|
||||
if (!check_path(path)) {
|
||||
res->write_head(404);
|
||||
res->end();
|
||||
res.write_head(404);
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -82,23 +80,39 @@ int main(int argc, char *argv[]) {
|
||||
path = docroot + path;
|
||||
auto fd = open(path.c_str(), O_RDONLY);
|
||||
if (fd == -1) {
|
||||
res->write_head(404);
|
||||
res->end();
|
||||
res.write_head(404);
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
auto headers = std::vector<header>();
|
||||
auto header = header_map();
|
||||
|
||||
struct stat stbuf;
|
||||
if (stat(path.c_str(), &stbuf) == 0) {
|
||||
headers.push_back(
|
||||
header{"content-length", std::to_string(stbuf.st_size)});
|
||||
headers.push_back(
|
||||
header{"last-modified", http_date(stbuf.st_mtim.tv_sec)});
|
||||
header.emplace("content-length",
|
||||
header_value{std::to_string(stbuf.st_size)});
|
||||
header.emplace("last-modified",
|
||||
header_value{http_date(stbuf.st_mtime)});
|
||||
}
|
||||
res->write_head(200, std::move(headers));
|
||||
res->end(file_reader_from_fd(fd));
|
||||
res.write_head(200, std::move(header));
|
||||
res.end(file_generator_from_fd(fd));
|
||||
});
|
||||
|
||||
if (argc >= 7) {
|
||||
boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23);
|
||||
tls.use_private_key_file(argv[5], boost::asio::ssl::context::pem);
|
||||
tls.use_certificate_chain_file(argv[6]);
|
||||
|
||||
configure_tls_context_easy(ec, tls);
|
||||
|
||||
if (server.listen_and_serve(ec, tls, addr, port)) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
}
|
||||
} else {
|
||||
if (server.listen_and_serve(ec, addr, port)) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
}
|
||||
}
|
||||
} catch (std::exception &e) {
|
||||
std::cerr << "exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
@@ -1,142 +0,0 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2014 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
// We wrote this code based on the original code which has the
|
||||
// following license:
|
||||
//
|
||||
// main.cpp
|
||||
// ~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#include <unistd.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <deque>
|
||||
|
||||
#include <nghttp2/asio_http2.h>
|
||||
|
||||
using namespace nghttp2::asio_http2;
|
||||
using namespace nghttp2::asio_http2::server;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
try {
|
||||
// Check command line arguments.
|
||||
if (argc < 4) {
|
||||
std::cerr << "Usage: asio-sv3 <port> <threads> <tasks> "
|
||||
<< " <private-key-file> <cert-file>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint16_t port = std::stoi(argv[1]);
|
||||
std::size_t num_threads = std::stoi(argv[2]);
|
||||
std::size_t num_concurrent_tasks = std::stoi(argv[3]);
|
||||
|
||||
http2 server;
|
||||
|
||||
server.num_threads(num_threads);
|
||||
|
||||
if (argc >= 5) {
|
||||
server.tls(argv[4], argv[5]);
|
||||
}
|
||||
|
||||
server.num_concurrent_tasks(num_concurrent_tasks);
|
||||
|
||||
server.listen("*", port, [](const std::shared_ptr<request> &req,
|
||||
const std::shared_ptr<response> &res) {
|
||||
res->write_head(200);
|
||||
|
||||
auto msgq = std::make_shared<std::deque<std::string>>();
|
||||
|
||||
res->end([msgq](uint8_t * buf, std::size_t len)
|
||||
-> std::pair<ssize_t, bool> {
|
||||
if (msgq->empty()) {
|
||||
// if msgq is empty, tells the library that don't call
|
||||
// this callback until we call res->resume(). This is
|
||||
// done by returing std::make_pair(0, false).
|
||||
return std::make_pair(0, false);
|
||||
}
|
||||
auto msg = std::move(msgq->front());
|
||||
msgq->pop_front();
|
||||
|
||||
if (msg.empty()) {
|
||||
// The empty message signals the end of response in
|
||||
// this simple protocol.
|
||||
return std::make_pair(0, true);
|
||||
}
|
||||
|
||||
auto nwrite = std::min(len, msg.size());
|
||||
std::copy(std::begin(msg), std::begin(msg) + nwrite, buf);
|
||||
if (msg.size() > nwrite) {
|
||||
msgq->push_front(msg.substr(nwrite));
|
||||
}
|
||||
return std::make_pair(nwrite, false);
|
||||
});
|
||||
|
||||
req->run_task([res, msgq](channel &channel) {
|
||||
// executed in different thread from request callback
|
||||
// was called.
|
||||
|
||||
// Using res and msgq is not safe inside this callback.
|
||||
// But using them in callback passed to channel::post is
|
||||
// safe.
|
||||
|
||||
// We just emit simple message "message N\n" in every 1
|
||||
// second and 3 times in total.
|
||||
for (std::size_t i = 0; i < 3; ++i) {
|
||||
msgq->push_back("message " + std::to_string(i + 1) + "\n");
|
||||
|
||||
channel.post([res]() {
|
||||
// executed in same thread where
|
||||
// request callback was called.
|
||||
|
||||
// Tells library we have new message.
|
||||
res->resume();
|
||||
});
|
||||
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
// Send empty message to signal the end of response
|
||||
// body.
|
||||
msgq->push_back("");
|
||||
|
||||
channel.post([res]() {
|
||||
// executed in same thread where request
|
||||
// callback was called.
|
||||
res->resume();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
} catch (std::exception &e) {
|
||||
std::cerr << "exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -558,6 +558,8 @@ static void fetch_uri(const struct URI *uri) {
|
||||
diec("nghttp2_session_client_new", rv);
|
||||
}
|
||||
|
||||
nghttp2_submit_settings(connection.session, NGHTTP2_FLAG_NONE, NULL, 0);
|
||||
|
||||
/* Submit the HTTP request to the outbound queue. */
|
||||
submit_request(&connection, &req);
|
||||
|
||||
|
||||
@@ -179,13 +179,57 @@ static char *io_buf_add_str(io_buf *buf, const void *src, size_t len) {
|
||||
return (char *)start;
|
||||
}
|
||||
|
||||
static int memseq(const uint8_t *a, size_t alen, const char *b) {
|
||||
const uint8_t *last = a + alen;
|
||||
static int memeq(const void *a, const void *b, size_t n) {
|
||||
return memcmp(a, b, n) == 0;
|
||||
}
|
||||
|
||||
for (; a != last && *b && *a == *b; ++a, ++b)
|
||||
;
|
||||
#define streq(A, B, N) ((sizeof((A)) - 1) == (N) && memeq((A), (B), (N)))
|
||||
|
||||
return a == last && *b == 0;
|
||||
typedef enum {
|
||||
NGHTTP2_TOKEN__AUTHORITY,
|
||||
NGHTTP2_TOKEN__METHOD,
|
||||
NGHTTP2_TOKEN__PATH,
|
||||
NGHTTP2_TOKEN__SCHEME,
|
||||
NGHTTP2_TOKEN_HOST,
|
||||
} nghttp2_token;
|
||||
|
||||
/* Inspired by h2o header lookup. https://github.com/h2o/h2o */
|
||||
static int lookup_token(const uint8_t *name, size_t namelen) {
|
||||
switch (namelen) {
|
||||
case 5:
|
||||
switch (name[namelen - 1]) {
|
||||
case 'h':
|
||||
if (streq(":pat", name, 4)) {
|
||||
return NGHTTP2_TOKEN__PATH;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
switch (name[namelen - 1]) {
|
||||
case 'd':
|
||||
if (streq(":metho", name, 6)) {
|
||||
return NGHTTP2_TOKEN__METHOD;
|
||||
}
|
||||
break;
|
||||
case 'e':
|
||||
if (streq(":schem", name, 6)) {
|
||||
return NGHTTP2_TOKEN__SCHEME;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 10:
|
||||
switch (name[namelen - 1]) {
|
||||
case 'y':
|
||||
if (streq(":authorit", name, 9)) {
|
||||
return NGHTTP2_TOKEN__AUTHORITY;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static char *cpydig(char *buf, int n, size_t len) {
|
||||
@@ -925,8 +969,7 @@ static int on_header_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame, const uint8_t *name,
|
||||
size_t namelen, const uint8_t *value,
|
||||
size_t valuelen, uint8_t flags _U_,
|
||||
void *user_data) {
|
||||
connection *conn = user_data;
|
||||
void *user_data _U_) {
|
||||
stream *strm;
|
||||
|
||||
if (frame->hd.type != NGHTTP2_HEADERS ||
|
||||
@@ -940,74 +983,42 @@ static int on_header_callback(nghttp2_session *session,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!nghttp2_check_header_name(name, namelen) ||
|
||||
!nghttp2_check_header_value(value, valuelen)) {
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
if (memseq(name, namelen, ":method")) {
|
||||
if (strm->method) {
|
||||
stream_error(conn, strm->stream_id, NGHTTP2_PROTOCOL_ERROR);
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
switch (lookup_token(name, namelen)) {
|
||||
case NGHTTP2_TOKEN__METHOD:
|
||||
strm->method = io_buf_add_str(&strm->scrbuf, value, valuelen);
|
||||
if (!strm->method) {
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
strm->methodlen = valuelen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (memseq(name, namelen, ":scheme")) {
|
||||
if (strm->scheme) {
|
||||
stream_error(conn, strm->stream_id, NGHTTP2_PROTOCOL_ERROR);
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
break;
|
||||
case NGHTTP2_TOKEN__SCHEME:
|
||||
strm->scheme = io_buf_add_str(&strm->scrbuf, value, valuelen);
|
||||
if (!strm->scheme) {
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
strm->schemelen = valuelen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (memseq(name, namelen, ":authority")) {
|
||||
if (strm->authority) {
|
||||
stream_error(conn, strm->stream_id, NGHTTP2_PROTOCOL_ERROR);
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
break;
|
||||
case NGHTTP2_TOKEN__AUTHORITY:
|
||||
strm->authority = io_buf_add_str(&strm->scrbuf, value, valuelen);
|
||||
if (!strm->authority) {
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
strm->authoritylen = valuelen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (memseq(name, namelen, ":path")) {
|
||||
if (strm->path) {
|
||||
stream_error(conn, strm->stream_id, NGHTTP2_PROTOCOL_ERROR);
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
break;
|
||||
case NGHTTP2_TOKEN__PATH:
|
||||
strm->path = io_buf_add_str(&strm->scrbuf, value, valuelen);
|
||||
if (!strm->path) {
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
strm->pathlen = valuelen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (name[0] == ':') {
|
||||
stream_error(conn, strm->stream_id, NGHTTP2_PROTOCOL_ERROR);
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
if (memseq(name, namelen, "host") && !strm->host) {
|
||||
break;
|
||||
case NGHTTP2_TOKEN_HOST:
|
||||
strm->host = io_buf_add_str(&strm->scrbuf, value, valuelen);
|
||||
if (!strm->host) {
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
strm->hostlen = valuelen;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -1029,12 +1040,6 @@ static int on_frame_recv_callback(nghttp2_session *session,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strm->method || !strm->scheme || !strm->path ||
|
||||
(!strm->authority && !strm->host)) {
|
||||
stream_error(conn, strm->stream_id, NGHTTP2_PROTOCOL_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strm->host) {
|
||||
strm->host = strm->authority;
|
||||
strm->hostlen = strm->authoritylen;
|
||||
|
||||
@@ -20,6 +20,12 @@ HEADERS = [
|
||||
"alt-svc",
|
||||
"content-length",
|
||||
"location",
|
||||
"trailer",
|
||||
"link",
|
||||
"accept-encoding",
|
||||
"accept-language",
|
||||
"cache-control",
|
||||
"user-agent",
|
||||
# disallowed h1 headers
|
||||
'connection',
|
||||
'keep-alive',
|
||||
@@ -78,7 +84,7 @@ int lookup_token(const uint8_t *name, size_t namelen) {
|
||||
case '{}':'''.format(c)
|
||||
for k in headers:
|
||||
print '''\
|
||||
if (util::streq("{}", name, {})) {{
|
||||
if (util::streq_l("{}", name, {})) {{
|
||||
return {};
|
||||
}}'''.format(k[:-1], size - 1, to_enum_hd(k))
|
||||
print '''\
|
||||
|
||||
85
genlibtokenlookup.py
Executable file
85
genlibtokenlookup.py
Executable file
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
HEADERS = [
|
||||
':authority',
|
||||
':method',
|
||||
':path',
|
||||
':scheme',
|
||||
':status',
|
||||
"content-length",
|
||||
"host",
|
||||
"te",
|
||||
'connection',
|
||||
'keep-alive',
|
||||
'proxy-connection',
|
||||
'transfer-encoding',
|
||||
'upgrade'
|
||||
]
|
||||
|
||||
def to_enum_hd(k):
|
||||
res = 'NGHTTP2_TOKEN_'
|
||||
for c in k.upper():
|
||||
if c == ':' or c == '-':
|
||||
res += '_'
|
||||
continue
|
||||
res += c
|
||||
return res
|
||||
|
||||
def build_header(headers):
|
||||
res = {}
|
||||
for k in headers:
|
||||
size = len(k)
|
||||
if size not in res:
|
||||
res[size] = {}
|
||||
ent = res[size]
|
||||
c = k[-1]
|
||||
if c not in ent:
|
||||
ent[c] = []
|
||||
ent[c].append(k)
|
||||
|
||||
return res
|
||||
|
||||
def gen_enum():
|
||||
print '''\
|
||||
typedef enum {'''
|
||||
for k in sorted(HEADERS):
|
||||
print '''\
|
||||
{},'''.format(to_enum_hd(k))
|
||||
print '''\
|
||||
NGHTTP2_TOKEN_MAXIDX,
|
||||
} nghttp2_token;'''
|
||||
|
||||
def gen_index_header():
|
||||
print '''\
|
||||
static int lookup_token(const uint8_t *name, size_t namelen) {
|
||||
switch (namelen) {'''
|
||||
b = build_header(HEADERS)
|
||||
for size in sorted(b.keys()):
|
||||
ents = b[size]
|
||||
print '''\
|
||||
case {}:'''.format(size)
|
||||
print '''\
|
||||
switch (name[namelen - 1]) {'''
|
||||
for c in sorted(ents.keys()):
|
||||
headers = sorted(ents[c])
|
||||
print '''\
|
||||
case '{}':'''.format(c)
|
||||
for k in headers:
|
||||
print '''\
|
||||
if (streq("{}", name, {})) {{
|
||||
return {};
|
||||
}}'''.format(k[:-1], size - 1, to_enum_hd(k))
|
||||
print '''\
|
||||
break;'''
|
||||
print '''\
|
||||
}
|
||||
break;'''
|
||||
print '''\
|
||||
}
|
||||
return -1;
|
||||
}'''
|
||||
|
||||
if __name__ == '__main__':
|
||||
gen_enum()
|
||||
print ''
|
||||
gen_index_header()
|
||||
10
help2rst.py
10
help2rst.py
@@ -8,7 +8,7 @@ import sys
|
||||
import re
|
||||
import argparse
|
||||
|
||||
arg_indent = ' ' * 21
|
||||
arg_indent = ' ' * 14
|
||||
|
||||
def help2man(infile):
|
||||
# We assume that first line is usage line like this:
|
||||
@@ -87,7 +87,7 @@ DESCRIPTION
|
||||
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.'.format(len(arg_indent)))
|
||||
sys.stderr.write('warning: argument description is not indented correctly. We need {} spaces as indentation.\n'.format(len(arg_indent)))
|
||||
print '{}'.format(format_arg_text(line[len(arg_indent):]))
|
||||
continue
|
||||
|
||||
@@ -96,8 +96,8 @@ DESCRIPTION
|
||||
in_arg = False
|
||||
|
||||
if line == 'Options:':
|
||||
print 'OPTIONS:'
|
||||
print '--------'
|
||||
print 'OPTIONS'
|
||||
print '-------'
|
||||
print ''
|
||||
continue
|
||||
|
||||
@@ -137,7 +137,7 @@ DESCRIPTION
|
||||
|
||||
if not line.startswith(' ') and line.endswith(':'):
|
||||
# subsection
|
||||
subsec = line.strip()
|
||||
subsec = line.strip()[:-1]
|
||||
print '{}'.format(subsec)
|
||||
print '{}'.format('~' * len(subsec))
|
||||
print ''
|
||||
|
||||
43
integration-tests/Makefile.am
Normal file
43
integration-tests/Makefile.am
Normal file
@@ -0,0 +1,43 @@
|
||||
# nghttp2 - HTTP/2 C Library
|
||||
|
||||
# Copyright (c) 2015 Tatsuhiro Tsujikawa
|
||||
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
EXTRA_DIST = \
|
||||
nghttpx_http1_test.go \
|
||||
nghttpx_http2_test.go \
|
||||
nghttpx_spdy_test.go \
|
||||
server_tester.go \
|
||||
server.key \
|
||||
server.crt \
|
||||
alt-server.key \
|
||||
alt-server.crt \
|
||||
setenv
|
||||
|
||||
.PHONY: itprep it
|
||||
|
||||
itprep:
|
||||
go get -d -v github.com/bradfitz/http2
|
||||
go get -d -v github.com/tatsuhiro-t/go-nghttp2
|
||||
go get -d -v golang.org/x/net/spdy
|
||||
|
||||
it:
|
||||
sh setenv go test -v
|
||||
21
integration-tests/alt-server.crt
Normal file
21
integration-tests/alt-server.crt
Normal file
@@ -0,0 +1,21 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDhzCCAm+gAwIBAgIJANfuEldiquMNMA0GCSqGSIb3DQEBCwUAMFoxCzAJBgNV
|
||||
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
|
||||
aWRnaXRzIFB0eSBMdGQxEzARBgNVBAMMCmFsdC1kb21haW4wHhcNMTUwMTI1MDYy
|
||||
NTQxWhcNMjUwMTIyMDYyNTQxWjBaMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29t
|
||||
ZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRMwEQYD
|
||||
VQQDDAphbHQtZG9tYWluMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
|
||||
0IwhDOGDipGrJQ9IoRSzPdkU/Ii4aJgGKHlXminym42X0VI3IW61RLvOHRlHVmVH
|
||||
JQjFuDo2x+y81t9NlDg3HGUbSpzOzpm6StiutB7c4hreT5G4r0YKya1ugiemN0+p
|
||||
qjIPJWm2jVnf448eZvUKRKEQ9W0MLZjiNjVGKrKlwo7fIlXg4N3+YixLYffAT1NV
|
||||
d1T6V5jzlbruj15gK2nGjMQ9D1h1t9vTbTxY+mtk72aX0Y64IE6pPBWLFSSH8ozU
|
||||
idDoL3AZwz2Jker+ALKK8CM4uho/RPpyW1C06HH+HLdH2MqEjDOROde/Nzxm668O
|
||||
gK/JWGIEyUqYiUXx0yhFxwIDAQABo1AwTjAdBgNVHQ4EFgQU/Y0GDN2uPjbyePcu
|
||||
95ZvYEK/gHIwHwYDVR0jBBgwFoAU/Y0GDN2uPjbyePcu95ZvYEK/gHIwDAYDVR0T
|
||||
BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAodD6LVCzL3wfsZ6TxTzf9TfgIdbj
|
||||
ilL3SEMT/xnfTXT3SLYScTRqQIAI29Y7dOLMq89p4hY2wmeUEhBUAz+y9G2JVr8o
|
||||
6EbxXrQpWgNJogELqoNnMdrDxB5RsmDDKEJ/rLjDfSkjWbK7B2PZsqVTDgjekCFw
|
||||
u6FqTIjn/O1O/L5tjwxwxjHmQod/maFCvXoDOVBuwdHnkp298tqlvsHfHO8m++Wj
|
||||
+XYB8plMIjpeTh9v4w9Jc4QZ59lK/3Tt4qaENeQrMEubKSY/Zen7L2bzhk+cChWT
|
||||
GSGz9uNXieoZaH79D0wnyZaSZ5Ds4ActMevnGg3iYXuzuFqx8Pungn74Vg==
|
||||
-----END CERTIFICATE-----
|
||||
28
integration-tests/alt-server.key
Normal file
28
integration-tests/alt-server.key
Normal file
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDQjCEM4YOKkasl
|
||||
D0ihFLM92RT8iLhomAYoeVeaKfKbjZfRUjchbrVEu84dGUdWZUclCMW4OjbH7LzW
|
||||
302UODccZRtKnM7OmbpK2K60HtziGt5PkbivRgrJrW6CJ6Y3T6mqMg8labaNWd/j
|
||||
jx5m9QpEoRD1bQwtmOI2NUYqsqXCjt8iVeDg3f5iLEth98BPU1V3VPpXmPOVuu6P
|
||||
XmAracaMxD0PWHW329NtPFj6a2TvZpfRjrggTqk8FYsVJIfyjNSJ0OgvcBnDPYmR
|
||||
6v4AsorwIzi6Gj9E+nJbULTocf4ct0fYyoSMM5E51783PGbrrw6Ar8lYYgTJSpiJ
|
||||
RfHTKEXHAgMBAAECggEBALTrjFSXY72YB+h7rN+JjMIwDIPUvF6I3HbKZhQpJf6K
|
||||
xNVkRM2tNHavku0tm/S4ohLf3F+pqRKiL2Udjjjy1+S7VgTRqpwTQ0lhV5aNW8SP
|
||||
2KMg4R61XfB+k+s4KHu9kYxEJ12mqydPe+r3o0FgfYryTDsOYk1AX6b1aqzqFOGF
|
||||
7GaqLALSbKU59tcJJ1SZNBbpIKFUrAT9nZt9dW02/foqP5bzUk43Yjw48xmLwegc
|
||||
bMXXcpZhNZSktltvwRw7Q4Foc9kuRlMdTAnAD9PnMCcZwicS/YeVVF6Rz4fGviKv
|
||||
7/kPHQ7g4YpFktVDzuZ5xw6GDVFeJ6uGMVUX8+EePvkCgYEA+/nrcn82nFHCxm8Q
|
||||
0iiUhi/AoXjZg+O5Ytaje9O/YNoX+c4ywe13h0+TXKH79O0KfTwXeJyDgPZbAIFV
|
||||
9oURellRYUzKDafnBHis2f+Ywn6GqHL5e2X30ZxIp1GK46pcvne1YuvJhgGmiVay
|
||||
vd7sRx09OKU124dG22rIFCis6asCgYEA0+CsA6LrEwQ/aPJYASY3VHNO/WoAOnPg
|
||||
Cwsg+02XWsPEwP//lNmpanz8TUm2URS063ZK8bx7t3ejvDgBdsRwwjiMlDp7XTUU
|
||||
3Zk+mhCV2qkMi02aKemvz29bDhmh5JoH7W3IwsXtJYO0yZDYrDR3ioiKRccioPoE
|
||||
b/Nq781sEFUCgYEA4xqx9xRpaCLY5nicNI6WrwrDF8YQZisNn+PMnYKP7v8itOgA
|
||||
H4GkRbSXINpueKZc2dsbXH3UmJtyEdaAYBw3UIrIKmZHhl9afFE3mZQhXssjGxfl
|
||||
fC6/WZD+eq+n+uJFjPXf6jSSAdHjA828dB1D4CSeVTuyexZF6uUnR+QRVNkCgYEA
|
||||
i+pb7XLSpZYygY03zFp+Q0h6KyKqz+7hTqmkuA8/GfMZpRHop1UtaWLsAeXhfZ2c
|
||||
87kEOKptUHSzLYIWhWWnyLorK1+LQ7vf8Y5XJso5C1KDNCKk4XSuYt94U9FddWa6
|
||||
QXI0F1s5BYL6Cfma++0R2+va08Vy+rbf40XtojoXWJkCgYEA0hMQSCvok7is27nQ
|
||||
G80KXfmghU2eEB7zif3T00/fwJycxEbmnNeof+SKmhdY4ZgqTscfOxlQPflV/eqB
|
||||
xs4GnFDDeM0F8KH0BimOXxr7sJPFCg22PCCQQcRtM/KoU+ip/kNmTfwrsC0xMFPU
|
||||
HD8M1JCZF2eLMekXXP3cB0U4sUs=
|
||||
-----END PRIVATE KEY-----
|
||||
5
integration-tests/config.go.in
Normal file
5
integration-tests/config.go.in
Normal file
@@ -0,0 +1,5 @@
|
||||
package nghttp2
|
||||
|
||||
const (
|
||||
buildDir = "@top_builddir@"
|
||||
)
|
||||
440
integration-tests/nghttpx_http1_test.go
Normal file
440
integration-tests/nghttpx_http1_test.go
Normal file
@@ -0,0 +1,440 @@
|
||||
package nghttp2
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"github.com/bradfitz/http2/hpack"
|
||||
"io"
|
||||
"net/http"
|
||||
"syscall"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestH1H1PlainGET tests whether simple HTTP/1 GET request works.
|
||||
func TestH1H1PlainGET(t *testing.T) {
|
||||
st := newServerTester(nil, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http1(requestParam{
|
||||
name: "TestH1H1PlainGET",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http1() = %v", err)
|
||||
}
|
||||
|
||||
want := 200
|
||||
if got := res.status; got != want {
|
||||
t.Errorf("status = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H1PlainGETClose tests whether simple HTTP/1 GET request with
|
||||
// Connetion: close request header field works.
|
||||
func TestH1H1PlainGETClose(t *testing.T) {
|
||||
st := newServerTester(nil, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http1(requestParam{
|
||||
name: "TestH1H1PlainGETClose",
|
||||
header: []hpack.HeaderField{
|
||||
pair("Connection", "close"),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http1() = %v", err)
|
||||
}
|
||||
|
||||
want := 200
|
||||
if got := res.status; got != want {
|
||||
t.Errorf("status = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H1MultipleRequestCL tests that server rejects request which
|
||||
// contains multiple Content-Length header fields.
|
||||
func TestH1H1MultipleRequestCL(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Errorf("server should not forward bad request")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
if _, err := io.WriteString(st.conn, fmt.Sprintf(`GET / HTTP/1.1
|
||||
Host: %v
|
||||
Test-Case: TestH1H1MultipleRequestCL
|
||||
Content-Length: 0
|
||||
Content-Length: 0
|
||||
|
||||
`, st.authority)); err != nil {
|
||||
t.Fatalf("Error io.WriteString() = %v", err)
|
||||
}
|
||||
|
||||
resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Error http.ReadResponse() = %v", err)
|
||||
}
|
||||
|
||||
want := 400
|
||||
if got := resp.StatusCode; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H1ConnectFailure tests that server handles the situation that
|
||||
// connection attempt to HTTP/1 backend failed.
|
||||
func TestH1H1ConnectFailure(t *testing.T) {
|
||||
st := newServerTester(nil, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
// shutdown backend server to simulate backend connect failure
|
||||
st.ts.Close()
|
||||
|
||||
res, err := st.http1(requestParam{
|
||||
name: "TestH1H1ConnectFailure",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http1() = %v", err)
|
||||
}
|
||||
want := 503
|
||||
if got := res.status; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H1GracefulShutdown tests graceful shutdown.
|
||||
func TestH1H1GracefulShutdown(t *testing.T) {
|
||||
st := newServerTester(nil, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http1(requestParam{
|
||||
name: "TestH1H1GracefulShutdown-1",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http1() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
|
||||
st.cmd.Process.Signal(syscall.SIGQUIT)
|
||||
|
||||
res, err = st.http1(requestParam{
|
||||
name: "TestH1H1GracefulShutdown-2",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http1() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
|
||||
if got, want := res.connClose, true; got != want {
|
||||
t.Errorf("res.connClose: %v; want %v", got, want)
|
||||
}
|
||||
|
||||
want := io.EOF
|
||||
if _, err := st.conn.Read(nil); err == nil || err != want {
|
||||
t.Errorf("st.conn.Read(): %v; want %v", err, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H1HostRewrite tests that server rewrites Host header field
|
||||
func TestH1H1HostRewrite(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("request-host", r.Host)
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http1(requestParam{
|
||||
name: "TestH1H1HostRewrite",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http1() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
if got, want := res.header.Get("request-host"), st.backendHost; got != want {
|
||||
t.Errorf("request-host: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H1HTTP10 tests that server can accept HTTP/1.0 request
|
||||
// without Host header field
|
||||
func TestH1H1HTTP10(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("request-host", r.Host)
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H1HTTP10\r\n\r\n"); err != nil {
|
||||
t.Fatalf("Error io.WriteString() = %v", err)
|
||||
}
|
||||
|
||||
resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Error http.ReadResponse() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := resp.StatusCode, 200; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
if got, want := resp.Header.Get("request-host"), st.backendHost; got != want {
|
||||
t.Errorf("request-host: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H1HTTP10NoHostRewrite tests that server generates host header
|
||||
// field using actual backend server even if --no-http-rewrite is
|
||||
// used.
|
||||
func TestH1H1HTTP10NoHostRewrite(t *testing.T) {
|
||||
st := newServerTester([]string{"--no-host-rewrite"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("request-host", r.Host)
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H1HTTP10NoHostRewrite\r\n\r\n"); err != nil {
|
||||
t.Fatalf("Error io.WriteString() = %v", err)
|
||||
}
|
||||
|
||||
resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Error http.ReadResponse() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := resp.StatusCode, 200; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
if got, want := resp.Header.Get("request-host"), st.backendHost; got != want {
|
||||
t.Errorf("request-host: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H1RequestTrailer tests request trailer part is forwarded to
|
||||
// backend.
|
||||
func TestH1H1RequestTrailer(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
buf := make([]byte, 4096)
|
||||
for {
|
||||
_, err := r.Body.Read(buf)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("r.Body.Read() = %v", err)
|
||||
}
|
||||
}
|
||||
if got, want := r.Trailer.Get("foo"), "bar"; got != want {
|
||||
t.Errorf("r.Trailer.Get(foo): %v; want %v", got, want)
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http1(requestParam{
|
||||
name: "TestH1H1RequestTrailer",
|
||||
body: []byte("1"),
|
||||
trailer: []hpack.HeaderField{
|
||||
pair("foo", "bar"),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http1() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("res.status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H2ConnectFailure tests that server handles the situation that
|
||||
// connection attempt to HTTP/2 backend failed.
|
||||
func TestH1H2ConnectFailure(t *testing.T) {
|
||||
st := newServerTester([]string{"--http2-bridge"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
// simulate backend connect attempt failure
|
||||
st.ts.Close()
|
||||
|
||||
res, err := st.http1(requestParam{
|
||||
name: "TestH1H2ConnectFailure",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http1() = %v", err)
|
||||
}
|
||||
want := 503
|
||||
if got := res.status; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H2NoHost tests that server rejects request without Host
|
||||
// header field for HTTP/2 backend.
|
||||
func TestH1H2NoHost(t *testing.T) {
|
||||
st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Errorf("server should not forward bad request")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
// without Host header field, we expect 400 response
|
||||
if _, err := io.WriteString(st.conn, "GET / HTTP/1.1\r\nTest-Case: TestH1H2NoHost\r\n\r\n"); err != nil {
|
||||
t.Fatalf("Error io.WriteString() = %v", err)
|
||||
}
|
||||
|
||||
resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Error http.ReadResponse() = %v", err)
|
||||
}
|
||||
|
||||
want := 400
|
||||
if got := resp.StatusCode; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H2HTTP10 tests that server can accept HTTP/1.0 request
|
||||
// without Host header field
|
||||
func TestH1H2HTTP10(t *testing.T) {
|
||||
st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("request-host", r.Host)
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H2HTTP10\r\n\r\n"); err != nil {
|
||||
t.Fatalf("Error io.WriteString() = %v", err)
|
||||
}
|
||||
|
||||
resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Error http.ReadResponse() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := resp.StatusCode, 200; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
if got, want := resp.Header.Get("request-host"), st.backendHost; got != want {
|
||||
t.Errorf("request-host: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H2HTTP10NoHostRewrite tests that server generates host header
|
||||
// field using actual backend server even if --no-http-rewrite is
|
||||
// used.
|
||||
func TestH1H2HTTP10NoHostRewrite(t *testing.T) {
|
||||
st := newServerTester([]string{"--http2-bridge", "--no-host-rewrite"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("request-host", r.Host)
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H2HTTP10NoHostRewrite\r\n\r\n"); err != nil {
|
||||
t.Fatalf("Error io.WriteString() = %v", err)
|
||||
}
|
||||
|
||||
resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Error http.ReadResponse() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := resp.StatusCode, 200; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
if got, want := resp.Header.Get("request-host"), st.backendHost; got != want {
|
||||
t.Errorf("request-host: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H2CrumbleCookie tests that Cookies are crumbled and assembled
|
||||
// when forwarding to HTTP/2 backend link. go-nghttp2 server
|
||||
// concatenates crumbled Cookies automatically, so this test is not
|
||||
// much effective now.
|
||||
func TestH1H2CrumbleCookie(t *testing.T) {
|
||||
st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if got, want := r.Header.Get("Cookie"), "alpha; bravo; charlie"; got != want {
|
||||
t.Errorf("Cookie: %v; want %v", got, want)
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http1(requestParam{
|
||||
name: "TestH1H2CrumbleCookie",
|
||||
header: []hpack.HeaderField{
|
||||
pair("Cookie", "alpha; bravo; charlie"),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http1() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H2GenerateVia tests that server generates Via header field to and
|
||||
// from backend server.
|
||||
func TestH1H2GenerateVia(t *testing.T) {
|
||||
st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if got, want := r.Header.Get("Via"), "1.1 nghttpx"; got != want {
|
||||
t.Errorf("Via: %v; want %v", got, want)
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http1(requestParam{
|
||||
name: "TestH1H2GenerateVia",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http1() = %v", err)
|
||||
}
|
||||
if got, want := res.header.Get("Via"), "2 nghttpx"; got != want {
|
||||
t.Errorf("Via: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H2AppendVia tests that server adds value to existing Via
|
||||
// header field to and from backend server.
|
||||
func TestH1H2AppendVia(t *testing.T) {
|
||||
st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if got, want := r.Header.Get("Via"), "foo, 1.1 nghttpx"; got != want {
|
||||
t.Errorf("Via: %v; want %v", got, want)
|
||||
}
|
||||
w.Header().Add("Via", "bar")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http1(requestParam{
|
||||
name: "TestH1H2AppendVia",
|
||||
header: []hpack.HeaderField{
|
||||
pair("via", "foo"),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http1() = %v", err)
|
||||
}
|
||||
if got, want := res.header.Get("Via"), "bar, 2 nghttpx"; got != want {
|
||||
t.Errorf("Via: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H2NoVia tests that server does not add value to existing Via
|
||||
// header field to and from backend server.
|
||||
func TestH1H2NoVia(t *testing.T) {
|
||||
st := newServerTester([]string{"--http2-bridge", "--no-via"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if got, want := r.Header.Get("Via"), "foo"; got != want {
|
||||
t.Errorf("Via: %v; want %v", got, want)
|
||||
}
|
||||
w.Header().Add("Via", "bar")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http1(requestParam{
|
||||
name: "TestH1H2NoVia",
|
||||
header: []hpack.HeaderField{
|
||||
pair("via", "foo"),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http1() = %v", err)
|
||||
}
|
||||
if got, want := res.header.Get("Via"), "bar"; got != want {
|
||||
t.Errorf("Via: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
737
integration-tests/nghttpx_http2_test.go
Normal file
737
integration-tests/nghttpx_http2_test.go
Normal file
@@ -0,0 +1,737 @@
|
||||
package nghttp2
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"github.com/bradfitz/http2"
|
||||
"github.com/bradfitz/http2/hpack"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"syscall"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestH2H1PlainGET tests whether simple HTTP/2 GET request works.
|
||||
func TestH2H1PlainGET(t *testing.T) {
|
||||
st := newServerTester(nil, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1PlainGET",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
|
||||
want := 200
|
||||
if res.status != want {
|
||||
t.Errorf("status = %v; want %v", res.status, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1AddXff tests that server generates X-Forwarded-For header
|
||||
// field when forwarding request to backend.
|
||||
func TestH2H1AddXff(t *testing.T) {
|
||||
st := newServerTester([]string{"--add-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
xff := r.Header.Get("X-Forwarded-For")
|
||||
want := "127.0.0.1"
|
||||
if xff != want {
|
||||
t.Errorf("X-Forwarded-For = %v; want %v", xff, want)
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
_, err := st.http2(requestParam{
|
||||
name: "TestH2H1AddXff",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1AddXff2 tests that server appends X-Forwarded-For header
|
||||
// field to existing one when forwarding request to backend.
|
||||
func TestH2H1AddXff2(t *testing.T) {
|
||||
st := newServerTester([]string{"--add-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
xff := r.Header.Get("X-Forwarded-For")
|
||||
want := "host, 127.0.0.1"
|
||||
if xff != want {
|
||||
t.Errorf("X-Forwarded-For = %v; want %v", xff, want)
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
_, err := st.http2(requestParam{
|
||||
name: "TestH2H1AddXff2",
|
||||
header: []hpack.HeaderField{
|
||||
pair("x-forwarded-for", "host"),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1StripXff tests that --strip-incoming-x-forwarded-for
|
||||
// option.
|
||||
func TestH2H1StripXff(t *testing.T) {
|
||||
st := newServerTester([]string{"--strip-incoming-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if xff, found := r.Header["X-Forwarded-For"]; found {
|
||||
t.Errorf("X-Forwarded-For = %v; want nothing", xff)
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
_, err := st.http2(requestParam{
|
||||
name: "TestH2H1StripXff1",
|
||||
header: []hpack.HeaderField{
|
||||
pair("x-forwarded-for", "host"),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1StripAddXff tests that --strip-incoming-x-forwarded-for and
|
||||
// --add-x-forwarded-for options.
|
||||
func TestH2H1StripAddXff(t *testing.T) {
|
||||
args := []string{
|
||||
"--strip-incoming-x-forwarded-for",
|
||||
"--add-x-forwarded-for",
|
||||
}
|
||||
st := newServerTester(args, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
xff := r.Header.Get("X-Forwarded-For")
|
||||
want := "127.0.0.1"
|
||||
if xff != want {
|
||||
t.Errorf("X-Forwarded-For = %v; want %v", xff, want)
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
_, err := st.http2(requestParam{
|
||||
name: "TestH2H1StripAddXff",
|
||||
header: []hpack.HeaderField{
|
||||
pair("x-forwarded-for", "host"),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1GenerateVia tests that server generates Via header field to and
|
||||
// from backend server.
|
||||
func TestH2H1GenerateVia(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if got, want := r.Header.Get("Via"), "2 nghttpx"; got != want {
|
||||
t.Errorf("Via: %v; want %v", got, want)
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1GenerateVia",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.header.Get("Via"), "1.1 nghttpx"; got != want {
|
||||
t.Errorf("Via: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1AppendVia tests that server adds value to existing Via
|
||||
// header field to and from backend server.
|
||||
func TestH2H1AppendVia(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if got, want := r.Header.Get("Via"), "foo, 2 nghttpx"; got != want {
|
||||
t.Errorf("Via: %v; want %v", got, want)
|
||||
}
|
||||
w.Header().Add("Via", "bar")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1AppendVia",
|
||||
header: []hpack.HeaderField{
|
||||
pair("via", "foo"),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.header.Get("Via"), "bar, 1.1 nghttpx"; got != want {
|
||||
t.Errorf("Via: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1NoVia tests that server does not add value to existing Via
|
||||
// header field to and from backend server.
|
||||
func TestH2H1NoVia(t *testing.T) {
|
||||
st := newServerTester([]string{"--no-via"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if got, want := r.Header.Get("Via"), "foo"; got != want {
|
||||
t.Errorf("Via: %v; want %v", got, want)
|
||||
}
|
||||
w.Header().Add("Via", "bar")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1NoVia",
|
||||
header: []hpack.HeaderField{
|
||||
pair("via", "foo"),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.header.Get("Via"), "bar"; got != want {
|
||||
t.Errorf("Via: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1HostRewrite tests that server rewrites host header field
|
||||
func TestH2H1HostRewrite(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("request-host", r.Host)
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1HostRewrite",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
if got, want := res.header.Get("request-host"), st.backendHost; got != want {
|
||||
t.Errorf("request-host: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1NoHostRewrite tests that server does not rewrite host
|
||||
// header field
|
||||
func TestH2H1NoHostRewrite(t *testing.T) {
|
||||
st := newServerTester([]string{"--no-host-rewrite"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("request-host", r.Host)
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1NoHostRewrite",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
if got, want := res.header.Get("request-host"), st.frontendHost; got != want {
|
||||
t.Errorf("request-host: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1BadRequestCL tests that server rejects request whose
|
||||
// content-length header field value does not match its request body
|
||||
// size.
|
||||
func TestH2H1BadRequestCL(t *testing.T) {
|
||||
st := newServerTester(nil, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
// we set content-length: 1024, but the actual request body is
|
||||
// 3 bytes.
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1BadRequestCL",
|
||||
method: "POST",
|
||||
header: []hpack.HeaderField{
|
||||
pair("content-length", "1024"),
|
||||
},
|
||||
body: []byte("foo"),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
|
||||
want := http2.ErrCodeProtocol
|
||||
if res.errCode != want {
|
||||
t.Errorf("res.errCode = %v; want %v", res.errCode, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1BadResponseCL tests that server returns error when
|
||||
// content-length response header field value does not match its
|
||||
// response body size.
|
||||
func TestH2H1BadResponseCL(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
// we set content-length: 1024, but only send 3 bytes.
|
||||
w.Header().Add("Content-Length", "1024")
|
||||
w.Write([]byte("foo"))
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1BadResponseCL",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
|
||||
want := http2.ErrCodeProtocol
|
||||
if res.errCode != want {
|
||||
t.Errorf("res.errCode = %v; want %v", res.errCode, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1LocationRewrite tests location header field rewriting
|
||||
// works.
|
||||
func TestH2H1LocationRewrite(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO we cannot get st.ts's port number here.. 8443
|
||||
// is just a place holder. We ignore it on rewrite.
|
||||
w.Header().Add("Location", "http://127.0.0.1:8443/p/q?a=b#fragment")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1LocationRewrite",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
|
||||
want := fmt.Sprintf("http://127.0.0.1:%v/p/q?a=b#fragment", serverPort)
|
||||
if got := res.header.Get("Location"); got != want {
|
||||
t.Errorf("Location: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1ChunkedRequestBody tests that chunked request body works.
|
||||
func TestH2H1ChunkedRequestBody(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
want := "[chunked]"
|
||||
if got := fmt.Sprint(r.TransferEncoding); got != want {
|
||||
t.Errorf("Transfer-Encoding: %v; want %v", got, want)
|
||||
}
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("Error reading r.body: %v", err)
|
||||
}
|
||||
want = "foo"
|
||||
if got := string(body); got != want {
|
||||
t.Errorf("body: %v; want %v", got, want)
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
_, err := st.http2(requestParam{
|
||||
name: "TestH2H1ChunkedRequestBody",
|
||||
method: "POST",
|
||||
body: []byte("foo"),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1MultipleRequestCL tests that server rejects request with
|
||||
// multiple Content-Length request header fields.
|
||||
func TestH2H1MultipleRequestCL(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Errorf("server should not forward bad request")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1MultipleRequestCL",
|
||||
header: []hpack.HeaderField{
|
||||
pair("content-length", "1"),
|
||||
pair("content-length", "1"),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.errCode, http2.ErrCodeProtocol; got != want {
|
||||
t.Errorf("res.errCode: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1InvalidRequestCL tests that server rejects request with
|
||||
// Content-Length which cannot be parsed as a number.
|
||||
func TestH2H1InvalidRequestCL(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Errorf("server should not forward bad request")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1InvalidRequestCL",
|
||||
header: []hpack.HeaderField{
|
||||
pair("content-length", ""),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.errCode, http2.ErrCodeProtocol; got != want {
|
||||
t.Errorf("res.errCode: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1ConnectFailure tests that server handles the situation that
|
||||
// connection attempt to HTTP/1 backend failed.
|
||||
func TestH2H1ConnectFailure(t *testing.T) {
|
||||
st := newServerTester(nil, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
// shutdown backend server to simulate backend connect failure
|
||||
st.ts.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1ConnectFailure",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
want := 503
|
||||
if got := res.status; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1AssembleCookies tests that crumbled cookies in HTTP/2
|
||||
// request is assembled into 1 when forwarding to HTTP/1 backend link.
|
||||
func TestH2H1AssembleCookies(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if got, want := r.Header.Get("Cookie"), "alpha; bravo; charlie"; got != want {
|
||||
t.Errorf("Cookie: %v; want %v", got, want)
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1AssembleCookies",
|
||||
header: []hpack.HeaderField{
|
||||
pair("cookie", "alpha"),
|
||||
pair("cookie", "bravo"),
|
||||
pair("cookie", "charlie"),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1TETrailers tests that server accepts TE request header
|
||||
// field if it has trailers only.
|
||||
func TestH2H1TETrailers(t *testing.T) {
|
||||
st := newServerTester(nil, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1TETrailers",
|
||||
header: []hpack.HeaderField{
|
||||
pair("te", "trailers"),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1TEGzip tests that server resets stream if TE request header
|
||||
// field contains gzip.
|
||||
func TestH2H1TEGzip(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Error("server should not forward bad request")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1TEGzip",
|
||||
header: []hpack.HeaderField{
|
||||
pair("te", "gzip"),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.errCode, http2.ErrCodeProtocol; got != want {
|
||||
t.Errorf("res.errCode = %v; want %v", res.errCode, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1SNI tests server's TLS SNI extension feature. It must
|
||||
// choose appropriate certificate depending on the indicated
|
||||
// server_name from client.
|
||||
func TestH2H1SNI(t *testing.T) {
|
||||
st := newServerTesterTLSConfig([]string{"--subcert=" + testDir + "/alt-server.key:" + testDir + "/alt-server.crt"}, t, noopHandler, &tls.Config{
|
||||
ServerName: "alt-domain",
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
tlsConn := st.conn.(*tls.Conn)
|
||||
connState := tlsConn.ConnectionState()
|
||||
cert := connState.PeerCertificates[0]
|
||||
|
||||
if got, want := cert.Subject.CommonName, "alt-domain"; got != want {
|
||||
t.Errorf("CommonName: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1ServerPush tests server push using Link header field from
|
||||
// backend server.
|
||||
func TestH2H1ServerPush(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
// only resources marked as rel=preload are pushed
|
||||
w.Header().Add("Link", "</css/main.css>; rel=preload, </foo>, </css/theme.css>; rel=preload")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1ServerPush",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("res.status: %v; want %v", got, want)
|
||||
}
|
||||
if got, want := len(res.pushResponse), 2; got != want {
|
||||
t.Fatalf("len(res.pushResponse): %v; want %v", got, want)
|
||||
}
|
||||
mainCSS := res.pushResponse[0]
|
||||
if got, want := mainCSS.status, 200; got != want {
|
||||
t.Errorf("mainCSS.status: %v; want %v", got, want)
|
||||
}
|
||||
themeCSS := res.pushResponse[1]
|
||||
if got, want := themeCSS.status, 200; got != want {
|
||||
t.Errorf("themeCSS.status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1RequestTrailer tests request trailer part is forwarded to
|
||||
// backend.
|
||||
func TestH2H1RequestTrailer(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
buf := make([]byte, 4096)
|
||||
for {
|
||||
_, err := r.Body.Read(buf)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("r.Body.Read() = %v", err)
|
||||
}
|
||||
}
|
||||
if got, want := r.Trailer.Get("foo"), "bar"; got != want {
|
||||
t.Errorf("r.Trailer.Get(foo): %v; want %v", got, want)
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1RequestTrailer",
|
||||
body: []byte("1"),
|
||||
trailer: []hpack.HeaderField{
|
||||
pair("foo", "bar"),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("res.status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1GracefulShutdown tests graceful shutdown.
|
||||
func TestH2H1GracefulShutdown(t *testing.T) {
|
||||
st := newServerTester(nil, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
fmt.Fprint(st.conn, "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")
|
||||
if err := st.fr.WriteSettings(); err != nil {
|
||||
t.Fatalf("st.fr.WriteSettings(): %v", err)
|
||||
}
|
||||
|
||||
header := []hpack.HeaderField{
|
||||
pair(":method", "GET"),
|
||||
pair(":scheme", "http"),
|
||||
pair(":authority", st.authority),
|
||||
pair(":path", "/"),
|
||||
}
|
||||
|
||||
for _, h := range header {
|
||||
_ = st.enc.WriteField(h)
|
||||
}
|
||||
|
||||
if err := st.fr.WriteHeaders(http2.HeadersFrameParam{
|
||||
StreamID: 1,
|
||||
EndStream: false,
|
||||
EndHeaders: true,
|
||||
BlockFragment: st.headerBlkBuf.Bytes(),
|
||||
}); err != nil {
|
||||
t.Fatalf("st.fr.WriteHeaders(): %v", err)
|
||||
}
|
||||
|
||||
// send SIGQUIT signal to nghttpx to perform graceful shutdown
|
||||
st.cmd.Process.Signal(syscall.SIGQUIT)
|
||||
|
||||
// after signal, finish request body
|
||||
if err := st.fr.WriteData(1, true, nil); err != nil {
|
||||
t.Fatalf("st.fr.WriteData(): %v", err)
|
||||
}
|
||||
|
||||
numGoAway := 0
|
||||
|
||||
for {
|
||||
fr, err := st.readFrame()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
want := 2
|
||||
if got := numGoAway; got != want {
|
||||
t.Fatalf("numGoAway: %v; want %v", got, want)
|
||||
}
|
||||
return
|
||||
}
|
||||
t.Fatalf("st.readFrame(): %v", err)
|
||||
}
|
||||
switch f := fr.(type) {
|
||||
case *http2.GoAwayFrame:
|
||||
numGoAway += 1
|
||||
want := http2.ErrCodeNo
|
||||
if got := f.ErrCode; got != want {
|
||||
t.Fatalf("f.ErrCode(%v): %v; want %v", numGoAway, got, want)
|
||||
}
|
||||
switch numGoAway {
|
||||
case 1:
|
||||
want := (uint32(1) << 31) - 1
|
||||
if got := f.LastStreamID; got != want {
|
||||
t.Fatalf("f.LastStreamID(%v): %v; want %v", numGoAway, got, want)
|
||||
}
|
||||
case 2:
|
||||
want := uint32(1)
|
||||
if got := f.LastStreamID; got != want {
|
||||
t.Fatalf("f.LastStreamID(%v): %v; want %v", numGoAway, got, want)
|
||||
}
|
||||
case 3:
|
||||
t.Fatalf("too many GOAWAYs received")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H2MultipleResponseCL tests that server returns error if
|
||||
// multiple Content-Length response header fields are received.
|
||||
func TestH2H2MultipleResponseCL(t *testing.T) {
|
||||
st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("content-length", "1")
|
||||
w.Header().Add("content-length", "1")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H2MultipleResponseCL",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.errCode, http2.ErrCodeInternal; got != want {
|
||||
t.Errorf("res.errCode: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H2InvalidResponseCL tests that server returns error if
|
||||
// Content-Length response header field value cannot be parsed as a
|
||||
// number.
|
||||
func TestH2H2InvalidResponseCL(t *testing.T) {
|
||||
st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("content-length", "")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H2InvalidResponseCL",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.errCode, http2.ErrCodeInternal; got != want {
|
||||
t.Errorf("res.errCode: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H2ConnectFailure tests that server handles the situation that
|
||||
// connection attempt to HTTP/2 backend failed.
|
||||
func TestH2H2ConnectFailure(t *testing.T) {
|
||||
st := newServerTester([]string{"--http2-bridge"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
// simulate backend connect attempt failure
|
||||
st.ts.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H2ConnectFailure",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
want := 503
|
||||
if got := res.status; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H2HostRewrite tests that server rewrites host header field
|
||||
func TestH2H2HostRewrite(t *testing.T) {
|
||||
st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("request-host", r.Host)
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H2HostRewrite",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
if got, want := res.header.Get("request-host"), st.backendHost; got != want {
|
||||
t.Errorf("request-host: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H2NoHostRewrite tests that server does not rewrite host
|
||||
// header field
|
||||
func TestH2H2NoHostRewrite(t *testing.T) {
|
||||
st := newServerTester([]string{"--http2-bridge", "--no-host-rewrite"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("request-host", r.Host)
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H2NoHostRewrite",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
if got, want := res.header.Get("request-host"), st.frontendHost; got != want {
|
||||
t.Errorf("request-host: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
192
integration-tests/nghttpx_spdy_test.go
Normal file
192
integration-tests/nghttpx_spdy_test.go
Normal file
@@ -0,0 +1,192 @@
|
||||
package nghttp2
|
||||
|
||||
import (
|
||||
"github.com/bradfitz/http2/hpack"
|
||||
"golang.org/x/net/spdy"
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestS3H1PlainGET tests whether simple SPDY GET request works.
|
||||
func TestS3H1PlainGET(t *testing.T) {
|
||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.spdy(requestParam{
|
||||
name: "TestS3H1PlainGET",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.spdy() = %v", err)
|
||||
}
|
||||
|
||||
want := 200
|
||||
if got := res.status; got != want {
|
||||
t.Errorf("status = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestS3H1BadRequestCL tests that server rejects request whose
|
||||
// content-length header field value does not match its request body
|
||||
// size.
|
||||
func TestS3H1BadRequestCL(t *testing.T) {
|
||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
// we set content-length: 1024, but the actual request body is
|
||||
// 3 bytes.
|
||||
res, err := st.spdy(requestParam{
|
||||
name: "TestS3H1BadRequestCL",
|
||||
method: "POST",
|
||||
header: []hpack.HeaderField{
|
||||
pair("content-length", "1024"),
|
||||
},
|
||||
body: []byte("foo"),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.spdy() = %v", err)
|
||||
}
|
||||
|
||||
want := spdy.ProtocolError
|
||||
if got := res.spdyRstErrCode; got != want {
|
||||
t.Errorf("res.spdyRstErrCode = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestS3H1MultipleRequestCL tests that server rejects request with
|
||||
// multiple Content-Length request header fields.
|
||||
func TestS3H1MultipleRequestCL(t *testing.T) {
|
||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Errorf("server should not forward bad request")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.spdy(requestParam{
|
||||
name: "TestS3H1MultipleRequestCL",
|
||||
header: []hpack.HeaderField{
|
||||
pair("content-length", "1"),
|
||||
pair("content-length", "1"),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.spdy() = %v", err)
|
||||
}
|
||||
want := 400
|
||||
if got := res.status; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestS3H1InvalidRequestCL tests that server rejects request with
|
||||
// Content-Length which cannot be parsed as a number.
|
||||
func TestS3H1InvalidRequestCL(t *testing.T) {
|
||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Errorf("server should not forward bad request")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.spdy(requestParam{
|
||||
name: "TestS3H1InvalidRequestCL",
|
||||
header: []hpack.HeaderField{
|
||||
pair("content-length", ""),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.spdy() = %v", err)
|
||||
}
|
||||
want := 400
|
||||
if got := res.status; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestS3H1GenerateVia tests that server generates Via header field to and
|
||||
// from backend server.
|
||||
func TestS3H1GenerateVia(t *testing.T) {
|
||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if got, want := r.Header.Get("Via"), "1.1 nghttpx"; got != want {
|
||||
t.Errorf("Via: %v; want %v", got, want)
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.spdy(requestParam{
|
||||
name: "TestS3H1GenerateVia",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.spdy() = %v", err)
|
||||
}
|
||||
if got, want := res.header.Get("Via"), "1.1 nghttpx"; got != want {
|
||||
t.Errorf("Via: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestS3H1AppendVia tests that server adds value to existing Via
|
||||
// header field to and from backend server.
|
||||
func TestS3H1AppendVia(t *testing.T) {
|
||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if got, want := r.Header.Get("Via"), "foo, 1.1 nghttpx"; got != want {
|
||||
t.Errorf("Via: %v; want %v", got, want)
|
||||
}
|
||||
w.Header().Add("Via", "bar")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.spdy(requestParam{
|
||||
name: "TestS3H1AppendVia",
|
||||
header: []hpack.HeaderField{
|
||||
pair("via", "foo"),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.spdy() = %v", err)
|
||||
}
|
||||
if got, want := res.header.Get("Via"), "bar, 1.1 nghttpx"; got != want {
|
||||
t.Errorf("Via: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestS3H1NoVia tests that server does not add value to existing Via
|
||||
// header field to and from backend server.
|
||||
func TestS3H1NoVia(t *testing.T) {
|
||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--no-via"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if got, want := r.Header.Get("Via"), "foo"; got != want {
|
||||
t.Errorf("Via: %v; want %v", got, want)
|
||||
}
|
||||
w.Header().Add("Via", "bar")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.spdy(requestParam{
|
||||
name: "TestS3H1NoVia",
|
||||
header: []hpack.HeaderField{
|
||||
pair("via", "foo"),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.spdy() = %v", err)
|
||||
}
|
||||
if got, want := res.header.Get("Via"), "bar"; got != want {
|
||||
t.Errorf("Via: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestS3H2ConnectFailure tests that server handles the situation that
|
||||
// connection attempt to HTTP/2 backend failed.
|
||||
func TestS3H2ConnectFailure(t *testing.T) {
|
||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--http2-bridge"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
// simulate backend connect attempt failure
|
||||
st.ts.Close()
|
||||
|
||||
res, err := st.spdy(requestParam{
|
||||
name: "TestS3H2ConnectFailure",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.spdy() = %v", err)
|
||||
}
|
||||
want := 503
|
||||
if got := res.status; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
21
integration-tests/server.crt
Normal file
21
integration-tests/server.crt
Normal file
@@ -0,0 +1,21 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDhTCCAm2gAwIBAgIJAOvIx8xIxgyOMA0GCSqGSIb3DQEBCwUAMFkxCzAJBgNV
|
||||
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
|
||||
aWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMMCTEyNy4wLjAuMTAeFw0xNTAxMjMxMjI0
|
||||
MjdaFw0yNTAxMjAxMjI0MjdaMFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21l
|
||||
LVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNV
|
||||
BAMMCTEyNy4wLjAuMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMuI
|
||||
QZRI/iBaxPTjTWGemt8tCEfzZWxuIW3hY/gIhwJDfH2SbourBh1s9vqcqhBq5vmo
|
||||
kdfVQXAnNLjIG1uhWmcHuNnKrE5hU82N6i9RsmuM5TQRvhsamHri4G+EXJMu9GqF
|
||||
Mso8g7MWpRSGKf+8gfjAVNwfCHFiu8oBcMmy3l54MFHgRLSveAMhiPB0e3Xlnpr5
|
||||
2bS/oGTx5ynwPgBpEn2FrpT4Z/aLCLzJ/ysgNH8BXEh7n/v7xM3vd5grqB039rd5
|
||||
JoxlWvp+4XpzKp5upaqmOcVUq4pDSFUQ3w6C+v33Z3OK6Qaon7GMxLv3Us3b7PZ3
|
||||
1CLoWJR2o3OSnUfO/gUCAwEAAaNQME4wHQYDVR0OBBYEFLc5JWPUUVx4GJesogMV
|
||||
w2Rz0L3yMB8GA1UdIwQYMBaAFLc5JWPUUVx4GJesogMVw2Rz0L3yMAwGA1UdEwQF
|
||||
MAMBAf8wDQYJKoZIhvcNAQELBQADggEBAAP/cJWpM+GEjmVYHFacKTdbXBMox2Xn
|
||||
QY2NLm00WPOGvKnO7czMFfX/pEmiq71kD45rLLfbaJP205QpxqiAIvhFhuq50Co7
|
||||
sTDtwcDTPLX9H7Ugjt4sTMPiwC14uVXFfoT/J46zMjXwP00qKyfszc2tkIgHfrTl
|
||||
h4M1hkdfmMximir/Ii7TdYYJ3oGS8tdcYb6D4DZwAljKmxF6iUOwFCUgpTmqDBT5
|
||||
irXY8D27DzuNN5Pg07rwAlwXLCzrJE10UtO4MmRVXwpzmoaRQD4/tna6bZzdetvs
|
||||
gPdGP6W1o0q85gullieMJWeKyQA/wasoE7fypn4pHAdTZm/vH+v7GHg=
|
||||
-----END CERTIFICATE-----
|
||||
28
integration-tests/server.key
Normal file
28
integration-tests/server.key
Normal file
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDLiEGUSP4gWsT0
|
||||
401hnprfLQhH82VsbiFt4WP4CIcCQ3x9km6LqwYdbPb6nKoQaub5qJHX1UFwJzS4
|
||||
yBtboVpnB7jZyqxOYVPNjeovUbJrjOU0Eb4bGph64uBvhFyTLvRqhTLKPIOzFqUU
|
||||
hin/vIH4wFTcHwhxYrvKAXDJst5eeDBR4ES0r3gDIYjwdHt15Z6a+dm0v6Bk8ecp
|
||||
8D4AaRJ9ha6U+Gf2iwi8yf8rIDR/AVxIe5/7+8TN73eYK6gdN/a3eSaMZVr6fuF6
|
||||
cyqebqWqpjnFVKuKQ0hVEN8Ogvr992dziukGqJ+xjMS791LN2+z2d9Qi6FiUdqNz
|
||||
kp1Hzv4FAgMBAAECggEACG26GYP0Ui6wHVwUZkiFLVzWDPS9bIIbDEvbMfhYbvWQ
|
||||
gDrCLTKF7E4I5FP8jvV+XzRl5cRFE3nsKwLObzr9XWrqcsp73DsXl1mbKx58/ws0
|
||||
qrVZZBHz4pLmrHeUxduZ75dYhRuAcLgtWe48awTJdR2x5fO7C8cE89afbxrjLpJE
|
||||
tVyiw6vVB0GfWTZodxtAFMTX1KVm4bTngXfg0NF1FBNHAX3Cm6t4YCE41hKSc0IQ
|
||||
Jr3C4e9uj8poze1B17k79bGB8HNMbbc8Ws0sdbxi5xnY+HUA/mYQrmGXo8sdqiYC
|
||||
EYCMqPm3iJrCmmpHukGf2Vt9k1aLlJ+lxOclSwFO+QKBgQDoRmoprfdmU20LyxYH
|
||||
eVeVqggqmhNohwnuhIvOAyrWGUkbDsssqx2Vv82z0WHAAkwEvQ984UzaYWCCL3m3
|
||||
+JzpF2dz6aKhXIaYnXBlk3STMGUCDT5ysPvsin9z/unzkffh3vrbDBARGFYWG18x
|
||||
eUyTDOVVeTZNHUJXGjRyiftCkwKBgQDgUkR6dHU4ciSt7Y0UkyAgtZ7POR41T05L
|
||||
bcxbjJeqm6qlj+oP9WUk7JxeSEFUbrMiROABLPPqTwmGo4xrDRx/e7WrqN6QBKC+
|
||||
Y8CfalrKRb0np60x7Mxx0kbmHp5cwv9QDKznKViOYSgKxFrOFZyMAEXQdZ3FvjXF
|
||||
OQWrw86kBwKBgQDXuxa9MWO3uUJtkqkaNfw/+FVvY/0kt09lJdxHci+l/IQmyl2w
|
||||
Vhm7TRK7sXvtfvSl7gblgMgFiC2/nGKbmR/7ag5e3R98aVhlhMywuvyp/GfEORLI
|
||||
KVNChfwMezVFUUx+j8BEFHcTuZuzGqcWZ0fUyER0V4k0pDlKdv9BZqBkWwKBgCdP
|
||||
o3qGQCilMDJex/OMGPxCd9M+4kFbZZAobMC6cbXPU+dxwgYL7i67XGfVZ8WBJNlj
|
||||
kpICK7irIzM6JBh6krzwlBTCIkbA2N6kopQNUl3SPOTfKKXwJp/nxs77HKuK7K09
|
||||
m2tjPoatFhRU9sjY1rdeMN3oTr7hp5CpfonsZaEvAoGAEPsZcDd4N9ap5bgaeDy9
|
||||
NOfLsIyaxT5k6moRIiy83QPihvCuECP16+r6M5tiSfgt/PtCimdjhRiqXzIHNRhh
|
||||
Nfsv13vUtZgt8cYXuTdI4a8feKI7Q4876ME8Qp3WM5/UNZWq6/sWCuZFqbXUhqM0
|
||||
mwNEi5Zddzf8VsSL2gCraQg=
|
||||
-----END PRIVATE KEY-----
|
||||
669
integration-tests/server_tester.go
Normal file
669
integration-tests/server_tester.go
Normal file
@@ -0,0 +1,669 @@
|
||||
package nghttp2
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/bradfitz/http2"
|
||||
"github.com/bradfitz/http2/hpack"
|
||||
"github.com/tatsuhiro-t/go-nghttp2"
|
||||
"golang.org/x/net/spdy"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os/exec"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
serverBin = buildDir + "/src/nghttpx"
|
||||
serverPort = 3009
|
||||
testDir = buildDir + "/integration-tests"
|
||||
)
|
||||
|
||||
func pair(name, value string) hpack.HeaderField {
|
||||
return hpack.HeaderField{
|
||||
Name: name,
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
|
||||
type serverTester struct {
|
||||
args []string // command-line arguments
|
||||
cmd *exec.Cmd // test frontend server process, which is test subject
|
||||
url string // test frontend server URL
|
||||
t *testing.T
|
||||
ts *httptest.Server // backend server
|
||||
frontendHost string // frontend server host
|
||||
backendHost string // backend server host
|
||||
conn net.Conn // connection to frontend server
|
||||
h2PrefaceSent bool // HTTP/2 preface was sent in conn
|
||||
nextStreamID uint32 // next stream ID
|
||||
fr *http2.Framer // HTTP/2 framer
|
||||
spdyFr *spdy.Framer // SPDY/3.1 framer
|
||||
headerBlkBuf bytes.Buffer // buffer to store encoded header block
|
||||
enc *hpack.Encoder // HTTP/2 HPACK encoder
|
||||
header http.Header // received header fields
|
||||
dec *hpack.Decoder // HTTP/2 HPACK decoder
|
||||
authority string // server's host:port
|
||||
frCh chan http2.Frame // used for incoming HTTP/2 frame
|
||||
spdyFrCh chan spdy.Frame // used for incoming SPDY frame
|
||||
errCh chan error
|
||||
}
|
||||
|
||||
// newServerTester creates test context for plain TCP frontend
|
||||
// connection.
|
||||
func newServerTester(args []string, t *testing.T, handler http.HandlerFunc) *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)
|
||||
}
|
||||
|
||||
// newServerTester creates test context for TLS frontend connection
|
||||
// with given clientConfig
|
||||
func newServerTesterTLSConfig(args []string, t *testing.T, handler http.HandlerFunc, clientConfig *tls.Config) *serverTester {
|
||||
return newServerTesterInternal(args, t, handler, true, clientConfig)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
ts := httptest.NewUnstartedServer(handler)
|
||||
|
||||
backendTLS := false
|
||||
for _, k := range args {
|
||||
switch k {
|
||||
case "--http2-bridge":
|
||||
backendTLS = true
|
||||
}
|
||||
}
|
||||
if backendTLS {
|
||||
nghttp2.ConfigureServer(ts.Config, &nghttp2.Server{})
|
||||
// According to httptest/server.go, we have to set
|
||||
// NextProtos separately for ts.TLS. NextProtos set
|
||||
// in nghttp2.ConfigureServer is effectively ignored.
|
||||
ts.TLS = new(tls.Config)
|
||||
ts.TLS.NextProtos = append(ts.TLS.NextProtos, "h2-14")
|
||||
ts.StartTLS()
|
||||
args = append(args, "-k")
|
||||
} else {
|
||||
ts.Start()
|
||||
}
|
||||
scheme := "http"
|
||||
if frontendTLS {
|
||||
scheme = "https"
|
||||
args = append(args, testDir+"/server.key", testDir+"/server.crt")
|
||||
} else {
|
||||
args = append(args, "--frontend-no-tls")
|
||||
}
|
||||
|
||||
backendURL, err := url.Parse(ts.URL)
|
||||
if err != nil {
|
||||
t.Fatalf("Error parsing URL from httptest.Server: %v", err)
|
||||
}
|
||||
|
||||
// URL.Host looks like "127.0.0.1:8080", but we want
|
||||
// "127.0.0.1,8080"
|
||||
b := "-b" + strings.Replace(backendURL.Host, ":", ",", -1)
|
||||
args = append(args, fmt.Sprintf("-f127.0.0.1,%v", serverPort), b,
|
||||
"--errorlog-file="+testDir+"/log.txt", "-LINFO")
|
||||
|
||||
authority := fmt.Sprintf("127.0.0.1:%v", serverPort)
|
||||
|
||||
st := &serverTester{
|
||||
cmd: exec.Command(serverBin, args...),
|
||||
t: t,
|
||||
ts: ts,
|
||||
url: fmt.Sprintf("%v://%v", scheme, authority),
|
||||
frontendHost: fmt.Sprintf("127.0.0.1:%v", serverPort),
|
||||
backendHost: backendURL.Host,
|
||||
nextStreamID: 1,
|
||||
authority: authority,
|
||||
frCh: make(chan http2.Frame),
|
||||
spdyFrCh: make(chan spdy.Frame),
|
||||
errCh: make(chan error),
|
||||
}
|
||||
|
||||
if err := st.cmd.Start(); err != nil {
|
||||
st.t.Fatalf("Error starting %v: %v", serverBin, err)
|
||||
}
|
||||
|
||||
retry := 0
|
||||
for {
|
||||
var conn net.Conn
|
||||
var err error
|
||||
if frontendTLS {
|
||||
var tlsConfig *tls.Config
|
||||
if clientConfig == nil {
|
||||
tlsConfig = new(tls.Config)
|
||||
} else {
|
||||
tlsConfig = clientConfig
|
||||
}
|
||||
tlsConfig.InsecureSkipVerify = true
|
||||
tlsConfig.NextProtos = []string{"h2-14", "spdy/3.1"}
|
||||
conn, err = tls.Dial("tcp", authority, tlsConfig)
|
||||
} else {
|
||||
conn, err = net.Dial("tcp", authority)
|
||||
}
|
||||
if err != nil {
|
||||
retry += 1
|
||||
if retry >= 100 {
|
||||
st.Close()
|
||||
st.t.Fatalf("Error server is not responding too long; server command-line arguments may be invalid")
|
||||
}
|
||||
time.Sleep(150 * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
if frontendTLS {
|
||||
tlsConn := conn.(*tls.Conn)
|
||||
cs := tlsConn.ConnectionState()
|
||||
if !cs.NegotiatedProtocolIsMutual {
|
||||
st.Close()
|
||||
st.t.Fatalf("Error negotiated next protocol is not mutual")
|
||||
}
|
||||
}
|
||||
st.conn = conn
|
||||
break
|
||||
}
|
||||
|
||||
st.fr = http2.NewFramer(st.conn, st.conn)
|
||||
spdyFr, err := spdy.NewFramer(st.conn, st.conn)
|
||||
if err != nil {
|
||||
st.Close()
|
||||
st.t.Fatalf("Error spdy.NewFramer: %v", err)
|
||||
}
|
||||
st.spdyFr = spdyFr
|
||||
st.enc = hpack.NewEncoder(&st.headerBlkBuf)
|
||||
st.dec = hpack.NewDecoder(4096, func(f hpack.HeaderField) {
|
||||
st.header.Add(f.Name, f.Value)
|
||||
})
|
||||
|
||||
return st
|
||||
}
|
||||
|
||||
func (st *serverTester) Close() {
|
||||
if st.conn != nil {
|
||||
st.conn.Close()
|
||||
}
|
||||
if st.cmd != nil {
|
||||
st.cmd.Process.Kill()
|
||||
st.cmd.Wait()
|
||||
}
|
||||
if st.ts != nil {
|
||||
st.ts.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (st *serverTester) readFrame() (http2.Frame, error) {
|
||||
go func() {
|
||||
f, err := st.fr.ReadFrame()
|
||||
if err != nil {
|
||||
st.errCh <- err
|
||||
return
|
||||
}
|
||||
st.frCh <- f
|
||||
}()
|
||||
|
||||
select {
|
||||
case f := <-st.frCh:
|
||||
return f, nil
|
||||
case err := <-st.errCh:
|
||||
return nil, err
|
||||
case <-time.After(5 * time.Second):
|
||||
return nil, errors.New("timeout waiting for frame")
|
||||
}
|
||||
}
|
||||
|
||||
func (st *serverTester) readSpdyFrame() (spdy.Frame, error) {
|
||||
go func() {
|
||||
f, err := st.spdyFr.ReadFrame()
|
||||
if err != nil {
|
||||
st.errCh <- err
|
||||
return
|
||||
}
|
||||
st.spdyFrCh <- f
|
||||
}()
|
||||
|
||||
select {
|
||||
case f := <-st.spdyFrCh:
|
||||
return f, nil
|
||||
case err := <-st.errCh:
|
||||
return nil, err
|
||||
case <-time.After(2 * time.Second):
|
||||
return nil, errors.New("timeout waiting for frame")
|
||||
}
|
||||
}
|
||||
|
||||
type requestParam struct {
|
||||
name string // name for this request to identify the request in log easily
|
||||
streamID uint32 // stream ID, automatically assigned if 0
|
||||
method string // method, defaults to GET
|
||||
scheme string // scheme, defaults to http
|
||||
authority string // authority, defaults to backend server address
|
||||
path string // path, defaults to /
|
||||
header []hpack.HeaderField // additional request header fields
|
||||
body []byte // request body
|
||||
trailer []hpack.HeaderField // trailer part
|
||||
}
|
||||
|
||||
// wrapper for request body to set trailer part
|
||||
type chunkedBodyReader struct {
|
||||
trailer []hpack.HeaderField
|
||||
trailerWritten bool
|
||||
body io.Reader
|
||||
req *http.Request
|
||||
}
|
||||
|
||||
func (cbr *chunkedBodyReader) Read(p []byte) (n int, err error) {
|
||||
// document says that we have to set http.Request.Trailer
|
||||
// after request was sent and before body returns EOF.
|
||||
if !cbr.trailerWritten {
|
||||
cbr.trailerWritten = true
|
||||
for _, h := range cbr.trailer {
|
||||
cbr.req.Trailer.Set(h.Name, h.Value)
|
||||
}
|
||||
}
|
||||
return cbr.body.Read(p)
|
||||
}
|
||||
|
||||
func (st *serverTester) http1(rp requestParam) (*serverResponse, error) {
|
||||
method := "GET"
|
||||
if rp.method != "" {
|
||||
method = rp.method
|
||||
}
|
||||
|
||||
var body io.Reader
|
||||
var cbr *chunkedBodyReader
|
||||
if rp.body != nil {
|
||||
body = bytes.NewBuffer(rp.body)
|
||||
if len(rp.trailer) != 0 {
|
||||
cbr = &chunkedBodyReader{
|
||||
trailer: rp.trailer,
|
||||
body: body,
|
||||
}
|
||||
body = cbr
|
||||
}
|
||||
}
|
||||
req, err := http.NewRequest(method, st.url, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, h := range rp.header {
|
||||
req.Header.Add(h.Name, h.Value)
|
||||
}
|
||||
req.Header.Add("Test-Case", rp.name)
|
||||
if cbr != nil {
|
||||
cbr.req = req
|
||||
// this makes request use chunked encoding
|
||||
req.ContentLength = -1
|
||||
req.Trailer = make(http.Header)
|
||||
for _, h := range cbr.trailer {
|
||||
req.Trailer.Set(h.Name, "")
|
||||
}
|
||||
}
|
||||
if err := req.Write(st.conn); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp, err := http.ReadResponse(bufio.NewReader(st.conn), req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
respBody, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
res := &serverResponse{
|
||||
status: resp.StatusCode,
|
||||
header: resp.Header,
|
||||
body: respBody,
|
||||
connClose: resp.Close,
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (st *serverTester) spdy(rp requestParam) (*serverResponse, error) {
|
||||
res := &serverResponse{}
|
||||
|
||||
var id spdy.StreamId
|
||||
if rp.streamID != 0 {
|
||||
id = spdy.StreamId(rp.streamID)
|
||||
if id >= spdy.StreamId(st.nextStreamID) && id%2 == 1 {
|
||||
st.nextStreamID = uint32(id) + 2
|
||||
}
|
||||
} else {
|
||||
id = spdy.StreamId(st.nextStreamID)
|
||||
st.nextStreamID += 2
|
||||
}
|
||||
|
||||
method := "GET"
|
||||
if rp.method != "" {
|
||||
method = rp.method
|
||||
}
|
||||
|
||||
scheme := "http"
|
||||
if rp.scheme != "" {
|
||||
scheme = rp.scheme
|
||||
}
|
||||
|
||||
host := st.authority
|
||||
if rp.authority != "" {
|
||||
host = rp.authority
|
||||
}
|
||||
|
||||
path := "/"
|
||||
if rp.path != "" {
|
||||
path = rp.path
|
||||
}
|
||||
|
||||
header := make(http.Header)
|
||||
header.Add(":method", method)
|
||||
header.Add(":scheme", scheme)
|
||||
header.Add(":host", host)
|
||||
header.Add(":path", path)
|
||||
header.Add(":version", "HTTP/1.1")
|
||||
header.Add("test-case", rp.name)
|
||||
for _, h := range rp.header {
|
||||
header.Add(h.Name, h.Value)
|
||||
}
|
||||
|
||||
var synStreamFlags spdy.ControlFlags
|
||||
if len(rp.body) == 0 {
|
||||
synStreamFlags = spdy.ControlFlagFin
|
||||
}
|
||||
if err := st.spdyFr.WriteFrame(&spdy.SynStreamFrame{
|
||||
CFHeader: spdy.ControlFrameHeader{
|
||||
Flags: synStreamFlags,
|
||||
},
|
||||
StreamId: id,
|
||||
Headers: header,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(rp.body) != 0 {
|
||||
if err := st.spdyFr.WriteFrame(&spdy.DataFrame{
|
||||
StreamId: id,
|
||||
Flags: spdy.DataFlagFin,
|
||||
Data: rp.body,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
loop:
|
||||
for {
|
||||
fr, err := st.readSpdyFrame()
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
switch f := fr.(type) {
|
||||
case *spdy.SynReplyFrame:
|
||||
if f.StreamId != id {
|
||||
break
|
||||
}
|
||||
res.header = cloneHeader(f.Headers)
|
||||
if _, err := fmt.Sscan(res.header.Get(":status"), &res.status); err != nil {
|
||||
return res, fmt.Errorf("Error parsing status code: %v", err)
|
||||
}
|
||||
if f.CFHeader.Flags&spdy.ControlFlagFin != 0 {
|
||||
break loop
|
||||
}
|
||||
case *spdy.DataFrame:
|
||||
if f.StreamId != id {
|
||||
break
|
||||
}
|
||||
res.body = append(res.body, f.Data...)
|
||||
if f.Flags&spdy.DataFlagFin != 0 {
|
||||
break loop
|
||||
}
|
||||
case *spdy.RstStreamFrame:
|
||||
if f.StreamId != id {
|
||||
break
|
||||
}
|
||||
res.spdyRstErrCode = f.Status
|
||||
break loop
|
||||
case *spdy.GoAwayFrame:
|
||||
if f.Status == spdy.GoAwayOK {
|
||||
break
|
||||
}
|
||||
res.spdyGoAwayErrCode = f.Status
|
||||
break loop
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (st *serverTester) http2(rp requestParam) (*serverResponse, error) {
|
||||
st.headerBlkBuf.Reset()
|
||||
st.header = make(http.Header)
|
||||
|
||||
var id uint32
|
||||
if rp.streamID != 0 {
|
||||
id = rp.streamID
|
||||
if id >= st.nextStreamID && id%2 == 1 {
|
||||
st.nextStreamID = id + 2
|
||||
}
|
||||
} else {
|
||||
id = st.nextStreamID
|
||||
st.nextStreamID += 2
|
||||
}
|
||||
|
||||
if !st.h2PrefaceSent {
|
||||
st.h2PrefaceSent = true
|
||||
fmt.Fprint(st.conn, "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")
|
||||
if err := st.fr.WriteSettings(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
res := &serverResponse{
|
||||
streamID: id,
|
||||
}
|
||||
|
||||
streams := make(map[uint32]*serverResponse)
|
||||
streams[id] = res
|
||||
|
||||
method := "GET"
|
||||
if rp.method != "" {
|
||||
method = rp.method
|
||||
}
|
||||
_ = st.enc.WriteField(pair(":method", method))
|
||||
|
||||
scheme := "http"
|
||||
if rp.scheme != "" {
|
||||
scheme = rp.scheme
|
||||
}
|
||||
_ = st.enc.WriteField(pair(":scheme", scheme))
|
||||
|
||||
authority := st.authority
|
||||
if rp.authority != "" {
|
||||
authority = rp.authority
|
||||
}
|
||||
_ = st.enc.WriteField(pair(":authority", authority))
|
||||
|
||||
path := "/"
|
||||
if rp.path != "" {
|
||||
path = rp.path
|
||||
}
|
||||
_ = st.enc.WriteField(pair(":path", path))
|
||||
|
||||
_ = st.enc.WriteField(pair("test-case", rp.name))
|
||||
|
||||
for _, h := range rp.header {
|
||||
_ = st.enc.WriteField(h)
|
||||
}
|
||||
|
||||
err := st.fr.WriteHeaders(http2.HeadersFrameParam{
|
||||
StreamID: id,
|
||||
EndStream: len(rp.body) == 0 && len(rp.trailer) == 0,
|
||||
EndHeaders: true,
|
||||
BlockFragment: st.headerBlkBuf.Bytes(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(rp.body) != 0 {
|
||||
// TODO we assume rp.body fits in 1 frame
|
||||
if err := st.fr.WriteData(id, len(rp.trailer) == 0, rp.body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if len(rp.trailer) != 0 {
|
||||
st.headerBlkBuf.Reset()
|
||||
for _, h := range rp.trailer {
|
||||
_ = st.enc.WriteField(h)
|
||||
}
|
||||
err := st.fr.WriteHeaders(http2.HeadersFrameParam{
|
||||
StreamID: id,
|
||||
EndStream: true,
|
||||
EndHeaders: true,
|
||||
BlockFragment: st.headerBlkBuf.Bytes(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
loop:
|
||||
for {
|
||||
fr, err := st.readFrame()
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
switch f := fr.(type) {
|
||||
case *http2.HeadersFrame:
|
||||
_, err := st.dec.Write(f.HeaderBlockFragment())
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
sr, ok := streams[f.FrameHeader.StreamID]
|
||||
if !ok {
|
||||
st.header = make(http.Header)
|
||||
break
|
||||
}
|
||||
sr.header = cloneHeader(st.header)
|
||||
var status int
|
||||
status, err = strconv.Atoi(sr.header.Get(":status"))
|
||||
if err != nil {
|
||||
return res, fmt.Errorf("Error parsing status code: %v", err)
|
||||
}
|
||||
sr.status = status
|
||||
if f.StreamEnded() {
|
||||
if streamEnded(res, streams, sr) {
|
||||
break loop
|
||||
}
|
||||
}
|
||||
case *http2.PushPromiseFrame:
|
||||
_, err := st.dec.Write(f.HeaderBlockFragment())
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
sr := &serverResponse{
|
||||
streamID: f.PromiseID,
|
||||
reqHeader: cloneHeader(st.header),
|
||||
}
|
||||
streams[sr.streamID] = sr
|
||||
case *http2.DataFrame:
|
||||
sr, ok := streams[f.FrameHeader.StreamID]
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
sr.body = append(sr.body, f.Data()...)
|
||||
if f.StreamEnded() {
|
||||
if streamEnded(res, streams, sr) {
|
||||
break loop
|
||||
}
|
||||
}
|
||||
case *http2.RSTStreamFrame:
|
||||
sr, ok := streams[f.FrameHeader.StreamID]
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
sr.errCode = f.ErrCode
|
||||
if streamEnded(res, streams, sr) {
|
||||
break loop
|
||||
}
|
||||
case *http2.GoAwayFrame:
|
||||
if f.ErrCode == http2.ErrCodeNo {
|
||||
break
|
||||
}
|
||||
res.errCode = f.ErrCode
|
||||
res.connErr = true
|
||||
break loop
|
||||
case *http2.SettingsFrame:
|
||||
if f.IsAck() {
|
||||
break
|
||||
}
|
||||
if err := st.fr.WriteSettingsAck(); err != nil {
|
||||
return res, err
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Sort(ByStreamID(res.pushResponse))
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func streamEnded(mainSr *serverResponse, streams map[uint32]*serverResponse, sr *serverResponse) bool {
|
||||
delete(streams, sr.streamID)
|
||||
if mainSr.streamID != sr.streamID {
|
||||
mainSr.pushResponse = append(mainSr.pushResponse, sr)
|
||||
}
|
||||
return len(streams) == 0
|
||||
}
|
||||
|
||||
type serverResponse struct {
|
||||
status int // HTTP status code
|
||||
header http.Header // response header fields
|
||||
body []byte // response body
|
||||
streamID uint32 // stream ID in HTTP/2
|
||||
errCode http2.ErrCode // error code received in HTTP/2 RST_STREAM or GOAWAY
|
||||
connErr bool // true if HTTP/2 connection error
|
||||
spdyGoAwayErrCode spdy.GoAwayStatus // status code received in SPDY RST_STREAM
|
||||
spdyRstErrCode spdy.RstStreamStatus // status code received in SPDY GOAWAY
|
||||
connClose bool // Conection: close is included in response header in HTTP/1 test
|
||||
reqHeader http.Header // http request header, currently only sotres pushed request header
|
||||
pushResponse []*serverResponse // pushed response
|
||||
}
|
||||
|
||||
type ByStreamID []*serverResponse
|
||||
|
||||
func (b ByStreamID) Len() int {
|
||||
return len(b)
|
||||
}
|
||||
|
||||
func (b ByStreamID) Swap(i, j int) {
|
||||
b[i], b[j] = b[j], b[i]
|
||||
}
|
||||
|
||||
func (b ByStreamID) Less(i, j int) bool {
|
||||
return b[i].streamID < b[j].streamID
|
||||
}
|
||||
|
||||
func cloneHeader(h http.Header) http.Header {
|
||||
h2 := make(http.Header, len(h))
|
||||
for k, vv := range h {
|
||||
vv2 := make([]string, len(vv))
|
||||
copy(vv2, vv)
|
||||
h2[k] = vv2
|
||||
}
|
||||
return h2
|
||||
}
|
||||
|
||||
func noopHandler(w http.ResponseWriter, r *http.Request) {}
|
||||
6
integration-tests/setenv.in
Normal file
6
integration-tests/setenv.in
Normal file
@@ -0,0 +1,6 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
export CGO_CFLAGS="-I@abs_top_srcdir@/lib/includes -I@abs_top_builddir@/lib/includes"
|
||||
export CGO_LDFLAGS="-L@abs_top_builddir@/lib/.libs"
|
||||
export LD_LIBRARY_PATH="@abs_top_builddir@/lib/.libs"
|
||||
"$@"
|
||||
@@ -45,7 +45,8 @@ OBJECTS = nghttp2_pq.c nghttp2_map.c nghttp2_queue.c \
|
||||
nghttp2_priority_spec.c \
|
||||
nghttp2_option.c \
|
||||
nghttp2_callbacks.c \
|
||||
nghttp2_mem.c
|
||||
nghttp2_mem.c \
|
||||
nghttp2_http.c
|
||||
|
||||
HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \
|
||||
nghttp2_frame.h \
|
||||
@@ -58,7 +59,8 @@ HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \
|
||||
nghttp2_priority_spec.h \
|
||||
nghttp2_option.h \
|
||||
nghttp2_callbacks.h \
|
||||
nghttp2_mem.h
|
||||
nghttp2_mem.h \
|
||||
nghttp2_http.h
|
||||
|
||||
libnghttp2_la_SOURCES = $(HFILES) $(OBJECTS)
|
||||
libnghttp2_la_LDFLAGS = -no-undefined \
|
||||
|
||||
@@ -49,6 +49,24 @@ extern "C" {
|
||||
*/
|
||||
#define NGHTTP2_PROTO_VERSION_ID_LEN 5
|
||||
|
||||
/**
|
||||
* @macro
|
||||
*
|
||||
* The seriazlied form of ALPN protocol identifier this library
|
||||
* supports. Notice that first byte is the length of following
|
||||
* protocol identifier. This is the same wire format of `TLS ALPN
|
||||
* extension <https://tools.ietf.org/html/rfc7301>`_. This is useful
|
||||
* to process incoming ALPN tokens in wire format.
|
||||
*/
|
||||
#define NGHTTP2_PROTO_ALPN "\x5h2-14"
|
||||
|
||||
/**
|
||||
* @macro
|
||||
*
|
||||
* The length of :macro:`NGHTTP2_PROTO_ALPN`.
|
||||
*/
|
||||
#define NGHTTP2_PROTO_ALPN_LEN (sizeof(NGHTTP2_PROTO_ALPN) - 1)
|
||||
|
||||
/**
|
||||
* @macro
|
||||
*
|
||||
@@ -201,7 +219,7 @@ typedef enum {
|
||||
*/
|
||||
NGHTTP2_ERR_INVALID_ARGUMENT = -501,
|
||||
/**
|
||||
* Ouf of buffer space.
|
||||
* Out of buffer space.
|
||||
*/
|
||||
NGHTTP2_ERR_BUFFER_ERROR = -502,
|
||||
/**
|
||||
@@ -325,6 +343,16 @@ typedef enum {
|
||||
* not been fully processed yet.
|
||||
*/
|
||||
NGHTTP2_ERR_DATA_EXIST = -529,
|
||||
/**
|
||||
* The current session is closing due to a connection error or
|
||||
* `nghttp2_session_terminate_session()` is called.
|
||||
*/
|
||||
NGHTTP2_ERR_SESSION_CLOSING = -530,
|
||||
/**
|
||||
* Invalid HTTP header field was received and stream is going to be
|
||||
* closed.
|
||||
*/
|
||||
NGHTTP2_ERR_HTTP_HEADER = -531,
|
||||
/**
|
||||
* The errors < :enum:`NGHTTP2_ERR_FATAL` mean that the library is
|
||||
* under unexpected condition and processing was terminated (e.g.,
|
||||
@@ -360,7 +388,9 @@ typedef enum {
|
||||
*/
|
||||
NGHTTP2_NV_FLAG_NONE = 0,
|
||||
/**
|
||||
* Indicates that this name/value pair must not be indexed.
|
||||
* Indicates that this name/value pair must not be indexed ("Literal
|
||||
* Header Field never Indexed" representation must be used in HPACK
|
||||
* encoding). Other implementation calls this bit as "sensitive".
|
||||
*/
|
||||
NGHTTP2_NV_FLAG_NO_INDEX = 0x01
|
||||
} nghttp2_nv_flag;
|
||||
@@ -438,7 +468,9 @@ typedef enum {
|
||||
*/
|
||||
NGHTTP2_WINDOW_UPDATE = 0x08,
|
||||
/**
|
||||
* The CONTINUATION frame.
|
||||
* The CONTINUATION frame. This frame type won't be passed to any
|
||||
* callbacks because the library processes this frame type and its
|
||||
* preceding HEADERS/PUSH_PROMISE as a single frame.
|
||||
*/
|
||||
NGHTTP2_CONTINUATION = 0x09
|
||||
} nghttp2_frame_type;
|
||||
@@ -652,7 +684,14 @@ typedef enum {
|
||||
/**
|
||||
* Indicates EOF was sensed.
|
||||
*/
|
||||
NGHTTP2_DATA_FLAG_EOF = 0x01
|
||||
NGHTTP2_DATA_FLAG_EOF = 0x01,
|
||||
/**
|
||||
* Indicates that END_STREAM flag must not be set even if
|
||||
* NGHTTP2_DATA_FLAG_EOF is set. Usually this flag is used to send
|
||||
* trailer header fields with `nghttp2_submit_request()` or
|
||||
* `nghttp2_submit_response()`.
|
||||
*/
|
||||
NGHTTP2_DATA_FLAG_NO_END_STREAM = 0x02
|
||||
} nghttp2_data_flag;
|
||||
|
||||
/**
|
||||
@@ -665,6 +704,21 @@ typedef enum {
|
||||
* them in |buf| and return number of data stored in |buf|. If EOF is
|
||||
* reached, set :enum:`NGHTTP2_DATA_FLAG_EOF` flag in |*data_flags|.
|
||||
*
|
||||
* If this callback is set by `nghttp2_submit_request()`,
|
||||
* `nghttp2_submit_response()` or `nghttp2_submit_headers()` and
|
||||
* `nghttp2_submit_data()` with flag parameter
|
||||
* :enum:`NGHTTP2_FLAG_END_STREAM` set, and
|
||||
* :enum:`NGHTTP2_DATA_FLAG_EOF` flag is set to |*data_flags|, DATA
|
||||
* frame will have END_STREAM flag set. Usually, this is expected
|
||||
* behaviour and all are fine. One exception is send trailer header
|
||||
* fields. You cannot send trailers after sending frame with
|
||||
* END_STREAM set. To avoid this problem, one can set
|
||||
* :enum:`NGHTTP2_DATA_FLAG_NO_END_STREAM` along with
|
||||
* :enum:`NGHTTP2_DATA_FLAG_EOF` to signal the library not to set
|
||||
* END_STREAM in DATA frame. Then application can use
|
||||
* `nghttp2_submit_trailer()` to send trailers.
|
||||
* `nghttp2_submit_trailer()` can be called inside this callback.
|
||||
*
|
||||
* If the application wants to postpone DATA frames (e.g.,
|
||||
* asynchronous I/O, or reading data blocks for long time), it is
|
||||
* achieved by returning :enum:`NGHTTP2_ERR_DEFERRED` without reading
|
||||
@@ -1299,6 +1353,9 @@ typedef int (*nghttp2_on_frame_send_callback)(nghttp2_session *session,
|
||||
* `nghttp2_session_recv()` and `nghttp2_session_send()` functions
|
||||
* immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
|
||||
*
|
||||
* `nghttp2_session_get_stream_user_data()` can be used to get
|
||||
* associated data.
|
||||
*
|
||||
* To set this callback to :type:`nghttp2_session_callbacks`, use
|
||||
* `nghttp2_session_callbacks_set_on_frame_not_send_callback()`.
|
||||
*/
|
||||
@@ -1347,6 +1404,27 @@ typedef int (*nghttp2_on_stream_close_callback)(nghttp2_session *session,
|
||||
* not need to care about that because the header name/value pairs are
|
||||
* emitted transparently regardless of CONTINUATION frames.
|
||||
*
|
||||
* The server applications probably create an object to store
|
||||
* information about new stream if ``frame->hd.type ==
|
||||
* NGHTTP2_HEADERS`` and ``frame->headers.cat ==
|
||||
* NGHTTP2_HCAT_REQUEST``. If |session| is configured as server side,
|
||||
* ``frame->headers.cat`` is either ``NGHTTP2_HCAT_REQUEST``
|
||||
* containing request headers or ``NGHTTP2_HCAT_HEADERS`` containing
|
||||
* trailer headers and never get PUSH_PROMISE in this callback.
|
||||
*
|
||||
* For the client applications, ``frame->hd.type`` is either
|
||||
* ``NGHTTP2_HEADERS`` or ``NGHTTP2_PUSH_PROMISE``. In case of
|
||||
* ``NGHTTP2_HEADERS``, ``frame->headers.cat ==
|
||||
* NGHTTP2_HCAT_RESPONSE`` means that it is the first response
|
||||
* headers, but it may be non-final response which is indicated by 1xx
|
||||
* status code. In this case, there may be zero or more HEADERS frame
|
||||
* with ``frame->headers.cat == NGHTTP2_HCAT_HEADERS`` which has
|
||||
* non-final response code and finally client gets exactly one HEADERS
|
||||
* 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.
|
||||
*
|
||||
* 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
|
||||
@@ -1372,7 +1450,8 @@ typedef int (*nghttp2_on_begin_headers_callback)(nghttp2_session *session,
|
||||
*
|
||||
* If :enum:`NGHTTP2_NV_FLAG_NO_INDEX` is set in |flags|, the receiver
|
||||
* must not index this name/value pair when forwarding it to the next
|
||||
* hop.
|
||||
* hop. More specifically, "Literal Header Field never Indexed"
|
||||
* representation must be used in HPACK encoding.
|
||||
*
|
||||
* When this callback is invoked, ``frame->hd.type`` is either
|
||||
* :enum:`NGHTTP2_HEADERS` or :enum:`NGHTTP2_PUSH_PROMISE`. After all
|
||||
@@ -1382,21 +1461,14 @@ typedef int (*nghttp2_on_begin_headers_callback)(nghttp2_session *session,
|
||||
* :type:`nghttp2_on_frame_recv_callback` for the |frame| will not be
|
||||
* invoked.
|
||||
*
|
||||
* The |name| may be ``NULL`` if the |namelen| is 0. The same thing
|
||||
* can be said about the |value|.
|
||||
* The |value| may be ``NULL`` if the |valuelen| is 0.
|
||||
*
|
||||
* Please note that nghttp2 library does not perform any validity
|
||||
* check against the |name| and the |value|. For example, the
|
||||
* |namelen| could be 0, and/or the |value| contains ``0x0a`` or
|
||||
* ``0x0d``. The application must check them if it matters. The
|
||||
* helper function `nghttp2_check_header_name()` and
|
||||
* `nghttp2_check_header_value()` provide simple validation against
|
||||
* HTTP2 header field construction rule.
|
||||
*
|
||||
* HTTP/2 specification requires that pseudo header fields (header
|
||||
* field starting with ':') must appear in front of regular header
|
||||
* fields. The library does not validate this requirement. The
|
||||
* application must check them if it matters.
|
||||
* Please note that unless `nghttp2_option_set_no_http_messaging()` is
|
||||
* 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
|
||||
* explained in `HTTP Messaging`_ section.
|
||||
*
|
||||
* If the application uses `nghttp2_session_mem_recv()`, it can return
|
||||
* :enum:`NGHTTP2_ERR_PAUSE` to make `nghttp2_session_mem_recv()`
|
||||
@@ -1864,6 +1936,18 @@ void nghttp2_option_set_peer_max_concurrent_streams(nghttp2_option *option,
|
||||
*/
|
||||
void nghttp2_option_set_recv_client_preface(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 `HTTP Messaging`_ section for details. For those applications
|
||||
* who use nghttp2 library as non-HTTP use, give nonzero to |val| to
|
||||
* disable this enforcement.
|
||||
*/
|
||||
void nghttp2_option_set_no_http_messaging(nghttp2_option *option, int val);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
@@ -2457,6 +2541,43 @@ int nghttp2_session_terminate_session2(nghttp2_session *session,
|
||||
int32_t last_stream_id,
|
||||
uint32_t error_code);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* Signals to the client that the server started graceful shutdown
|
||||
* procedure.
|
||||
*
|
||||
* This function is only usable for server. If this function is
|
||||
* called with client side session, this function returns
|
||||
* :enum:`NGHTTP2_ERR_INVALID_STATE`.
|
||||
*
|
||||
* To gracefully shutdown HTTP/2 session, server should call this
|
||||
* function to send GOAWAY with last_stream_id (1u << 31) - 1. And
|
||||
* after some delay (e.g., 1 RTT), send another GOAWAY with the stream
|
||||
* ID that the server has some processing using
|
||||
* `nghttp2_submit_goaway()`. See also
|
||||
* `nghttp2_session_get_last_proc_stream_id()`.
|
||||
*
|
||||
* Unlike `nghttp2_submit_goaway()`, this function just sends GOAWAY
|
||||
* and does nothing more. This is a mere indication to the client
|
||||
* that session shutdown is imminent. The application should call
|
||||
* `nghttp2_submit_goaway()` with appropriate last_stream_id after
|
||||
* this call.
|
||||
*
|
||||
* If one or more GOAWAY frame have been already sent by either
|
||||
* `nghttp2_submit_goaway()` or `nghttp2_session_terminate_session()`,
|
||||
* this function has no effect.
|
||||
*
|
||||
* 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 |session| is initialized as client.
|
||||
*/
|
||||
int nghttp2_submit_shutdown_notice(nghttp2_session *session);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
@@ -2737,6 +2858,53 @@ int nghttp2_submit_response(nghttp2_session *session, int32_t stream_id,
|
||||
const nghttp2_nv *nva, size_t nvlen,
|
||||
const nghttp2_data_provider *data_prd);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* Submits trailer HEADERS against the stream |stream_id|.
|
||||
*
|
||||
* The |nva| is an array of name/value pair :type:`nghttp2_nv` with
|
||||
* |nvlen| elements. The application is responsible not to include
|
||||
* required pseudo-header fields (header field whose name starts with
|
||||
* ":") in |nva|.
|
||||
*
|
||||
* This function creates copies of all name/value pairs in |nva|. It
|
||||
* also lower-cases all names in |nva|. The order of elements in
|
||||
* |nva| is preserved.
|
||||
*
|
||||
* For server, trailer must be followed by response HEADERS or
|
||||
* response DATA. The library does not check that response HEADERS
|
||||
* has already sent and if `nghttp2_submit_trailer()` is called before
|
||||
* any response HEADERS submission (usually by
|
||||
* `nghttp2_submit_response()`), the content of |nva| will be sent as
|
||||
* reponse headers, which will result in error.
|
||||
*
|
||||
* This function has the same effect with `nghttp2_submit_headers()`,
|
||||
* with flags = :enum:`NGHTTP2_FLAG_END_HEADERS` and both pri_spec and
|
||||
* stream_user_data to NULL.
|
||||
*
|
||||
* To submit trailer after `nghttp2_submit_response()` is called, the
|
||||
* application has to specify :type:`nghttp2_data_provider` to
|
||||
* `nghttp2_submit_response()`. In side
|
||||
* :type:`nghttp2_data_source_read_callback`, when setting
|
||||
* :enum:`NGHTTP2_DATA_FLAG_EOF`, also set
|
||||
* :enum:`NGHTTP2_DATA_FLAG_NO_END_STREAM`. After that, the
|
||||
* application can send trailer using `nghttp2_submit_trailer()`.
|
||||
* `nghttp2_submit_trailer()` can be used inside
|
||||
* :type:`nghttp2_data_source_read_callback`.
|
||||
*
|
||||
* This function returns 0 if it succeeds and |stream_id| is -1.
|
||||
* Otherwise, 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_ARGUMENT`
|
||||
* The |stream_id| is 0.
|
||||
*/
|
||||
int nghttp2_submit_trailer(nghttp2_session *session, int32_t stream_id,
|
||||
const nghttp2_nv *nva, size_t nvlen);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
@@ -2974,6 +3142,9 @@ int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags,
|
||||
*
|
||||
* The client side is not allowed to use this function.
|
||||
*
|
||||
* To submit response headers and data, use
|
||||
* `nghttp2_submit_response()`.
|
||||
*
|
||||
* This function returns assigned promised stream ID if it succeeds,
|
||||
* or one of the following negative error codes:
|
||||
*
|
||||
@@ -3056,6 +3227,13 @@ int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags,
|
||||
* keep this memory after the return of this function. If the
|
||||
* |opaque_data_len| is 0, the |opaque_data| could be ``NULL``.
|
||||
*
|
||||
* After successful transmission of GOAWAY, following things happen.
|
||||
* All incoming streams having strictly more than |last_stream_id| are
|
||||
* closed. All incoming HEADERS which starts new stream are simply
|
||||
* ignored. After all active streams are handled, both
|
||||
* `nghttp2_session_want_read()` and `nghttp2_session_want_write()`
|
||||
* return 0 and the application can close session.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
@@ -3069,6 +3247,19 @@ int nghttp2_submit_goaway(nghttp2_session *session, uint8_t flags,
|
||||
int32_t last_stream_id, uint32_t error_code,
|
||||
const uint8_t *opaque_data, size_t opaque_data_len);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* Returns the last stream ID of a stream for which
|
||||
* :type:`nghttp2_on_frame_recv_callback` was invoked most recently.
|
||||
* The returned value can be used as last_stream_id parameter for
|
||||
* `nghttp2_submit_goaway()` and
|
||||
* `nghttp2_session_terminate_session2()`.
|
||||
*
|
||||
* This function always succeeds.
|
||||
*/
|
||||
int32_t nghttp2_session_get_last_proc_stream_id(nghttp2_session *session);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
|
||||
@@ -55,10 +55,8 @@
|
||||
/* Number of inbound buffer */
|
||||
#define NGHTTP2_FRAMEBUF_MAX_NUM 5
|
||||
|
||||
/* The default length of DATA frame payload. This should be small enough
|
||||
* for the data payload and the header to fit into 1 TLS record */
|
||||
#define NGHTTP2_DATA_PAYLOADLEN \
|
||||
((NGHTTP2_MAX_FRAME_SIZE_MIN) - (NGHTTP2_FRAME_HDLEN))
|
||||
/* The default length of DATA frame payload. */
|
||||
#define NGHTTP2_DATA_PAYLOADLEN NGHTTP2_MAX_FRAME_SIZE_MIN
|
||||
|
||||
/* Maximum headers payload length, calculated in compressed form.
|
||||
This applies to transmission only. */
|
||||
|
||||
@@ -37,8 +37,8 @@
|
||||
#define MAKE_STATIC_ENT(I, N, V, NH, VH) \
|
||||
{ \
|
||||
{ \
|
||||
{ (uint8_t *) N, (uint8_t *)V, sizeof(N) - 1, sizeof(V) - 1, 0 } \
|
||||
, NH, VH, 1, NGHTTP2_HD_FLAG_NONE \
|
||||
{ (uint8_t *)(N), (uint8_t *)(V), sizeof((N)) - 1, sizeof((V)) - 1, 0 } \
|
||||
, (NH), (VH), 1, NGHTTP2_HD_FLAG_NONE \
|
||||
} \
|
||||
, I \
|
||||
}
|
||||
|
||||
@@ -293,6 +293,10 @@ const char *nghttp2_strerror(int error_code) {
|
||||
return "Server push is disabled by peer";
|
||||
case NGHTTP2_ERR_DATA_EXIST:
|
||||
return "DATA frame already exists";
|
||||
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_NOMEM:
|
||||
return "Out of memory";
|
||||
case NGHTTP2_ERR_CALLBACK_FAILURE:
|
||||
|
||||
548
lib/nghttp2_http.c
Normal file
548
lib/nghttp2_http.c
Normal file
@@ -0,0 +1,548 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "nghttp2_http.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static int memeq(const void *a, const void *b, size_t n) {
|
||||
return memcmp(a, b, n) == 0;
|
||||
}
|
||||
|
||||
#define streq(A, B, N) ((sizeof((A)) - 1) == (N) && memeq((A), (B), (N)))
|
||||
|
||||
static char downcase(char c) {
|
||||
return 'A' <= c && c <= 'Z' ? (c - 'A' + 'a') : c;
|
||||
}
|
||||
|
||||
static int memieq(const void *a, const void *b, size_t n) {
|
||||
size_t i;
|
||||
const uint8_t *aa = a, *bb = b;
|
||||
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (downcase(aa[i]) != downcase(bb[i])) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define strieq(A, B, N) ((sizeof((A)) - 1) == (N) && memieq((A), (B), (N)))
|
||||
|
||||
typedef enum {
|
||||
NGHTTP2_TOKEN__AUTHORITY,
|
||||
NGHTTP2_TOKEN__METHOD,
|
||||
NGHTTP2_TOKEN__PATH,
|
||||
NGHTTP2_TOKEN__SCHEME,
|
||||
NGHTTP2_TOKEN__STATUS,
|
||||
NGHTTP2_TOKEN_CONNECTION,
|
||||
NGHTTP2_TOKEN_CONTENT_LENGTH,
|
||||
NGHTTP2_TOKEN_HOST,
|
||||
NGHTTP2_TOKEN_KEEP_ALIVE,
|
||||
NGHTTP2_TOKEN_PROXY_CONNECTION,
|
||||
NGHTTP2_TOKEN_TE,
|
||||
NGHTTP2_TOKEN_TRANSFER_ENCODING,
|
||||
NGHTTP2_TOKEN_UPGRADE,
|
||||
NGHTTP2_TOKEN_MAXIDX,
|
||||
} nghttp2_token;
|
||||
|
||||
/*
|
||||
* This function was generated by genlibtokenlookup.py. Inspired by
|
||||
* h2o header lookup. https://github.com/h2o/h2o
|
||||
*/
|
||||
static int lookup_token(const uint8_t *name, size_t namelen) {
|
||||
switch (namelen) {
|
||||
case 2:
|
||||
switch (name[namelen - 1]) {
|
||||
case 'e':
|
||||
if (streq("t", name, 1)) {
|
||||
return NGHTTP2_TOKEN_TE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
switch (name[namelen - 1]) {
|
||||
case 't':
|
||||
if (streq("hos", name, 3)) {
|
||||
return NGHTTP2_TOKEN_HOST;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
switch (name[namelen - 1]) {
|
||||
case 'h':
|
||||
if (streq(":pat", name, 4)) {
|
||||
return NGHTTP2_TOKEN__PATH;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
switch (name[namelen - 1]) {
|
||||
case 'd':
|
||||
if (streq(":metho", name, 6)) {
|
||||
return NGHTTP2_TOKEN__METHOD;
|
||||
}
|
||||
break;
|
||||
case 'e':
|
||||
if (streq(":schem", name, 6)) {
|
||||
return NGHTTP2_TOKEN__SCHEME;
|
||||
}
|
||||
if (streq("upgrad", name, 6)) {
|
||||
return NGHTTP2_TOKEN_UPGRADE;
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
if (streq(":statu", name, 6)) {
|
||||
return NGHTTP2_TOKEN__STATUS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 10:
|
||||
switch (name[namelen - 1]) {
|
||||
case 'e':
|
||||
if (streq("keep-aliv", name, 9)) {
|
||||
return NGHTTP2_TOKEN_KEEP_ALIVE;
|
||||
}
|
||||
break;
|
||||
case 'n':
|
||||
if (streq("connectio", name, 9)) {
|
||||
return NGHTTP2_TOKEN_CONNECTION;
|
||||
}
|
||||
break;
|
||||
case 'y':
|
||||
if (streq(":authorit", name, 9)) {
|
||||
return NGHTTP2_TOKEN__AUTHORITY;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 14:
|
||||
switch (name[namelen - 1]) {
|
||||
case 'h':
|
||||
if (streq("content-lengt", name, 13)) {
|
||||
return NGHTTP2_TOKEN_CONTENT_LENGTH;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 16:
|
||||
switch (name[namelen - 1]) {
|
||||
case 'n':
|
||||
if (streq("proxy-connectio", name, 15)) {
|
||||
return NGHTTP2_TOKEN_PROXY_CONNECTION;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 17:
|
||||
switch (name[namelen - 1]) {
|
||||
case 'g':
|
||||
if (streq("transfer-encodin", name, 16)) {
|
||||
return NGHTTP2_TOKEN_TRANSFER_ENCODING;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int64_t parse_uint(const uint8_t *s, size_t len) {
|
||||
int64_t n = 0;
|
||||
size_t i;
|
||||
if (len == 0) {
|
||||
return -1;
|
||||
}
|
||||
for (i = 0; i < len; ++i) {
|
||||
if ('0' <= s[i] && s[i] <= '9') {
|
||||
if (n > INT64_MAX / 10) {
|
||||
return -1;
|
||||
}
|
||||
n *= 10;
|
||||
if (n > INT64_MAX - (s[i] - '0')) {
|
||||
return -1;
|
||||
}
|
||||
n += s[i] - '0';
|
||||
continue;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static int lws(const uint8_t *s, size_t n) {
|
||||
size_t i;
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (s[i] != ' ' && s[i] != '\t') {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int check_pseudo_header(nghttp2_stream *stream, const nghttp2_nv *nv,
|
||||
int flag) {
|
||||
if (stream->http_flags & flag) {
|
||||
return 0;
|
||||
}
|
||||
if (lws(nv->value, nv->valuelen)) {
|
||||
return 0;
|
||||
}
|
||||
stream->http_flags |= flag;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int expect_response_body(nghttp2_stream *stream) {
|
||||
return (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_HEAD) == 0 &&
|
||||
stream->status_code / 100 != 1 && stream->status_code != 304 &&
|
||||
stream->status_code != 204;
|
||||
}
|
||||
|
||||
static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
|
||||
int trailer) {
|
||||
int token;
|
||||
|
||||
if (nv->name[0] == ':') {
|
||||
if (trailer ||
|
||||
(stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
}
|
||||
|
||||
token = lookup_token(nv->name, nv->namelen);
|
||||
|
||||
switch (token) {
|
||||
case NGHTTP2_TOKEN__AUTHORITY:
|
||||
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__AUTHORITY)) {
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
break;
|
||||
case NGHTTP2_TOKEN__METHOD:
|
||||
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__METHOD)) {
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
if (streq("HEAD", nv->value, nv->valuelen)) {
|
||||
stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
|
||||
} else if (streq("CONNECT", nv->value, nv->valuelen)) {
|
||||
if (stream->stream_id % 2 == 0) {
|
||||
/* we won't allow CONNECT for push */
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
|
||||
if (stream->http_flags &
|
||||
(NGHTTP2_HTTP_FLAG__PATH | NGHTTP2_HTTP_FLAG__SCHEME)) {
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NGHTTP2_TOKEN__PATH:
|
||||
if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) {
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) {
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
break;
|
||||
case NGHTTP2_TOKEN__SCHEME:
|
||||
if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) {
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) {
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
break;
|
||||
case NGHTTP2_TOKEN_HOST:
|
||||
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG_HOST)) {
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
break;
|
||||
case NGHTTP2_TOKEN_CONTENT_LENGTH: {
|
||||
if (stream->content_length != -1) {
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
stream->content_length = parse_uint(nv->value, nv->valuelen);
|
||||
if (stream->content_length == -1) {
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* disallowed header fields */
|
||||
case NGHTTP2_TOKEN_CONNECTION:
|
||||
case NGHTTP2_TOKEN_KEEP_ALIVE:
|
||||
case NGHTTP2_TOKEN_PROXY_CONNECTION:
|
||||
case NGHTTP2_TOKEN_TRANSFER_ENCODING:
|
||||
case NGHTTP2_TOKEN_UPGRADE:
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
case NGHTTP2_TOKEN_TE:
|
||||
if (!strieq("trailers", nv->value, nv->valuelen)) {
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (nv->name[0] == ':') {
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
}
|
||||
|
||||
if (nv->name[0] != ':') {
|
||||
stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int http_response_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
|
||||
int trailer) {
|
||||
int token;
|
||||
|
||||
if (nv->name[0] == ':') {
|
||||
if (trailer ||
|
||||
(stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
}
|
||||
|
||||
token = lookup_token(nv->name, nv->namelen);
|
||||
|
||||
switch (token) {
|
||||
case NGHTTP2_TOKEN__STATUS: {
|
||||
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__STATUS)) {
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
if (nv->valuelen != 3) {
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
stream->status_code = parse_uint(nv->value, nv->valuelen);
|
||||
if (stream->status_code == -1) {
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NGHTTP2_TOKEN_CONTENT_LENGTH: {
|
||||
if (stream->content_length != -1) {
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
stream->content_length = parse_uint(nv->value, nv->valuelen);
|
||||
if (stream->content_length == -1) {
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* disallowed header fields */
|
||||
case NGHTTP2_TOKEN_CONNECTION:
|
||||
case NGHTTP2_TOKEN_KEEP_ALIVE:
|
||||
case NGHTTP2_TOKEN_PROXY_CONNECTION:
|
||||
case NGHTTP2_TOKEN_TRANSFER_ENCODING:
|
||||
case NGHTTP2_TOKEN_UPGRADE:
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
case NGHTTP2_TOKEN_TE:
|
||||
if (!strieq("trailers", nv->value, nv->valuelen)) {
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (nv->name[0] == ':') {
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
}
|
||||
|
||||
if (nv->name[0] != ':') {
|
||||
stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
|
||||
nghttp2_frame *frame, nghttp2_nv *nv, int trailer) {
|
||||
/* We are strict for pseudo header field. One bad character should
|
||||
lead to fail. OTOH, we should be a bit forgiving for regular
|
||||
headers, since existing public internet has so much illegal
|
||||
headers floating around and if we kill the stream because of
|
||||
this, we may disrupt many web sites and/or libraries. So we
|
||||
become conservative here, and just ignore those illegal regular
|
||||
headers. */
|
||||
if (!nghttp2_check_header_name(nv->name, nv->namelen)) {
|
||||
size_t i;
|
||||
if (nv->namelen > 0 && nv->name[0] == ':') {
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
/* header field name must be lower-cased without exception */
|
||||
for (i = 0; i < nv->namelen; ++i) {
|
||||
char c = nv->name[i];
|
||||
if ('A' <= c && c <= 'Z') {
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
}
|
||||
/* When ignoring regular headers, we set this flag so that we
|
||||
still enforce header field ordering rule for pseudo header
|
||||
fields. */
|
||||
stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
|
||||
return NGHTTP2_ERR_IGN_HTTP_HEADER;
|
||||
}
|
||||
|
||||
if (!nghttp2_check_header_value(nv->value, nv->valuelen)) {
|
||||
assert(nv->namelen > 0);
|
||||
if (nv->name[0] == ':') {
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
/* When ignoring regular headers, we set this flag so that we
|
||||
still enforce header field ordering rule for pseudo header
|
||||
fields. */
|
||||
stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
|
||||
return NGHTTP2_ERR_IGN_HTTP_HEADER;
|
||||
}
|
||||
|
||||
if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
|
||||
return http_request_on_header(stream, nv, trailer);
|
||||
}
|
||||
|
||||
return http_response_on_header(stream, nv, trailer);
|
||||
}
|
||||
|
||||
int nghttp2_http_on_request_headers(nghttp2_stream *stream,
|
||||
nghttp2_frame *frame) {
|
||||
if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) {
|
||||
if ((stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0) {
|
||||
return -1;
|
||||
}
|
||||
stream->content_length = -1;
|
||||
} else if ((stream->http_flags & NGHTTP2_HTTP_FLAG_REQ_HEADERS) !=
|
||||
NGHTTP2_HTTP_FLAG_REQ_HEADERS ||
|
||||
(stream->http_flags &
|
||||
(NGHTTP2_HTTP_FLAG__AUTHORITY | NGHTTP2_HTTP_FLAG_HOST)) == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (frame->hd.type == NGHTTP2_PUSH_PROMISE) {
|
||||
/* we are going to reuse data fields for upcoming response. Clear
|
||||
them now, except for method flags. */
|
||||
stream->http_flags &= NGHTTP2_HTTP_FLAG_METH_ALL;
|
||||
stream->content_length = -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nghttp2_http_on_response_headers(nghttp2_stream *stream) {
|
||||
if ((stream->http_flags & NGHTTP2_HTTP_FLAG__STATUS) == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
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->content_length = -1;
|
||||
stream->status_code = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
stream->http_flags &= ~NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE;
|
||||
|
||||
if (!expect_response_body(stream)) {
|
||||
stream->content_length = 0;
|
||||
} else if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) {
|
||||
stream->content_length = -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nghttp2_http_on_trailer_headers(nghttp2_stream *stream _U_,
|
||||
nghttp2_frame *frame) {
|
||||
if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream) {
|
||||
if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (stream->content_length != -1 &&
|
||||
stream->content_length != stream->recv_content_length) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n) {
|
||||
stream->recv_content_length += n;
|
||||
|
||||
if ((stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) ||
|
||||
(stream->content_length != -1 &&
|
||||
stream->recv_content_length > stream->content_length)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nghttp2_http_record_request_method(nghttp2_stream *stream,
|
||||
nghttp2_frame *frame) {
|
||||
const nghttp2_nv *nva;
|
||||
size_t nvlen;
|
||||
size_t i;
|
||||
|
||||
switch (frame->hd.type) {
|
||||
case NGHTTP2_HEADERS:
|
||||
nva = frame->headers.nva;
|
||||
nvlen = frame->headers.nvlen;
|
||||
break;
|
||||
case NGHTTP2_PUSH_PROMISE:
|
||||
nva = frame->push_promise.nva;
|
||||
nvlen = frame->push_promise.nvlen;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
/* TODO we should do this strictly. */
|
||||
for (i = 0; i < nvlen; ++i) {
|
||||
const nghttp2_nv *nv = &nva[i];
|
||||
if (lookup_token(nv->name, nv->namelen) != NGHTTP2_TOKEN__METHOD) {
|
||||
continue;
|
||||
}
|
||||
if (streq("CONNECT", nv->value, nv->valuelen)) {
|
||||
stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
|
||||
return;
|
||||
}
|
||||
if (streq("HEAD", nv->value, nv->valuelen)) {
|
||||
stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
96
lib/nghttp2_http.h
Normal file
96
lib/nghttp2_http.h
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef NGHTTP2_HTTP_H
|
||||
#define NGHTTP2_HTTP_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
#include "nghttp2_session.h"
|
||||
#include "nghttp2_stream.h"
|
||||
|
||||
/*
|
||||
* This function is called when HTTP header field |nv| in |frame| is
|
||||
* received for |stream|. This function will validate |nv| against
|
||||
* the current state of stream.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_HTTP_HEADER
|
||||
* Invalid HTTP header field was received.
|
||||
* NGHTTP2_ERR_IGN_HTTP_HEADER
|
||||
* Invalid HTTP header field was received but it can be treated as
|
||||
* if it was not received because of compatibility reasons.
|
||||
*/
|
||||
int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
|
||||
nghttp2_frame *frame, nghttp2_nv *nv, int trailer);
|
||||
|
||||
/*
|
||||
* This function is called when request header is received. This
|
||||
* function performs validation and returns 0 if it succeeds, or -1.
|
||||
*/
|
||||
int nghttp2_http_on_request_headers(nghttp2_stream *stream,
|
||||
nghttp2_frame *frame);
|
||||
|
||||
/*
|
||||
* This function is called when response header is received. This
|
||||
* function performs validation and returns 0 if it succeeds, or -1.
|
||||
*/
|
||||
int nghttp2_http_on_response_headers(nghttp2_stream *stream);
|
||||
|
||||
/*
|
||||
* This function is called trailer header (for both request and
|
||||
* response) is received. This function performs validation and
|
||||
* returns 0 if it succeeds, or -1.
|
||||
*/
|
||||
int nghttp2_http_on_trailer_headers(nghttp2_stream *stream,
|
||||
nghttp2_frame *frame);
|
||||
|
||||
/*
|
||||
* This function is called when END_STREAM flag is seen in incoming
|
||||
* frame. This function performs validation and returns 0 if it
|
||||
* succeeds, or -1.
|
||||
*/
|
||||
int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream);
|
||||
|
||||
/*
|
||||
* This function is called when chunk of data is received. This
|
||||
* function performs validation and returns 0 if it succeeds, or -1.
|
||||
*/
|
||||
int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n);
|
||||
|
||||
/*
|
||||
* This function inspects header field in |frame| and records its
|
||||
* method in stream->http_flags. If frame->hd.type is neither
|
||||
* NGHTTP2_HEADERS nor NGHTTP2_PUSH_PROMISE, this function does
|
||||
* nothing.
|
||||
*/
|
||||
void nghttp2_http_record_request_method(nghttp2_stream *stream,
|
||||
nghttp2_frame *frame);
|
||||
|
||||
#endif /* NGHTTP2_HTTP_H */
|
||||
@@ -46,7 +46,12 @@ typedef int (*nghttp2_compar)(const void *lhs, const void *rhs);
|
||||
typedef enum {
|
||||
NGHTTP2_ERR_CREDENTIAL_PENDING = -101,
|
||||
NGHTTP2_ERR_IGN_HEADER_BLOCK = -103,
|
||||
NGHTTP2_ERR_IGN_PAYLOAD = -104
|
||||
NGHTTP2_ERR_IGN_PAYLOAD = -104,
|
||||
/*
|
||||
* Invalid HTTP header field was received but it can be treated as
|
||||
* if it was not received because of compatibility reasons.
|
||||
*/
|
||||
NGHTTP2_ERR_IGN_HTTP_HEADER = -105,
|
||||
} nghttp2_internal_error;
|
||||
|
||||
#endif /* NGHTTP2_INT_H */
|
||||
|
||||
@@ -40,6 +40,10 @@ typedef uint32_t key_type;
|
||||
typedef struct nghttp2_map_entry {
|
||||
struct nghttp2_map_entry *next;
|
||||
key_type key;
|
||||
#if SIZEOF_INT_P == 4
|
||||
/* we requires 8 bytes aligment */
|
||||
int64_t pad;
|
||||
#endif
|
||||
} nghttp2_map_entry;
|
||||
|
||||
typedef struct {
|
||||
|
||||
@@ -26,29 +26,32 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
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) {
|
||||
if (memcmp(&in[i], key, keylen) == 0) {
|
||||
*out = (unsigned char *)&in[i + 1];
|
||||
*outlen = in[i];
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
#define NGHTTP2_HTTP_1_1_ALPN "\x8http/1.1"
|
||||
#define NGHTTP2_HTTP_1_1_ALPN_LEN (sizeof(NGHTTP2_HTTP_1_1_ALPN) - 1)
|
||||
|
||||
int nghttp2_select_next_protocol(unsigned char **out, unsigned char *outlen,
|
||||
const unsigned char *in, unsigned int inlen) {
|
||||
int http_selected = 0;
|
||||
unsigned int i = 0;
|
||||
for (; i < inlen; i += in [i] + 1) {
|
||||
if (in[i] == NGHTTP2_PROTO_VERSION_ID_LEN && i + 1 + in[i] <= inlen &&
|
||||
memcmp(&in[i + 1], NGHTTP2_PROTO_VERSION_ID, in[i]) == 0) {
|
||||
*out = (unsigned char *)&in[i + 1];
|
||||
*outlen = in[i];
|
||||
return 1;
|
||||
}
|
||||
if (in[i] == 8 && i + 1 + in[i] <= inlen &&
|
||||
memcmp(&in[i + 1], "http/1.1", in[i]) == 0) {
|
||||
http_selected = 1;
|
||||
*out = (unsigned char *)&in[i + 1];
|
||||
*outlen = in[i];
|
||||
/* Go through to the next iteration, because "HTTP/2" may be
|
||||
there */
|
||||
}
|
||||
if (select_next_protocol(out, outlen, in, inlen, NGHTTP2_PROTO_ALPN,
|
||||
NGHTTP2_PROTO_ALPN_LEN) == 0) {
|
||||
return 1;
|
||||
}
|
||||
if (http_selected) {
|
||||
if (select_next_protocol(out, outlen, in, inlen, NGHTTP2_HTTP_1_1_ALPN,
|
||||
NGHTTP2_HTTP_1_1_ALPN_LEN) == 0) {
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -51,3 +51,8 @@ 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_http_messaging(nghttp2_option *option, int val) {
|
||||
option->opt_set_mask |= NGHTTP2_OPT_NO_HTTP_MESSAGING;
|
||||
option->no_http_messaging = val;
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ typedef enum {
|
||||
*/
|
||||
NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS = 1 << 1,
|
||||
NGHTTP2_OPT_RECV_CLIENT_PREFACE = 1 << 2,
|
||||
NGHTTP2_OPT_NO_HTTP_MESSAGING = 1 << 3,
|
||||
} nghttp2_option_flag;
|
||||
|
||||
/**
|
||||
@@ -81,6 +82,10 @@ struct nghttp2_option {
|
||||
* NGHTTP2_OPT_RECV_CLIENT_PREFACE
|
||||
*/
|
||||
uint8_t recv_client_preface;
|
||||
/**
|
||||
* NGHTTP2_OPT_NO_HTTP_MESSAGING
|
||||
*/
|
||||
uint8_t no_http_messaging;
|
||||
};
|
||||
|
||||
#endif /* NGHTTP2_OPTION_H */
|
||||
|
||||
@@ -44,6 +44,12 @@
|
||||
typedef struct {
|
||||
nghttp2_data_provider data_prd;
|
||||
void *stream_user_data;
|
||||
/* error code when request HEADERS is canceled by RST_STREAM while
|
||||
it is in queue. */
|
||||
uint32_t error_code;
|
||||
/* nonzero if request HEADERS is canceled. The error code is stored
|
||||
in |error_code|. */
|
||||
uint8_t canceled;
|
||||
/* nonzero if this item should be attached to stream object to make
|
||||
it under priority control */
|
||||
uint8_t attach_stream;
|
||||
@@ -70,11 +76,21 @@ typedef struct {
|
||||
uint8_t eof;
|
||||
} nghttp2_data_aux_data;
|
||||
|
||||
typedef enum {
|
||||
NGHTTP2_GOAWAY_AUX_NONE = 0x0,
|
||||
/* indicates that session should be terminated after the
|
||||
transmission of this frame. */
|
||||
NGHTTP2_GOAWAY_AUX_TERM_ON_SEND = 0x1,
|
||||
/* 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_flag;
|
||||
|
||||
/* struct used for GOAWAY frame */
|
||||
typedef struct {
|
||||
/* nonzero if session should be terminated after the transmission of
|
||||
this frame. */
|
||||
int terminate_on_send;
|
||||
/* bitwise-OR of one or more of nghttp2_goaway_aux_flag. */
|
||||
uint8_t flags;
|
||||
} nghttp2_goaway_aux_data;
|
||||
|
||||
/* Additional data which cannot be stored in nghttp2_frame struct */
|
||||
|
||||
@@ -130,3 +130,17 @@ 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) {
|
||||
size_t i;
|
||||
|
||||
if (pq->length == 0) {
|
||||
return 0;
|
||||
}
|
||||
for (i = 0; i < pq->length; ++i) {
|
||||
if ((*fun)(pq->q[i], arg)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -109,4 +109,13 @@ typedef int (*nghttp2_pq_item_cb)(void *item, void *arg);
|
||||
*/
|
||||
void nghttp2_pq_update(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg);
|
||||
|
||||
/*
|
||||
* Applys |fun| to each item in |pq|. The |arg| is passed as arg
|
||||
* parameter to callback function. This function must not change the
|
||||
* ordering key. If the return value from callback is nonzero, this
|
||||
* function returns 1 immediately without iterating remaining items.
|
||||
* Otherwise this function returns 0.
|
||||
*/
|
||||
int nghttp2_pq_each(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg);
|
||||
|
||||
#endif /* NGHTTP2_PQ_H */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -47,6 +47,7 @@
|
||||
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;
|
||||
|
||||
typedef enum {
|
||||
@@ -81,7 +82,8 @@ typedef enum {
|
||||
NGHTTP2_IB_IGN_CONTINUATION,
|
||||
NGHTTP2_IB_READ_PAD_DATA,
|
||||
NGHTTP2_IB_READ_DATA,
|
||||
NGHTTP2_IB_IGN_DATA
|
||||
NGHTTP2_IB_IGN_DATA,
|
||||
NGHTTP2_IB_IGN_ALL,
|
||||
} nghttp2_inbound_state;
|
||||
|
||||
#define NGHTTP2_INBOUND_NUM_IV 7
|
||||
@@ -251,6 +253,9 @@ struct nghttp2_session {
|
||||
/* Unacked local SETTINGS_MAX_CONCURRENT_STREAMS value. We use this
|
||||
to refuse the incoming stream if it exceeds this value. */
|
||||
uint32_t pending_local_max_concurrent_stream;
|
||||
/* Unacked local ENABLE_PUSH value. We use this to refuse
|
||||
PUSH_PROMISE before SETTINGS ACK is received. */
|
||||
uint8_t pending_enable_push;
|
||||
/* Nonzero if the session is server side. */
|
||||
uint8_t server;
|
||||
/* Flags indicating GOAWAY is sent and/or recieved. The flags are
|
||||
@@ -347,7 +352,9 @@ int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags,
|
||||
/*
|
||||
* Adds GOAWAY frame with the last-stream-ID |last_stream_id| and the
|
||||
* error code |error_code|. This is a convenient function built on top
|
||||
* of nghttp2_session_add_frame() to add GOAWAY easily.
|
||||
* of nghttp2_session_add_frame() to add GOAWAY easily. The
|
||||
* |aux_flags| are bitwise-OR of one or more of
|
||||
* nghttp2_goaway_aux_flag.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
@@ -359,7 +366,7 @@ int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags,
|
||||
*/
|
||||
int nghttp2_session_add_goaway(nghttp2_session *session, int32_t last_stream_id,
|
||||
uint32_t error_code, const uint8_t *opaque_data,
|
||||
size_t opaque_data_len, int terminate_on_send);
|
||||
size_t opaque_data_len, uint8_t aux_flags);
|
||||
|
||||
/*
|
||||
* Adds WINDOW_UPDATE frame with stream ID |stream_id| and
|
||||
@@ -398,6 +405,9 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags,
|
||||
* is a pointer to the arbitrary user supplied data to be associated
|
||||
* to this stream.
|
||||
*
|
||||
* If |initial_state| is NGHTTP2_STREAM_RESERVED, this function sets
|
||||
* NGHTTP2_STREAM_FLAG_PUSH flag set.
|
||||
*
|
||||
* This function returns a pointer to created new stream object, or
|
||||
* NULL.
|
||||
*/
|
||||
|
||||
@@ -68,6 +68,11 @@ void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
|
||||
stream->roots = roots;
|
||||
stream->root_prev = NULL;
|
||||
stream->root_next = NULL;
|
||||
|
||||
stream->http_flags = NGHTTP2_HTTP_FLAG_NONE;
|
||||
stream->content_length = -1;
|
||||
stream->recv_content_length = 0;
|
||||
stream->status_code = -1;
|
||||
}
|
||||
|
||||
void nghttp2_stream_free(nghttp2_stream *stream _U_) {
|
||||
@@ -114,6 +119,7 @@ static int stream_push_item(nghttp2_stream *stream, nghttp2_session *session) {
|
||||
}
|
||||
break;
|
||||
default:
|
||||
rv = 0;
|
||||
/* should not reach here */
|
||||
assert(0);
|
||||
}
|
||||
@@ -515,6 +521,7 @@ int nghttp2_stream_update_local_initial_window_size(
|
||||
|
||||
void nghttp2_stream_promise_fulfilled(nghttp2_stream *stream) {
|
||||
stream->state = NGHTTP2_STREAM_OPENED;
|
||||
stream->flags &= ~NGHTTP2_STREAM_FLAG_PUSH;
|
||||
}
|
||||
|
||||
nghttp2_stream *nghttp2_stream_get_dep_root(nghttp2_stream *stream) {
|
||||
|
||||
@@ -84,7 +84,8 @@ typedef enum {
|
||||
|
||||
typedef enum {
|
||||
NGHTTP2_STREAM_FLAG_NONE = 0,
|
||||
/* Indicates that this stream is pushed stream */
|
||||
/* Indicates that this stream is pushed stream and not opened
|
||||
yet. */
|
||||
NGHTTP2_STREAM_FLAG_PUSH = 0x01,
|
||||
/* Indicates that this stream was closed */
|
||||
NGHTTP2_STREAM_FLAG_CLOSED = 0x02,
|
||||
@@ -98,6 +99,33 @@ typedef enum {
|
||||
|
||||
} nghttp2_stream_flag;
|
||||
|
||||
/* HTTP related flags to enforce HTTP semantics */
|
||||
typedef enum {
|
||||
NGHTTP2_HTTP_FLAG_NONE = 0,
|
||||
/* header field seen so far */
|
||||
NGHTTP2_HTTP_FLAG__AUTHORITY = 1,
|
||||
NGHTTP2_HTTP_FLAG__PATH = 1 << 1,
|
||||
NGHTTP2_HTTP_FLAG__METHOD = 1 << 2,
|
||||
NGHTTP2_HTTP_FLAG__SCHEME = 1 << 3,
|
||||
/* host is not pseudo header, but we require either host or
|
||||
:authority */
|
||||
NGHTTP2_HTTP_FLAG_HOST = 1 << 4,
|
||||
NGHTTP2_HTTP_FLAG__STATUS = 1 << 5,
|
||||
/* required header fields for HTTP request except for CONNECT
|
||||
method. */
|
||||
NGHTTP2_HTTP_FLAG_REQ_HEADERS = NGHTTP2_HTTP_FLAG__METHOD |
|
||||
NGHTTP2_HTTP_FLAG__PATH |
|
||||
NGHTTP2_HTTP_FLAG__SCHEME,
|
||||
NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED = 1 << 6,
|
||||
/* HTTP method flags */
|
||||
NGHTTP2_HTTP_FLAG_METH_CONNECT = 1 << 7,
|
||||
NGHTTP2_HTTP_FLAG_METH_HEAD = 1 << 8,
|
||||
NGHTTP2_HTTP_FLAG_METH_ALL =
|
||||
NGHTTP2_HTTP_FLAG_METH_CONNECT | NGHTTP2_HTTP_FLAG_METH_HEAD,
|
||||
/* set if final response is expected */
|
||||
NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE = 1 << 9,
|
||||
} nghttp2_http_flag;
|
||||
|
||||
typedef enum {
|
||||
NGHTTP2_STREAM_DPRI_NONE = 0,
|
||||
NGHTTP2_STREAM_DPRI_NO_ITEM = 0x01,
|
||||
@@ -183,6 +211,14 @@ struct nghttp2_stream {
|
||||
uint8_t flags;
|
||||
/* Bitwise OR of zero or more nghttp2_shut_flag values */
|
||||
uint8_t shut_flags;
|
||||
/* Content-Length of request/response body. -1 if unknown. */
|
||||
int64_t content_length;
|
||||
/* Received body so far */
|
||||
int64_t recv_content_length;
|
||||
/* status code from remote server */
|
||||
int16_t status_code;
|
||||
/* Bitwise OR of zero or more nghttp2_http_flag values */
|
||||
uint16_t http_flags;
|
||||
};
|
||||
|
||||
void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
|
||||
|
||||
@@ -155,6 +155,12 @@ static int32_t submit_headers_shared_nva(nghttp2_session *session,
|
||||
attach_stream);
|
||||
}
|
||||
|
||||
int32_t nghttp2_submit_trailer(nghttp2_session *session, int32_t stream_id,
|
||||
const nghttp2_nv *nva, size_t nvlen) {
|
||||
return submit_headers_shared_nva(session, NGHTTP2_FLAG_END_STREAM, stream_id,
|
||||
NULL, nva, nvlen, NULL, NULL, 0);
|
||||
}
|
||||
|
||||
int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags,
|
||||
int32_t stream_id,
|
||||
const nghttp2_priority_spec *pri_spec,
|
||||
@@ -236,8 +242,24 @@ int nghttp2_submit_rst_stream(nghttp2_session *session, uint8_t flags _U_,
|
||||
int nghttp2_submit_goaway(nghttp2_session *session, uint8_t flags _U_,
|
||||
int32_t last_stream_id, uint32_t error_code,
|
||||
const uint8_t *opaque_data, size_t opaque_data_len) {
|
||||
if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) {
|
||||
return 0;
|
||||
}
|
||||
return nghttp2_session_add_goaway(session, last_stream_id, error_code,
|
||||
opaque_data, opaque_data_len, 0);
|
||||
opaque_data, opaque_data_len,
|
||||
NGHTTP2_GOAWAY_AUX_NONE);
|
||||
}
|
||||
|
||||
int nghttp2_submit_shutdown_notice(nghttp2_session *session) {
|
||||
if (!session->server) {
|
||||
return NGHTTP2_ERR_INVALID_STATE;
|
||||
}
|
||||
if (session->goaway_flags) {
|
||||
return 0;
|
||||
}
|
||||
return nghttp2_session_add_goaway(session, (1u << 31) - 1, NGHTTP2_NO_ERROR,
|
||||
NULL, 0,
|
||||
NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE);
|
||||
}
|
||||
|
||||
int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags _U_,
|
||||
|
||||
188
m4/libxml2.m4
Normal file
188
m4/libxml2.m4
Normal file
@@ -0,0 +1,188 @@
|
||||
# Configure paths for LIBXML2
|
||||
# Mike Hommey 2004-06-19
|
||||
# use CPPFLAGS instead of CFLAGS
|
||||
# Toshio Kuratomi 2001-04-21
|
||||
# Adapted from:
|
||||
# Configure paths for GLIB
|
||||
# Owen Taylor 97-11-3
|
||||
|
||||
dnl AM_PATH_XML2([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]])
|
||||
dnl Test for XML, and define XML_CPPFLAGS and XML_LIBS
|
||||
dnl
|
||||
AC_DEFUN([AM_PATH_XML2],[
|
||||
AC_ARG_WITH(xml-prefix,
|
||||
[ --with-xml-prefix=PFX Prefix where libxml is installed (optional)],
|
||||
xml_config_prefix="$withval", xml_config_prefix="")
|
||||
AC_ARG_WITH(xml-exec-prefix,
|
||||
[ --with-xml-exec-prefix=PFX Exec prefix where libxml is installed (optional)],
|
||||
xml_config_exec_prefix="$withval", xml_config_exec_prefix="")
|
||||
AC_ARG_ENABLE(xmltest,
|
||||
[ --disable-xmltest Do not try to compile and run a test LIBXML program],,
|
||||
enable_xmltest=yes)
|
||||
|
||||
if test x$xml_config_exec_prefix != x ; then
|
||||
xml_config_args="$xml_config_args"
|
||||
if test x${XML2_CONFIG+set} != xset ; then
|
||||
XML2_CONFIG=$xml_config_exec_prefix/bin/xml2-config
|
||||
fi
|
||||
fi
|
||||
if test x$xml_config_prefix != x ; then
|
||||
xml_config_args="$xml_config_args --prefix=$xml_config_prefix"
|
||||
if test x${XML2_CONFIG+set} != xset ; then
|
||||
XML2_CONFIG=$xml_config_prefix/bin/xml2-config
|
||||
fi
|
||||
fi
|
||||
|
||||
AC_PATH_PROG(XML2_CONFIG, xml2-config, no)
|
||||
min_xml_version=ifelse([$1], ,2.0.0,[$1])
|
||||
AC_MSG_CHECKING(for libxml - version >= $min_xml_version)
|
||||
no_xml=""
|
||||
if test "$XML2_CONFIG" = "no" ; then
|
||||
no_xml=yes
|
||||
else
|
||||
XML_CPPFLAGS=`$XML2_CONFIG $xml_config_args --cflags`
|
||||
XML_LIBS=`$XML2_CONFIG $xml_config_args --libs`
|
||||
xml_config_major_version=`$XML2_CONFIG $xml_config_args --version | \
|
||||
sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
|
||||
xml_config_minor_version=`$XML2_CONFIG $xml_config_args --version | \
|
||||
sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
|
||||
xml_config_micro_version=`$XML2_CONFIG $xml_config_args --version | \
|
||||
sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
|
||||
if test "x$enable_xmltest" = "xyes" ; then
|
||||
ac_save_CPPFLAGS="$CPPFLAGS"
|
||||
ac_save_LIBS="$LIBS"
|
||||
CPPFLAGS="$CPPFLAGS $XML_CPPFLAGS"
|
||||
LIBS="$XML_LIBS $LIBS"
|
||||
dnl
|
||||
dnl Now check if the installed libxml is sufficiently new.
|
||||
dnl (Also sanity checks the results of xml2-config to some extent)
|
||||
dnl
|
||||
rm -f conf.xmltest
|
||||
AC_TRY_RUN([
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <libxml/xmlversion.h>
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
int xml_major_version, xml_minor_version, xml_micro_version;
|
||||
int major, minor, micro;
|
||||
char *tmp_version;
|
||||
|
||||
system("touch conf.xmltest");
|
||||
|
||||
/* Capture xml2-config output via autoconf/configure variables */
|
||||
/* HP/UX 9 (%@#!) writes to sscanf strings */
|
||||
tmp_version = (char *)strdup("$min_xml_version");
|
||||
if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) {
|
||||
printf("%s, bad version string from xml2-config\n", "$min_xml_version");
|
||||
exit(1);
|
||||
}
|
||||
free(tmp_version);
|
||||
|
||||
/* Capture the version information from the header files */
|
||||
tmp_version = (char *)strdup(LIBXML_DOTTED_VERSION);
|
||||
if (sscanf(tmp_version, "%d.%d.%d", &xml_major_version, &xml_minor_version, &xml_micro_version) != 3) {
|
||||
printf("%s, bad version string from libxml includes\n", "LIBXML_DOTTED_VERSION");
|
||||
exit(1);
|
||||
}
|
||||
free(tmp_version);
|
||||
|
||||
/* Compare xml2-config output to the libxml headers */
|
||||
if ((xml_major_version != $xml_config_major_version) ||
|
||||
(xml_minor_version != $xml_config_minor_version) ||
|
||||
(xml_micro_version != $xml_config_micro_version))
|
||||
{
|
||||
printf("*** libxml header files (version %d.%d.%d) do not match\n",
|
||||
xml_major_version, xml_minor_version, xml_micro_version);
|
||||
printf("*** xml2-config (version %d.%d.%d)\n",
|
||||
$xml_config_major_version, $xml_config_minor_version, $xml_config_micro_version);
|
||||
return 1;
|
||||
}
|
||||
/* Compare the headers to the library to make sure we match */
|
||||
/* Less than ideal -- doesn't provide us with return value feedback,
|
||||
* only exits if there's a serious mismatch between header and library.
|
||||
*/
|
||||
LIBXML_TEST_VERSION;
|
||||
|
||||
/* Test that the library is greater than our minimum version */
|
||||
if ((xml_major_version > major) ||
|
||||
((xml_major_version == major) && (xml_minor_version > minor)) ||
|
||||
((xml_major_version == major) && (xml_minor_version == minor) &&
|
||||
(xml_micro_version >= micro)))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("\n*** An old version of libxml (%d.%d.%d) was found.\n",
|
||||
xml_major_version, xml_minor_version, xml_micro_version);
|
||||
printf("*** You need a version of libxml newer than %d.%d.%d. The latest version of\n",
|
||||
major, minor, micro);
|
||||
printf("*** libxml is always available from ftp://ftp.xmlsoft.org.\n");
|
||||
printf("***\n");
|
||||
printf("*** If you have already installed a sufficiently new version, this error\n");
|
||||
printf("*** probably means that the wrong copy of the xml2-config shell script is\n");
|
||||
printf("*** being found. The easiest way to fix this is to remove the old version\n");
|
||||
printf("*** of LIBXML, but you can also set the XML2_CONFIG environment to point to the\n");
|
||||
printf("*** correct copy of xml2-config. (In this case, you will have to\n");
|
||||
printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n");
|
||||
printf("*** so that the correct libraries are found at run-time))\n");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
],, no_xml=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
|
||||
CPPFLAGS="$ac_save_CPPFLAGS"
|
||||
LIBS="$ac_save_LIBS"
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "x$no_xml" = x ; then
|
||||
AC_MSG_RESULT(yes (version $xml_config_major_version.$xml_config_minor_version.$xml_config_micro_version))
|
||||
ifelse([$2], , :, [$2])
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
if test "$XML2_CONFIG" = "no" ; then
|
||||
echo "*** The xml2-config script installed by LIBXML could not be found"
|
||||
echo "*** If libxml was installed in PREFIX, make sure PREFIX/bin is in"
|
||||
echo "*** your path, or set the XML2_CONFIG environment variable to the"
|
||||
echo "*** full path to xml2-config."
|
||||
else
|
||||
if test -f conf.xmltest ; then
|
||||
:
|
||||
else
|
||||
echo "*** Could not run libxml test program, checking why..."
|
||||
CPPFLAGS="$CPPFLAGS $XML_CPPFLAGS"
|
||||
LIBS="$LIBS $XML_LIBS"
|
||||
AC_TRY_LINK([
|
||||
#include <libxml/xmlversion.h>
|
||||
#include <stdio.h>
|
||||
], [ LIBXML_TEST_VERSION; return 0;],
|
||||
[ echo "*** The test program compiled, but did not run. This usually means"
|
||||
echo "*** that the run-time linker is not finding LIBXML or finding the wrong"
|
||||
echo "*** version of LIBXML. If it is not finding LIBXML, you'll need to set your"
|
||||
echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
|
||||
echo "*** to the installed location Also, make sure you have run ldconfig if that"
|
||||
echo "*** is required on your system"
|
||||
echo "***"
|
||||
echo "*** If you have an old version installed, it is best to remove it, although"
|
||||
echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH" ],
|
||||
[ echo "*** The test program failed to compile or link. See the file config.log for the"
|
||||
echo "*** exact error that occured. This usually means LIBXML was incorrectly installed"
|
||||
echo "*** or that you have moved LIBXML since it was installed. In the latter case, you"
|
||||
echo "*** may want to edit the xml2-config script: $XML2_CONFIG" ])
|
||||
CPPFLAGS="$ac_save_CPPFLAGS"
|
||||
LIBS="$ac_save_LIBS"
|
||||
fi
|
||||
fi
|
||||
|
||||
XML_CPPFLAGS=""
|
||||
XML_LIBS=""
|
||||
ifelse([$3], , :, [$3])
|
||||
fi
|
||||
AC_SUBST(XML_CPPFLAGS)
|
||||
AC_SUBST(XML_LIBS)
|
||||
rm -f conf.xmltest
|
||||
])
|
||||
7
makebashcompletion
Executable file
7
makebashcompletion
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
BCPATH=doc/bash_completion
|
||||
|
||||
for prog in nghttp nghttpd nghttpx h2load; do
|
||||
$BCPATH/make_bash_completion.py src/$prog > $BCPATH/$prog
|
||||
done
|
||||
@@ -322,8 +322,6 @@ cdef int client_on_header(cnghttp2.nghttp2_session *session,
|
||||
logging.debug('client_on_header, type:%s, stream_id:%s', frame.hd.type, frame.hd.stream_id)
|
||||
|
||||
if frame.hd.type == cnghttp2.NGHTTP2_HEADERS:
|
||||
if frame.headers.cat == cnghttp2.NGHTTP2_HCAT_REQUEST:
|
||||
return 0
|
||||
handler = _get_stream_user_data(session, frame.hd.stream_id)
|
||||
elif frame.hd.type == cnghttp2.NGHTTP2_PUSH_PROMISE:
|
||||
handler = _get_stream_user_data(session, frame.push_promise.promised_stream_id)
|
||||
@@ -400,12 +398,6 @@ cdef int server_on_frame_recv(cnghttp2.nghttp2_session *session,
|
||||
handler = _get_stream_user_data(session, frame.hd.stream_id)
|
||||
if not handler:
|
||||
return 0
|
||||
# Check required header fields. We expect that :authority
|
||||
# or host header field.
|
||||
if handler.scheme is None or handler.method is None or\
|
||||
handler.host is None or handler.path is None:
|
||||
return http2._rst_stream(frame.hd.stream_id,
|
||||
cnghttp2.NGHTTP2_PROTOCOL_ERROR)
|
||||
if handler.cookies:
|
||||
handler.headers.append((b'cookie',
|
||||
b'; '.join(handler.cookies)))
|
||||
@@ -551,15 +543,12 @@ cdef int client_on_frame_recv(cnghttp2.nghttp2_session *session,
|
||||
sys.stderr.write(traceback.format_exc())
|
||||
return http2._rst_stream(frame.hd.stream_id)
|
||||
elif frame.hd.type == cnghttp2.NGHTTP2_HEADERS:
|
||||
if frame.headers.cat == cnghttp2.NGHTTP2_HCAT_RESPONSE:
|
||||
if frame.headers.cat == cnghttp2.NGHTTP2_HCAT_RESPONSE or frame.headers.cat == cnghttp2.NGHTTP2_HCAT_PUSH_RESPONSE:
|
||||
handler = _get_stream_user_data(session, frame.hd.stream_id)
|
||||
|
||||
if not handler:
|
||||
return 0
|
||||
# Check required header fields. We expect a status.
|
||||
if handler.status is None:
|
||||
return http2._rst_stream(frame.hd.stream_id,
|
||||
cnghttp2.NGHTTP2_PROTOCOL_ERROR)
|
||||
# TODO handle 1xx non-final response
|
||||
if handler.cookies:
|
||||
handler.headers.append((b'cookie',
|
||||
b'; '.join(handler.cookies)))
|
||||
@@ -587,10 +576,6 @@ cdef int client_on_frame_recv(cnghttp2.nghttp2_session *session,
|
||||
cnghttp2.nghttp2_session_set_stream_user_data(session, frame.push_promise.promised_stream_id,
|
||||
<void*>NULL)
|
||||
|
||||
if push_handler.scheme is None or push_handler.method is None or\
|
||||
push_handler.host is None or push_handler.path is None:
|
||||
return http2._rst_stream(frame.push_promise.promised_stream_id,
|
||||
cnghttp2.NGHTTP2_PROTOCOL_ERROR)
|
||||
try:
|
||||
handler.on_push_promise(push_handler)
|
||||
except:
|
||||
@@ -983,7 +968,6 @@ cdef class _HTTP2ClientSessionCore(_HTTP2SessionCoreBase):
|
||||
handler.method = push_promise.method
|
||||
handler.host = push_promise.host
|
||||
handler.path = push_promise.path
|
||||
handler.headers = push_promise.headers
|
||||
handler.cookies = push_promise.cookies
|
||||
handler.stream_id = push_promise.stream_id
|
||||
handler.http2 = self
|
||||
@@ -1046,6 +1030,9 @@ if asyncio:
|
||||
path
|
||||
This is a value of :path header field.
|
||||
|
||||
headers
|
||||
Request header fields
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, http2, stream_id):
|
||||
@@ -1358,6 +1345,11 @@ if asyncio:
|
||||
path
|
||||
This is a value of :path header field.
|
||||
|
||||
headers
|
||||
Response header fields. There is a special exception. If this
|
||||
object is passed to push_promise(), this instance variable contains
|
||||
pushed request header fields.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, http2=None, stream_id=-1):
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include <fcntl.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <set>
|
||||
@@ -47,6 +48,7 @@
|
||||
#include "http2.h"
|
||||
#include "util.h"
|
||||
#include "ssl.h"
|
||||
#include "template.h"
|
||||
|
||||
#ifndef O_BINARY
|
||||
#define O_BINARY (0)
|
||||
@@ -76,7 +78,7 @@ void print_session_id(int64_t id) { std::cout << "[id=" << id << "] "; }
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void append_nv(Stream *stream, const std::vector<nghttp2_nv> &nva) {
|
||||
template <typename Array> void append_nv(Stream *stream, const Array &nva) {
|
||||
for (size_t i = 0; i < nva.size(); ++i) {
|
||||
auto &nv = nva[i];
|
||||
auto token = http2::lookup_token(nv.name, nv.namelen);
|
||||
@@ -84,7 +86,7 @@ void append_nv(Stream *stream, const std::vector<nghttp2_nv> &nva) {
|
||||
http2::index_header(stream->hdidx, token, i);
|
||||
}
|
||||
http2::add_header(stream->headers, nv.name, nv.namelen, nv.value,
|
||||
nv.valuelen, nv.flags & NGHTTP2_NV_FLAG_NO_INDEX);
|
||||
nv.valuelen, nv.flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
@@ -171,7 +173,8 @@ class Sessions {
|
||||
public:
|
||||
Sessions(struct ev_loop *loop, const Config *config, SSL_CTX *ssl_ctx)
|
||||
: loop_(loop), config_(config), ssl_ctx_(ssl_ctx), callbacks_(nullptr),
|
||||
next_session_id_(1) {
|
||||
next_session_id_(1), tstamp_cached_(ev_now(loop)),
|
||||
cached_date_(util::http_date(tstamp_cached_)) {
|
||||
nghttp2_session_callbacks_new(&callbacks_);
|
||||
|
||||
fill_callback(callbacks_, config_);
|
||||
@@ -223,7 +226,7 @@ public:
|
||||
}
|
||||
}
|
||||
auto handler =
|
||||
util::make_unique<Http2Handler>(this, fd, ssl, get_next_session_id());
|
||||
make_unique<Http2Handler>(this, fd, ssl, get_next_session_id());
|
||||
handler->setup_bev();
|
||||
if (!ssl) {
|
||||
if (handler->on_connect() != 0) {
|
||||
@@ -232,17 +235,25 @@ public:
|
||||
}
|
||||
add_handler(handler.release());
|
||||
}
|
||||
void update_cached_date() { cached_date_ = util::http_date(time(nullptr)); }
|
||||
const std::string &get_cached_date() const { return cached_date_; }
|
||||
void update_cached_date() { cached_date_ = util::http_date(tstamp_cached_); }
|
||||
const std::string &get_cached_date() {
|
||||
auto t = ev_now(loop_);
|
||||
if (t != tstamp_cached_) {
|
||||
tstamp_cached_ = t;
|
||||
update_cached_date();
|
||||
}
|
||||
return cached_date_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::set<Http2Handler *> handlers_;
|
||||
std::string cached_date_;
|
||||
struct ev_loop *loop_;
|
||||
const Config *config_;
|
||||
SSL_CTX *ssl_ctx_;
|
||||
nghttp2_session_callbacks *callbacks_;
|
||||
int64_t next_session_id_;
|
||||
ev_tstamp tstamp_cached_;
|
||||
std::string cached_date_;
|
||||
};
|
||||
|
||||
Stream::Stream(Http2Handler *handler, int32_t stream_id)
|
||||
@@ -400,11 +411,11 @@ int Http2Handler::fill_wb() {
|
||||
|
||||
int Http2Handler::read_clear() {
|
||||
int rv;
|
||||
uint8_t buf[8192];
|
||||
std::array<uint8_t, 8192> buf;
|
||||
|
||||
for (;;) {
|
||||
ssize_t nread;
|
||||
while ((nread = read(fd_, buf, sizeof(buf))) == -1 && errno == EINTR)
|
||||
while ((nread = read(fd_, buf.data(), buf.size())) == -1 && errno == EINTR)
|
||||
;
|
||||
if (nread == -1) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
@@ -415,10 +426,12 @@ int Http2Handler::read_clear() {
|
||||
if (nread == 0) {
|
||||
return -1;
|
||||
}
|
||||
rv = nghttp2_session_mem_recv(session_, buf, nread);
|
||||
rv = nghttp2_session_mem_recv(session_, buf.data(), nread);
|
||||
if (rv < 0) {
|
||||
std::cerr << "nghttp2_session_mem_recv() returned error: "
|
||||
<< nghttp2_strerror(rv) << std::endl;
|
||||
if (rv != NGHTTP2_ERR_BAD_PREFACE) {
|
||||
std::cerr << "nghttp2_session_mem_recv() returned error: "
|
||||
<< nghttp2_strerror(rv) << std::endl;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -430,11 +443,9 @@ int Http2Handler::write_clear() {
|
||||
auto loop = sessions_->get_loop();
|
||||
for (;;) {
|
||||
if (wb_.rleft() > 0) {
|
||||
struct iovec iov[2];
|
||||
auto iovcnt = wb_.riovec(iov);
|
||||
|
||||
ssize_t nwrite;
|
||||
while ((nwrite = writev(fd_, iov, iovcnt)) == -1 && errno == EINTR)
|
||||
while ((nwrite = write(fd_, wb_.pos, wb_.rleft())) == -1 &&
|
||||
errno == EINTR)
|
||||
;
|
||||
if (nwrite == -1) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
@@ -512,12 +523,12 @@ int Http2Handler::tls_handshake() {
|
||||
}
|
||||
|
||||
int Http2Handler::read_tls() {
|
||||
uint8_t buf[8192];
|
||||
std::array<uint8_t, 8192> buf;
|
||||
|
||||
ERR_clear_error();
|
||||
|
||||
for (;;) {
|
||||
auto rv = SSL_read(ssl_, buf, sizeof(buf));
|
||||
auto rv = SSL_read(ssl_, buf.data(), buf.size());
|
||||
|
||||
if (rv == 0) {
|
||||
return -1;
|
||||
@@ -537,10 +548,12 @@ int Http2Handler::read_tls() {
|
||||
}
|
||||
|
||||
auto nread = rv;
|
||||
rv = nghttp2_session_mem_recv(session_, buf, nread);
|
||||
rv = nghttp2_session_mem_recv(session_, buf.data(), nread);
|
||||
if (rv < 0) {
|
||||
std::cerr << "nghttp2_session_mem_recv() returned error: "
|
||||
<< nghttp2_strerror(rv) << std::endl;
|
||||
if (rv != NGHTTP2_ERR_BAD_PREFACE) {
|
||||
std::cerr << "nghttp2_session_mem_recv() returned error: "
|
||||
<< nghttp2_strerror(rv) << std::endl;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -556,11 +569,7 @@ int Http2Handler::write_tls() {
|
||||
|
||||
for (;;) {
|
||||
if (wb_.rleft() > 0) {
|
||||
const void *p;
|
||||
size_t len;
|
||||
std::tie(p, len) = wb_.get();
|
||||
|
||||
auto rv = SSL_write(ssl_, p, len);
|
||||
auto rv = SSL_write(ssl_, wb_.pos, wb_.rleft());
|
||||
|
||||
if (rv == 0) {
|
||||
return -1;
|
||||
@@ -618,7 +627,7 @@ int Http2Handler::on_connect() {
|
||||
if (r != 0) {
|
||||
return r;
|
||||
}
|
||||
nghttp2_settings_entry entry[4];
|
||||
std::array<nghttp2_settings_entry, 4> entry;
|
||||
size_t niv = 1;
|
||||
|
||||
entry[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
|
||||
@@ -629,7 +638,7 @@ int Http2Handler::on_connect() {
|
||||
entry[niv].value = sessions_->get_config()->header_table_size;
|
||||
++niv;
|
||||
}
|
||||
r = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, entry, niv);
|
||||
r = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, entry.data(), niv);
|
||||
if (r != 0) {
|
||||
return r;
|
||||
}
|
||||
@@ -676,30 +685,39 @@ int Http2Handler::submit_file_response(const std::string &status,
|
||||
nghttp2_data_provider *data_prd) {
|
||||
std::string content_length = util::utos(file_length);
|
||||
std::string last_modified_str;
|
||||
nghttp2_nv nva[] = {
|
||||
http2::make_nv_ls(":status", status),
|
||||
http2::make_nv_ls("server", NGHTTPD_SERVER),
|
||||
http2::make_nv_ls("content-length", content_length),
|
||||
http2::make_nv_ll("cache-control", "max-age=3600"),
|
||||
http2::make_nv_ls("date", sessions_->get_cached_date()),
|
||||
http2::make_nv_ll("", ""),
|
||||
};
|
||||
auto nva = make_array(http2::make_nv_ls(":status", status),
|
||||
http2::make_nv_ls("server", NGHTTPD_SERVER),
|
||||
http2::make_nv_ls("content-length", content_length),
|
||||
http2::make_nv_ll("cache-control", "max-age=3600"),
|
||||
http2::make_nv_ls("date", sessions_->get_cached_date()),
|
||||
http2::make_nv_ll("", ""), http2::make_nv_ll("", ""));
|
||||
size_t nvlen = 5;
|
||||
if (last_modified != 0) {
|
||||
last_modified_str = util::http_date(last_modified);
|
||||
nva[nvlen++] = http2::make_nv_ls("last-modified", last_modified_str);
|
||||
}
|
||||
return nghttp2_submit_response(session_, stream->stream_id, nva, nvlen,
|
||||
auto &trailer = get_config()->trailer;
|
||||
std::string trailer_names;
|
||||
if (!trailer.empty()) {
|
||||
trailer_names = trailer[0].name;
|
||||
for (size_t i = 1; i < trailer.size(); ++i) {
|
||||
trailer_names += ", ";
|
||||
trailer_names += trailer[i].name;
|
||||
}
|
||||
nva[nvlen++] = http2::make_nv_ls("trailer", trailer_names);
|
||||
}
|
||||
return nghttp2_submit_response(session_, stream->stream_id, nva.data(), nvlen,
|
||||
data_prd);
|
||||
}
|
||||
|
||||
int Http2Handler::submit_response(const std::string &status, int32_t stream_id,
|
||||
const Headers &headers,
|
||||
nghttp2_data_provider *data_prd) {
|
||||
auto nva = std::vector<nghttp2_nv>{
|
||||
http2::make_nv_ls(":status", status),
|
||||
http2::make_nv_ls("server", NGHTTPD_SERVER),
|
||||
http2::make_nv_ls("date", sessions_->get_cached_date())};
|
||||
auto nva = std::vector<nghttp2_nv>();
|
||||
nva.reserve(3 + headers.size());
|
||||
nva.push_back(http2::make_nv_ls(":status", status));
|
||||
nva.push_back(http2::make_nv_ls("server", NGHTTPD_SERVER));
|
||||
nva.push_back(http2::make_nv_ls("date", sessions_->get_cached_date()));
|
||||
for (auto &nv : headers) {
|
||||
nva.push_back(http2::make_nv(nv.name, nv.value, nv.no_index));
|
||||
}
|
||||
@@ -710,16 +728,15 @@ int Http2Handler::submit_response(const std::string &status, int32_t stream_id,
|
||||
|
||||
int Http2Handler::submit_response(const std::string &status, int32_t stream_id,
|
||||
nghttp2_data_provider *data_prd) {
|
||||
auto nva =
|
||||
std::vector<nghttp2_nv>{http2::make_nv_ls(":status", status),
|
||||
http2::make_nv_ls("server", NGHTTPD_SERVER)};
|
||||
auto nva = make_array(http2::make_nv_ls(":status", status),
|
||||
http2::make_nv_ls("server", NGHTTPD_SERVER));
|
||||
return nghttp2_submit_response(session_, stream_id, nva.data(), nva.size(),
|
||||
data_prd);
|
||||
}
|
||||
|
||||
int Http2Handler::submit_non_final_response(const std::string &status,
|
||||
int32_t stream_id) {
|
||||
auto nva = std::vector<nghttp2_nv>{http2::make_nv_ls(":status", status)};
|
||||
auto nva = make_array(http2::make_nv_ls(":status", status));
|
||||
return nghttp2_submit_headers(session_, NGHTTP2_FLAG_NONE, stream_id, nullptr,
|
||||
nva.data(), nva.size(), nullptr);
|
||||
}
|
||||
@@ -734,12 +751,12 @@ int Http2Handler::submit_push_promise(Stream *stream,
|
||||
http2::get_header(stream->hdidx, http2::HD_HOST, stream->headers);
|
||||
}
|
||||
|
||||
auto nva = std::vector<nghttp2_nv>{
|
||||
http2::make_nv_ll(":method", "GET"),
|
||||
http2::make_nv_ls(":path", push_path),
|
||||
get_config()->no_tls ? http2::make_nv_ll(":scheme", "http")
|
||||
: http2::make_nv_ll(":scheme", "https"),
|
||||
http2::make_nv_ls(":authority", authority->value)};
|
||||
auto nva =
|
||||
make_array(http2::make_nv_ll(":method", "GET"),
|
||||
http2::make_nv_ls(":path", push_path),
|
||||
get_config()->no_tls ? http2::make_nv_ll(":scheme", "http")
|
||||
: http2::make_nv_ll(":scheme", "https"),
|
||||
http2::make_nv_ls(":authority", authority->value));
|
||||
|
||||
auto promised_stream_id = nghttp2_submit_push_promise(
|
||||
session_, NGHTTP2_FLAG_END_HEADERS, stream->stream_id, nva.data(),
|
||||
@@ -749,7 +766,7 @@ int Http2Handler::submit_push_promise(Stream *stream,
|
||||
return promised_stream_id;
|
||||
}
|
||||
|
||||
auto promised_stream = util::make_unique<Stream>(this, promised_stream_id);
|
||||
auto promised_stream = make_unique<Stream>(this, promised_stream_id);
|
||||
|
||||
append_nv(promised_stream.get(), nva);
|
||||
add_stream(promised_stream_id, std::move(promised_stream));
|
||||
@@ -802,6 +819,7 @@ void Http2Handler::terminate_session(uint32_t error_code) {
|
||||
ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
|
||||
uint8_t *buf, size_t length, uint32_t *data_flags,
|
||||
nghttp2_data_source *source, void *user_data) {
|
||||
int rv;
|
||||
auto hd = static_cast<Http2Handler *>(user_data);
|
||||
auto stream = hd->get_stream(stream_id);
|
||||
|
||||
@@ -822,6 +840,23 @@ ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
|
||||
if (nread == 0 || stream->body_left <= 0) {
|
||||
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
||||
|
||||
auto config = hd->get_config();
|
||||
if (!config->trailer.empty()) {
|
||||
std::vector<nghttp2_nv> nva;
|
||||
nva.reserve(config->trailer.size());
|
||||
for (auto &kv : config->trailer) {
|
||||
nva.push_back(http2::make_nv(kv.name, kv.value, kv.no_index));
|
||||
}
|
||||
rv = nghttp2_submit_trailer(session, stream_id, nva.data(), nva.size());
|
||||
if (rv != 0) {
|
||||
if (nghttp2_is_fatal(rv)) {
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
} else {
|
||||
*data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM;
|
||||
}
|
||||
}
|
||||
|
||||
if (nghttp2_session_get_stream_remote_close(session, stream_id) == 0) {
|
||||
remove_stream_read_timeout(stream);
|
||||
remove_stream_write_timeout(stream);
|
||||
@@ -1022,32 +1057,12 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||
if (!stream) {
|
||||
return 0;
|
||||
}
|
||||
if (!http2::check_nv(name, namelen, value, valuelen)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto token = http2::lookup_token(name, namelen);
|
||||
|
||||
if (name[0] == ':') {
|
||||
if ((!stream->headers.empty() &&
|
||||
stream->headers.back().name.c_str()[0] != ':') ||
|
||||
!http2::check_http2_request_pseudo_header(stream->hdidx, token)) {
|
||||
|
||||
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id,
|
||||
NGHTTP2_PROTOCOL_ERROR);
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!http2::http2_header_allowed(token)) {
|
||||
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id,
|
||||
NGHTTP2_PROTOCOL_ERROR);
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
http2::index_header(stream->hdidx, token, stream->headers.size());
|
||||
http2::add_header(stream->headers, name, namelen, value, valuelen,
|
||||
flags & NGHTTP2_NV_FLAG_NO_INDEX);
|
||||
flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
@@ -1062,7 +1077,7 @@ int on_begin_headers_callback(nghttp2_session *session,
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto stream = util::make_unique<Stream>(hd, frame->hd.stream_id);
|
||||
auto stream = make_unique<Stream>(hd, frame->hd.stream_id);
|
||||
|
||||
add_stream_read_timeout(stream.get());
|
||||
|
||||
@@ -1107,15 +1122,10 @@ int hd_on_frame_recv_callback(nghttp2_session *session,
|
||||
|
||||
if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
|
||||
|
||||
if (!http2::http2_mandatory_request_headers_presence(stream->hdidx)) {
|
||||
hd->submit_rst_stream(stream, NGHTTP2_PROTOCOL_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto expect100 =
|
||||
http2::get_header(stream->hdidx, http2::HD_EXPECT, stream->headers);
|
||||
|
||||
if (expect100 && util::strieq("100-continue", expect100->value.c_str())) {
|
||||
if (expect100 && util::strieq_l("100-continue", expect100->value)) {
|
||||
hd->submit_non_final_response("100", frame->hd.stream_id);
|
||||
}
|
||||
|
||||
@@ -1140,11 +1150,6 @@ int hd_on_frame_recv_callback(nghttp2_session *session,
|
||||
hd->remove_settings_timer();
|
||||
}
|
||||
break;
|
||||
case NGHTTP2_PUSH_PROMISE:
|
||||
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
|
||||
frame->push_promise.promised_stream_id,
|
||||
NGHTTP2_REFUSED_STREAM);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1309,21 +1314,9 @@ void worker_acceptcb(struct ev_loop *loop, ev_async *w, int revents) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void refresh_cb(struct ev_loop *loop, ev_timer *w, int revents) {
|
||||
auto sessions = static_cast<Sessions *>(w->data);
|
||||
sessions->update_cached_date();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void run_worker(Worker *worker) {
|
||||
auto loop = worker->sessions->get_loop();
|
||||
ev_timer w;
|
||||
ev_timer_init(&w, refresh_cb, 0., 1.);
|
||||
w.data = worker->sessions.get();
|
||||
ev_timer_again(loop, &w);
|
||||
worker->sessions->update_cached_date();
|
||||
|
||||
ev_run(loop, 0);
|
||||
}
|
||||
@@ -1340,10 +1333,10 @@ public:
|
||||
if (config_->verbose) {
|
||||
std::cerr << "spawning thread #" << i << std::endl;
|
||||
}
|
||||
auto worker = util::make_unique<Worker>();
|
||||
auto worker = make_unique<Worker>();
|
||||
auto loop = ev_loop_new(0);
|
||||
worker->sessions =
|
||||
util::make_unique<Sessions>(loop, config_, sessions_->get_ssl_ctx());
|
||||
make_unique<Sessions>(loop, config_, sessions_->get_ssl_ctx());
|
||||
ev_async_init(&worker->w, worker_acceptcb);
|
||||
worker->w.data = worker.get();
|
||||
ev_async_start(loop, &worker->w);
|
||||
@@ -1404,10 +1397,7 @@ public:
|
||||
auto fd = accept(fd_, nullptr, nullptr);
|
||||
#endif // !HAVE_ACCEPT4
|
||||
if (fd == -1) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
#ifndef HAVE_ACCEPT4
|
||||
util::make_socket_nonblocking(fd);
|
||||
@@ -1456,6 +1446,7 @@ int start_listen(struct ev_loop *loop, Sessions *sessions,
|
||||
addrinfo hints;
|
||||
int r;
|
||||
bool ok = false;
|
||||
const char *addr = nullptr;
|
||||
|
||||
auto acceptor = std::make_shared<AcceptHandler>(sessions, config);
|
||||
auto service = util::utos(config->port);
|
||||
@@ -1468,12 +1459,17 @@ int start_listen(struct ev_loop *loop, Sessions *sessions,
|
||||
hints.ai_flags |= AI_ADDRCONFIG;
|
||||
#endif // AI_ADDRCONFIG
|
||||
|
||||
if (!config->address.empty()) {
|
||||
addr = config->address.c_str();
|
||||
}
|
||||
|
||||
addrinfo *res, *rp;
|
||||
r = getaddrinfo(nullptr, service.c_str(), &hints, &res);
|
||||
r = getaddrinfo(addr, service.c_str(), &hints, &res);
|
||||
if (r != 0) {
|
||||
std::cerr << "getaddrinfo() failed: " << gai_strerror(r) << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (rp = res; rp; rp = rp->ai_next) {
|
||||
int fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
||||
if (fd == -1) {
|
||||
@@ -1499,8 +1495,9 @@ int start_listen(struct ev_loop *loop, Sessions *sessions,
|
||||
new ListenEventHandler(sessions, fd, acceptor);
|
||||
|
||||
if (config->verbose) {
|
||||
std::cout << (rp->ai_family == AF_INET ? "IPv4" : "IPv6")
|
||||
<< ": listen on port " << config->port << std::endl;
|
||||
std::string s = util::numeric_name(rp->ai_addr, rp->ai_addrlen);
|
||||
std::cout << (rp->ai_family == AF_INET ? "IPv4" : "IPv6") << ": listen "
|
||||
<< s << ":" << config->port << std::endl;
|
||||
}
|
||||
ok = true;
|
||||
continue;
|
||||
@@ -1652,14 +1649,6 @@ int HttpServer::run() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ev_timer refresh_timer;
|
||||
ev_timer_init(&refresh_timer, refresh_cb, 0., 1.);
|
||||
refresh_timer.data = &sessions;
|
||||
if (config_->num_worker == 1) {
|
||||
ev_timer_again(loop, &refresh_timer);
|
||||
sessions.update_cached_date();
|
||||
}
|
||||
|
||||
ev_run(loop, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -44,17 +44,19 @@
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
#include "http2.h"
|
||||
#include "ringbuf.h"
|
||||
#include "buffer.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
struct Config {
|
||||
std::map<std::string, std::vector<std::string>> push;
|
||||
Headers trailer;
|
||||
std::string htdocs;
|
||||
std::string host;
|
||||
std::string private_key_file;
|
||||
std::string cert_file;
|
||||
std::string dh_param_file;
|
||||
std::string address;
|
||||
ev_tstamp stream_read_timeout;
|
||||
ev_tstamp stream_write_timeout;
|
||||
nghttp2_option *session_option;
|
||||
@@ -83,7 +85,7 @@ struct Stream {
|
||||
int64_t body_left;
|
||||
int32_t stream_id;
|
||||
int file;
|
||||
int hdidx[http2::HD_MAXIDX];
|
||||
http2::HeaderIndex hdidx;
|
||||
Stream(Http2Handler *handler, int32_t stream_id);
|
||||
~Stream();
|
||||
};
|
||||
@@ -142,7 +144,7 @@ private:
|
||||
ev_io rev_;
|
||||
ev_timer settings_timerev_;
|
||||
std::map<int32_t, std::unique_ptr<Stream>> id2stream_;
|
||||
RingBuf<65536> wb_;
|
||||
Buffer<65536> wb_;
|
||||
std::function<int(Http2Handler &)> read_, write_;
|
||||
int64_t session_id_;
|
||||
nghttp2_session *session_;
|
||||
|
||||
@@ -41,7 +41,9 @@ AM_CPPFLAGS = \
|
||||
@JANSSON_CFLAGS@ \
|
||||
@ZLIB_CFLAGS@ \
|
||||
@DEFS@
|
||||
AM_LDFLAGS = \
|
||||
|
||||
LDADD = $(top_builddir)/lib/libnghttp2.la \
|
||||
$(top_builddir)/third-party/libhttp-parser.la \
|
||||
@JEMALLOC_LIBS@ \
|
||||
@LIBSPDYLAY_LIBS@ \
|
||||
@XML_LIBS@ \
|
||||
@@ -51,10 +53,6 @@ AM_LDFLAGS = \
|
||||
@ZLIB_LIBS@ \
|
||||
@APPLDFLAGS@
|
||||
|
||||
LDADD = \
|
||||
$(top_builddir)/lib/libnghttp2.la \
|
||||
$(top_builddir)/third-party/libhttp-parser.la
|
||||
|
||||
if ENABLE_APP
|
||||
|
||||
bin_PROGRAMS += nghttp nghttpd nghttpx
|
||||
@@ -78,8 +76,7 @@ nghttp_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} nghttp.cc nghttp.h \
|
||||
|
||||
nghttpd_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} nghttpd.cc \
|
||||
ssl.cc ssl.h \
|
||||
HttpServer.cc HttpServer.h \
|
||||
ringbuf.h
|
||||
HttpServer.cc HttpServer.h
|
||||
|
||||
bin_PROGRAMS += h2load
|
||||
|
||||
@@ -117,11 +114,12 @@ NGHTTPX_SRCS = \
|
||||
shrpx_io_control.cc shrpx_io_control.h \
|
||||
shrpx_ssl.cc shrpx_ssl.h \
|
||||
shrpx_worker.cc shrpx_worker.h \
|
||||
shrpx_worker_config.cc shrpx_worker_config.h \
|
||||
shrpx_log_config.cc shrpx_log_config.h \
|
||||
shrpx_connect_blocker.cc shrpx_connect_blocker.h \
|
||||
shrpx_downstream_connection_pool.cc shrpx_downstream_connection_pool.h \
|
||||
shrpx_rate_limit.cc shrpx_rate_limit.h \
|
||||
ringbuf.h memchunk.h
|
||||
shrpx_connection.cc shrpx_connection.h \
|
||||
buffer.h memchunk.h template.h
|
||||
|
||||
if HAVE_SPDYLAY
|
||||
NGHTTPX_SRCS += shrpx_spdy_upstream.cc shrpx_spdy_upstream.h
|
||||
@@ -131,8 +129,7 @@ noinst_LIBRARIES = libnghttpx.a
|
||||
libnghttpx_a_SOURCES = ${NGHTTPX_SRCS}
|
||||
|
||||
nghttpx_SOURCES = shrpx.cc shrpx.h
|
||||
nghttpx_LDFLAGS =
|
||||
nghttpx_LDADD = libnghttpx.a ${LDADD} ${AM_LDFLAGS}
|
||||
nghttpx_LDADD = libnghttpx.a ${LDADD}
|
||||
|
||||
if HAVE_CUNIT
|
||||
check_PROGRAMS += nghttpx-unittest
|
||||
@@ -144,13 +141,11 @@ nghttpx_unittest_SOURCES = shrpx-unittest.cc \
|
||||
util_test.cc util_test.h \
|
||||
nghttp2_gzip_test.c nghttp2_gzip_test.h \
|
||||
nghttp2_gzip.c nghttp2_gzip.h \
|
||||
ringbuf_test.cc ringbuf_test.h \
|
||||
buffer_test.cc buffer_test.h \
|
||||
memchunk_test.cc memchunk_test.h
|
||||
nghttpx_unittest_CPPFLAGS = ${AM_CPPFLAGS}\
|
||||
-DNGHTTP2_TESTS_DIR=\"$(top_srcdir)/tests\"
|
||||
nghttpx_unittest_LDFLAGS = ${AM_LDFLAGS} \
|
||||
@CUNIT_LIBS@ @TESTLDFLAGS@
|
||||
nghttpx_unittest_LDADD = libnghttpx.a ${LDADD}
|
||||
-DNGHTTP2_TESTS_DIR=\"$(top_srcdir)/tests\"
|
||||
nghttpx_unittest_LDADD = libnghttpx.a ${LDADD} @CUNIT_LIBS@ @TESTLDADD@
|
||||
|
||||
TESTS += nghttpx-unittest
|
||||
endif # HAVE_CUNIT
|
||||
@@ -178,23 +173,43 @@ DISTCLEANFILES = $(pkgconfig_DATA)
|
||||
lib_LTLIBRARIES = libnghttp2_asio.la
|
||||
|
||||
libnghttp2_asio_la_SOURCES = \
|
||||
asio_connection.h \
|
||||
asio_server.cc asio_server.h \
|
||||
asio_io_service_pool.cc asio_io_service_pool.h \
|
||||
asio_http2_handler.cc asio_http2_handler.h \
|
||||
asio_http2_impl.cc asio_http2_impl.h \
|
||||
util.cc util.h http2.cc http2.h \
|
||||
ssl.cc ssl.h
|
||||
ssl.cc ssl.h \
|
||||
asio_common.cc asio_common.h \
|
||||
asio_io_service_pool.cc asio_io_service_pool.h \
|
||||
asio_server_http2.cc \
|
||||
asio_server_http2_impl.cc asio_server_http2_impl.h \
|
||||
asio_server.cc asio_server.h \
|
||||
asio_server_http2_handler.cc asio_server_http2_handler.h \
|
||||
asio_server_connection.h \
|
||||
asio_server_request.cc \
|
||||
asio_server_request_impl.cc asio_server_request_impl.h \
|
||||
asio_server_response.cc \
|
||||
asio_server_response_impl.cc asio_server_response_impl.h \
|
||||
asio_server_stream.cc asio_server_stream.h \
|
||||
asio_server_serve_mux.cc asio_server_serve_mux.h \
|
||||
asio_server_request_handler.cc asio_server_request_handler.h \
|
||||
asio_server_tls_context.cc asio_server_tls_context.h \
|
||||
asio_client_session.cc \
|
||||
asio_client_session_impl.cc asio_client_session_impl.h \
|
||||
asio_client_session_tcp_impl.cc asio_client_session_tcp_impl.h \
|
||||
asio_client_session_tls_impl.cc asio_client_session_tls_impl.h \
|
||||
asio_client_response.cc \
|
||||
asio_client_response_impl.cc asio_client_response_impl.h \
|
||||
asio_client_request.cc \
|
||||
asio_client_request_impl.cc asio_client_request_impl.h \
|
||||
asio_client_stream.cc asio_client_stream.h \
|
||||
asio_client_tls_context.cc asio_client_tls_context.h
|
||||
|
||||
libnghttp2_asio_la_CPPFLAGS= ${AM_CPPFLAGS} ${BOOST_CPPFLAGS}
|
||||
libnghttp2_asio_la_LDFLAGS = \
|
||||
libnghttp2_asio_la_CPPFLAGS = ${AM_CPPFLAGS} ${BOOST_CPPFLAGS}
|
||||
libnghttp2_asio_la_LDFLAGS = -no-undefined -version-info 1:0:0
|
||||
libnghttp2_asio_la_LIBADD = \
|
||||
$(top_builddir)/lib/libnghttp2.la \
|
||||
$(top_builddir)/third-party/libhttp-parser.la \
|
||||
${BOOST_LDFLAGS} \
|
||||
${BOOST_ASIO_LIB} \
|
||||
${BOOST_THREAD_LIB} \
|
||||
${BOOST_SYSTEM_LIB} \
|
||||
@OPENSSL_LIBS@ \
|
||||
-no-undefined \
|
||||
-version-info 0:0:0
|
||||
libnghttp2_asio_la_LIBADD = $(top_builddir)/lib/libnghttp2.la
|
||||
@OPENSSL_LIBS@
|
||||
|
||||
endif # ENABLE_ASIO_LIB
|
||||
|
||||
@@ -408,8 +408,11 @@ int verbose_on_header_callback(nghttp2_session *session,
|
||||
namelen, valuelen};
|
||||
|
||||
print_timer();
|
||||
fprintf(outfile, " recv (stream_id=%d, noind=%d) ", frame->hd.stream_id,
|
||||
(flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0);
|
||||
fprintf(outfile, " recv (stream_id=%d", frame->hd.stream_id);
|
||||
if (flags & NGHTTP2_NV_FLAG_NO_INDEX) {
|
||||
fprintf(outfile, ", sensitive");
|
||||
}
|
||||
fprintf(outfile, ") ");
|
||||
|
||||
print_nv(&nv);
|
||||
fflush(outfile);
|
||||
|
||||
67
src/asio_client_request.cc
Normal file
67
src/asio_client_request.cc
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <nghttp2/asio_http2_client.h>
|
||||
|
||||
#include "asio_client_request_impl.h"
|
||||
|
||||
#include "template.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace client {
|
||||
|
||||
request::request() : impl_(make_unique<request_impl>()) {}
|
||||
|
||||
request::~request() {}
|
||||
|
||||
void request::write_trailer(header_map h) const {
|
||||
impl_->write_trailer(std::move(h));
|
||||
}
|
||||
|
||||
void request::cancel(uint32_t error_code) const { impl_->cancel(error_code); }
|
||||
|
||||
void request::on_response(response_cb cb) const {
|
||||
impl_->on_response(std::move(cb));
|
||||
}
|
||||
|
||||
void request::on_push(request_cb cb) const { impl_->on_push(std::move(cb)); }
|
||||
|
||||
void request::on_close(close_cb cb) const { impl_->on_close(std::move(cb)); }
|
||||
|
||||
const uri_ref &request::uri() const { return impl_->uri(); }
|
||||
|
||||
const std::string &request::method() const { return impl_->method(); }
|
||||
|
||||
const header_map &request::header() const { return impl_->header(); }
|
||||
|
||||
void request::resume() const { impl_->resume(); }
|
||||
|
||||
request_impl &request::impl() const { return *impl_; }
|
||||
|
||||
} // namespace client
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
||||
110
src/asio_client_request_impl.cc
Normal file
110
src/asio_client_request_impl.cc
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "asio_client_request_impl.h"
|
||||
|
||||
#include "asio_client_stream.h"
|
||||
#include "asio_client_session_impl.h"
|
||||
#include "template.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace client {
|
||||
|
||||
request_impl::request_impl() : strm_(nullptr) {}
|
||||
|
||||
void request_impl::write_trailer(header_map h) {
|
||||
auto sess = strm_->session();
|
||||
sess->write_trailer(*strm_, std::move(h));
|
||||
}
|
||||
|
||||
void request_impl::cancel(uint32_t error_code) {
|
||||
auto sess = strm_->session();
|
||||
sess->cancel(*strm_, error_code);
|
||||
}
|
||||
|
||||
void request_impl::on_response(response_cb cb) { response_cb_ = std::move(cb); }
|
||||
|
||||
void request_impl::call_on_response(response &res) {
|
||||
if (response_cb_) {
|
||||
response_cb_(res);
|
||||
}
|
||||
}
|
||||
|
||||
void request_impl::on_push(request_cb cb) { push_request_cb_ = std::move(cb); }
|
||||
|
||||
void request_impl::call_on_push(request &push_req) {
|
||||
if (push_request_cb_) {
|
||||
push_request_cb_(push_req);
|
||||
}
|
||||
};
|
||||
|
||||
void request_impl::on_close(close_cb cb) { close_cb_ = std::move(cb); }
|
||||
|
||||
void request_impl::call_on_close(uint32_t error_code) {
|
||||
if (close_cb_) {
|
||||
close_cb_(error_code);
|
||||
}
|
||||
}
|
||||
|
||||
void request_impl::on_read(generator_cb cb) { generator_cb_ = std::move(cb); }
|
||||
|
||||
generator_cb::result_type request_impl::call_on_read(uint8_t *buf,
|
||||
std::size_t len,
|
||||
uint32_t *data_flags) {
|
||||
if (generator_cb_) {
|
||||
return generator_cb_(buf, len, data_flags);
|
||||
}
|
||||
|
||||
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void request_impl::resume() {
|
||||
auto sess = strm_->session();
|
||||
sess->resume(*strm_);
|
||||
}
|
||||
|
||||
void request_impl::header(header_map h) { header_ = std::move(h); }
|
||||
|
||||
header_map &request_impl::header() { return header_; }
|
||||
|
||||
const header_map &request_impl::header() const { return header_; }
|
||||
|
||||
void request_impl::stream(class stream *strm) { strm_ = strm; }
|
||||
|
||||
void request_impl::uri(uri_ref uri) { uri_ = std::move(uri); }
|
||||
|
||||
const uri_ref &request_impl::uri() const { return uri_; }
|
||||
|
||||
uri_ref &request_impl::uri() { return uri_; }
|
||||
|
||||
void request_impl::method(std::string s) { method_ = std::move(s); }
|
||||
|
||||
const std::string &request_impl::method() const { return method_; }
|
||||
|
||||
} // namespace client
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
||||
93
src/asio_client_request_impl.h
Normal file
93
src/asio_client_request_impl.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef ASIO_CLIENT_REQUEST_IMPL_H
|
||||
#define ASIO_CLIENT_REQUEST_IMPL_H
|
||||
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <nghttp2/asio_http2_client.h>
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace client {
|
||||
|
||||
class response;
|
||||
class stream;
|
||||
|
||||
class request_impl {
|
||||
public:
|
||||
request_impl();
|
||||
|
||||
request_impl(const request_impl &) = delete;
|
||||
request_impl &operator=(const request_impl &) = delete;
|
||||
|
||||
void write_trailer(header_map h);
|
||||
|
||||
void cancel(uint32_t error_code);
|
||||
|
||||
void on_response(response_cb cb);
|
||||
void call_on_response(response &res);
|
||||
|
||||
void on_push(request_cb cb);
|
||||
void call_on_push(request &push_req);
|
||||
|
||||
void on_close(close_cb cb);
|
||||
void call_on_close(uint32_t error_code);
|
||||
|
||||
void on_read(generator_cb cb);
|
||||
generator_cb::result_type call_on_read(uint8_t *buf, std::size_t len,
|
||||
uint32_t *data_flags);
|
||||
|
||||
void resume();
|
||||
|
||||
void header(header_map h);
|
||||
header_map &header();
|
||||
const header_map &header() const;
|
||||
|
||||
void stream(class stream *strm);
|
||||
|
||||
void uri(uri_ref uri);
|
||||
const uri_ref &uri() const;
|
||||
uri_ref &uri();
|
||||
|
||||
void method(std::string s);
|
||||
const std::string &method() const;
|
||||
|
||||
private:
|
||||
header_map header_;
|
||||
response_cb response_cb_;
|
||||
request_cb push_request_cb_;
|
||||
close_cb close_cb_;
|
||||
generator_cb generator_cb_;
|
||||
class stream *strm_;
|
||||
uri_ref uri_;
|
||||
std::string method_;
|
||||
};
|
||||
|
||||
} // namespace client
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
||||
|
||||
#endif // ASIO_CLIENT_REQUEST_IMPL_H
|
||||
53
src/asio_client_response.cc
Normal file
53
src/asio_client_response.cc
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <nghttp2/asio_http2_client.h>
|
||||
|
||||
#include "asio_client_response_impl.h"
|
||||
|
||||
#include "template.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace client {
|
||||
|
||||
response::response() : impl_(make_unique<response_impl>()) {}
|
||||
|
||||
response::~response() {}
|
||||
|
||||
void response::on_data(data_cb cb) const { impl_->on_data(std::move(cb)); }
|
||||
|
||||
int response::status_code() const { return impl_->status_code(); }
|
||||
|
||||
int64_t response::content_length() const { return impl_->content_length(); }
|
||||
|
||||
const header_map &response::header() const { return impl_->header(); }
|
||||
|
||||
response_impl &response::impl() const { return *impl_; }
|
||||
|
||||
} // namespace client
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
||||
57
src/asio_client_response_impl.cc
Normal file
57
src/asio_client_response_impl.cc
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "asio_client_response_impl.h"
|
||||
|
||||
#include "template.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace client {
|
||||
|
||||
response_impl::response_impl() : content_length_(-1), status_code_(0) {}
|
||||
|
||||
void response_impl::on_data(data_cb cb) { data_cb_ = std::move(cb); }
|
||||
|
||||
void response_impl::call_on_data(const uint8_t *data, std::size_t len) {
|
||||
if (data_cb_) {
|
||||
data_cb_(data, len);
|
||||
}
|
||||
}
|
||||
|
||||
void response_impl::status_code(int sc) { status_code_ = sc; }
|
||||
|
||||
int response_impl::status_code() const { return status_code_; }
|
||||
|
||||
void response_impl::content_length(int64_t n) { content_length_ = n; }
|
||||
|
||||
int64_t response_impl::content_length() const { return content_length_; }
|
||||
|
||||
header_map &response_impl::header() { return header_; }
|
||||
|
||||
const header_map &response_impl::header() const { return header_; }
|
||||
|
||||
} // namespace client
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
||||
69
src/asio_client_response_impl.h
Normal file
69
src/asio_client_response_impl.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef ASIO_CLIENT_RESPONSE_IMPL_H
|
||||
#define ASIO_CLIENT_RESPONSE_IMPL_H
|
||||
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <nghttp2/asio_http2_client.h>
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace client {
|
||||
|
||||
class response_impl {
|
||||
public:
|
||||
response_impl();
|
||||
|
||||
response_impl(const response_impl &) = delete;
|
||||
response_impl &operator=(const response_impl &) = delete;
|
||||
|
||||
void on_data(data_cb cb);
|
||||
|
||||
void call_on_data(const uint8_t *data, std::size_t len);
|
||||
|
||||
void status_code(int sc);
|
||||
int status_code() const;
|
||||
|
||||
void content_length(int64_t n);
|
||||
int64_t content_length() const;
|
||||
|
||||
header_map &header();
|
||||
const header_map &header() const;
|
||||
|
||||
private:
|
||||
data_cb data_cb_;
|
||||
|
||||
header_map header_;
|
||||
|
||||
int64_t content_length_;
|
||||
int status_code_;
|
||||
};
|
||||
|
||||
} // namespace client
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
||||
|
||||
#endif // ASIO_CLIENT_RESPONSE_IMPL_H
|
||||
98
src/asio_client_session.cc
Normal file
98
src/asio_client_session.cc
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <nghttp2/asio_http2_client.h>
|
||||
|
||||
#include "asio_client_session_tcp_impl.h"
|
||||
#include "asio_client_session_tls_impl.h"
|
||||
#include "asio_common.h"
|
||||
#include "template.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace client {
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
session::session(boost::asio::io_service &io_service, const std::string &host,
|
||||
const std::string &service)
|
||||
: impl_(make_unique<session_tcp_impl>(io_service, host, service)) {}
|
||||
|
||||
session::session(boost::asio::io_service &io_service,
|
||||
boost::asio::ssl::context &tls_ctx, const std::string &host,
|
||||
const std::string &service)
|
||||
: impl_(make_unique<session_tls_impl>(io_service, tls_ctx, host, service)) {
|
||||
}
|
||||
|
||||
session::~session() {}
|
||||
|
||||
session::session(session &&other) noexcept : impl_(std::move(other.impl_)) {}
|
||||
|
||||
session &session::operator=(session &&other) noexcept {
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
impl_ = std::move(other.impl_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void session::on_connect(connect_cb cb) const {
|
||||
impl_->on_connect(std::move(cb));
|
||||
}
|
||||
|
||||
void session::on_error(error_cb cb) const { impl_->on_error(std::move(cb)); }
|
||||
|
||||
void session::shutdown() const { impl_->shutdown(); }
|
||||
|
||||
boost::asio::io_service &session::io_service() const {
|
||||
return impl_->io_service();
|
||||
}
|
||||
|
||||
const request *session::submit(boost::system::error_code &ec,
|
||||
const std::string &method,
|
||||
const std::string &uri, header_map h) const {
|
||||
return impl_->submit(ec, method, uri, generator_cb(), std::move(h));
|
||||
}
|
||||
|
||||
const request *session::submit(boost::system::error_code &ec,
|
||||
const std::string &method,
|
||||
const std::string &uri, std::string data,
|
||||
header_map h) const {
|
||||
return impl_->submit(ec, method, uri, string_generator(std::move(data)),
|
||||
std::move(h));
|
||||
}
|
||||
|
||||
const request *session::submit(boost::system::error_code &ec,
|
||||
const std::string &method,
|
||||
const std::string &uri, generator_cb cb,
|
||||
header_map h) const {
|
||||
return impl_->submit(ec, method, uri, std::move(cb), std::move(h));
|
||||
}
|
||||
|
||||
} // namespace client
|
||||
} // namespace asio_http2
|
||||
} // nghttp2
|
||||
621
src/asio_client_session_impl.cc
Normal file
621
src/asio_client_session_impl.cc
Normal file
@@ -0,0 +1,621 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "asio_client_session_impl.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "asio_client_stream.h"
|
||||
#include "asio_client_request_impl.h"
|
||||
#include "asio_client_response_impl.h"
|
||||
#include "asio_common.h"
|
||||
#include "template.h"
|
||||
#include "util.h"
|
||||
#include "http2.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace client {
|
||||
|
||||
session_impl::session_impl(boost::asio::io_service &io_service)
|
||||
: wblen_(0), io_service_(io_service), resolver_(io_service),
|
||||
session_(nullptr), data_pending_(nullptr), data_pendinglen_(0),
|
||||
writing_(false), inside_callback_(false) {}
|
||||
|
||||
session_impl::~session_impl() {
|
||||
// finish up all active stream
|
||||
for (auto &p : streams_) {
|
||||
auto &strm = p.second;
|
||||
auto &req = strm->request().impl();
|
||||
req.call_on_close(NGHTTP2_INTERNAL_ERROR);
|
||||
}
|
||||
|
||||
nghttp2_session_del(session_);
|
||||
}
|
||||
|
||||
void session_impl::start_resolve(const std::string &host,
|
||||
const std::string &service) {
|
||||
resolver_.async_resolve({host, service},
|
||||
[this](const boost::system::error_code &ec,
|
||||
tcp::resolver::iterator endpoint_it) {
|
||||
if (ec) {
|
||||
not_connected(ec);
|
||||
return;
|
||||
}
|
||||
|
||||
start_connect(endpoint_it);
|
||||
});
|
||||
}
|
||||
|
||||
void session_impl::connected(tcp::resolver::iterator endpoint_it) {
|
||||
if (!setup_session()) {
|
||||
return;
|
||||
}
|
||||
|
||||
socket().set_option(boost::asio::ip::tcp::no_delay(true));
|
||||
|
||||
std::copy_n(NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN, std::begin(wb_));
|
||||
wblen_ = NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN;
|
||||
|
||||
do_write();
|
||||
do_read();
|
||||
|
||||
auto &connect_cb = on_connect();
|
||||
if (connect_cb) {
|
||||
connect_cb(endpoint_it);
|
||||
}
|
||||
}
|
||||
|
||||
void session_impl::not_connected(const boost::system::error_code &ec) {
|
||||
auto &error_cb = on_error();
|
||||
if (error_cb) {
|
||||
error_cb(ec);
|
||||
}
|
||||
}
|
||||
|
||||
void session_impl::on_connect(connect_cb cb) { connect_cb_ = std::move(cb); }
|
||||
|
||||
void session_impl::on_error(error_cb cb) { error_cb_ = std::move(cb); }
|
||||
|
||||
const connect_cb &session_impl::on_connect() const { return connect_cb_; }
|
||||
|
||||
const error_cb &session_impl::on_error() const { return error_cb_; }
|
||||
|
||||
namespace {
|
||||
int on_begin_headers_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame, void *user_data) {
|
||||
if (frame->hd.type != NGHTTP2_PUSH_PROMISE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto sess = static_cast<session_impl *>(user_data);
|
||||
sess->create_push_stream(frame->push_promise.promised_stream_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||
const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen, uint8_t flags,
|
||||
void *user_data) {
|
||||
auto sess = static_cast<session_impl *>(user_data);
|
||||
stream *strm;
|
||||
|
||||
switch (frame->hd.type) {
|
||||
case NGHTTP2_HEADERS: {
|
||||
strm = sess->find_stream(frame->hd.stream_id);
|
||||
if (!strm) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ignore trailers
|
||||
if (frame->headers.cat == NGHTTP2_HCAT_HEADERS &&
|
||||
!strm->expect_final_response()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto token = http2::lookup_token(name, namelen);
|
||||
|
||||
auto &res = strm->response().impl();
|
||||
if (token == http2::HD__STATUS) {
|
||||
res.status_code(util::parse_uint(value, valuelen));
|
||||
} else {
|
||||
|
||||
if (token == http2::HD_CONTENT_LENGTH) {
|
||||
res.content_length(util::parse_uint(value, valuelen));
|
||||
}
|
||||
|
||||
res.header().emplace(
|
||||
std::string(name, name + namelen),
|
||||
header_value{std::string(value, value + valuelen),
|
||||
(flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0});
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NGHTTP2_PUSH_PROMISE: {
|
||||
strm = sess->find_stream(frame->push_promise.promised_stream_id);
|
||||
if (!strm) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto &req = strm->request().impl();
|
||||
auto &uri = req.uri();
|
||||
|
||||
switch (http2::lookup_token(name, namelen)) {
|
||||
case http2::HD__METHOD:
|
||||
req.method(std::string(value, value + valuelen));
|
||||
break;
|
||||
case http2::HD__SCHEME:
|
||||
uri.scheme.assign(value, value + valuelen);
|
||||
break;
|
||||
case http2::HD__PATH:
|
||||
split_path(uri, value, value + valuelen);
|
||||
break;
|
||||
case http2::HD__AUTHORITY:
|
||||
uri.host.assign(value, value + valuelen);
|
||||
break;
|
||||
case http2::HD_HOST:
|
||||
if (uri.host.empty()) {
|
||||
uri.host.assign(value, value + valuelen);
|
||||
}
|
||||
// fall through
|
||||
default:
|
||||
req.header().emplace(
|
||||
std::string(name, name + namelen),
|
||||
header_value{std::string(value, value + valuelen),
|
||||
(flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0});
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||
void *user_data) {
|
||||
auto sess = static_cast<session_impl *>(user_data);
|
||||
auto strm = sess->find_stream(frame->hd.stream_id);
|
||||
|
||||
switch (frame->hd.type) {
|
||||
case NGHTTP2_DATA: {
|
||||
if (!strm) {
|
||||
return 0;
|
||||
}
|
||||
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||
strm->response().impl().call_on_data(nullptr, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NGHTTP2_HEADERS: {
|
||||
if (!strm) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ignore trailers
|
||||
if (frame->headers.cat == NGHTTP2_HCAT_HEADERS &&
|
||||
!strm->expect_final_response()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strm->expect_final_response()) {
|
||||
// wait for final response
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto &req = strm->request().impl();
|
||||
req.call_on_response(strm->response());
|
||||
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||
strm->response().impl().call_on_data(nullptr, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NGHTTP2_PUSH_PROMISE: {
|
||||
if (!strm) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto push_strm = sess->find_stream(frame->push_promise.promised_stream_id);
|
||||
if (!push_strm) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
strm->request().impl().call_on_push(push_strm->request());
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
||||
int32_t stream_id, const uint8_t *data,
|
||||
size_t len, void *user_data) {
|
||||
auto sess = static_cast<session_impl *>(user_data);
|
||||
auto strm = sess->find_stream(stream_id);
|
||||
if (!strm) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto &res = strm->response().impl();
|
||||
res.call_on_data(data, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||
uint32_t error_code, void *user_data) {
|
||||
auto sess = static_cast<session_impl *>(user_data);
|
||||
auto strm = sess->pop_stream(stream_id);
|
||||
if (!strm) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
strm->request().impl().call_on_close(error_code);
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool session_impl::setup_session() {
|
||||
nghttp2_session_callbacks *callbacks;
|
||||
nghttp2_session_callbacks_new(&callbacks);
|
||||
auto cb_del = defer(nghttp2_session_callbacks_del, callbacks);
|
||||
|
||||
nghttp2_session_callbacks_set_on_begin_headers_callback(
|
||||
callbacks, on_begin_headers_callback);
|
||||
nghttp2_session_callbacks_set_on_header_callback(callbacks,
|
||||
on_header_callback);
|
||||
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
|
||||
on_frame_recv_callback);
|
||||
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
|
||||
callbacks, on_data_chunk_recv_callback);
|
||||
nghttp2_session_callbacks_set_on_stream_close_callback(
|
||||
callbacks, on_stream_close_callback);
|
||||
|
||||
auto rv = nghttp2_session_client_new(&session_, callbacks, this);
|
||||
if (rv != 0) {
|
||||
auto &error_cb = on_error();
|
||||
if (error_cb) {
|
||||
error_cb(make_error_code(static_cast<nghttp2_error>(rv)));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint32_t window_size = 256 * 1024 * 1024;
|
||||
|
||||
std::array<nghttp2_settings_entry, 2> iv{
|
||||
{{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100},
|
||||
// typically client is just a *sink* and just process data as
|
||||
// much as possible. Use large window size by default.
|
||||
{NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, window_size}}};
|
||||
nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, iv.data(), iv.size());
|
||||
// increase connection window size up to window_size
|
||||
nghttp2_submit_window_update(session_, NGHTTP2_FLAG_NONE, 0,
|
||||
window_size -
|
||||
NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE);
|
||||
return true;
|
||||
}
|
||||
|
||||
int session_impl::write_trailer(stream &strm, header_map h) {
|
||||
int rv;
|
||||
auto nva = std::vector<nghttp2_nv>();
|
||||
nva.reserve(h.size());
|
||||
for (auto &hd : h) {
|
||||
nva.push_back(nghttp2::http2::make_nv(hd.first, hd.second.value,
|
||||
hd.second.sensitive));
|
||||
}
|
||||
|
||||
rv = nghttp2_submit_trailer(session_, strm.stream_id(), nva.data(),
|
||||
nva.size());
|
||||
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
signal_write();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void session_impl::cancel(stream &strm, uint32_t error_code) {
|
||||
nghttp2_submit_rst_stream(session_, NGHTTP2_FLAG_NONE, strm.stream_id(),
|
||||
error_code);
|
||||
signal_write();
|
||||
}
|
||||
|
||||
void session_impl::resume(stream &strm) {
|
||||
nghttp2_session_resume_data(session_, strm.stream_id());
|
||||
signal_write();
|
||||
}
|
||||
|
||||
stream *session_impl::find_stream(int32_t stream_id) {
|
||||
auto it = streams_.find(stream_id);
|
||||
if (it == std::end(streams_)) {
|
||||
return nullptr;
|
||||
}
|
||||
return (*it).second.get();
|
||||
}
|
||||
|
||||
std::unique_ptr<stream> session_impl::pop_stream(int32_t stream_id) {
|
||||
auto it = streams_.find(stream_id);
|
||||
if (it == std::end(streams_)) {
|
||||
return nullptr;
|
||||
}
|
||||
auto strm = std::move((*it).second);
|
||||
streams_.erase(it);
|
||||
return strm;
|
||||
}
|
||||
|
||||
stream *session_impl::create_push_stream(int32_t stream_id) {
|
||||
auto strm = create_stream();
|
||||
strm->stream_id(stream_id);
|
||||
auto p = streams_.emplace(stream_id, std::move(strm));
|
||||
assert(p.second);
|
||||
return (*p.first).second.get();
|
||||
}
|
||||
|
||||
std::unique_ptr<stream> session_impl::create_stream() {
|
||||
return make_unique<stream>(this);
|
||||
}
|
||||
|
||||
const request *session_impl::submit(boost::system::error_code &ec,
|
||||
const std::string &method,
|
||||
const std::string &uri, generator_cb cb,
|
||||
header_map h) {
|
||||
ec.clear();
|
||||
|
||||
http_parser_url u{};
|
||||
// TODO Handle CONNECT method
|
||||
if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) {
|
||||
ec = make_error_code(boost::system::errc::invalid_argument);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if ((u.field_set & (1 << UF_SCHEMA)) == 0 ||
|
||||
(u.field_set & (1 << UF_HOST)) == 0) {
|
||||
ec = make_error_code(boost::system::errc::invalid_argument);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto strm = create_stream();
|
||||
auto &req = strm->request().impl();
|
||||
auto &uref = req.uri();
|
||||
|
||||
http2::copy_url_component(uref.scheme, &u, UF_SCHEMA, uri.c_str());
|
||||
http2::copy_url_component(uref.host, &u, UF_HOST, uri.c_str());
|
||||
http2::copy_url_component(uref.raw_path, &u, UF_PATH, uri.c_str());
|
||||
http2::copy_url_component(uref.raw_query, &u, UF_QUERY, uri.c_str());
|
||||
|
||||
if (util::ipv6_numeric_addr(uref.host.c_str())) {
|
||||
uref.host = "[" + uref.host;
|
||||
uref.host += "]";
|
||||
}
|
||||
if (u.field_set & (1 << UF_PORT)) {
|
||||
uref.host += ":";
|
||||
uref.host += util::utos(u.port);
|
||||
}
|
||||
|
||||
if (uref.raw_path.empty()) {
|
||||
uref.raw_path = "/";
|
||||
}
|
||||
|
||||
uref.path = percent_decode(uref.raw_path);
|
||||
|
||||
auto path = uref.raw_path;
|
||||
if (u.field_set & (1 << UF_QUERY)) {
|
||||
path += "?";
|
||||
path += uref.raw_query;
|
||||
}
|
||||
|
||||
auto nva = std::vector<nghttp2_nv>();
|
||||
nva.reserve(3 + h.size());
|
||||
nva.push_back(http2::make_nv_ls(":method", method));
|
||||
nva.push_back(http2::make_nv_ls(":scheme", uref.scheme));
|
||||
nva.push_back(http2::make_nv_ls(":path", path));
|
||||
nva.push_back(http2::make_nv_ls(":authority", uref.host));
|
||||
for (auto &kv : h) {
|
||||
nva.push_back(
|
||||
http2::make_nv(kv.first, kv.second.value, kv.second.sensitive));
|
||||
}
|
||||
|
||||
req.header(std::move(h));
|
||||
|
||||
nghttp2_data_provider *prdptr = nullptr;
|
||||
nghttp2_data_provider prd;
|
||||
|
||||
if (cb) {
|
||||
strm->request().impl().on_read(std::move(cb));
|
||||
prd.source.ptr = strm.get();
|
||||
prd.read_callback =
|
||||
[](nghttp2_session *session, int32_t stream_id, uint8_t *buf,
|
||||
size_t length, uint32_t *data_flags, nghttp2_data_source *source,
|
||||
void *user_data) -> ssize_t {
|
||||
auto strm = static_cast<stream *>(source->ptr);
|
||||
return strm->request().impl().call_on_read(buf, length, data_flags);
|
||||
};
|
||||
prdptr = &prd;
|
||||
}
|
||||
|
||||
auto stream_id = nghttp2_submit_request(session_, nullptr, nva.data(),
|
||||
nva.size(), prdptr, strm.get());
|
||||
if (stream_id < 0) {
|
||||
ec = make_error_code(static_cast<nghttp2_error>(stream_id));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
signal_write();
|
||||
|
||||
strm->stream_id(stream_id);
|
||||
|
||||
auto p = streams_.emplace(stream_id, std::move(strm));
|
||||
assert(p.second);
|
||||
return &(*p.first).second->request();
|
||||
}
|
||||
|
||||
void session_impl::shutdown() {
|
||||
nghttp2_session_terminate_session(session_, NGHTTP2_NO_ERROR);
|
||||
signal_write();
|
||||
}
|
||||
|
||||
boost::asio::io_service &session_impl::io_service() { return io_service_; }
|
||||
|
||||
void session_impl::signal_write() {
|
||||
if (!inside_callback_) {
|
||||
do_write();
|
||||
}
|
||||
}
|
||||
|
||||
bool session_impl::should_stop() const {
|
||||
return !writing_ && !nghttp2_session_want_read(session_) &&
|
||||
!nghttp2_session_want_write(session_);
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct callback_guard {
|
||||
callback_guard(session_impl &sess) : sess(sess) { sess.enter_callback(); }
|
||||
~callback_guard() { sess.leave_callback(); }
|
||||
|
||||
session_impl &sess;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
void session_impl::enter_callback() {
|
||||
assert(!inside_callback_);
|
||||
inside_callback_ = true;
|
||||
}
|
||||
|
||||
void session_impl::leave_callback() {
|
||||
assert(inside_callback_);
|
||||
inside_callback_ = false;
|
||||
}
|
||||
|
||||
void session_impl::do_read() {
|
||||
read_socket([this](const boost::system::error_code &ec,
|
||||
std::size_t bytes_transferred) {
|
||||
if (ec) {
|
||||
if (ec.value() == boost::asio::error::operation_aborted) {
|
||||
shutdown_socket();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
callback_guard cg(*this);
|
||||
|
||||
auto rv =
|
||||
nghttp2_session_mem_recv(session_, rb_.data(), bytes_transferred);
|
||||
|
||||
if (rv != static_cast<ssize_t>(bytes_transferred)) {
|
||||
shutdown_socket();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
do_write();
|
||||
|
||||
if (should_stop()) {
|
||||
shutdown_socket();
|
||||
return;
|
||||
}
|
||||
|
||||
do_read();
|
||||
});
|
||||
}
|
||||
|
||||
void session_impl::do_write() {
|
||||
if (writing_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (data_pending_) {
|
||||
std::copy_n(data_pending_, data_pendinglen_, std::begin(wb_) + wblen_);
|
||||
|
||||
wblen_ += data_pendinglen_;
|
||||
|
||||
data_pending_ = nullptr;
|
||||
data_pendinglen_ = 0;
|
||||
}
|
||||
|
||||
{
|
||||
callback_guard cg(*this);
|
||||
|
||||
for (;;) {
|
||||
const uint8_t *data;
|
||||
auto n = nghttp2_session_mem_send(session_, &data);
|
||||
if (n < 0) {
|
||||
shutdown_socket();
|
||||
return;
|
||||
}
|
||||
|
||||
if (n == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (wblen_ + n > wb_.size()) {
|
||||
data_pending_ = data;
|
||||
data_pendinglen_ = n;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
std::copy_n(data, n, std::begin(wb_) + wblen_);
|
||||
|
||||
wblen_ += n;
|
||||
}
|
||||
}
|
||||
|
||||
if (wblen_ == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
writing_ = true;
|
||||
|
||||
write_socket([this](const boost::system::error_code &ec, std::size_t n) {
|
||||
if (ec) {
|
||||
return;
|
||||
}
|
||||
|
||||
wblen_ = 0;
|
||||
writing_ = false;
|
||||
|
||||
do_write();
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace client
|
||||
} // namespace asio_http2
|
||||
} // nghttp2
|
||||
122
src/asio_client_session_impl.h
Normal file
122
src/asio_client_session_impl.h
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef ASIO_CLIENT_SESSION_IMPL_H
|
||||
#define ASIO_CLIENT_SESSION_IMPL_H
|
||||
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <boost/array.hpp>
|
||||
|
||||
#include <nghttp2/asio_http2_client.h>
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace client {
|
||||
|
||||
class stream;
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
class session_impl {
|
||||
public:
|
||||
session_impl(boost::asio::io_service &io_service);
|
||||
virtual ~session_impl();
|
||||
|
||||
void start_resolve(const std::string &host, const std::string &service);
|
||||
|
||||
void connected(tcp::resolver::iterator endpoint_it);
|
||||
void not_connected(const boost::system::error_code &ec);
|
||||
|
||||
void on_connect(connect_cb cb);
|
||||
void on_error(error_cb cb);
|
||||
|
||||
const connect_cb &on_connect() const;
|
||||
const error_cb &on_error() const;
|
||||
|
||||
int write_trailer(stream &strm, header_map h);
|
||||
|
||||
void cancel(stream &strm, uint32_t error_code);
|
||||
void resume(stream &strm);
|
||||
|
||||
std::unique_ptr<stream> create_stream();
|
||||
std::unique_ptr<stream> pop_stream(int32_t stream_id);
|
||||
stream *create_push_stream(int32_t stream_id);
|
||||
stream *find_stream(int32_t stream_id);
|
||||
|
||||
const request *submit(boost::system::error_code &ec,
|
||||
const std::string &method, const std::string &uri,
|
||||
generator_cb cb, header_map h);
|
||||
|
||||
virtual void start_connect(tcp::resolver::iterator endpoint_it) = 0;
|
||||
virtual tcp::socket &socket() = 0;
|
||||
virtual void read_socket(std::function<
|
||||
void(const boost::system::error_code &ec, std::size_t n)> h) = 0;
|
||||
virtual void write_socket(std::function<
|
||||
void(const boost::system::error_code &ec, std::size_t n)> h) = 0;
|
||||
virtual void shutdown_socket() = 0;
|
||||
|
||||
void shutdown();
|
||||
|
||||
boost::asio::io_service &io_service();
|
||||
|
||||
void signal_write();
|
||||
|
||||
void enter_callback();
|
||||
void leave_callback();
|
||||
|
||||
void do_read();
|
||||
void do_write();
|
||||
|
||||
protected:
|
||||
boost::array<uint8_t, 8192> rb_;
|
||||
boost::array<uint8_t, 65536> wb_;
|
||||
std::size_t wblen_;
|
||||
|
||||
private:
|
||||
bool should_stop() const;
|
||||
bool setup_session();
|
||||
|
||||
boost::asio::io_service &io_service_;
|
||||
tcp::resolver resolver_;
|
||||
|
||||
std::map<int32_t, std::unique_ptr<stream>> streams_;
|
||||
|
||||
connect_cb connect_cb_;
|
||||
error_cb error_cb_;
|
||||
|
||||
nghttp2_session *session_;
|
||||
|
||||
const uint8_t *data_pending_;
|
||||
std::size_t data_pendinglen_;
|
||||
|
||||
bool writing_;
|
||||
bool inside_callback_;
|
||||
};
|
||||
|
||||
} // namespace client
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
||||
|
||||
#endif // ASIO_CLIENT_SESSION_IMPL_H
|
||||
69
src/asio_client_session_tcp_impl.cc
Normal file
69
src/asio_client_session_tcp_impl.cc
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "asio_client_session_tcp_impl.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace client {
|
||||
|
||||
session_tcp_impl::session_tcp_impl(boost::asio::io_service &io_service,
|
||||
const std::string &host,
|
||||
const std::string &service)
|
||||
: session_impl(io_service), socket_(io_service) {
|
||||
start_resolve(host, service);
|
||||
}
|
||||
|
||||
session_tcp_impl::~session_tcp_impl() {}
|
||||
|
||||
void session_tcp_impl::start_connect(tcp::resolver::iterator endpoint_it) {
|
||||
boost::asio::async_connect(socket_, endpoint_it,
|
||||
[this](const boost::system::error_code &ec,
|
||||
tcp::resolver::iterator endpoint_it) {
|
||||
if (ec) {
|
||||
not_connected(ec);
|
||||
return;
|
||||
}
|
||||
|
||||
connected(endpoint_it);
|
||||
});
|
||||
}
|
||||
|
||||
tcp::socket &session_tcp_impl::socket() { return socket_; }
|
||||
|
||||
void session_tcp_impl::read_socket(
|
||||
std::function<void(const boost::system::error_code &ec, std::size_t n)> h) {
|
||||
socket_.async_read_some(boost::asio::buffer(rb_), h);
|
||||
}
|
||||
|
||||
void session_tcp_impl::write_socket(
|
||||
std::function<void(const boost::system::error_code &ec, std::size_t n)> h) {
|
||||
boost::asio::async_write(socket_, boost::asio::buffer(wb_, wblen_), h);
|
||||
}
|
||||
|
||||
void session_tcp_impl::shutdown_socket() { socket_.close(); }
|
||||
|
||||
} // namespace client
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
||||
60
src/asio_client_session_tcp_impl.h
Normal file
60
src/asio_client_session_tcp_impl.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef ASIO_CLIENT_SESSION_TCP_IMPL_H
|
||||
#define ASIO_CLIENT_SESSION_TCP_IMPL_H
|
||||
|
||||
#include "asio_client_session_impl.h"
|
||||
|
||||
#include <nghttp2/asio_http2_client.h>
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace client {
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
class session_tcp_impl : public session_impl {
|
||||
public:
|
||||
session_tcp_impl(boost::asio::io_service &io_service, const std::string &host,
|
||||
const std::string &service);
|
||||
virtual ~session_tcp_impl();
|
||||
|
||||
virtual void start_connect(tcp::resolver::iterator endpoint_it);
|
||||
virtual tcp::socket &socket();
|
||||
virtual void read_socket(std::function<
|
||||
void(const boost::system::error_code &ec, std::size_t n)> h);
|
||||
virtual void write_socket(std::function<
|
||||
void(const boost::system::error_code &ec, std::size_t n)> h);
|
||||
virtual void shutdown_socket();
|
||||
|
||||
private:
|
||||
tcp::socket socket_;
|
||||
};
|
||||
|
||||
} // namespace client
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
||||
|
||||
#endif // ASIO_CLIENT_SESSION_TCP_IMPL_H
|
||||
85
src/asio_client_session_tls_impl.cc
Normal file
85
src/asio_client_session_tls_impl.cc
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "asio_client_session_tls_impl.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace client {
|
||||
|
||||
session_tls_impl::session_tls_impl(boost::asio::io_service &io_service,
|
||||
boost::asio::ssl::context &tls_ctx,
|
||||
const std::string &host,
|
||||
const std::string &service)
|
||||
: session_impl(io_service), socket_(io_service, tls_ctx) {
|
||||
// this callback setting is no effect is
|
||||
// ssl::context::set_verify_mode(boost::asio::ssl::verify_peer) is
|
||||
// not used, which is what we want.
|
||||
socket_.set_verify_callback(boost::asio::ssl::rfc2818_verification(host));
|
||||
|
||||
start_resolve(host, service);
|
||||
}
|
||||
|
||||
session_tls_impl::~session_tls_impl() {}
|
||||
|
||||
void session_tls_impl::start_connect(tcp::resolver::iterator endpoint_it) {
|
||||
boost::asio::async_connect(socket(), endpoint_it,
|
||||
[this](const boost::system::error_code &ec,
|
||||
tcp::resolver::iterator endpoint_it) {
|
||||
if (ec) {
|
||||
not_connected(ec);
|
||||
return;
|
||||
}
|
||||
|
||||
socket_.async_handshake(
|
||||
boost::asio::ssl::stream_base::client,
|
||||
[this, endpoint_it](const boost::system::error_code &ec) {
|
||||
if (ec) {
|
||||
not_connected(ec);
|
||||
return;
|
||||
}
|
||||
connected(endpoint_it);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
tcp::socket &session_tls_impl::socket() { return socket_.next_layer(); }
|
||||
|
||||
void session_tls_impl::read_socket(
|
||||
std::function<void(const boost::system::error_code &ec, std::size_t n)> h) {
|
||||
socket_.async_read_some(boost::asio::buffer(rb_), h);
|
||||
}
|
||||
|
||||
void session_tls_impl::write_socket(
|
||||
std::function<void(const boost::system::error_code &ec, std::size_t n)> h) {
|
||||
boost::asio::async_write(socket_, boost::asio::buffer(wb_, wblen_), h);
|
||||
}
|
||||
|
||||
void session_tls_impl::shutdown_socket() {
|
||||
socket_.async_shutdown([](const boost::system::error_code &ec) {});
|
||||
}
|
||||
|
||||
} // namespace client
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
||||
63
src/asio_client_session_tls_impl.h
Normal file
63
src/asio_client_session_tls_impl.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef ASIO_CLIENT_SESSION_TLS_IMPL_H
|
||||
#define ASIO_CLIENT_SESSION_TLS_IMPL_H
|
||||
|
||||
#include "asio_client_session_impl.h"
|
||||
|
||||
#include <nghttp2/asio_http2_client.h>
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace client {
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
using ssl_socket = boost::asio::ssl::stream<tcp::socket>;
|
||||
|
||||
class session_tls_impl : public session_impl {
|
||||
public:
|
||||
session_tls_impl(boost::asio::io_service &io_service,
|
||||
boost::asio::ssl::context &tls_ctx, const std::string &host,
|
||||
const std::string &service);
|
||||
virtual ~session_tls_impl();
|
||||
|
||||
virtual void start_connect(tcp::resolver::iterator endpoint_it);
|
||||
virtual tcp::socket &socket();
|
||||
virtual void read_socket(std::function<
|
||||
void(const boost::system::error_code &ec, std::size_t n)> h);
|
||||
virtual void write_socket(std::function<
|
||||
void(const boost::system::error_code &ec, std::size_t n)> h);
|
||||
virtual void shutdown_socket();
|
||||
|
||||
private:
|
||||
ssl_socket socket_;
|
||||
};
|
||||
|
||||
} // namespace client
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
||||
|
||||
#endif // ASIO_CLIENT_SESSION_TLS_IMPL_H
|
||||
59
src/asio_client_stream.cc
Normal file
59
src/asio_client_stream.cc
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "asio_client_stream.h"
|
||||
|
||||
#include "asio_client_request_impl.h"
|
||||
#include "asio_client_response_impl.h"
|
||||
#include "asio_client_session_impl.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace client {
|
||||
|
||||
stream::stream(session_impl *sess) : sess_(sess), stream_id_(0) {
|
||||
request_.impl().stream(this);
|
||||
}
|
||||
|
||||
void stream::stream_id(int32_t stream_id) { stream_id_ = stream_id; }
|
||||
|
||||
int32_t stream::stream_id() const { return stream_id_; }
|
||||
|
||||
class request &stream::request() {
|
||||
return request_;
|
||||
}
|
||||
|
||||
class response &stream::response() {
|
||||
return response_;
|
||||
}
|
||||
|
||||
session_impl *stream::session() const { return sess_; }
|
||||
|
||||
bool stream::expect_final_response() const {
|
||||
return response_.status_code() / 100 == 1;
|
||||
}
|
||||
|
||||
} // namespace client
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
||||
68
src/asio_client_stream.h
Normal file
68
src/asio_client_stream.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef ASIO_CLIENT_STREAM_H
|
||||
#define ASIO_CLIENT_STREAM_H
|
||||
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <nghttp2/asio_http2_client.h>
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace client {
|
||||
|
||||
class request;
|
||||
class response;
|
||||
class session_impl;
|
||||
|
||||
class stream {
|
||||
public:
|
||||
stream(session_impl *sess);
|
||||
|
||||
stream(const stream &) = delete;
|
||||
stream &operator=(const stream &) = delete;
|
||||
|
||||
void stream_id(int32_t stream_id);
|
||||
int32_t stream_id() const;
|
||||
|
||||
class request &request();
|
||||
class response &response();
|
||||
|
||||
session_impl *session() const;
|
||||
|
||||
bool expect_final_response() const;
|
||||
|
||||
private:
|
||||
nghttp2::asio_http2::client::request request_;
|
||||
nghttp2::asio_http2::client::response response_;
|
||||
session_impl *sess_;
|
||||
uint32_t stream_id_;
|
||||
};
|
||||
|
||||
} // namespace client
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
||||
|
||||
#endif // ASIO_CLIENT_STREAM_H
|
||||
64
src/asio_client_tls_context.cc
Normal file
64
src/asio_client_tls_context.cc
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "asio_client_tls_context.h"
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
#include <boost/asio/ssl.hpp>
|
||||
|
||||
#include "ssl.h"
|
||||
#include "util.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace client {
|
||||
|
||||
namespace {
|
||||
int client_select_next_proto_cb(SSL *ssl, unsigned char **out,
|
||||
unsigned char *outlen, const unsigned char *in,
|
||||
unsigned int inlen, void *arg) {
|
||||
if (!util::select_h2(const_cast<const unsigned char **>(out), outlen, in,
|
||||
inlen)) {
|
||||
return SSL_TLSEXT_ERR_NOACK;
|
||||
}
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
boost::system::error_code
|
||||
configure_tls_context(boost::system::error_code &ec,
|
||||
boost::asio::ssl::context &tls_ctx) {
|
||||
ec.clear();
|
||||
|
||||
auto ctx = tls_ctx.native_handle();
|
||||
|
||||
SSL_CTX_set_next_proto_select_cb(ctx, client_select_next_proto_cb, nullptr);
|
||||
|
||||
return ec;
|
||||
}
|
||||
|
||||
} // namespace client
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2014 Tatsuhiro Tsujikawa
|
||||
* 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
|
||||
@@ -22,17 +22,11 @@
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "shrpx_worker_config.h"
|
||||
#ifndef ASIO_CLIENT_TLS_CONTEXT_H
|
||||
#define ASIO_CLIENT_TLS_CONTEXT_H
|
||||
|
||||
namespace shrpx {
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
WorkerConfig::WorkerConfig()
|
||||
: accesslog_fd(-1), errorlog_fd(-1), errorlog_tty(false),
|
||||
graceful_shutdown(false) {}
|
||||
#include <nghttp2/asio_http2_client.h>
|
||||
|
||||
#ifndef NOTHREADS
|
||||
thread_local
|
||||
#endif // NOTHREADS
|
||||
WorkerConfig *worker_config = new WorkerConfig();
|
||||
|
||||
} // namespace shrpx
|
||||
#endif // ASIO_CLIENT_TLS_CONTEXT_H
|
||||
148
src/asio_common.cc
Normal file
148
src/asio_common.cc
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "asio_common.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "util.h"
|
||||
#include "template.h"
|
||||
#include "http2.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
|
||||
class nghttp2_category_impl : public boost::system::error_category {
|
||||
public:
|
||||
const char *name() const noexcept { return "nghttp2"; }
|
||||
std::string message(int ev) const { return nghttp2_strerror(ev); }
|
||||
};
|
||||
|
||||
const boost::system::error_category &nghttp2_category() noexcept {
|
||||
static nghttp2_category_impl cat;
|
||||
return cat;
|
||||
}
|
||||
|
||||
boost::system::error_code make_error_code(nghttp2_error ev) {
|
||||
return boost::system::error_code(static_cast<int>(ev), nghttp2_category());
|
||||
}
|
||||
|
||||
generator_cb string_generator(std::string data) {
|
||||
auto strio = std::make_shared<std::pair<std::string, size_t>>(std::move(data),
|
||||
data.size());
|
||||
return [strio](uint8_t *buf, size_t len, uint32_t *data_flags) {
|
||||
auto &data = strio->first;
|
||||
auto &left = strio->second;
|
||||
auto n = std::min(len, left);
|
||||
std::copy_n(data.c_str() + data.size() - left, n, buf);
|
||||
left -= n;
|
||||
if (left == 0) {
|
||||
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
||||
}
|
||||
return n;
|
||||
};
|
||||
}
|
||||
|
||||
generator_cb deferred_generator() {
|
||||
return [](uint8_t *buf, size_t len,
|
||||
uint32_t *data_flags) { return NGHTTP2_ERR_DEFERRED; };
|
||||
}
|
||||
|
||||
template <typename F, typename... T>
|
||||
std::shared_ptr<Defer<F, T...>> defer_shared(F &&f, T &&... t) {
|
||||
return std::make_shared<Defer<F, T...>>(std::forward<F>(f),
|
||||
std::forward<T>(t)...);
|
||||
}
|
||||
|
||||
generator_cb file_generator(const std::string &path) {
|
||||
auto fd = open(path.c_str(), O_RDONLY);
|
||||
if (fd == -1) {
|
||||
return generator_cb();
|
||||
}
|
||||
|
||||
return file_generator_from_fd(fd);
|
||||
}
|
||||
|
||||
generator_cb file_generator_from_fd(int fd) {
|
||||
auto d = defer_shared(close, fd);
|
||||
|
||||
return [fd, d](uint8_t *buf, size_t len, uint32_t *data_flags)
|
||||
-> generator_cb::result_type {
|
||||
ssize_t n;
|
||||
while ((n = read(fd, buf, len)) == -1 && errno == EINTR)
|
||||
;
|
||||
|
||||
if (n == -1) {
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
if (n == 0) {
|
||||
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
||||
}
|
||||
|
||||
return n;
|
||||
};
|
||||
}
|
||||
|
||||
bool check_path(const std::string &path) { return util::check_path(path); }
|
||||
|
||||
std::string percent_decode(const std::string &s) {
|
||||
return util::percentDecode(std::begin(s), std::end(s));
|
||||
}
|
||||
|
||||
std::string http_date(int64_t t) { return util::http_date(t); }
|
||||
|
||||
boost::system::error_code host_service_from_uri(boost::system::error_code &ec,
|
||||
std::string &scheme,
|
||||
std::string &host,
|
||||
std::string &service,
|
||||
const std::string &uri) {
|
||||
ec.clear();
|
||||
|
||||
http_parser_url u{};
|
||||
if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) {
|
||||
ec = make_error_code(boost::system::errc::invalid_argument);
|
||||
return ec;
|
||||
}
|
||||
|
||||
if ((u.field_set & (1 << UF_SCHEMA)) == 0 ||
|
||||
(u.field_set & (1 << UF_HOST)) == 0) {
|
||||
ec = make_error_code(boost::system::errc::invalid_argument);
|
||||
return ec;
|
||||
}
|
||||
|
||||
http2::copy_url_component(scheme, &u, UF_SCHEMA, uri.c_str());
|
||||
http2::copy_url_component(host, &u, UF_HOST, uri.c_str());
|
||||
|
||||
if (u.field_set & (1 << UF_PORT)) {
|
||||
http2::copy_url_component(service, &u, UF_PORT, uri.c_str());
|
||||
} else {
|
||||
service = scheme;
|
||||
}
|
||||
|
||||
return ec;
|
||||
}
|
||||
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
||||
65
src/asio_common.h
Normal file
65
src/asio_common.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef ASIO_COMMON_H
|
||||
#define ASIO_COMMON_H
|
||||
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <nghttp2/asio_http2.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
namespace asio_http2 {
|
||||
|
||||
boost::system::error_code make_error_code(nghttp2_error ev);
|
||||
|
||||
generator_cb string_generator(std::string data);
|
||||
|
||||
// Returns generator_cb, which just returns NGHTTP2_ERR_DEFERRED
|
||||
generator_cb deferred_generator();
|
||||
|
||||
template <typename InputIt>
|
||||
void split_path(uri_ref &dst, InputIt first, InputIt last) {
|
||||
auto path_last = std::find(first, last, '?');
|
||||
InputIt query_first;
|
||||
if (path_last == last) {
|
||||
query_first = path_last = last;
|
||||
} else {
|
||||
query_first = path_last + 1;
|
||||
}
|
||||
dst.path = util::percentDecode(first, path_last);
|
||||
dst.raw_path.assign(first, path_last);
|
||||
dst.raw_query.assign(query_first, last);
|
||||
}
|
||||
|
||||
} // namespace asio_http2
|
||||
|
||||
} // namespace nghttp2
|
||||
|
||||
#endif // ASIO_COMMON_H
|
||||
@@ -1,745 +0,0 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2014 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "asio_http2_handler.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "http2.h"
|
||||
#include "util.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
namespace asio_http2 {
|
||||
|
||||
channel::channel() : impl_(util::make_unique<channel_impl>()) {}
|
||||
|
||||
void channel::post(void_cb cb) { impl_->post(std::move(cb)); }
|
||||
|
||||
channel_impl &channel::impl() { return *impl_; }
|
||||
|
||||
channel_impl::channel_impl() : strand_(nullptr) {}
|
||||
|
||||
void channel_impl::post(void_cb cb) { strand_->post(std::move(cb)); }
|
||||
|
||||
void channel_impl::strand(boost::asio::io_service::strand *strand) {
|
||||
strand_ = strand;
|
||||
}
|
||||
|
||||
namespace server {
|
||||
|
||||
extern std::shared_ptr<std::string> cached_date;
|
||||
|
||||
request::request() : impl_(util::make_unique<request_impl>()) {}
|
||||
|
||||
const std::vector<header> &request::headers() const { return impl_->headers(); }
|
||||
|
||||
const std::string &request::method() const { return impl_->method(); }
|
||||
|
||||
const std::string &request::scheme() const { return impl_->scheme(); }
|
||||
|
||||
const std::string &request::authority() const { return impl_->authority(); }
|
||||
|
||||
const std::string &request::host() const { return impl_->host(); }
|
||||
|
||||
const std::string &request::path() const { return impl_->path(); }
|
||||
|
||||
bool request::push(std::string method, std::string path,
|
||||
std::vector<header> headers) {
|
||||
return impl_->push(std::move(method), std::move(path), std::move(headers));
|
||||
}
|
||||
|
||||
bool request::pushed() const { return impl_->pushed(); }
|
||||
|
||||
bool request::closed() const { return impl_->closed(); }
|
||||
|
||||
void request::on_data(data_cb cb) { return impl_->on_data(std::move(cb)); }
|
||||
|
||||
void request::on_end(void_cb cb) { return impl_->on_end(std::move(cb)); }
|
||||
|
||||
bool request::run_task(thread_cb start) {
|
||||
return impl_->run_task(std::move(start));
|
||||
}
|
||||
|
||||
request_impl &request::impl() { return *impl_; }
|
||||
|
||||
response::response() : impl_(util::make_unique<response_impl>()) {}
|
||||
|
||||
void response::write_head(unsigned int status_code,
|
||||
std::vector<header> headers) {
|
||||
impl_->write_head(status_code, std::move(headers));
|
||||
}
|
||||
|
||||
void response::end(std::string data) { impl_->end(std::move(data)); }
|
||||
|
||||
void response::end(read_cb cb) { impl_->end(std::move(cb)); }
|
||||
|
||||
void response::resume() { impl_->resume(); }
|
||||
|
||||
unsigned int response::status_code() const { return impl_->status_code(); }
|
||||
|
||||
bool response::started() const { return impl_->started(); }
|
||||
|
||||
response_impl &response::impl() { return *impl_; }
|
||||
|
||||
request_impl::request_impl() : pushed_(false) {}
|
||||
|
||||
const std::vector<header> &request_impl::headers() const { return headers_; }
|
||||
|
||||
const std::string &request_impl::method() const { return method_; }
|
||||
|
||||
const std::string &request_impl::scheme() const { return scheme_; }
|
||||
|
||||
const std::string &request_impl::authority() const { return authority_; }
|
||||
|
||||
const std::string &request_impl::host() const { return host_; }
|
||||
|
||||
const std::string &request_impl::path() const { return path_; }
|
||||
|
||||
void request_impl::set_header(std::vector<header> headers) {
|
||||
headers_ = std::move(headers);
|
||||
}
|
||||
|
||||
void request_impl::add_header(std::string name, std::string value) {
|
||||
headers_.push_back(header{std::move(name), std::move(value)});
|
||||
}
|
||||
|
||||
void request_impl::method(std::string arg) { method_ = std::move(arg); }
|
||||
|
||||
void request_impl::scheme(std::string arg) { scheme_ = std::move(arg); }
|
||||
|
||||
void request_impl::authority(std::string arg) { authority_ = std::move(arg); }
|
||||
|
||||
void request_impl::host(std::string arg) { host_ = std::move(arg); }
|
||||
|
||||
void request_impl::path(std::string arg) { path_ = std::move(arg); }
|
||||
|
||||
bool request_impl::push(std::string method, std::string path,
|
||||
std::vector<header> headers) {
|
||||
if (closed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto handler = handler_.lock();
|
||||
auto stream = stream_.lock();
|
||||
auto rv = handler->push_promise(*stream, std::move(method), std::move(path),
|
||||
std::move(headers));
|
||||
return rv == 0;
|
||||
}
|
||||
|
||||
bool request_impl::pushed() const { return pushed_; }
|
||||
|
||||
void request_impl::pushed(bool f) { pushed_ = f; }
|
||||
|
||||
bool request_impl::closed() const {
|
||||
return handler_.expired() || stream_.expired();
|
||||
}
|
||||
|
||||
void request_impl::on_data(data_cb cb) { on_data_cb_ = std::move(cb); }
|
||||
|
||||
void request_impl::on_end(void_cb cb) { on_end_cb_ = std::move(cb); }
|
||||
|
||||
bool request_impl::run_task(thread_cb start) {
|
||||
if (closed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto handler = handler_.lock();
|
||||
|
||||
return handler->run_task(std::move(start));
|
||||
}
|
||||
|
||||
void request_impl::handler(std::weak_ptr<http2_handler> h) {
|
||||
handler_ = std::move(h);
|
||||
}
|
||||
|
||||
void request_impl::stream(std::weak_ptr<http2_stream> s) {
|
||||
stream_ = std::move(s);
|
||||
}
|
||||
|
||||
void request_impl::call_on_data(const uint8_t *data, std::size_t len) {
|
||||
if (on_data_cb_) {
|
||||
on_data_cb_(data, len);
|
||||
}
|
||||
}
|
||||
|
||||
void request_impl::call_on_end() {
|
||||
if (on_end_cb_) {
|
||||
on_end_cb_();
|
||||
}
|
||||
}
|
||||
|
||||
response_impl::response_impl() : status_code_(200), started_(false) {}
|
||||
|
||||
unsigned int response_impl::status_code() const { return status_code_; }
|
||||
|
||||
void response_impl::write_head(unsigned int status_code,
|
||||
std::vector<header> headers) {
|
||||
status_code_ = status_code;
|
||||
headers_ = std::move(headers);
|
||||
}
|
||||
|
||||
void response_impl::end(std::string data) {
|
||||
if (started_) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto strio = std::make_shared<std::pair<std::string, size_t>>(std::move(data),
|
||||
data.size());
|
||||
auto read_cb = [strio](uint8_t *buf, size_t len) {
|
||||
auto nread = std::min(len, strio->second);
|
||||
memcpy(buf, strio->first.c_str(), nread);
|
||||
strio->second -= nread;
|
||||
if (strio->second == 0) {
|
||||
return std::make_pair(nread, true);
|
||||
}
|
||||
|
||||
return std::make_pair(nread, false);
|
||||
};
|
||||
|
||||
end(std::move(read_cb));
|
||||
}
|
||||
|
||||
void response_impl::end(read_cb cb) {
|
||||
if (started_ || closed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
read_cb_ = std::move(cb);
|
||||
started_ = true;
|
||||
|
||||
auto handler = handler_.lock();
|
||||
auto stream = stream_.lock();
|
||||
|
||||
if (handler->start_response(*stream) != 0) {
|
||||
handler->stream_error(stream->get_stream_id(), NGHTTP2_INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!handler->inside_callback()) {
|
||||
handler->initiate_write();
|
||||
}
|
||||
}
|
||||
|
||||
bool response_impl::closed() const {
|
||||
return handler_.expired() || stream_.expired();
|
||||
}
|
||||
|
||||
void response_impl::resume() {
|
||||
if (closed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto handler = handler_.lock();
|
||||
auto stream = stream_.lock();
|
||||
handler->resume(*stream);
|
||||
|
||||
if (!handler->inside_callback()) {
|
||||
handler->initiate_write();
|
||||
}
|
||||
}
|
||||
|
||||
bool response_impl::started() const { return started_; }
|
||||
|
||||
const std::vector<header> &response_impl::headers() const { return headers_; }
|
||||
|
||||
void response_impl::handler(std::weak_ptr<http2_handler> h) {
|
||||
handler_ = std::move(h);
|
||||
}
|
||||
|
||||
void response_impl::stream(std::weak_ptr<http2_stream> s) {
|
||||
stream_ = std::move(s);
|
||||
}
|
||||
|
||||
std::pair<ssize_t, bool> response_impl::call_read(uint8_t *data,
|
||||
std::size_t len) {
|
||||
if (read_cb_) {
|
||||
return read_cb_(data, len);
|
||||
}
|
||||
|
||||
return std::make_pair(0, true);
|
||||
}
|
||||
|
||||
http2_stream::http2_stream(int32_t stream_id)
|
||||
: request_(std::make_shared<request>()),
|
||||
response_(std::make_shared<response>()), stream_id_(stream_id) {}
|
||||
|
||||
int32_t http2_stream::get_stream_id() const { return stream_id_; }
|
||||
|
||||
const std::shared_ptr<request> &http2_stream::get_request() { return request_; }
|
||||
|
||||
const std::shared_ptr<response> &http2_stream::get_response() {
|
||||
return response_;
|
||||
}
|
||||
|
||||
namespace {
|
||||
int stream_error(nghttp2_session *session, int32_t stream_id,
|
||||
uint32_t error_code) {
|
||||
return nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id,
|
||||
error_code);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int on_begin_headers_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame, void *user_data) {
|
||||
auto handler = static_cast<http2_handler *>(user_data);
|
||||
|
||||
if (frame->hd.type != NGHTTP2_HEADERS ||
|
||||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
handler->create_stream(frame->hd.stream_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||
const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen, uint8_t flags,
|
||||
void *user_data) {
|
||||
auto handler = static_cast<http2_handler *>(user_data);
|
||||
auto stream_id = frame->hd.stream_id;
|
||||
|
||||
if (frame->hd.type != NGHTTP2_HEADERS ||
|
||||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto stream = handler->find_stream(stream_id);
|
||||
if (!stream) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!nghttp2_check_header_name(name, namelen) ||
|
||||
!nghttp2_check_header_value(value, valuelen)) {
|
||||
stream_error(session, stream_id, NGHTTP2_PROTOCOL_ERROR);
|
||||
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
auto &req = stream->get_request()->impl();
|
||||
|
||||
if (name[0] == ':' && !req.headers().empty()) {
|
||||
stream_error(session, stream_id, NGHTTP2_PROTOCOL_ERROR);
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
if (util::streq(":method", name, namelen)) {
|
||||
if (!req.method().empty()) {
|
||||
stream_error(session, stream_id, NGHTTP2_PROTOCOL_ERROR);
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
req.method(std::string(value, value + valuelen));
|
||||
} else if (util::streq(":scheme", name, namelen)) {
|
||||
if (!req.scheme().empty()) {
|
||||
stream_error(session, stream_id, NGHTTP2_PROTOCOL_ERROR);
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
req.scheme(std::string(value, value + valuelen));
|
||||
} else if (util::streq(":authority", name, namelen)) {
|
||||
if (!req.authority().empty()) {
|
||||
stream_error(session, stream_id, NGHTTP2_PROTOCOL_ERROR);
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
req.authority(std::string(value, value + valuelen));
|
||||
} else if (util::streq(":path", name, namelen)) {
|
||||
if (!req.path().empty()) {
|
||||
stream_error(session, stream_id, NGHTTP2_PROTOCOL_ERROR);
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
req.path(std::string(value, value + valuelen));
|
||||
} else {
|
||||
if (name[0] == ':') {
|
||||
stream_error(session, stream_id, NGHTTP2_PROTOCOL_ERROR);
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
if (util::streq("host", name, namelen)) {
|
||||
req.host(std::string(value, value + valuelen));
|
||||
}
|
||||
|
||||
req.add_header(std::string(name, name + namelen),
|
||||
std::string(value, value + valuelen));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||
void *user_data) {
|
||||
auto handler = static_cast<http2_handler *>(user_data);
|
||||
auto stream = handler->find_stream(frame->hd.stream_id);
|
||||
|
||||
switch (frame->hd.type) {
|
||||
case NGHTTP2_DATA:
|
||||
if (!stream) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||
stream->get_request()->impl().call_on_end();
|
||||
}
|
||||
|
||||
break;
|
||||
case NGHTTP2_HEADERS: {
|
||||
if (!stream || frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
||||
break;
|
||||
}
|
||||
|
||||
auto &req = stream->get_request()->impl();
|
||||
|
||||
if (req.method().empty() || req.scheme().empty() || req.path().empty() ||
|
||||
(req.authority().empty() && req.host().empty())) {
|
||||
stream_error(session, frame->hd.stream_id, NGHTTP2_PROTOCOL_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (req.host().empty()) {
|
||||
req.host(req.authority());
|
||||
}
|
||||
|
||||
handler->call_on_request(*stream);
|
||||
|
||||
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||
stream->get_request()->impl().call_on_end();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
||||
int32_t stream_id, const uint8_t *data,
|
||||
size_t len, void *user_data) {
|
||||
auto handler = static_cast<http2_handler *>(user_data);
|
||||
auto stream = handler->find_stream(stream_id);
|
||||
|
||||
if (!stream) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
stream->get_request()->impl().call_on_data(data, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||
uint32_t error_code, void *user_data) {
|
||||
auto handler = static_cast<http2_handler *>(user_data);
|
||||
|
||||
handler->close_stream(stream_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||
void *user_data) {
|
||||
auto handler = static_cast<http2_handler *>(user_data);
|
||||
|
||||
if (frame->hd.type != NGHTTP2_PUSH_PROMISE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto stream = handler->find_stream(frame->push_promise.promised_stream_id);
|
||||
|
||||
if (!stream) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
handler->call_on_request(*stream);
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int on_frame_not_send_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame, int lib_error_code,
|
||||
void *user_data) {
|
||||
if (frame->hd.type != NGHTTP2_HEADERS) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Issue RST_STREAM so that stream does not hang around.
|
||||
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id,
|
||||
NGHTTP2_INTERNAL_ERROR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
http2_handler::http2_handler(boost::asio::io_service &io_service,
|
||||
boost::asio::io_service &task_io_service_,
|
||||
connection_write writefun, request_cb cb)
|
||||
: writefun_(writefun), request_cb_(std::move(cb)), io_service_(io_service),
|
||||
task_io_service_(task_io_service_),
|
||||
strand_(std::make_shared<boost::asio::io_service::strand>(io_service_)),
|
||||
session_(nullptr), buf_(nullptr), buflen_(0), inside_callback_(false) {}
|
||||
|
||||
http2_handler::~http2_handler() { nghttp2_session_del(session_); }
|
||||
|
||||
int http2_handler::start() {
|
||||
int rv;
|
||||
|
||||
nghttp2_session_callbacks *callbacks;
|
||||
rv = nghttp2_session_callbacks_new(&callbacks);
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto cb_del = util::defer(callbacks, nghttp2_session_callbacks_del);
|
||||
|
||||
nghttp2_session_callbacks_set_on_begin_headers_callback(
|
||||
callbacks, on_begin_headers_callback);
|
||||
nghttp2_session_callbacks_set_on_header_callback(callbacks,
|
||||
on_header_callback);
|
||||
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
|
||||
on_frame_recv_callback);
|
||||
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
|
||||
callbacks, on_data_chunk_recv_callback);
|
||||
nghttp2_session_callbacks_set_on_stream_close_callback(
|
||||
callbacks, on_stream_close_callback);
|
||||
nghttp2_session_callbacks_set_on_frame_send_callback(callbacks,
|
||||
on_frame_send_callback);
|
||||
nghttp2_session_callbacks_set_on_frame_not_send_callback(
|
||||
callbacks, on_frame_not_send_callback);
|
||||
|
||||
nghttp2_option *option;
|
||||
rv = nghttp2_option_new(&option);
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto opt_del = util::defer(option, nghttp2_option_del);
|
||||
|
||||
nghttp2_option_set_recv_client_preface(option, 1);
|
||||
|
||||
rv = nghttp2_session_server_new2(&session_, callbacks, this, option);
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
nghttp2_settings_entry ent{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100};
|
||||
nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, &ent, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::shared_ptr<http2_stream> http2_handler::create_stream(int32_t stream_id) {
|
||||
auto stream = std::make_shared<http2_stream>(stream_id);
|
||||
streams_.emplace(stream_id, stream);
|
||||
|
||||
auto self = shared_from_this();
|
||||
auto &req = stream->get_request()->impl();
|
||||
auto &res = stream->get_response()->impl();
|
||||
req.handler(self);
|
||||
req.stream(stream);
|
||||
res.handler(self);
|
||||
res.stream(stream);
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
void http2_handler::close_stream(int32_t stream_id) {
|
||||
streams_.erase(stream_id);
|
||||
}
|
||||
|
||||
std::shared_ptr<http2_stream> http2_handler::find_stream(int32_t stream_id) {
|
||||
auto i = streams_.find(stream_id);
|
||||
if (i == std::end(streams_)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return (*i).second;
|
||||
}
|
||||
|
||||
void http2_handler::call_on_request(http2_stream &stream) {
|
||||
request_cb_(stream.get_request(), stream.get_response());
|
||||
}
|
||||
|
||||
bool http2_handler::should_stop() const {
|
||||
return !nghttp2_session_want_read(session_) &&
|
||||
!nghttp2_session_want_write(session_);
|
||||
}
|
||||
|
||||
int http2_handler::start_response(http2_stream &stream) {
|
||||
int rv;
|
||||
|
||||
auto &res = stream.get_response()->impl();
|
||||
auto &headers = res.headers();
|
||||
auto nva = std::vector<nghttp2_nv>();
|
||||
nva.reserve(2 + headers.size());
|
||||
auto status = util::utos(res.status_code());
|
||||
auto date = cached_date;
|
||||
nva.push_back(nghttp2::http2::make_nv_ls(":status", status));
|
||||
nva.push_back(nghttp2::http2::make_nv_ls("date", *date));
|
||||
for (auto &hd : headers) {
|
||||
nva.push_back(nghttp2::http2::make_nv(hd.name, hd.value));
|
||||
}
|
||||
|
||||
nghttp2_data_provider prd;
|
||||
prd.source.ptr = &stream;
|
||||
prd.read_callback =
|
||||
[](nghttp2_session *session, int32_t stream_id, uint8_t *buf,
|
||||
size_t length, uint32_t *data_flags, nghttp2_data_source *source,
|
||||
void *user_data) -> ssize_t {
|
||||
auto &stream = *static_cast<http2_stream *>(source->ptr);
|
||||
auto rv = stream.get_response()->impl().call_read(buf, length);
|
||||
if (rv.first < 0) {
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
if (rv.second) {
|
||||
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
||||
} else if (rv.first == 0) {
|
||||
return NGHTTP2_ERR_DEFERRED;
|
||||
}
|
||||
|
||||
return rv.first;
|
||||
};
|
||||
|
||||
rv = nghttp2_submit_response(session_, stream.get_stream_id(), nva.data(),
|
||||
nva.size(), &prd);
|
||||
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void http2_handler::enter_callback() {
|
||||
assert(!inside_callback_);
|
||||
inside_callback_ = true;
|
||||
}
|
||||
|
||||
void http2_handler::leave_callback() {
|
||||
assert(inside_callback_);
|
||||
inside_callback_ = false;
|
||||
}
|
||||
|
||||
bool http2_handler::inside_callback() const { return inside_callback_; }
|
||||
|
||||
void http2_handler::stream_error(int32_t stream_id, uint32_t error_code) {
|
||||
::nghttp2::asio_http2::server::stream_error(session_, stream_id, error_code);
|
||||
}
|
||||
|
||||
void http2_handler::initiate_write() { writefun_(); }
|
||||
|
||||
void http2_handler::resume(http2_stream &stream) {
|
||||
nghttp2_session_resume_data(session_, stream.get_stream_id());
|
||||
}
|
||||
|
||||
int http2_handler::push_promise(http2_stream &stream, std::string method,
|
||||
std::string path, std::vector<header> headers) {
|
||||
int rv;
|
||||
|
||||
auto &req = stream.get_request()->impl();
|
||||
|
||||
auto nva = std::vector<nghttp2_nv>();
|
||||
nva.reserve(5 + headers.size());
|
||||
nva.push_back(nghttp2::http2::make_nv_ls(":method", method));
|
||||
nva.push_back(nghttp2::http2::make_nv_ls(":scheme", req.scheme()));
|
||||
if (!req.authority().empty()) {
|
||||
nva.push_back(nghttp2::http2::make_nv_ls(":authority", req.authority()));
|
||||
}
|
||||
nva.push_back(nghttp2::http2::make_nv_ls(":path", path));
|
||||
if (!req.host().empty()) {
|
||||
nva.push_back(nghttp2::http2::make_nv_ls("host", req.host()));
|
||||
}
|
||||
|
||||
for (auto &hd : headers) {
|
||||
nva.push_back(nghttp2::http2::make_nv(hd.name, hd.value));
|
||||
}
|
||||
|
||||
rv = nghttp2_submit_push_promise(session_, NGHTTP2_FLAG_NONE,
|
||||
stream.get_stream_id(), nva.data(),
|
||||
nva.size(), nullptr);
|
||||
|
||||
if (rv < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto promised_stream = create_stream(rv);
|
||||
auto &promised_req = promised_stream->get_request()->impl();
|
||||
promised_req.pushed(true);
|
||||
promised_req.method(std::move(method));
|
||||
promised_req.scheme(req.scheme());
|
||||
promised_req.authority(req.authority());
|
||||
promised_req.path(std::move(path));
|
||||
promised_req.host(req.host());
|
||||
promised_req.set_header(std::move(headers));
|
||||
if (!req.host().empty()) {
|
||||
promised_req.add_header("host", req.host());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool http2_handler::run_task(thread_cb start) {
|
||||
auto strand = strand_;
|
||||
|
||||
try {
|
||||
task_io_service_.post([start, strand]() {
|
||||
channel chan;
|
||||
chan.impl().strand(strand.get());
|
||||
|
||||
start(chan);
|
||||
});
|
||||
|
||||
return true;
|
||||
} catch (std::exception &ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
boost::asio::io_service &http2_handler::io_service() { return io_service_; }
|
||||
|
||||
callback_guard::callback_guard(http2_handler &h) : handler(h) {
|
||||
handler.enter_callback();
|
||||
}
|
||||
|
||||
callback_guard::~callback_guard() { handler.leave_callback(); }
|
||||
|
||||
} // namespace server
|
||||
|
||||
} // namespace asio_http2
|
||||
|
||||
} // namespace nghttp2
|
||||
@@ -1,265 +0,0 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2014 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef HTTP2_HANDLER_H
|
||||
#define HTTP2_HANDLER_H
|
||||
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <boost/array.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
#include <nghttp2/asio_http2.h>
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
|
||||
class channel_impl {
|
||||
public:
|
||||
channel_impl();
|
||||
void post(void_cb cb);
|
||||
void strand(boost::asio::io_service::strand *strand);
|
||||
|
||||
private:
|
||||
boost::asio::io_service::strand *strand_;
|
||||
};
|
||||
|
||||
namespace server {
|
||||
|
||||
class http2_handler;
|
||||
class http2_stream;
|
||||
|
||||
class request_impl {
|
||||
public:
|
||||
request_impl();
|
||||
|
||||
const std::vector<header> &headers() const;
|
||||
const std::string &method() const;
|
||||
const std::string &scheme() const;
|
||||
const std::string &authority() const;
|
||||
const std::string &host() const;
|
||||
const std::string &path() const;
|
||||
|
||||
bool push(std::string method, std::string path,
|
||||
std::vector<header> headers = {});
|
||||
|
||||
bool pushed() const;
|
||||
bool closed() const;
|
||||
|
||||
void on_data(data_cb cb);
|
||||
void on_end(void_cb cb);
|
||||
|
||||
bool run_task(thread_cb start);
|
||||
|
||||
void set_header(std::vector<header> headers);
|
||||
void add_header(std::string name, std::string value);
|
||||
void method(std::string method);
|
||||
void scheme(std::string scheme);
|
||||
void authority(std::string authority);
|
||||
void host(std::string host);
|
||||
void path(std::string path);
|
||||
void pushed(bool f);
|
||||
void handler(std::weak_ptr<http2_handler> h);
|
||||
void stream(std::weak_ptr<http2_stream> s);
|
||||
void call_on_data(const uint8_t *data, std::size_t len);
|
||||
void call_on_end();
|
||||
|
||||
private:
|
||||
std::vector<header> headers_;
|
||||
std::string method_;
|
||||
std::string scheme_;
|
||||
std::string authority_;
|
||||
std::string host_;
|
||||
std::string path_;
|
||||
data_cb on_data_cb_;
|
||||
void_cb on_end_cb_;
|
||||
std::weak_ptr<http2_handler> handler_;
|
||||
std::weak_ptr<http2_stream> stream_;
|
||||
bool pushed_;
|
||||
};
|
||||
|
||||
class response_impl {
|
||||
public:
|
||||
response_impl();
|
||||
void write_head(unsigned int status_code, std::vector<header> headers = {});
|
||||
void end(std::string data = "");
|
||||
void end(read_cb cb);
|
||||
void resume();
|
||||
bool closed() const;
|
||||
|
||||
unsigned int status_code() const;
|
||||
const std::vector<header> &headers() const;
|
||||
bool started() const;
|
||||
void handler(std::weak_ptr<http2_handler> h);
|
||||
void stream(std::weak_ptr<http2_stream> s);
|
||||
read_cb::result_type call_read(uint8_t *data, std::size_t len);
|
||||
|
||||
private:
|
||||
std::vector<header> headers_;
|
||||
read_cb read_cb_;
|
||||
std::weak_ptr<http2_handler> handler_;
|
||||
std::weak_ptr<http2_stream> stream_;
|
||||
unsigned int status_code_;
|
||||
bool started_;
|
||||
};
|
||||
|
||||
class http2_stream {
|
||||
public:
|
||||
http2_stream(int32_t stream_id);
|
||||
|
||||
int32_t get_stream_id() const;
|
||||
const std::shared_ptr<request> &get_request();
|
||||
const std::shared_ptr<response> &get_response();
|
||||
|
||||
private:
|
||||
std::shared_ptr<request> request_;
|
||||
std::shared_ptr<response> response_;
|
||||
int32_t stream_id_;
|
||||
};
|
||||
|
||||
struct callback_guard {
|
||||
callback_guard(http2_handler &h);
|
||||
~callback_guard();
|
||||
http2_handler &handler;
|
||||
};
|
||||
|
||||
typedef std::function<void(void)> connection_write;
|
||||
|
||||
class http2_handler : public std::enable_shared_from_this<http2_handler> {
|
||||
public:
|
||||
http2_handler(boost::asio::io_service &io_service,
|
||||
boost::asio::io_service &task_io_service,
|
||||
connection_write writefun, request_cb cb);
|
||||
|
||||
~http2_handler();
|
||||
|
||||
int start();
|
||||
|
||||
std::shared_ptr<http2_stream> create_stream(int32_t stream_id);
|
||||
void close_stream(int32_t stream_id);
|
||||
std::shared_ptr<http2_stream> find_stream(int32_t stream_id);
|
||||
|
||||
void call_on_request(http2_stream &stream);
|
||||
|
||||
bool should_stop() const;
|
||||
|
||||
int start_response(http2_stream &stream);
|
||||
|
||||
void stream_error(int32_t stream_id, uint32_t error_code);
|
||||
|
||||
void initiate_write();
|
||||
|
||||
void enter_callback();
|
||||
void leave_callback();
|
||||
bool inside_callback() const;
|
||||
|
||||
void resume(http2_stream &stream);
|
||||
|
||||
int push_promise(http2_stream &stream, std::string method, std::string path,
|
||||
std::vector<header> headers);
|
||||
|
||||
bool run_task(thread_cb start);
|
||||
|
||||
boost::asio::io_service &io_service();
|
||||
|
||||
template <size_t N>
|
||||
int on_read(const boost::array<uint8_t, N> &buffer, std::size_t len) {
|
||||
callback_guard cg(*this);
|
||||
|
||||
int rv;
|
||||
|
||||
rv = nghttp2_session_mem_recv(session_, buffer.data(), len);
|
||||
|
||||
if (rv < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
int on_write(boost::array<uint8_t, N> &buffer, std::size_t &len) {
|
||||
callback_guard cg(*this);
|
||||
|
||||
len = 0;
|
||||
|
||||
if (buf_) {
|
||||
std::copy(buf_, buf_ + buflen_, std::begin(buffer));
|
||||
|
||||
len += buflen_;
|
||||
|
||||
buf_ = nullptr;
|
||||
buflen_ = 0;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
const uint8_t *data;
|
||||
auto nread = nghttp2_session_mem_send(session_, &data);
|
||||
if (nread < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (nread == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (len + nread > buffer.size()) {
|
||||
buf_ = data;
|
||||
buflen_ = nread;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
std::copy(data, data + nread, std::begin(buffer) + len);
|
||||
|
||||
len += nread;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<int32_t, std::shared_ptr<http2_stream>> streams_;
|
||||
connection_write writefun_;
|
||||
request_cb request_cb_;
|
||||
boost::asio::io_service &io_service_;
|
||||
boost::asio::io_service &task_io_service_;
|
||||
std::shared_ptr<boost::asio::io_service::strand> strand_;
|
||||
nghttp2_session *session_;
|
||||
const uint8_t *buf_;
|
||||
std::size_t buflen_;
|
||||
bool inside_callback_;
|
||||
};
|
||||
|
||||
} // namespace server
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp
|
||||
|
||||
#endif // HTTP2_HANDLER_H
|
||||
@@ -1,182 +0,0 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2014 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "asio_http2_impl.h"
|
||||
|
||||
#include <boost/asio/ssl.hpp>
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
#include "asio_server.h"
|
||||
#include "util.h"
|
||||
#include "ssl.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
namespace asio_http2 {
|
||||
|
||||
namespace server {
|
||||
|
||||
http2::http2() : impl_(util::make_unique<http2_impl>()) {}
|
||||
|
||||
http2::~http2() {}
|
||||
|
||||
void http2::listen(const std::string &address, uint16_t port, request_cb cb) {
|
||||
impl_->listen(address, port, std::move(cb));
|
||||
}
|
||||
|
||||
void http2::num_threads(size_t num_threads) { impl_->num_threads(num_threads); }
|
||||
|
||||
void http2::tls(std::string private_key_file, std::string certificate_file) {
|
||||
impl_->tls(std::move(private_key_file), std::move(certificate_file));
|
||||
}
|
||||
|
||||
void http2::num_concurrent_tasks(size_t num_concurrent_tasks) {
|
||||
impl_->num_concurrent_tasks(num_concurrent_tasks);
|
||||
}
|
||||
|
||||
void http2::backlog(int backlog) { impl_->backlog(backlog); }
|
||||
|
||||
http2_impl::http2_impl()
|
||||
: num_threads_(1), num_concurrent_tasks_(1), backlog_(-1) {}
|
||||
|
||||
namespace {
|
||||
std::vector<unsigned char> &get_alpn_token() {
|
||||
static auto alpn_token = util::get_default_alpn();
|
||||
return alpn_token;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void http2_impl::listen(const std::string &address, uint16_t port,
|
||||
request_cb cb) {
|
||||
std::unique_ptr<boost::asio::ssl::context> ssl_ctx;
|
||||
|
||||
if (!private_key_file_.empty() && !certificate_file_.empty()) {
|
||||
ssl_ctx = util::make_unique<boost::asio::ssl::context>(
|
||||
boost::asio::ssl::context::sslv23);
|
||||
|
||||
ssl_ctx->use_private_key_file(private_key_file_,
|
||||
boost::asio::ssl::context::pem);
|
||||
ssl_ctx->use_certificate_chain_file(certificate_file_);
|
||||
|
||||
auto ctx = ssl_ctx->native_handle();
|
||||
|
||||
SSL_CTX_set_options(ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
|
||||
SSL_OP_NO_COMPRESSION |
|
||||
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION |
|
||||
SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_TICKET |
|
||||
SSL_OP_CIPHER_SERVER_PREFERENCE);
|
||||
SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
|
||||
SSL_CTX_set_mode(ctx, SSL_MODE_RELEASE_BUFFERS);
|
||||
SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
|
||||
|
||||
SSL_CTX_set_cipher_list(ctx, ssl::DEFAULT_CIPHER_LIST);
|
||||
|
||||
auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
|
||||
if (ecdh) {
|
||||
SSL_CTX_set_tmp_ecdh(ctx, ecdh);
|
||||
EC_KEY_free(ecdh);
|
||||
}
|
||||
|
||||
SSL_CTX_set_next_protos_advertised_cb(
|
||||
ctx,
|
||||
[](SSL *s, const unsigned char **data, unsigned int *len, void *arg) {
|
||||
auto &token = get_alpn_token();
|
||||
|
||||
*data = token.data();
|
||||
*len = token.size();
|
||||
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
},
|
||||
nullptr);
|
||||
}
|
||||
|
||||
server(address, port, num_threads_, num_concurrent_tasks_, std::move(cb),
|
||||
std::move(ssl_ctx), backlog_).run();
|
||||
}
|
||||
|
||||
void http2_impl::num_threads(size_t num_threads) { num_threads_ = num_threads; }
|
||||
|
||||
void http2_impl::tls(std::string private_key_file,
|
||||
std::string certificate_file) {
|
||||
private_key_file_ = std::move(private_key_file);
|
||||
certificate_file_ = std::move(certificate_file);
|
||||
}
|
||||
|
||||
void http2_impl::num_concurrent_tasks(size_t num_concurrent_tasks) {
|
||||
num_concurrent_tasks_ = num_concurrent_tasks;
|
||||
}
|
||||
|
||||
void http2_impl::backlog(int backlog) { backlog_ = backlog; }
|
||||
|
||||
} // namespace server
|
||||
|
||||
template <typename T, typename F>
|
||||
std::shared_ptr<util::Defer<T, F>> defer_shared(T &&t, F f) {
|
||||
return std::make_shared<util::Defer<T, F>>(std::forward<T>(t),
|
||||
std::forward<F>(f));
|
||||
}
|
||||
|
||||
read_cb file_reader(const std::string &path) {
|
||||
auto fd = open(path.c_str(), O_RDONLY);
|
||||
if (fd == -1) {
|
||||
return read_cb();
|
||||
}
|
||||
|
||||
return file_reader_from_fd(fd);
|
||||
}
|
||||
|
||||
read_cb file_reader_from_fd(int fd) {
|
||||
auto d = defer_shared(static_cast<int>(fd), close);
|
||||
|
||||
return [fd, d](uint8_t *buf, size_t len) -> read_cb::result_type {
|
||||
int rv;
|
||||
while ((rv = read(fd, buf, len)) == -1 && errno == EINTR)
|
||||
;
|
||||
|
||||
if (rv == -1) {
|
||||
return std::make_pair(-1, false);
|
||||
}
|
||||
|
||||
if (rv == 0) {
|
||||
return std::make_pair(rv, true);
|
||||
}
|
||||
|
||||
return std::make_pair(rv, false);
|
||||
};
|
||||
}
|
||||
|
||||
bool check_path(const std::string &path) { return util::check_path(path); }
|
||||
|
||||
std::string percent_decode(const std::string &s) {
|
||||
return util::percentDecode(std::begin(s), std::end(s));
|
||||
}
|
||||
|
||||
std::string http_date(int64_t t) { return util::http_date(t); }
|
||||
|
||||
} // namespace asio_http2
|
||||
|
||||
} // namespace nghttp2
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user