mirror of
https://github.com/nghttp2/nghttp2.git
synced 2025-12-09 03:28:52 +08:00
Compare commits
390 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
453e12cd1f | ||
|
|
de5c821530 | ||
|
|
1ac028e166 | ||
|
|
2778e4aafc | ||
|
|
3f80472e0a | ||
|
|
ec30af9117 | ||
|
|
3b5b5ce254 | ||
|
|
694cd07f1d | ||
|
|
5e71f293e5 | ||
|
|
d4ec542107 | ||
|
|
ffdc764d85 | ||
|
|
2ae1da113e | ||
|
|
5b4f02dfe0 | ||
|
|
e47b976691 | ||
|
|
be4c75a7e9 | ||
|
|
7b9a8acc22 | ||
|
|
03e2dabea9 | ||
|
|
2411a08b09 | ||
|
|
062d6a8398 | ||
|
|
ad4a4ee567 | ||
|
|
ab76468971 | ||
|
|
bc6d952361 | ||
|
|
f85c592818 | ||
|
|
c9c9beddeb | ||
|
|
d3fa938f1f | ||
|
|
9683f88e6a | ||
|
|
2e5c7f598f | ||
|
|
88234cbac0 | ||
|
|
5b208c6277 | ||
|
|
4fffd23dd3 | ||
|
|
b187895e1d | ||
|
|
6e7d0286e3 | ||
|
|
f2bb7947ee | ||
|
|
dc791a641d | ||
|
|
a93e04c6f8 | ||
|
|
d46e13ae52 | ||
|
|
0707720b11 | ||
|
|
74ad10c355 | ||
|
|
f131705ba4 | ||
|
|
6a70584459 | ||
|
|
4c8d4f8a85 | ||
|
|
3ebb3faf32 | ||
|
|
65bbdf56cd | ||
|
|
3e3d51842b | ||
|
|
b041218a2a | ||
|
|
29d2386f13 | ||
|
|
1bd43e094a | ||
|
|
48fc0c04bc | ||
|
|
d2890dfb91 | ||
|
|
e8de437d5c | ||
|
|
51e79c5a3d | ||
|
|
d11cac48f1 | ||
|
|
ed63674b88 | ||
|
|
8e22eadc76 | ||
|
|
9228e223fa | ||
|
|
43fb7f707f | ||
|
|
f88599197e | ||
|
|
4f027c1562 | ||
|
|
704f362804 | ||
|
|
e2535df505 | ||
|
|
f207089604 | ||
|
|
7b7b0ebcca | ||
|
|
8289943a58 | ||
|
|
56d6784d8d | ||
|
|
1d26678934 | ||
|
|
9125499dd0 | ||
|
|
f3f9210dae | ||
|
|
9ca63de9e8 | ||
|
|
813c750c12 | ||
|
|
3fc1d2dfaa | ||
|
|
855f39743a | ||
|
|
3c431da6aa | ||
|
|
66ed7f6a59 | ||
|
|
8ca2f6aa92 | ||
|
|
fa2fbe944f | ||
|
|
33a6851abe | ||
|
|
763cdc3499 | ||
|
|
0f5c28ac46 | ||
|
|
941236948f | ||
|
|
660c536275 | ||
|
|
abe74f869f | ||
|
|
52b74144ee | ||
|
|
1b79114d2d | ||
|
|
ab634853df | ||
|
|
d2e64317ba | ||
|
|
7730b13e5a | ||
|
|
ed5339953e | ||
|
|
b2f07b1d8c | ||
|
|
eff5c7d0d0 | ||
|
|
e00b8f1f73 | ||
|
|
fe6b541233 | ||
|
|
167a1102e0 | ||
|
|
d61208b394 | ||
|
|
4cf023d94c | ||
|
|
b5d793dee6 | ||
|
|
4caddec9ba | ||
|
|
3b4aedd566 | ||
|
|
bc50062964 | ||
|
|
c69f6f4186 | ||
|
|
8c5db539b3 | ||
|
|
fa8b310cfd | ||
|
|
6d5f402380 | ||
|
|
cc7929bdcc | ||
|
|
a82b7f09c8 | ||
|
|
75bfbc94dd | ||
|
|
6c66bd5c7c | ||
|
|
cd69ed20c3 | ||
|
|
a8a2236da9 | ||
|
|
293b717b04 | ||
|
|
078b1de12e | ||
|
|
d84d0b8c5c | ||
|
|
cb6a3cf4e7 | ||
|
|
59e42c1c69 | ||
|
|
a5f715963e | ||
|
|
d49733a5c9 | ||
|
|
052be3296c | ||
|
|
6bb410d603 | ||
|
|
2d4b92fc2b | ||
|
|
ee26469cd9 | ||
|
|
853c9888d9 | ||
|
|
85190f3677 | ||
|
|
efd90c349d | ||
|
|
5aa0a0d099 | ||
|
|
80eb988511 | ||
|
|
a8525a131a | ||
|
|
6a598d8fb8 | ||
|
|
1d5a1b895b | ||
|
|
e6fdb3418d | ||
|
|
5240f8ad7e | ||
|
|
91b616dd6f | ||
|
|
1c1843297c | ||
|
|
aa4d43f31e | ||
|
|
ac86b51e37 | ||
|
|
27a91fc30e | ||
|
|
ece6521d26 | ||
|
|
792938d410 | ||
|
|
9b3d5a8be5 | ||
|
|
8658163aac | ||
|
|
6326aec089 | ||
|
|
f9f6cdc93d | ||
|
|
5b3deec186 | ||
|
|
f3f031f94c | ||
|
|
be7aa8f53a | ||
|
|
7563839756 | ||
|
|
ffcbffc28b | ||
|
|
d998e79549 | ||
|
|
1aa69e334d | ||
|
|
1c00e715a3 | ||
|
|
59c9c4511c | ||
|
|
f763d76110 | ||
|
|
5b55874d4d | ||
|
|
c9f3de5f6b | ||
|
|
c2bb9c01a6 | ||
|
|
0a527f16f5 | ||
|
|
8f23c0c38b | ||
|
|
c1060f0d48 | ||
|
|
15e8d0de7b | ||
|
|
e7ad3633c7 | ||
|
|
a0d93e7744 | ||
|
|
124da7720f | ||
|
|
d668d2448b | ||
|
|
21ab2f135b | ||
|
|
1e38ceb1cd | ||
|
|
2a49e164c8 | ||
|
|
22c88af1ab | ||
|
|
5d80d18f31 | ||
|
|
37e1626478 | ||
|
|
6cddfaf263 | ||
|
|
ac2a8ef4a2 | ||
|
|
b671375abc | ||
|
|
db6c41a219 | ||
|
|
1d38df0a31 | ||
|
|
b1edb1f3ae | ||
|
|
c53c1dc669 | ||
|
|
580a19e097 | ||
|
|
ef40879b5f | ||
|
|
35a45f9d47 | ||
|
|
2685e3405f | ||
|
|
9c4c99bf96 | ||
|
|
9d9eb48258 | ||
|
|
c9f90924a9 | ||
|
|
e5b0303481 | ||
|
|
b1722cbe28 | ||
|
|
7877b676e3 | ||
|
|
c55df4a30b | ||
|
|
24cb90806d | ||
|
|
da5db205ca | ||
|
|
f2d945734e | ||
|
|
f5ead55f0e | ||
|
|
f785e56dba | ||
|
|
b143039b60 | ||
|
|
6afb7442b5 | ||
|
|
b85e2ab7f7 | ||
|
|
f011bda377 | ||
|
|
34581d830d | ||
|
|
334658044e | ||
|
|
60a2c260a5 | ||
|
|
ab2dc5967d | ||
|
|
705051ceb7 | ||
|
|
d3962becf4 | ||
|
|
21d5986157 | ||
|
|
74daa16a1c | ||
|
|
a9d97d9d35 | ||
|
|
bd7d335d9a | ||
|
|
c12b6bc360 | ||
|
|
58da463ad6 | ||
|
|
36c8de9da5 | ||
|
|
f7162ab702 | ||
|
|
8ccb6e463d | ||
|
|
e4dacb2f6f | ||
|
|
dbb82b0f9c | ||
|
|
e1eebf08fb | ||
|
|
01586f473d | ||
|
|
3c631b5625 | ||
|
|
774cf88f68 | ||
|
|
0a80b0c1aa | ||
|
|
1dfe2f8670 | ||
|
|
fac42788bc | ||
|
|
464fef7c6e | ||
|
|
334656b704 | ||
|
|
ba5d9d3352 | ||
|
|
d0fbbe6932 | ||
|
|
c945d4ebbe | ||
|
|
c048ac5eff | ||
|
|
68b392817b | ||
|
|
fbfa3adc42 | ||
|
|
775d07ace4 | ||
|
|
fef01a3c39 | ||
|
|
a7eb6502a9 | ||
|
|
a457d2a138 | ||
|
|
843ecd8cc1 | ||
|
|
5b81f7c713 | ||
|
|
2ec4b10805 | ||
|
|
781d1a2b70 | ||
|
|
7ffa594d4c | ||
|
|
136d997596 | ||
|
|
2b7627f70c | ||
|
|
0620052f50 | ||
|
|
142b433533 | ||
|
|
b464cb78ac | ||
|
|
344d663e90 | ||
|
|
d48eca60cf | ||
|
|
aefc0d1ebb | ||
|
|
1be8d1b797 | ||
|
|
0fa4779d38 | ||
|
|
d07bb1ddff | ||
|
|
0666a73e10 | ||
|
|
3f56c938d8 | ||
|
|
7b2d585896 | ||
|
|
74f899fc01 | ||
|
|
e803c6b65e | ||
|
|
358b4386d3 | ||
|
|
d074cb611f | ||
|
|
54dab50015 | ||
|
|
b60679808b | ||
|
|
547d6d1fb5 | ||
|
|
382024a180 | ||
|
|
12d92a621d | ||
|
|
7f18eced0b | ||
|
|
27e161dc31 | ||
|
|
3ca4539f99 | ||
|
|
ddfa93ff5b | ||
|
|
a61ca763df | ||
|
|
749cc08f52 | ||
|
|
caeeba681f | ||
|
|
d4ea2418d8 | ||
|
|
73f55e7b7a | ||
|
|
13be30e582 | ||
|
|
60fc117c9c | ||
|
|
5cfb51c881 | ||
|
|
f3183efe04 | ||
|
|
b5341ebac6 | ||
|
|
e34b8ac7fb | ||
|
|
58485bd1d8 | ||
|
|
3d211e1cfd | ||
|
|
79c5032708 | ||
|
|
e06cc13edb | ||
|
|
1337fa8bfe | ||
|
|
c1e1a1be5a | ||
|
|
979feaecc6 | ||
|
|
6f0b9128b4 | ||
|
|
a85455ed0b | ||
|
|
227a48cea1 | ||
|
|
ddf6162528 | ||
|
|
abdbd29d5f | ||
|
|
1fee4fd2df | ||
|
|
18357512ed | ||
|
|
52cec35906 | ||
|
|
13cc3f2fe9 | ||
|
|
7cf574d0d8 | ||
|
|
57af995fd0 | ||
|
|
911ffb24fa | ||
|
|
add07c4303 | ||
|
|
4dcb68d128 | ||
|
|
98715f4374 | ||
|
|
9cc7f9fb36 | ||
|
|
d1c1deaf03 | ||
|
|
86aa905c10 | ||
|
|
09fd95ac5c | ||
|
|
5e88be0b2c | ||
|
|
fc25143418 | ||
|
|
a21175398d | ||
|
|
6c51bd0979 | ||
|
|
dc82a6026e | ||
|
|
f152dd8881 | ||
|
|
9703c5de5c | ||
|
|
3395f7158f | ||
|
|
659c3b0aa0 | ||
|
|
46f5d4b1c4 | ||
|
|
bbc09b005b | ||
|
|
1e1e77ad5e | ||
|
|
cdf5d5402b | ||
|
|
3c96041c43 | ||
|
|
6320bd8926 | ||
|
|
30b3855194 | ||
|
|
4ced1c1622 | ||
|
|
2966ad2d15 | ||
|
|
649586fff6 | ||
|
|
a9991133af | ||
|
|
f5342494f4 | ||
|
|
1fd5fdd54a | ||
|
|
88607f09e5 | ||
|
|
c7c496b029 | ||
|
|
5cc24cb7c2 | ||
|
|
a5353c22a6 | ||
|
|
b2ab5178a3 | ||
|
|
27b3091ab6 | ||
|
|
62b73133e5 | ||
|
|
66832e9f4e | ||
|
|
53302406d3 | ||
|
|
bc0ce40dc2 | ||
|
|
652dc250fd | ||
|
|
0da79865b8 | ||
|
|
7504d89f9b | ||
|
|
3f3f258cd6 | ||
|
|
1e95c8b313 | ||
|
|
622f783675 | ||
|
|
7ab4206269 | ||
|
|
e74fbdf6b4 | ||
|
|
fd88c6160d | ||
|
|
082876d92d | ||
|
|
c7a17093cb | ||
|
|
bac31e844a | ||
|
|
067a4d05c3 | ||
|
|
7822bbd7e8 | ||
|
|
cd3eae3dd2 | ||
|
|
dbb131d13d | ||
|
|
6364ae1a98 | ||
|
|
16b5e99e88 | ||
|
|
c280cc7c4d | ||
|
|
788072af9b | ||
|
|
cf0b880b15 | ||
|
|
39fe7a5cfa | ||
|
|
3144bcbe20 | ||
|
|
eb2856f3df | ||
|
|
9865b46905 | ||
|
|
6c40928fed | ||
|
|
109b8cedde | ||
|
|
e78a2100ec | ||
|
|
78d202ac30 | ||
|
|
118ed09da5 | ||
|
|
5b58b4ace5 | ||
|
|
68b5ffc1dc | ||
|
|
ce53d7bd9e | ||
|
|
c7c283f3a9 | ||
|
|
909b79e69b | ||
|
|
256c97d89b | ||
|
|
ba95cd936d | ||
|
|
72e2e145c5 | ||
|
|
2c4dc08aee | ||
|
|
5884794a7f | ||
|
|
dffa078c11 | ||
|
|
945c57c335 | ||
|
|
1db2195389 | ||
|
|
10feab02e8 | ||
|
|
9c30ed1a64 | ||
|
|
e9d1ba2539 | ||
|
|
2ff3d97b2e | ||
|
|
748f6e65bd | ||
|
|
52a1f56d14 | ||
|
|
b6a0eff8a8 | ||
|
|
be9d5efa4c | ||
|
|
814d0f76f3 | ||
|
|
f26270b5b4 | ||
|
|
d584888601 | ||
|
|
40a5756564 | ||
|
|
f2c654f898 | ||
|
|
112b49cb9a | ||
|
|
c79adf6997 | ||
|
|
196406da0e |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -35,3 +35,4 @@ doc/nghttp2ver.h.rst
|
||||
doc/package_README.rst
|
||||
doc/tutorial-client.rst
|
||||
doc/tutorial-server.rst
|
||||
doc/nghttpx-howto.rst
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# nghttp2 - HTTP/2.0 C Library
|
||||
# nghttp2 - HTTP/2 C Library
|
||||
|
||||
# Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
|
||||
|
||||
596
README.rst
596
README.rst
@@ -1,57 +1,52 @@
|
||||
nghttp2 - HTTP/2.0 C Library
|
||||
nghttp2 - HTTP/2 C Library
|
||||
============================
|
||||
|
||||
This is an experimental implementation of Hypertext Transfer Protocol
|
||||
version 2.0.
|
||||
version 2.
|
||||
|
||||
Development Status
|
||||
------------------
|
||||
|
||||
We started to implement HTTP-draft-09/2.0
|
||||
(http://tools.ietf.org/html/draft-ietf-httpbis-http2-09) and the
|
||||
We started to implement h2-12
|
||||
(http://tools.ietf.org/html/draft-ietf-httpbis-http2-12) and the
|
||||
header compression
|
||||
(http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-05).
|
||||
(http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-07).
|
||||
|
||||
The nghttp2 code base was forked from spdylay project.
|
||||
|
||||
========================== =================
|
||||
Features HTTP-draft-09/2.0
|
||||
========================== =================
|
||||
:authority Done
|
||||
HPACK-draft-05 Done
|
||||
SETTINGS_HEADER_TABLE_SIZE Done
|
||||
SETTINGS_ENABLE_PUSH Done
|
||||
FRAME_SIZE_ERROR Done
|
||||
SETTINGS with ACK Done
|
||||
Header Continuation Done
|
||||
ALPN Done
|
||||
========================== =================
|
||||
========================== =====
|
||||
Features h2-12
|
||||
========================== =====
|
||||
Dependency based priority Done
|
||||
BLOCKED frame Done
|
||||
COMPRESSED DATA Done
|
||||
========================== =====
|
||||
|
||||
Public Test Server
|
||||
------------------
|
||||
|
||||
The following endpoints are available to try out nghttp2
|
||||
implementation. These endpoints supports ``HTTP-draft-09/2.0`` and
|
||||
the earlier draft versions are not supporeted.
|
||||
implementation.
|
||||
|
||||
* https://106.186.112.116 (TLS + NPN / ALPN)
|
||||
* https://nghttp2.org/ (TLS + NPN / ALPN)
|
||||
|
||||
ALPN and NPN offer ``HTTP-draft-09/2.0``, ``spdy/3.1``, ``spdy/3``,
|
||||
``spdy/2`` and ``http/1.1``.
|
||||
ALPN and NPN offer ``h2-12``, ``spdy/3.1`` and ``http/1.1``.
|
||||
|
||||
Note: certificate is self-signed and a browser will show alert
|
||||
* http://nghttp2.org/ (Upgrade / Direct)
|
||||
|
||||
``h2c-12`` and ``http/1.1``. We configured this server to send
|
||||
ALTSVC frame or Alt-Svc header field to announce that alternative
|
||||
service is available at port 443.
|
||||
|
||||
* http://106.186.112.116 (Upgrade + Direct)
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
The following packages are needed to build the library:
|
||||
The following package is required to build the libnghttp2 library:
|
||||
|
||||
* pkg-config >= 0.20
|
||||
* zlib >= 1.2.3
|
||||
|
||||
To build and run the unit test programs, the following packages are
|
||||
To build and run the unit test programs, the following package is
|
||||
required:
|
||||
|
||||
* cunit >= 2.1
|
||||
@@ -66,16 +61,17 @@ required:
|
||||
|
||||
* OpenSSL >= 1.0.1
|
||||
* libevent-openssl >= 2.0.8
|
||||
* zlib >= 1.2.3
|
||||
|
||||
ALPN support requires unreleased version OpenSSL >= 1.0.2.
|
||||
|
||||
To enable SPDY protocol in the application program ``nghttpx``, the
|
||||
following packages are required:
|
||||
To enable SPDY protocol in the application program ``nghttpx`` and
|
||||
``h2load``, the following package is required:
|
||||
|
||||
* spdylay >= 1.2.3
|
||||
|
||||
To enable ``-a`` option (getting linked assets from the downloaded
|
||||
resource) in ``nghttp``, the following packages are needed:
|
||||
resource) in ``nghttp``, the following package is required:
|
||||
|
||||
* libxml2 >= 2.7.7
|
||||
|
||||
@@ -83,12 +79,17 @@ The HPACK tools require the following package:
|
||||
|
||||
* jansson >= 2.5
|
||||
|
||||
To mitigate heap fragmentation in long running server programs
|
||||
(``nghttpd`` and ``nghttpx``), jemalloc is recommended:
|
||||
|
||||
* jemalloc
|
||||
|
||||
The Python bindings require the following packages:
|
||||
|
||||
* cython >= 0.19
|
||||
* python >= 2.7
|
||||
|
||||
If you are using Ubuntu 12.04, you need the following packages
|
||||
If you are using Ubuntu 14.04 LTS, you need the following packages
|
||||
installed:
|
||||
|
||||
* autoconf
|
||||
@@ -102,6 +103,9 @@ installed:
|
||||
* libxml2-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/
|
||||
@@ -134,141 +138,170 @@ The documents will be generated under ``doc/manual/html/``.
|
||||
The generated documents will not be installed with ``make install``.
|
||||
|
||||
The online documentation is available at
|
||||
http://tatsuhiro-t.github.io/nghttp2/
|
||||
https://nghttp2.org/documentation/
|
||||
|
||||
Client, Server and Proxy programs
|
||||
---------------------------------
|
||||
|
||||
The src directory contains HTTP/2.0 client, server and proxy programs.
|
||||
The src directory contains HTTP/2 client, server and proxy programs.
|
||||
|
||||
nghttp - client
|
||||
+++++++++++++++
|
||||
|
||||
``nghttp`` is a HTTP/2.0 client. It can connect to the HTTP/2.0 server
|
||||
``nghttp`` is a HTTP/2 client. It can connect to the HTTP/2 server
|
||||
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 -vn https://localhost:8443
|
||||
[ 0.003] NPN select next protocol: the remote server offers:
|
||||
* HTTP-draft-09/2.0
|
||||
* spdy/3
|
||||
* spdy/2
|
||||
$ src/nghttp -nv https://nghttp2.org
|
||||
[ 0.033][NPN] server offers:
|
||||
* h2-12
|
||||
* spdy/3.1
|
||||
* http/1.1
|
||||
NPN selected the protocol: HTTP-draft-09/2.0
|
||||
[ 0.005] send SETTINGS frame <length=16, flags=0x00, stream_id=0>
|
||||
(niv=2)
|
||||
[SETTINGS_MAX_CONCURRENT_STREAMS(4):100]
|
||||
[SETTINGS_INITIAL_WINDOW_SIZE(7):65535]
|
||||
[ 0.006] send HEADERS frame <length=47, flags=0x05, stream_id=1>
|
||||
The negotiated protocol: h2-12
|
||||
[ 0.068] send SETTINGS frame <length=15, flags=0x00, stream_id=0>
|
||||
(niv=3)
|
||||
[SETTINGS_MAX_CONCURRENT_STREAMS(3):100]
|
||||
[SETTINGS_INITIAL_WINDOW_SIZE(4):65535]
|
||||
[SETTINGS_COMPRESS_DATA(5):1]
|
||||
[ 0.068] send HEADERS frame <length=46, flags=0x05, stream_id=1>
|
||||
; END_STREAM | END_HEADERS
|
||||
(padlen=0)
|
||||
; Open new stream
|
||||
:authority: localhost:8443
|
||||
:authority: nghttp2.org
|
||||
:method: GET
|
||||
:path: /
|
||||
:scheme: https
|
||||
accept: */*
|
||||
accept-encoding: gzip, deflate
|
||||
user-agent: nghttp2/0.1.0-DEV
|
||||
[ 0.006] recv SETTINGS frame <length=16, flags=0x00, stream_id=0>
|
||||
user-agent: nghttp2/0.4.0-DEV
|
||||
[ 0.068] recv SETTINGS frame <length=10, flags=0x00, stream_id=0>
|
||||
(niv=2)
|
||||
[SETTINGS_MAX_CONCURRENT_STREAMS(4):100]
|
||||
[SETTINGS_INITIAL_WINDOW_SIZE(7):65535]
|
||||
[ 0.006] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
|
||||
[SETTINGS_MAX_CONCURRENT_STREAMS(3):100]
|
||||
[SETTINGS_INITIAL_WINDOW_SIZE(4):65535]
|
||||
[ 0.068] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
|
||||
; ACK
|
||||
(niv=0)
|
||||
[ 0.006] recv WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
|
||||
(window_size_increment=1000000007)
|
||||
[ 0.006] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>
|
||||
[ 0.079] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>
|
||||
; ACK
|
||||
(niv=0)
|
||||
[ 0.006] recv HEADERS frame <length=132, flags=0x04, stream_id=1>
|
||||
[ 0.080] (stream_id=1, noind=0) :status: 200
|
||||
[ 0.080] (stream_id=1, noind=0) accept-ranges: bytes
|
||||
[ 0.080] (stream_id=1, noind=0) age: 15
|
||||
[ 0.080] (stream_id=1, noind=0) content-length: 40243
|
||||
[ 0.080] (stream_id=1, noind=0) content-type: text/html
|
||||
[ 0.080] (stream_id=1, noind=0) date: Wed, 14 May 2014 15:14:30 GMT
|
||||
[ 0.080] (stream_id=1, noind=0) etag: "535d0eea-9d33"
|
||||
[ 0.080] (stream_id=1, noind=0) last-modified: Sun, 27 Apr 2014 14:06:34 GMT
|
||||
[ 0.080] (stream_id=1, noind=0) server: nginx/1.4.6 (Ubuntu)
|
||||
[ 0.080] (stream_id=1, noind=0) x-varnish: 2114900538 2114900537
|
||||
[ 0.080] (stream_id=1, noind=0) via: 1.1 varnish, 1.1 nghttpx
|
||||
[ 0.080] (stream_id=1, noind=0) strict-transport-security: max-age=31536000
|
||||
[ 0.080] recv HEADERS frame <length=162, flags=0x04, stream_id=1>
|
||||
; END_HEADERS
|
||||
(padlen=0)
|
||||
; First response header
|
||||
:status: 200
|
||||
accept-ranges: bytes
|
||||
content-encoding: gzip
|
||||
content-length: 146
|
||||
content-type: text/html
|
||||
date: Sun, 27 Oct 2013 14:23:54 GMT
|
||||
etag: "b1-4e5535a027780-gzip"
|
||||
last-modified: Sun, 01 Sep 2013 14:34:22 GMT
|
||||
server: Apache/2.4.6 (Debian)
|
||||
vary: Accept-Encoding
|
||||
via: 1.1 nghttpx
|
||||
[ 0.006] recv DATA frame <length=146, flags=0x00, stream_id=1>
|
||||
[ 0.006] recv DATA frame <length=0, flags=0x01, stream_id=1>
|
||||
[ 0.080] recv DATA frame <length=3786, flags=0x00, stream_id=1>
|
||||
[ 0.080] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.081] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.093] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.093] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.094] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.094] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.094] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.096] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.096] send WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
|
||||
(window_size_increment=36554)
|
||||
[ 0.096] send WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=1>
|
||||
(window_size_increment=36554)
|
||||
[ 0.108] recv DATA frame <length=3689, flags=0x00, stream_id=1>
|
||||
[ 0.108] recv DATA frame <length=0, flags=0x01, stream_id=1>
|
||||
; END_STREAM
|
||||
[ 0.007] send GOAWAY frame <length=8, flags=0x00, stream_id=0>
|
||||
[ 0.108] send GOAWAY frame <length=8, flags=0x00, stream_id=0>
|
||||
(last_stream_id=0, error_code=NO_ERROR(0), opaque_data(0)=[])
|
||||
|
||||
The HTTP Upgrade is performed like this::
|
||||
|
||||
$ src/nghttp -vnu http://localhost:8080
|
||||
[ 0.000] HTTP Upgrade request
|
||||
$ src/nghttp -nvu http://nghttp2.org
|
||||
[ 0.013] HTTP Upgrade request
|
||||
GET / HTTP/1.1
|
||||
Host: localhost:8080
|
||||
Host: nghttp2.org
|
||||
Connection: Upgrade, HTTP2-Settings
|
||||
Upgrade: HTTP-draft-09/2.0
|
||||
HTTP2-Settings: AAAABAAAAGQAAAAHAAD__w
|
||||
Upgrade: h2c-12
|
||||
HTTP2-Settings: AwAAAGQEAAD__wUAAAAB
|
||||
Accept: */*
|
||||
User-Agent: nghttp2/0.1.0-DEV
|
||||
User-Agent: nghttp2/0.4.0-DEV
|
||||
|
||||
|
||||
[ 0.000] HTTP Upgrade response
|
||||
[ 0.024] HTTP Upgrade response
|
||||
HTTP/1.1 101 Switching Protocols
|
||||
Connection: Upgrade
|
||||
Upgrade: HTTP-draft-09/2.0
|
||||
Upgrade: h2c-12
|
||||
|
||||
|
||||
[ 0.001] HTTP Upgrade success
|
||||
[ 0.001] send SETTINGS frame <length=16, flags=0x00, stream_id=0>
|
||||
[ 0.024] HTTP Upgrade success
|
||||
[ 0.024] send SETTINGS frame <length=15, flags=0x00, stream_id=0>
|
||||
(niv=3)
|
||||
[SETTINGS_MAX_CONCURRENT_STREAMS(3):100]
|
||||
[SETTINGS_INITIAL_WINDOW_SIZE(4):65535]
|
||||
[SETTINGS_COMPRESS_DATA(5):1]
|
||||
[ 0.024] recv SETTINGS frame <length=10, flags=0x00, stream_id=0>
|
||||
(niv=2)
|
||||
[SETTINGS_MAX_CONCURRENT_STREAMS(4):100]
|
||||
[SETTINGS_INITIAL_WINDOW_SIZE(7):65535]
|
||||
[ 0.001] recv SETTINGS frame <length=16, flags=0x00, stream_id=0>
|
||||
(niv=2)
|
||||
[SETTINGS_MAX_CONCURRENT_STREAMS(4):100]
|
||||
[SETTINGS_INITIAL_WINDOW_SIZE(7):65535]
|
||||
[ 0.001] recv WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
|
||||
(window_size_increment=1000000007)
|
||||
[ 0.001] recv HEADERS frame <length=121, flags=0x04, stream_id=1>
|
||||
[SETTINGS_MAX_CONCURRENT_STREAMS(3):100]
|
||||
[SETTINGS_INITIAL_WINDOW_SIZE(4):65535]
|
||||
[ 0.024] recv ALTSVC frame <length=43, flags=0x00, stream_id=0>
|
||||
(max-age=86400, port=443, protocol_id=h2-12, host=nghttp2.org, origin=http://nghttp2.org)
|
||||
[ 0.024] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
|
||||
; ACK
|
||||
(niv=0)
|
||||
[ 0.024] (stream_id=1, noind=0) :status: 200
|
||||
[ 0.024] (stream_id=1, noind=0) accept-ranges: bytes
|
||||
[ 0.024] (stream_id=1, noind=0) age: 10
|
||||
[ 0.024] (stream_id=1, noind=0) content-length: 40243
|
||||
[ 0.024] (stream_id=1, noind=0) content-type: text/html
|
||||
[ 0.024] (stream_id=1, noind=0) date: Wed, 14 May 2014 15:16:34 GMT
|
||||
[ 0.024] (stream_id=1, noind=0) etag: "535d0eea-9d33"
|
||||
[ 0.024] (stream_id=1, noind=0) last-modified: Sun, 27 Apr 2014 14:06:34 GMT
|
||||
[ 0.024] (stream_id=1, noind=0) server: nginx/1.4.6 (Ubuntu)
|
||||
[ 0.024] (stream_id=1, noind=0) x-varnish: 2114900541 2114900540
|
||||
[ 0.024] (stream_id=1, noind=0) via: 1.1 varnish, 1.1 nghttpx
|
||||
[ 0.024] recv HEADERS frame <length=148, flags=0x04, stream_id=1>
|
||||
; END_HEADERS
|
||||
(padlen=0)
|
||||
; First response header
|
||||
:status: 200
|
||||
accept-ranges: bytes
|
||||
content-length: 177
|
||||
content-type: text/html
|
||||
date: Sun, 27 Oct 2013 14:26:04 GMT
|
||||
etag: "b1-4e5535a027780"
|
||||
last-modified: Sun, 01 Sep 2013 14:34:22 GMT
|
||||
server: Apache/2.4.6 (Debian)
|
||||
vary: Accept-Encoding
|
||||
via: 1.1 nghttpx
|
||||
[ 0.001] recv DATA frame <length=177, flags=0x00, stream_id=1>
|
||||
[ 0.001] recv DATA frame <length=0, flags=0x01, stream_id=1>
|
||||
[ 0.024] recv DATA frame <length=3786, flags=0x00, stream_id=1>
|
||||
[ 0.025] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.031] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.031] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.032] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.032] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.033] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.033] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.033] send WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
|
||||
(window_size_increment=33164)
|
||||
[ 0.033] send WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=1>
|
||||
(window_size_increment=33164)
|
||||
[ 0.038] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.038] recv DATA frame <length=3689, flags=0x00, stream_id=1>
|
||||
[ 0.038] recv DATA frame <length=0, flags=0x01, stream_id=1>
|
||||
; END_STREAM
|
||||
[ 0.001] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
|
||||
[ 0.038] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>
|
||||
; ACK
|
||||
(niv=0)
|
||||
[ 0.001] send GOAWAY frame <length=8, flags=0x00, stream_id=0>
|
||||
[ 0.038] send GOAWAY frame <length=8, flags=0x00, stream_id=0>
|
||||
(last_stream_id=0, error_code=NO_ERROR(0), opaque_data(0)=[])
|
||||
[ 0.001] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>
|
||||
; ACK
|
||||
(niv=0)
|
||||
|
||||
nghttpd - server
|
||||
++++++++++++++++
|
||||
|
||||
``nghttpd`` is static web server. It is single threaded and
|
||||
multiplexes connections using non-blocking socket.
|
||||
``nghttpd`` is a multi-threaded static web server.
|
||||
|
||||
By default, it uses SSL/TLS connection. Use ``--no-tls`` option to
|
||||
disable it.
|
||||
|
||||
``nghttpd`` only accept the HTTP/2.0 connection via NPN/ALPN or direct
|
||||
HTTP/2.0 connection. No HTTP Upgrade is supported.
|
||||
``nghttpd`` only accepts the HTTP/2 connection via NPN/ALPN or direct
|
||||
HTTP/2 connection. No HTTP Upgrade is supported.
|
||||
|
||||
``-p`` option allows users to configure server push.
|
||||
|
||||
@@ -278,91 +311,97 @@ information. Here is sample output from ``nghttpd`` server::
|
||||
$ src/nghttpd --no-tls -v 8080
|
||||
IPv4: listen on port 8080
|
||||
IPv6: listen on port 8080
|
||||
[id=1] [ 1.189] send SETTINGS frame <length=8, flags=0x00, stream_id=0>
|
||||
(niv=1)
|
||||
[SETTINGS_MAX_CONCURRENT_STREAMS(4):100]
|
||||
[id=1] [ 1.191] recv SETTINGS frame <length=16, flags=0x00, stream_id=0>
|
||||
[id=1] [ 15.921] send SETTINGS frame <length=10, flags=0x00, stream_id=0>
|
||||
(niv=2)
|
||||
[SETTINGS_MAX_CONCURRENT_STREAMS(4):100]
|
||||
[SETTINGS_INITIAL_WINDOW_SIZE(7):65535]
|
||||
[id=1] [ 1.191] recv HEADERS frame <length=47, flags=0x05, stream_id=1>
|
||||
[SETTINGS_MAX_CONCURRENT_STREAMS(3):100]
|
||||
[SETTINGS_COMPRESS_DATA(5):1]
|
||||
[id=1] [ 15.921] recv SETTINGS frame <length=15, flags=0x00, stream_id=0>
|
||||
(niv=3)
|
||||
[SETTINGS_MAX_CONCURRENT_STREAMS(3):100]
|
||||
[SETTINGS_INITIAL_WINDOW_SIZE(4):65535]
|
||||
[SETTINGS_COMPRESS_DATA(5):1]
|
||||
[id=1] [ 15.921] (stream_id=1, noind=0) :authority: localhost:8080
|
||||
[id=1] [ 15.921] (stream_id=1, noind=0) :method: GET
|
||||
[id=1] [ 15.921] (stream_id=1, noind=0) :path: /
|
||||
[id=1] [ 15.921] (stream_id=1, noind=0) :scheme: http
|
||||
[id=1] [ 15.921] (stream_id=1, noind=0) accept: */*
|
||||
[id=1] [ 15.921] (stream_id=1, noind=0) accept-encoding: gzip, deflate
|
||||
[id=1] [ 15.921] (stream_id=1, noind=0) user-agent: nghttp2/0.4.0-DEV
|
||||
[id=1] [ 15.921] recv HEADERS frame <length=48, flags=0x05, stream_id=1>
|
||||
; END_STREAM | END_HEADERS
|
||||
(padlen=0)
|
||||
; Open new stream
|
||||
:authority: localhost:8080
|
||||
:method: GET
|
||||
:path: /
|
||||
:scheme: http
|
||||
accept: */*
|
||||
accept-encoding: gzip, deflate
|
||||
user-agent: nghttp2/0.1.0-DEV
|
||||
[id=1] [ 1.192] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
|
||||
[id=1] [ 15.921] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>
|
||||
; ACK
|
||||
(niv=0)
|
||||
[id=1] [ 1.192] send HEADERS frame <length=70, flags=0x04, stream_id=1>
|
||||
[id=1] [ 15.921] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
|
||||
; ACK
|
||||
(niv=0)
|
||||
[id=1] [ 15.921] send HEADERS frame <length=82, flags=0x04, stream_id=1>
|
||||
; END_HEADERS
|
||||
(padlen=0)
|
||||
; First response header
|
||||
:status: 404
|
||||
content-encoding: gzip
|
||||
content-type: text/html; charset=UTF-8
|
||||
date: Sun, 27 Oct 2013 14:27:53 GMT
|
||||
server: nghttpd nghttp2/0.1.0-DEV
|
||||
[id=1] [ 1.192] send DATA frame <length=117, flags=0x00, stream_id=1>
|
||||
[id=1] [ 1.192] send DATA frame <length=0, flags=0x01, stream_id=1>
|
||||
:status: 200
|
||||
cache-control: max-age=3600
|
||||
content-length: 612
|
||||
date: Wed, 14 May 2014 15:19:03 GMT
|
||||
last-modified: Sat, 08 Mar 2014 16:04:06 GMT
|
||||
server: nghttpd nghttp2/0.4.0-DEV
|
||||
[id=1] [ 15.922] send DATA frame <length=381, flags=0x20, stream_id=1>
|
||||
; COMPRESSED
|
||||
[id=1] [ 15.922] send DATA frame <length=0, flags=0x01, stream_id=1>
|
||||
; END_STREAM
|
||||
[id=1] [ 1.192] stream_id=1 closed
|
||||
[id=1] [ 1.192] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>
|
||||
; ACK
|
||||
(niv=0)
|
||||
[id=1] [ 1.192] recv GOAWAY frame <length=8, flags=0x00, stream_id=0>
|
||||
[id=1] [ 15.922] stream_id=1 closed
|
||||
[id=1] [ 15.922] recv GOAWAY frame <length=8, flags=0x00, stream_id=0>
|
||||
(last_stream_id=0, error_code=NO_ERROR(0), opaque_data(0)=[])
|
||||
[id=1] [ 1.192] closed
|
||||
[id=1] [ 15.922] closed
|
||||
|
||||
nghttpx - proxy
|
||||
+++++++++++++++
|
||||
|
||||
The ``nghttpx`` is a multi-threaded reverse proxy for
|
||||
HTTP-draft-09/2.0, SPDY and HTTP/1.1. It has several operation modes:
|
||||
``nghttpx`` is a multi-threaded reverse proxy for
|
||||
``h2-12``, SPDY and HTTP/1.1. It has several operation modes:
|
||||
|
||||
================== ============================== ============== =============
|
||||
================== ============================ ============== =============
|
||||
Mode option Frontend Backend Note
|
||||
================== ============================== ============== =============
|
||||
default mode HTTP/2.0, SPDY, HTTP/1.1 (TLS) HTTP/1.1 Reverse proxy
|
||||
``--http2-proxy`` HTTP/2.0, SPDY, HTTP/1.1 (TLS) HTTP/1.1 SPDY proxy
|
||||
``--http2-bridge`` HTTP/2.0, SPDY, HTTP/1.1 (TLS) HTTP/2.0 (TLS)
|
||||
``--client`` HTTP/2.0, HTTP/1.1 HTTP/2.0 (TLS)
|
||||
``--client-proxy`` HTTP/2.0, HTTP/1.1 HTTP/2.0 (TLS) Forward proxy
|
||||
================== ============================== ============== =============
|
||||
================== ============================ ============== =============
|
||||
default mode HTTP/2, SPDY, HTTP/1.1 (TLS) HTTP/1.1 Reverse proxy
|
||||
``--http2-proxy`` HTTP/2, SPDY, HTTP/1.1 (TLS) HTTP/1.1 SPDY proxy
|
||||
``--http2-bridge`` HTTP/2, SPDY, HTTP/1.1 (TLS) HTTP/2 (TLS)
|
||||
``--client`` HTTP/2, HTTP/1.1 HTTP/2 (TLS)
|
||||
``--client-proxy`` HTTP/2, HTTP/1.1 HTTP/2 (TLS) Forward proxy
|
||||
================== ============================ ============== =============
|
||||
|
||||
The interesting mode at the moment is the default mode. It works like
|
||||
a reverse proxy and listens HTTP-draft-09/2.0, SPDY and HTTP/1.1 and
|
||||
can be deployed SSL/TLS terminator for existing web server.
|
||||
a reverse proxy and listens for ``h2-12``, SPDY and HTTP/1.1 and can
|
||||
be deployed SSL/TLS terminator for existing web server.
|
||||
|
||||
The default mode, ``--http2-proxy`` and ``--http2-bridge`` modes use
|
||||
SSL/TLS in the frontend connection by default. To disable SSL/TLS, use
|
||||
``--frontend-no-tls`` option. If that option is used, SPDY is disabled
|
||||
in the frontend and incoming HTTP/1.1 connection can be upgraded to
|
||||
HTTP/2.0 through HTTP Upgrade.
|
||||
SSL/TLS in the frontend connection by default. To disable SSL/TLS,
|
||||
use ``--frontend-no-tls`` option. If that option is used, SPDY is
|
||||
disabled in the frontend and incoming HTTP/1.1 connection can be
|
||||
upgraded to HTTP/2 through HTTP Upgrade.
|
||||
|
||||
The ``--http2-bridge``, ``--client`` and ``--client-proxy`` modes use
|
||||
SSL/TLS in the backend connection by deafult. To disable SSL/TLS, use
|
||||
``--backend-no-tls`` option.
|
||||
|
||||
The ``nghttpx`` supports configuration file. See ``--conf`` option and
|
||||
``nghttpx`` supports configuration file. See ``--conf`` option and
|
||||
sample configuration file ``nghttpx.conf.sample``.
|
||||
|
||||
The ``nghttpx`` does not support server push.
|
||||
``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::
|
||||
|
||||
Client <-- (HTTP/2.0, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/1.1) --> Web Server
|
||||
Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/1.1) --> Web Server
|
||||
[reverse proxy]
|
||||
|
||||
With ``--http2-proxy`` option, it works as so called secure proxy (aka
|
||||
SPDY proxy)::
|
||||
|
||||
Client <-- (HTTP/2.0, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/1.1) --> Proxy
|
||||
Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/1.1) --> Proxy
|
||||
[secure proxy] (e.g., Squid)
|
||||
|
||||
The ``Client`` in the above is needs to be configured to use
|
||||
@@ -382,57 +421,95 @@ create proxy.pac script like this:
|
||||
machine nghttpx is running. Please note that Chrome requires valid
|
||||
certificate for secure proxy.
|
||||
|
||||
Then run chrome with the following arguments::
|
||||
Then run Chrome with the following arguments::
|
||||
|
||||
$ google-chrome --proxy-pac-url=file:///path/to/proxy.pac --use-npn
|
||||
|
||||
With ``--http2-bridge``, it accepts HTTP/2.0, SPDY and HTTP/1.1
|
||||
connections and communicates with backend in HTTP/2.0::
|
||||
With ``--http2-bridge``, it accepts HTTP/2, SPDY and HTTP/1.1
|
||||
connections and communicates with backend in HTTP/2::
|
||||
|
||||
Client <-- (HTTP/2.0, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/2.0) --> Web or HTTP/2.0 Proxy etc
|
||||
Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/2) --> Web or HTTP/2 Proxy etc
|
||||
(e.g., nghttpx -s)
|
||||
|
||||
With ``--client-proxy`` option, it works as forward proxy and expects
|
||||
that the backend is HTTP/2.0 proxy::
|
||||
that the backend is HTTP/2 proxy::
|
||||
|
||||
Client <-- (HTTP/2.0, HTTP/1.1) --> nghttpx <-- (HTTP/2.0) --> HTTP/2.0 Proxy
|
||||
Client <-- (HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/2) --> HTTP/2 Proxy
|
||||
[forward proxy] (e.g., nghttpx -s)
|
||||
|
||||
The ``Client`` is needs to be configured to use nghttpx as forward
|
||||
proxy. The frontend HTTP/1.1 connection can be upgraded to HTTP/2.0
|
||||
The ``Client`` needs to be configured to use nghttpx as forward
|
||||
proxy. The frontend HTTP/1.1 connection can be upgraded to HTTP/2
|
||||
through HTTP Upgrade. With the above configuration, one can use
|
||||
HTTP/1.1 client to access and test their HTTP/2.0 servers.
|
||||
HTTP/1.1 client to access and test their HTTP/2 servers.
|
||||
|
||||
With ``--client`` option, it works as reverse proxy and expects that
|
||||
the backend is HTTP/2.0 Web server::
|
||||
the backend is HTTP/2 Web server::
|
||||
|
||||
Client <-- (HTTP/2.0, HTTP/1.1) --> nghttpx <-- (HTTP/2.0) --> Web Server
|
||||
Client <-- (HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/2) --> Web Server
|
||||
[reverse proxy]
|
||||
|
||||
The frontend HTTP/1.1 connection can be upgraded to HTTP/2.0
|
||||
The frontend HTTP/1.1 connection can be upgraded to HTTP/2
|
||||
through HTTP Upgrade.
|
||||
|
||||
For the operation modes which talk to the backend in HTTP/2.0 over
|
||||
SSL/TLS, the backend connections can be tunneled though HTTP
|
||||
proxy. The proxy is specified using ``--backend-http-proxy-uri``
|
||||
option. The following figure illustrates the example of
|
||||
``--http2-bridge`` and ``--backend-http-proxy-uri`` option to talk to
|
||||
the outside HTTP/2.0 proxy through HTTP proxy::
|
||||
For the operation modes which talk to the backend in HTTP/2 over
|
||||
SSL/TLS, the backend connections can be tunneled through HTTP proxy.
|
||||
The proxy is specified using ``--backend-http-proxy-uri`` option. The
|
||||
following figure illustrates the example of ``--http2-bridge`` and
|
||||
``--backend-http-proxy-uri`` options to talk to the outside HTTP/2
|
||||
proxy through HTTP proxy::
|
||||
|
||||
Client <-- (HTTP/2.0, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/2.0) --
|
||||
Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/2) --
|
||||
|
||||
--===================---> HTTP/2.0 Proxy
|
||||
--===================---> HTTP/2 Proxy
|
||||
(HTTP proxy tunnel) (e.g., nghttpx -s)
|
||||
|
||||
Benchmarking tool
|
||||
-----------------
|
||||
|
||||
The ``h2load`` program is a benchmarking tool for HTTP/2 and SPDY.
|
||||
The SPDY support is enabled if the program was built with spdylay
|
||||
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/
|
||||
starting benchmark...
|
||||
progress: 10% done
|
||||
progress: 20% done
|
||||
progress: 30% done
|
||||
progress: 40% done
|
||||
progress: 50% done
|
||||
progress: 60% done
|
||||
progress: 70% done
|
||||
progress: 80% done
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
.. warning::
|
||||
|
||||
**Don't use this tool against publicly available servers.** That is
|
||||
considered a DOS attack. Please only use against your private
|
||||
servers.
|
||||
|
||||
HPACK tools
|
||||
-----------
|
||||
|
||||
The ``src`` directory contains HPACK tools. The ``deflatehd`` is
|
||||
The ``src`` directory contains HPACK tools. The ``deflatehd`` is a
|
||||
command-line header compression tool. The ``inflatehd`` is
|
||||
command-line header decompression tool. Both tools read input from
|
||||
stdin and write output to stdout. The errors are written to
|
||||
stderr. They take JSON as input and output. We use the same JSON data
|
||||
format used in https://github.com/Jxck/hpack-test-case
|
||||
stdin and write output to stdout. The errors are written to stderr.
|
||||
They take JSON as input and output. We use (mostly) same JSON data
|
||||
format described at https://github.com/http2jp/hpack-test-case
|
||||
|
||||
deflatehd - header compressor
|
||||
+++++++++++++++++++++++++++++
|
||||
@@ -440,21 +517,18 @@ deflatehd - header compressor
|
||||
The ``deflatehd`` reads JSON data or HTTP/1-style header fields from
|
||||
stdin and outputs compressed header block in JSON.
|
||||
|
||||
For the JSON input, the root JSON object must contain ``context`` key,
|
||||
which indicates which compression context is used. If it is
|
||||
``request``, request compression context is used. Otherwise, response
|
||||
compression context is used. The value of ``cases`` key contains the
|
||||
sequence of input header set. They share the same compression context
|
||||
and are processed in the order they appear. Each item in the sequence
|
||||
is a JSON object and it must have at least ``headers`` key. Its value
|
||||
is an array of a JSON object containing exactly one name/value pair.
|
||||
For the JSON input, the root JSON object must include ``cases`` key.
|
||||
Its value has to include the sequence of input header set. They share
|
||||
the same compression context and are processed in the order they
|
||||
appear. Each item in the sequence is a JSON object and it must
|
||||
include ``headers`` key. Its value is an array of a JSON object,
|
||||
which includes exactly one name/value pair.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"context": "request",
|
||||
"cases":
|
||||
[
|
||||
{
|
||||
@@ -485,9 +559,7 @@ Example::
|
||||
:method: POST
|
||||
user-agent: nghttp2
|
||||
|
||||
The output is JSON object. It contains ``context`` key and its value
|
||||
is ``request`` if the compression context is request, otherwise
|
||||
``response``. The root JSON object also contains ``cases`` key and its
|
||||
The output is JSON object. It should include ``cases`` key and its
|
||||
value is an array of JSON object, which has at least following keys:
|
||||
|
||||
seq
|
||||
@@ -509,14 +581,13 @@ headers
|
||||
The input header set.
|
||||
|
||||
header_table_size
|
||||
The header table size adjsuted before deflating header set.
|
||||
The header table size adjusted before deflating header set.
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"context": "request",
|
||||
"cases":
|
||||
[
|
||||
{
|
||||
@@ -578,19 +649,16 @@ The output can be used as the input for ``inflatehd`` and
|
||||
``deflatehd``.
|
||||
|
||||
With ``-d`` option, the extra ``header_table`` key is added and its
|
||||
associated value contains the state of dyanmic header table after the
|
||||
corresponding header set was processed. The value contains following
|
||||
keys:
|
||||
associated value includes the state of dynamic header table after the
|
||||
corresponding header set was processed. The value includes at least
|
||||
the following keys:
|
||||
|
||||
entries
|
||||
The entry in the header table. If ``referenced`` is ``true``, it
|
||||
is in the reference set. The ``size`` includes the overhead (32
|
||||
bytes). The ``index`` corresponds to the index of header table.
|
||||
The ``name`` is the header field name and the ``value`` is the
|
||||
header field value. They may be displayed as ``**DEALLOCATED**``,
|
||||
which means that the memory for that string is freed and not
|
||||
available. This will happen when the specifying smaller value in
|
||||
``-S`` than ``-s``.
|
||||
header field value.
|
||||
|
||||
size
|
||||
The sum of the spaces entries occupied, this includes the
|
||||
@@ -616,7 +684,6 @@ Example:
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"context": "request",
|
||||
"cases":
|
||||
[
|
||||
{
|
||||
@@ -772,21 +839,17 @@ inflatehd - header decompressor
|
||||
The ``inflatehd`` reads JSON data from stdin and outputs decompressed
|
||||
name/value pairs in JSON.
|
||||
|
||||
The root JSON object must contain ``context`` key, which indicates
|
||||
which compression context is used. If it is ``request``, request
|
||||
compression context is used. Otherwise, response compression context
|
||||
is used. The value of ``cases`` key contains the sequence of
|
||||
compressed header block. They share the same compression context and
|
||||
are processed in the order they appear. Each item in the sequence is a
|
||||
JSON object and it must have at least ``wire`` key. Its value is a
|
||||
string containing compressed header block in hex string.
|
||||
The root JSON object must include ``cases`` key. Its value has to
|
||||
include the sequence of compressed header block. They share the same
|
||||
compression context and are processed in the order they appear. Each
|
||||
item in the sequence is a JSON object and it must have at least
|
||||
``wire`` key. Its value is a compressed header block in hex string.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"context": "request",
|
||||
"cases":
|
||||
[
|
||||
{ "wire": "8285" },
|
||||
@@ -794,22 +857,20 @@ Example:
|
||||
]
|
||||
}
|
||||
|
||||
The output is JSON object. It contains ``context`` key and its value
|
||||
is ``request`` if the compression context is request, otherwise
|
||||
``response``. The root JSON object also contains ``cases`` key and its
|
||||
The output is JSON object. It should include ``cases`` key and its
|
||||
value is an array of JSON object, which has at least following keys:
|
||||
|
||||
seq
|
||||
The index of header set in the input.
|
||||
|
||||
headers
|
||||
The JSON array contains decompressed name/value pairs.
|
||||
The JSON array includes decompressed name/value pairs.
|
||||
|
||||
wire
|
||||
The compressed header block in hex string.
|
||||
|
||||
header_table_size
|
||||
The header table size adjsuted before inflating compressed header
|
||||
The header table size adjusted before inflating compressed header
|
||||
block.
|
||||
|
||||
Example:
|
||||
@@ -817,7 +878,6 @@ Example:
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"context": "request",
|
||||
"cases":
|
||||
[
|
||||
{
|
||||
@@ -872,7 +932,7 @@ The output can be used as the input for ``deflatehd`` and
|
||||
``inflatehd``.
|
||||
|
||||
With ``-d`` option, the extra ``header_table`` key is added and its
|
||||
associated value contains the state of dyanmic header table after the
|
||||
associated value includes the state of dynamic header table after the
|
||||
corresponding header set was processed. The format is the same as
|
||||
``deflatehd``.
|
||||
|
||||
@@ -880,8 +940,8 @@ Python bindings
|
||||
---------------
|
||||
|
||||
This ``python`` directory contains nghttp2 Python bindings. The
|
||||
bindings currently only provide HPACK compressor and decompressor
|
||||
classes.
|
||||
bindings currently provide HPACK compressor and decompressor classes
|
||||
and HTTP/2 server.
|
||||
|
||||
The extension module is called ``nghttp2``.
|
||||
|
||||
@@ -889,10 +949,7 @@ The extension module is called ``nghttp2``.
|
||||
determined by configure script. If the detected Python version is not
|
||||
what you expect, specify a path to Python executable in ``PYTHON``
|
||||
variable as an argument to configure script (e.g., ``./configure
|
||||
PYTHON=/usr/bin/python3.3``).
|
||||
|
||||
Example
|
||||
+++++++
|
||||
PYTHON=/usr/bin/python3.4``).
|
||||
|
||||
The following example code illustrates basic usage of HPACK compressor
|
||||
and decompressor in Python:
|
||||
@@ -902,8 +959,8 @@ and decompressor in Python:
|
||||
import binascii
|
||||
import nghttp2
|
||||
|
||||
deflater = nghttp2.HDDeflater(nghttp2.HD_SIDE_REQUEST)
|
||||
inflater = nghttp2.HDInflater(nghttp2.HD_SIDE_REQUEST)
|
||||
deflater = nghttp2.HDDeflater()
|
||||
inflater = nghttp2.HDInflater()
|
||||
|
||||
data = deflater.deflate([(b'foo', b'bar'),
|
||||
(b'baz', b'buz')])
|
||||
@@ -911,3 +968,86 @@ and decompressor in Python:
|
||||
|
||||
hdrs = inflater.inflate(data)
|
||||
print(hdrs)
|
||||
|
||||
The ``nghttp2.HTTP2Server`` class builds on top of the asyncio event
|
||||
loop. On construction, *RequestHandlerClass* must be given, which
|
||||
must be a subclass of ``nghttp2.BaseRequestHandler`` class.
|
||||
|
||||
The ``BaseRequestHandler`` class is used to handle the HTTP/2 stream.
|
||||
By default, it does nothing. It must be subclassed to handle each
|
||||
event callback method.
|
||||
|
||||
The first callback method invoked is ``on_headers()``. It is called
|
||||
when HEADERS frame, which includes request header fields, has arrived.
|
||||
|
||||
If request has request body, ``on_data(data)`` is invoked for each
|
||||
chunk of received data.
|
||||
|
||||
When whole request is received, ``on_request_done()`` is invoked.
|
||||
|
||||
When stream is closed, ``on_close(error_code)`` is called.
|
||||
|
||||
The application can send response using ``send_response()`` method.
|
||||
It can be used in ``on_headers()``, ``on_data()`` or
|
||||
``on_request_done()``.
|
||||
|
||||
The application can push resource using ``push()`` method. It must be
|
||||
used before ``send_response()`` call.
|
||||
|
||||
The following instance variables are available:
|
||||
|
||||
client_address
|
||||
Contains a tuple of the form (host, port) referring to the
|
||||
client's address.
|
||||
|
||||
stream_id
|
||||
Stream ID of this stream.
|
||||
|
||||
scheme
|
||||
Scheme of the request URI. This is a value of :scheme header
|
||||
field.
|
||||
|
||||
method
|
||||
Method of this stream. This is a value of :method header field.
|
||||
|
||||
host
|
||||
This is a value of :authority or host header field.
|
||||
|
||||
path
|
||||
This is a value of :path header field.
|
||||
|
||||
The following example illustrates the HTTP2Server and
|
||||
BaseRequestHandler usage:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
#!/usr/bin/env python
|
||||
|
||||
import io, ssl
|
||||
import nghttp2
|
||||
|
||||
class Handler(nghttp2.BaseRequestHandler):
|
||||
|
||||
def on_headers(self):
|
||||
self.push(path='/css/bootstrap.css',
|
||||
request_headers = [('content-length', '3')],
|
||||
status=200,
|
||||
body='foo')
|
||||
|
||||
self.push(path='/js/bootstrap.js',
|
||||
method='GET',
|
||||
request_headers = [('content-length', '10')],
|
||||
status=200,
|
||||
body='foobarbuzz')
|
||||
|
||||
self.send_response(status=200,
|
||||
headers = [('content-type', 'text/plain')],
|
||||
body=io.BytesIO(b'nghttp2-python FTW'))
|
||||
|
||||
ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
|
||||
ctx.options = ssl.OP_ALL | ssl.OP_NO_SSLv2
|
||||
ctx.load_cert_chain('server.crt', 'server.key')
|
||||
|
||||
# give None to ssl to make the server non-SSL/TLS
|
||||
server = nghttp2.HTTP2Server(('127.0.0.1', 8443), Handler, ssl=ctx)
|
||||
server.serve_forever()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# nghttp2 - HTTP/2.0 C Library
|
||||
# nghttp2 - HTTP/2 C Library
|
||||
#
|
||||
# Copyright (c) 2013 Tatsuhiro Tsujikawa
|
||||
#
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# nghttp2 - HTTP/2.0 C Library
|
||||
# nghttp2 - HTTP/2 C Library
|
||||
#
|
||||
# Copyright (c) 2013 Tatsuhiro Tsujikawa
|
||||
#
|
||||
|
||||
137
configure.ac
137
configure.ac
@@ -1,4 +1,4 @@
|
||||
dnl nghttp2 - HTTP/2.0 C Library
|
||||
dnl nghttp2 - HTTP/2 C Library
|
||||
|
||||
dnl Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa
|
||||
|
||||
@@ -21,13 +21,13 @@ dnl LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
dnl OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
dnl WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
AC_PREREQ(2.61)
|
||||
AC_INIT([nghttp2], [0.3.2], [t-tujikawa@users.sourceforge.net])
|
||||
AC_INIT([nghttp2], [0.4.0], [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, 2)
|
||||
AC_SUBST(LT_REVISION, 2)
|
||||
AC_SUBST(LT_CURRENT, 3)
|
||||
AC_SUBST(LT_REVISION, 0)
|
||||
AC_SUBST(LT_AGE, 0)
|
||||
|
||||
major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"`
|
||||
@@ -45,13 +45,26 @@ AC_CANONICAL_TARGET
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
|
||||
AM_INIT_AUTOMAKE([subdir-objects])
|
||||
|
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
|
||||
dnl Checks for command-line options
|
||||
AC_ARG_ENABLE([maintainer-mode],
|
||||
[AS_HELP_STRING([--enable-maintainer-mode],
|
||||
AC_ARG_ENABLE([werror],
|
||||
[AS_HELP_STRING([--enable-werror],
|
||||
[Turn on compile time warnings])],
|
||||
[maintainer_mode=$enableval], [maintainer_mode=no])
|
||||
[werror=$enableval], [werror=no])
|
||||
|
||||
AC_ARG_ENABLE([debug],
|
||||
[AS_HELP_STRING([--enable-debug],
|
||||
[Turn on debug output])],
|
||||
[debug=$enableval], [debug=no])
|
||||
|
||||
AC_ARG_ENABLE([threads],
|
||||
[AS_HELP_STRING([--disable-threads],
|
||||
[Turn off threading in apps])],
|
||||
[threads=$enableval], [threads=yes])
|
||||
|
||||
AC_ARG_ENABLE([app],
|
||||
[AS_HELP_STRING([--enable-app],
|
||||
@@ -83,6 +96,16 @@ AC_ARG_WITH([libxml2],
|
||||
[Use libxml2 [default=check]])],
|
||||
[request_libxml2=$withval], [request_libxml2=check])
|
||||
|
||||
AC_ARG_WITH([jemalloc],
|
||||
[AS_HELP_STRING([--with-jemalloc],
|
||||
[Use jemalloc [default=check]])],
|
||||
[request_jemalloc=$withval], [request_jemalloc=check])
|
||||
|
||||
AC_ARG_WITH([spdylay],
|
||||
[AS_HELP_STRING([--with-spdylay],
|
||||
[Use spdylay [default=check]])],
|
||||
[request_spdylay=$withval], [request_spdylay=check])
|
||||
|
||||
AC_ARG_WITH([cython],
|
||||
[AS_HELP_STRING([--with-cython=PATH],
|
||||
[Use cython in given PATH])],
|
||||
@@ -170,11 +193,13 @@ esac
|
||||
# zlib
|
||||
if test "x$android_build" = "xyes"; then
|
||||
# Use zlib provided by NDK
|
||||
LIBS="-lz $LIBS"
|
||||
SRC_LIBS="-lz $SRC_LIBS"
|
||||
else
|
||||
PKG_CHECK_MODULES([ZLIB], [zlib >= 1.2.3])
|
||||
LIBS="$ZLIB_LIBS $LIBS"
|
||||
CFLAGS="$CFLAGS $ZLIB_CFLAGS"
|
||||
PKG_CHECK_MODULES([ZLIB], [zlib >= 1.2.3], [have_zlib=yes], [have_zlib=no])
|
||||
|
||||
if test "x${have_zlib}" = "xno"; then
|
||||
AC_MSG_NOTICE($ZLIB_PKG_ERRORS)
|
||||
fi
|
||||
fi
|
||||
|
||||
# cunit
|
||||
@@ -244,21 +269,48 @@ fi
|
||||
|
||||
AM_CONDITIONAL([HAVE_LIBXML2], [ test "x${have_libxml2}" = "xyes" ])
|
||||
|
||||
# spdylay (for src/nghttpx)
|
||||
# jemalloc
|
||||
have_jemalloc=no
|
||||
if test "x${request_jemalloc}" != "xno"; then
|
||||
LIBS_OLD=$LIBS
|
||||
AC_SEARCH_LIBS([malloc_stats_print], [jemalloc], [have_jemalloc=yes])
|
||||
LIBS=$LIBS_OLD
|
||||
if test "x${have_jemalloc}" = "xyes"; then
|
||||
JEMALLOC_LIBS="-ljemalloc"
|
||||
AC_SUBST([JEMALLOC_LIBS])
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "x${request_jemalloc}" = "xyes" &&
|
||||
test "x${have_jemalloc}" != "xyes"; then
|
||||
AC_MSG_ERROR([jemalloc was requested (--with-jemalloc) but not found])
|
||||
fi
|
||||
|
||||
# spdylay (for src/nghttpx and src/h2load)
|
||||
have_spdylay=no
|
||||
if test "x${request_spdylay}" != "xno"; then
|
||||
PKG_CHECK_MODULES([LIBSPDYLAY], [libspdylay >= 1.2.3],
|
||||
[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.])
|
||||
else
|
||||
AC_MSG_NOTICE($LIBSPDYLAY_PKG_ERRORS)
|
||||
AC_MSG_NOTICE([The SPDY support in nghttpx will be disabled.])
|
||||
AC_MSG_NOTICE([The SPDY support in nghttpx and h2load will be disabled.])
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "x${request_spdylay}" = "xyes" &&
|
||||
test "x${have_spdylay}" != "xyes"; then
|
||||
AC_MSG_ERROR([spdylay was requested (--with-spdylay) but not found])
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL([HAVE_SPDYLAY], [ test "x${have_spdylay}" = "xyes" ])
|
||||
|
||||
# The nghttp, nghttpd and nghttpx under src depend on OpenSSL and
|
||||
# libevent_openssl
|
||||
# The nghttp, nghttpd and nghttpx under src depend on zlib, OpenSSL
|
||||
# and libevent_openssl
|
||||
enable_app=no
|
||||
if test "x${request_app}" != "xno" &&
|
||||
test "x${have_zlib}" = "xyes" &&
|
||||
test "x${have_openssl}" = "xyes" &&
|
||||
test "x${have_libevent_openssl}" = "xyes"; then
|
||||
enable_app=yes
|
||||
@@ -317,6 +369,10 @@ fi
|
||||
AM_CONDITIONAL([ENABLE_PYTHON_BINDINGS],
|
||||
[test "x${enable_python_bindings}" = "xyes"])
|
||||
|
||||
# Produce cython conditional, so that we can distribute generated C
|
||||
# source
|
||||
AM_CONDITIONAL([HAVE_CYTHON], [test "x${CYTHON}" != "x"])
|
||||
|
||||
# failmalloc tests
|
||||
AM_CONDITIONAL([ENABLE_FAILMALLOC], [ test "x${enable_failmalloc}" = "xyes" ])
|
||||
|
||||
@@ -370,16 +426,44 @@ case "${host}" in
|
||||
;;
|
||||
esac
|
||||
|
||||
if test "x$maintainer_mode" != "xno"; then
|
||||
CFLAGS="$CFLAGS -Wall -Wextra -Werror"
|
||||
CFLAGS="$CFLAGS -Wmissing-prototypes -Wstrict-prototypes"
|
||||
CFLAGS="$CFLAGS -Wmissing-declarations -Wpointer-arith"
|
||||
CFLAGS="$CFLAGS -Wdeclaration-after-statement"
|
||||
CFLAGS="$CFLAGS -Wformat-security"
|
||||
CFLAGS="$CFLAGS -Wwrite-strings -Wshadow -Winline -Wnested-externs"
|
||||
CFLAGS="$CFLAGS -Wfloat-equal -Wundef -Wendif-labels -Wempty-body"
|
||||
CFLAGS="$CFLAGS -Wcast-align -Wclobbered -Wvla"
|
||||
CFLAGS="$CFLAGS -Wno-unused-parameter"
|
||||
if test "x$werror" != "xno"; then
|
||||
AX_CHECK_COMPILE_FLAG([-Wall], [CFLAGS="$CFLAGS -Wall"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wextra], [CFLAGS="$CFLAGS -Wextra"])
|
||||
AX_CHECK_COMPILE_FLAG([-Werror], [CFLAGS="$CFLAGS -Werror"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wmissing-prototypes], [CFLAGS="$CFLAGS -Wmissing-prototypes"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wstrict-prototypes], [CFLAGS="$CFLAGS -Wstrict-prototypes"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wmissing-declarations], [CFLAGS="$CFLAGS -Wmissing-declarations"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wpointer-arith], [CFLAGS="$CFLAGS -Wpointer-arith"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wdeclaration-after-statement], [CFLAGS="$CFLAGS -Wdeclaration-after-statement"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wformat-security], [CFLAGS="$CFLAGS -Wformat-security"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wwrite-strings], [CFLAGS="$CFLAGS -Wwrite-strings"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wshadow], [CFLAGS="$CFLAGS -Wshadow"])
|
||||
AX_CHECK_COMPILE_FLAG([-Winline], [CFLAGS="$CFLAGS -Winline"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wnested-externs], [CFLAGS="$CFLAGS -Wnested-externs"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wfloat-equal], [CFLAGS="$CFLAGS -Wfloat-equal"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wundef], [CFLAGS="$CFLAGS -Wundef"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wendif-labels], [CFLAGS="$CFLAGS -Wendif-labels"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wempty-body], [CFLAGS="$CFLAGS -Wempty-body"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wcast-align], [CFLAGS="$CFLAGS -Wcast-align"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wclobbered], [CFLAGS="$CFLAGS -Wclobbered"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wvla], [CFLAGS="$CFLAGS -Wvla"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wno-unused-parameter], [CFLAGS="$CFLAGS -Wno-unused-parameter"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wpragmas], [CFLAGS="$CFLAGS -Wpragmas"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wunreachable-code], [CFLAGS="$CFLAGS -Wunreachable-code"])
|
||||
AX_CHECK_COMPILE_FLAG([-Waddress], [CFLAGS="$CFLAGS -Waddress"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wattributes], [CFLAGS="$CFLAGS -Wattributes"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wdiv-by-zero], [CFLAGS="$CFLAGS -Wdiv-by-zero"])
|
||||
|
||||
# Only work with Clang for the moment
|
||||
AX_CHECK_COMPILE_FLAG([-Wheader-guard], [CFLAGS="$CFLAGS -Wheader-guard"])
|
||||
fi
|
||||
|
||||
if test "x$debug" != "xno"; then
|
||||
AC_DEFINE([DEBUGBUILD], [1], [Define to 1 to enable debug output.])
|
||||
fi
|
||||
|
||||
if test "x$threads" != "xyes"; then
|
||||
AC_DEFINE([NOTHREADS], [1], [Define to 1 if you want to disable threads.])
|
||||
fi
|
||||
|
||||
AC_SUBST([TESTS_LIBS])
|
||||
@@ -397,12 +481,14 @@ AC_CONFIG_FILES([
|
||||
src/Makefile
|
||||
examples/Makefile
|
||||
python/Makefile
|
||||
python/setup.py
|
||||
doc/Makefile
|
||||
doc/conf.py
|
||||
doc/index.rst
|
||||
doc/package_README.rst
|
||||
doc/tutorial-client.rst
|
||||
doc/tutorial-server.rst
|
||||
doc/nghttpx-howto.rst
|
||||
doc/nghttp2.h.rst
|
||||
doc/nghttp2ver.h.rst
|
||||
])
|
||||
@@ -436,6 +522,7 @@ AC_MSG_NOTICE([summary of build options:
|
||||
Libevent(SSL): ${have_libevent_openssl}
|
||||
Spdylay: ${have_spdylay}
|
||||
Jansson: ${have_jansson}
|
||||
Jemalloc: ${have_jemalloc}
|
||||
Applications: ${enable_app}
|
||||
HPACK tools: ${enable_hpack_tools}
|
||||
Examples: ${enable_examples}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# nghttp2 - HTTP/2.0 C Library
|
||||
# nghttp2 - HTTP/2 C Library
|
||||
|
||||
# Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
man_MANS = nghttp.1 nghttpd.1 nghttpx.1
|
||||
man_MANS = nghttp.1 nghttpd.1 nghttpx.1 h2load.1
|
||||
|
||||
EXTRA_DIST = \
|
||||
mkapiref.py \
|
||||
@@ -30,6 +30,7 @@ EXTRA_DIST = \
|
||||
sources/index.rst \
|
||||
sources/tutorial-client.rst \
|
||||
sources/tutorial-server.rst \
|
||||
sources/nghttpx-howto.rst \
|
||||
_themes/sphinx_rtd_theme/footer.html \
|
||||
_themes/sphinx_rtd_theme/theme.conf \
|
||||
_themes/sphinx_rtd_theme/layout_old.html \
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
<li>{{ title }}</li>
|
||||
<li class="wy-breadcrumbs-aside">
|
||||
{% if display_github %}
|
||||
<a href="https://github.com/{{ github_user }}/{{ github_repo }}/blob/{{ github_version }}{{ conf_py_path }}{{ pagename }}.rst" class="fa fa-github"> Edit on GitHub</a>
|
||||
<a href="https://github.com/{{ github_user }}/{{ github_repo }}/blob/{{ github_version }}{{ conf_py_path }}{{ pagename }}{{ source_suffix }}" class="fa fa-github"> Edit on GitHub</a>
|
||||
{% elif display_bitbucket %}
|
||||
<a href="https://bitbucket.org/{{ bitbucket_user }}/{{ bitbucket_repo }}/src/{{ bitbucket_version}}{{ conf_py_path }}{{ pagename }}.rst" class="fa fa-bitbucket"> Edit on Bitbucket</a>
|
||||
<a href="https://bitbucket.org/{{ bitbucket_user }}/{{ bitbucket_repo }}/src/{{ bitbucket_version}}{{ conf_py_path }}{{ pagename }}{{ source_suffix }}" class="fa fa-bitbucket"> Edit on Bitbucket</a>
|
||||
{% elif show_source and has_source and sourcename %}
|
||||
<a href="{{ pathto('_sources/' + sourcename, true)|e }}" rel="nofollow"> View page source</a>
|
||||
{% endif %}
|
||||
|
||||
2
doc/_themes/sphinx_rtd_theme/footer.html
vendored
2
doc/_themes/sphinx_rtd_theme/footer.html
vendored
@@ -2,7 +2,7 @@
|
||||
{% if next or prev %}
|
||||
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
|
||||
{% if next %}
|
||||
<a href="{{ next.link|e }}" class="btn btn-neutral float-right" title="{{ next.title|striptags|e }}"/>Next <span class="fa fa-arrow-circle-right"></span></a>
|
||||
<a href="{{ next.link|e }}" class="btn btn-neutral float-right" title="{{ next.title|striptags|e }}">Next <span class="fa fa-arrow-circle-right"></span></a>
|
||||
{% endif %}
|
||||
{% if prev %}
|
||||
<a href="{{ prev.link|e }}" class="btn btn-neutral" title="{{ prev.title|striptags|e }}"><span class="fa fa-arrow-circle-left"></span> Previous</a>
|
||||
|
||||
63
doc/_themes/sphinx_rtd_theme/layout.html
vendored
63
doc/_themes/sphinx_rtd_theme/layout.html
vendored
@@ -25,41 +25,17 @@
|
||||
{# CSS #}
|
||||
<link href='https://fonts.googleapis.com/css?family=Lato:400,700|Roboto+Slab:400,700|Inconsolata:400,700' rel='stylesheet' type='text/css'>
|
||||
|
||||
{# JS #}
|
||||
{# OPENSEARCH #}
|
||||
{% if not embedded %}
|
||||
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT:'{{ url_root }}',
|
||||
VERSION:'{{ release|e }}',
|
||||
COLLAPSE_INDEX:false,
|
||||
FILE_SUFFIX:'{{ '' if no_search_suffix else file_suffix }}',
|
||||
HAS_SOURCE: {{ has_source|lower }}
|
||||
};
|
||||
</script>
|
||||
{%- for scriptfile in script_files %}
|
||||
<script type="text/javascript" src="{{ pathto(scriptfile, 1) }}"></script>
|
||||
{%- endfor %}
|
||||
|
||||
{% if use_opensearch %}
|
||||
<link rel="search" type="application/opensearchdescription+xml" title="{% trans docstitle=docstitle|e %}Search within {{ docstitle }}{% endtrans %}" href="{{ pathto('_static/opensearch.xml', 1) }}"/>
|
||||
{% endif %}
|
||||
|
||||
{% endif %}
|
||||
|
||||
{# RTD hosts these file themselves, so just load on non RTD builds #}
|
||||
{# RTD hosts this file, so just load on non RTD builds #}
|
||||
{% if not READTHEDOCS %}
|
||||
<link rel="stylesheet" href="{{ pathto('_static/' + style, 1) }}" type="text/css" />
|
||||
<script type="text/javascript" src="{{ pathto('_static/js/theme.js', 1) }}"></script>
|
||||
{% endif %}
|
||||
|
||||
{# STICKY NAVIGATION #}
|
||||
{% if theme_sticky_navigation %}
|
||||
<script type="text/javascript">
|
||||
jQuery(function () {
|
||||
SphinxRtdTheme.StickyNav.enable();
|
||||
});
|
||||
</script>
|
||||
{% endif %}
|
||||
|
||||
{% for cssfile in css_files %}
|
||||
@@ -94,6 +70,7 @@
|
||||
{%- endblock %}
|
||||
{%- block extrahead %} {% endblock %}
|
||||
|
||||
{# Keep modernizr in head - http://modernizr.com/docs/#installing #}
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/modernizr/2.6.2/modernizr.min.js"></script>
|
||||
|
||||
</head>
|
||||
@@ -145,5 +122,39 @@
|
||||
|
||||
</div>
|
||||
{% include "versions.html" %}
|
||||
|
||||
{% if not embedded %}
|
||||
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT:'{{ url_root }}',
|
||||
VERSION:'{{ release|e }}',
|
||||
COLLAPSE_INDEX:false,
|
||||
FILE_SUFFIX:'{{ '' if no_search_suffix else file_suffix }}',
|
||||
HAS_SOURCE: {{ has_source|lower }}
|
||||
};
|
||||
</script>
|
||||
{%- for scriptfile in script_files %}
|
||||
<script type="text/javascript" src="{{ pathto(scriptfile, 1) }}"></script>
|
||||
{%- endfor %}
|
||||
|
||||
{% endif %}
|
||||
|
||||
{# RTD hosts this file, so just load on non RTD builds #}
|
||||
{% if not READTHEDOCS %}
|
||||
<script type="text/javascript" src="{{ pathto('_static/js/theme.js', 1) }}"></script>
|
||||
{% endif %}
|
||||
|
||||
{# STICKY NAVIGATION #}
|
||||
{% if theme_sticky_navigation %}
|
||||
<script type="text/javascript">
|
||||
jQuery(function () {
|
||||
SphinxRtdTheme.StickyNav.enable();
|
||||
});
|
||||
</script>
|
||||
{% endif %}
|
||||
|
||||
{%- block footer %} {% endblock %}
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
2
doc/_themes/sphinx_rtd_theme/search.html
vendored
2
doc/_themes/sphinx_rtd_theme/search.html
vendored
@@ -10,7 +10,7 @@
|
||||
{%- extends "layout.html" %}
|
||||
{% set title = _('Search') %}
|
||||
{% set script_files = script_files + ['_static/searchtools.js'] %}
|
||||
{% block extrahead %}
|
||||
{% block footer %}
|
||||
<script type="text/javascript">
|
||||
jQuery(function() { Search.loadIndex("{{ pathto('searchindex.js', 1) }}"); });
|
||||
</script>
|
||||
|
||||
@@ -1 +1 @@
|
||||
.font-smooth,.fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-weight:normal;font-style:normal;src:url("../font/fontawesome_webfont.eot");src:url("../font/fontawesome_webfont.eot?#iefix") format("embedded-opentype"),url("../font/fontawesome_webfont.woff") format("woff"),url("../font/fontawesome_webfont.ttf") format("truetype"),url("../font/fontawesome_webfont.svg#FontAwesome") format("svg")}.fa:before{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa{display:inline-block;text-decoration:inherit}li .fa{display:inline-block}li .fa-large:before,li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-0.8em}ul.fas li .fa{width:0.8em}ul.fas li .fa-large:before,ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before{content:"\f02d"}.icon-book:before{content:"\f02d"}.fa-caret-down:before{content:"\f0d7"}.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{width:100%;height:auto}}
|
||||
.fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-weight:normal;font-style:normal;src:url("../font/fontawesome_webfont.eot");src:url("../font/fontawesome_webfont.eot?#iefix") format("embedded-opentype"),url("../font/fontawesome_webfont.woff") format("woff"),url("../font/fontawesome_webfont.ttf") format("truetype"),url("../font/fontawesome_webfont.svg#FontAwesome") format("svg")}.fa:before{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa{display:inline-block;text-decoration:inherit}li .fa{display:inline-block}li .fa-large:before,li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-0.8em}ul.fas li .fa{width:0.8em}ul.fas li .fa-large:before,ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before{content:"\f02d"}.icon-book:before{content:"\f02d"}.fa-caret-down:before{content:"\f0d7"}.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{width:100%;height:auto}}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -14,9 +14,18 @@ The header files are also available online: :doc:`nghttp2.h` and
|
||||
Remarks
|
||||
-------
|
||||
|
||||
Do not call `nghttp2_session_send`, `nghttp2_session_recv` or
|
||||
`nghttp2_session_mem_recv` from the nghttp2 callback functions
|
||||
directly or indirectly. It will lead to the crash. You can submit
|
||||
requests or frames in the callbacks then call `nghttp2_session_send`,
|
||||
`nghttp2_session_recv` or `nghttp2_session_mem_recv` outside of the
|
||||
callbacks.
|
||||
Do not call `nghttp2_session_send()`, `nghttp2_session_mem_send()`,
|
||||
`nghttp2_session_recv()` or `nghttp2_session_mem_recv()` from the
|
||||
nghttp2 callback functions directly or indirectly. It will lead to the
|
||||
crash. You can submit requests or frames in the callbacks then call
|
||||
these functions outside the callbacks.
|
||||
|
||||
Currently, `nghttp2_session_send()` and `nghttp2_session_mem_send()`
|
||||
do not send client connection preface
|
||||
(:macro:`NGHTTP2_CLIENT_CONNECTION_PREFACE`). The applications are
|
||||
responsible to send it before sending any HTTP/2 frames using these
|
||||
functions if :type:`nghttp2_session` is configured as client.
|
||||
Similarly, `nghttp2_session_recv()` and `nghttp2_session_mem_recv()`
|
||||
do not consume client connection preface. The applications are
|
||||
responsible to receive it before calling these functions if
|
||||
:type:`nghttp2_session` is configured as server.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# nghttp2 - HTTP/2.0 C Library
|
||||
# nghttp2 - HTTP/2 C Library
|
||||
|
||||
# Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
|
||||
|
||||
61
doc/h2load.1
Normal file
61
doc/h2load.1
Normal file
@@ -0,0 +1,61 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.45.1.
|
||||
.TH H2LOAD "1" "May 2014" "h2load nghttp2/0.4.0" "User Commands"
|
||||
.SH NAME
|
||||
h2load \- HTTP/2 benchmarking tool
|
||||
.SH SYNOPSIS
|
||||
.B h2load
|
||||
[\fI\,OPTIONS\/\fR]... \fI\,<URI>\/\fR...
|
||||
.SH DESCRIPTION
|
||||
benchmarking tool for HTTP/2 and SPDY server
|
||||
.TP
|
||||
<URI>
|
||||
Specify URI to access. Multiple URIs can be
|
||||
specified. URIs are used in this order for each
|
||||
client. All URIs are used, then first URI is
|
||||
used and then 2nd URI, and so on. The scheme,
|
||||
host and port in the subsequent URIs, if present,
|
||||
are ignored. Those in the first URI are used
|
||||
solely.
|
||||
.SH OPTIONS
|
||||
.HP
|
||||
\fB\-n\fR, \fB\-\-requests=\fR<N> Number of requests. Default: 1
|
||||
.TP
|
||||
\fB\-c\fR, \fB\-\-clients=\fR<N>
|
||||
Number of concurrent clients. Default: 1
|
||||
.TP
|
||||
\fB\-t\fR, \fB\-\-threads=\fR<N>
|
||||
Number of native threads. Default: 1
|
||||
.TP
|
||||
\fB\-m\fR, \fB\-\-max\-concurrent\-streams=\fR(auto|<N>)
|
||||
Max concurrent streams to issue per session. If
|
||||
"auto" is given, the number of given URIs is
|
||||
used. Default: auto
|
||||
.TP
|
||||
\fB\-w\fR, \fB\-\-window\-bits=\fR<N>
|
||||
Sets the stream level initial window size to
|
||||
(2**<N>)\-1. For SPDY, 2**<N> is used instead.
|
||||
.TP
|
||||
\fB\-W\fR, \fB\-\-connection\-window\-bits=\fR<N>
|
||||
Sets the connection level initial window size to
|
||||
(2**<N>)\-1. For SPDY, if <N> is strictly less
|
||||
than 16, this option is ignored. Otherwise
|
||||
2**<N> is used for SPDY.
|
||||
.TP
|
||||
\fB\-p\fR, \fB\-\-no\-tls\-proto=\fR<PROTOID>
|
||||
Specify ALPN identifier of the protocol to be
|
||||
used when accessing http URI without SSL/TLS.
|
||||
Available protocols: spdy/2, spdy/3, spdy/3.1 and
|
||||
h2c\-12
|
||||
Default: h2c\-12
|
||||
.TP
|
||||
\fB\-v\fR, \fB\-\-verbose\fR
|
||||
Output debug information.
|
||||
.TP
|
||||
\fB\-\-version\fR
|
||||
Display version information and exit.
|
||||
.TP
|
||||
\fB\-h\fR, \fB\-\-help\fR
|
||||
Display this help and exit.
|
||||
.SH "SEE ALSO"
|
||||
|
||||
nghttp(1), nghttpd(1), nghttpx(1)
|
||||
3
doc/h2load.h2m
Normal file
3
doc/h2load.h2m
Normal file
@@ -0,0 +1,3 @@
|
||||
[SEE ALSO]
|
||||
|
||||
nghttp(1), nghttpd(1), nghttpx(1)
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# nghttp2 - HTTP/2.0 C Library
|
||||
# nghttp2 - HTTP/2 C Library
|
||||
|
||||
# Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
|
||||
|
||||
105
doc/nghttp.1
105
doc/nghttp.1
@@ -1,43 +1,46 @@
|
||||
.\" nghttp2 manual page
|
||||
.TH nghttp2 "1" "January 2014" "nghttp2" "User Commands"
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.45.1.
|
||||
.TH NGHTTP "1" "May 2014" "nghttp nghttp2/0.4.0" "User Commands"
|
||||
.SH NAME
|
||||
nghttp2 \- HTTP2 experimental client
|
||||
nghttp \- HTTP/2 experimental client
|
||||
.SH SYNOPSIS
|
||||
\fBnghttp\fP [\fIOPTIONS\fP] \fIURI\fP...
|
||||
.B nghttp
|
||||
[\fI\,OPTIONS\/\fR]... \fI\,<URI>\/\fR...
|
||||
.SH DESCRIPTION
|
||||
Experimental client for HTTP 2.0.
|
||||
HTTP/2 experimental client
|
||||
.TP
|
||||
<URI>
|
||||
Specify URI to access.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB\-v\fR, \fB\-\-verbose\fR
|
||||
Print debug information such as reception/
|
||||
Print debug information such as reception and
|
||||
transmission of frames and name/value pairs.
|
||||
.TP
|
||||
\fB\-n\fR, \fB\-\-null\-out\fR
|
||||
Discard downloaded data.
|
||||
.TP
|
||||
\fB\-O\fR, \fB\-\-remote\-name\fR
|
||||
Save download data in the current directory.
|
||||
The filename is dereived from URI. If URI
|
||||
ends with '/', 'index.html' is used as a
|
||||
filename. Not implemented yet.
|
||||
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.
|
||||
.TP
|
||||
\fB\-t\fR, \fB\-\-timeout=\fR<N>
|
||||
Timeout each request after <N> seconds.
|
||||
.TP
|
||||
\fB\-w\fR, \fB\-\-window\-bits=\fR<N>
|
||||
Sets the stream level initial window size
|
||||
to 2**<N>\-1.
|
||||
Sets the stream level initial window size to
|
||||
2**<N>\-1.
|
||||
.TP
|
||||
\fB\-W\fR, \fB\-\-connection\-window\-bits=\fR<N>
|
||||
Sets the connection level initial window
|
||||
size to 2**<N>\-1.
|
||||
Sets the connection level initial window size to
|
||||
2**<N>\-1.
|
||||
.TP
|
||||
\fB\-a\fR, \fB\-\-get\-assets\fR
|
||||
Download assets such as stylesheets, images
|
||||
and script files linked from the downloaded
|
||||
resource. Only links whose origins are the
|
||||
same with the linking resource will be
|
||||
downloaded.
|
||||
Download assets such as stylesheets, images and
|
||||
script files linked from the downloaded resource.
|
||||
Only links whose origins are the same with the
|
||||
linking resource will be downloaded.
|
||||
.TP
|
||||
\fB\-s\fR, \fB\-\-stat\fR
|
||||
Print statistics.
|
||||
@@ -46,45 +49,61 @@ Print statistics.
|
||||
Add a header to the requests.
|
||||
.TP
|
||||
\fB\-\-cert=\fR<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.
|
||||
.TP
|
||||
\fB\-\-key=\fR<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.
|
||||
.TP
|
||||
\fB\-d\fR, \fB\-\-data=\fR<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.
|
||||
.TP
|
||||
\fB\-m\fR, \fB\-\-multiply=\fR<N> Request each URI <N> times. By default, same
|
||||
URI is not requested twice. This option
|
||||
disables it too.
|
||||
\fB\-g\fR, \fB\-\-compress\-data\fR
|
||||
When used with \fB\-d\fR option, compress request body
|
||||
on the fly using per\-frame compression.
|
||||
.TP
|
||||
\fB\-f\fR, \fB\-\-no\-flow\-control\fR
|
||||
Disables connection and stream level flow
|
||||
controls.
|
||||
\fB\-m\fR, \fB\-\-multiply=\fR<N> Request each URI <N> times.
|
||||
By default, same URI
|
||||
is not requested twice. This option disables it
|
||||
too.
|
||||
.TP
|
||||
\fB\-u\fR, \fB\-\-upgrade\fR
|
||||
Perform HTTP Upgrade for HTTP/2.0. This
|
||||
option is ignored if the request URI has
|
||||
https scheme.
|
||||
If \fB\-d\fR is used, the HTTP upgrade request is
|
||||
performed with OPTIONS method.
|
||||
Perform HTTP Upgrade for HTTP/2. This option is
|
||||
ignored if the request URI has https scheme. If
|
||||
\fB\-d\fR is used, the HTTP upgrade request is performed
|
||||
with OPTIONS method.
|
||||
.TP
|
||||
\fB\-p\fR, \fB\-\-pri=\fR<PRIORITY>
|
||||
Sets stream priority. Default: 1073741824
|
||||
\fB\-p\fR, \fB\-\-weight=\fR<WEIGHT>
|
||||
Sets priority group weight. The valid value
|
||||
range is [1, 256], inclusive.
|
||||
Default: 16
|
||||
.TP
|
||||
\fB\-M\fR, \fB\-\-peer\-max\-concurrent\-streams=\fR<N>
|
||||
Use <N> as SETTINGS_MAX_CONCURRENT_STREAMS
|
||||
value of remote endpoint as if it is
|
||||
received in SETTINGS frame. The default
|
||||
is large enough as it is seen as unlimited.
|
||||
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.
|
||||
.TP
|
||||
\fB\-c\fR, \fB\-\-header\-table\-size=\fR<N>
|
||||
Specify decoder header table size.
|
||||
.TP
|
||||
\fB\-b\fR, \fB\-\-padding=\fR<N>
|
||||
Add at most <N> bytes to a frame payload as
|
||||
padding. Specify 0 to disable padding.
|
||||
.TP
|
||||
\fB\-\-color\fR
|
||||
Force colored log output.
|
||||
.TP
|
||||
\fB\-\-continuation\fR
|
||||
Send large header to test CONTINUATION.
|
||||
.TP
|
||||
\fB\-\-version\fR
|
||||
Display version information and exit.
|
||||
.TP
|
||||
\fB\-h\fR, \fB\-\-help\fR
|
||||
Display this help and exit.
|
||||
.SH "SEE ALSO"
|
||||
nghttpd(1), nghttpx(1)
|
||||
|
||||
nghttpd(1), nghttpx(1), h2load(1)
|
||||
|
||||
3
doc/nghttp.h2m
Normal file
3
doc/nghttp.h2m
Normal file
@@ -0,0 +1,3 @@
|
||||
[SEE ALSO]
|
||||
|
||||
nghttpd(1), nghttpx(1), h2load(1)
|
||||
@@ -1,39 +1,45 @@
|
||||
.\" nghttpd manual page
|
||||
.TH nghttpd "1" "January 2014" "nghttpd" "User Commands"
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.45.1.
|
||||
.TH NGHTTPD "1" "May 2014" "nghttpd nghttp2/0.4.0" "User Commands"
|
||||
.SH NAME
|
||||
nghttpd \- HTTP 2.0 experimental server
|
||||
nghttpd \- HTTP/2 experimental server
|
||||
.SH SYNOPSIS
|
||||
\fBnghttpd\fP [\fIOPTIONS\fP...] [\fIPRIVATE_KEY\fP \fICERT\fP]
|
||||
.B nghttpd
|
||||
[\fI\,OPTION\/\fR]... \fI\,<PORT> <PRIVATE_KEY> <CERT>\/\fR
|
||||
.br
|
||||
.B nghttpd
|
||||
\fI\,--no-tls \/\fR[\fI\,OPTION\/\fR]... \fI\,<PORT>\/\fR
|
||||
.SH DESCRIPTION
|
||||
Experimental HTTP 2.0 server.
|
||||
.SH "Positional arguments"
|
||||
HTTP/2 experimental server
|
||||
.TP
|
||||
\fIPRIVATE_KEY\fP
|
||||
<PORT>
|
||||
Specify listening port number.
|
||||
.TP
|
||||
<PRIVATE_KEY>
|
||||
Set path to server's private key. Required
|
||||
unless either \fB\-p\fR or \fB\-\-client\fR is specified.
|
||||
unless \fB\-\-no\-tls\fR is specified.
|
||||
.TP
|
||||
\fICERT\fP
|
||||
<CERT>
|
||||
Set path to server's certificate. Required
|
||||
unless either \fB\-p\fR or \fB\-\-client\fR is specified.
|
||||
unless \fB\-\-no\-tls\fR is specified.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB\-D\fR, \fB\-\-daemon\fR
|
||||
Run in a background. If \fB\-D\fR is used, the
|
||||
current working directory is changed to '/'.
|
||||
Therefore if this option is used, \fB\-d\fR option
|
||||
must be specified.
|
||||
Run in a background. If \fB\-D\fR is used, the current
|
||||
working directory is changed to '/'. Therefore
|
||||
if this option is used, \fB\-d\fR option must be
|
||||
specified.
|
||||
.TP
|
||||
\fB\-V\fR, \fB\-\-verify\-client\fR
|
||||
The server sends a client certificate
|
||||
request. If the client did not return a
|
||||
certificate, the handshake is terminated.
|
||||
Currently, this option just requests a
|
||||
client certificate and does not verify it.
|
||||
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.
|
||||
.TP
|
||||
\fB\-d\fR, \fB\-\-htdocs=\fR<PATH>
|
||||
Specify document root. If this option is
|
||||
not specified, the document root is the
|
||||
current working directory.
|
||||
Specify document root. If this option is not
|
||||
specified, the document root is the current
|
||||
working directory.
|
||||
.TP
|
||||
\fB\-v\fR, \fB\-\-verbose\fR
|
||||
Print debug information such as reception/
|
||||
@@ -42,14 +48,36 @@ transmission of frames and name/value pairs.
|
||||
\fB\-\-no\-tls\fR
|
||||
Disable SSL/TLS.
|
||||
.TP
|
||||
\fB\-f\fR, \fB\-\-no\-flow\-control\fR
|
||||
Disables connection and stream level flow
|
||||
controls.
|
||||
\fB\-c\fR, \fB\-\-header\-table\-size=\fR<N>
|
||||
Specify decoder header table size.
|
||||
.TP
|
||||
\fB\-\-color\fR
|
||||
Force colored log output.
|
||||
.TP
|
||||
\fB\-p\fR, \fB\-\-push=\fR<PATH>=<PUSH_PATH,...>
|
||||
Push resources <PUSH_PATH>s when <PATH> is
|
||||
requested. This option can be used repeatedly to
|
||||
specify multiple push configurations. <PATH> and
|
||||
<PUSH_PATH>s are relative to document root. See
|
||||
\fB\-\-htdocs\fR option. Example: \fB\-p\fR/=/foo.png
|
||||
\fB\-p\fR/doc=/bar.css
|
||||
.TP
|
||||
\fB\-b\fR, \fB\-\-padding=\fR<N>
|
||||
Add at most <N> bytes to a frame payload as
|
||||
padding. Specify 0 to disable padding.
|
||||
.TP
|
||||
\fB\-n\fR, \fB\-\-workers=\fR<CORE>
|
||||
Set the number of worker threads.
|
||||
Default: 1
|
||||
.TP
|
||||
\fB\-e\fR, \fB\-\-error\-gzip\fR
|
||||
Make error response gzipped.
|
||||
.TP
|
||||
\fB\-\-version\fR
|
||||
Display version information and exit.
|
||||
.TP
|
||||
\fB\-h\fR, \fB\-\-help\fR
|
||||
Print this help.
|
||||
Display this help and exit.
|
||||
.SH "SEE ALSO"
|
||||
nghttp(1), nghttpx(1)
|
||||
|
||||
nghttp(1), nghttpx(1), h2load(1)
|
||||
|
||||
3
doc/nghttpd.h2m
Normal file
3
doc/nghttpd.h2m
Normal file
@@ -0,0 +1,3 @@
|
||||
[SEE ALSO]
|
||||
|
||||
nghttp(1), nghttpx(1), h2load(1)
|
||||
1
doc/nghttpx-howto.rst.in
Normal file
1
doc/nghttpx-howto.rst.in
Normal file
@@ -0,0 +1 @@
|
||||
.. include:: @top_srcdir@/doc/sources/nghttpx-howto.rst
|
||||
366
doc/nghttpx.1
366
doc/nghttpx.1
@@ -1,27 +1,26 @@
|
||||
.\" nghttpx manual page
|
||||
.TH nghttpx "1" "January 2014" "nghttpx" "User Commands"
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.45.1.
|
||||
.TH NGHTTPX "1" "May 2014" "nghttpx nghttp2/0.4.0" "User Commands"
|
||||
.SH NAME
|
||||
nghttpx \- HTTP 2.0 experimental proxy
|
||||
nghttpx \- HTTP/2 experimental proxy
|
||||
.SH SYNOPSIS
|
||||
\fBnghttpx\fP [\fIOPTIONS\fP...] [\fIPRIVATE_KEY\fP \fICERT\fP]
|
||||
.B nghttpx
|
||||
[\fI\,OPTIONS\/\fR]... [\fI\,<PRIVATE_KEY> <CERT>\/\fR]
|
||||
.SH DESCRIPTION
|
||||
Experimental HTTP 2.0 reverse proxy.
|
||||
.LP
|
||||
The default mode is to accept HTTP/2.0, SPDY (if compiled in) and
|
||||
HTTP/1.1 over SSL/TLS. If \fB\-\-frontend\-no\-tls\fR is used, accept
|
||||
HTTP/2.0 and HTTP/1.1. The incoming HTTP/1.1 connection can be
|
||||
upgraded to HTTP/2.0 through HTTP Upgrade. The protocol to the
|
||||
backend is HTTP/1.1.
|
||||
.SH "Positional arguments"
|
||||
A reverse proxy for HTTP/2, HTTP/1 and SPDY.
|
||||
.TP
|
||||
\fIPRIVATE_KEY\fP
|
||||
<PRIVATE_KEY>
|
||||
Set path to server's private key. Required
|
||||
unless either \fB\-p\fR or \fB\-\-client\fR is specified.
|
||||
unless \fB\-p\fR, \fB\-\-client\fR or \fB\-\-frontend\-no\-tls\fR are
|
||||
given.
|
||||
.TP
|
||||
\fICERT\fP
|
||||
<CERT>
|
||||
Set path to server's certificate. Required
|
||||
unless either \fB\-p\fR or \fB\-\-client\fR is specified.
|
||||
unless \fB\-p\fR, \fB\-\-client\fR or \fB\-\-frontend\-no\-tls\fR are
|
||||
given.
|
||||
.SH OPTIONS
|
||||
.IP
|
||||
The options are categorized into several groups.
|
||||
.SS "Connections:"
|
||||
.TP
|
||||
\fB\-b\fR, \fB\-\-backend=\fR<HOST,PORT>
|
||||
Set backend host and port.
|
||||
@@ -32,207 +31,278 @@ Set frontend host and port.
|
||||
Default: '0.0.0.0,3000'
|
||||
.TP
|
||||
\fB\-\-backlog=\fR<NUM>
|
||||
Set listen backlog size.
|
||||
Default: 256
|
||||
Set listen backlog size. If \fB\-1\fR is given,
|
||||
libevent will choose suitable value.
|
||||
Default: \fB\-1\fR
|
||||
.TP
|
||||
\fB\-\-backend\-ipv4\fR
|
||||
Resolve backend hostname to IPv4 address
|
||||
only.
|
||||
Resolve backend hostname to IPv4 address only.
|
||||
.TP
|
||||
\fB\-\-backend\-ipv6\fR
|
||||
Resolve backend hostname to IPv6 address
|
||||
only.
|
||||
Resolve backend hostname to IPv6 address only.
|
||||
.SS "Performance:"
|
||||
.TP
|
||||
\fB\-n\fR, \fB\-\-workers=\fR<CORES>
|
||||
Set the number of worker threads.
|
||||
Default: 1
|
||||
.TP
|
||||
\fB\-\-read\-rate=\fR<RATE> Set maximum average read rate on frontend
|
||||
connection. Setting 0 to this option means
|
||||
read rate is unlimited.
|
||||
\fB\-\-read\-rate=\fR<RATE>
|
||||
Set maximum average read rate on frontend
|
||||
connection. Setting 0 to this option means read
|
||||
rate is unlimited.
|
||||
Default: 1048576
|
||||
.TP
|
||||
\fB\-\-read\-burst=\fR<SIZE>
|
||||
Set maximum read burst size on frontend
|
||||
connection. Setting 0 to this option means
|
||||
read burst size is unlimited.
|
||||
connection. Setting 0 to this option means read
|
||||
burst size is unlimited.
|
||||
Default: 4194304
|
||||
.TP
|
||||
\fB\-\-write\-rate=\fR<RATE>
|
||||
Set maximum average write rate on frontend
|
||||
connection. Setting 0 to this option means
|
||||
write rate is unlimited.
|
||||
connection. Setting 0 to this option means write
|
||||
rate is unlimited.
|
||||
Default: 0
|
||||
.TP
|
||||
\fB\-\-write\-burst=\fR<SIZE>
|
||||
Set maximum write burst size on frontend
|
||||
connection. Setting 0 to this option means
|
||||
write burst size is unlimited.
|
||||
connection. Setting 0 to this option means write
|
||||
burst size is unlimited.
|
||||
Default: 0
|
||||
.TP
|
||||
\fB\-\-worker\-read\-rate=\fR<RATE>
|
||||
Set maximum average read rate on frontend
|
||||
connection per worker. Setting 0 to this option
|
||||
means read rate is unlimited.
|
||||
Default: 0
|
||||
.TP
|
||||
\fB\-\-worker\-read\-burst=\fR<SIZE>
|
||||
Set maximum read burst size on frontend
|
||||
connection per worker. Setting 0 to this option
|
||||
means read burst size is unlimited.
|
||||
Default: 0
|
||||
.TP
|
||||
\fB\-\-worker\-write\-rate=\fR<RATE>
|
||||
Set maximum average write rate on frontend
|
||||
connection per worker. Setting 0 to this option
|
||||
means write rate is unlimited.
|
||||
Default: 0
|
||||
.TP
|
||||
\fB\-\-worker\-write\-burst=\fR<SIZE>
|
||||
Set maximum write burst size on frontend
|
||||
connection per worker. Setting 0 to this option
|
||||
means write burst size is unlimited.
|
||||
Default: 0
|
||||
.SS "Timeout:"
|
||||
.TP
|
||||
\fB\-\-frontend\-http2\-read\-timeout=\fR<SEC>
|
||||
Specify read timeout for HTTP/2.0 and SPDY frontend
|
||||
connection. Default: 180
|
||||
Specify read timeout for HTTP/2 and SPDY frontend
|
||||
connection.
|
||||
Default: 180
|
||||
.TP
|
||||
\fB\-\-frontend\-read\-timeout=\fR<SEC>
|
||||
Specify read timeout for HTTP/1.1 frontend
|
||||
connection. Default: 180
|
||||
connection.
|
||||
Default: 180
|
||||
.TP
|
||||
\fB\-\-frontend\-write\-timeout=\fR<SEC>
|
||||
Specify write timeout for all frontends.
|
||||
connection. Default: 60
|
||||
Specify write timeout for all frontend
|
||||
connections.
|
||||
Default: 60
|
||||
.TP
|
||||
\fB\-\-backend\-read\-timeout=\fR<SEC>
|
||||
Specify read timeout for backend connection.
|
||||
Default: 900
|
||||
.TP
|
||||
\fB\-\-backend\-write\-timeout=\fR<SEC>
|
||||
Specify write timeout for backend
|
||||
connection. Default: 60
|
||||
Specify write timeout for backend connection.
|
||||
Default: 60
|
||||
.TP
|
||||
\fB\-\-backend\-keep\-alive\-timeout=\fR<SEC>
|
||||
Specify keep\-alive timeout for backend
|
||||
connection. Default: 60
|
||||
connection.
|
||||
Default: 60
|
||||
.TP
|
||||
\fB\-\-backend\-http\-proxy\-uri=\fR<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.0. 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 \fB\-\-backend\-read\-timeout\fR
|
||||
and \fB\-\-backend\-write\-timeout\fR options.
|
||||
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
|
||||
\fB\-\-backend\-read\-timeout\fR and
|
||||
\fB\-\-backend\-write\-timeout\fR options.
|
||||
.SS "SSL/TLS:"
|
||||
.TP
|
||||
\fB\-\-ciphers=\fR<SUITE>
|
||||
Set allowed cipher list. The format of the
|
||||
string is described in OpenSSL ciphers(1).
|
||||
If this option is used, \fB\-\-honor\-cipher\-order\fR
|
||||
is implicitly enabled.
|
||||
string is described in OpenSSL ciphers(1). If
|
||||
this option is used, \fB\-\-honor\-cipher\-order\fR is
|
||||
implicitly enabled.
|
||||
.TP
|
||||
\fB\-\-honor\-cipher\-order\fR
|
||||
Honor server cipher order, giving the
|
||||
ability to mitigate BEAST attacks.
|
||||
Honor server cipher order, giving the ability to
|
||||
mitigate BEAST attacks.
|
||||
.TP
|
||||
\fB\-k\fR, \fB\-\-insecure\fR
|
||||
When used with \fB\-p\fR or \fB\-\-client\fR, don't verify
|
||||
backend server's certificate.
|
||||
Don't verify backend server's certificate if \fB\-p\fR,
|
||||
\fB\-\-client\fR or \fB\-\-http2\-bridge\fR are given and
|
||||
\fB\-\-backend\-no\-tls\fR is not given.
|
||||
.TP
|
||||
\fB\-\-cacert=\fR<PATH>
|
||||
When used with \fB\-p\fR or \fB\-\-client\fR, set path to
|
||||
trusted CA certificate file.
|
||||
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 \fB\-p\fR,
|
||||
\fB\-\-client\fR or \fB\-\-http2\-bridge\fR are given and
|
||||
\fB\-\-backend\-no\-tls\fR 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.
|
||||
.TP
|
||||
\fB\-\-private\-key\-passwd\-file=\fR<FILEPATH>
|
||||
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.
|
||||
server's private key. If none is given and the
|
||||
private key is password protected it'll be
|
||||
requested interactively.
|
||||
.TP
|
||||
\fB\-\-subcert=\fR<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.
|
||||
.TP
|
||||
\fB\-\-backend\-tls\-sni\-field=\fR<HOST>
|
||||
Explicitly set the content of the TLS SNI
|
||||
extension. This will default to the backend
|
||||
HOST name.
|
||||
extension. This will default to the backend HOST
|
||||
name.
|
||||
.TP
|
||||
\fB\-\-dh\-param\-file=\fR<PATH>
|
||||
Path to file that contains DH parameters in
|
||||
PEM format. Without this option, DHE cipher
|
||||
suites are not available.
|
||||
Path to file that contains DH parameters in PEM
|
||||
format. Without this option, DHE cipher suites
|
||||
are not available.
|
||||
.TP
|
||||
\fB\-\-npn\-list=\fR<LIST>
|
||||
Comma delimited list of NPN protocol sorted
|
||||
in the order of preference. That means
|
||||
most desirable protocol comes first.
|
||||
The parameter must be delimited by a single
|
||||
comma only and any white spaces are treated
|
||||
as a part of protocol string.
|
||||
Default: HTTP\-draft\-07/2.0,http/1.1
|
||||
Comma delimited list of NPN/ALPN protocol sorted
|
||||
in the order of preference. That means most
|
||||
desirable protocol comes first. The parameter
|
||||
must be delimited by a single comma only and any
|
||||
white spaces are treated as a part of protocol
|
||||
string.
|
||||
Default: h2\-12,spdy/3.1,spdy/3,spdy/2,http/1.1
|
||||
.TP
|
||||
\fB\-\-verify\-client\fR
|
||||
Require and verify client certificate.
|
||||
.TP
|
||||
\fB\-\-verify\-client\-cacert=\fR<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.
|
||||
.TP
|
||||
\fB\-\-client\-private\-key\-file=\fR<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.
|
||||
.TP
|
||||
\fB\-\-client\-cert\-file=\fR<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.
|
||||
.TP
|
||||
\fB\-\-tls\-proto\-list=\fR<LIST>
|
||||
Comma delimited list of SSL/TLS protocol to be
|
||||
enabled. The following protocols are available:
|
||||
TLSv1.2, TLSv1.1, TLSv1.0 and SSLv3. 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,TLSv1.0
|
||||
.SS "HTTP/2 and SPDY:"
|
||||
.TP
|
||||
\fB\-c\fR, \fB\-\-http2\-max\-concurrent\-streams=\fR<NUM>
|
||||
Set the maximum number of the concurrent
|
||||
streams in one HTTP/2.0 and SPDY session.
|
||||
Set the maximum number of the concurrent streams
|
||||
in one HTTP/2 and SPDY session.
|
||||
Default: 100
|
||||
.TP
|
||||
\fB\-\-frontend\-http2\-window\-bits=\fR<N>
|
||||
Sets the initial window size of HTTP/2.0 and SPDY
|
||||
frontend connection to 2**<N>\-1.
|
||||
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
|
||||
.TP
|
||||
\fB\-\-frontend\-http2\-connection\-window\-bits=\fR<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
|
||||
.TP
|
||||
\fB\-\-frontend\-no\-tls\fR
|
||||
Disable SSL/TLS on frontend connections.
|
||||
.TP
|
||||
\fB\-\-backend\-http2\-window\-bits=\fR<N>
|
||||
Sets the initial window size of HTTP/2.0 and SPDY
|
||||
Sets the initial window size of HTTP/2 backend
|
||||
connection to 2**<N>\-1.
|
||||
Default: 16
|
||||
.TP
|
||||
\fB\-\-backend\-http2\-connection\-window\-bits=\fR<N>
|
||||
Sets the per\-connection window size of HTTP/2
|
||||
backend connection to 2**<N>\-1.
|
||||
Default: 16
|
||||
.TP
|
||||
\fB\-\-backend\-no\-tls\fR
|
||||
Disable SSL/TLS on backend connections.
|
||||
.TP
|
||||
\fB\-\-http2\-no\-cookie\-crumbling\fR
|
||||
Don't crumble cookie header field.
|
||||
.TP
|
||||
\fB\-\-padding=\fR<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.
|
||||
.SS "Mode:"
|
||||
.TP
|
||||
(default mode)
|
||||
Accept HTTP/2, SPDY and HTTP/1.1 over SSL/TLS.
|
||||
If \fB\-\-frontend\-no\-tls\fR 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.
|
||||
.TP
|
||||
\fB\-s\fR, \fB\-\-http2\-proxy\fR
|
||||
Like default mode, but enable secure proxy mode.
|
||||
.TP
|
||||
\fB\-\-http2\-bridge\fR
|
||||
Like default mode, but communicate with the
|
||||
backend in HTTP/2.0 over SSL/TLS. Thus the
|
||||
incoming all connections are converted
|
||||
to HTTP/2.0 connection and relayed to
|
||||
the backend. See \fB\-\-backend\-http\-proxy\-uri\fR
|
||||
option if you are behind the proxy and want
|
||||
to connect to the outside HTTP/2.0 proxy.
|
||||
backend in HTTP/2 over SSL/TLS. Thus the
|
||||
incoming all connections are converted to HTTP/2
|
||||
connection and relayed to the backend. See
|
||||
\fB\-\-backend\-http\-proxy\-uri\fR option if you are behind
|
||||
the proxy and want to connect to the outside
|
||||
HTTP/2 proxy.
|
||||
.TP
|
||||
\fB\-\-client\fR
|
||||
Accept HTTP/2.0 and HTTP/1.1 without SSL/TLS.
|
||||
The incoming HTTP/1.1 connection can be
|
||||
upgraded to HTTP/2.0 connection through
|
||||
HTTP Upgrade.
|
||||
The protocol to the backend is HTTP/2.0.
|
||||
To use nghttpx as a forward proxy, use \fB\-p\fR
|
||||
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 \fB\-p\fR option
|
||||
instead.
|
||||
.TP
|
||||
\fB\-p\fR, \fB\-\-client\-proxy\fR Like \fB\-\-client\fR option, but it also requires
|
||||
the request path from frontend must be
|
||||
an absolute URI, suitable for use as a
|
||||
forward proxy.
|
||||
\fB\-p\fR, \fB\-\-client\-proxy\fR
|
||||
Like \fB\-\-client\fR option, but it also requires the
|
||||
request path from frontend must be an absolute
|
||||
URI, suitable for use as a forward proxy.
|
||||
.SS "Logging:"
|
||||
.TP
|
||||
\fB\-L\fR, \fB\-\-log\-level=\fR<LEVEL>
|
||||
Set the severity level of log output.
|
||||
INFO, WARNING, ERROR and FATAL.
|
||||
Set the severity level of log output. <LEVEL>
|
||||
must be one of INFO, WARNING, ERROR and FATAL.
|
||||
Default: WARNING
|
||||
.TP
|
||||
\fB\-\-accesslog\fR
|
||||
@@ -242,32 +312,69 @@ Print simple accesslog to stderr.
|
||||
Send log messages to syslog.
|
||||
.TP
|
||||
\fB\-\-syslog\-facility=\fR<FACILITY>
|
||||
Set syslog facility.
|
||||
Set syslog facility to <FACILITY>.
|
||||
Default: daemon
|
||||
.SS "Misc:"
|
||||
.TP
|
||||
\fB\-\-add\-x\-forwarded\-for\fR
|
||||
Append X\-Forwarded\-For header field to the
|
||||
downstream request.
|
||||
.TP
|
||||
\fB\-\-no\-via\fR
|
||||
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.
|
||||
.TP
|
||||
\fB\-\-altsvc=\fR<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: \fB\-\-altsvc\fR=\fI\,h2\/\fR,443
|
||||
.TP
|
||||
\fB\-\-add\-response\-header=\fR<HEADER>
|
||||
Specify additional header field to add to
|
||||
response header set. This option just appends
|
||||
header field and won't replace anything already
|
||||
set. This option can be used several times to
|
||||
specify multiple header fields.
|
||||
Example: \fB\-\-add\-response\-header=\fR"foo: bar"
|
||||
.TP
|
||||
\fB\-\-frontend\-http2\-dump\-request\-header=\fR<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 \fB\-n\fR<N>, where <N> >= 2.
|
||||
.TP
|
||||
\fB\-\-frontend\-http2\-dump\-response\-header=\fR<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 \fB\-n\fR<N>, where <N> >= 2.
|
||||
.TP
|
||||
\fB\-o\fR, \fB\-\-frontend\-frame\-debug\fR
|
||||
Print HTTP/2 frames in frontend to stderr. This
|
||||
option is not thread safe and MUST NOT be used
|
||||
with option \fB\-n\fR=\fI\,N\/\fR, where N >= 2.
|
||||
.TP
|
||||
\fB\-D\fR, \fB\-\-daemon\fR
|
||||
Run in a background. If \fB\-D\fR is used, the
|
||||
current working directory is changed to '/'.
|
||||
Run in a background. If \fB\-D\fR is used, the current
|
||||
working directory is changed to '/'.
|
||||
.TP
|
||||
\fB\-\-pid\-file=\fR<PATH>
|
||||
Set path to save PID of this program.
|
||||
.TP
|
||||
\fB\-\-user=\fR<USER>
|
||||
Run this program as USER. This option is
|
||||
Run this program as <USER>. This option is
|
||||
intended to be used to drop root privileges.
|
||||
.TP
|
||||
\fB\-\-conf=\fR<PATH>
|
||||
Load configuration from PATH.
|
||||
Default: \fI/etc/nghttpx/nghttpx.conf\fP
|
||||
Load configuration from <PATH>.
|
||||
Default: \fI\,/etc/nghttpx/nghttpx.conf\/\fP
|
||||
.TP
|
||||
\fB\-v\fR, \fB\-\-version\fR
|
||||
Print version and exit.
|
||||
@@ -275,4 +382,5 @@ Print version and exit.
|
||||
\fB\-h\fR, \fB\-\-help\fR
|
||||
Print this help and exit.
|
||||
.SH "SEE ALSO"
|
||||
nghttp(1), nghttpd(1)
|
||||
|
||||
nghttp(1), nghttpd(1), h2load(1)
|
||||
|
||||
3
doc/nghttpx.h2m
Normal file
3
doc/nghttpx.h2m
Normal file
@@ -0,0 +1,3 @@
|
||||
[SEE ALSO]
|
||||
|
||||
nghttp(1), nghttpd(1), h2load(1)
|
||||
1
doc/python-apiref.rst
Normal file
1
doc/python-apiref.rst
Normal file
@@ -0,0 +1 @@
|
||||
.. include:: ../doc/sources/python-apiref.rst
|
||||
@@ -3,11 +3,11 @@
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
nghttp2 - HTTP/2.0 C Library
|
||||
nghttp2 - HTTP/2 C Library
|
||||
============================
|
||||
|
||||
This is an experimental implementation of Hypertext Transfer Protocol
|
||||
version 2.0.
|
||||
version 2.
|
||||
|
||||
The project is hosted at `github.com/tatsuhiro-t/nghttp2 <https://github.com/tatsuhiro-t/nghttp2>`_.
|
||||
|
||||
@@ -19,7 +19,9 @@ Contents:
|
||||
package_README
|
||||
tutorial-client
|
||||
tutorial-server
|
||||
nghttpx-howto
|
||||
apiref
|
||||
python-apiref
|
||||
nghttp2.h
|
||||
nghttp2ver.h
|
||||
Source <https://github.com/tatsuhiro-t/nghttp2>
|
||||
@@ -28,6 +30,8 @@ Contents:
|
||||
Released Versions
|
||||
=================
|
||||
|
||||
* `v0.3.2 <released-versions/v0.3.2/>`_ `(Download v0.3.2) <https://github.com/tatsuhiro-t/nghttp2/releases/tag/v0.3.2>`_
|
||||
* `v0.3.1 <released-versions/v0.3.1/>`_ `(Download v0.3.1) <https://github.com/tatsuhiro-t/nghttp2/releases/tag/v0.3.1>`_
|
||||
* `v0.3.0 <released-versions/v0.3.0/>`_ `(Download v0.3.0) <https://github.com/tatsuhiro-t/nghttp2/releases/tag/v0.3.0>`_
|
||||
* `v0.2.0 <released-versions/v0.2.0/>`_ `(Download v0.2.0) <https://github.com/tatsuhiro-t/nghttp2/releases/tag/v0.2.0>`_
|
||||
* `v0.1.0 <released-versions/v0.1.0/>`_ `(Download v0.1.0) <https://github.com/tatsuhiro-t/nghttp2/releases/tag/v0.1.0>`_
|
||||
@@ -36,5 +40,5 @@ Released Versions
|
||||
Resources
|
||||
---------
|
||||
|
||||
* http://tools.ietf.org/html/draft-ietf-httpbis-http2-09
|
||||
* http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-05
|
||||
* http://tools.ietf.org/html/draft-ietf-httpbis-http2-12
|
||||
* http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-07
|
||||
|
||||
232
doc/sources/nghttpx-howto.rst
Normal file
232
doc/sources/nghttpx-howto.rst
Normal file
@@ -0,0 +1,232 @@
|
||||
nghttpx - HOW-TO
|
||||
================
|
||||
|
||||
nghttpx is a proxy translating protocols between HTTP/2 and other
|
||||
protocols (e.g., HTTP/1, SPDY). It operates in several modes and each
|
||||
mode may require additional programs to work with. This article
|
||||
describes each operation mode and explains the intended use-cases. It
|
||||
also covers some useful options later.
|
||||
|
||||
Default mode
|
||||
------------
|
||||
|
||||
If nghttpx is invoked without any ``-s``, ``-p`` and ``--client``, it
|
||||
operates in default mode. In this mode, nghttpx frontend listens for
|
||||
HTTP/2 requests and translates them to HTTP/1 requests. Thus it works
|
||||
as reverse proxy (gateway) for HTTP/2 clients to HTTP/1 web server.
|
||||
HTTP/1 requests are also supported in frontend as a fallback. If
|
||||
nghttpx is linked with spdylay library and frontend connection is
|
||||
SSL/TLS, the frontend also supports SPDY protocol.
|
||||
|
||||
By default, this mode's frontend connection is encrypted using
|
||||
SSL/TLS. So server's private key and certificate must be supplied to
|
||||
the command line (or through configuration file). In this case, the
|
||||
fontend protocol selection will is done via ALPN or NPN.
|
||||
|
||||
With ``--frontend-no-tls`` option, user can turn off SSL/TLS in
|
||||
frontend connection. In this case, SPDY protocol is not available
|
||||
even if spdylay library is liked to nghttpx. HTTP/2 and HTTP/1 are
|
||||
available on the frontend and a HTTP/1 connection can be upgraded to
|
||||
HTTP/2 using HTTP Upgrade. Starting HTTP/2 connection by sending
|
||||
HTTP/2 connection preface is also supported.
|
||||
|
||||
The backend is supposed to be HTTP/1 Web server. For example, to make
|
||||
nghttpx listen to encrypted HTTP/2 requests at port 8443, and a
|
||||
backend HTTP/1 web server is configured to listen to HTTP/1 request at
|
||||
port 8080 in the same host, run nghttpx command-line like this::
|
||||
|
||||
$ nghttpx -f0.0.0.0,8443 -b127.0.0.1,8080 /path/to/server.key /path/to/server.crt
|
||||
|
||||
Then HTTP/2 enabled client can access to the nghttpx in HTTP/2. For
|
||||
example, you can send GET request to the server using nghttp::
|
||||
|
||||
$ nghttp -nv https://localhost:8443/
|
||||
|
||||
HTTP/2 proxy mode
|
||||
-----------------
|
||||
|
||||
If nghttpx is invoked with ``-s`` option, it operates in HTTP/2 proxy
|
||||
mode. The supported protocols in frontend and backend connections are
|
||||
the same in `default mode`_. The difference is that this mode acts like
|
||||
forward proxy and assumes the backend is HTTP/1 proxy server (e.g.,
|
||||
squid). So HTTP/1 request must include absolute URI in request line.
|
||||
|
||||
By default, frontend connection is encrypted, this mode is also called
|
||||
secure proxy. If nghttpx is linked with spdylay, it supports SPDY
|
||||
protocols and it works as so called SPDY proxy.
|
||||
|
||||
With ``--frontend-no-tls`` option, SSL/TLS is turned off in frontend
|
||||
connection, so the connection gets insecure.
|
||||
|
||||
The backend must be HTTP/1 proxy server. nghttpx only supports 1
|
||||
backend server address. It translates incoming requests to HTTP/1
|
||||
request to backend server. The backend server performs real proxy
|
||||
work for each request, for example, dispatching requests to the origin
|
||||
server and caching contents.
|
||||
|
||||
For example, to make nghttpx listen to encrypted HTTP/2 requests at
|
||||
port 8443, and a backend HTTP/1 proxy server is configured to listen
|
||||
to HTTP/1 request at port 3128 in the same host, run nghttpx
|
||||
command-line like this::
|
||||
|
||||
$ nghttpx -s -f0.0.0.0,8443 -b127.0.0.1,3128 /path/to/server.key /path/to/server.crt
|
||||
|
||||
At the time of this writing, there is no known HTTP/2 client which
|
||||
supports HTTP/2 proxy in this fashion. You can use Google Chrome to
|
||||
use this as secure (SPDY) proxy to test it out, though it does not use
|
||||
HTTP/2 at all.
|
||||
|
||||
The one way to configure Google Chrome to use secure proxy is create
|
||||
proxy.pac script like this:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
function FindProxyForURL(url, host) {
|
||||
return "HTTPS SERVERADDR:PORT";
|
||||
}
|
||||
|
||||
``SERVERADDR`` and ``PORT`` is the hostname/address and port of the
|
||||
machine nghttpx is running. Please note that Google Chrome requires
|
||||
valid certificate for secure proxy.
|
||||
|
||||
Then run Google Chrome with the following arguments::
|
||||
|
||||
$ google-chrome --proxy-pac-url=file:///path/to/proxy.pac --use-npn
|
||||
|
||||
Client mode
|
||||
-----------
|
||||
|
||||
If nghttpx is invoked with ``--client`` option, it operates in client
|
||||
mode. In this mode, nghttpx listens for plain, unencrypted HTTP/2 and
|
||||
HTTP/1 requests and translates them to encrypted HTTP/2 requests to
|
||||
the backend. User cannot enable SSL/TLS in frontend connection.
|
||||
|
||||
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).
|
||||
|
||||
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
|
||||
clients to test HTTP/2 deployment. Suppose that HTTP/2 web server
|
||||
listens to port 80 without encryption. Then run nghttpx as client
|
||||
mode to access to that web server::
|
||||
|
||||
$ nghttpx --client -f127.0.0.1,8080 -b127.0.0.1,80 --backend-no-tls
|
||||
|
||||
.. note::
|
||||
|
||||
You may need ``-k`` option if HTTP/2 server enables SSL/TLS and
|
||||
its certificate is self-signed. But please note that it is
|
||||
insecure.
|
||||
|
||||
Then you can use curl to access HTTP/2 server via nghttpx::
|
||||
|
||||
$ curl http://localhost:8080/
|
||||
|
||||
Client proxy mode
|
||||
-----------------
|
||||
|
||||
If nghttpx is invoked with ``-p`` option, it operates in client proxy
|
||||
mode. This mode behaves like `client mode`_, but it works like
|
||||
forward proxy. So HTTP/1 request must include absolute URI in request
|
||||
line.
|
||||
|
||||
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).
|
||||
|
||||
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
|
||||
is utilize existing HTTP/1 clients to test HTTP/2 connections between
|
||||
2 proxies. The another use-case is use this mode to aggregate local
|
||||
HTTP/1 connections to one HTTP/2 backend encrypted connection. This
|
||||
makes HTTP/1 clients which does not support secure proxy can use
|
||||
secure HTTP/2 proxy via nghttpx client mode.
|
||||
|
||||
Suppose that HTTP/2 proxy listens to port 8443, just like we saw in
|
||||
`HTTP/2 proxy mode`_. To run nghttpx in client proxy mode to access
|
||||
that server, invoke nghttpx like this::
|
||||
|
||||
$ nghttpx -p -f127.0.0.1,8080 -b127.0.0.1,8443
|
||||
|
||||
.. note::
|
||||
|
||||
You may need ``-k`` option if HTTP/2 server'ss certificate is
|
||||
self-signed. But please note that it is insecure.
|
||||
|
||||
Then you can use curl to issue HTTP request via HTTP/2 proxy::
|
||||
|
||||
$ curl --http-proxy=http://localhost:8080 http://www.google.com/
|
||||
|
||||
You can configure web browser to use localhost:8080 as forward
|
||||
proxy.
|
||||
|
||||
HTTP/2 bridge mode
|
||||
------------------
|
||||
|
||||
If nghttpx is invoked with ``--http2-bridge`` option, it operates in
|
||||
HTTP/2 bridge mode. The supported protocols in frontend and backend
|
||||
connections are the same in `default mode`_.
|
||||
|
||||
With ``--frontend-no-tls`` option, SSL/TLS is turned off in frontend
|
||||
connection, so the connection gets insecure.
|
||||
|
||||
The backend server is supporsed to be a HTTP/2 web server or HTTP/2
|
||||
proxy. Since HTTP/2 requests opaque between proxied and non-proxied
|
||||
request, the backend server may be proxy or just web server depending
|
||||
on the context of incoming requests.
|
||||
|
||||
The use-case of this mode is aggregate the incoming connections to one
|
||||
HTTP/2 connection. One backend HTTP/2 connection is created per
|
||||
worker (thread).
|
||||
|
||||
Disable SSL/TLS
|
||||
---------------
|
||||
|
||||
In `default mode`_, `HTTP/2 proxy mode`_ and `HTTP/2 bridge mode`_,
|
||||
frontend connections are encrypted with SSL/TLS by default. To turn
|
||||
off SSL/TLS, use ``--frontend-no-tls`` option. If this option is
|
||||
used, the private key and certificate are not required to run nghttpx.
|
||||
|
||||
In `client mode`_, `client proxy mode`_ and `HTTP/2 bridge mode`_,
|
||||
backend connections are encrypted with SSL/TLS by default. To turn
|
||||
off SSL/TLS, use ``--backend-no-tls`` option.
|
||||
|
||||
Specifying additional CA certificate
|
||||
------------------------------------
|
||||
|
||||
By default, nghttpx tries to read CA certificate from system. But
|
||||
depending on the system you use, this may fail or is not supported.
|
||||
To specify CA certificate manually, use ``--cacert`` option. The
|
||||
specified file must be PEM format and can contain multiple
|
||||
certificates.
|
||||
|
||||
By default, nghttpx validates server's certificate. If you want to
|
||||
turn off this validation, knowing this is really insecure and what you
|
||||
are doing, you can use ``-k`` option to disable certificate
|
||||
validation.
|
||||
|
||||
Read/write rate limit
|
||||
---------------------
|
||||
|
||||
nghttpx supports transfer rate limiting on frontend connections. You
|
||||
can do rate limit per connection or per worker (thread) for reading
|
||||
and writeing individually.
|
||||
|
||||
To rate limit per connection for reading, use ``--read-rate`` and
|
||||
``--read-burst`` options. For writing, use ``--write-rate`` and
|
||||
``--write-burst`` options.
|
||||
|
||||
To rate limit per worker (thread), use ``--worker-read-rate`` and
|
||||
``--worker-read-burst`` options. For writing, use
|
||||
``--worker-write-rate`` and ``--worker-write-burst``.
|
||||
|
||||
If both per connection and per worker rate limit configurations are
|
||||
specified, the lower rate is used.
|
||||
|
||||
Please note that rate limit is performed on top of TCP and nothing to
|
||||
do with HTTP/2 flow control.
|
||||
319
doc/sources/python-apiref.rst
Normal file
319
doc/sources/python-apiref.rst
Normal file
@@ -0,0 +1,319 @@
|
||||
Python API Reference
|
||||
====================
|
||||
|
||||
.. py:module:: nghttp2
|
||||
|
||||
nghttp2 offers some high level Python API to C library. The bindings
|
||||
currently provide HPACK compressor and decompressor classes and HTTP/2
|
||||
server class.
|
||||
|
||||
The extension module is called ``nghttp2``.
|
||||
|
||||
``make`` will build the bindings. The target Python version is
|
||||
determined by configure script. If the detected Python version is not
|
||||
what you expect, specify a path to Python executable in ``PYTHON``
|
||||
variable as an argument to configure script (e.g., ``./configure
|
||||
PYTHON=/usr/bin/python3.4``).
|
||||
|
||||
HPACK API
|
||||
---------
|
||||
|
||||
.. py:class:: HDDeflater(hd_table_bufsize_max=DEFLATE_MAX_HEADER_TABLE_SIZE)
|
||||
|
||||
This class is used to perform header compression. The
|
||||
*hd_table_bufsize_max* limits the usage of header table in the
|
||||
given amount of bytes. The default value is
|
||||
:py:data:`DEFLATE_MAX_HEADER_TABLE_SIZE`. This is necessary
|
||||
because the deflater and inflater share the same amount of header
|
||||
table and the inflater decides that number. The deflater may not
|
||||
want to use all header table size because of limited memory
|
||||
availability. In that case, *hd_table_bufsize_max* can be used to
|
||||
cap the upper limit of table size whatever the header table size is
|
||||
chosen by the inflater.
|
||||
|
||||
.. py:method:: deflate(headers)
|
||||
|
||||
Deflates the *headers*. The *headers* must be sequence of tuple
|
||||
of name/value pair, which are byte strings (not unicode string).
|
||||
|
||||
This method returns the deflated header block in byte string.
|
||||
Raises the exception if any error occurs.
|
||||
|
||||
.. py:method:: set_no_refset(no_refset)
|
||||
|
||||
Tells the deflater not to use reference set if *no_refset* is
|
||||
evaluated to ``True``. If that happens, on each subsequent
|
||||
invocation of :py:meth:`deflate()`, deflater will clear up
|
||||
refersent set.
|
||||
|
||||
.. py:method:: change_table_size(hd_table_bufsize_max)
|
||||
|
||||
Changes header table size to *hd_table_bufsize_max* byte. if
|
||||
*hd_table_bufsize_max* is strictly larger than
|
||||
``hd_table_bufsize_max`` given in constructor,
|
||||
``hd_table_bufsize_max`` is used as header table size instead.
|
||||
|
||||
Raises the exception if any error occurs.
|
||||
|
||||
.. py:method:: get_hd_table()
|
||||
|
||||
Returns copy of current dynamic header table.
|
||||
|
||||
The following example shows how to deflate header name/value pairs:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import binascii, nghttp2
|
||||
|
||||
deflater = nghttp2.HDDeflater()
|
||||
|
||||
res = deflater.deflate([(b'foo', b'bar'),
|
||||
(b'baz', b'buz')])
|
||||
|
||||
print(binascii.b2a_hex(res))
|
||||
|
||||
|
||||
.. py:class:: HDInflater()
|
||||
|
||||
This class is used to perform header decompression.
|
||||
|
||||
.. py:method:: inflate(data)
|
||||
|
||||
Inflates the deflated header block *data*. The *data* must be
|
||||
byte string.
|
||||
|
||||
Raises the exception if any error occurs.
|
||||
|
||||
.. py:method:: change_table_size(hd_table_bufsize_max)
|
||||
|
||||
Changes header table size to *hd_table_bufsize_max* byte.
|
||||
|
||||
Raises the exception if any error occurs.
|
||||
|
||||
.. py:method:: get_hd_table()
|
||||
|
||||
Returns copy of current dynamic header table.
|
||||
|
||||
The following example shows how to inflate deflated header block:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
deflater = nghttp2.HDDeflater()
|
||||
|
||||
data = deflater.deflate([(b'foo', b'bar'),
|
||||
(b'baz', b'buz')])
|
||||
|
||||
inflater = nghttp2.HDInflater()
|
||||
|
||||
hdrs = inflater.inflate(data)
|
||||
|
||||
print(hdrs)
|
||||
|
||||
|
||||
.. py:function:: print_hd_table(hdtable)
|
||||
|
||||
Convenient function to print *hdtable* to the standard output. The
|
||||
*hdtable* is the one retrieved by
|
||||
:py:meth:`HDDeflater.get_hd_table()` or
|
||||
:py:meth:`HDInflater.get_hd_table()`. This function does not work
|
||||
if header name/value cannot be decoded using UTF-8 encoding.
|
||||
|
||||
In output, ``s=N`` means the entry occupies ``N`` bytes in header
|
||||
table. If ``r=y``, then the entry is in the reference set.
|
||||
|
||||
.. py:data:: DEFAULT_HEADER_TABLE_SIZE
|
||||
|
||||
The default header table size, which is 4096 as per HTTP/2
|
||||
specification.
|
||||
|
||||
.. py:data:: DEFLATE_MAX_HEADER_TABLE_SIZE
|
||||
|
||||
The default header table size for deflater. The initial value
|
||||
is 4096.
|
||||
|
||||
HTTP/2 servers
|
||||
--------------
|
||||
|
||||
.. note::
|
||||
|
||||
We use :py:mod:`asyncio` for HTTP/2 server classes. Therefore,
|
||||
Python 3.4 or later is required to use these objects. To
|
||||
explicitly configure nghttp2 build to use Python 3.4, specify the
|
||||
``PYTHON`` variable to the path to Python 3.4 executable when
|
||||
invoking configure script like this::
|
||||
|
||||
$ ./configure PYTHON=/usr/bin/python3.4
|
||||
|
||||
.. py:class:: HTTP2Server(address, RequestHandlerClass, ssl=None)
|
||||
|
||||
This class builds on top of the :py:mod:`asyncio` event loop. On
|
||||
construction, *RequestHandlerClass* must be given, which must be a
|
||||
subclass of :py:class:`BaseRequestHandler` class.
|
||||
|
||||
The *address* must be a tuple of hostname/IP address and port to
|
||||
bind. If hostname/IP address is ``None``, all interfaces are
|
||||
assumed.
|
||||
|
||||
To enable SSL/TLS, specify instance of :py:class:`ssl.SSLContext`
|
||||
in *ssl*. Before passing *ssl* to
|
||||
:py:func:`BaseEventLoop.create_server`, ALPN protocol identifiers
|
||||
are set using :py:meth:`ssl.SSLContext.set_npn_protocols`.
|
||||
|
||||
To disable SSL/TLS, omit *ssl* or specify ``None``.
|
||||
|
||||
.. py:method:: serve_forever()
|
||||
|
||||
Runs server and processes incoming requests forever.
|
||||
|
||||
.. py:class:: BaseRequestHandler(http2, stream_id)
|
||||
|
||||
The class is used to handle the single HTTP/2 stream. By default,
|
||||
it does not nothing. It must be subclassed to handle each event
|
||||
callback method.
|
||||
|
||||
The first callback method invoked is :py:meth:`on_headers()`. It is
|
||||
called when HEADERS frame, which includes request header fields, is
|
||||
arrived.
|
||||
|
||||
If request has request body, :py:meth:`on_data()` is invoked for
|
||||
each chunk of received data chunk.
|
||||
|
||||
When whole request is received, :py:meth:`on_request_done()` is
|
||||
invoked.
|
||||
|
||||
When stream is closed, :py:meth:`on_close()` is called.
|
||||
|
||||
The application can send response using :py:meth:`send_response()`
|
||||
method. It can be used in :py:meth:`on_headers()`,
|
||||
:py:meth:`on_data()` or :py:meth:`on_request_done()`.
|
||||
|
||||
The application can push resource using :py:meth:`push()` method.
|
||||
It must be used before :py:meth:`send_response()` call.
|
||||
|
||||
A :py:class:`BaseRequestHandler` has the following instance
|
||||
variables:
|
||||
|
||||
.. py:attribute:: client_address
|
||||
|
||||
Contains a tuple of the form ``(host, port)`` referring to the
|
||||
client's address.
|
||||
|
||||
.. py:attribute:: stream_id
|
||||
|
||||
Stream ID of this stream
|
||||
|
||||
.. py:attribute:: scheme
|
||||
|
||||
Scheme of the request URI. This is a value of ``:scheme``
|
||||
header field.
|
||||
|
||||
.. py:attribute:: method
|
||||
|
||||
Method of this stream. This is a value of ``:method`` header
|
||||
field.
|
||||
|
||||
.. py:attribute:: host
|
||||
|
||||
This is a value of ``:authority`` or ``host`` header field.
|
||||
|
||||
.. py:attribute:: path
|
||||
|
||||
This is a value of ``:path`` header field.
|
||||
|
||||
A :py:class:`BaseRequestHandler` has the following methods:
|
||||
|
||||
.. py:method:: on_headers()
|
||||
|
||||
Called when request HEADERS is arrived. By default, this method
|
||||
does nothing.
|
||||
|
||||
.. py:method:: on_data(data)
|
||||
|
||||
Called when a chunk of request body *data* is arrived. This
|
||||
method will be called multiple times until all data are
|
||||
received. By default, this method does nothing.
|
||||
|
||||
.. py:method:: on_request_done()
|
||||
|
||||
Called when whole request was received. By default, this method
|
||||
does nothing.
|
||||
|
||||
.. py:method:: on_close(error_code)
|
||||
|
||||
Called when stream is about to close. The *error_code*
|
||||
indicates the reason of closure. If it is ``0``, the stream is
|
||||
going to close without error.
|
||||
|
||||
.. py:method:: send_response(status=200, headers=None, body=None)
|
||||
|
||||
Send response. The *status* is HTTP status code. The *headers*
|
||||
is additional response headers. The *:status* header field will
|
||||
be appended by the library. The *body* is the response body.
|
||||
It could be ``None`` if response body is empty. Or it must be
|
||||
instance of either ``str``, ``bytes`` or :py:class:`io.IOBase`.
|
||||
If instance of ``str`` is specified, it will be encoded using
|
||||
UTF-8.
|
||||
|
||||
The *headers* is a list of tuple of the form ``(name,
|
||||
value)``. The ``name`` and ``value`` can be either byte string
|
||||
or unicode string. In the latter case, they will be encoded
|
||||
using UTF-8.
|
||||
|
||||
Raises the exception if any error occurs.
|
||||
|
||||
.. py:method:: push(path, method='GET', request_headers=None, status=200, headers=None, body=None)
|
||||
|
||||
Push a specified resource. The *path* is a path portion of
|
||||
request URI for this resource. The *method* is a method to
|
||||
access this resource. The *request_headers* is additional
|
||||
request headers to access this resource. The ``:scheme``,
|
||||
``:method``, ``:authority`` and ``:path`` are appended by the
|
||||
library. The ``:scheme`` and ``:authority`` are inherited from
|
||||
request header fields of the associated stream.
|
||||
|
||||
The *status* is HTTP status code. The *headers* is additional
|
||||
response headers. The ``:status`` header field is appended by
|
||||
the library. The *body* is the response body. It could be
|
||||
``None`` if response body is empty. Or it must be instance of
|
||||
either ``str``, ``bytes`` or ``io.IOBase``. If instance of
|
||||
``str`` is specified, it is encoded using UTF-8.
|
||||
|
||||
The headers and request_headers are a list of tuple of the form
|
||||
``(name, value)``. The ``name`` and ``value`` can be either byte
|
||||
string or unicode string. In the latter case, they will be
|
||||
encoded using UTF-8.
|
||||
|
||||
Returns an instance of ``RequestHandlerClass`` specified in
|
||||
:py:class:`HTTP2Server` constructor for the pushed resource.
|
||||
|
||||
Raises the exception if any error occurs.
|
||||
|
||||
The following example illustrates :py:class:`HTTP2Server` and
|
||||
:py:class:`BaseRequestHandler` usage:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
#!/usr/bin/env python
|
||||
|
||||
import io, ssl
|
||||
import nghttp2
|
||||
|
||||
class Handler(nghttp2.BaseRequestHandler):
|
||||
|
||||
def on_headers(self):
|
||||
self.push(path='/css/style.css',
|
||||
request_headers = [('content-type', 'text/css')],
|
||||
status=200,
|
||||
body='body{margin:0;}')
|
||||
|
||||
self.send_response(status=200,
|
||||
headers = [('content-type', 'text/plain')],
|
||||
body=io.BytesIO(b'nghttp2-python FTW'))
|
||||
|
||||
ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
|
||||
ctx.options = ssl.OP_ALL | ssl.OP_NO_SSLv2
|
||||
ctx.load_cert_chain('server.crt', 'server.key')
|
||||
|
||||
# give None to ssl to make the server non-SSL/TLS
|
||||
server = nghttp2.HTTP2Server(('127.0.0.1', 8443), Handler, ssl=ctx)
|
||||
server.serve_forever()
|
||||
@@ -1,7 +1,7 @@
|
||||
Tutorial: HTTP/2.0 client
|
||||
Tutorial: HTTP/2 client
|
||||
=========================
|
||||
|
||||
In this tutorial, we are going to write very primitive HTTP/2.0
|
||||
In this tutorial, we are going to write very primitive HTTP/2
|
||||
client. The complete source code, `libevent-client.c`_, is attached at
|
||||
the end of this page. It also resides in examples directory in the
|
||||
archive or repository.
|
||||
@@ -19,7 +19,7 @@ function ``main()`` and ``run()``, which is not so relevant to nghttp2
|
||||
library use. The one thing you should look at is setup NPN callback.
|
||||
The NPN callback is used for the client to select the next application
|
||||
protocol over the SSL/TLS transport. In this tutorial, we use
|
||||
`nghttp2_select_next_protocol()` function to select the HTTP/2.0
|
||||
`nghttp2_select_next_protocol()` function to select the HTTP/2
|
||||
protocol the library supports::
|
||||
|
||||
static int select_next_proto_cb(SSL* ssl,
|
||||
@@ -52,7 +52,7 @@ The callback is set to the SSL_CTX object using
|
||||
}
|
||||
|
||||
We use ``http2_session_data`` structure to store the data related to
|
||||
the HTTP/2.0 session::
|
||||
the HTTP/2 session::
|
||||
|
||||
typedef struct {
|
||||
nghttp2_session *session;
|
||||
@@ -159,7 +159,6 @@ finished successfully. We first initialize nghttp2 session object in
|
||||
nghttp2_session_callbacks callbacks = {0};
|
||||
|
||||
callbacks.send_callback = send_callback;
|
||||
callbacks.before_frame_send_callback = before_frame_send_callback;
|
||||
callbacks.on_frame_recv_callback = on_frame_recv_callback;
|
||||
callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback;
|
||||
callbacks.on_stream_close_callback = on_stream_close_callback;
|
||||
@@ -176,10 +175,10 @@ The `delete_http2_session_data()` destroys ``session_data`` and frees
|
||||
its bufferevent, so it closes underlying connection as well. It also
|
||||
calls `nghttp2_session_del()` to delete nghttp2 session object.
|
||||
|
||||
We begin HTTP/2.0 communication by sending client connection header,
|
||||
We begin HTTP/2 communication by sending client connection preface,
|
||||
which is 24 bytes magic byte sequence
|
||||
(:macro:`NGHTTP2_CLIENT_CONNECTION_HEADER`) followed by SETTINGS
|
||||
frame. The transmission of client connection header is done in
|
||||
(:macro:`NGHTTP2_CLIENT_CONNECTION_PREFACE`) and SETTINGS frame. The
|
||||
transmission of client connection header is done in
|
||||
``send_client_connection_header()``::
|
||||
|
||||
static void send_client_connection_header(http2_session_data *session_data)
|
||||
@@ -190,8 +189,8 @@ frame. The transmission of client connection header is done in
|
||||
int rv;
|
||||
|
||||
bufferevent_write(session_data->bev,
|
||||
NGHTTP2_CLIENT_CONNECTION_HEADER,
|
||||
NGHTTP2_CLIENT_CONNECTION_HEADER_LEN);
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
|
||||
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE,
|
||||
iv, ARRLEN(iv));
|
||||
if(rv != 0) {
|
||||
@@ -213,7 +212,7 @@ request in ``submit_request()`` function::
|
||||
|
||||
static void submit_request(http2_session_data *session_data)
|
||||
{
|
||||
int rv;
|
||||
int32_t stream_id;
|
||||
http2_stream_data *stream_data = session_data->stream_data;
|
||||
const char *uri = stream_data->uri;
|
||||
const struct http_parser_url *u = stream_data->u;
|
||||
@@ -226,11 +225,13 @@ request in ``submit_request()`` function::
|
||||
};
|
||||
fprintf(stderr, "Request headers:\n");
|
||||
print_headers(stderr, hdrs, ARRLEN(hdrs));
|
||||
rv = nghttp2_submit_request(session_data->session, NGHTTP2_PRI_DEFAULT,
|
||||
stream_id = nghttp2_submit_request(session_data->session, NULL,
|
||||
hdrs, ARRLEN(hdrs), NULL, stream_data);
|
||||
if(rv != 0) {
|
||||
errx(1, "Could not submit HTTP request: %s", nghttp2_strerror(rv));
|
||||
if(stream_id < 0) {
|
||||
errx(1, "Could not submit HTTP request: %s", nghttp2_strerror(stream_id));
|
||||
}
|
||||
|
||||
stream_data->stream_id = stream_id;
|
||||
}
|
||||
|
||||
We build HTTP request header fields in ``hdrs`` which is an array of
|
||||
@@ -239,6 +240,8 @@ We build HTTP request header fields in ``hdrs`` which is an array of
|
||||
we use `nghttp2_submit_request()` function. The `stream_data` is
|
||||
passed in *stream_user_data* parameter. It is used in nghttp2
|
||||
callbacks which we'll describe about later.
|
||||
`nghttp2_submit_request()` returns the newly assigned stream ID for
|
||||
this request.
|
||||
|
||||
The next bufferevent callback is ``readcb()``, which is invoked when
|
||||
data is available to read in the bufferevent input buffer::
|
||||
@@ -344,48 +347,6 @@ We have already described about nghttp2 callback ``send_callback()``.
|
||||
Let's describe remaining nghttp2 callbacks we setup in
|
||||
``initialize_nghttp2_setup()`` function.
|
||||
|
||||
The `before_frame_send_callback()` function is invoked when a frame is
|
||||
about to be sent::
|
||||
|
||||
static int before_frame_send_callback
|
||||
(nghttp2_session *session, const nghttp2_frame *frame, void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
http2_stream_data *stream_data;
|
||||
|
||||
if(frame->hd.type == NGHTTP2_HEADERS &&
|
||||
frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
|
||||
stream_data =
|
||||
(http2_stream_data*)nghttp2_session_get_stream_user_data
|
||||
(session, frame->hd.stream_id);
|
||||
if(stream_data == session_data->stream_data) {
|
||||
stream_data->stream_id = frame->hd.stream_id;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Remember that we have not get stream ID when we submit HTTP request
|
||||
using `nghttp2_submit_request()`. Since nghttp2 library reorders the
|
||||
request based on priority and stream ID must be monotonically
|
||||
increased, the stream ID is not assigned just before transmission.
|
||||
The one of the purpose of this callback is get the stream ID assigned
|
||||
to the frame. First we check that the frame is HEADERS frame. Since
|
||||
HEADERS has several meanings in HTTP/2.0, we check that it is request
|
||||
HEADERS (which means that the first HEADERS frame to create a stream).
|
||||
The assigned stream ID is ``frame->hd.stream_id``. Recall that we
|
||||
passed ``stream_data`` in the *stream_user_data* parameter of
|
||||
`nghttp2_submit_request()` function. We can get it using
|
||||
`nghttp2_session_get_stream_user_data()` function. To really sure that
|
||||
this HEADERS frame is the request HEADERS we have queued, we check
|
||||
that ``session_data->stream_data`` and ``stream_data`` returned from
|
||||
`nghttp2_session_get_stream_user_data()` are pointing the same
|
||||
location. In this example program, we just only uses 1 stream, it is
|
||||
unnecessary to compare them, but real applications surely deal with
|
||||
multiple streams, and *stream_user_data* is very handy to identify
|
||||
which HEADERS we are seeing in the callback. Therefore we just show
|
||||
how to use it here.
|
||||
|
||||
Each request header name/value pair is emitted via
|
||||
``on_header_callback`` function::
|
||||
|
||||
@@ -393,6 +354,7 @@ Each request header name/value pair is emitted via
|
||||
const nghttp2_frame *frame,
|
||||
const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen,
|
||||
uint8_t flags,
|
||||
void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
@@ -479,6 +441,6 @@ If the stream ID matches the one we initiated, it means that its
|
||||
stream is going to be closed. Since we have finished to get the
|
||||
resource we want (or the stream was reset by RST_STREAM from the
|
||||
remote peer), we call `nghttp2_session_terminate_session()` to
|
||||
commencing the closure of the HTTP/2.0 session gracefully. If you have
|
||||
commencing the closure of the HTTP/2 session gracefully. If you have
|
||||
some data associated for the stream to be closed, you may delete it
|
||||
here.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
Tutorial: HTTP/2.0 server
|
||||
Tutorial: HTTP/2 server
|
||||
=========================
|
||||
|
||||
In this tutorial, we are going to write single-threaded, event-based
|
||||
HTTP/2.0 web server, which supports HTTPS only. It can handle
|
||||
HTTP/2 web server, which supports HTTPS only. It can handle
|
||||
concurrent multiple requests, but only GET method is supported. The
|
||||
complete source code, `libevent-server.c`_, is attached at the end of
|
||||
this page. It also resides in examples directory in the archive or
|
||||
@@ -57,7 +57,7 @@ life time::
|
||||
|
||||
The wire format of NPN is a sequence of length prefixed string. The
|
||||
exactly one byte is used to specify the length of each protocol
|
||||
identifier. In this tutorial, we advertise the HTTP/2.0 protocol the
|
||||
identifier. In this tutorial, we advertise the HTTP/2 protocol the
|
||||
nghttp2 library supports. The nghttp2 library exports its identifier
|
||||
in :macro:`NGHTTP2_PROTO_VERSION_ID`. The ``next_proto_cb()`` function
|
||||
is the server-side NPN callback. In OpenSSL implementation, we just
|
||||
@@ -73,7 +73,7 @@ We use ``app_content`` structure to store the application-wide data::
|
||||
};
|
||||
|
||||
We use ``http2_session_data`` structure to store the session-level
|
||||
(which corresponds to 1 HTTP/2.0 connection) data::
|
||||
(which corresponds to 1 HTTP/2 connection) data::
|
||||
|
||||
typedef struct http2_session_data {
|
||||
struct http2_stream_data root;
|
||||
@@ -94,19 +94,20 @@ data::
|
||||
int fd;
|
||||
} http2_stream_data;
|
||||
|
||||
1 HTTP/2.0 session can have multiple streams. We manage these
|
||||
multiple streams by intrusive doubly linked list to add and remove the
|
||||
object in O(1). The first element of this list is pointed by the
|
||||
1 HTTP/2 session can have multiple streams. We manage these multiple
|
||||
streams by intrusive doubly linked list to add and remove the object
|
||||
in O(1). The first element of this list is pointed by the
|
||||
``root->next`` in ``http2_session_data``. Initially, ``root->next``
|
||||
is ``NULL``. The ``handshake_leftlen`` member of
|
||||
``http2_session_data`` is used to track the number of bytes remaining
|
||||
when receiving first 24 bytes magic value
|
||||
(:macro:`NGHTTP2_CLIENT_CONNECTION_HEADER`) from the client. We use
|
||||
libevent's bufferevent structure to perform network I/O. Notice that
|
||||
bufferevent object is in ``http2_session_data`` and not in
|
||||
``http2_stream_data``. This is because ``http2_stream_data`` is just a
|
||||
logical stream multiplexed over the single connection managed by
|
||||
bufferevent in ``http2_session_data``.
|
||||
when receiving first client connection preface
|
||||
(:macro:`NGHTTP2_CLIENT_CONNECTION_PREFACE`), which is 24 bytes magic
|
||||
byte string, from the client. We use libevent's bufferevent structure
|
||||
to perform network I/O. Notice that bufferevent object is in
|
||||
``http2_session_data`` and not in ``http2_stream_data``. This is
|
||||
because ``http2_stream_data`` is just a logical stream multiplexed
|
||||
over the single connection managed by bufferevent in
|
||||
``http2_session_data``.
|
||||
|
||||
We first create listener object to accept incoming connections.
|
||||
We use libevent's ``struct evconnlistener`` for this purpose::
|
||||
@@ -200,9 +201,9 @@ it::
|
||||
uint8_t data[24];
|
||||
struct evbuffer *input = bufferevent_get_input(session_data->bev);
|
||||
int readlen = evbuffer_remove(input, data, session_data->handshake_leftlen);
|
||||
const char *conhead = NGHTTP2_CLIENT_CONNECTION_HEADER;
|
||||
const char *conhead = NGHTTP2_CLIENT_CONNECTION_PREFACE;
|
||||
|
||||
if(memcmp(conhead + NGHTTP2_CLIENT_CONNECTION_HEADER_LEN
|
||||
if(memcmp(conhead + NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN
|
||||
- session_data->handshake_leftlen, data, readlen) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
@@ -225,11 +226,11 @@ it::
|
||||
}
|
||||
|
||||
We check that the received byte string matches
|
||||
:macro:`NGHTTP2_CLIENT_CONNECTION_HEADER`. When they match, the
|
||||
connection state is ready for starting HTTP/2.0 communication. First
|
||||
:macro:`NGHTTP2_CLIENT_CONNECTION_PREFACE`. When they match, the
|
||||
connection state is ready for starting HTTP/2 communication. First
|
||||
we change the callback functions for the bufferevent object. We use
|
||||
same ``eventcb`` as before. But we specify new ``readcb`` and
|
||||
``writecb`` function to handle HTTP/2.0 communication. We describe
|
||||
``writecb`` function to handle HTTP/2 communication. We describe
|
||||
these 2 functions later.
|
||||
|
||||
We initialize nghttp2 session object which is done in
|
||||
@@ -435,7 +436,7 @@ of header block in HEADERS or PUSH_PROMISE frame is started::
|
||||
}
|
||||
|
||||
We only interested in HEADERS frame in this function. Since HEADERS
|
||||
frame has several roles in HTTP/2.0 protocol, we check that it is a
|
||||
frame has several roles in HTTP/2 protocol, we check that it is a
|
||||
request HEADERS, which opens new stream. If frame is request HEADERS,
|
||||
then we create ``http2_stream_data`` object to store stream related
|
||||
data. We associate created ``http2_stream_data`` object to the stream
|
||||
@@ -462,7 +463,7 @@ is emitted via ``on_header_callback`` function, which is called after
|
||||
}
|
||||
stream_data = nghttp2_session_get_stream_user_data(session,
|
||||
frame->hd.stream_id);
|
||||
if(stream_data->request_path) {
|
||||
if(!stream_data || stream_data->request_path) {
|
||||
break;
|
||||
}
|
||||
if(namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) {
|
||||
@@ -544,7 +545,7 @@ function to read content of the file::
|
||||
|
||||
static ssize_t file_read_callback
|
||||
(nghttp2_session *session, int32_t stream_id,
|
||||
uint8_t *buf, size_t length, int *eof,
|
||||
uint8_t *buf, size_t length, uint32_t *data_flags,
|
||||
nghttp2_data_source *source, void *user_data)
|
||||
{
|
||||
int fd = source->fd;
|
||||
@@ -554,16 +555,16 @@ function to read content of the file::
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
if(r == 0) {
|
||||
*eof = 1;
|
||||
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
If error happens while reading file, we return
|
||||
:macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. This tells the library
|
||||
to send RST_STREAM to the stream. When all data is read, set 1 to
|
||||
``*eof`` to tell the nghttp2 library that we have finished reading
|
||||
file.
|
||||
:macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. This tells the
|
||||
library to send RST_STREAM to the stream. When all data are read, set
|
||||
:macro:`NGHTTP2_DATA_FLAG_EOF` flag to ``*data_flags`` to tell the
|
||||
nghttp2 library that we have finished reading file.
|
||||
|
||||
The `nghttp2_submit_response()` is used to send response to the remote
|
||||
peer.
|
||||
@@ -580,6 +581,9 @@ is about to close::
|
||||
http2_stream_data *stream_data;
|
||||
|
||||
stream_data = nghttp2_session_get_stream_user_data(session, stream_id);
|
||||
if(!stream_data) {
|
||||
return 0;
|
||||
}
|
||||
remove_stream(session_data, stream_data);
|
||||
delete_http2_stream_data(stream_data);
|
||||
return 0;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# nghttp2 - HTTP/2.0 C Library
|
||||
# nghttp2 - HTTP/2 C Library
|
||||
|
||||
# Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2013 Tatsuhiro Tsujikawa
|
||||
*
|
||||
@@ -52,12 +52,12 @@ enum {
|
||||
};
|
||||
|
||||
#define MAKE_NV(NAME, VALUE) \
|
||||
{(uint8_t*)NAME, (uint8_t*)VALUE, \
|
||||
(uint16_t)(sizeof(NAME) - 1), (uint16_t)(sizeof(VALUE) - 1) }
|
||||
{(uint8_t*)NAME, (uint8_t*)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \
|
||||
NGHTTP2_NV_FLAG_NONE}
|
||||
|
||||
#define MAKE_NV_CS(NAME, VALUE) \
|
||||
{(uint8_t*)NAME, (uint8_t*)VALUE, \
|
||||
(uint16_t)(sizeof(NAME) - 1), (uint16_t)(strlen(VALUE)) }
|
||||
{(uint8_t*)NAME, (uint8_t*)VALUE, sizeof(NAME) - 1, strlen(VALUE), \
|
||||
NGHTTP2_NV_FLAG_NONE}
|
||||
|
||||
struct Connection {
|
||||
SSL *ssl;
|
||||
@@ -72,8 +72,6 @@ struct Connection {
|
||||
};
|
||||
|
||||
struct Request {
|
||||
/* The gzip stream inflater for the compressed response. */
|
||||
nghttp2_gzip *inflater;
|
||||
char *host;
|
||||
/* In this program, path contains query component as well. */
|
||||
char *path;
|
||||
@@ -139,36 +137,6 @@ static void diec(const char *func, int error_code)
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static char CONTENT_LENGTH[] = "content-encoding";
|
||||
static size_t CONTENT_LENGTH_LEN = sizeof(CONTENT_LENGTH) - 1;
|
||||
static char GZIP[] = "gzip";
|
||||
static size_t GZIP_LEN = sizeof(GZIP) - 1;
|
||||
|
||||
/*
|
||||
* Check response is content-encoding: gzip. We need this because
|
||||
* HTTP/2.0 client is required to support gzip.
|
||||
*/
|
||||
static void check_gzip(struct Request *req, nghttp2_nv *nva, size_t nvlen)
|
||||
{
|
||||
size_t i;
|
||||
if(req->inflater) {
|
||||
return;
|
||||
}
|
||||
for(i = 0; i < nvlen; ++i) {
|
||||
if(CONTENT_LENGTH_LEN == nva[i].namelen &&
|
||||
memcmp(CONTENT_LENGTH, nva[i].name, nva[i].namelen) == 0 &&
|
||||
GZIP_LEN == nva[i].valuelen &&
|
||||
memcmp(GZIP, nva[i].value, nva[i].valuelen) == 0) {
|
||||
int rv;
|
||||
rv = nghttp2_gzip_inflate_new(&req->inflater);
|
||||
if(rv != 0) {
|
||||
die("Can't allocate inflate stream.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The implementation of nghttp2_send_callback type. Here we write
|
||||
* |data| with size |length| to the network and return the number of
|
||||
@@ -229,29 +197,6 @@ static ssize_t recv_callback(nghttp2_session *session,
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*
|
||||
* The implementation of nghttp2_before_frame_send_callback type. We
|
||||
* use this function to get stream ID of the request. This is because
|
||||
* stream ID is not known when we submit the request
|
||||
* (nghttp2_submit_request).
|
||||
*/
|
||||
static int before_frame_send_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame,
|
||||
void *user_data)
|
||||
{
|
||||
if(frame->hd.type == NGHTTP2_HEADERS &&
|
||||
frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
|
||||
struct Request *req;
|
||||
int32_t stream_id = frame->hd.stream_id;
|
||||
req = nghttp2_session_get_stream_user_data(session, stream_id);
|
||||
if(req && req->stream_id == -1) {
|
||||
req->stream_id = stream_id;
|
||||
printf("[INFO] Stream ID = %d\n", stream_id);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_frame_send_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame, void *user_data)
|
||||
{
|
||||
@@ -290,7 +235,6 @@ static int on_frame_recv_callback(nghttp2_session *session,
|
||||
struct Request *req;
|
||||
req = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
|
||||
if(req) {
|
||||
check_gzip(req, frame->headers.nva, frame->headers.nvlen);
|
||||
printf("[INFO] C <---------------------------- S (HEADERS)\n");
|
||||
for(i = 0; i < frame->headers.nvlen; ++i) {
|
||||
fwrite(nva[i].name, nva[i].namelen, 1, stdout);
|
||||
@@ -351,25 +295,7 @@ static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
||||
if(req) {
|
||||
printf("[INFO] C <---------------------------- S (DATA chunk)\n"
|
||||
"%lu bytes\n", (unsigned long int)len);
|
||||
if(req->inflater) {
|
||||
while(len > 0) {
|
||||
uint8_t out[MAX_OUTLEN];
|
||||
size_t outlen = MAX_OUTLEN;
|
||||
size_t tlen = len;
|
||||
int rv;
|
||||
rv = nghttp2_gzip_inflate(req->inflater, out, &outlen, data, &tlen);
|
||||
if(rv == -1) {
|
||||
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id,
|
||||
NGHTTP2_INTERNAL_ERROR);
|
||||
break;
|
||||
}
|
||||
fwrite(out, 1, outlen, stdout);
|
||||
data += tlen;
|
||||
len -= tlen;
|
||||
}
|
||||
} else {
|
||||
fwrite(data, 1, len, stdout);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
return 0;
|
||||
@@ -386,7 +312,6 @@ static void setup_nghttp2_callbacks(nghttp2_session_callbacks *callbacks)
|
||||
memset(callbacks, 0, sizeof(nghttp2_session_callbacks));
|
||||
callbacks->send_callback = send_callback;
|
||||
callbacks->recv_callback = recv_callback;
|
||||
callbacks->before_frame_send_callback = before_frame_send_callback;
|
||||
callbacks->on_frame_send_callback = on_frame_send_callback;
|
||||
callbacks->on_frame_recv_callback = on_frame_recv_callback;
|
||||
callbacks->on_stream_close_callback = on_stream_close_callback;
|
||||
@@ -395,7 +320,7 @@ static void setup_nghttp2_callbacks(nghttp2_session_callbacks *callbacks)
|
||||
|
||||
/*
|
||||
* Callback function for TLS NPN. Since this program only supports
|
||||
* HTTP/2.0 protocol, if server does not offer HTTP/2.0 the nghttp2
|
||||
* HTTP/2 protocol, if server does not offer HTTP/2 the nghttp2
|
||||
* library supports, we terminate program.
|
||||
*/
|
||||
static int select_next_proto_cb(SSL* ssl,
|
||||
@@ -404,11 +329,11 @@ static int select_next_proto_cb(SSL* ssl,
|
||||
void *arg)
|
||||
{
|
||||
int rv;
|
||||
/* nghttp2_select_next_protocol() selects HTTP/2.0 protocol the
|
||||
/* nghttp2_select_next_protocol() selects HTTP/2 protocol the
|
||||
nghttp2 library supports. */
|
||||
rv = nghttp2_select_next_protocol(out, outlen, in, inlen);
|
||||
if(rv <= 0) {
|
||||
die("Server did not advertise HTTP/2.0 protocol");
|
||||
die("Server did not advertise HTTP/2 protocol");
|
||||
}
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
@@ -521,8 +446,7 @@ static void ctl_poll(struct pollfd *pollfd, struct Connection *connection)
|
||||
*/
|
||||
static void submit_request(struct Connection *connection, struct Request *req)
|
||||
{
|
||||
int pri = 0;
|
||||
int rv;
|
||||
int32_t stream_id;
|
||||
const nghttp2_nv nva[] = {
|
||||
/* Make sure that the last item is NULL */
|
||||
MAKE_NV(":method", "GET"),
|
||||
@@ -532,11 +456,17 @@ static void submit_request(struct Connection *connection, struct Request *req)
|
||||
MAKE_NV("accept", "*/*"),
|
||||
MAKE_NV("user-agent", "nghttp2/"NGHTTP2_VERSION)
|
||||
};
|
||||
rv = nghttp2_submit_request(connection->session, pri,
|
||||
nva, sizeof(nva)/sizeof(nva[0]), NULL, req);
|
||||
if(rv != 0) {
|
||||
diec("nghttp2_submit_request", rv);
|
||||
|
||||
stream_id = nghttp2_submit_request(connection->session, NULL,
|
||||
nva, sizeof(nva)/sizeof(nva[0]),
|
||||
NULL, req);
|
||||
|
||||
if(stream_id < 0) {
|
||||
diec("nghttp2_submit_request", stream_id);
|
||||
}
|
||||
|
||||
req->stream_id = stream_id;
|
||||
printf("[INFO] Stream ID = %d\n", stream_id);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -562,7 +492,6 @@ static void request_init(struct Request *req, const struct URI *uri)
|
||||
req->path = strcopy(uri->path, uri->pathlen);
|
||||
req->hostport = strcopy(uri->hostport, uri->hostportlen);
|
||||
req->stream_id = -1;
|
||||
req->inflater = NULL;
|
||||
}
|
||||
|
||||
static void request_free(struct Request *req)
|
||||
@@ -570,7 +499,6 @@ static void request_free(struct Request *req)
|
||||
free(req->host);
|
||||
free(req->path);
|
||||
free(req->hostport);
|
||||
nghttp2_gzip_inflate_del(req->inflater);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -614,8 +542,8 @@ static void fetch_uri(const struct URI *uri)
|
||||
connection.want_io = IO_NONE;
|
||||
|
||||
/* Send connection header in blocking I/O mode */
|
||||
SSL_write(ssl, NGHTTP2_CLIENT_CONNECTION_HEADER,
|
||||
NGHTTP2_CLIENT_CONNECTION_HEADER_LEN);
|
||||
SSL_write(ssl, NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
|
||||
|
||||
/* Here make file descriptor non-block */
|
||||
make_non_block(fd);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2013 Tatsuhiro Tsujikawa
|
||||
*
|
||||
@@ -188,34 +188,13 @@ static ssize_t send_callback(nghttp2_session *session,
|
||||
return length;
|
||||
}
|
||||
|
||||
/* nghttp2_before_frame_send_callback: Called when nghttp2 library is
|
||||
about to send a frame. We use this callback to get stream ID of new
|
||||
stream. Since HEADERS in HTTP/2.0 has several roles, we check that
|
||||
it is a HTTP request HEADERS. */
|
||||
static int before_frame_send_callback
|
||||
(nghttp2_session *session, const nghttp2_frame *frame, void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
http2_stream_data *stream_data;
|
||||
|
||||
if(frame->hd.type == NGHTTP2_HEADERS &&
|
||||
frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
|
||||
stream_data =
|
||||
(http2_stream_data*)nghttp2_session_get_stream_user_data
|
||||
(session, frame->hd.stream_id);
|
||||
if(stream_data == session_data->stream_data) {
|
||||
stream_data->stream_id = frame->hd.stream_id;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* nghttp2_on_header_callback: Called when nghttp2 library emits
|
||||
single header name/value pair. */
|
||||
static int on_header_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame,
|
||||
const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen,
|
||||
uint8_t flags,
|
||||
void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
@@ -308,7 +287,7 @@ static int on_stream_close_callback(nghttp2_session *session,
|
||||
}
|
||||
|
||||
/* NPN TLS extension client callback. We check that server advertised
|
||||
the HTTP/2.0 protocol the nghttp2 library supports. If not, exit
|
||||
the HTTP/2 protocol the nghttp2 library supports. If not, exit
|
||||
the program. */
|
||||
static int select_next_proto_cb(SSL* ssl,
|
||||
unsigned char **out, unsigned char *outlen,
|
||||
@@ -351,10 +330,11 @@ static SSL* create_ssl(SSL_CTX *ssl_ctx)
|
||||
|
||||
static void initialize_nghttp2_session(http2_session_data *session_data)
|
||||
{
|
||||
nghttp2_session_callbacks callbacks = {0};
|
||||
nghttp2_session_callbacks callbacks;
|
||||
|
||||
memset(&callbacks, 0, sizeof(callbacks));
|
||||
|
||||
callbacks.send_callback = send_callback;
|
||||
callbacks.before_frame_send_callback = before_frame_send_callback;
|
||||
callbacks.on_frame_recv_callback = on_frame_recv_callback;
|
||||
callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback;
|
||||
callbacks.on_stream_close_callback = on_stream_close_callback;
|
||||
@@ -371,8 +351,8 @@ static void send_client_connection_header(http2_session_data *session_data)
|
||||
int rv;
|
||||
|
||||
bufferevent_write(session_data->bev,
|
||||
NGHTTP2_CLIENT_CONNECTION_HEADER,
|
||||
NGHTTP2_CLIENT_CONNECTION_HEADER_LEN);
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
|
||||
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE,
|
||||
iv, ARRLEN(iv));
|
||||
if(rv != 0) {
|
||||
@@ -381,15 +361,17 @@ static void send_client_connection_header(http2_session_data *session_data)
|
||||
}
|
||||
|
||||
#define MAKE_NV(NAME, VALUE, VALUELEN) \
|
||||
{ (uint8_t*)NAME, (uint8_t*)VALUE, sizeof(NAME) - 1, VALUELEN }
|
||||
{ (uint8_t*)NAME, (uint8_t*)VALUE, sizeof(NAME) - 1, VALUELEN, \
|
||||
NGHTTP2_NV_FLAG_NONE }
|
||||
|
||||
#define MAKE_NV2(NAME, VALUE) \
|
||||
{ (uint8_t*)NAME, (uint8_t*)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1 }
|
||||
{ (uint8_t*)NAME, (uint8_t*)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \
|
||||
NGHTTP2_NV_FLAG_NONE }
|
||||
|
||||
/* Send HTTP request to the remote peer */
|
||||
static void submit_request(http2_session_data *session_data)
|
||||
{
|
||||
int rv;
|
||||
int32_t stream_id;
|
||||
http2_stream_data *stream_data = session_data->stream_data;
|
||||
const char *uri = stream_data->uri;
|
||||
const struct http_parser_url *u = stream_data->u;
|
||||
@@ -402,11 +384,13 @@ static void submit_request(http2_session_data *session_data)
|
||||
};
|
||||
fprintf(stderr, "Request headers:\n");
|
||||
print_headers(stderr, hdrs, ARRLEN(hdrs));
|
||||
rv = nghttp2_submit_request(session_data->session, NGHTTP2_PRI_DEFAULT,
|
||||
stream_id = nghttp2_submit_request(session_data->session, NULL,
|
||||
hdrs, ARRLEN(hdrs), NULL, stream_data);
|
||||
if(rv != 0) {
|
||||
errx(1, "Could not submit HTTP request: %s", nghttp2_strerror(rv));
|
||||
if(stream_id < 0) {
|
||||
errx(1, "Could not submit HTTP request: %s", nghttp2_strerror(stream_id));
|
||||
}
|
||||
|
||||
stream_data->stream_id = stream_id;
|
||||
}
|
||||
|
||||
/* Serialize the frame and send (or buffer) the data to
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2013 Tatsuhiro Tsujikawa
|
||||
*
|
||||
@@ -49,7 +49,8 @@
|
||||
#define ARRLEN(x) (sizeof(x)/sizeof(x[0]))
|
||||
|
||||
#define MAKE_NV(NAME, VALUE) \
|
||||
{ (uint8_t*)NAME, (uint8_t*)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1 }
|
||||
{ (uint8_t*)NAME, (uint8_t*)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \
|
||||
NGHTTP2_NV_FLAG_NONE }
|
||||
|
||||
struct app_context;
|
||||
typedef struct app_context app_context;
|
||||
@@ -190,7 +191,7 @@ static http2_session_data* create_http2_session_data(app_context *app_ctx,
|
||||
(app_ctx->evbase, fd, ssl,
|
||||
BUFFEREVENT_SSL_ACCEPTING,
|
||||
BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
|
||||
session_data->handshake_leftlen = NGHTTP2_CLIENT_CONNECTION_HEADER_LEN;
|
||||
session_data->handshake_leftlen = NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN;
|
||||
rv = getnameinfo(addr, addrlen, host, sizeof(host), NULL, 0, NI_NUMERICHOST);
|
||||
if(rv != 0) {
|
||||
session_data->client_addr = strdup("(unknown)");
|
||||
@@ -327,7 +328,7 @@ static char* percent_decode(const uint8_t *value, size_t valuelen)
|
||||
|
||||
static ssize_t file_read_callback
|
||||
(nghttp2_session *session, int32_t stream_id,
|
||||
uint8_t *buf, size_t length, int *eof,
|
||||
uint8_t *buf, size_t length, uint32_t *data_flags,
|
||||
nghttp2_data_source *source, void *user_data)
|
||||
{
|
||||
int fd = source->fd;
|
||||
@@ -337,7 +338,7 @@ static ssize_t file_read_callback
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
if(r == 0) {
|
||||
*eof = 1;
|
||||
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
@@ -382,9 +383,17 @@ static int error_reply(nghttp2_session *session,
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
write(pipefd[1], ERROR_HTML, sizeof(ERROR_HTML) - 1);
|
||||
|
||||
rv = write(pipefd[1], ERROR_HTML, sizeof(ERROR_HTML) - 1);
|
||||
close(pipefd[1]);
|
||||
|
||||
if(rv != sizeof(ERROR_HTML)) {
|
||||
close(pipefd[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
stream_data->fd = pipefd[0];
|
||||
|
||||
if(send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs),
|
||||
pipefd[0]) != 0) {
|
||||
close(pipefd[0]);
|
||||
@@ -399,6 +408,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,
|
||||
void *user_data)
|
||||
{
|
||||
http2_stream_data *stream_data;
|
||||
@@ -410,7 +420,7 @@ static int on_header_callback(nghttp2_session *session,
|
||||
}
|
||||
stream_data = nghttp2_session_get_stream_user_data(session,
|
||||
frame->hd.stream_id);
|
||||
if(stream_data->request_path) {
|
||||
if(!stream_data || stream_data->request_path) {
|
||||
break;
|
||||
}
|
||||
if(namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) {
|
||||
@@ -529,6 +539,9 @@ static int on_stream_close_callback(nghttp2_session *session,
|
||||
http2_stream_data *stream_data;
|
||||
|
||||
stream_data = nghttp2_session_get_stream_user_data(session, stream_id);
|
||||
if(!stream_data) {
|
||||
return 0;
|
||||
}
|
||||
remove_stream(session_data, stream_data);
|
||||
delete_http2_stream_data(stream_data);
|
||||
return 0;
|
||||
@@ -536,7 +549,9 @@ static int on_stream_close_callback(nghttp2_session *session,
|
||||
|
||||
static void initialize_nghttp2_session(http2_session_data *session_data)
|
||||
{
|
||||
nghttp2_session_callbacks callbacks = {0};
|
||||
nghttp2_session_callbacks callbacks;
|
||||
|
||||
memset(&callbacks, 0, sizeof(callbacks));
|
||||
|
||||
callbacks.send_callback = send_callback;
|
||||
callbacks.on_frame_recv_callback = on_frame_recv_callback;
|
||||
@@ -546,7 +561,7 @@ static void initialize_nghttp2_session(http2_session_data *session_data)
|
||||
nghttp2_session_server_new(&session_data->session, &callbacks, session_data);
|
||||
}
|
||||
|
||||
/* Send HTTP/2.0 client connection header, which includes 24 bytes
|
||||
/* Send HTTP/2 client connection header, which includes 24 bytes
|
||||
magic octets and SETTINGS frame */
|
||||
static int send_server_connection_header(http2_session_data *session_data)
|
||||
{
|
||||
@@ -626,9 +641,9 @@ static void handshake_readcb(struct bufferevent *bev, void *ptr)
|
||||
uint8_t data[24];
|
||||
struct evbuffer *input = bufferevent_get_input(session_data->bev);
|
||||
int readlen = evbuffer_remove(input, data, session_data->handshake_leftlen);
|
||||
const char *conhead = NGHTTP2_CLIENT_CONNECTION_HEADER;
|
||||
const char *conhead = NGHTTP2_CLIENT_CONNECTION_PREFACE;
|
||||
|
||||
if(memcmp(conhead + NGHTTP2_CLIENT_CONNECTION_HEADER_LEN
|
||||
if(memcmp(conhead + NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN
|
||||
- session_data->handshake_leftlen, data, readlen) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
@@ -675,7 +690,7 @@ static void start_listen(struct event_base *evbase, const char *service,
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
#ifdef AI_ADDRCONFIG
|
||||
hints.ai_flags |= AI_ADDRCONFIG;
|
||||
#endif // AI_ADDRCONFIG
|
||||
#endif /* AI_ADDRCONFIG */
|
||||
|
||||
rv = getaddrinfo(NULL, service, &hints, &res);
|
||||
if(rv != 0) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# nghttp2 - HTTP/2.0 C Library
|
||||
# nghttp2 - HTTP/2 C Library
|
||||
|
||||
# Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa
|
||||
|
||||
@@ -32,21 +32,27 @@ DISTCLEANFILES = $(pkgconfig_DATA)
|
||||
lib_LTLIBRARIES = libnghttp2.la
|
||||
|
||||
OBJECTS = nghttp2_pq.c nghttp2_map.c nghttp2_queue.c \
|
||||
nghttp2_buffer.c nghttp2_frame.c \
|
||||
nghttp2_frame.c \
|
||||
nghttp2_buf.c \
|
||||
nghttp2_stream.c nghttp2_outbound_item.c \
|
||||
nghttp2_session.c nghttp2_submit.c \
|
||||
nghttp2_helper.c \
|
||||
nghttp2_npn.c nghttp2_gzip.c \
|
||||
nghttp2_npn.c \
|
||||
nghttp2_hd.c nghttp2_hd_huffman.c nghttp2_hd_huffman_data.c \
|
||||
nghttp2_version.c
|
||||
nghttp2_version.c \
|
||||
nghttp2_priority_spec.c \
|
||||
nghttp2_option.c
|
||||
|
||||
HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \
|
||||
nghttp2_buffer.h nghttp2_frame.h \
|
||||
nghttp2_frame.h \
|
||||
nghttp2_buf.h \
|
||||
nghttp2_session.h nghttp2_helper.h nghttp2_stream.h nghttp2_int.h \
|
||||
nghttp2_npn.h nghttp2_gzip.h \
|
||||
nghttp2_npn.h \
|
||||
nghttp2_submit.h nghttp2_outbound_item.h \
|
||||
nghttp2_net.h \
|
||||
nghttp2_hd.h nghttp2_hd_huffman.h
|
||||
nghttp2_hd.h nghttp2_hd_huffman.h \
|
||||
nghttp2_priority_spec.h \
|
||||
nghttp2_option.h
|
||||
|
||||
libnghttp2_la_SOURCES = $(HFILES) $(OBJECTS)
|
||||
libnghttp2_la_LDFLAGS = -no-undefined \
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# nghttp2 - HTTP/2.0 C Library
|
||||
# nghttp2 - HTTP/2 C Library
|
||||
|
||||
# Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa
|
||||
*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# nghttp2 - HTTP/2.0 C Library
|
||||
# nghttp2 - HTTP/2 C Library
|
||||
|
||||
# Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa
|
||||
|
||||
@@ -26,9 +26,8 @@ libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
Name: libnghttp2
|
||||
Description: HTTP/2.0 C library
|
||||
Description: HTTP/2 C library
|
||||
URL: https://github.com/tatsuhiro-t/nghttp2
|
||||
Version: @VERSION@
|
||||
Libs: -L${libdir} -lnghttp2
|
||||
Libs.private: -lz
|
||||
Cflags: -I${includedir}
|
||||
|
||||
477
lib/nghttp2_buf.c
Normal file
477
lib/nghttp2_buf.c
Normal file
@@ -0,0 +1,477 @@
|
||||
/*
|
||||
* 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 "nghttp2_buf.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "nghttp2_helper.h"
|
||||
|
||||
void nghttp2_buf_init(nghttp2_buf *buf)
|
||||
{
|
||||
buf->begin = NULL;
|
||||
buf->end = NULL;
|
||||
buf->pos = NULL;
|
||||
buf->last = NULL;
|
||||
buf->mark = NULL;
|
||||
}
|
||||
|
||||
int nghttp2_buf_init2(nghttp2_buf *buf, size_t initial)
|
||||
{
|
||||
nghttp2_buf_init(buf);
|
||||
return nghttp2_buf_reserve(buf, initial);
|
||||
}
|
||||
|
||||
void nghttp2_buf_free(nghttp2_buf *buf)
|
||||
{
|
||||
free(buf->begin);
|
||||
}
|
||||
|
||||
int nghttp2_buf_reserve(nghttp2_buf *buf, size_t new_cap)
|
||||
{
|
||||
uint8_t *ptr;
|
||||
size_t cap;
|
||||
|
||||
cap = nghttp2_buf_cap(buf);
|
||||
|
||||
if(cap >= new_cap) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
new_cap = nghttp2_max(new_cap, cap * 2);
|
||||
|
||||
ptr = realloc(buf->begin, new_cap);
|
||||
if(ptr == NULL) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
buf->pos = ptr + (buf->pos - buf->begin);
|
||||
buf->last = ptr + (buf->last - buf->begin);
|
||||
buf->mark = ptr + (buf->mark - buf->begin);
|
||||
buf->begin = ptr;
|
||||
buf->end = ptr + new_cap;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nghttp2_buf_pos_reserve(nghttp2_buf *buf, size_t new_rel_cap)
|
||||
{
|
||||
return nghttp2_buf_reserve(buf, nghttp2_buf_pos_offset(buf) + new_rel_cap);
|
||||
}
|
||||
|
||||
int nghttp2_buf_last_reserve(nghttp2_buf *buf, size_t new_rel_cap)
|
||||
{
|
||||
return nghttp2_buf_reserve(buf, nghttp2_buf_last_offset(buf) + new_rel_cap);
|
||||
}
|
||||
|
||||
void nghttp2_buf_reset(nghttp2_buf *buf)
|
||||
{
|
||||
buf->pos = buf->last = buf->mark = buf->begin;
|
||||
}
|
||||
|
||||
void nghttp2_buf_wrap_init(nghttp2_buf *buf, uint8_t *begin, size_t len)
|
||||
{
|
||||
buf->begin = buf->pos = buf->last = buf->mark = begin;
|
||||
buf->end = begin + len;
|
||||
}
|
||||
|
||||
static int buf_chain_new(nghttp2_buf_chain **chain, size_t chunk_length)
|
||||
{
|
||||
int rv;
|
||||
|
||||
*chain = malloc(sizeof(nghttp2_buf_chain));
|
||||
if(*chain == NULL) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
(*chain)->next = NULL;
|
||||
|
||||
rv = nghttp2_buf_init2(&(*chain)->buf, chunk_length);
|
||||
if(rv != 0) {
|
||||
free(*chain);
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void buf_chain_del(nghttp2_buf_chain *chain)
|
||||
{
|
||||
nghttp2_buf_free(&chain->buf);
|
||||
free(chain);
|
||||
}
|
||||
|
||||
int nghttp2_bufs_init(nghttp2_bufs *bufs, size_t chunk_length,
|
||||
size_t max_chunk)
|
||||
{
|
||||
return nghttp2_bufs_init2(bufs, chunk_length, max_chunk, 0);
|
||||
}
|
||||
|
||||
int nghttp2_bufs_init2(nghttp2_bufs *bufs, size_t chunk_length,
|
||||
size_t max_chunk, size_t offset)
|
||||
{
|
||||
return nghttp2_bufs_init3(bufs, chunk_length, max_chunk, max_chunk, offset);
|
||||
}
|
||||
|
||||
int nghttp2_bufs_init3(nghttp2_bufs *bufs, size_t chunk_length,
|
||||
size_t max_chunk, size_t chunk_keep, size_t offset)
|
||||
{
|
||||
int rv;
|
||||
nghttp2_buf_chain *chain;
|
||||
|
||||
if(chunk_keep == 0 || max_chunk < chunk_keep || chunk_length < offset) {
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
rv = buf_chain_new(&chain, chunk_length);
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
bufs->offset = offset;
|
||||
|
||||
bufs->head = chain;
|
||||
bufs->cur = bufs->head;
|
||||
|
||||
nghttp2_buf_shift_right(&bufs->cur->buf, offset);
|
||||
|
||||
bufs->chunk_length = chunk_length;
|
||||
bufs->chunk_used = 1;
|
||||
bufs->max_chunk = max_chunk;
|
||||
bufs->chunk_keep = chunk_keep;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nghttp2_bufs_free(nghttp2_bufs *bufs)
|
||||
{
|
||||
nghttp2_buf_chain *chain, *next_chain;
|
||||
|
||||
for(chain = bufs->head; chain;) {
|
||||
next_chain = chain->next;
|
||||
|
||||
buf_chain_del(chain);
|
||||
|
||||
chain = next_chain;
|
||||
}
|
||||
}
|
||||
|
||||
int nghttp2_bufs_wrap_init(nghttp2_bufs *bufs, uint8_t *begin, size_t len)
|
||||
{
|
||||
nghttp2_buf_chain *chain;
|
||||
|
||||
chain = malloc(sizeof(nghttp2_buf_chain));
|
||||
if(chain == NULL) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
chain->next = NULL;
|
||||
|
||||
nghttp2_buf_wrap_init(&chain->buf, begin, len);
|
||||
|
||||
bufs->offset = 0;
|
||||
|
||||
bufs->head = chain;
|
||||
bufs->cur = bufs->head;
|
||||
|
||||
bufs->chunk_length = len;
|
||||
bufs->chunk_used = 1;
|
||||
bufs->max_chunk = 1;
|
||||
bufs->chunk_keep = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nghttp2_bufs_wrap_free(nghttp2_bufs *bufs)
|
||||
{
|
||||
free(bufs->head);
|
||||
}
|
||||
|
||||
void nghttp2_bufs_seek_last_present(nghttp2_bufs *bufs)
|
||||
{
|
||||
nghttp2_buf_chain *ci;
|
||||
|
||||
for(ci = bufs->cur; ci; ci = ci->next) {
|
||||
if(nghttp2_buf_len(&ci->buf) == 0) {
|
||||
return;
|
||||
} else {
|
||||
bufs->cur = ci;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t nghttp2_bufs_len(nghttp2_bufs *bufs)
|
||||
{
|
||||
nghttp2_buf_chain *ci;
|
||||
ssize_t len;
|
||||
|
||||
len = 0;
|
||||
for(ci = bufs->head; ci; ci = ci->next) {
|
||||
len += nghttp2_buf_len(&ci->buf);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int bufs_avail(nghttp2_bufs *bufs)
|
||||
{
|
||||
return nghttp2_buf_avail(&bufs->cur->buf) +
|
||||
(bufs->chunk_length - bufs->offset) * (bufs->max_chunk - bufs->chunk_used);
|
||||
}
|
||||
|
||||
static int bufs_alloc_chain(nghttp2_bufs *bufs)
|
||||
{
|
||||
int rv;
|
||||
nghttp2_buf_chain *chain;
|
||||
|
||||
if(bufs->cur->next) {
|
||||
bufs->cur = bufs->cur->next;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(bufs->max_chunk == bufs->chunk_used) {
|
||||
return NGHTTP2_ERR_BUFFER_ERROR;
|
||||
}
|
||||
|
||||
rv = buf_chain_new(&chain, bufs->chunk_length);
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
DEBUGF(fprintf(stderr,
|
||||
"new buffer %zu bytes allocated for bufs %p, used %zu\n",
|
||||
bufs->chunk_length, bufs, bufs->chunk_used));
|
||||
|
||||
++bufs->chunk_used;
|
||||
|
||||
bufs->cur->next = chain;
|
||||
bufs->cur = chain;
|
||||
|
||||
nghttp2_buf_shift_right(&bufs->cur->buf, bufs->offset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nghttp2_bufs_add(nghttp2_bufs *bufs, const void *data, size_t len)
|
||||
{
|
||||
int rv;
|
||||
size_t nwrite;
|
||||
nghttp2_buf *buf;
|
||||
const uint8_t *p;
|
||||
|
||||
if(bufs_avail(bufs) < (ssize_t)len) {
|
||||
return NGHTTP2_ERR_BUFFER_ERROR;
|
||||
}
|
||||
|
||||
p = data;
|
||||
|
||||
while(len) {
|
||||
buf = &bufs->cur->buf;
|
||||
|
||||
nwrite = nghttp2_min((size_t)nghttp2_buf_avail(buf), len);
|
||||
if(nwrite == 0) {
|
||||
rv = bufs_alloc_chain(bufs);
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
buf->last = nghttp2_cpymem(buf->last, p, nwrite);
|
||||
p += len;
|
||||
len -= nwrite;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bufs_ensure_addb(nghttp2_bufs *bufs)
|
||||
{
|
||||
int rv;
|
||||
nghttp2_buf *buf;
|
||||
|
||||
buf = &bufs->cur->buf;
|
||||
|
||||
if(nghttp2_buf_avail(buf) > 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rv = bufs_alloc_chain(bufs);
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nghttp2_bufs_addb(nghttp2_bufs *bufs, uint8_t b)
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv = bufs_ensure_addb(bufs);
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
*bufs->cur->buf.last++ = b;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nghttp2_bufs_addb_hold(nghttp2_bufs *bufs, uint8_t b)
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv = bufs_ensure_addb(bufs);
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
*bufs->cur->buf.last = b;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nghttp2_bufs_orb(nghttp2_bufs *bufs, uint8_t b)
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv = bufs_ensure_addb(bufs);
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
*bufs->cur->buf.last++ |= b;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b)
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv = bufs_ensure_addb(bufs);
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
*bufs->cur->buf.last |= b;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out)
|
||||
{
|
||||
size_t len;
|
||||
nghttp2_buf_chain *chain;
|
||||
nghttp2_buf *buf;
|
||||
uint8_t *res;
|
||||
nghttp2_buf resbuf;
|
||||
|
||||
len = 0;
|
||||
|
||||
for(chain = bufs->head; chain; chain = chain->next) {
|
||||
len += nghttp2_buf_len(&chain->buf);
|
||||
}
|
||||
|
||||
if(!len) {
|
||||
res = NULL;
|
||||
} else {
|
||||
res = malloc(len);
|
||||
|
||||
if(res == NULL) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
nghttp2_buf_wrap_init(&resbuf, res, len);
|
||||
|
||||
for(chain = bufs->head; chain; chain = chain->next) {
|
||||
buf = &chain->buf;
|
||||
|
||||
if(resbuf.last) {
|
||||
resbuf.last = nghttp2_cpymem(resbuf.last,
|
||||
buf->pos, nghttp2_buf_len(buf));
|
||||
}
|
||||
|
||||
nghttp2_buf_reset(buf);
|
||||
nghttp2_buf_shift_right(&chain->buf, bufs->offset);
|
||||
}
|
||||
|
||||
bufs->cur = bufs->head;
|
||||
|
||||
*out = res;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void nghttp2_bufs_reset(nghttp2_bufs *bufs)
|
||||
{
|
||||
nghttp2_buf_chain *chain, *ci;
|
||||
size_t k;
|
||||
|
||||
k = bufs->chunk_keep;
|
||||
|
||||
for(ci = bufs->head; ci; ci = ci->next) {
|
||||
nghttp2_buf_reset(&ci->buf);
|
||||
nghttp2_buf_shift_right(&ci->buf, bufs->offset);
|
||||
|
||||
if(--k == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(ci) {
|
||||
chain = ci->next;
|
||||
ci->next = NULL;
|
||||
|
||||
for(ci = chain; ci;) {
|
||||
chain = ci->next;
|
||||
|
||||
buf_chain_del(ci);
|
||||
|
||||
ci = chain;
|
||||
}
|
||||
|
||||
bufs->chunk_used = bufs->chunk_keep;
|
||||
}
|
||||
|
||||
bufs->cur = bufs->head;
|
||||
}
|
||||
|
||||
int nghttp2_bufs_advance(nghttp2_bufs *bufs)
|
||||
{
|
||||
return bufs_alloc_chain(bufs);
|
||||
}
|
||||
|
||||
int nghttp2_bufs_next_present(nghttp2_bufs *bufs)
|
||||
{
|
||||
nghttp2_buf_chain *chain;
|
||||
|
||||
chain = bufs->cur->next;
|
||||
|
||||
return chain && nghttp2_buf_len(&chain->buf);
|
||||
}
|
||||
|
||||
371
lib/nghttp2_buf.h
Normal file
371
lib/nghttp2_buf.h
Normal file
@@ -0,0 +1,371 @@
|
||||
/*
|
||||
* 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 NGHTTP2_BUF_H
|
||||
#define NGHTTP2_BUF_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
#include "nghttp2_int.h"
|
||||
|
||||
typedef struct {
|
||||
/* This points to the beginning of the buffer. The effective range
|
||||
of buffer is [begin, end). */
|
||||
uint8_t *begin;
|
||||
/* This points to the memory one byte beyond the end of the
|
||||
buffer. */
|
||||
uint8_t *end;
|
||||
/* The position indicator for effective start of the buffer. pos <=
|
||||
last must be hold. */
|
||||
uint8_t *pos;
|
||||
/* The position indicator for effective one beyond of the end of the
|
||||
buffer. last <= end must be hold. */
|
||||
uint8_t *last;
|
||||
/* Mark arbitrary position in buffer [begin, end) */
|
||||
uint8_t *mark;
|
||||
} nghttp2_buf;
|
||||
|
||||
#define nghttp2_buf_len(BUF) ((ssize_t)((BUF)->last - (BUF)->pos))
|
||||
#define nghttp2_buf_avail(BUF) ((ssize_t)((BUF)->end - (BUF)->last))
|
||||
#define nghttp2_buf_mark_avail(BUF) ((ssize_t)((BUF)->mark - (BUF)->last))
|
||||
#define nghttp2_buf_cap(BUF) ((ssize_t)((BUF)->end - (BUF)->begin))
|
||||
|
||||
#define nghttp2_buf_pos_offset(BUF) ((ssize_t)((BUF)->pos - (BUF)->begin))
|
||||
#define nghttp2_buf_last_offset(BUF) ((ssize_t)((BUF)->last - (BUF)->begin))
|
||||
|
||||
#define nghttp2_buf_shift_right(BUF, AMT) \
|
||||
do { \
|
||||
(BUF)->pos += AMT; \
|
||||
(BUF)->last += AMT; \
|
||||
} while(0)
|
||||
|
||||
#define nghttp2_buf_shift_left(BUF, AMT) \
|
||||
do { \
|
||||
(BUF)->pos -= AMT; \
|
||||
(BUF)->last -= AMT; \
|
||||
} while(0)
|
||||
|
||||
/*
|
||||
* Initializes the |buf|. No memory is allocated in this function. Use
|
||||
* nghttp2_buf_reserve() or nghttp2_buf_reserve2() to allocate memory.
|
||||
*/
|
||||
void nghttp2_buf_init(nghttp2_buf *buf);
|
||||
|
||||
|
||||
/*
|
||||
* Initializes the |buf| and allocates at least |initial| bytes of
|
||||
* memory.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
int nghttp2_buf_init2(nghttp2_buf *buf, size_t initial);
|
||||
|
||||
/*
|
||||
* Frees buffer in |buf|.
|
||||
*/
|
||||
void nghttp2_buf_free(nghttp2_buf *buf);
|
||||
|
||||
/*
|
||||
* Extends buffer so that nghttp2_buf_cap() returns at least
|
||||
* |new_cap|. If extensions took place, buffer pointers in |buf| will
|
||||
* change.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the followings
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
int nghttp2_buf_reserve(nghttp2_buf *buf, size_t new_cap);
|
||||
|
||||
/*
|
||||
* This function behaves like nghttp2_buf_reserve(), but new capacity
|
||||
* is calculated as nghttp2_buf_pos_offset(buf) + new_rel_cap. In
|
||||
* other words, this function reserves memory at least |new_rel_cap|
|
||||
* bytes from buf->pos.
|
||||
*/
|
||||
int nghttp2_buf_pos_reserve(nghttp2_buf *buf, size_t new_rel_cap);
|
||||
|
||||
/*
|
||||
* This function behaves like nghttp2_buf_reserve(), but new capacity
|
||||
* is calculated as nghttp2_buf_last_offset(buf) + new_rel_cap. In
|
||||
* other words, this function reserves memory at least |new_rel_cap|
|
||||
* bytes from buf->last.
|
||||
*/
|
||||
int nghttp2_buf_last_reserve(nghttp2_buf *buf, size_t new_rel_cap);
|
||||
|
||||
/*
|
||||
* Resets pos, last, mark member of |buf| to buf->begin.
|
||||
*/
|
||||
void nghttp2_buf_reset(nghttp2_buf *buf);
|
||||
|
||||
/*
|
||||
* Initializes |buf| using supplied buffer |begin| of length
|
||||
* |len|. Semantically, the application should not call *_reserve() or
|
||||
* nghttp2_free() functions for |buf|.
|
||||
*/
|
||||
void nghttp2_buf_wrap_init(nghttp2_buf *buf, uint8_t *begin, size_t len);
|
||||
|
||||
struct nghttp2_buf_chain;
|
||||
|
||||
typedef struct nghttp2_buf_chain nghttp2_buf_chain;
|
||||
|
||||
/* Chains 2 buffers */
|
||||
struct nghttp2_buf_chain {
|
||||
/* Points to the subsequent buffer. NULL if there is no such
|
||||
buffer. */
|
||||
nghttp2_buf_chain *next;
|
||||
nghttp2_buf buf;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
/* Points to the first buffer */
|
||||
nghttp2_buf_chain *head;
|
||||
/* Buffer pointer where write occurs. */
|
||||
nghttp2_buf_chain *cur;
|
||||
/* The buffer capacity of each buf */
|
||||
size_t chunk_length;
|
||||
/* The maximum number of nghttp2_buf_chain */
|
||||
size_t max_chunk;
|
||||
/* The number of nghttp2_buf_chain allocated */
|
||||
size_t chunk_used;
|
||||
/* The number of nghttp2_buf_chain to keep on reset */
|
||||
size_t chunk_keep;
|
||||
/* pos offset from begin in each buffers. On initialization and
|
||||
reset, buf->pos and buf->last are positioned at buf->begin +
|
||||
offset. */
|
||||
size_t offset;
|
||||
} nghttp2_bufs;
|
||||
|
||||
/*
|
||||
* This is the same as calling nghttp2_bufs_init2 with the given
|
||||
* arguments and offset = 0.
|
||||
*/
|
||||
int nghttp2_bufs_init(nghttp2_bufs *bufs, size_t chunk_length,
|
||||
size_t max_chunk);
|
||||
|
||||
/*
|
||||
* This is the same as calling nghttp2_bufs_init3 with the given
|
||||
* arguments and chunk_keep = max_chunk.
|
||||
*/
|
||||
int nghttp2_bufs_init2(nghttp2_bufs *bufs, size_t chunk_length,
|
||||
size_t max_chunk, size_t offset);
|
||||
|
||||
/*
|
||||
* Initializes |bufs|. Each buffer size is given in the
|
||||
* |chunk_length|. The maximum number of buffers is given in the
|
||||
* |max_chunk|. On reset, first |chunk_keep| buffers are kept and
|
||||
* remaining buffers are deleted. Each buffer will have bufs->pos and
|
||||
* bufs->last shifted to left by |offset| bytes on creation and reset.
|
||||
*
|
||||
* This function allocates first buffer. bufs->head and bufs->cur
|
||||
* will point to the first buffer after this call.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
* NGHTTP2_ERR_INVALID_ARGUMENT
|
||||
* chunk_keep is 0; or max_chunk < chunk_keep; or offset is too
|
||||
* long.
|
||||
*/
|
||||
int nghttp2_bufs_init3(nghttp2_bufs *bufs, size_t chunk_length,
|
||||
size_t max_chunk, size_t chunk_keep, size_t offset);
|
||||
|
||||
/*
|
||||
* Frees any related resources to the |bufs|.
|
||||
*/
|
||||
void nghttp2_bufs_free(nghttp2_bufs *bufs);
|
||||
|
||||
/*
|
||||
* Initializes |bufs| using supplied buffer |begin| of length |len|.
|
||||
* The first buffer bufs->head uses buffer |begin|. The buffer size
|
||||
* is fixed and no allocate extra chunk buffer is allocated. In other
|
||||
* words, max_chunk = chunk_keep = 1. To free the resource allocated
|
||||
* for |bufs|, use nghttp2_bufs_wrap_free().
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
int nghttp2_bufs_wrap_init(nghttp2_bufs *bufs, uint8_t *begin, size_t len);
|
||||
|
||||
/*
|
||||
* Frees any related resource to the |bufs|. This function does not
|
||||
* free supplied buffer provided in nghttp2_bufs_wrap_init().
|
||||
*/
|
||||
void nghttp2_bufs_wrap_free(nghttp2_bufs *bufs);
|
||||
|
||||
/*
|
||||
* Appends the |data| of length |len| to the |bufs|. The write starts
|
||||
* at bufs->cur->buf.last. A new buffers will be allocated to store
|
||||
* all data.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
* NGHTTP2_ERR_BUFFER_ERROR
|
||||
* Out of buffer space.
|
||||
*/
|
||||
int nghttp2_bufs_add(nghttp2_bufs *bufs, const void *data, size_t len);
|
||||
|
||||
/*
|
||||
* Appends a single byte |b| to the |bufs|. The write starts at
|
||||
* bufs->cur->buf.last. A new buffers will be allocated to store all
|
||||
* data.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
* NGHTTP2_ERR_BUFFER_ERROR
|
||||
* Out of buffer space.
|
||||
*/
|
||||
int nghttp2_bufs_addb(nghttp2_bufs *bufs, uint8_t b);
|
||||
|
||||
/*
|
||||
* Behaves like nghttp2_bufs_addb(), but this does not update
|
||||
* buf->last pointer.
|
||||
*/
|
||||
int nghttp2_bufs_addb_hold(nghttp2_bufs *bufs, uint8_t b);
|
||||
|
||||
#define nghttp2_bufs_fast_addb(BUFS, B) \
|
||||
do { \
|
||||
*(BUFS)->cur->buf.last++ = B; \
|
||||
} while(0)
|
||||
|
||||
#define nghttp2_bufs_fast_addb_hold(BUFS, B) \
|
||||
do { \
|
||||
*(BUFS)->cur->buf.last = B; \
|
||||
} while(0)
|
||||
|
||||
/*
|
||||
* Performs bitwise-OR of |b| at bufs->cur->buf.last. A new buffers
|
||||
* will be allocated if necessary.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
* NGHTTP2_ERR_BUFFER_ERROR
|
||||
* Out of buffer space.
|
||||
*/
|
||||
int nghttp2_bufs_orb(nghttp2_bufs *bufs, uint8_t b);
|
||||
|
||||
/*
|
||||
* Behaves like nghttp2_bufs_orb(), but does not update buf->last
|
||||
* pointer.
|
||||
*/
|
||||
int nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b);
|
||||
|
||||
#define nghttp2_bufs_fast_orb(BUFS, B) \
|
||||
do { \
|
||||
*(BUFS)->cur->buf.last++ |= B; \
|
||||
} while(0)
|
||||
|
||||
#define nghttp2_bufs_fast_orb_hold(BUFS, B) \
|
||||
do { \
|
||||
*(BUFS)->cur->buf.last |= B; \
|
||||
} while(0)
|
||||
|
||||
/*
|
||||
* Copies all data stored in |bufs| to the contagious buffer. This
|
||||
* function allocates the contagious memory to store all data in
|
||||
* |bufs| and assigns it to |*out|.
|
||||
*
|
||||
* On successful return, nghttp2_bufs_len(bufs) returns 0, just like
|
||||
* after calling nghttp2_bufs_reset().
|
||||
|
||||
* This function returns the length of copied data and assigns the
|
||||
* pointer to copied data to |*out| if it succeeds, or one of the
|
||||
* following negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out);
|
||||
|
||||
/*
|
||||
* Resets |bufs| and makes the buffers empty.
|
||||
*/
|
||||
void nghttp2_bufs_reset(nghttp2_bufs *bufs);
|
||||
|
||||
/*
|
||||
* Moves bufs->cur to bufs->cur->next. If resulting bufs->cur is
|
||||
* NULL, this function allocates new buffers and bufs->cur points to
|
||||
* it.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
* NGHTTP2_ERR_BUFFER_ERROR
|
||||
* Out of buffer space.
|
||||
*/
|
||||
int nghttp2_bufs_advance(nghttp2_bufs *bufs);
|
||||
|
||||
/* Sets bufs->cur to bufs->head */
|
||||
#define nghttp2_bufs_rewind(BUFS) \
|
||||
do { \
|
||||
(BUFS)->cur = (BUFS)->head; \
|
||||
} while(0)
|
||||
|
||||
/*
|
||||
* Move bufs->cur, from the current position, using next member, to
|
||||
* the last buf which has nghttp2_buf_len(buf) > 0 without seeing buf
|
||||
* which satisfies nghttp2_buf_len(buf) == 0. If
|
||||
* nghttp2_buf_len(&bufs->cur->buf) == 0 or bufs->cur->next is NULL,
|
||||
* bufs->cur is unchanged.
|
||||
*/
|
||||
void nghttp2_bufs_seek_last_present(nghttp2_bufs *bufs);
|
||||
|
||||
/*
|
||||
* Returns nonzero if bufs->cur->next is not emtpy.
|
||||
*/
|
||||
int nghttp2_bufs_next_present(nghttp2_bufs *bufs);
|
||||
|
||||
#define nghttp2_bufs_cur_avail(BUFS) nghttp2_buf_avail(&(BUFS)->cur->buf)
|
||||
|
||||
/*
|
||||
* Returns the buffer length of |bufs|.
|
||||
*/
|
||||
ssize_t nghttp2_bufs_len(nghttp2_bufs *bufs);
|
||||
|
||||
#endif /* NGHTTP2_BUF_H */
|
||||
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2.0 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 "nghttp2_buffer.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "nghttp2_helper.h"
|
||||
|
||||
void nghttp2_buffer_init(nghttp2_buffer *buffer, size_t max_capacity)
|
||||
{
|
||||
buffer->buf = NULL;
|
||||
buffer->len = 0;
|
||||
buffer->capacity = 0;
|
||||
buffer->max_capacity = max_capacity;
|
||||
}
|
||||
|
||||
void nghttp2_buffer_free(nghttp2_buffer *buffer)
|
||||
{
|
||||
free(buffer->buf);
|
||||
}
|
||||
|
||||
int nghttp2_buffer_reserve(nghttp2_buffer *buffer, size_t len)
|
||||
{
|
||||
if(len > buffer->max_capacity) {
|
||||
return NGHTTP2_ERR_BUFFER_ERROR;
|
||||
}
|
||||
if(buffer->capacity < len) {
|
||||
uint8_t *new_buf;
|
||||
size_t new_cap = buffer->capacity == 0 ? 8 : buffer->capacity * 3 / 2;
|
||||
new_cap = nghttp2_min(buffer->max_capacity, nghttp2_max(new_cap, len));
|
||||
new_buf = realloc(buffer->buf, new_cap);
|
||||
if(new_buf == NULL) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
buffer->buf = new_buf;
|
||||
buffer->capacity = new_cap;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nghttp2_buffer_add(nghttp2_buffer *buffer,
|
||||
const uint8_t *data, size_t len)
|
||||
{
|
||||
int rv;
|
||||
rv = nghttp2_buffer_reserve(buffer, buffer->len + len);
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
memcpy(buffer->buf + buffer->len, data, len);
|
||||
buffer->len += len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nghttp2_buffer_add_byte(nghttp2_buffer *buffer, uint8_t b)
|
||||
{
|
||||
int rv;
|
||||
rv = nghttp2_buffer_reserve(buffer, buffer->len + 1);
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
buffer->buf[buffer->len] = b;
|
||||
++buffer->len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nghttp2_buffer_release(nghttp2_buffer *buffer)
|
||||
{
|
||||
buffer->buf = NULL;
|
||||
buffer->len = 0;
|
||||
buffer->capacity = 0;
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2.0 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 NGHTTP2_BUFFER_H
|
||||
#define NGHTTP2_BUFFER_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
#include "nghttp2_int.h"
|
||||
|
||||
/*
|
||||
* Byte array buffer
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t *buf;
|
||||
/* Capacity of this buffer */
|
||||
size_t capacity;
|
||||
/* How many bytes are written to buf. len <= capacity must hold. */
|
||||
size_t len;
|
||||
/* Maximum capacity this buffer can grow up */
|
||||
size_t max_capacity;
|
||||
} nghttp2_buffer;
|
||||
|
||||
void nghttp2_buffer_init(nghttp2_buffer *buffer, size_t max_capacity);
|
||||
|
||||
void nghttp2_buffer_free(nghttp2_buffer *buffer);
|
||||
|
||||
/*
|
||||
* Expands capacity so that it can contain at least |len| bytes of
|
||||
* data. If buffer->capacity >= len, no action is taken. If len >
|
||||
* buffer->max_capacity, NGHTTP2_ERR_BUFFER_ERROR is returned.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_BUFFER_ERROR
|
||||
* The |len| is strictly larger than buffer->max_capacity
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
int nghttp2_buffer_reserve(nghttp2_buffer *buffer, size_t len);
|
||||
|
||||
/*
|
||||
* Appends the |data| with |len| bytes to the buffer. The data is
|
||||
* copied. The |buffer| will be expanded as needed.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_BUFFER_ERROR
|
||||
* The |len| is strictly larger than buffer->max_capacity
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
int nghttp2_buffer_add(nghttp2_buffer *buffer,
|
||||
const uint8_t *data, size_t len);
|
||||
|
||||
/*
|
||||
* Appends the a single byte|b| to the buffer. The data is copied. The
|
||||
* |buffer| will be expanded as needed.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_BUFFER_ERROR
|
||||
* The |len| is strictly larger than buffer->max_capacity
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
int nghttp2_buffer_add_byte(nghttp2_buffer *buffer, uint8_t b);
|
||||
|
||||
/*
|
||||
* Releases the buffer without freeing it. The data members in buffer
|
||||
* is initialized.
|
||||
*/
|
||||
void nghttp2_buffer_release(nghttp2_buffer *buffer);
|
||||
|
||||
#endif /* NGHTTP2_BUFFER_H */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
*
|
||||
@@ -31,22 +31,34 @@
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
#include "nghttp2_hd.h"
|
||||
#include "nghttp2_buffer.h"
|
||||
#include "nghttp2_buf.h"
|
||||
|
||||
#define NGHTTP2_FRAME_LENGTH_MASK ((1 << 14) - 1)
|
||||
#define NGHTTP2_STREAM_ID_MASK ((1u << 31) - 1)
|
||||
#define NGHTTP2_PRI_GROUP_ID_MASK ((1u << 31) - 1)
|
||||
#define NGHTTP2_PRIORITY_MASK ((1u << 31) - 1)
|
||||
#define NGHTTP2_WINDOW_SIZE_INCREMENT_MASK ((1u << 31) - 1)
|
||||
#define NGHTTP2_SETTINGS_ID_MASK ((1 << 24) - 1)
|
||||
|
||||
/* The maximum payload length of a frame */
|
||||
#define NGHTTP2_MAX_FRAME_LENGTH ((1 << 14) - 1)
|
||||
/* The number of bytes of frame header. */
|
||||
#define NGHTTP2_FRAME_HDLEN 8
|
||||
|
||||
#define NGHTTP2_MAX_PAYLOADLEN 16383
|
||||
/* The one frame buffer length for tranmission. We may use several of
|
||||
them to support CONTINUATION. To account for padding specifiers
|
||||
(PAD_HIGH and PAD_LOW), we allocate extra 2 bytes, which saves
|
||||
extra large memcopying. */
|
||||
#define NGHTTP2_FRAMEBUF_CHUNKLEN \
|
||||
(NGHTTP2_FRAME_HDLEN + 2 + NGHTTP2_MAX_PAYLOADLEN)
|
||||
|
||||
/* The maximum length of DATA frame payload. */
|
||||
#define NGHTTP2_DATA_PAYLOAD_LENGTH 4096
|
||||
#define NGHTTP2_DATA_PAYLOADLEN 4096
|
||||
|
||||
/* The number of bytes of frame header. */
|
||||
#define NGHTTP2_FRAME_HEAD_LENGTH 8
|
||||
/* The number of bytes for each SETTINGS entry */
|
||||
#define NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH 5
|
||||
|
||||
/* The maximum header table size in SETTINGS_HEADER_TABLE_SIZE */
|
||||
#define NGHTTP2_MAX_HEADER_TABLE_SIZE ((1u << 31) - 1)
|
||||
|
||||
/* Category of frames. */
|
||||
typedef enum {
|
||||
@@ -68,6 +80,11 @@ typedef struct {
|
||||
* The data to be sent for this DATA frame.
|
||||
*/
|
||||
nghttp2_data_provider data_prd;
|
||||
/**
|
||||
* The number of bytes added as padding. This includes PAD_HIGH and
|
||||
* PAD_LOW.
|
||||
*/
|
||||
size_t padlen;
|
||||
/**
|
||||
* The flag to indicate whether EOF was reached or not. Initially
|
||||
* |eof| is 0. It becomes 1 after all data were read. This is used
|
||||
@@ -82,6 +99,31 @@ void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd);
|
||||
|
||||
void nghttp2_frame_unpack_frame_hd(nghttp2_frame_hd *hd, const uint8_t* buf);
|
||||
|
||||
/**
|
||||
* Returns the number of priority field depending on the |flags|. If
|
||||
* |flags| has neither NGHTTP2_FLAG_PRIORITY_GROUP nor
|
||||
* NGHTTP2_FLAG_PRIORITY_DEPENDENCY set, return 0.
|
||||
*/
|
||||
size_t nghttp2_frame_priority_len(uint8_t flags);
|
||||
|
||||
/**
|
||||
* Packs the |pri_spec| in |buf|. This function assumes |buf| has
|
||||
* enough space for serialization.
|
||||
*/
|
||||
void nghttp2_frame_pack_priority_spec(uint8_t *buf,
|
||||
const nghttp2_priority_spec *pri_spec);
|
||||
|
||||
/**
|
||||
* Unpacks the priority specification from payload |payload| of length
|
||||
* |payloadlen| to |pri_spec|. The |flags| is used to determine what
|
||||
* kind of priority specification is in |payload|. This function
|
||||
* assumes the |payload| contains whole priority specification.
|
||||
*/
|
||||
void nghttp2_frame_unpack_priority_spec(nghttp2_priority_spec *pri_spec,
|
||||
uint8_t flags,
|
||||
const uint8_t *payload,
|
||||
size_t payloadlen);
|
||||
|
||||
/*
|
||||
* Returns the offset from the HEADERS frame payload where the
|
||||
* compressed header block starts. The frame payload does not include
|
||||
@@ -90,42 +132,34 @@ void nghttp2_frame_unpack_frame_hd(nghttp2_frame_hd *hd, const uint8_t* buf);
|
||||
size_t nghttp2_frame_headers_payload_nv_offset(nghttp2_headers *frame);
|
||||
|
||||
/*
|
||||
* Packs HEADERS frame |frame| in wire format and store it in
|
||||
* |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| bytes.
|
||||
* This function expands |*buf_ptr| as necessary to store frame. When
|
||||
* expansion occurred, memory previously pointed by |*buf_ptr| may
|
||||
* change. |*buf_ptr| and |*buflen_ptr| are updated accordingly.
|
||||
* Packs HEADERS frame |frame| in wire format and store it in |bufs|.
|
||||
* This function expands |bufs| as necessary to store frame.
|
||||
*
|
||||
* The caller must make sure that nghttp2_bufs_reset(bufs) is called
|
||||
* before calling this function.
|
||||
*
|
||||
* frame->hd.length is assigned after length is determined during
|
||||
* packing process. If payload length is strictly larger than
|
||||
* NGHTTP2_MAX_FRAME_LENGTH, payload data is still serialized as is,
|
||||
* but frame->hd.length is set to NGHTTP2_MAX_FRAME_LENGTH and
|
||||
* NGHTTP2_FLAG_END_HEADERS flag is cleared from frame->hd.flags.
|
||||
* packing process. CONTINUATION frames are also serialized in this
|
||||
* function. This function does not handle padding.
|
||||
*
|
||||
* This function returns the size of packed frame if it succeeds, or
|
||||
* returns one of the following negative error codes:
|
||||
* This function returns 0 if it succeeds, or returns one of the
|
||||
* following negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_HEADER_COMP
|
||||
* The deflate operation failed.
|
||||
* NGHTTP2_ERR_FRAME_TOO_LARGE
|
||||
* The length of the frame is too large.
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
ssize_t nghttp2_frame_pack_headers(uint8_t **buf_ptr,
|
||||
size_t *buflen_ptr,
|
||||
int nghttp2_frame_pack_headers(nghttp2_bufs *bufs,
|
||||
nghttp2_headers *frame,
|
||||
nghttp2_hd_deflater *deflater);
|
||||
|
||||
/*
|
||||
* Unpacks HEADERS frame byte sequence into |frame|. This function
|
||||
* only unapcks bytes that come before name/value header block.
|
||||
* only unapcks bytes that come before name/value header block and
|
||||
* after PAD_HIGH and PAD_LOW.
|
||||
*
|
||||
* This function returns 0 if it succeeds or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_PROTO
|
||||
* TODO END_HEADERS flag is not set
|
||||
* This function always succeeds and returns 0.
|
||||
*/
|
||||
int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame,
|
||||
const uint8_t *payload,
|
||||
@@ -133,17 +167,14 @@ int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame,
|
||||
|
||||
/*
|
||||
* Packs PRIORITY frame |frame| in wire format and store it in
|
||||
* |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr|
|
||||
* length. This function expands |*buf_ptr| as necessary to store
|
||||
* given |frame|.
|
||||
* |bufs|.
|
||||
*
|
||||
* This function returns 0 if it succeeds or one of the following
|
||||
* negative error codes:
|
||||
* The caller must make sure that nghttp2_bufs_reset(bufs) is called
|
||||
* before calling this function.
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
* This function always succeeds and returns 0.
|
||||
*/
|
||||
ssize_t nghttp2_frame_pack_priority(uint8_t **buf_ptr, size_t *buflen_ptr,
|
||||
int nghttp2_frame_pack_priority(nghttp2_bufs *bufs,
|
||||
nghttp2_priority *frame);
|
||||
|
||||
/*
|
||||
@@ -155,18 +186,14 @@ void nghttp2_frame_unpack_priority_payload(nghttp2_priority *frame,
|
||||
|
||||
/*
|
||||
* Packs RST_STREAM frame |frame| in wire frame format and store it in
|
||||
* |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr|
|
||||
* length. This function expands |*buf_ptr| as necessary to store
|
||||
* given |frame|. In spdy/2 spec, RST_STREAM wire format is always 16
|
||||
* bytes long.
|
||||
* |bufs|.
|
||||
*
|
||||
* This function returns the size of packed frame if it succeeds, or
|
||||
* returns one of the following negative error codes:
|
||||
* The caller must make sure that nghttp2_bufs_reset(bufs) is called
|
||||
* before calling this function.
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
* This function always succeeds and returns 0.
|
||||
*/
|
||||
ssize_t nghttp2_frame_pack_rst_stream(uint8_t **buf_ptr, size_t *buflen_ptr,
|
||||
int nghttp2_frame_pack_rst_stream(nghttp2_bufs *bufs,
|
||||
nghttp2_rst_stream *frame);
|
||||
|
||||
/*
|
||||
@@ -178,18 +205,18 @@ void nghttp2_frame_unpack_rst_stream_payload(nghttp2_rst_stream *frame,
|
||||
|
||||
/*
|
||||
* Packs SETTINGS frame |frame| in wire format and store it in
|
||||
* |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr|
|
||||
* length. This function expands |*buf_ptr| as necessary to store
|
||||
* given |frame|.
|
||||
* |bufs|.
|
||||
*
|
||||
* This function returns the size of packed frame if it succeeds, or
|
||||
* returns one of the following negative error codes:
|
||||
* The caller must make sure that nghttp2_bufs_reset(bufs) is called
|
||||
* before calling this function.
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
* This function returns 0 if it succeeds, or returns one of the
|
||||
* following negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_FRAME_SIZE_ERROR
|
||||
* The length of the frame is too large.
|
||||
*/
|
||||
ssize_t nghttp2_frame_pack_settings(uint8_t **buf_ptr, size_t *buflen_ptr,
|
||||
nghttp2_settings *frame);
|
||||
int nghttp2_frame_pack_settings(nghttp2_bufs *bufs, nghttp2_settings *frame);
|
||||
|
||||
/*
|
||||
* Packs the |iv|, which includes |niv| entries, in the |buf|,
|
||||
@@ -237,35 +264,32 @@ int nghttp2_frame_unpack_settings_payload2(nghttp2_settings_entry **iv_ptr,
|
||||
|
||||
/*
|
||||
* Packs PUSH_PROMISE frame |frame| in wire format and store it in
|
||||
* |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| bytes.
|
||||
* This function expands |*buf_ptr| as necessary to store frame. When
|
||||
* expansion occurred, memory previously pointed by |*buf_ptr| may
|
||||
* change. |*buf_ptr| and |*buflen_ptr| are updated accordingly.
|
||||
* |bufs|. This function expands |bufs| as necessary to store
|
||||
* frame.
|
||||
*
|
||||
* The caller must make sure that nghttp2_bufs_reset(bufs) is called
|
||||
* before calling this function.
|
||||
*
|
||||
* frame->hd.length is assigned after length is determined during
|
||||
* packing process. If payload length is strictly larger than
|
||||
* NGHTTP2_MAX_FRAME_LENGTH, payload data is still serialized as is,
|
||||
* but frame->hd.length is set to NGHTTP2_MAX_FRAME_LENGTH and
|
||||
* NGHTTP2_FLAG_END_HEADERS flag is cleared from frame->hd.flags.
|
||||
* packing process. CONTINUATION frames are also serialized in this
|
||||
* function. This function does not handle padding.
|
||||
*
|
||||
* This function returns the size of packed frame if it succeeds, or
|
||||
* returns one of the following negative error codes:
|
||||
* This function returns 0 if it succeeds, or returns one of the
|
||||
* following negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_HEADER_COMP
|
||||
* The deflate operation failed.
|
||||
* NGHTTP2_ERR_FRAME_TOO_LARGE
|
||||
* The length of the frame is too large.
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
ssize_t nghttp2_frame_pack_push_promise(uint8_t **buf_ptr,
|
||||
size_t *buflen_ptr,
|
||||
int nghttp2_frame_pack_push_promise(nghttp2_bufs *bufs,
|
||||
nghttp2_push_promise *frame,
|
||||
nghttp2_hd_deflater *deflater);
|
||||
|
||||
/*
|
||||
* Unpacks PUSH_PROMISE frame byte sequence into |frame|. This function
|
||||
* only unapcks bytes that come before name/value header block.
|
||||
* Unpacks PUSH_PROMISE frame byte sequence into |frame|. This
|
||||
* function only unapcks bytes that come before name/value header
|
||||
* block and after PAD_HIGH and PAD_LOW.
|
||||
*
|
||||
* This function returns 0 if it succeeds or one of the following
|
||||
* negative error codes:
|
||||
@@ -279,18 +303,14 @@ int nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame,
|
||||
|
||||
/*
|
||||
* Packs PING frame |frame| in wire format and store it in
|
||||
* |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr|
|
||||
* length. This function expands |*buf_ptr| as necessary to store
|
||||
* given |frame|.
|
||||
* |bufs|.
|
||||
*
|
||||
* This function returns 0 if it succeeds or one of the following
|
||||
* negative error codes:
|
||||
* The caller must make sure that nghttp2_bufs_reset(bufs) is called
|
||||
* before calling this function.
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
* This function always succeeds and returns 0.
|
||||
*/
|
||||
ssize_t nghttp2_frame_pack_ping(uint8_t **buf_ptr, size_t *buflen_ptr,
|
||||
nghttp2_ping *frame);
|
||||
int nghttp2_frame_pack_ping(nghttp2_bufs *bufs, nghttp2_ping *frame);
|
||||
|
||||
/*
|
||||
* Unpacks PING wire format into |frame|.
|
||||
@@ -300,40 +320,61 @@ void nghttp2_frame_unpack_ping_payload(nghttp2_ping *frame,
|
||||
size_t payloadlen);
|
||||
|
||||
/*
|
||||
* Packs GOAWAY frame |frame | in wire format and store it in
|
||||
* |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr|
|
||||
* length. This function expands |*buf_ptr| as necessary to store
|
||||
* given |frame|.
|
||||
* Packs GOAWAY frame |frame| in wire format and store it in |bufs|.
|
||||
* This function expands |bufs| as necessary to store frame.
|
||||
*
|
||||
* The caller must make sure that nghttp2_bufs_reset(bufs) is called
|
||||
* before calling this function.
|
||||
*
|
||||
* This function returns 0 if it succeeds or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
* NGHTTP2_ERR_FRAME_SIZE_ERROR
|
||||
* The length of the frame is too large.
|
||||
*/
|
||||
ssize_t nghttp2_frame_pack_goaway(uint8_t **buf_ptr, size_t *buflen_ptr,
|
||||
nghttp2_goaway *frame);
|
||||
int nghttp2_frame_pack_goaway(nghttp2_bufs *bufs, nghttp2_goaway *frame);
|
||||
|
||||
/*
|
||||
* Unpacks GOAWAY wire format into |frame|.
|
||||
* Unpacks GOAWAY wire format into |frame|. The |payload| of length
|
||||
* |payloadlen| contains first 8 bytes of payload. The
|
||||
* |var_gift_payload| of length |var_gift_payloadlen| contains
|
||||
* remaining payload and its buffer is gifted to the function and then
|
||||
* |frame|. The |var_gift_payloadlen| must be freed by
|
||||
* nghttp2_frame_goaway_free().
|
||||
*/
|
||||
void nghttp2_frame_unpack_goaway_payload(nghttp2_goaway *frame,
|
||||
const uint8_t *payload,
|
||||
size_t payloadlen,
|
||||
uint8_t *var_gift_payload,
|
||||
size_t var_gift_payloadlen);
|
||||
|
||||
/*
|
||||
* Unpacks GOAWAY wire format into |frame|. This function only exists
|
||||
* for unit test. After allocating buffer for debug data, this
|
||||
* function internally calls nghttp2_frame_unpack_goaway_payload().
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
int nghttp2_frame_unpack_goaway_payload2(nghttp2_goaway *frame,
|
||||
const uint8_t *payload,
|
||||
size_t payloadlen);
|
||||
|
||||
/*
|
||||
* Packs WINDOW_UPDATE frame |frame| in wire frame format and store it
|
||||
* in |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr|
|
||||
* length. This function expands |*buf_ptr| as necessary to store
|
||||
* given |frame|.
|
||||
* in |bufs|.
|
||||
*
|
||||
* This function returns the size of packed frame if it succeeds, or
|
||||
* returns one of the following negative error codes:
|
||||
* The caller must make sure that nghttp2_bufs_reset(bufs) is called
|
||||
* before calling this function.
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
* This function always succeeds and returns 0.
|
||||
*/
|
||||
ssize_t nghttp2_frame_pack_window_update(uint8_t **buf_ptr, size_t *buflen_ptr,
|
||||
int nghttp2_frame_pack_window_update(nghttp2_bufs *bufs,
|
||||
nghttp2_window_update *frame);
|
||||
|
||||
/*
|
||||
@@ -343,20 +384,70 @@ void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame,
|
||||
const uint8_t *payload,
|
||||
size_t payloadlen);
|
||||
|
||||
/*
|
||||
* Packs ALTSVC frame |frame| in wire format and store it in |bufs|.
|
||||
* This function expands |bufs| as necessary to store frame.
|
||||
*
|
||||
* The caller must make sure that nghttp2_bufs_reset(bufs) is called
|
||||
* before calling this function.
|
||||
*
|
||||
* This function returns 0 if it succeeds or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
* NGHTTP2_ERR_FRAME_SIZE_ERROR
|
||||
* The length of the frame is too large.
|
||||
*/
|
||||
int nghttp2_frame_pack_altsvc(nghttp2_bufs *bufs, nghttp2_altsvc *frame);
|
||||
|
||||
|
||||
/*
|
||||
* Unpacks ALTSVC frame byte sequence into |frame|.
|
||||
* The |payload| of length |payloadlen| contains first 8 bytes of
|
||||
* payload. The |var_gift_payload| of length |var_gift_payloadlen|
|
||||
* contains remaining payload and its buffer is gifted to the function
|
||||
* and then |frame|. The |var_gift_payloadlen| must be freed by
|
||||
* nghttp2_frame_altsvc_free().
|
||||
*
|
||||
* This function returns 0 if it succeeds or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_FRAME_SIZE_ERROR
|
||||
* The |var_gift_payload| does not contain required data.
|
||||
*/
|
||||
int nghttp2_frame_unpack_altsvc_payload(nghttp2_altsvc *frame,
|
||||
const uint8_t *payload,
|
||||
size_t payloadlen,
|
||||
uint8_t *var_gift_payload,
|
||||
size_t var_gift_payloadlen);
|
||||
|
||||
/*
|
||||
* Packs BLOCKED frame |frame| in wire format and store it in |bufs|.
|
||||
*
|
||||
* The caller must make sure that nghttp2_bufs_reset(bufs) is called
|
||||
* before calling this function.
|
||||
*
|
||||
* This function always returns 0.
|
||||
*/
|
||||
int nghttp2_frame_pack_blocked(nghttp2_bufs *bufs, nghttp2_blocked *frame);
|
||||
|
||||
/*
|
||||
* Initializes HEADERS frame |frame| with given values. |frame| takes
|
||||
* ownership of |nva|, so caller must not free it. If |stream_id| is
|
||||
* not assigned yet, it must be -1.
|
||||
*/
|
||||
void nghttp2_frame_headers_init(nghttp2_headers *frame,
|
||||
uint8_t flags, int32_t stream_id, int32_t pri,
|
||||
uint8_t flags, int32_t stream_id,
|
||||
nghttp2_headers_category cat,
|
||||
const nghttp2_priority_spec *pri_spec,
|
||||
nghttp2_nv *nva, size_t nvlen);
|
||||
|
||||
void nghttp2_frame_headers_free(nghttp2_headers *frame);
|
||||
|
||||
|
||||
void nghttp2_frame_priority_init(nghttp2_priority *frame, int32_t stream_id,
|
||||
int32_t pri);
|
||||
const nghttp2_priority_spec *pri_spec);
|
||||
|
||||
void nghttp2_frame_priority_free(nghttp2_priority *frame);
|
||||
|
||||
@@ -416,8 +507,35 @@ void nghttp2_frame_window_update_init(nghttp2_window_update *frame,
|
||||
|
||||
void nghttp2_frame_window_update_free(nghttp2_window_update *frame);
|
||||
|
||||
/* protocol_id, host and origin must be allocated to the one chunk of
|
||||
memory region and protocol_id must point to it. We only free
|
||||
protocol_id. This means that |protocol_id| is not NULL even if
|
||||
|protocol_id_len| == 0 and |host_len| + |origin_len| > 0. If
|
||||
|protocol_id_len|, |host_len| and |origin_len| are all zero,
|
||||
|protocol_id| can be NULL. */
|
||||
void nghttp2_frame_altsvc_init(nghttp2_altsvc *frame, int32_t stream_id,
|
||||
uint32_t max_age,
|
||||
uint16_t port,
|
||||
uint8_t *protocol_id,
|
||||
size_t protocol_id_len,
|
||||
uint8_t *host, size_t host_len,
|
||||
uint8_t *origin, size_t origin_len);
|
||||
|
||||
void nghttp2_frame_altsvc_free(nghttp2_altsvc *frame);
|
||||
|
||||
void nghttp2_frame_blocked_init(nghttp2_blocked *frame, int32_t stream_id);
|
||||
|
||||
void nghttp2_frame_blocked_free(nghttp2_blocked *frame);
|
||||
|
||||
void nghttp2_frame_data_init(nghttp2_data *frame, nghttp2_private_data *pdata);
|
||||
|
||||
/*
|
||||
* Returns the number of padding bytes after payload. The total
|
||||
* padding length is given in the |padlen|. The returned value does
|
||||
* not include the PAD_HIGH and PAD_LOW.
|
||||
*/
|
||||
size_t nghttp2_frame_trail_padlen(nghttp2_frame *frame, size_t padlen);
|
||||
|
||||
void nghttp2_frame_private_data_init(nghttp2_private_data *frame,
|
||||
uint8_t flags,
|
||||
int32_t stream_id,
|
||||
@@ -451,9 +569,6 @@ void nghttp2_nv_array_sort(nghttp2_nv *nva, size_t nvlen);
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
* NGHTTP2_ERR_INVALID_ARGUMENT
|
||||
* The length of name or value in |nva| is strictly larger than
|
||||
* NGHTTP2_MAX_HD_VALUE_LENGTH.
|
||||
*/
|
||||
ssize_t nghttp2_nv_array_copy(nghttp2_nv **nva_ptr,
|
||||
const nghttp2_nv *nva, size_t nvlen);
|
||||
@@ -472,12 +587,28 @@ void nghttp2_nv_array_del(nghttp2_nv *nva);
|
||||
|
||||
/*
|
||||
* Checks that the |iv|, which includes |niv| entries, does not have
|
||||
* invalid values. The |flow_control_opt| is current flow control
|
||||
* option value.
|
||||
* invalid values.
|
||||
*
|
||||
* This function returns nonzero if it succeeds, or 0.
|
||||
*/
|
||||
int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv,
|
||||
int32_t flow_control_opt);
|
||||
int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv);
|
||||
|
||||
/*
|
||||
* Sets PAD_HIGH and PAD_LOW fields, flags and adjust frame header
|
||||
* position of each buffers in |bufs|. The padding is given in the
|
||||
* |padlen|. The |hd| is the frame header for the serialized data.
|
||||
* The |type| is used as a frame type when padding requires additional
|
||||
* buffers.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
* NGHTTP2_ERR_FRAME_SIZE_ERROR
|
||||
* The length of the resulting frame is too large.
|
||||
*/
|
||||
int nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd,
|
||||
size_t padlen, nghttp2_frame_type type);
|
||||
|
||||
#endif /* NGHTTP2_FRAME_H */
|
||||
|
||||
1368
lib/nghttp2_hd.c
1368
lib/nghttp2_hd.c
File diff suppressed because it is too large
Load Diff
249
lib/nghttp2_hd.h
249
lib/nghttp2_hd.h
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2013 Tatsuhiro Tsujikawa
|
||||
*
|
||||
@@ -22,8 +22,8 @@
|
||||
* 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_HD_COMP_H
|
||||
#define NGHTTP2_HD_COMP_H
|
||||
#ifndef NGHTTP2_HD_H
|
||||
#define NGHTTP2_HD_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
@@ -32,27 +32,21 @@
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
#include "nghttp2_hd_huffman.h"
|
||||
#include "nghttp2_buffer.h"
|
||||
#include "nghttp2_buf.h"
|
||||
|
||||
#define NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE (1 << 12)
|
||||
#define NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE NGHTTP2_DEFAULT_HEADER_TABLE_SIZE
|
||||
#define NGHTTP2_HD_ENTRY_OVERHEAD 32
|
||||
|
||||
/* The maximum value length of name/value pair. This is not specified
|
||||
by the spec. We just chose the arbitrary size */
|
||||
#define NGHTTP2_HD_MAX_NAME 256
|
||||
#define NGHTTP2_HD_MAX_VALUE 4096
|
||||
#define NGHTTP2_HD_MAX_BUFFER_LENGTH (1 << 15)
|
||||
#define NGHTTP2_HD_MAX_VALUE 8192
|
||||
|
||||
/* Default size of maximum table buffer size for encoder. Even if
|
||||
remote decoder notifies larger buffer size for its decoding,
|
||||
encoder only uses the memory up to this value. */
|
||||
#define NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE (1 << 12)
|
||||
|
||||
typedef enum {
|
||||
NGHTTP2_HD_SIDE_REQUEST = 0,
|
||||
NGHTTP2_HD_SIDE_RESPONSE = 1
|
||||
} nghttp2_hd_side;
|
||||
|
||||
typedef enum {
|
||||
NGHTTP2_HD_ROLE_DEFLATE,
|
||||
NGHTTP2_HD_ROLE_INFLATE
|
||||
@@ -108,6 +102,8 @@ typedef enum {
|
||||
|
||||
typedef enum {
|
||||
NGHTTP2_HD_STATE_OPCODE,
|
||||
NGHTTP2_HD_STATE_CLEAR_REFSET,
|
||||
NGHTTP2_HD_STATE_READ_TABLE_SIZE,
|
||||
NGHTTP2_HD_STATE_READ_INDEX,
|
||||
NGHTTP2_HD_STATE_NEWNAME_CHECK_NAMELEN,
|
||||
NGHTTP2_HD_STATE_NEWNAME_READ_NAMELEN,
|
||||
@@ -116,7 +112,7 @@ typedef enum {
|
||||
NGHTTP2_HD_STATE_CHECK_VALUELEN,
|
||||
NGHTTP2_HD_STATE_READ_VALUELEN,
|
||||
NGHTTP2_HD_STATE_READ_VALUEHUFF,
|
||||
NGHTTP2_HD_STATE_READ_VALUE,
|
||||
NGHTTP2_HD_STATE_READ_VALUE
|
||||
} nghttp2_hd_inflate_state;
|
||||
|
||||
typedef struct {
|
||||
@@ -126,49 +122,34 @@ typedef struct {
|
||||
is the sum of length of name/value in hd_table +
|
||||
NGHTTP2_HD_ENTRY_OVERHEAD bytes overhead per each entry. */
|
||||
size_t hd_table_bufsize;
|
||||
/* The header table size for decoding. If the context is initialized
|
||||
as encoder, this value is advertised by remote endpoint
|
||||
decoder. */
|
||||
/* The effective header table size. */
|
||||
size_t hd_table_bufsize_max;
|
||||
/* The current effective header table size for encoding. This value
|
||||
is always equal to |hd_table_bufsize| on decoder
|
||||
context. |deflate_hd_table_bufsize| <= |hd_table_bufsize| must be
|
||||
hold. */
|
||||
size_t deflate_hd_table_bufsize;
|
||||
/* The maximum effective header table for encoding. Although header
|
||||
table size is bounded by |hd_table_bufsize_max|, the encoder can
|
||||
use smaller buffer by not retaining the header name/values beyond
|
||||
the |deflate_hd_table_bufsize_max| and not referencing those
|
||||
entries. This value is always equal to |hd_table_bufsize_max| on
|
||||
decoder context. */
|
||||
size_t deflate_hd_table_bufsize_max;
|
||||
/* The number of effective entry in |hd_table|. This value is always
|
||||
equal to hd_table.len on decoder side. */
|
||||
size_t deflate_hd_tablelen;
|
||||
/* Role of this context; deflate or infalte */
|
||||
nghttp2_hd_role role;
|
||||
/* NGHTTP2_HD_SIDE_REQUEST for processing request, otherwise
|
||||
response. */
|
||||
nghttp2_hd_side side;
|
||||
/* If inflate/deflate error occurred, this value is set to 1 and
|
||||
further invocation of inflate/deflate will fail with
|
||||
NGHTTP2_ERR_HEADER_COMP. */
|
||||
uint8_t bad;
|
||||
} nghttp2_hd_context;
|
||||
|
||||
typedef struct {
|
||||
struct nghttp2_hd_deflater {
|
||||
nghttp2_hd_context ctx;
|
||||
/* The upper limit of the header table size the deflater accepts. */
|
||||
size_t deflate_hd_table_bufsize_max;
|
||||
/* Set to this nonzero to clear reference set on each deflation each
|
||||
time. */
|
||||
uint8_t no_refset;
|
||||
} nghttp2_hd_deflater;
|
||||
/* If nonzero, send header table size using encoding context update
|
||||
in the next deflate process */
|
||||
uint8_t notify_table_size_change;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
struct nghttp2_hd_inflater {
|
||||
nghttp2_hd_context ctx;
|
||||
/* header name buffer */
|
||||
nghttp2_buffer namebuf;
|
||||
nghttp2_bufs namebufs;
|
||||
/* header value buffer */
|
||||
nghttp2_buffer valuebuf;
|
||||
nghttp2_bufs valuebufs;
|
||||
/* Stores current state of huffman decoding */
|
||||
nghttp2_hd_huff_decode_context huff_decode_ctx;
|
||||
/* Pointer to the nghttp2_hd_entry which is used current header
|
||||
@@ -189,13 +170,19 @@ typedef struct {
|
||||
/* The index of header table to toggle off the entry from reference
|
||||
set at the end of decompression. */
|
||||
size_t end_headers_index;
|
||||
/* The maximum header table size the inflater supports. This is the
|
||||
same value transmitted in SETTINGS_HEADER_TABLE_SIZE */
|
||||
size_t settings_hd_table_bufsize_max;
|
||||
nghttp2_hd_opcode opcode;
|
||||
nghttp2_hd_inflate_state state;
|
||||
/* nonzero if string is huffman encoded */
|
||||
uint8_t huffman_encoded;
|
||||
/* nonzero if deflater requires that current entry is indexed */
|
||||
uint8_t index_required;
|
||||
} nghttp2_hd_inflater;
|
||||
/* nonzero if deflater requires that current entry must not be
|
||||
indexed */
|
||||
uint8_t no_index;
|
||||
};
|
||||
|
||||
/*
|
||||
* Initializes the |ent| members. If NGHTTP2_HD_FLAG_NAME_ALLOC bit
|
||||
@@ -211,8 +198,8 @@ typedef struct {
|
||||
* Out of memory.
|
||||
*/
|
||||
int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t flags,
|
||||
uint8_t *name, uint16_t namelen,
|
||||
uint8_t *value, uint16_t valuelen);
|
||||
uint8_t *name, size_t namelen,
|
||||
uint8_t *value, size_t valuelen);
|
||||
|
||||
void nghttp2_hd_entry_free(nghttp2_hd_entry *ent);
|
||||
|
||||
@@ -230,8 +217,7 @@ void nghttp2_hd_entry_free(nghttp2_hd_entry *ent);
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
int nghttp2_hd_deflate_init(nghttp2_hd_deflater *deflater,
|
||||
nghttp2_hd_side side);
|
||||
int nghttp2_hd_deflate_init(nghttp2_hd_deflater *deflater);
|
||||
|
||||
/*
|
||||
* Initializes |deflater| for deflating name/values pairs.
|
||||
@@ -247,146 +233,63 @@ int nghttp2_hd_deflate_init(nghttp2_hd_deflater *deflater,
|
||||
* Out of memory.
|
||||
*/
|
||||
int nghttp2_hd_deflate_init2(nghttp2_hd_deflater *deflater,
|
||||
nghttp2_hd_side side,
|
||||
size_t deflate_hd_table_bufsize_max);
|
||||
|
||||
/*
|
||||
* Initializes |inflater| for inflating name/values pairs.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater,
|
||||
nghttp2_hd_side side);
|
||||
|
||||
/*
|
||||
* Deallocates any resources allocated for |deflater|.
|
||||
*/
|
||||
void nghttp2_hd_deflate_free(nghttp2_hd_deflater *deflater);
|
||||
|
||||
/*
|
||||
* Deallocates any resources allocated for |inflater|.
|
||||
*/
|
||||
void nghttp2_hd_inflate_free(nghttp2_hd_inflater *inflater);
|
||||
|
||||
/*
|
||||
* Sets the availability of reference set in the |deflater|. If
|
||||
* |no_refset| is nonzero, the deflater will first emit index=0 in the
|
||||
* each invocation of nghttp2_hd_deflate_hd() to clear up reference
|
||||
* set. By default, the deflater uses reference set.
|
||||
*/
|
||||
void nghttp2_hd_deflate_set_no_refset(nghttp2_hd_deflater *deflater,
|
||||
uint8_t no_refset);
|
||||
|
||||
/*
|
||||
* Changes header table size in |context|. This may trigger eviction
|
||||
* in the dynamic table.
|
||||
* Deflates the |nva|, which has the |nvlen| name/value pairs, into
|
||||
* the |bufs|.
|
||||
*
|
||||
* This function can be used for deflater and inflater.
|
||||
* This function expands |bufs| as necessary to store the result. If
|
||||
* buffers is full and the process still requires more space, this
|
||||
* funtion fails and returns NGHTTP2_ERR_HEADER_COMP.
|
||||
*
|
||||
* After this function returns, it is safe to delete the |nva|.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
int nghttp2_hd_change_table_size(nghttp2_hd_context *context,
|
||||
size_t hd_table_bufsize_max);
|
||||
|
||||
/*
|
||||
* Deflates the |nva|, which has the |nvlen| name/value pairs, into
|
||||
* the buffer pointed by the |*buf_ptr| with the length |*buflen_ptr|.
|
||||
* The output starts after |nv_offset| bytes from |*buf_ptr|.
|
||||
*
|
||||
* This function expands |*buf_ptr| as necessary to store the
|
||||
* result. When expansion occurred, memory previously pointed by
|
||||
* |*buf_ptr| may change. |*buf_ptr| and |*buflen_ptr| are updated
|
||||
* accordingly.
|
||||
*
|
||||
* This function copies necessary data into |*buf_ptr|. After this
|
||||
* function returns, it is safe to delete the |nva|.
|
||||
*
|
||||
* TODO: The rest of the code call nghttp2_hd_end_headers() after this
|
||||
* call, but it is just a regacy of the first implementation. Now it
|
||||
* is not required to be called as of now.
|
||||
*
|
||||
* This function returns the number of bytes outputted if it succeeds,
|
||||
* or one of the following negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
* NGHTTP2_ERR_HEADER_COMP
|
||||
* Deflation process has failed.
|
||||
* NGHTTP2_ERR_BUFFER_ERROR
|
||||
* Out of buffer space.
|
||||
*/
|
||||
ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater,
|
||||
uint8_t **buf_ptr, size_t *buflen_ptr,
|
||||
size_t nv_offset,
|
||||
int nghttp2_hd_deflate_hd_bufs(nghttp2_hd_deflater *deflater,
|
||||
nghttp2_bufs *bufs,
|
||||
nghttp2_nv *nva, size_t nvlen);
|
||||
|
||||
typedef enum {
|
||||
NGHTTP2_HD_INFLATE_NONE = 0,
|
||||
NGHTTP2_HD_INFLATE_FINAL = 1,
|
||||
NGHTTP2_HD_INFLATE_EMIT = (1 << 1)
|
||||
} nghttp2_hd_inflate_flag;
|
||||
|
||||
/*
|
||||
* Inflates name/value block stored in |in| with length |inlen|. This
|
||||
* function performs decompression. For each successful emission of
|
||||
* header name/value pair, NGHTTP2_HD_INFLATE_EMIT is set in
|
||||
* |*inflate_flags| and name/value pair is assigned to the |nv_out|
|
||||
* and the function returns. The caller must not free the members of
|
||||
* |nv_out|.
|
||||
* Initializes |inflater| for inflating name/values pairs.
|
||||
*
|
||||
* The |nv_out| may include pointers to the memory region in the
|
||||
* |in|. The caller must retain the |in| while the |nv_out| is used.
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* The application should call this function repeatedly until the
|
||||
* |(*inflate_flags) & NGHTTP2_HD_INFLATE_FINAL| is nonzero and return
|
||||
* value is non-negative. This means the all input values are
|
||||
* processed successfully. Then the application must call
|
||||
* `nghttp2_hd_inflate_end_headers()` to prepare for the next header
|
||||
* block input.
|
||||
*
|
||||
* The caller can feed complete compressed header block. It also can
|
||||
* feed it in several chunks. The caller must set |in_final| to
|
||||
* nonzero if the given input is the last block of the compressed
|
||||
* header.
|
||||
*
|
||||
* This function returns the number of bytes processed if it succeeds,
|
||||
* or one of the following negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* :enum:`NGHTTP2_ERR_NOMEM`
|
||||
* Out of memory.
|
||||
* NGHTTP2_ERR_HEADER_COMP
|
||||
* Inflation process has failed.
|
||||
*/
|
||||
ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater,
|
||||
nghttp2_nv *nv_out, int *inflate_flags,
|
||||
uint8_t *in, size_t inlen, int in_final);
|
||||
int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater);
|
||||
|
||||
/*
|
||||
* Signals the end of decompression for one header block.
|
||||
*
|
||||
* This function returns 0 if it succeeds. Currently this function
|
||||
* always succeeds.
|
||||
* Deallocates any resources allocated for |inflater|.
|
||||
*/
|
||||
int nghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater);
|
||||
void nghttp2_hd_inflate_free(nghttp2_hd_inflater *inflater);
|
||||
|
||||
/* For unittesting purpose */
|
||||
int nghttp2_hd_emit_indname_block(uint8_t **buf_ptr, size_t *buflen_ptr,
|
||||
size_t *offset_ptr, size_t index,
|
||||
const uint8_t *value, size_t valuelen,
|
||||
int inc_indexing,
|
||||
nghttp2_hd_side side);
|
||||
int nghttp2_hd_emit_indname_block(nghttp2_bufs *bufs, size_t index,
|
||||
nghttp2_nv *nv, int inc_indexing);
|
||||
|
||||
/* For unittesting purpose */
|
||||
int nghttp2_hd_emit_newname_block(uint8_t **buf_ptr, size_t *buflen_ptr,
|
||||
size_t *offset_ptr, nghttp2_nv *nv,
|
||||
int inc_indexing,
|
||||
nghttp2_hd_side side);
|
||||
int nghttp2_hd_emit_newname_block(nghttp2_bufs *bufs, nghttp2_nv *nv,
|
||||
int inc_indexing);
|
||||
|
||||
/* For unittesting purpose */
|
||||
int nghttp2_hd_emit_table_size(nghttp2_bufs *bufs, size_t table_size);
|
||||
|
||||
/* For unittesting purpose */
|
||||
nghttp2_hd_entry* nghttp2_hd_table_get(nghttp2_hd_context *context,
|
||||
@@ -395,44 +298,38 @@ nghttp2_hd_entry* nghttp2_hd_table_get(nghttp2_hd_context *context,
|
||||
/* Huffman encoding/decoding functions */
|
||||
|
||||
/*
|
||||
* Counts the required bytes to encode |src| with length |len|. If
|
||||
* |side| is NGHTTP2_HD_SIDE_REQUEST, the request huffman code table
|
||||
* is used. Otherwise, the response code table is used.
|
||||
* Counts the required bytes to encode |src| with length |len|.
|
||||
*
|
||||
* This function returns the number of required bytes to encode given
|
||||
* data, including padding of prefix of terminal symbol code. This
|
||||
* function always succeeds.
|
||||
*/
|
||||
size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len,
|
||||
nghttp2_hd_side side);
|
||||
size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len);
|
||||
|
||||
/*
|
||||
* Encodes the given data |src| with length |srclen| to the given
|
||||
* memory location pointed by |dest|, allocated at lest |destlen|
|
||||
* bytes. The caller is responsible to specify |destlen| at least the
|
||||
* length that nghttp2_hd_huff_encode_count() returns. If |side| is
|
||||
* NGHTTP2_HD_SIDE_REQUEST, the request huffman code table is
|
||||
* used. Otherwise, the response code table is used.
|
||||
* Encodes the given data |src| with length |srclen| to the |bufs|.
|
||||
* This function expands extra buffers in |bufs| if necessary.
|
||||
*
|
||||
* This function returns the number of written bytes, including
|
||||
* padding of prefix of terminal symbol code. This return value is
|
||||
* exactly the same with the return value of
|
||||
* nghttp2_hd_huff_encode_count() if it is given with the same |src|,
|
||||
* |srclen|, and |side|. This function always succeeds.
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
* NGHTTP2_ERR_BUFFER_ERROR
|
||||
* Out of buffer space.
|
||||
*/
|
||||
ssize_t nghttp2_hd_huff_encode(uint8_t *dest, size_t destlen,
|
||||
const uint8_t *src, size_t srclen,
|
||||
nghttp2_hd_side side);
|
||||
int nghttp2_hd_huff_encode(nghttp2_bufs *bufs,
|
||||
const uint8_t *src, size_t srclen);
|
||||
|
||||
void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx,
|
||||
nghttp2_hd_side side);
|
||||
void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx);
|
||||
|
||||
/*
|
||||
* Decodes the given data |src| with length |srclen|. The |ctx| must
|
||||
* be initialized by nghttp2_hd_huff_decode_context_init(). The result
|
||||
* will be added to |dest|. This function may expand |dest| as
|
||||
* needed. The caller is responsible to release the memory of |dest|
|
||||
* by calling nghttp2_buffer_free().
|
||||
* by calling nghttp2_bufs_free() or export its content using
|
||||
* nghttp2_bufs_remove().
|
||||
*
|
||||
* The caller must set the |final| to nonzero if the given input is
|
||||
* the final block.
|
||||
@@ -450,7 +347,7 @@ void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx,
|
||||
* Decoding process has failed.
|
||||
*/
|
||||
ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx,
|
||||
nghttp2_buffer *dest,
|
||||
nghttp2_bufs *bufs,
|
||||
const uint8_t *src, size_t srclen, int final);
|
||||
|
||||
#endif /* NGHTTP2_HD_COMP_H */
|
||||
#endif /* NGHTTP2_HD_H */
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2013 Tatsuhiro Tsujikawa
|
||||
*
|
||||
@@ -30,11 +30,8 @@
|
||||
|
||||
#include "nghttp2_hd.h"
|
||||
|
||||
extern const nghttp2_huff_sym req_huff_sym_table[];
|
||||
extern const nghttp2_huff_decode req_huff_decode_table[][16];
|
||||
|
||||
extern const nghttp2_huff_sym res_huff_sym_table[];
|
||||
extern const nghttp2_huff_decode res_huff_decode_table[][16];
|
||||
extern const nghttp2_huff_sym huff_sym_table[];
|
||||
extern const nghttp2_huff_decode huff_decode_table[][16];
|
||||
|
||||
/*
|
||||
* Encodes huffman code |sym| into |*dest_ptr|, whose least |rembits|
|
||||
@@ -43,40 +40,69 @@ extern const nghttp2_huff_decode res_huff_decode_table[][16];
|
||||
* and points where next output should be placed. The number of
|
||||
* unfilled bits in the pointed location is returned.
|
||||
*/
|
||||
static size_t huff_encode_sym(uint8_t **dest_ptr, size_t rembits,
|
||||
static ssize_t huff_encode_sym(nghttp2_bufs *bufs, size_t *avail_ptr,
|
||||
size_t rembits,
|
||||
const nghttp2_huff_sym *sym)
|
||||
{
|
||||
int rv;
|
||||
size_t nbits = sym->nbits;
|
||||
|
||||
for(;;) {
|
||||
if(rembits > nbits) {
|
||||
**dest_ptr |= sym->code << (rembits - nbits);
|
||||
if(*avail_ptr) {
|
||||
nghttp2_bufs_fast_orb_hold(bufs, sym->code << (rembits - nbits));
|
||||
} else {
|
||||
rv = nghttp2_bufs_orb_hold(bufs, sym->code << (rembits - nbits));
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
*avail_ptr = nghttp2_bufs_cur_avail(bufs);
|
||||
}
|
||||
|
||||
rembits -= nbits;
|
||||
|
||||
break;
|
||||
}
|
||||
**dest_ptr |= sym->code >> (nbits - rembits);
|
||||
++*dest_ptr;
|
||||
|
||||
if(*avail_ptr) {
|
||||
nghttp2_bufs_fast_orb(bufs, sym->code >> (nbits - rembits));
|
||||
--*avail_ptr;
|
||||
} else {
|
||||
rv = nghttp2_bufs_orb(bufs, sym->code >> (nbits - rembits));
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
*avail_ptr = nghttp2_bufs_cur_avail(bufs);
|
||||
}
|
||||
|
||||
nbits -= rembits;
|
||||
rembits = 8;
|
||||
|
||||
if(nbits == 0) {
|
||||
break;
|
||||
}
|
||||
**dest_ptr = 0;
|
||||
|
||||
if(*avail_ptr) {
|
||||
nghttp2_bufs_fast_addb_hold(bufs, 0);
|
||||
} else {
|
||||
rv = nghttp2_bufs_addb_hold(bufs, 0);
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
*avail_ptr = nghttp2_bufs_cur_avail(bufs);
|
||||
}
|
||||
}
|
||||
return rembits;
|
||||
}
|
||||
|
||||
size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len,
|
||||
nghttp2_hd_side side)
|
||||
size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len)
|
||||
{
|
||||
size_t i;
|
||||
size_t nbits = 0;
|
||||
const nghttp2_huff_sym *huff_sym_table;
|
||||
|
||||
if(side == NGHTTP2_HD_SIDE_REQUEST) {
|
||||
huff_sym_table = req_huff_sym_table;
|
||||
} else {
|
||||
huff_sym_table = res_huff_sym_table;
|
||||
}
|
||||
for(i = 0; i < len; ++i) {
|
||||
nbits += huff_sym_table[src[i]].nbits;
|
||||
}
|
||||
@@ -84,68 +110,88 @@ size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len,
|
||||
return (nbits + 7) / 8;
|
||||
}
|
||||
|
||||
ssize_t nghttp2_hd_huff_encode(uint8_t *dest, size_t destlen,
|
||||
const uint8_t *src, size_t srclen,
|
||||
nghttp2_hd_side side)
|
||||
int nghttp2_hd_huff_encode(nghttp2_bufs *bufs,
|
||||
const uint8_t *src, size_t srclen)
|
||||
{
|
||||
int rv;
|
||||
int rembits = 8;
|
||||
uint8_t *dest_first = dest;
|
||||
size_t i;
|
||||
const nghttp2_huff_sym *huff_sym_table;
|
||||
size_t avail;
|
||||
|
||||
avail = nghttp2_bufs_cur_avail(bufs);
|
||||
|
||||
if(side == NGHTTP2_HD_SIDE_REQUEST) {
|
||||
huff_sym_table = req_huff_sym_table;
|
||||
} else {
|
||||
huff_sym_table = res_huff_sym_table;
|
||||
}
|
||||
for(i = 0; i < srclen; ++i) {
|
||||
const nghttp2_huff_sym *sym = &huff_sym_table[src[i]];
|
||||
if(rembits == 8) {
|
||||
*dest = 0;
|
||||
if(avail) {
|
||||
nghttp2_bufs_fast_addb_hold(bufs, 0);
|
||||
} else {
|
||||
rv = nghttp2_bufs_addb_hold(bufs, 0);
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
avail = nghttp2_bufs_cur_avail(bufs);
|
||||
}
|
||||
}
|
||||
rembits = huff_encode_sym(bufs, &avail, rembits, sym);
|
||||
if(rembits < 0) {
|
||||
return rembits;
|
||||
}
|
||||
rembits = huff_encode_sym(&dest, rembits, sym);
|
||||
}
|
||||
/* 256 is special terminal symbol, pad with its prefix */
|
||||
if(rembits < 8) {
|
||||
const nghttp2_huff_sym *sym = &huff_sym_table[256];
|
||||
*dest |= sym->code >> (sym->nbits - rembits);
|
||||
++dest;
|
||||
|
||||
/* Caution we no longer adjust avail here */
|
||||
if(avail) {
|
||||
nghttp2_bufs_fast_orb(bufs, sym->code >> (sym->nbits - rembits));
|
||||
} else {
|
||||
rv = nghttp2_bufs_orb(bufs, sym->code >> (sym->nbits - rembits));
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
return dest - dest_first;
|
||||
}
|
||||
|
||||
void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx,
|
||||
nghttp2_hd_side side)
|
||||
{
|
||||
if(side == NGHTTP2_HD_SIDE_REQUEST) {
|
||||
ctx->huff_decode_table = req_huff_decode_table;
|
||||
} else {
|
||||
ctx->huff_decode_table = res_huff_decode_table;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx)
|
||||
{
|
||||
ctx->state = 0;
|
||||
ctx->accept = 1;
|
||||
}
|
||||
|
||||
ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx,
|
||||
nghttp2_buffer *dest,
|
||||
nghttp2_bufs *bufs,
|
||||
const uint8_t *src, size_t srclen, int final)
|
||||
{
|
||||
size_t i, j;
|
||||
int rv;
|
||||
size_t avail;
|
||||
|
||||
avail = nghttp2_bufs_cur_avail(bufs);
|
||||
|
||||
/* We use the decoding algorithm described in
|
||||
http://graphics.ics.uci.edu/pub/Prefix.pdf */
|
||||
for(i = 0; i < srclen; ++i) {
|
||||
uint8_t in = src[i] >> 4;
|
||||
for(j = 0; j < 2; ++j) {
|
||||
const nghttp2_huff_decode *t = &ctx->huff_decode_table[ctx->state][in];
|
||||
const nghttp2_huff_decode *t = &huff_decode_table[ctx->state][in];
|
||||
if(t->state == -1) {
|
||||
return NGHTTP2_ERR_HEADER_COMP;
|
||||
}
|
||||
if(t->flags & NGHTTP2_HUFF_SYM) {
|
||||
rv = nghttp2_buffer_add_byte(dest, t->sym);
|
||||
if(avail) {
|
||||
nghttp2_bufs_fast_addb(bufs, t->sym);
|
||||
--avail;
|
||||
} else {
|
||||
rv = nghttp2_bufs_addb(bufs, t->sym);
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
avail = nghttp2_bufs_cur_avail(bufs);
|
||||
}
|
||||
}
|
||||
ctx->state = t->state;
|
||||
ctx->accept = (t->flags & NGHTTP2_HUFF_ACCEPTED) != 0;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2013 Tatsuhiro Tsujikawa
|
||||
*
|
||||
@@ -52,7 +52,6 @@ typedef struct {
|
||||
typedef nghttp2_huff_decode huff_decode_table_type[16];
|
||||
|
||||
typedef struct {
|
||||
const huff_decode_table_type *huff_decode_table;
|
||||
/* Current huffman decoding state. We stripped leaf nodes, so the
|
||||
value range is [0..255], inclusive. */
|
||||
uint8_t state;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
*
|
||||
@@ -74,7 +74,13 @@ int nghttp2_reserve_buffer(uint8_t **buf_ptr, size_t *buflen_ptr,
|
||||
|
||||
void* nghttp2_memdup(const void* src, size_t n)
|
||||
{
|
||||
void* dest = malloc(n);
|
||||
void* dest;
|
||||
|
||||
if(n == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dest = malloc(n);
|
||||
if(dest == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
@@ -161,6 +167,8 @@ const char* nghttp2_strerror(int error_code)
|
||||
return "Success";
|
||||
case NGHTTP2_ERR_INVALID_ARGUMENT:
|
||||
return "Invalid argument";
|
||||
case NGHTTP2_ERR_BUFFER_ERROR:
|
||||
return "Out of buffer space";
|
||||
case NGHTTP2_ERR_UNSUPPORTED_VERSION:
|
||||
return "Unsupported SPDY version";
|
||||
case NGHTTP2_ERR_WOULDBLOCK:
|
||||
@@ -195,14 +203,24 @@ const char* nghttp2_strerror(int error_code)
|
||||
return "Invalid header block";
|
||||
case NGHTTP2_ERR_INVALID_STATE:
|
||||
return "Invalid state";
|
||||
case NGHTTP2_ERR_GZIP:
|
||||
return "Gzip error";
|
||||
case NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE:
|
||||
return "The user callback function failed due to the temporal error";
|
||||
case NGHTTP2_ERR_FRAME_SIZE_ERROR:
|
||||
return "The length of the frame is invalid";
|
||||
case NGHTTP2_ERR_HEADER_COMP:
|
||||
return "Header compression/decompression error";
|
||||
case NGHTTP2_ERR_FLOW_CONTROL:
|
||||
return "Flow control error";
|
||||
case NGHTTP2_ERR_INSUFF_BUFSIZE:
|
||||
return "Insufficient buffer size given to function";
|
||||
case NGHTTP2_ERR_PAUSE:
|
||||
return "Callback was paused by the application";
|
||||
case NGHTTP2_ERR_TOO_MANY_INFLIGHT_SETTINGS:
|
||||
return "Too many inflight SETTINGS";
|
||||
case NGHTTP2_ERR_PUSH_DISABLED:
|
||||
return "Server push is disabled by peer";
|
||||
case NGHTTP2_ERR_DATA_EXIST:
|
||||
return "DATA frame already exists";
|
||||
case NGHTTP2_ERR_NOMEM:
|
||||
return "Out of memory";
|
||||
case NGHTTP2_ERR_CALLBACK_FAILURE:
|
||||
@@ -382,3 +400,10 @@ int nghttp2_check_header_value(const uint8_t *value, size_t len)
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t* nghttp2_cpymem(uint8_t *dest, const void *src, size_t len)
|
||||
{
|
||||
memcpy(dest, src, len);
|
||||
|
||||
return dest + len;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
*
|
||||
@@ -78,7 +78,7 @@ int nghttp2_reserve_buffer(uint8_t **buf_ptr, size_t *buflen_ptr,
|
||||
size_t min_length);
|
||||
|
||||
/*
|
||||
* Allocates |n| bytes of memory and copy the meory region pointed by
|
||||
* Allocates |n| bytes of memory and copy the memory region pointed by
|
||||
* |src| with the length |n| bytes into it. Returns the allocated memory.
|
||||
*
|
||||
* This function returns pointer to allocated memory, or one of the
|
||||
@@ -126,4 +126,11 @@ int nghttp2_should_send_window_update(int32_t local_window_size,
|
||||
*/
|
||||
void nghttp2_free(void *ptr);
|
||||
|
||||
/*
|
||||
* Copies the buffer |src| of length |len| to the destination pointed
|
||||
* by the |dest|, assuming that the |dest| is at lest |len| bytes long
|
||||
* . Returns dest + len.
|
||||
*/
|
||||
uint8_t* nghttp2_cpymem(uint8_t *dest, const void *src, size_t len);
|
||||
|
||||
#endif /* NGHTTP2_HELPER_H */
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
*
|
||||
@@ -45,9 +45,8 @@ typedef int (*nghttp2_compar)(const void *lhs, const void *rhs);
|
||||
inclusive. */
|
||||
typedef enum {
|
||||
NGHTTP2_ERR_CREDENTIAL_PENDING = -101,
|
||||
NGHTTP2_ERR_BUFFER_ERROR = -102,
|
||||
NGHTTP2_ERR_IGN_HEADER_BLOCK = -103,
|
||||
NGHTTP2_ERR_IGN_PAYLOAD = -104,
|
||||
NGHTTP2_ERR_IGN_PAYLOAD = -104
|
||||
} nghttp2_internal_error;
|
||||
|
||||
#endif /* NGHTTP2_INT_H */
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
*
|
||||
@@ -31,12 +31,13 @@
|
||||
int nghttp2_map_init(nghttp2_map *map)
|
||||
{
|
||||
map->tablelen = INITIAL_TABLE_LENGTH;
|
||||
map->table = malloc(sizeof(nghttp2_map_entry*) * map->tablelen);
|
||||
map->table = calloc(map->tablelen, sizeof(nghttp2_map_entry*));
|
||||
if(map->table == NULL) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
memset(map->table, 0, sizeof(nghttp2_map_entry*) * map->tablelen);
|
||||
|
||||
map->size = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -85,12 +86,13 @@ void nghttp2_map_entry_init(nghttp2_map_entry *entry, key_type key)
|
||||
entry->next = NULL;
|
||||
}
|
||||
|
||||
/* Same hash function in openjdk HashMap source code. */
|
||||
/* Same hash function in android HashMap source code. */
|
||||
/* The |mod| must be power of 2 */
|
||||
static int32_t hash(int32_t h, int32_t mod)
|
||||
{
|
||||
h ^= (h >> 20) ^ (h >> 12);
|
||||
return (h ^ (h >> 7) ^ (h >> 4)) & (mod - 1);
|
||||
h ^= (h >> 7) ^ (h >> 4);
|
||||
return h & (mod - 1);
|
||||
}
|
||||
|
||||
static int insert(nghttp2_map_entry **table, size_t tablelen,
|
||||
@@ -118,11 +120,11 @@ static int resize(nghttp2_map *map, size_t new_tablelen)
|
||||
{
|
||||
size_t i;
|
||||
nghttp2_map_entry **new_table;
|
||||
new_table = malloc(sizeof(nghttp2_map_entry*) * new_tablelen);
|
||||
new_table = calloc(new_tablelen, sizeof(nghttp2_map_entry*));
|
||||
if(new_table == NULL) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
memset(new_table, 0, sizeof(nghttp2_map_entry*) * new_tablelen);
|
||||
|
||||
for(i = 0; i < map->tablelen; ++i) {
|
||||
nghttp2_map_entry *entry;
|
||||
for(entry = map->table[i]; entry;) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
*
|
||||
@@ -33,16 +33,18 @@ int nghttp2_select_next_protocol(unsigned char **out, unsigned char *outlen,
|
||||
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 && memcmp(&in[i+1], "http/1.1", in[i]) == 0) {
|
||||
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.0" may be
|
||||
/* Go through to the next iteration, because "HTTP/2" may be
|
||||
there */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
*
|
||||
|
||||
62
lib/nghttp2_option.c
Normal file
62
lib/nghttp2_option.c
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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 "nghttp2_option.h"
|
||||
|
||||
int nghttp2_option_new(nghttp2_option **option_ptr)
|
||||
{
|
||||
*option_ptr = calloc(1, sizeof(nghttp2_option));
|
||||
|
||||
if(*option_ptr == NULL) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nghttp2_option_del(nghttp2_option *option)
|
||||
{
|
||||
free(option);
|
||||
}
|
||||
|
||||
void nghttp2_option_set_no_auto_stream_window_update(nghttp2_option *option,
|
||||
int val)
|
||||
{
|
||||
option->opt_set_mask |= NGHTTP2_OPT_NO_AUTO_STREAM_WINDOW_UPDATE;
|
||||
option->no_auto_stream_window_update = val;
|
||||
}
|
||||
|
||||
void nghttp2_option_set_no_auto_connection_window_update
|
||||
(nghttp2_option *option, int val)
|
||||
{
|
||||
option->opt_set_mask |= NGHTTP2_OPT_NO_AUTO_CONNECTION_WINDOW_UPDATE;
|
||||
option->no_auto_connection_window_update = val;
|
||||
}
|
||||
|
||||
void nghttp2_option_set_peer_max_concurrent_streams(nghttp2_option *option,
|
||||
uint32_t val)
|
||||
{
|
||||
option->opt_set_mask |= NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS;
|
||||
option->peer_max_concurrent_streams = val;
|
||||
}
|
||||
95
lib/nghttp2_option.h
Normal file
95
lib/nghttp2_option.h
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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 NGHTTP2_OPTION_H
|
||||
#define NGHTTP2_OPTION_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
/**
|
||||
* Configuration options
|
||||
*/
|
||||
typedef enum {
|
||||
/**
|
||||
* This option prevents the library from sending WINDOW_UPDATE for a
|
||||
* stream automatically. If this option is set to nonzero, the
|
||||
* library won't send WINDOW_UPDATE for a stream and the application
|
||||
* is responsible for sending WINDOW_UPDATE using
|
||||
* `nghttp2_submit_window_update`. By default, this option is set to
|
||||
* zero.
|
||||
*/
|
||||
NGHTTP2_OPT_NO_AUTO_STREAM_WINDOW_UPDATE = 1,
|
||||
/**
|
||||
* This option prevents the library from sending WINDOW_UPDATE for a
|
||||
* connection automatically. If this option is set to nonzero, the
|
||||
* library won't send WINDOW_UPDATE for a connection and the
|
||||
* application is responsible for sending WINDOW_UPDATE with stream
|
||||
* ID 0 using `nghttp2_submit_window_update`. By default, this
|
||||
* option is set to zero.
|
||||
*/
|
||||
NGHTTP2_OPT_NO_AUTO_CONNECTION_WINDOW_UPDATE = 1 << 1,
|
||||
/**
|
||||
* This option sets the SETTINGS_MAX_CONCURRENT_STREAMS value of
|
||||
* remote endpoint as if it is received in SETTINGS frame. Without
|
||||
* specifying this option, before the local endpoint receives
|
||||
* SETTINGS_MAX_CONCURRENT_STREAMS in SETTINGS frame from remote
|
||||
* endpoint, SETTINGS_MAX_CONCURRENT_STREAMS is unlimited. This may
|
||||
* cause problem if local endpoint submits lots of requests
|
||||
* initially and sending them at once to the remote peer may lead to
|
||||
* the rejection of some requests. Specifying this option to the
|
||||
* sensible value, say 100, may avoid this kind of issue. This value
|
||||
* will be overwritten if the local endpoint receives
|
||||
* SETTINGS_MAX_CONCURRENT_STREAMS from the remote endpoint.
|
||||
*/
|
||||
NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS = 1 << 2
|
||||
} nghttp2_option_flag;
|
||||
|
||||
/**
|
||||
* Struct to store option values for nghttp2_session.
|
||||
*/
|
||||
struct nghttp2_option {
|
||||
/**
|
||||
* Bitwise OR of nghttp2_option_flag to determine that which fields
|
||||
* are specified.
|
||||
*/
|
||||
uint32_t opt_set_mask;
|
||||
/**
|
||||
* NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS
|
||||
*/
|
||||
uint32_t peer_max_concurrent_streams;
|
||||
/**
|
||||
* NGHTTP2_OPT_NO_AUTO_STREAM_WINDOW_UPDATE
|
||||
*/
|
||||
uint8_t no_auto_stream_window_update;
|
||||
/**
|
||||
* NGHTTP2_OPT_NO_AUTO_CONNECTION_WINDOW_UPDATE
|
||||
*/
|
||||
uint8_t no_auto_connection_window_update;
|
||||
};
|
||||
|
||||
#endif /* NGHTTP2_OPTION_H */
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
*
|
||||
@@ -62,6 +62,9 @@ void nghttp2_outbound_item_free(nghttp2_outbound_item *item)
|
||||
case NGHTTP2_WINDOW_UPDATE:
|
||||
nghttp2_frame_window_update_free(&frame->window_update);
|
||||
break;
|
||||
case NGHTTP2_ALTSVC:
|
||||
nghttp2_frame_altsvc_free(&frame->altsvc);
|
||||
break;
|
||||
}
|
||||
} else if(item->frame_cat == NGHTTP2_CAT_DATA) {
|
||||
nghttp2_private_data *data_frame;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
*
|
||||
@@ -32,10 +32,12 @@
|
||||
#include <nghttp2/nghttp2.h>
|
||||
#include "nghttp2_frame.h"
|
||||
|
||||
/* Priority for PING */
|
||||
#define NGHTTP2_OB_PRI_PING -10
|
||||
/* Priority for SETTINGS */
|
||||
#define NGHTTP2_OB_PRI_SETTINGS -9
|
||||
/* A bit higher weight for non-DATA frames */
|
||||
#define NGHTTP2_OB_EX_WEIGHT 300
|
||||
/* Higher weight for SETTINGS */
|
||||
#define NGHTTP2_OB_SETTINGS_WEIGHT 301
|
||||
/* Highest weight for PING */
|
||||
#define NGHTTP2_OB_PING_WEIGHT 302
|
||||
|
||||
typedef struct {
|
||||
nghttp2_data_provider *data_prd;
|
||||
@@ -44,13 +46,19 @@ typedef struct {
|
||||
|
||||
typedef struct {
|
||||
int64_t seq;
|
||||
/* Reset count of weight. See comment for last_cycle in
|
||||
nghttp2_session.h */
|
||||
uint64_t cycle;
|
||||
void *frame;
|
||||
void *aux_data;
|
||||
/* Type of |frame|. NGHTTP2_CTRL: nghttp2_frame*, NGHTTP2_DATA:
|
||||
nghttp2_private_data* */
|
||||
nghttp2_frame_category frame_cat;
|
||||
/* The priority used in priority comparion */
|
||||
int32_t pri;
|
||||
/* The priority used in priority comparion. Larger is served
|
||||
ealier. */
|
||||
int32_t weight;
|
||||
/* nonzero if this object is queued. */
|
||||
uint8_t queued;
|
||||
} nghttp2_outbound_item;
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
*
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_compar compar)
|
||||
{
|
||||
pq->capacity = 4096;
|
||||
pq->capacity = 128;
|
||||
pq->q = malloc(pq->capacity * sizeof(void*));
|
||||
if(pq->q == NULL) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
*
|
||||
|
||||
48
lib/nghttp2_priority_spec.c
Normal file
48
lib/nghttp2_priority_spec.c
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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 "nghttp2_priority_spec.h"
|
||||
|
||||
void nghttp2_priority_spec_init(nghttp2_priority_spec *pri_spec,
|
||||
int32_t stream_id, int32_t weight,
|
||||
int exclusive)
|
||||
{
|
||||
pri_spec->stream_id = stream_id;
|
||||
pri_spec->weight = weight;
|
||||
pri_spec->exclusive = exclusive != 0;
|
||||
}
|
||||
|
||||
void nghttp2_priority_spec_default_init(nghttp2_priority_spec *pri_spec)
|
||||
{
|
||||
pri_spec->stream_id = 0;
|
||||
pri_spec->weight = NGHTTP2_DEFAULT_WEIGHT;
|
||||
pri_spec->exclusive = 0;
|
||||
}
|
||||
|
||||
int nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec)
|
||||
{
|
||||
return pri_spec->stream_id == 0 &&
|
||||
pri_spec->weight == NGHTTP2_DEFAULT_WEIGHT &&
|
||||
pri_spec->exclusive == 0;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
* 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
|
||||
@@ -22,18 +22,13 @@
|
||||
* 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_GZIP_H
|
||||
#ifndef NGHTTP2_PRIORITY_SPEC_H
|
||||
#define NGHTTP2_PRIORITY_SPEC_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
#include <zlib.h>
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
struct nghttp2_gzip {
|
||||
z_stream zst;
|
||||
int8_t finished;
|
||||
};
|
||||
|
||||
#endif /* NGHTTP2_GZIP_H */
|
||||
#endif /* NGHTTP2_PRIORITY_SPEC_H */
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
*
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
*
|
||||
@@ -35,9 +35,9 @@
|
||||
#include "nghttp2_frame.h"
|
||||
#include "nghttp2_hd.h"
|
||||
#include "nghttp2_stream.h"
|
||||
#include "nghttp2_buffer.h"
|
||||
#include "nghttp2_outbound_item.h"
|
||||
#include "nghttp2_int.h"
|
||||
#include "nghttp2_buf.h"
|
||||
|
||||
/*
|
||||
* Option flags.
|
||||
@@ -47,32 +47,21 @@ typedef enum {
|
||||
NGHTTP2_OPTMASK_NO_AUTO_CONNECTION_WINDOW_UPDATE = 1 << 1
|
||||
} nghttp2_optmask;
|
||||
|
||||
typedef enum {
|
||||
NGHTTP2_OB_POP_ITEM,
|
||||
NGHTTP2_OB_SEND_DATA
|
||||
} nghttp2_outbound_state;
|
||||
|
||||
typedef struct {
|
||||
nghttp2_outbound_item *item;
|
||||
/* Buffer for outbound frames. Used to pack one frame. The memory
|
||||
pointed by framebuf is initially allocated by
|
||||
nghttp2_session_{client,server}_new() and deallocated by
|
||||
nghttp2_session_del() */
|
||||
uint8_t *framebuf;
|
||||
/* The capacity of framebuf in bytes */
|
||||
size_t framebufmax;
|
||||
/* The length of the frame stored in framebuf */
|
||||
size_t framebuflen;
|
||||
/* The number of bytes has been sent */
|
||||
size_t framebufoff;
|
||||
/* Marks the last position to send. This is used to implement
|
||||
CONTINUATION */
|
||||
size_t framebufmark;
|
||||
nghttp2_bufs framebufs;
|
||||
nghttp2_outbound_state state;
|
||||
} nghttp2_active_outbound_item;
|
||||
|
||||
/* Buffer length for inbound raw byte stream. */
|
||||
/* Buffer length for inbound raw byte stream used in
|
||||
nghttp2_session_recv(). */
|
||||
#define NGHTTP2_INBOUND_BUFFER_LENGTH 16384
|
||||
|
||||
#define NGHTTP2_INITIAL_OUTBOUND_FRAMEBUF_LENGTH (NGHTTP2_DATA_PAYLOAD_LENGTH+8)
|
||||
#define NGHTTP2_INITIAL_INBOUND_FRAMEBUF_LENGTH \
|
||||
NGHTTP2_INITIAL_OUTBOUND_FRAMEBUF_LENGTH
|
||||
#define NGHTTP2_INITIAL_NV_BUFFER_LENGTH 4096
|
||||
|
||||
/* Internal state when receiving incoming frame */
|
||||
typedef enum {
|
||||
/* Receiving frame header */
|
||||
@@ -84,8 +73,12 @@ typedef enum {
|
||||
NGHTTP2_IB_FRAME_SIZE_ERROR,
|
||||
NGHTTP2_IB_READ_SETTINGS,
|
||||
NGHTTP2_IB_READ_GOAWAY_DEBUG,
|
||||
NGHTTP2_IB_READ_ALTSVC,
|
||||
NGHTTP2_IB_EXPECT_CONTINUATION,
|
||||
NGHTTP2_IB_IGN_CONTINUATION,
|
||||
NGHTTP2_IB_READ_PAD_CONTINUATION,
|
||||
NGHTTP2_IB_IGN_PAD_CONTINUATION,
|
||||
NGHTTP2_IB_READ_PAD_DATA,
|
||||
NGHTTP2_IB_READ_DATA,
|
||||
NGHTTP2_IB_IGN_DATA
|
||||
} nghttp2_inbound_state;
|
||||
@@ -96,18 +89,20 @@ typedef struct {
|
||||
about the defined settings ID. If unknown ID is received, it is
|
||||
subject to connection error */
|
||||
nghttp2_settings_entry iv[5];
|
||||
/* buffer pointers to small buffer, raw_sbuf */
|
||||
nghttp2_buf sbuf;
|
||||
/* buffer pointers to large buffer, raw_lbuf */
|
||||
nghttp2_buf lbuf;
|
||||
/* Large buffer, malloced on demand */
|
||||
uint8_t *raw_lbuf;
|
||||
/* The number of entry filled in |iv| */
|
||||
size_t niv;
|
||||
/* How many bytes we still need to receive in the |buf| */
|
||||
size_t left;
|
||||
/* How many bytes we still need to receive for current frame */
|
||||
size_t payloadleft;
|
||||
/* padding length for the current frame */
|
||||
size_t padlen;
|
||||
nghttp2_inbound_state state;
|
||||
/* TODO, remove this. Error code */
|
||||
int error_code;
|
||||
uint8_t buf[8];
|
||||
/* How many bytes have been written to |buf| */
|
||||
uint8_t buflen;
|
||||
uint8_t raw_sbuf[8];
|
||||
} nghttp2_inbound_frame;
|
||||
|
||||
typedef enum {
|
||||
@@ -122,6 +117,7 @@ typedef enum {
|
||||
|
||||
struct nghttp2_session {
|
||||
nghttp2_map /* <nghttp2_stream*> */ streams;
|
||||
nghttp2_stream_roots roots;
|
||||
/* Queue for outbound frames other than stream-creating HEADERS */
|
||||
nghttp2_pq /* <nghttp2_outbound_item*> */ ob_pq;
|
||||
/* Queue for outbound stream-creating HEADERS frame */
|
||||
@@ -134,7 +130,27 @@ struct nghttp2_session {
|
||||
/* Sequence number of outbound frame to maintain the order of
|
||||
enqueue if priority is equal. */
|
||||
int64_t next_seq;
|
||||
/* Reset count of nghttp2_outbound_item's weight. We decrements
|
||||
weight each time DATA is sent to simulate resource sharing. We
|
||||
use priority queue and larger weight has the precedence. If
|
||||
weight is reached to lowest weight, it resets to its initial
|
||||
weight. If this happens, other items which have the lower weight
|
||||
currently but same initial weight cannot send DATA until item
|
||||
having large weight is decreased. To avoid this, we use this
|
||||
cycle variable. Initally, this is set to 1. If weight gets
|
||||
lowest weight, and if item's cycle == last_cycle, we increments
|
||||
last_cycle and assigns it to item's cycle. Otherwise, just
|
||||
assign last_cycle. In priority queue comparator, we first
|
||||
compare items' cycle value. Lower cycle value has the
|
||||
precedence. */
|
||||
uint64_t last_cycle;
|
||||
void *user_data;
|
||||
/* Points to the latest closed stream. NULL if there is no closed
|
||||
stream. Only used when session is initialized as server. */
|
||||
nghttp2_stream *closed_stream_head;
|
||||
/* Points to the oldest closed stream. NULL if there is no closed
|
||||
stream. Only used when session is initialized as server. */
|
||||
nghttp2_stream *closed_stream_tail;
|
||||
/* In-flight SETTINGS values. NULL does not necessarily mean there
|
||||
is no in-flight SETTINGS. */
|
||||
nghttp2_settings_entry *inflight_iv;
|
||||
@@ -147,6 +163,11 @@ struct nghttp2_session {
|
||||
/* The number of incoming streams. This will be capped by
|
||||
local_settings[NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS]. */
|
||||
size_t num_incoming_streams;
|
||||
/* The number of closed streams still kept in |streams| hash. The
|
||||
closed streams can be accessed through single linked list
|
||||
|closed_stream_head|. The current implementation only keeps
|
||||
incoming streams and session is initialized as server. */
|
||||
size_t num_closed_streams;
|
||||
/* The number of bytes allocated for nvbuf */
|
||||
size_t nvbuflen;
|
||||
/* Next Stream ID. Made unsigned int to detect >= (1 << 31). */
|
||||
@@ -184,19 +205,17 @@ struct nghttp2_session {
|
||||
uint32_t local_settings[NGHTTP2_SETTINGS_MAX+1];
|
||||
/* Option flags. This is bitwise-OR of 0 or more of nghttp2_optmask. */
|
||||
uint32_t opt_flags;
|
||||
/* 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;
|
||||
/* Nonzero if the session is server side. */
|
||||
uint8_t server;
|
||||
/* Flags indicating GOAWAY is sent and/or recieved. The flags are
|
||||
composed by bitwise OR-ing nghttp2_goaway_flag. */
|
||||
uint8_t goaway_flags;
|
||||
/* Non-zero indicates connection-level flow control on remote side
|
||||
is in effect. This will be disabled when WINDOW_UPDATE with
|
||||
END_FLOW_CONTROL bit set is received. */
|
||||
uint8_t remote_flow_control;
|
||||
/* Non-zero indicates connection-level flow control on local side is
|
||||
in effect. This will be disabled when WINDOW_UPDATE with
|
||||
END_FLOW_CONTROL bit set is sent. */
|
||||
uint8_t local_flow_control;
|
||||
/* nonzero if blocked was sent and remote_window_size is still 0 or
|
||||
negative */
|
||||
uint8_t blocked_sent;
|
||||
};
|
||||
|
||||
/* Struct used when updating initial window size of each active
|
||||
@@ -240,6 +259,10 @@ int nghttp2_session_add_frame(nghttp2_session *session,
|
||||
* code |error_code|. This is a convenient function built on top of
|
||||
* nghttp2_session_add_frame() to add RST_STREAM easily.
|
||||
*
|
||||
* This function simply returns 0 without adding RST_STREAM frame if
|
||||
* given stream is in NGHTTP2_STREAM_CLOSING state, because multiple
|
||||
* RST_STREAM for a stream is redundant.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
@@ -277,11 +300,14 @@ int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags,
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
* NGHTTP2_ERR_INVALID_ARGUMENT
|
||||
* The |opaque_data_len| is too large.
|
||||
*/
|
||||
int nghttp2_session_add_goaway(nghttp2_session *session,
|
||||
int32_t last_stream_id,
|
||||
nghttp2_error_code error_code,
|
||||
uint8_t *opaque_data, size_t opaque_data_len);
|
||||
const uint8_t *opaque_data,
|
||||
size_t opaque_data_len);
|
||||
|
||||
/*
|
||||
* Adds WINDOW_UPDATE frame with stream ID |stream_id| and
|
||||
@@ -313,9 +339,8 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags,
|
||||
|
||||
/*
|
||||
* Creates new stream in |session| with stream ID |stream_id|,
|
||||
* priority |pri| and flags |flags|. NGHTTP2_FLAG_END_STREAM flag is
|
||||
* set in |flags|, the sender of HEADERS will not send any further
|
||||
* data in this stream. Since this function is called when initial
|
||||
* priority |pri_spec| and flags |flags|. The |flags| is bitwise OR
|
||||
* of nghttp2_stream_flag. Since this function is called when initial
|
||||
* HEADERS is sent or received, these flags are taken from it. The
|
||||
* state of stream is set to |initial_state|. The |stream_user_data|
|
||||
* is a pointer to the arbitrary user supplied data to be associated
|
||||
@@ -326,7 +351,8 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags,
|
||||
*/
|
||||
nghttp2_stream* nghttp2_session_open_stream(nghttp2_session *session,
|
||||
int32_t stream_id,
|
||||
uint8_t flags, int32_t pri,
|
||||
uint8_t flags,
|
||||
nghttp2_priority_spec *pri_spec,
|
||||
nghttp2_stream_state initial_state,
|
||||
void *stream_user_data);
|
||||
|
||||
@@ -335,15 +361,51 @@ nghttp2_stream* nghttp2_session_open_stream(nghttp2_session *session,
|
||||
* is indicated by the |error_code|. When closing the stream,
|
||||
* on_stream_close_callback will be called.
|
||||
*
|
||||
* If the session is initialized as server and |stream| is incoming
|
||||
* stream, stream is just marked closed and this function calls
|
||||
* nghttp2_session_keep_closed_stream() with |stream|. Otherwise,
|
||||
* |stream| will be deleted from memory.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
* NGHTTP2_ERR_INVALID_ARGUMENT
|
||||
* The specified stream does not exist.
|
||||
* NGHTTP2_ERR_CALLBACK_FAILURE
|
||||
* The callback function failed.
|
||||
*/
|
||||
int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id,
|
||||
nghttp2_error_code error_code);
|
||||
|
||||
/*
|
||||
* Deletes |stream| from memory. After this function returns, stream
|
||||
* cannot be accessed.
|
||||
*
|
||||
*/
|
||||
void nghttp2_session_destroy_stream(nghttp2_session *session,
|
||||
nghttp2_stream *stream);
|
||||
|
||||
/*
|
||||
* Tries to keep incoming closed stream |stream|. Due to the
|
||||
* limitation of maximum number of streams in memory, |stream| is not
|
||||
* closed and just deleted from memory (see
|
||||
* nghttp2_session_destroy_stream).
|
||||
*/
|
||||
void nghttp2_session_keep_closed_stream(nghttp2_session *session,
|
||||
nghttp2_stream *stream);
|
||||
|
||||
/*
|
||||
* Deletes closed stream to ensure that number of incoming streams
|
||||
* including active and closed is in the maximum number of allowed
|
||||
* stream. If |offset| is nonzero, it is decreased from the maximum
|
||||
* number of allowed stream when comparing number of active and closed
|
||||
* stream and the maximum number.
|
||||
*/
|
||||
void nghttp2_session_adjust_closed_stream(nghttp2_session *session,
|
||||
ssize_t offset);
|
||||
|
||||
/*
|
||||
* If further receptions and transmissions over the stream |stream_id|
|
||||
* are disallowed, close the stream with error code NGHTTP2_NO_ERROR.
|
||||
@@ -391,6 +453,11 @@ int nghttp2_session_on_push_response_headers_received(nghttp2_session *session,
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
* NGHTTP2_ERR_IGN_HEADER_BLOCK
|
||||
* Frame was rejected and header block must be decoded but
|
||||
* result must be ignored.
|
||||
* NGHTTP2_ERR_CALLBACK_FAILURE
|
||||
* The read_callback failed
|
||||
*/
|
||||
int nghttp2_session_on_headers_received(nghttp2_session *session,
|
||||
nghttp2_frame *frame,
|
||||
@@ -406,6 +473,8 @@ int nghttp2_session_on_headers_received(nghttp2_session *session,
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
* NGHTTP2_ERR_CALLBACK_FAILURE
|
||||
* The read_callback failed
|
||||
*/
|
||||
int nghttp2_session_on_priority_received(nghttp2_session *session,
|
||||
nghttp2_frame *frame);
|
||||
@@ -417,7 +486,10 @@ int nghttp2_session_on_priority_received(nghttp2_session *session,
|
||||
* This function returns 0 if it succeeds, or one the following
|
||||
* negative error codes:
|
||||
*
|
||||
* TBD
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
* NGHTTP2_ERR_CALLBACK_FAILURE
|
||||
* The read_callback failed
|
||||
*/
|
||||
int nghttp2_session_on_rst_stream_received(nghttp2_session *session,
|
||||
nghttp2_frame *frame);
|
||||
@@ -433,8 +505,6 @@ int nghttp2_session_on_rst_stream_received(nghttp2_session *session,
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
* NGHTTP2_ERR_PAUSE
|
||||
* Callback function returns NGHTTP2_ERR_PAUSE
|
||||
* NGHTTP2_ERR_CALLBACK_FAILURE
|
||||
* The read_callback failed
|
||||
*/
|
||||
@@ -451,6 +521,11 @@ int nghttp2_session_on_settings_received(nghttp2_session *session,
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
* NGHTTP2_ERR_IGN_HEADER_BLOCK
|
||||
* Frame was rejected and header block must be decoded but
|
||||
* result must be ignored.
|
||||
* NGHTTP2_ERR_CALLBACK_FAILURE
|
||||
* The read_callback failed
|
||||
*/
|
||||
int nghttp2_session_on_push_promise_received(nghttp2_session *session,
|
||||
nghttp2_frame *frame);
|
||||
@@ -464,6 +539,8 @@ int nghttp2_session_on_push_promise_received(nghttp2_session *session,
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
* NGHTTP2_ERR_CALLBACK_FAILURE
|
||||
* The callback function failed.
|
||||
*/
|
||||
int nghttp2_session_on_ping_received(nghttp2_session *session,
|
||||
nghttp2_frame *frame);
|
||||
@@ -472,7 +549,13 @@ int nghttp2_session_on_ping_received(nghttp2_session *session,
|
||||
* Called when GOAWAY is received, assuming |frame| is properly
|
||||
* initialized.
|
||||
*
|
||||
* This function returns 0 and never fail.
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
* NGHTTP2_ERR_CALLBACK_FAILURE
|
||||
* The callback function failed.
|
||||
*/
|
||||
int nghttp2_session_on_goaway_received(nghttp2_session *session,
|
||||
nghttp2_frame *frame);
|
||||
@@ -486,10 +569,40 @@ int nghttp2_session_on_goaway_received(nghttp2_session *session,
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
* NGHTTP2_ERR_CALLBACK_FAILURE
|
||||
* The callback function failed.
|
||||
*/
|
||||
int nghttp2_session_on_window_update_received(nghttp2_session *session,
|
||||
nghttp2_frame *frame);
|
||||
|
||||
/*
|
||||
* Called when ALTSVC is received, assuming |frame| is properly
|
||||
* initialized.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
* NGHTTP2_ERR_CALLBACK_FAILURE
|
||||
* The callback function failed.
|
||||
*/
|
||||
int nghttp2_session_on_altsvc_received(nghttp2_session *session,
|
||||
nghttp2_frame *frame);
|
||||
|
||||
/*
|
||||
* Called when BLOCKED is received, assuming |frame| is properly
|
||||
* initialized.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_CALLBACK_FAILURE
|
||||
* The callback function failed.
|
||||
*/
|
||||
int nghttp2_session_on_blocked_received(nghttp2_session *session,
|
||||
nghttp2_frame *frame);
|
||||
|
||||
/*
|
||||
* Called when DATA is received, assuming |frame| is properly
|
||||
* initialized.
|
||||
@@ -507,21 +620,31 @@ int nghttp2_session_on_data_received(nghttp2_session *session,
|
||||
|
||||
/*
|
||||
* Returns nghttp2_stream* object whose stream ID is |stream_id|. It
|
||||
* could be NULL if such stream does not exist.
|
||||
* could be NULL if such stream does not exist. This function returns
|
||||
* NULL if stream is marked as closed.
|
||||
*/
|
||||
nghttp2_stream* nghttp2_session_get_stream(nghttp2_session *session,
|
||||
int32_t stream_id);
|
||||
|
||||
/*
|
||||
* This function behaves like nghttp2_session_get_stream(), but it
|
||||
* returns stream object even if it is marked as closed.
|
||||
*/
|
||||
nghttp2_stream* nghttp2_session_get_stream_raw(nghttp2_session *session,
|
||||
int32_t stream_id);
|
||||
|
||||
/*
|
||||
* Packs DATA frame |frame| in wire frame format and stores it in
|
||||
* |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr|
|
||||
* length. This function expands |*buf_ptr| as necessary to store
|
||||
* given |frame|. It packs header in first 8 bytes. Remaining bytes
|
||||
* are the DATA apyload and are filled using |frame->data_prd|. The
|
||||
* length of payload is at most |datamax| bytes.
|
||||
* given |frame|. It packs header in first 8 bytes starting
|
||||
* |*bufoff_ptr| offset. The |*bufoff_ptr| is calculated based on
|
||||
* usage of padding. Remaining bytes are the DATA apyload and are
|
||||
* filled using |frame->data_prd|. The length of payload is at most
|
||||
* |datamax| bytes.
|
||||
*
|
||||
* This function returns the size of packed frame if it succeeds, or
|
||||
* one of the following negative error codes:
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_DEFERRED
|
||||
* The DATA frame is postponed.
|
||||
@@ -532,8 +655,8 @@ nghttp2_stream* nghttp2_session_get_stream(nghttp2_session *session,
|
||||
* NGHTTP2_ERR_CALLBACK_FAILURE
|
||||
* The read_callback failed (session error).
|
||||
*/
|
||||
ssize_t nghttp2_session_pack_data(nghttp2_session *session,
|
||||
uint8_t **buf_ptr, size_t *buflen_ptr,
|
||||
int nghttp2_session_pack_data(nghttp2_session *session,
|
||||
nghttp2_bufs *bufs,
|
||||
size_t datamax,
|
||||
nghttp2_private_data *frame);
|
||||
|
||||
@@ -584,9 +707,17 @@ int nghttp2_session_update_local_settings(nghttp2_session *session,
|
||||
size_t niv);
|
||||
|
||||
/*
|
||||
* Re-prioritize |stream|. The new priority is |pri|.
|
||||
* Re-prioritize |stream|. The new priority specification is
|
||||
* |pri_spec|.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
void nghttp2_session_reprioritize_stream
|
||||
(nghttp2_session *session, nghttp2_stream *stream, int32_t pri);
|
||||
int nghttp2_session_reprioritize_stream
|
||||
(nghttp2_session *session, nghttp2_stream *stream,
|
||||
const nghttp2_priority_spec *pri_spec);
|
||||
|
||||
#endif /* NGHTTP2_SESSION_H */
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
*
|
||||
@@ -25,12 +25,15 @@
|
||||
#include "nghttp2_stream.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "nghttp2_helper.h"
|
||||
|
||||
void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
|
||||
uint8_t flags, int32_t pri,
|
||||
uint8_t flags,
|
||||
nghttp2_stream_state initial_state,
|
||||
uint8_t remote_flow_control,
|
||||
uint8_t local_flow_control,
|
||||
int32_t weight,
|
||||
nghttp2_stream_roots *roots,
|
||||
int32_t remote_initial_window_size,
|
||||
int32_t local_initial_window_size,
|
||||
void *stream_user_data)
|
||||
@@ -38,24 +41,43 @@ void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
|
||||
nghttp2_map_entry_init(&stream->map_entry, stream_id);
|
||||
stream->stream_id = stream_id;
|
||||
stream->flags = flags;
|
||||
stream->pri = pri;
|
||||
stream->state = initial_state;
|
||||
stream->shut_flags = NGHTTP2_SHUT_NONE;
|
||||
stream->stream_user_data = stream_user_data;
|
||||
stream->deferred_data = NULL;
|
||||
stream->deferred_flags = NGHTTP2_DEFERRED_NONE;
|
||||
stream->remote_flow_control = remote_flow_control;
|
||||
stream->local_flow_control = local_flow_control;
|
||||
stream->data_item = NULL;
|
||||
stream->remote_window_size = remote_initial_window_size;
|
||||
stream->local_window_size = local_initial_window_size;
|
||||
stream->recv_window_size = 0;
|
||||
stream->recv_reduction = 0;
|
||||
stream->blocked_sent = 0;
|
||||
|
||||
stream->dep_prev = NULL;
|
||||
stream->dep_next = NULL;
|
||||
stream->sib_prev = NULL;
|
||||
stream->sib_next = NULL;
|
||||
|
||||
stream->closed_next = NULL;
|
||||
|
||||
stream->dpri = NGHTTP2_STREAM_DPRI_NO_DATA;
|
||||
stream->num_substreams = 1;
|
||||
stream->weight = weight;
|
||||
stream->effective_weight = stream->weight;
|
||||
stream->sum_dep_weight = 0;
|
||||
stream->sum_norest_weight = 0;
|
||||
|
||||
stream->roots = roots;
|
||||
stream->root_prev = NULL;
|
||||
stream->root_next = NULL;
|
||||
}
|
||||
|
||||
void nghttp2_stream_free(nghttp2_stream *stream)
|
||||
{
|
||||
nghttp2_outbound_item_free(stream->deferred_data);
|
||||
free(stream->deferred_data);
|
||||
if(stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) {
|
||||
nghttp2_outbound_item_free(stream->data_item);
|
||||
free(stream->data_item);
|
||||
}
|
||||
|
||||
/* We don't free stream->data_item otherwise. */
|
||||
}
|
||||
|
||||
void nghttp2_stream_shutdown(nghttp2_stream *stream, nghttp2_shut_flag flag)
|
||||
@@ -63,19 +85,328 @@ void nghttp2_stream_shutdown(nghttp2_stream *stream, nghttp2_shut_flag flag)
|
||||
stream->shut_flags |= flag;
|
||||
}
|
||||
|
||||
void nghttp2_stream_defer_data(nghttp2_stream *stream,
|
||||
nghttp2_outbound_item *data,
|
||||
uint8_t flags)
|
||||
static int stream_push_data(nghttp2_stream *stream, nghttp2_pq *pq,
|
||||
uint64_t cycle)
|
||||
{
|
||||
assert(stream->deferred_data == NULL);
|
||||
stream->deferred_data = data;
|
||||
stream->deferred_flags = flags;
|
||||
int rv;
|
||||
|
||||
assert(stream->data_item);
|
||||
assert(stream->data_item->queued == 0);
|
||||
|
||||
if(stream->data_item->weight > stream->effective_weight) {
|
||||
stream->data_item->weight = stream->effective_weight;
|
||||
}
|
||||
|
||||
void nghttp2_stream_detach_deferred_data(nghttp2_stream *stream)
|
||||
stream->data_item->cycle = cycle;
|
||||
|
||||
rv = nghttp2_pq_push(pq, stream->data_item);
|
||||
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
stream->data_item->queued = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static nghttp2_stream* stream_first_sib(nghttp2_stream *stream)
|
||||
{
|
||||
stream->deferred_data = NULL;
|
||||
stream->deferred_flags = NGHTTP2_DEFERRED_NONE;
|
||||
for(; stream->sib_prev; stream = stream->sib_prev);
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
static nghttp2_stream* stream_last_sib(nghttp2_stream *stream)
|
||||
{
|
||||
for(; stream->sib_next; stream = stream->sib_next);
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
static nghttp2_stream* stream_update_dep_length(nghttp2_stream *stream,
|
||||
ssize_t delta)
|
||||
{
|
||||
stream->num_substreams += delta;
|
||||
|
||||
stream = stream_first_sib(stream);
|
||||
|
||||
if(stream->dep_prev) {
|
||||
return stream_update_dep_length(stream->dep_prev, delta);
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
int32_t nghttp2_stream_dep_distributed_weight(nghttp2_stream *stream,
|
||||
int32_t weight)
|
||||
{
|
||||
weight = stream->weight * weight / stream->sum_dep_weight;
|
||||
|
||||
return nghttp2_max(1, weight);
|
||||
}
|
||||
|
||||
int32_t nghttp2_stream_dep_distributed_effective_weight
|
||||
(nghttp2_stream *stream, int32_t weight)
|
||||
{
|
||||
if(stream->sum_norest_weight == 0) {
|
||||
return stream->effective_weight;
|
||||
}
|
||||
|
||||
weight = stream->effective_weight * weight / stream->sum_norest_weight;
|
||||
|
||||
return nghttp2_max(1, weight);
|
||||
}
|
||||
|
||||
/* Updates effective_weight of descendant streams in subtree of
|
||||
|stream|. We assume that stream->effective_weight is already set
|
||||
right. */
|
||||
static void stream_update_dep_effective_weight(nghttp2_stream *stream)
|
||||
{
|
||||
nghttp2_stream *si;
|
||||
|
||||
DEBUGF(fprintf(stderr, "stream: update_dep_effective_weight "
|
||||
"stream(%p)=%d, weight=%d, sum_norest_weight=%d\n",
|
||||
stream, stream->stream_id, stream->weight,
|
||||
stream->sum_norest_weight));
|
||||
|
||||
/* stream->sum_norest_weight == 0 means there is no
|
||||
NGHTTP2_STREAM_DPRI_TOP under stream */
|
||||
if(stream->dpri != NGHTTP2_STREAM_DPRI_NO_DATA ||
|
||||
stream->sum_norest_weight == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for(si = stream->dep_next; si; si = si->sib_next) {
|
||||
if(si->dpri != NGHTTP2_STREAM_DPRI_REST) {
|
||||
si->effective_weight = nghttp2_stream_dep_distributed_effective_weight
|
||||
(stream, si->weight);
|
||||
}
|
||||
|
||||
stream_update_dep_effective_weight(si);
|
||||
}
|
||||
}
|
||||
|
||||
static void stream_update_dep_set_rest(nghttp2_stream *stream)
|
||||
{
|
||||
if(stream == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(stream->dpri == NGHTTP2_STREAM_DPRI_REST) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(stream->dpri == NGHTTP2_STREAM_DPRI_TOP) {
|
||||
stream->dpri = NGHTTP2_STREAM_DPRI_REST;
|
||||
|
||||
stream_update_dep_set_rest(stream->sib_next);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
stream_update_dep_set_rest(stream->sib_next);
|
||||
stream_update_dep_set_rest(stream->dep_next);
|
||||
}
|
||||
|
||||
/*
|
||||
* Performs dfs starting |stream|, search stream which can become
|
||||
* NGHTTP2_STREAM_DPRI_TOP and queues its data_item.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
static int stream_update_dep_set_top(nghttp2_stream *stream, nghttp2_pq *pq,
|
||||
uint64_t cycle)
|
||||
{
|
||||
int rv;
|
||||
nghttp2_stream *si;
|
||||
|
||||
if(stream->dpri == NGHTTP2_STREAM_DPRI_TOP) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(stream->dpri == NGHTTP2_STREAM_DPRI_REST) {
|
||||
DEBUGF(fprintf(stderr, "stream: stream=%d data is top\n",
|
||||
stream->stream_id));
|
||||
|
||||
if(!stream->data_item->queued) {
|
||||
rv = stream_push_data(stream, pq, cycle);
|
||||
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
stream->dpri = NGHTTP2_STREAM_DPRI_TOP;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
for(si = stream->dep_next; si; si = si->sib_next) {
|
||||
rv = stream_update_dep_set_top(si, pq, cycle);
|
||||
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Updates stream->sum_norest_weight recursively. We have to gather
|
||||
* effective sum of weight of descendants. If stream->dpri ==
|
||||
* NGHTTP2_STREAM_DPRI_NO_DATA, we have to go deeper and check that
|
||||
* any of its descendants has dpri value of NGHTTP2_STREAM_DPRI_TOP.
|
||||
* If so, we have to add weight of its direct descendants to
|
||||
* stream->sum_norest_weight. To make this work, this function
|
||||
* returns 1 if any of its descendants has dpri value of
|
||||
* NGHTTP2_STREAM_DPRI_TOP, otherwise 0.
|
||||
*/
|
||||
static int stream_update_dep_sum_norest_weight(nghttp2_stream *stream)
|
||||
{
|
||||
nghttp2_stream *si;
|
||||
int rv;
|
||||
|
||||
stream->sum_norest_weight = 0;
|
||||
|
||||
if(stream->dpri == NGHTTP2_STREAM_DPRI_TOP) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(stream->dpri == NGHTTP2_STREAM_DPRI_REST) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rv = 0;
|
||||
|
||||
for(si = stream->dep_next; si; si = si->sib_next) {
|
||||
|
||||
if(stream_update_dep_sum_norest_weight(si)) {
|
||||
rv = 1;
|
||||
stream->sum_norest_weight += si->weight;
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int stream_update_dep_on_attach_data(nghttp2_stream *stream,
|
||||
nghttp2_pq *pq, uint64_t cycle)
|
||||
{
|
||||
int rv;
|
||||
nghttp2_stream *root_stream;
|
||||
|
||||
stream->dpri = NGHTTP2_STREAM_DPRI_REST;
|
||||
|
||||
stream_update_dep_set_rest(stream->dep_next);
|
||||
|
||||
root_stream = nghttp2_stream_get_dep_root(stream);
|
||||
|
||||
DEBUGF(fprintf(stderr, "root=%p, stream=%p\n", root_stream, stream));
|
||||
|
||||
rv = stream_update_dep_set_top(root_stream, pq, cycle);
|
||||
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
stream_update_dep_sum_norest_weight(root_stream);
|
||||
stream_update_dep_effective_weight(root_stream);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stream_update_dep_on_detach_data(nghttp2_stream *stream,
|
||||
nghttp2_pq *pq, uint64_t cycle)
|
||||
{
|
||||
int rv;
|
||||
nghttp2_stream *root_stream;
|
||||
|
||||
stream->dpri = NGHTTP2_STREAM_DPRI_NO_DATA;
|
||||
|
||||
root_stream = nghttp2_stream_get_dep_root(stream);
|
||||
|
||||
rv = stream_update_dep_set_top(root_stream, pq, cycle);
|
||||
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
stream_update_dep_sum_norest_weight(root_stream);
|
||||
stream_update_dep_effective_weight(root_stream);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nghttp2_stream_attach_data(nghttp2_stream *stream,
|
||||
nghttp2_outbound_item *data_item,
|
||||
nghttp2_pq *pq,
|
||||
uint64_t cycle)
|
||||
{
|
||||
assert((stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) == 0);
|
||||
assert(stream->data_item == NULL);
|
||||
|
||||
DEBUGF(fprintf(stderr, "stream: stream=%d attach data=%p\n",
|
||||
stream->stream_id, data_item));
|
||||
|
||||
stream->data_item = data_item;
|
||||
|
||||
return stream_update_dep_on_attach_data(stream, pq, cycle);
|
||||
}
|
||||
|
||||
int nghttp2_stream_detach_data(nghttp2_stream *stream, nghttp2_pq *pq,
|
||||
uint64_t cycle)
|
||||
{
|
||||
DEBUGF(fprintf(stderr, "stream: stream=%d detach data=%p\n",
|
||||
stream->stream_id, stream->data_item));
|
||||
|
||||
stream->data_item = NULL;
|
||||
stream->flags &= ~NGHTTP2_STREAM_FLAG_DEFERRED_ALL;
|
||||
|
||||
return stream_update_dep_on_detach_data(stream, pq, cycle);
|
||||
}
|
||||
|
||||
int nghttp2_stream_defer_data(nghttp2_stream *stream, uint8_t flags,
|
||||
nghttp2_pq *pq, uint64_t cycle)
|
||||
{
|
||||
assert(stream->data_item);
|
||||
|
||||
DEBUGF(fprintf(stderr, "stream: stream=%d defer data=%p cause=%02x\n",
|
||||
stream->stream_id, stream->data_item, flags));
|
||||
|
||||
stream->flags |= flags;
|
||||
|
||||
return stream_update_dep_on_detach_data(stream, pq, cycle);
|
||||
}
|
||||
|
||||
int nghttp2_stream_resume_deferred_data(nghttp2_stream *stream,
|
||||
nghttp2_pq *pq, uint64_t cycle)
|
||||
{
|
||||
assert(stream->data_item);
|
||||
|
||||
DEBUGF(fprintf(stderr, "stream: stream=%d resume data=%p\n",
|
||||
stream->stream_id, stream->data_item));
|
||||
|
||||
stream->flags &= ~NGHTTP2_STREAM_FLAG_DEFERRED_ALL;
|
||||
|
||||
return stream_update_dep_on_attach_data(stream, pq, cycle);
|
||||
}
|
||||
|
||||
int nghttp2_stream_check_deferred_data(nghttp2_stream *stream)
|
||||
{
|
||||
return stream->data_item &&
|
||||
(stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL);
|
||||
}
|
||||
|
||||
int nghttp2_stream_check_deferred_by_flow_control(nghttp2_stream *stream)
|
||||
{
|
||||
return stream->data_item &&
|
||||
(stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL);
|
||||
}
|
||||
|
||||
static int update_initial_window_size
|
||||
@@ -117,3 +448,541 @@ void nghttp2_stream_promise_fulfilled(nghttp2_stream *stream)
|
||||
{
|
||||
stream->state = NGHTTP2_STREAM_OPENED;
|
||||
}
|
||||
|
||||
nghttp2_stream* nghttp2_stream_get_dep_root(nghttp2_stream *stream)
|
||||
{
|
||||
for(;;) {
|
||||
if(stream->sib_prev) {
|
||||
stream = stream->sib_prev;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if(stream->dep_prev) {
|
||||
stream = stream->dep_prev;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
int nghttp2_stream_dep_subtree_find(nghttp2_stream *stream,
|
||||
nghttp2_stream *target)
|
||||
{
|
||||
if(stream == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(stream == target) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(nghttp2_stream_dep_subtree_find(stream->sib_next, target)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return nghttp2_stream_dep_subtree_find(stream->dep_next, target);
|
||||
}
|
||||
|
||||
void nghttp2_stream_dep_insert(nghttp2_stream *dep_stream,
|
||||
nghttp2_stream *stream)
|
||||
{
|
||||
nghttp2_stream *si;
|
||||
nghttp2_stream *root_stream;
|
||||
|
||||
assert(stream->data_item == NULL);
|
||||
|
||||
DEBUGF(fprintf(stderr,
|
||||
"stream: dep_insert dep_stream(%p)=%d, stream(%p)=%d\n",
|
||||
dep_stream, dep_stream->stream_id,
|
||||
stream, stream->stream_id));
|
||||
|
||||
stream->sum_dep_weight = dep_stream->sum_dep_weight;
|
||||
dep_stream->sum_dep_weight = stream->weight;
|
||||
|
||||
if(dep_stream->dep_next) {
|
||||
for(si = dep_stream->dep_next; si; si = si->sib_next) {
|
||||
stream->num_substreams += si->num_substreams;
|
||||
}
|
||||
|
||||
stream->dep_next = dep_stream->dep_next;
|
||||
stream->dep_next->dep_prev = stream;
|
||||
}
|
||||
|
||||
dep_stream->dep_next = stream;
|
||||
stream->dep_prev = dep_stream;
|
||||
|
||||
root_stream = stream_update_dep_length(dep_stream, 1);
|
||||
|
||||
stream_update_dep_sum_norest_weight(root_stream);
|
||||
stream_update_dep_effective_weight(root_stream);
|
||||
|
||||
++stream->roots->num_streams;
|
||||
}
|
||||
|
||||
void nghttp2_stream_dep_add(nghttp2_stream *dep_stream,
|
||||
nghttp2_stream *stream)
|
||||
{
|
||||
nghttp2_stream *last_sib;
|
||||
nghttp2_stream *root_stream;
|
||||
|
||||
assert(stream->data_item == NULL);
|
||||
|
||||
DEBUGF(fprintf(stderr,
|
||||
"stream: dep_add dep_stream(%p)=%d, stream(%p)=%d\n",
|
||||
dep_stream, dep_stream->stream_id,
|
||||
stream, stream->stream_id));
|
||||
|
||||
root_stream = stream_update_dep_length(dep_stream, 1);
|
||||
|
||||
dep_stream->sum_dep_weight += stream->weight;
|
||||
|
||||
if(dep_stream->dep_next == NULL) {
|
||||
dep_stream->dep_next = stream;
|
||||
stream->dep_prev = dep_stream;
|
||||
} else {
|
||||
last_sib = stream_last_sib(dep_stream->dep_next);
|
||||
last_sib->sib_next = stream;
|
||||
stream->sib_prev = last_sib;
|
||||
}
|
||||
|
||||
stream_update_dep_sum_norest_weight(root_stream);
|
||||
stream_update_dep_effective_weight(root_stream);
|
||||
|
||||
++stream->roots->num_streams;
|
||||
}
|
||||
|
||||
void nghttp2_stream_dep_remove(nghttp2_stream *stream)
|
||||
{
|
||||
nghttp2_stream *prev, *next, *dep_next, *dep_prev, *si, *root_stream;
|
||||
int32_t sum_dep_weight_delta;
|
||||
|
||||
root_stream = NULL;
|
||||
|
||||
DEBUGF(fprintf(stderr, "stream: dep_remove stream(%p)=%d\n",
|
||||
stream, stream->stream_id));
|
||||
|
||||
/* Distribute weight of |stream| to direct descendants */
|
||||
sum_dep_weight_delta = -stream->weight;
|
||||
|
||||
for(si = stream->dep_next; si; si = si->sib_next) {
|
||||
si->weight = nghttp2_stream_dep_distributed_weight(stream, si->weight);
|
||||
|
||||
sum_dep_weight_delta += si->weight;
|
||||
}
|
||||
|
||||
prev = stream_first_sib(stream);
|
||||
|
||||
dep_prev = prev->dep_prev;
|
||||
|
||||
if(dep_prev) {
|
||||
root_stream = stream_update_dep_length(dep_prev, -1);
|
||||
|
||||
dep_prev->sum_dep_weight += sum_dep_weight_delta;
|
||||
}
|
||||
|
||||
if(stream->sib_prev) {
|
||||
prev = stream->sib_prev;
|
||||
dep_next = stream->dep_next;
|
||||
|
||||
if(dep_next) {
|
||||
dep_next->dep_prev = NULL;
|
||||
|
||||
prev->sib_next = dep_next;
|
||||
dep_next->sib_prev = prev;
|
||||
} else {
|
||||
next = stream->sib_next;
|
||||
|
||||
prev->sib_next = next;
|
||||
|
||||
if(next) {
|
||||
next->sib_prev = prev;
|
||||
}
|
||||
}
|
||||
} else if(stream->dep_prev) {
|
||||
prev = stream->dep_prev;
|
||||
dep_next = stream->dep_next;
|
||||
|
||||
if(dep_next) {
|
||||
prev->dep_next = dep_next;
|
||||
dep_next->dep_prev = prev;
|
||||
} else if(stream->sib_next) {
|
||||
next = stream->sib_next;
|
||||
|
||||
prev->dep_next = next;
|
||||
next->dep_prev = prev;
|
||||
|
||||
next->sib_prev = NULL;
|
||||
} else {
|
||||
prev->dep_next = NULL;
|
||||
dep_next = NULL;
|
||||
}
|
||||
} else {
|
||||
nghttp2_stream_roots_remove(stream->roots, stream);
|
||||
|
||||
dep_next = NULL;
|
||||
|
||||
/* stream is a root of tree. Removing stream makes its
|
||||
descendants a root of its own subtree. */
|
||||
|
||||
for(si = stream->dep_next; si;) {
|
||||
next = si->sib_next;
|
||||
|
||||
si->dep_prev = NULL;
|
||||
si->sib_prev = NULL;
|
||||
si->sib_next = NULL;
|
||||
|
||||
/* We already distributed weight of |stream| to this. */
|
||||
si->effective_weight = si->weight;
|
||||
|
||||
nghttp2_stream_roots_add(si->roots, si);
|
||||
|
||||
si = next;
|
||||
}
|
||||
}
|
||||
|
||||
if(dep_next && stream->sib_next) {
|
||||
prev = stream_last_sib(dep_next);
|
||||
next = stream->sib_next;
|
||||
|
||||
prev->sib_next = next;
|
||||
next->sib_prev = prev;
|
||||
}
|
||||
|
||||
if(root_stream) {
|
||||
stream_update_dep_sum_norest_weight(root_stream);
|
||||
stream_update_dep_effective_weight(root_stream);
|
||||
}
|
||||
|
||||
stream->num_substreams = 1;
|
||||
stream->sum_dep_weight = 0;
|
||||
|
||||
stream->dep_prev = NULL;
|
||||
stream->dep_next = NULL;
|
||||
stream->sib_prev = NULL;
|
||||
stream->sib_next = NULL;
|
||||
|
||||
--stream->roots->num_streams;
|
||||
}
|
||||
|
||||
int nghttp2_stream_dep_insert_subtree(nghttp2_stream *dep_stream,
|
||||
nghttp2_stream *stream,
|
||||
nghttp2_pq *pq,
|
||||
uint64_t cycle)
|
||||
{
|
||||
nghttp2_stream *last_sib;
|
||||
nghttp2_stream *dep_next;
|
||||
nghttp2_stream *root_stream;
|
||||
size_t delta_substreams;
|
||||
int rv;
|
||||
|
||||
DEBUGF(fprintf(stderr, "stream: dep_insert_subtree dep_stream(%p)=%d "
|
||||
"stream(%p)=%d\n",
|
||||
dep_stream, dep_stream->stream_id,
|
||||
stream, stream->stream_id));
|
||||
|
||||
delta_substreams = stream->num_substreams;
|
||||
|
||||
stream_update_dep_set_rest(stream);
|
||||
|
||||
if(dep_stream->dep_next) {
|
||||
/* dep_stream->num_substreams includes dep_stream itself */
|
||||
stream->num_substreams += dep_stream->num_substreams - 1;
|
||||
|
||||
stream->sum_dep_weight += dep_stream->sum_dep_weight;
|
||||
dep_stream->sum_dep_weight = stream->weight;
|
||||
|
||||
dep_next = dep_stream->dep_next;
|
||||
|
||||
stream_update_dep_set_rest(dep_next);
|
||||
|
||||
dep_stream->dep_next = stream;
|
||||
stream->dep_prev = dep_stream;
|
||||
|
||||
if(stream->dep_next) {
|
||||
last_sib = stream_last_sib(stream->dep_next);
|
||||
|
||||
last_sib->sib_next = dep_next;
|
||||
dep_next->sib_prev = last_sib;
|
||||
|
||||
dep_next->dep_prev = NULL;
|
||||
} else {
|
||||
stream->dep_next = dep_next;
|
||||
dep_next->dep_prev = stream;
|
||||
}
|
||||
} else {
|
||||
dep_stream->dep_next = stream;
|
||||
stream->dep_prev = dep_stream;
|
||||
|
||||
assert(dep_stream->sum_dep_weight == 0);
|
||||
dep_stream->sum_dep_weight = stream->weight;
|
||||
}
|
||||
|
||||
root_stream = stream_update_dep_length(dep_stream, delta_substreams);
|
||||
|
||||
rv = stream_update_dep_set_top(root_stream, pq, cycle);
|
||||
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
stream_update_dep_sum_norest_weight(root_stream);
|
||||
stream_update_dep_effective_weight(root_stream);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nghttp2_stream_dep_add_subtree(nghttp2_stream *dep_stream,
|
||||
nghttp2_stream *stream,
|
||||
nghttp2_pq *pq,
|
||||
uint64_t cycle)
|
||||
{
|
||||
nghttp2_stream *last_sib;
|
||||
nghttp2_stream *root_stream;
|
||||
int rv;
|
||||
|
||||
DEBUGF(fprintf(stderr, "stream: dep_add_subtree dep_stream(%p)=%d "
|
||||
"stream(%p)=%d\n",
|
||||
dep_stream, dep_stream->stream_id,
|
||||
stream, stream->stream_id));
|
||||
|
||||
stream_update_dep_set_rest(stream);
|
||||
|
||||
if(dep_stream->dep_next) {
|
||||
dep_stream->sum_dep_weight += stream->weight;
|
||||
|
||||
last_sib = stream_last_sib(dep_stream->dep_next);
|
||||
|
||||
last_sib->sib_next = stream;
|
||||
stream->sib_prev = last_sib;
|
||||
} else {
|
||||
dep_stream->dep_next = stream;
|
||||
stream->dep_prev = dep_stream;
|
||||
|
||||
assert(dep_stream->sum_dep_weight == 0);
|
||||
dep_stream->sum_dep_weight = stream->weight;
|
||||
}
|
||||
|
||||
root_stream = stream_update_dep_length(dep_stream, stream->num_substreams);
|
||||
|
||||
rv = stream_update_dep_set_top(root_stream, pq, cycle);
|
||||
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
stream_update_dep_sum_norest_weight(root_stream);
|
||||
stream_update_dep_effective_weight(root_stream);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nghttp2_stream_dep_remove_subtree(nghttp2_stream *stream)
|
||||
{
|
||||
nghttp2_stream *prev, *next, *dep_prev, *root_stream;
|
||||
|
||||
DEBUGF(fprintf(stderr, "stream: dep_remove_subtree stream(%p)=%d\n",
|
||||
stream, stream->stream_id));
|
||||
|
||||
if(stream->sib_prev) {
|
||||
prev = stream->sib_prev;
|
||||
|
||||
prev->sib_next = stream->sib_next;
|
||||
if(prev->sib_next) {
|
||||
prev->sib_next->sib_prev = prev;
|
||||
}
|
||||
|
||||
prev = stream_first_sib(prev);
|
||||
|
||||
dep_prev = prev->dep_prev;
|
||||
|
||||
} else if(stream->dep_prev) {
|
||||
dep_prev = stream->dep_prev;
|
||||
next = stream->sib_next;
|
||||
|
||||
dep_prev->dep_next = next;
|
||||
|
||||
if(next) {
|
||||
next->dep_prev = dep_prev;
|
||||
|
||||
next->sib_prev = NULL;
|
||||
}
|
||||
|
||||
} else {
|
||||
nghttp2_stream_roots_remove(stream->roots, stream);
|
||||
|
||||
dep_prev = NULL;
|
||||
}
|
||||
|
||||
if(dep_prev) {
|
||||
dep_prev->sum_dep_weight -= stream->weight;
|
||||
|
||||
root_stream = stream_update_dep_length(dep_prev, -stream->num_substreams);
|
||||
|
||||
stream_update_dep_sum_norest_weight(root_stream);
|
||||
stream_update_dep_effective_weight(root_stream);
|
||||
}
|
||||
|
||||
stream->sib_prev = NULL;
|
||||
stream->sib_next = NULL;
|
||||
stream->dep_prev = NULL;
|
||||
}
|
||||
|
||||
int nghttp2_stream_dep_make_root(nghttp2_stream *stream, nghttp2_pq *pq,
|
||||
uint64_t cycle)
|
||||
{
|
||||
int rv;
|
||||
|
||||
DEBUGF(fprintf(stderr, "stream: dep_make_root stream(%p)=%d\n",
|
||||
stream, stream->stream_id));
|
||||
|
||||
nghttp2_stream_roots_add(stream->roots, stream);
|
||||
|
||||
stream_update_dep_set_rest(stream);
|
||||
|
||||
stream->effective_weight = stream->weight;
|
||||
|
||||
rv = stream_update_dep_set_top(stream, pq, cycle);
|
||||
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
stream_update_dep_sum_norest_weight(stream);
|
||||
stream_update_dep_effective_weight(stream);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nghttp2_stream_dep_all_your_stream_are_belong_to_us
|
||||
(nghttp2_stream *stream, nghttp2_pq *pq, uint64_t cycle)
|
||||
{
|
||||
nghttp2_stream *first, *si;
|
||||
|
||||
DEBUGF(fprintf(stderr, "stream: ALL YOUR STREAM ARE BELONG TO US "
|
||||
"stream(%p)=%d\n",
|
||||
stream, stream->stream_id));
|
||||
|
||||
first = stream->roots->head;
|
||||
|
||||
/* stream must not be include in stream->roots->head list */
|
||||
assert(first != stream);
|
||||
|
||||
if(first) {
|
||||
nghttp2_stream *prev;
|
||||
|
||||
prev = first;
|
||||
|
||||
DEBUGF(fprintf(stderr, "stream: root stream(%p)=%d\n",
|
||||
first, first->stream_id));
|
||||
|
||||
stream->sum_dep_weight += first->weight;
|
||||
stream->num_substreams += first->num_substreams;
|
||||
|
||||
for(si = first->root_next; si; si = si->root_next) {
|
||||
|
||||
assert(si != stream);
|
||||
|
||||
DEBUGF(fprintf(stderr, "stream: root stream(%p)=%d\n",
|
||||
si, si->stream_id));
|
||||
|
||||
stream->sum_dep_weight += si->weight;
|
||||
stream->num_substreams += si->num_substreams;
|
||||
|
||||
si->sib_prev = prev;
|
||||
prev->sib_next = si;
|
||||
|
||||
prev = si;
|
||||
}
|
||||
|
||||
if(stream->dep_next) {
|
||||
nghttp2_stream *last_sib;
|
||||
|
||||
last_sib = stream_last_sib(stream->dep_next);
|
||||
|
||||
last_sib->sib_next = first;
|
||||
first->sib_prev = last_sib;
|
||||
} else {
|
||||
stream->dep_next = first;
|
||||
first->dep_prev = stream;
|
||||
}
|
||||
}
|
||||
|
||||
nghttp2_stream_roots_remove_all(stream->roots);
|
||||
|
||||
return nghttp2_stream_dep_make_root(stream, pq, cycle);
|
||||
}
|
||||
|
||||
int nghttp2_stream_in_dep_tree(nghttp2_stream *stream)
|
||||
{
|
||||
return stream->dep_prev || stream->dep_next ||
|
||||
stream->sib_prev || stream->sib_next ||
|
||||
stream->root_next || stream->root_prev ||
|
||||
stream->roots->head == stream;
|
||||
}
|
||||
|
||||
void nghttp2_stream_roots_init(nghttp2_stream_roots *roots)
|
||||
{
|
||||
roots->head = NULL;
|
||||
roots->num_streams = 0;
|
||||
}
|
||||
|
||||
void nghttp2_stream_roots_free(nghttp2_stream_roots *roots)
|
||||
{}
|
||||
|
||||
void nghttp2_stream_roots_add(nghttp2_stream_roots *roots,
|
||||
nghttp2_stream *stream)
|
||||
{
|
||||
if(roots->head) {
|
||||
stream->root_next = roots->head;
|
||||
roots->head->root_prev = stream;
|
||||
}
|
||||
|
||||
roots->head = stream;
|
||||
}
|
||||
|
||||
void nghttp2_stream_roots_remove(nghttp2_stream_roots *roots,
|
||||
nghttp2_stream *stream)
|
||||
{
|
||||
nghttp2_stream *root_prev, *root_next;
|
||||
|
||||
root_prev = stream->root_prev;
|
||||
root_next = stream->root_next;
|
||||
|
||||
if(root_prev) {
|
||||
root_prev->root_next = root_next;
|
||||
|
||||
if(root_next) {
|
||||
root_next->root_prev = root_prev;
|
||||
}
|
||||
} else {
|
||||
if(root_next) {
|
||||
root_next->root_prev = NULL;
|
||||
}
|
||||
|
||||
roots->head = root_next;
|
||||
}
|
||||
|
||||
stream->root_prev = NULL;
|
||||
stream->root_next = NULL;
|
||||
}
|
||||
|
||||
void nghttp2_stream_roots_remove_all(nghttp2_stream_roots *roots)
|
||||
{
|
||||
nghttp2_stream *si, *next;
|
||||
|
||||
for(si = roots->head; si;) {
|
||||
next = si->root_next;
|
||||
|
||||
si->root_prev = NULL;
|
||||
si->root_next = NULL;
|
||||
|
||||
si = next;
|
||||
}
|
||||
|
||||
roots->head = NULL;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
*
|
||||
@@ -32,27 +32,34 @@
|
||||
#include <nghttp2/nghttp2.h>
|
||||
#include "nghttp2_outbound_item.h"
|
||||
#include "nghttp2_map.h"
|
||||
#include "nghttp2_pq.h"
|
||||
#include "nghttp2_int.h"
|
||||
|
||||
/*
|
||||
* Maximum number of streams in one dependency tree.
|
||||
*/
|
||||
#define NGHTTP2_MAX_DEP_TREE_LENGTH 100
|
||||
|
||||
/*
|
||||
* If local peer is stream initiator:
|
||||
* NGHTTP2_STREAM_OPENING : upon sending SYN_STREAM
|
||||
* NGHTTP2_STREAM_OPENED : upon receiving SYN_REPLY
|
||||
* NGHTTP2_STREAM_OPENING : upon sending request HEADERS
|
||||
* NGHTTP2_STREAM_OPENED : upon receiving response HEADERS
|
||||
* NGHTTP2_STREAM_CLOSING : upon queuing RST_STREAM
|
||||
*
|
||||
* If remote peer is stream initiator:
|
||||
* NGHTTP2_STREAM_OPENING : upon receiving SYN_STREAM
|
||||
* NGHTTP2_STREAM_OPENED : upon sending SYN_REPLY
|
||||
* NGHTTP2_STREAM_OPENING : upon receiving request HEADERS
|
||||
* NGHTTP2_STREAM_OPENED : upon sending response HEADERS
|
||||
* NGHTTP2_STREAM_CLOSING : upon queuing RST_STREAM
|
||||
*/
|
||||
typedef enum {
|
||||
/* Initial state */
|
||||
NGHTTP2_STREAM_INITIAL,
|
||||
/* For stream initiator: SYN_STREAM has been sent, but SYN_REPLY is
|
||||
not received yet. For receiver: SYN_STREAM has been received,
|
||||
but it does not send SYN_REPLY yet. */
|
||||
/* For stream initiator: request HEADERS has been sent, but response
|
||||
HEADERS has not been received yet. For receiver: request HEADERS
|
||||
has been received, but it does not send response HEADERS yet. */
|
||||
NGHTTP2_STREAM_OPENING,
|
||||
/* For stream initiator: SYN_REPLY is received. For receiver:
|
||||
SYN_REPLY is sent. */
|
||||
/* For stream initiator: response HEADERS is received. For receiver:
|
||||
response HEADERS is sent. */
|
||||
NGHTTP2_STREAM_OPENED,
|
||||
/* RST_STREAM is received, but somehow we need to keep stream in
|
||||
memory. */
|
||||
@@ -75,26 +82,68 @@ typedef enum {
|
||||
typedef enum {
|
||||
NGHTTP2_STREAM_FLAG_NONE = 0,
|
||||
/* Indicates that this stream is pushed stream */
|
||||
NGHTTP2_STREAM_FLAG_PUSH = 0x01
|
||||
NGHTTP2_STREAM_FLAG_PUSH = 0x01,
|
||||
/* Indicates that this stream was closed */
|
||||
NGHTTP2_STREAM_FLAG_CLOSED = 0x02,
|
||||
/* Indicates the DATA is deferred due to flow control. */
|
||||
NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL = 0x04,
|
||||
/* Indicates the DATA is deferred by user callback */
|
||||
NGHTTP2_STREAM_FLAG_DEFERRED_USER = 0x08,
|
||||
/* bitwise OR of NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL and
|
||||
NGHTTP2_STREAM_FLAG_DEFERRED_USER. */
|
||||
NGHTTP2_STREAM_FLAG_DEFERRED_ALL = 0x0c
|
||||
|
||||
} nghttp2_stream_flag;
|
||||
|
||||
typedef enum {
|
||||
NGHTTP2_DEFERRED_NONE = 0,
|
||||
/* Indicates the DATA is deferred due to flow control. */
|
||||
NGHTTP2_DEFERRED_FLOW_CONTROL = 0x01
|
||||
} nghttp2_deferred_flag;
|
||||
NGHTTP2_STREAM_DPRI_NONE = 0,
|
||||
NGHTTP2_STREAM_DPRI_NO_DATA = 0x01,
|
||||
NGHTTP2_STREAM_DPRI_TOP = 0x02,
|
||||
NGHTTP2_STREAM_DPRI_REST = 0x04
|
||||
} nghttp2_stream_dpri;
|
||||
|
||||
typedef struct {
|
||||
struct nghttp2_stream_roots;
|
||||
|
||||
typedef struct nghttp2_stream_roots nghttp2_stream_roots;
|
||||
|
||||
struct nghttp2_stream;
|
||||
|
||||
typedef struct nghttp2_stream nghttp2_stream;
|
||||
|
||||
struct nghttp2_stream {
|
||||
/* Intrusive Map */
|
||||
nghttp2_map_entry map_entry;
|
||||
/* pointers to form dependency tree. If multiple streams depend on
|
||||
a stream, only one stream (left most) has non-NULL dep_prev which
|
||||
points to the stream it depends on. The remaining streams are
|
||||
linked using sib_prev and sib_next. The stream which has
|
||||
non-NULL dep_prev always NULL sib_prev. The right most stream
|
||||
has NULL sib_next. If this stream is a root of dependency tree,
|
||||
dep_prev and sib_prev are NULL. */
|
||||
nghttp2_stream *dep_prev, *dep_next;
|
||||
nghttp2_stream *sib_prev, *sib_next;
|
||||
/* pointers to track dependency tree root streams. This is
|
||||
doubly-linked list and first element is pointed by
|
||||
roots->head. */
|
||||
nghttp2_stream *root_prev, *root_next;
|
||||
/* When stream is kept after closure, it may be kept in single
|
||||
linked list pointed by nghttp2_session closed_stream_head.
|
||||
closed_next points to the next stream object if it is the element
|
||||
of the list. */
|
||||
nghttp2_stream *closed_next;
|
||||
/* pointer to roots, which tracks dependency tree roots */
|
||||
nghttp2_stream_roots *roots;
|
||||
/* The arbitrary data provided by user for this stream. */
|
||||
void *stream_user_data;
|
||||
/* Deferred DATA frame */
|
||||
nghttp2_outbound_item *deferred_data;
|
||||
/* DATA frame item */
|
||||
nghttp2_outbound_item *data_item;
|
||||
/* stream ID */
|
||||
int32_t stream_id;
|
||||
/* Use same value in SYN_STREAM frame */
|
||||
int32_t pri;
|
||||
/* categorized priority of this stream. Only stream bearing
|
||||
NGHTTP2_STREAM_DPRI_TOP can send DATA frame. */
|
||||
nghttp2_stream_dpri dpri;
|
||||
/* the number of streams in subtree */
|
||||
size_t num_substreams;
|
||||
/* Current remote window size. This value is computed against the
|
||||
current initial window size of remote endpoint. */
|
||||
int32_t remote_window_size;
|
||||
@@ -109,33 +158,31 @@ typedef struct {
|
||||
NGHTTP2_INITIAL_WINDOW_SIZE and could be increased/decreased by
|
||||
submitting WINDOW_UPDATE. See nghttp2_submit_window_update(). */
|
||||
int32_t local_window_size;
|
||||
/* weight of this stream */
|
||||
int32_t weight;
|
||||
/* effective weight of this stream in belonging dependency tree */
|
||||
int32_t effective_weight;
|
||||
/* sum of weight (not effective_weight) of direct descendants */
|
||||
int32_t sum_dep_weight;
|
||||
/* sum of weight of direct descendants which have at least one
|
||||
descendant with dpri == NGHTTP2_STREAM_DPRI_TOP. We use this
|
||||
value to calculate effective weight. */
|
||||
int32_t sum_norest_weight;
|
||||
nghttp2_stream_state state;
|
||||
/* This is bitwise-OR of 0 or more of nghttp2_stream_flag. */
|
||||
uint8_t flags;
|
||||
/* Bitwise OR of zero or more nghttp2_shut_flag values */
|
||||
uint8_t shut_flags;
|
||||
/* The flags for defered DATA. Bitwise OR of zero or more
|
||||
nghttp2_deferred_flag values */
|
||||
uint8_t deferred_flags;
|
||||
/* Flag to indicate whether the remote side has flow control
|
||||
enabled. If it is enabled, we have to enforces flow control to
|
||||
send data to the other side. This could be disabled when
|
||||
receiving SETTINGS with flow control options off or receiving
|
||||
WINDOW_UPDATE with END_FLOW_CONTROL bit set. */
|
||||
uint8_t remote_flow_control;
|
||||
/* Flag to indicate whether the local side has flow control
|
||||
enabled. If it is enabled, the received data are subject to the
|
||||
flow control. This could be disabled by sending SETTINGS with
|
||||
flow control options off or sending WINDOW_UPDATE with
|
||||
END_FLOW_CONTROL bit set. */
|
||||
uint8_t local_flow_control;
|
||||
} nghttp2_stream;
|
||||
/* nonzero if blocked was sent and remote_window_size is still 0 or
|
||||
negative */
|
||||
uint8_t blocked_sent;
|
||||
};
|
||||
|
||||
void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
|
||||
uint8_t flags, int32_t pri,
|
||||
uint8_t flags,
|
||||
nghttp2_stream_state initial_state,
|
||||
uint8_t remote_flow_control,
|
||||
uint8_t local_flow_control,
|
||||
int32_t weight,
|
||||
nghttp2_stream_roots *roots,
|
||||
int32_t remote_initial_window_size,
|
||||
int32_t local_initial_window_size,
|
||||
void *stream_user_data);
|
||||
@@ -149,19 +196,39 @@ void nghttp2_stream_free(nghttp2_stream *stream);
|
||||
void nghttp2_stream_shutdown(nghttp2_stream *stream, nghttp2_shut_flag flag);
|
||||
|
||||
/*
|
||||
* Defer DATA frame |data|. We won't call this function in the
|
||||
* situation where stream->deferred_data != NULL. If |flags| is
|
||||
* bitwise OR of zero or more nghttp2_deferred_flag values.
|
||||
* Defer DATA frame |stream->data_item|. We won't call this function
|
||||
* in the situation where |stream->data_item| == NULL. If |flags| is
|
||||
* bitwise OR of zero or more of NGHTTP2_STREAM_FLAG_DEFERRED_USER and
|
||||
* NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL. The |flags| indicates
|
||||
* the reason of this action.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
void nghttp2_stream_defer_data(nghttp2_stream *stream,
|
||||
nghttp2_outbound_item *data,
|
||||
uint8_t flags);
|
||||
int nghttp2_stream_defer_data(nghttp2_stream *stream, uint8_t flags,
|
||||
nghttp2_pq *pq, uint64_t cycle);
|
||||
|
||||
/*
|
||||
* Detaches deferred data from this stream. This function does not
|
||||
* free deferred data.
|
||||
* Detaches deferred data in this stream and it is back to active
|
||||
* state. The flags NGHTTP2_STREAM_FLAG_DEFERRED_USER and
|
||||
* NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL are cleared if they are
|
||||
* set.
|
||||
*/
|
||||
void nghttp2_stream_detach_deferred_data(nghttp2_stream *stream);
|
||||
int nghttp2_stream_resume_deferred_data(nghttp2_stream *stream,
|
||||
nghttp2_pq *pq, uint64_t cycle);
|
||||
|
||||
/*
|
||||
* Returns nonzero if data item is deferred by whatever reason.
|
||||
*/
|
||||
int nghttp2_stream_check_deferred_data(nghttp2_stream *stream);
|
||||
|
||||
/*
|
||||
* Returns nonzero if data item is deferred by flow control.
|
||||
*/
|
||||
int nghttp2_stream_check_deferred_by_flow_control(nghttp2_stream *stream);
|
||||
|
||||
/*
|
||||
* Updates the remote window size with the new value
|
||||
@@ -196,4 +263,180 @@ int nghttp2_stream_update_local_initial_window_size
|
||||
*/
|
||||
void nghttp2_stream_promise_fulfilled(nghttp2_stream *stream);
|
||||
|
||||
/*
|
||||
* Returns the stream positioned in root of the dependency tree the
|
||||
* |stream| belongs to.
|
||||
*/
|
||||
nghttp2_stream* nghttp2_stream_get_dep_root(nghttp2_stream *stream);
|
||||
|
||||
/*
|
||||
* Returns nonzero if |target| is found in subtree of |stream|.
|
||||
*/
|
||||
int nghttp2_stream_dep_subtree_find(nghttp2_stream *stream,
|
||||
nghttp2_stream *target);
|
||||
|
||||
/*
|
||||
* Computes distributed weight of a stream of the |weight| under the
|
||||
* |stream| if |stream| is removed from a dependency tree. The result
|
||||
* is computed using stream->weight rather than
|
||||
* stream->effective_weight.
|
||||
*/
|
||||
int32_t nghttp2_stream_dep_distributed_weight(nghttp2_stream *stream,
|
||||
int32_t weight);
|
||||
|
||||
/*
|
||||
* Computes effective weight of a stream of the |weight| under the
|
||||
* |stream|. The result is computed using stream->effective_weight
|
||||
* rather than stream->weight. This function is used to determine
|
||||
* weight in dependency tree.
|
||||
*/
|
||||
int32_t nghttp2_stream_dep_distributed_effective_weight
|
||||
(nghttp2_stream *stream, int32_t weight);
|
||||
|
||||
/*
|
||||
* Makes the |stream| depend on the |dep_stream|. This dependency is
|
||||
* exclusive. All existing direct descendants of |dep_stream| become
|
||||
* the descendants of the |stream|. This function assumes
|
||||
* |stream->data| is NULL and no dpri members are changed in this
|
||||
* dependency tree.
|
||||
*/
|
||||
void nghttp2_stream_dep_insert(nghttp2_stream *dep_stream,
|
||||
nghttp2_stream *stream);
|
||||
|
||||
/*
|
||||
* Makes the |stream| depend on the |dep_stream|. This dependency is
|
||||
* not exclusive. This function assumes |stream->data| is NULL and no
|
||||
* dpri members are changed in this dependency tree.
|
||||
*/
|
||||
void nghttp2_stream_dep_add(nghttp2_stream *dep_stream,
|
||||
nghttp2_stream *stream);
|
||||
|
||||
/*
|
||||
* Removes the |stream| from the current dependency tree. This
|
||||
* function assumes |stream->data| is NULL.
|
||||
*/
|
||||
void nghttp2_stream_dep_remove(nghttp2_stream *stream);
|
||||
|
||||
/*
|
||||
* Attaches |data_item| to |stream|. Updates dpri members in this
|
||||
* dependency tree.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
int nghttp2_stream_attach_data(nghttp2_stream *stream,
|
||||
nghttp2_outbound_item *data_item,
|
||||
nghttp2_pq *pq,
|
||||
uint64_t cycle);
|
||||
|
||||
/*
|
||||
* Detaches |stream->data_item|. Updates dpri members in this
|
||||
* dependency tree. This function does not free |stream->data_item|.
|
||||
* The caller must free it.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
int nghttp2_stream_detach_data(nghttp2_stream *stream, nghttp2_pq *pq,
|
||||
uint64_t cycle);
|
||||
|
||||
|
||||
/*
|
||||
* Makes the |stream| depend on the |dep_stream|. This dependency is
|
||||
* exclusive. Updates dpri members in this dependency tree.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
int nghttp2_stream_dep_insert_subtree(nghttp2_stream *dep_stream,
|
||||
nghttp2_stream *stream,
|
||||
nghttp2_pq *pq,
|
||||
uint64_t cycle);
|
||||
|
||||
/*
|
||||
* Makes the |stream| depend on the |dep_stream|. This dependency is
|
||||
* not exclusive. Updates dpri members in this dependency tree.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
int nghttp2_stream_dep_add_subtree(nghttp2_stream *dep_stream,
|
||||
nghttp2_stream *stream,
|
||||
nghttp2_pq *pq,
|
||||
uint64_t cycle);
|
||||
|
||||
/*
|
||||
* Removes subtree whose root stream is |stream|. Removing subtree
|
||||
* does not change dpri values. The effective_weight of streams in
|
||||
* removed subtree is not updated.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
void nghttp2_stream_dep_remove_subtree(nghttp2_stream *stream);
|
||||
|
||||
/*
|
||||
* Makes the |stream| as root. Updates dpri members in this
|
||||
* dependency tree.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
int nghttp2_stream_dep_make_root(nghttp2_stream *stream, nghttp2_pq *pq,
|
||||
uint64_t cycle);
|
||||
|
||||
/*
|
||||
* Makes the |stream| as root and all existing root streams become
|
||||
* direct children of |stream|.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
int nghttp2_stream_dep_all_your_stream_are_belong_to_us
|
||||
(nghttp2_stream *stream, nghttp2_pq *pq, uint64_t cycle);
|
||||
|
||||
/*
|
||||
* Returns nonzero if |stream| is in any dependency tree.
|
||||
*/
|
||||
int nghttp2_stream_in_dep_tree(nghttp2_stream *stream);
|
||||
|
||||
struct nghttp2_stream_roots {
|
||||
nghttp2_stream *head;
|
||||
|
||||
int32_t num_streams;
|
||||
};
|
||||
|
||||
void nghttp2_stream_roots_init(nghttp2_stream_roots *roots);
|
||||
|
||||
void nghttp2_stream_roots_free(nghttp2_stream_roots *roots);
|
||||
|
||||
void nghttp2_stream_roots_add(nghttp2_stream_roots *roots,
|
||||
nghttp2_stream *stream);
|
||||
|
||||
void nghttp2_stream_roots_remove(nghttp2_stream_roots *roots,
|
||||
nghttp2_stream *stream);
|
||||
|
||||
void nghttp2_stream_roots_remove_all(nghttp2_stream_roots *roots);
|
||||
|
||||
#endif /* NGHTTP2_STREAM */
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa
|
||||
*
|
||||
@@ -30,15 +30,16 @@
|
||||
#include "nghttp2_session.h"
|
||||
#include "nghttp2_frame.h"
|
||||
#include "nghttp2_helper.h"
|
||||
#include "nghttp2_priority_spec.h"
|
||||
|
||||
/* This function takes ownership of |nva_copy|. Regardless of the
|
||||
return value, the caller must not free |nva_copy| after this
|
||||
function returns. */
|
||||
static int nghttp2_submit_headers_shared
|
||||
static int32_t submit_headers_shared
|
||||
(nghttp2_session *session,
|
||||
uint8_t flags,
|
||||
int32_t stream_id,
|
||||
int32_t pri,
|
||||
const nghttp2_priority_spec *pri_spec,
|
||||
nghttp2_nv *nva_copy,
|
||||
size_t nvlen,
|
||||
const nghttp2_data_provider *data_prd,
|
||||
@@ -49,10 +50,8 @@ static int nghttp2_submit_headers_shared
|
||||
nghttp2_frame *frame = NULL;
|
||||
nghttp2_data_provider *data_prd_copy = NULL;
|
||||
nghttp2_headers_aux_data *aux_data = NULL;
|
||||
if(pri < 0) {
|
||||
rv = NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
goto fail;
|
||||
}
|
||||
nghttp2_headers_category hcat;
|
||||
|
||||
if(data_prd != NULL && data_prd->read_callback != NULL) {
|
||||
data_prd_copy = malloc(sizeof(nghttp2_data_provider));
|
||||
if(data_prd_copy == NULL) {
|
||||
@@ -75,19 +74,46 @@ static int nghttp2_submit_headers_shared
|
||||
rv = NGHTTP2_ERR_NOMEM;
|
||||
goto fail;
|
||||
}
|
||||
/* TODO Implement header continuation */
|
||||
flags_copy = (flags & (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_PRIORITY)) |
|
||||
|
||||
flags_copy =
|
||||
(flags & (NGHTTP2_FLAG_END_STREAM |
|
||||
NGHTTP2_FLAG_END_SEGMENT |
|
||||
NGHTTP2_FLAG_PRIORITY)) |
|
||||
NGHTTP2_FLAG_END_HEADERS;
|
||||
|
||||
nghttp2_frame_headers_init(&frame->headers, flags_copy, stream_id, pri,
|
||||
nva_copy, nvlen);
|
||||
if(stream_id == -1) {
|
||||
if(session->next_stream_id > INT32_MAX) {
|
||||
rv = NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
stream_id = session->next_stream_id;
|
||||
session->next_stream_id += 2;
|
||||
|
||||
hcat = NGHTTP2_HCAT_REQUEST;
|
||||
} else {
|
||||
/* More specific categorization will be done later. */
|
||||
hcat = NGHTTP2_HCAT_HEADERS;
|
||||
}
|
||||
|
||||
nghttp2_frame_headers_init(&frame->headers, flags_copy, stream_id,
|
||||
hcat, pri_spec, nva_copy, nvlen);
|
||||
|
||||
|
||||
rv = nghttp2_session_add_frame(session, NGHTTP2_CAT_CTRL, frame,
|
||||
aux_data);
|
||||
|
||||
if(rv != 0) {
|
||||
nghttp2_frame_headers_free(&frame->headers);
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
if(hcat == NGHTTP2_HCAT_REQUEST) {
|
||||
return stream_id;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
/* nghttp2_frame_headers_init() takes ownership of nva_copy. */
|
||||
nghttp2_nv_array_del(nva_copy);
|
||||
@@ -98,11 +124,20 @@ static int nghttp2_submit_headers_shared
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int nghttp2_submit_headers_shared_nva
|
||||
static void adjust_priority_spec_weight(nghttp2_priority_spec *pri_spec)
|
||||
{
|
||||
if(pri_spec->weight < NGHTTP2_MIN_WEIGHT) {
|
||||
pri_spec->weight = NGHTTP2_MIN_WEIGHT;
|
||||
} else if(pri_spec->weight > NGHTTP2_MAX_WEIGHT) {
|
||||
pri_spec->weight = NGHTTP2_MAX_WEIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t submit_headers_shared_nva
|
||||
(nghttp2_session *session,
|
||||
uint8_t flags,
|
||||
int32_t stream_id,
|
||||
int32_t pri,
|
||||
const nghttp2_priority_spec *pri_spec,
|
||||
const nghttp2_nv *nva,
|
||||
size_t nvlen,
|
||||
const nghttp2_data_provider *data_prd,
|
||||
@@ -110,21 +145,40 @@ static int nghttp2_submit_headers_shared_nva
|
||||
{
|
||||
ssize_t rv;
|
||||
nghttp2_nv *nva_copy;
|
||||
nghttp2_priority_spec copy_pri_spec;
|
||||
|
||||
if(pri_spec) {
|
||||
copy_pri_spec = *pri_spec;
|
||||
adjust_priority_spec_weight(©_pri_spec);
|
||||
} else {
|
||||
nghttp2_priority_spec_default_init(©_pri_spec);
|
||||
}
|
||||
|
||||
rv = nghttp2_nv_array_copy(&nva_copy, nva, nvlen);
|
||||
if(rv < 0) {
|
||||
return rv;
|
||||
}
|
||||
return nghttp2_submit_headers_shared(session, flags, stream_id,
|
||||
pri, nva_copy, rv, data_prd,
|
||||
|
||||
return submit_headers_shared(session, flags, stream_id,
|
||||
©_pri_spec, nva_copy, rv, data_prd,
|
||||
stream_user_data);
|
||||
}
|
||||
|
||||
int nghttp2_submit_headers(nghttp2_session *session, uint8_t flags,
|
||||
int32_t stream_id, int32_t pri,
|
||||
int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags,
|
||||
int32_t stream_id,
|
||||
const nghttp2_priority_spec *pri_spec,
|
||||
const nghttp2_nv *nva, size_t nvlen,
|
||||
void *stream_user_data)
|
||||
{
|
||||
return nghttp2_submit_headers_shared_nva(session, flags, stream_id, pri,
|
||||
flags &= NGHTTP2_FLAG_END_STREAM;
|
||||
|
||||
if(pri_spec && !nghttp2_priority_spec_check_default(pri_spec)) {
|
||||
flags |= NGHTTP2_FLAG_PRIORITY;
|
||||
} else {
|
||||
pri_spec = NULL;
|
||||
}
|
||||
|
||||
return submit_headers_shared_nva(session, flags, stream_id, pri_spec,
|
||||
nva, nvlen, NULL, stream_user_data);
|
||||
}
|
||||
|
||||
@@ -136,24 +190,42 @@ int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags,
|
||||
}
|
||||
|
||||
int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags,
|
||||
int32_t stream_id, int32_t pri)
|
||||
int32_t stream_id,
|
||||
const nghttp2_priority_spec *pri_spec)
|
||||
{
|
||||
int r;
|
||||
int rv;
|
||||
nghttp2_frame *frame;
|
||||
if(pri < 0) {
|
||||
nghttp2_priority_spec copy_pri_spec;
|
||||
|
||||
if(pri_spec == NULL) {
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if(stream_id == pri_spec->stream_id) {
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
copy_pri_spec = *pri_spec;
|
||||
|
||||
adjust_priority_spec_weight(©_pri_spec);
|
||||
|
||||
frame = malloc(sizeof(nghttp2_frame));
|
||||
|
||||
if(frame == NULL) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
nghttp2_frame_priority_init(&frame->priority, stream_id, pri);
|
||||
r = nghttp2_session_add_frame(session, NGHTTP2_CAT_CTRL, frame, NULL);
|
||||
if(r != 0) {
|
||||
|
||||
nghttp2_frame_priority_init(&frame->priority, stream_id, ©_pri_spec);
|
||||
|
||||
rv = nghttp2_session_add_frame(session, NGHTTP2_CAT_CTRL, frame, NULL);
|
||||
|
||||
if(rv != 0) {
|
||||
nghttp2_frame_priority_free(&frame->priority);
|
||||
free(frame);
|
||||
return r;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -166,7 +238,7 @@ int nghttp2_submit_rst_stream(nghttp2_session *session, uint8_t flags,
|
||||
|
||||
int nghttp2_submit_goaway(nghttp2_session *session, uint8_t flags,
|
||||
nghttp2_error_code error_code,
|
||||
uint8_t *opaque_data, size_t opaque_data_len)
|
||||
const uint8_t *opaque_data, size_t opaque_data_len)
|
||||
{
|
||||
return nghttp2_session_add_goaway(session, session->last_stream_id,
|
||||
error_code, opaque_data, opaque_data_len);
|
||||
@@ -178,34 +250,70 @@ int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags,
|
||||
return nghttp2_session_add_settings(session, NGHTTP2_FLAG_NONE, iv, niv);
|
||||
}
|
||||
|
||||
int nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags,
|
||||
int32_t nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags,
|
||||
int32_t stream_id,
|
||||
const nghttp2_nv *nva, size_t nvlen)
|
||||
const nghttp2_nv *nva, size_t nvlen,
|
||||
void *promised_stream_user_data)
|
||||
{
|
||||
nghttp2_frame *frame;
|
||||
nghttp2_nv *nva_copy;
|
||||
uint8_t flags_copy;
|
||||
nghttp2_headers_aux_data *aux_data = NULL;
|
||||
int32_t promised_stream_id;
|
||||
int rv;
|
||||
|
||||
if(!session->server) {
|
||||
return NGHTTP2_ERR_PROTO;
|
||||
}
|
||||
|
||||
frame = malloc(sizeof(nghttp2_frame));
|
||||
if(frame == NULL) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
if(promised_stream_user_data) {
|
||||
aux_data = malloc(sizeof(nghttp2_headers_aux_data));
|
||||
if(aux_data == NULL) {
|
||||
free(frame);
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
aux_data->data_prd = NULL;
|
||||
aux_data->stream_user_data = promised_stream_user_data;
|
||||
}
|
||||
rv = nghttp2_nv_array_copy(&nva_copy, nva, nvlen);
|
||||
if(rv < 0) {
|
||||
free(aux_data);
|
||||
free(frame);
|
||||
return rv;
|
||||
}
|
||||
/* TODO Implement header continuation */
|
||||
flags_copy = NGHTTP2_FLAG_END_PUSH_PROMISE;
|
||||
|
||||
flags_copy = NGHTTP2_FLAG_END_HEADERS;
|
||||
|
||||
/* All 32bit signed stream IDs are spent. */
|
||||
if(session->next_stream_id > INT32_MAX) {
|
||||
free(aux_data);
|
||||
free(frame);
|
||||
|
||||
return NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
promised_stream_id = session->next_stream_id;
|
||||
session->next_stream_id += 2;
|
||||
|
||||
nghttp2_frame_push_promise_init(&frame->push_promise, flags_copy,
|
||||
stream_id, -1, nva_copy, nvlen);
|
||||
rv = nghttp2_session_add_frame(session, NGHTTP2_CAT_CTRL, frame, NULL);
|
||||
stream_id, promised_stream_id,
|
||||
nva_copy, nvlen);
|
||||
|
||||
rv = nghttp2_session_add_frame(session, NGHTTP2_CAT_CTRL, frame, aux_data);
|
||||
|
||||
if(rv != 0) {
|
||||
nghttp2_frame_push_promise_free(&frame->push_promise);
|
||||
free(aux_data);
|
||||
free(frame);
|
||||
|
||||
return rv;
|
||||
}
|
||||
return 0;
|
||||
|
||||
return promised_stream_id;
|
||||
}
|
||||
|
||||
int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags,
|
||||
@@ -219,9 +327,6 @@ int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags,
|
||||
}
|
||||
flags = 0;
|
||||
if(stream_id == 0) {
|
||||
if(!session->local_flow_control) {
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
rv = nghttp2_adjust_local_window_size(&session->local_window_size,
|
||||
&session->recv_window_size,
|
||||
&session->recv_reduction,
|
||||
@@ -232,9 +337,6 @@ int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags,
|
||||
} else {
|
||||
stream = nghttp2_session_get_stream(session, stream_id);
|
||||
if(stream) {
|
||||
if(!stream->local_flow_control) {
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
rv = nghttp2_adjust_local_window_size(&stream->local_window_size,
|
||||
&stream->recv_window_size,
|
||||
&stream->recv_reduction,
|
||||
@@ -253,26 +355,114 @@ int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t set_request_flags(int32_t pri,
|
||||
int nghttp2_submit_altsvc(nghttp2_session *session, uint8_t flags,
|
||||
int32_t stream_id,
|
||||
uint32_t max_age, uint16_t port,
|
||||
const uint8_t *protocol_id, size_t protocol_id_len,
|
||||
const uint8_t *host, size_t host_len,
|
||||
const uint8_t *origin, size_t origin_len)
|
||||
{
|
||||
int rv;
|
||||
size_t varlen;
|
||||
uint8_t *var, *varp;
|
||||
nghttp2_frame *frame;
|
||||
uint8_t *copy_protocol_id, *copy_host, *copy_origin;
|
||||
|
||||
if(!session->server) {
|
||||
return NGHTTP2_ERR_PROTO;
|
||||
}
|
||||
|
||||
varlen = protocol_id_len + host_len + origin_len;
|
||||
|
||||
/* 9 = fixed part 8 bytes + HOST_LEN 1 byte */
|
||||
if(varlen + 9 > NGHTTP2_MAX_PAYLOADLEN) {
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if(varlen == 0) {
|
||||
var = NULL;
|
||||
copy_protocol_id = NULL;
|
||||
copy_host = NULL;
|
||||
copy_origin = NULL;
|
||||
} else {
|
||||
var = malloc(varlen);
|
||||
|
||||
if(var == NULL) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
varp = var;
|
||||
|
||||
memcpy(varp, protocol_id, protocol_id_len);
|
||||
|
||||
copy_protocol_id = varp;
|
||||
varp += protocol_id_len;
|
||||
|
||||
memcpy(varp, host, host_len);
|
||||
|
||||
copy_host = varp;
|
||||
varp += host_len;
|
||||
|
||||
memcpy(varp, origin, origin_len);
|
||||
|
||||
copy_origin = varp;
|
||||
}
|
||||
|
||||
frame = malloc(sizeof(nghttp2_frame));
|
||||
|
||||
if(frame == NULL) {
|
||||
free(var);
|
||||
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
nghttp2_frame_altsvc_init(&frame->altsvc, stream_id, max_age, port,
|
||||
copy_protocol_id, protocol_id_len,
|
||||
copy_host, host_len, copy_origin, origin_len);
|
||||
|
||||
rv = nghttp2_session_add_frame(session, NGHTTP2_CAT_CTRL, frame, NULL);
|
||||
|
||||
if(rv != 0) {
|
||||
nghttp2_frame_altsvc_free(&frame->altsvc);
|
||||
free(frame);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t set_request_flags(const nghttp2_priority_spec *pri_spec,
|
||||
const nghttp2_data_provider *data_prd)
|
||||
{
|
||||
uint8_t flags = NGHTTP2_FLAG_NONE;
|
||||
if(data_prd == NULL || data_prd->read_callback == NULL) {
|
||||
flags |= NGHTTP2_FLAG_END_STREAM;
|
||||
}
|
||||
if(pri != NGHTTP2_PRI_DEFAULT) {
|
||||
|
||||
if(pri_spec) {
|
||||
flags |= NGHTTP2_FLAG_PRIORITY;
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
int nghttp2_submit_request(nghttp2_session *session, int32_t pri,
|
||||
int32_t nghttp2_submit_request(nghttp2_session *session,
|
||||
const nghttp2_priority_spec *pri_spec,
|
||||
const nghttp2_nv *nva, size_t nvlen,
|
||||
const nghttp2_data_provider *data_prd,
|
||||
void *stream_user_data)
|
||||
{
|
||||
uint8_t flags = set_request_flags(pri, data_prd);
|
||||
return nghttp2_submit_headers_shared_nva(session, flags, -1, pri, nva, nvlen,
|
||||
uint8_t flags;
|
||||
|
||||
if(pri_spec && nghttp2_priority_spec_check_default(pri_spec)) {
|
||||
pri_spec = NULL;
|
||||
}
|
||||
|
||||
flags = set_request_flags(pri_spec, data_prd);
|
||||
|
||||
return submit_headers_shared_nva(session, flags, -1, pri_spec,
|
||||
nva, nvlen,
|
||||
data_prd, stream_user_data);
|
||||
}
|
||||
|
||||
@@ -291,8 +481,8 @@ int nghttp2_submit_response(nghttp2_session *session,
|
||||
const nghttp2_data_provider *data_prd)
|
||||
{
|
||||
uint8_t flags = set_response_flags(data_prd);
|
||||
return nghttp2_submit_headers_shared_nva(session, flags, stream_id,
|
||||
NGHTTP2_PRI_DEFAULT, nva, nvlen,
|
||||
return submit_headers_shared_nva(session, flags, stream_id,
|
||||
NULL, nva, nvlen,
|
||||
data_prd, NULL);
|
||||
}
|
||||
|
||||
@@ -300,24 +490,23 @@ int nghttp2_submit_data(nghttp2_session *session, uint8_t flags,
|
||||
int32_t stream_id,
|
||||
const nghttp2_data_provider *data_prd)
|
||||
{
|
||||
int r;
|
||||
int rv;
|
||||
nghttp2_private_data *data_frame;
|
||||
uint8_t nflags = 0;
|
||||
uint8_t nflags = flags & (NGHTTP2_FLAG_END_STREAM |
|
||||
NGHTTP2_FLAG_END_SEGMENT);
|
||||
|
||||
data_frame = malloc(sizeof(nghttp2_private_data));
|
||||
if(data_frame == NULL) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
if(flags & NGHTTP2_FLAG_END_STREAM) {
|
||||
nflags |= NGHTTP2_FLAG_END_STREAM;
|
||||
}
|
||||
nghttp2_frame_private_data_init(data_frame, nflags, stream_id, data_prd);
|
||||
r = nghttp2_session_add_frame(session, NGHTTP2_CAT_DATA, data_frame, NULL);
|
||||
if(r != 0) {
|
||||
rv = nghttp2_session_add_frame(session, NGHTTP2_CAT_DATA, data_frame, NULL);
|
||||
if(rv != 0) {
|
||||
nghttp2_frame_private_data_free(data_frame);
|
||||
free(data_frame);
|
||||
return rv;
|
||||
}
|
||||
return r;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t nghttp2_pack_settings_payload(uint8_t *buf,
|
||||
@@ -325,14 +514,13 @@ ssize_t nghttp2_pack_settings_payload(uint8_t *buf,
|
||||
const nghttp2_settings_entry *iv,
|
||||
size_t niv)
|
||||
{
|
||||
/* Assume that current flow_control_option is 0 (which means that
|
||||
flow control is enabled) */
|
||||
if(!nghttp2_iv_check(iv, niv, 0)) {
|
||||
if(!nghttp2_iv_check(iv, niv)) {
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if(buflen < (niv * 8))
|
||||
if(buflen < (niv * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH)) {
|
||||
return NGHTTP2_ERR_INSUFF_BUFSIZE;
|
||||
}
|
||||
|
||||
return nghttp2_frame_pack_settings_payload(buf, iv, niv);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa
|
||||
*
|
||||
|
||||
74
m4/ax_check_compile_flag.m4
Normal file
74
m4/ax_check_compile_flag.m4
Normal file
@@ -0,0 +1,74 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Check whether the given FLAG works with the current language's compiler
|
||||
# or gives an error. (Warnings, however, are ignored)
|
||||
#
|
||||
# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
|
||||
# success/failure.
|
||||
#
|
||||
# If EXTRA-FLAGS is defined, it is added to the current language's default
|
||||
# flags (e.g. CFLAGS) when the check is done. The check is thus made with
|
||||
# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to
|
||||
# force the compiler to issue an error when a bad flag is given.
|
||||
#
|
||||
# INPUT gives an alternative input source to AC_COMPILE_IFELSE.
|
||||
#
|
||||
# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
|
||||
# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
|
||||
# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by the
|
||||
# Free Software Foundation, either version 3 of the License, or (at your
|
||||
# option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
# Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# As a special exception, the respective Autoconf Macro's copyright owner
|
||||
# gives unlimited permission to copy, distribute and modify the configure
|
||||
# scripts that are the output of Autoconf when processing the Macro. You
|
||||
# need not follow the terms of the GNU General Public License when using
|
||||
# or distributing such scripts, even though portions of the text of the
|
||||
# Macro appear in them. The GNU General Public License (GPL) does govern
|
||||
# all other use of the material that constitutes the Autoconf Macro.
|
||||
#
|
||||
# This special exception to the GPL applies to versions of the Autoconf
|
||||
# Macro released by the Autoconf Archive. When you make and distribute a
|
||||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 3
|
||||
|
||||
AC_DEFUN([AX_CHECK_COMPILE_FLAG],
|
||||
[AC_PREREQ(2.59)dnl for _AC_LANG_PREFIX
|
||||
AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl
|
||||
AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [
|
||||
ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS
|
||||
_AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1"
|
||||
AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])],
|
||||
[AS_VAR_SET(CACHEVAR,[yes])],
|
||||
[AS_VAR_SET(CACHEVAR,[no])])
|
||||
_AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags])
|
||||
AS_IF([test x"AS_VAR_GET(CACHEVAR)" = xyes],
|
||||
[m4_default([$2], :)],
|
||||
[m4_default([$3], :)])
|
||||
AS_VAR_POPDEF([CACHEVAR])dnl
|
||||
])dnl AX_CHECK_COMPILE_FLAGS
|
||||
13
makemanpages
Executable file
13
makemanpages
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
help2man --output=doc/nghttp.1 --name="HTTP/2 experimental client" \
|
||||
-N --include doc/nghttp.h2m src/nghttp
|
||||
|
||||
help2man --output=doc/nghttpd.1 --name="HTTP/2 experimental server" \
|
||||
-N --include doc/nghttpd.h2m src/nghttpd
|
||||
|
||||
help2man --output=doc/nghttpx.1 --name="HTTP/2 experimental proxy" \
|
||||
-N --include doc/nghttpx.h2m src/nghttpx
|
||||
|
||||
help2man --output=doc/h2load.1 --name="HTTP/2 benchmarking tool" \
|
||||
-N --include doc/h2load.h2m src/h2load
|
||||
10
mkhufftbl.py
10
mkhufftbl.py
@@ -120,18 +120,18 @@ tables = {}
|
||||
root = Node()
|
||||
|
||||
for line in sys.stdin:
|
||||
m = re.match(r'.*\(\s*(\d+)\) ([|01]+) \[(\d+)\]\s+(\S+).*', line)
|
||||
m = re.match(r'.*\(\s*(\d+)\)\s+([|01]+)\s+(\S+)\s+\[\s*(\d+)\].*', line)
|
||||
if m:
|
||||
#print m.group(1), m.group(2), m.group(3)
|
||||
if len(m.group(4)) > 8:
|
||||
#print m.group(1), m.group(2), m.group(4)
|
||||
if len(m.group(3)) > 8:
|
||||
raise Error('Code is more than 4 bytes long')
|
||||
sym = int(m.group(1))
|
||||
bits = re.sub(r'\|', '', m.group(2))
|
||||
nbits = int(m.group(3))
|
||||
nbits = int(m.group(4))
|
||||
assert(len(bits) == nbits)
|
||||
binpat = to_bin(bits)
|
||||
assert(len(binpat) == (nbits+7)/8)
|
||||
symbol_tbl[sym] = (binpat, nbits, m.group(4))
|
||||
symbol_tbl[sym] = (binpat, nbits, m.group(3))
|
||||
#print "Inserting", sym
|
||||
insert(root, sym, bits)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# nghttp2 - HTTP/2.0 C Library
|
||||
# nghttp2 - HTTP/2 C Library
|
||||
|
||||
# Copyright (c) 2013 Tatsuhiro Tsujikawa
|
||||
|
||||
@@ -21,27 +21,28 @@
|
||||
# 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 = cnghttp2.pxd setup.py
|
||||
# This will avoid that setup.py gets deleted before it is executed in
|
||||
# clean-local in parallel build.
|
||||
.NOTPARALLEL:
|
||||
|
||||
EXTRA_DIST = cnghttp2.pxd nghttp2.pyx
|
||||
|
||||
if ENABLE_PYTHON_BINDINGS
|
||||
|
||||
distclean-local:
|
||||
-rm -f nghttp2.c
|
||||
all-local: nghttp2.c
|
||||
$(PYTHON) setup.py build
|
||||
|
||||
install-exec-local:
|
||||
$(PYTHON) setup.py install --prefix=$(DESTDIR)$(prefix)
|
||||
|
||||
uninstall-local:
|
||||
rm -rf $(DESTDIR)$(libdir)/python*/site-packages/*nghttp2*
|
||||
|
||||
clean-local:
|
||||
$(PYTHON) setup.py clean --all
|
||||
-rm -f $(builddir)/nghttp2.c
|
||||
|
||||
.pyx.c:
|
||||
$(CYTHON) -o $@ $<
|
||||
|
||||
pyexec_LTLIBRARIES = nghttp2.la
|
||||
nghttp2_la_SOURCES = nghttp2.pyx
|
||||
nghttp2_la_CPPFLAGS = \
|
||||
$(PYTHON_CPPFLAGS) \
|
||||
-fno-strict-aliasing \
|
||||
-I$(top_srcdir)/lib/includes \
|
||||
-I$(top_builddir)/lib/includes \
|
||||
-I$(top_srcdir)/lib
|
||||
nghttp2_la_LDFLAGS = \
|
||||
$(PYTHON_LDFLAGS) \
|
||||
-avoid-version -module
|
||||
nghttp2_la_LIBADD = $(top_builddir)/lib/libnghttp2.la
|
||||
|
||||
endif # ENABLE_PYTHON_BINDINGS
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# nghttp2 - HTTP/2.0 C Library
|
||||
# nghttp2 - HTTP/2 C Library
|
||||
|
||||
# Copyright (c) 2013 Tatsuhiro Tsujikawa
|
||||
|
||||
@@ -24,14 +24,244 @@ from libc.stdint cimport uint8_t, uint16_t, uint32_t, int32_t
|
||||
|
||||
cdef extern from 'nghttp2/nghttp2.h':
|
||||
|
||||
const char NGHTTP2_PROTO_VERSION_ID[]
|
||||
const char NGHTTP2_CLIENT_CONNECTION_PREFACE[]
|
||||
const size_t NGHTTP2_INITIAL_WINDOW_SIZE
|
||||
const size_t NGHTTP2_DEFAULT_HEADER_TABLE_SIZE
|
||||
|
||||
ctypedef struct nghttp2_session:
|
||||
pass
|
||||
|
||||
ctypedef enum nghttp2_error:
|
||||
NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE
|
||||
|
||||
ctypedef enum nghttp2_flag:
|
||||
NGHTTP2_FLAG_NONE
|
||||
NGHTTP2_FLAG_END_STREAM
|
||||
NGHTTP2_FLAG_ACK
|
||||
|
||||
ctypedef enum nghttp2_error_code:
|
||||
NGHTTP2_NO_ERROR
|
||||
NGHTTP2_PROTOCOL_ERROR
|
||||
NGHTTP2_INTERNAL_ERROR
|
||||
NGHTTP2_SETTINGS_TIMEOUT
|
||||
|
||||
ctypedef enum nghttp2_frame_type:
|
||||
NGHTTP2_DATA
|
||||
NGHTTP2_HEADERS
|
||||
NGHTTP2_RST_STREAM
|
||||
NGHTTP2_SETTINGS
|
||||
NGHTTP2_PUSH_PROMISE
|
||||
NGHTTP2_GOAWAY
|
||||
|
||||
ctypedef enum nghttp2_nv_flag:
|
||||
NGHTTP2_NV_FLAG_NONE
|
||||
NGHTTP2_NV_FLAG_NO_INDEX
|
||||
|
||||
ctypedef struct nghttp2_nv:
|
||||
uint8_t *name
|
||||
uint8_t *value
|
||||
uint16_t namelen
|
||||
uint16_t valuelen
|
||||
uint8_t flags
|
||||
|
||||
ctypedef enum nghttp2_settings_id:
|
||||
SETTINGS_HEADER_TABLE_SIZE
|
||||
NGHTTP2_SETTINGS_HEADER_TABLE_SIZE
|
||||
NGHTTP2_SETTINGS_ENABLE_PUSH
|
||||
NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS
|
||||
NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE
|
||||
|
||||
ctypedef struct nghttp2_settings_entry:
|
||||
int32_t settings_id
|
||||
uint32_t value
|
||||
|
||||
ctypedef struct nghttp2_frame_hd:
|
||||
size_t length
|
||||
int32_t stream_id
|
||||
uint8_t type
|
||||
uint8_t flags
|
||||
|
||||
ctypedef struct nghttp2_data:
|
||||
nghttp2_frame_hd hd
|
||||
size_t padlen
|
||||
|
||||
ctypedef enum nghttp2_headers_category:
|
||||
NGHTTP2_HCAT_REQUEST
|
||||
NGHTTP2_HCAT_RESPONSE
|
||||
NGHTTP2_HCAT_PUSH_RESPONSE
|
||||
NGHTTP2_HCAT_HEADERS
|
||||
|
||||
ctypedef struct nghttp2_headers:
|
||||
nghttp2_frame_hd hd
|
||||
size_t padlen
|
||||
nghttp2_nv *nva
|
||||
size_t nvlen
|
||||
nghttp2_headers_category cat
|
||||
int32_t pri
|
||||
|
||||
ctypedef struct nghttp2_rst_stream:
|
||||
nghttp2_frame_hd hd
|
||||
nghttp2_error_code error_code
|
||||
|
||||
|
||||
ctypedef struct nghttp2_push_promise:
|
||||
nghttp2_frame_hd hd
|
||||
nghttp2_nv *nva
|
||||
size_t nvlen
|
||||
int32_t promised_stream_id
|
||||
|
||||
ctypedef struct nghttp2_goaway:
|
||||
nghttp2_frame_hd hd
|
||||
int32_t last_stream_id
|
||||
nghttp2_error_code error_code
|
||||
uint8_t *opaque_data
|
||||
size_t opaque_data_len
|
||||
|
||||
ctypedef union nghttp2_frame:
|
||||
nghttp2_frame_hd hd
|
||||
nghttp2_data data
|
||||
nghttp2_headers headers
|
||||
nghttp2_rst_stream rst_stream
|
||||
nghttp2_push_promise push_promise
|
||||
nghttp2_goaway goaway
|
||||
|
||||
ctypedef ssize_t (*nghttp2_send_callback)\
|
||||
(nghttp2_session *session, const uint8_t *data, size_t length,
|
||||
int flags, void *user_data)
|
||||
|
||||
ctypedef int (*nghttp2_on_frame_recv_callback)\
|
||||
(nghttp2_session *session, const nghttp2_frame *frame, void *user_data)
|
||||
|
||||
ctypedef int (*nghttp2_on_data_chunk_recv_callback)\
|
||||
(nghttp2_session *session, uint8_t flags, int32_t stream_id,
|
||||
const uint8_t *data, size_t length, void *user_data)
|
||||
|
||||
ctypedef int (*nghttp2_before_frame_send_callback)\
|
||||
(nghttp2_session *session, const nghttp2_frame *frame, void *user_data)
|
||||
|
||||
ctypedef int (*nghttp2_on_stream_close_callback)\
|
||||
(nghttp2_session *session, int32_t stream_id,
|
||||
nghttp2_error_code error_code, void *user_data)
|
||||
|
||||
ctypedef int (*nghttp2_on_begin_headers_callback)\
|
||||
(nghttp2_session *session, const nghttp2_frame *frame, void *user_data)
|
||||
|
||||
ctypedef int (*nghttp2_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)
|
||||
|
||||
ctypedef int (*nghttp2_on_frame_send_callback)\
|
||||
(nghttp2_session *session, const nghttp2_frame *frame, void *user_data)
|
||||
|
||||
ctypedef int (*nghttp2_on_frame_not_send_callback)\
|
||||
(nghttp2_session *session, const nghttp2_frame *frame,
|
||||
int lib_error_code, void *user_data)
|
||||
|
||||
ctypedef struct nghttp2_session_callbacks:
|
||||
nghttp2_send_callback send_callback
|
||||
nghttp2_on_frame_recv_callback on_frame_recv_callback
|
||||
nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback
|
||||
nghttp2_before_frame_send_callback before_frame_send_callback
|
||||
nghttp2_on_frame_send_callback on_frame_send_callback
|
||||
nghttp2_on_frame_not_send_callback on_frame_not_send_callback
|
||||
nghttp2_on_stream_close_callback on_stream_close_callback
|
||||
nghttp2_on_begin_headers_callback on_begin_headers_callback
|
||||
nghttp2_on_header_callback on_header_callback
|
||||
|
||||
int nghttp2_session_client_new(nghttp2_session **session_ptr,
|
||||
const nghttp2_session_callbacks *callbacks,
|
||||
void *user_data)
|
||||
|
||||
int nghttp2_session_server_new(nghttp2_session **session_ptr,
|
||||
const nghttp2_session_callbacks *callbacks,
|
||||
void *user_data)
|
||||
|
||||
void nghttp2_session_del(nghttp2_session *session)
|
||||
|
||||
|
||||
ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
|
||||
const uint8_t *data, size_t datalen)
|
||||
|
||||
ssize_t nghttp2_session_mem_send(nghttp2_session *session,
|
||||
const uint8_t **data_ptr)
|
||||
|
||||
int nghttp2_session_send(nghttp2_session *session)
|
||||
|
||||
int nghttp2_session_want_read(nghttp2_session *session)
|
||||
|
||||
int nghttp2_session_want_write(nghttp2_session *session)
|
||||
|
||||
ctypedef union nghttp2_data_source:
|
||||
int fd
|
||||
void *ptr
|
||||
|
||||
ctypedef enum nghttp2_data_flag:
|
||||
NGHTTP2_DATA_FLAG_NONE
|
||||
NGHTTP2_DATA_FLAG_EOF
|
||||
|
||||
ctypedef ssize_t (*nghttp2_data_source_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)
|
||||
|
||||
ctypedef struct nghttp2_data_provider:
|
||||
nghttp2_data_source source
|
||||
nghttp2_data_source_read_callback read_callback
|
||||
|
||||
int nghttp2_submit_request(nghttp2_session *session, int32_t pri,
|
||||
const nghttp2_nv *nva, size_t nvlen,
|
||||
const nghttp2_data_provider *data_prd,
|
||||
void *stream_user_data)
|
||||
|
||||
int nghttp2_submit_response(nghttp2_session *session,
|
||||
int32_t stream_id,
|
||||
const nghttp2_nv *nva, size_t nvlen,
|
||||
const nghttp2_data_provider *data_prd)
|
||||
|
||||
int nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags,
|
||||
int32_t stream_id,
|
||||
const nghttp2_nv *nva, size_t nvlen,
|
||||
void *stream_user_data)
|
||||
|
||||
int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags,
|
||||
const nghttp2_settings_entry *iv, size_t niv)
|
||||
|
||||
int nghttp2_submit_rst_stream(nghttp2_session *session, uint8_t flags,
|
||||
int32_t stream_id,
|
||||
nghttp2_error_code error_code)
|
||||
|
||||
void* nghttp2_session_get_stream_user_data(nghttp2_session *session,
|
||||
uint32_t stream_id)
|
||||
|
||||
int nghttp2_session_set_stream_user_data(nghttp2_session *session,
|
||||
uint32_t stream_id,
|
||||
void *stream_user_data)
|
||||
|
||||
int nghttp2_session_terminate_session(nghttp2_session *session,
|
||||
nghttp2_error_code error_code)
|
||||
|
||||
const char* nghttp2_strerror(int lib_error_code)
|
||||
|
||||
void nghttp2_hd_deflate_set_no_refset(nghttp2_hd_deflater *deflater,
|
||||
uint8_t no_refset)
|
||||
|
||||
int nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater,
|
||||
size_t hd_table_bufsize_max)
|
||||
|
||||
int nghttp2_hd_inflate_change_table_size(nghttp2_hd_inflater *inflater,
|
||||
size_t hd_table_bufsize_max)
|
||||
|
||||
ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater,
|
||||
nghttp2_nv *nv_out, int *inflate_flags,
|
||||
uint8_t *input, size_t inlen, int in_final)
|
||||
|
||||
int nghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater)
|
||||
|
||||
cdef extern from 'nghttp2_helper.h':
|
||||
|
||||
void nghttp2_free(void *ptr)
|
||||
@@ -45,10 +275,6 @@ cdef extern from 'nghttp2_hd.h':
|
||||
# This is macro
|
||||
int NGHTTP2_HD_ENTRY_OVERHEAD
|
||||
|
||||
ctypedef enum nghttp2_hd_side:
|
||||
NGHTTP2_HD_SIDE_REQUEST
|
||||
NGHTTP2_HD_SIDE_RESPONSE
|
||||
|
||||
ctypedef enum nghttp2_hd_flags:
|
||||
NGHTTP2_HD_FLAG_REFSET
|
||||
|
||||
@@ -73,33 +299,29 @@ cdef extern from 'nghttp2_hd.h':
|
||||
nghttp2_hd_context ctx
|
||||
|
||||
int nghttp2_hd_deflate_init2(nghttp2_hd_deflater *deflater,
|
||||
nghttp2_hd_side side,
|
||||
size_t deflate_hd_table_bufsize_max)
|
||||
|
||||
int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater,
|
||||
nghttp2_hd_side side)
|
||||
|
||||
int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater)
|
||||
|
||||
void nghttp2_hd_deflate_free(nghttp2_hd_deflater *deflater)
|
||||
|
||||
void nghttp2_hd_inflate_free(nghttp2_hd_inflater *inflater)
|
||||
|
||||
void nghttp2_hd_deflate_set_no_refset(nghttp2_hd_deflater *deflater,
|
||||
uint8_t no_refset)
|
||||
|
||||
int nghttp2_hd_change_table_size(nghttp2_hd_context *context,
|
||||
size_t hd_table_bufsize_max)
|
||||
|
||||
ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater,
|
||||
uint8_t **buf_ptr, size_t *buflen_ptr,
|
||||
size_t nv_offset,
|
||||
int nghttp2_hd_deflate_hd_bufs(nghttp2_hd_deflater *deflater,
|
||||
nghttp2_bufs *bufs,
|
||||
nghttp2_nv *nva, size_t nvlen)
|
||||
|
||||
ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater,
|
||||
nghttp2_nv *nv_out, int *inflate_flags,
|
||||
uint8_t *input, size_t inlen, int in_final)
|
||||
|
||||
int nghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater)
|
||||
|
||||
nghttp2_hd_entry* nghttp2_hd_table_get(nghttp2_hd_context *context,
|
||||
size_t index)
|
||||
|
||||
cdef extern from 'nghttp2_buf.h':
|
||||
|
||||
ctypedef struct nghttp2_bufs:
|
||||
pass
|
||||
|
||||
void nghttp2_bufs_init(nghttp2_bufs *bufs, size_t chunk_size,
|
||||
size_t max_chunk)
|
||||
|
||||
void nghttp2_bufs_free(nghttp2_bufs *bufs)
|
||||
|
||||
ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out)
|
||||
|
||||
@@ -13,12 +13,7 @@ from binascii import a2b_hex
|
||||
import nghttp2
|
||||
|
||||
def testsuite(testdata):
|
||||
if testdata['context'] == 'request':
|
||||
side = nghttp2.HD_SIDE_REQUEST
|
||||
else:
|
||||
side = nghttp2.HD_SIDE_RESPONSE
|
||||
|
||||
inflater = nghttp2.HDInflater(side)
|
||||
inflater = nghttp2.HDInflater()
|
||||
|
||||
for casenum, item in enumerate(testdata['cases']):
|
||||
if 'header_table_size' in item:
|
||||
@@ -35,7 +30,12 @@ def testsuite(testdata):
|
||||
hdrs.sort()
|
||||
expected_hdrs.sort()
|
||||
if hdrs != expected_hdrs:
|
||||
sys.stderr.write('FAIL case#{}\n'.format(casenum + 1))
|
||||
if 'seqno' in item:
|
||||
seqno = item['seqno']
|
||||
else:
|
||||
seqno = casenum
|
||||
|
||||
sys.stderr.write('FAIL seqno#{}\n'.format(seqno))
|
||||
sys.stderr.write('expected:\n')
|
||||
for k, v in expected_hdrs:
|
||||
sys.stderr.write('{}: {}\n'.format(k, v))
|
||||
@@ -47,7 +47,7 @@ def testsuite(testdata):
|
||||
|
||||
if __name__ == '__main__':
|
||||
for filename in sys.argv[1:]:
|
||||
sys.stderr.write('{}\n'.format(filename))
|
||||
sys.stderr.write('{}: '.format(filename))
|
||||
with open(filename) as f:
|
||||
input = f.read()
|
||||
testsuite(json.loads(input))
|
||||
|
||||
@@ -14,13 +14,8 @@ from binascii import b2a_hex
|
||||
import nghttp2
|
||||
|
||||
def testsuite(testdata, filename, outdir, table_size, deflate_table_size):
|
||||
if testdata['context'] == 'request':
|
||||
side = nghttp2.HD_SIDE_REQUEST
|
||||
else:
|
||||
side = nghttp2.HD_SIDE_RESPONSE
|
||||
|
||||
res = {
|
||||
'draft':5, 'context': testdata['context'],
|
||||
'draft':7,
|
||||
'description': '''\
|
||||
Encoded by nghttp2. The basic encoding strategy is described in \
|
||||
http://lists.w3.org/Archives/Public/ietf-http-wg/2013JulSep/1135.html \
|
||||
@@ -29,11 +24,14 @@ original. We make some headers not indexing at all, but this does not always \
|
||||
result in less bits on the wire.'''
|
||||
}
|
||||
cases = []
|
||||
deflater = nghttp2.HDDeflater(side, deflate_table_size)
|
||||
deflater = nghttp2.HDDeflater(deflate_table_size)
|
||||
|
||||
if table_size != nghttp2.DEFAULT_HEADER_TABLE_SIZE:
|
||||
deflater.change_table_size(table_size)
|
||||
|
||||
for casenum, item in enumerate(testdata['cases']):
|
||||
outitem = {
|
||||
'header_table_size': table_size,
|
||||
'seqno': casenum,
|
||||
'headers': item['headers']
|
||||
}
|
||||
casenum += 1
|
||||
@@ -42,6 +40,10 @@ result in less bits on the wire.'''
|
||||
for x in item['headers']]
|
||||
outitem['wire'] = b2a_hex(deflater.deflate(hdrs)).decode('utf-8')
|
||||
cases.append(outitem)
|
||||
|
||||
if cases and table_size != nghttp2.DEFAULT_HEADER_TABLE_SIZE:
|
||||
cases[0]['header_table_size'] = table_size
|
||||
|
||||
res['cases'] = cases
|
||||
jsonstr = json.dumps(res, indent=2)
|
||||
with open(os.path.join(outdir, filename), 'w') as f:
|
||||
@@ -51,10 +53,10 @@ if __name__ == '__main__':
|
||||
ap = argparse.ArgumentParser(description='HPACK test case generator')
|
||||
ap.add_argument('-d', '--dir', help='output directory', default='out')
|
||||
ap.add_argument('-s', '--table-size', help='max header table size',
|
||||
type=int, default=4096)
|
||||
type=int, default=nghttp2.DEFAULT_HEADER_TABLE_SIZE)
|
||||
ap.add_argument('-S', '--deflate-table-size',
|
||||
help='max header table size for deflater',
|
||||
type=int, default=4096)
|
||||
type=int, default=nghttp2.DEFLATE_MAX_HEADER_TABLE_SIZE)
|
||||
ap.add_argument('file', nargs='*', help='input file')
|
||||
args = ap.parse_args()
|
||||
try:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# nghttp2 - HTTP/2.0 C Library
|
||||
# nghttp2 - HTTP/2 C Library
|
||||
|
||||
# Copyright (c) 2013 Tatsuhiro Tsujikawa
|
||||
|
||||
@@ -26,10 +26,8 @@ from libc.stdlib cimport malloc, free
|
||||
from libc.string cimport memcpy, memset
|
||||
from libc.stdint cimport uint8_t, uint16_t, uint32_t, int32_t
|
||||
|
||||
HD_SIDE_REQUEST = cnghttp2.NGHTTP2_HD_SIDE_REQUEST
|
||||
HD_SIDE_RESPONSE = cnghttp2.NGHTTP2_HD_SIDE_RESPONSE
|
||||
|
||||
HD_DEFLATE_HD_TABLE_BUFSIZE_MAX = 4096
|
||||
DEFAULT_HEADER_TABLE_SIZE = cnghttp2.NGHTTP2_DEFAULT_HEADER_TABLE_SIZE
|
||||
DEFLATE_MAX_HEADER_TABLE_SIZE = 4096
|
||||
|
||||
HD_ENTRY_OVERHEAD = cnghttp2.NGHTTP2_HD_ENTRY_OVERHEAD
|
||||
|
||||
@@ -45,12 +43,6 @@ class HDTableEntry:
|
||||
def space(self):
|
||||
return self.namelen + self.valuelen + HD_ENTRY_OVERHEAD
|
||||
|
||||
cdef _change_table_size(cnghttp2.nghttp2_hd_context *ctx, hd_table_bufsize_max):
|
||||
cdef int rv
|
||||
rv = cnghttp2.nghttp2_hd_change_table_size(ctx, hd_table_bufsize_max)
|
||||
if rv != 0:
|
||||
raise Exception(_strerror(rv))
|
||||
|
||||
cdef _get_hd_table(cnghttp2.nghttp2_hd_context *ctx):
|
||||
cdef int length = ctx.hd_table.len
|
||||
cdef cnghttp2.nghttp2_hd_entry *entry
|
||||
@@ -65,35 +57,25 @@ cdef _get_hd_table(cnghttp2.nghttp2_hd_context *ctx):
|
||||
return res
|
||||
|
||||
cdef _get_pybytes(uint8_t *b, uint16_t blen):
|
||||
# While the |blen| is positive, the |b| could be NULL. This is
|
||||
# because deflater may deallocate the byte strings its local table
|
||||
# space.
|
||||
if b == NULL and blen > 0:
|
||||
val = None
|
||||
else:
|
||||
val = b[:blen]
|
||||
return val
|
||||
return b[:blen]
|
||||
|
||||
cdef class HDDeflater:
|
||||
'''Performs header compression. The header compression algorithm has
|
||||
to know the header set to be compressed is request headers or
|
||||
response headers. It is indicated by |side| parameter in the
|
||||
constructor. The constructor also takes |hd_table_bufsize_max|
|
||||
parameter, which limits the usage of header table in the given
|
||||
amount of bytes. This is necessary because the header compressor
|
||||
and decompressor has to share the same amount of header table and
|
||||
the decompressor decides that number. The compressor may not want
|
||||
to use all header table size because of limited memory
|
||||
availability. In that case, the |hd_table_bufsize_max| can be used
|
||||
to cap the upper limit of talbe size whatever the header table
|
||||
size is chosen. The default value of |hd_table_bufsize_max| is
|
||||
4096 bytes.
|
||||
'''Performs header compression. The constructor takes
|
||||
|hd_table_bufsize_max| parameter, which limits the usage of header
|
||||
table in the given amount of bytes. This is necessary because the
|
||||
header compressor and decompressor share the same amount of
|
||||
header table and the decompressor decides that number. The
|
||||
compressor may not want to use all header table size because of
|
||||
limited memory availability. In that case, the
|
||||
|hd_table_bufsize_max| can be used to cap the upper limit of table
|
||||
size whatever the header table size is chosen by the decompressor.
|
||||
The default value of |hd_table_bufsize_max| is 4096 bytes.
|
||||
|
||||
The following example shows how to compress request header sets:
|
||||
|
||||
import binascii, nghttp2
|
||||
|
||||
deflater = nghttp2.HDDeflater(nghttp2.HD_SIDE_REQUEST)
|
||||
deflater = nghttp2.HDDeflater()
|
||||
res = deflater.deflate([(b'foo', b'bar'),
|
||||
(b'baz', b'buz')])
|
||||
print(binascii.b2a_hex(res))
|
||||
@@ -102,17 +84,12 @@ cdef class HDDeflater:
|
||||
|
||||
cdef cnghttp2.nghttp2_hd_deflater _deflater
|
||||
|
||||
def __cinit__(self, side,
|
||||
hd_table_bufsize_max = HD_DEFLATE_HD_TABLE_BUFSIZE_MAX):
|
||||
rv = cnghttp2.nghttp2_hd_deflate_init2(&self._deflater, side,
|
||||
def __cinit__(self, hd_table_bufsize_max = DEFLATE_MAX_HEADER_TABLE_SIZE):
|
||||
rv = cnghttp2.nghttp2_hd_deflate_init2(&self._deflater,
|
||||
hd_table_bufsize_max)
|
||||
if rv != 0:
|
||||
raise Exception(_strerror(rv))
|
||||
|
||||
def __init__(self, side,
|
||||
hd_table_bufsize_max = HD_DEFLATE_HD_TABLE_BUFSIZE_MAX):
|
||||
super(HDDeflater, self).__init__()
|
||||
|
||||
def __dealloc__(self):
|
||||
cnghttp2.nghttp2_hd_deflate_free(&self._deflater)
|
||||
|
||||
@@ -129,25 +106,46 @@ cdef class HDDeflater:
|
||||
malloc(sizeof(cnghttp2.nghttp2_nv)*\
|
||||
len(headers))
|
||||
cdef cnghttp2.nghttp2_nv *nvap = nva
|
||||
|
||||
for k, v in headers:
|
||||
nvap[0].name = k
|
||||
nvap[0].namelen = len(k)
|
||||
nvap[0].value = v
|
||||
nvap[0].valuelen = len(v)
|
||||
nvap[0].flags = cnghttp2.NGHTTP2_NV_FLAG_NONE
|
||||
nvap += 1
|
||||
cdef uint8_t *out = NULL
|
||||
|
||||
cdef cnghttp2.nghttp2_bufs bufs
|
||||
cdef size_t outcap = 0
|
||||
cdef ssize_t rv
|
||||
rv = cnghttp2.nghttp2_hd_deflate_hd(&self._deflater, &out, &outcap,
|
||||
0, nva, len(headers))
|
||||
cdef uint8_t *out
|
||||
|
||||
cnghttp2.nghttp2_bufs_init(&bufs, 4096, 16)
|
||||
|
||||
rv = cnghttp2.nghttp2_hd_deflate_hd_bufs(&self._deflater, &bufs,
|
||||
nva, len(headers))
|
||||
free(nva)
|
||||
|
||||
if rv < 0:
|
||||
cnghttp2.nghttp2_bufs_free(&bufs);
|
||||
|
||||
raise Exception(_strerror(rv))
|
||||
|
||||
rv = cnghttp2.nghttp2_bufs_remove(&bufs, &out)
|
||||
|
||||
if rv < 0:
|
||||
cnghttp2.nghttp2_bufs_free(&bufs);
|
||||
|
||||
raise Exception(_strerror(rv))
|
||||
|
||||
cdef bytes res
|
||||
|
||||
try:
|
||||
res = out[:rv]
|
||||
finally:
|
||||
cnghttp2.nghttp2_free(out)
|
||||
cnghttp2.nghttp2_bufs_free(&bufs)
|
||||
|
||||
return res
|
||||
|
||||
def set_no_refset(self, no_refset):
|
||||
@@ -165,9 +163,13 @@ cdef class HDDeflater:
|
||||
An exception will be raised on error.
|
||||
|
||||
'''
|
||||
_change_table_size(&self._deflater.ctx, hd_table_bufsize_max)
|
||||
cdef int rv
|
||||
rv = cnghttp2.nghttp2_hd_deflate_change_table_size(&self._deflater,
|
||||
hd_table_bufsize_max)
|
||||
if rv != 0:
|
||||
raise Exception(_strerror(rv))
|
||||
|
||||
def get_hd_table(self,):
|
||||
def get_hd_table(self):
|
||||
'''Returns copy of current dynamic header table.'''
|
||||
return _get_hd_table(&self._deflater.ctx)
|
||||
|
||||
@@ -177,7 +179,7 @@ cdef class HDInflater:
|
||||
The following example shows how to compress request header sets:
|
||||
|
||||
data = b'0082c5ad82bd0f000362617a0362757a'
|
||||
inflater = nghttp2.HDInflater(nghttp2.HD_SIDE_REQUEST)
|
||||
inflater = nghttp2.HDInflater()
|
||||
hdrs = inflater.inflate(data)
|
||||
print(hdrs)
|
||||
|
||||
@@ -185,14 +187,11 @@ cdef class HDInflater:
|
||||
|
||||
cdef cnghttp2.nghttp2_hd_inflater _inflater
|
||||
|
||||
def __cinit__(self, side):
|
||||
rv = cnghttp2.nghttp2_hd_inflate_init(&self._inflater, side)
|
||||
def __cinit__(self):
|
||||
rv = cnghttp2.nghttp2_hd_inflate_init(&self._inflater)
|
||||
if rv != 0:
|
||||
raise Exception(_strerror(rv))
|
||||
|
||||
def __init__(self, side):
|
||||
super(HDInflater, self).__init__()
|
||||
|
||||
def __dealloc__(self):
|
||||
cnghttp2.nghttp2_hd_inflate_free(&self._inflater)
|
||||
|
||||
@@ -231,7 +230,11 @@ cdef class HDInflater:
|
||||
An exception will be raised on error.
|
||||
|
||||
'''
|
||||
_change_table_size(&self._inflater.ctx, hd_table_bufsize_max)
|
||||
cdef int rv
|
||||
rv = cnghttp2.nghttp2_hd_inflate_change_table_size(&self._inflater,
|
||||
hd_table_bufsize_max)
|
||||
if rv != 0:
|
||||
raise Exception(_strerror(rv))
|
||||
|
||||
def get_hd_table(self):
|
||||
'''Returns copy of current dynamic header table.'''
|
||||
@@ -255,5 +258,746 @@ def print_hd_table(hdtable):
|
||||
print('[{}] (s={}) (r={}) {}: {}'\
|
||||
.format(idx, entry.space(),
|
||||
'y' if entry.ref else 'n',
|
||||
'**DEALLOCATED**' if entry.name is None else entry.name.decode('utf-8'),
|
||||
'**DEALLOCATED**' if entry.value is None else entry.value.decode('utf-8')))
|
||||
entry.name.decode('utf-8'),
|
||||
entry.value.decode('utf-8')))
|
||||
|
||||
try:
|
||||
import socket
|
||||
import io
|
||||
import asyncio
|
||||
import traceback
|
||||
import sys
|
||||
import email.utils
|
||||
import datetime
|
||||
import time
|
||||
except ImportError:
|
||||
asyncio = None
|
||||
|
||||
cdef _get_stream_user_data(cnghttp2.nghttp2_session *session,
|
||||
int32_t stream_id):
|
||||
cdef void *stream_user_data
|
||||
|
||||
stream_user_data = cnghttp2.nghttp2_session_get_stream_user_data\
|
||||
(session, stream_id)
|
||||
if stream_user_data == NULL:
|
||||
return None
|
||||
|
||||
return <object>stream_user_data
|
||||
|
||||
cdef size_t _make_nva(cnghttp2.nghttp2_nv **nva_ptr, headers):
|
||||
cdef cnghttp2.nghttp2_nv *nva
|
||||
cdef size_t nvlen
|
||||
|
||||
nvlen = len(headers)
|
||||
nva = <cnghttp2.nghttp2_nv*>malloc(sizeof(cnghttp2.nghttp2_nv) * nvlen)
|
||||
for i, (k, v) in enumerate(headers):
|
||||
nva[i].name = k
|
||||
nva[i].namelen = len(k)
|
||||
nva[i].value = v
|
||||
nva[i].valuelen = len(v)
|
||||
nva[i].flags = cnghttp2.NGHTTP2_NV_FLAG_NONE
|
||||
|
||||
nva_ptr[0] = nva
|
||||
|
||||
return nvlen
|
||||
|
||||
cdef int server_on_header(cnghttp2.nghttp2_session *session,
|
||||
const cnghttp2.nghttp2_frame *frame,
|
||||
const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen,
|
||||
uint8_t flags,
|
||||
void *user_data):
|
||||
cdef http2 = <_HTTP2SessionCore>user_data
|
||||
|
||||
handler = _get_stream_user_data(session, frame.hd.stream_id)
|
||||
if not handler:
|
||||
return 0
|
||||
|
||||
key = name[:namelen]
|
||||
values = value[:valuelen].split(b'\x00')
|
||||
if key == b':scheme':
|
||||
handler.scheme = values[0]
|
||||
elif key == b':method':
|
||||
handler.method = values[0]
|
||||
elif key == b':authority' or key == b'host':
|
||||
handler.host = values[0]
|
||||
elif key == b':path':
|
||||
handler.path = values[0]
|
||||
|
||||
if key == b'cookie':
|
||||
handler.cookies.extend(values)
|
||||
else:
|
||||
for v in values:
|
||||
handler.headers.append((key, v))
|
||||
|
||||
return 0
|
||||
|
||||
cdef int server_on_begin_request_headers(cnghttp2.nghttp2_session *session,
|
||||
const cnghttp2.nghttp2_frame *frame,
|
||||
void *user_data):
|
||||
cdef http2 = <_HTTP2SessionCore>user_data
|
||||
|
||||
handler = http2._make_handler(frame.hd.stream_id)
|
||||
cnghttp2.nghttp2_session_set_stream_user_data(session, frame.hd.stream_id,
|
||||
<void*>handler)
|
||||
|
||||
return 0
|
||||
|
||||
cdef int server_on_begin_headers(cnghttp2.nghttp2_session *session,
|
||||
const cnghttp2.nghttp2_frame *frame,
|
||||
void *user_data):
|
||||
if frame.hd.type == cnghttp2.NGHTTP2_HEADERS:
|
||||
if frame.headers.cat == cnghttp2.NGHTTP2_HCAT_REQUEST:
|
||||
return server_on_begin_request_headers(session, frame, user_data)
|
||||
|
||||
return 0
|
||||
|
||||
cdef int server_on_frame_recv(cnghttp2.nghttp2_session *session,
|
||||
const cnghttp2.nghttp2_frame *frame,
|
||||
void *user_data):
|
||||
cdef http2 = <_HTTP2SessionCore>user_data
|
||||
|
||||
if frame.hd.type == cnghttp2.NGHTTP2_DATA:
|
||||
if frame.hd.flags & cnghttp2.NGHTTP2_FLAG_END_STREAM:
|
||||
handler = _get_stream_user_data(session, frame.hd.stream_id)
|
||||
if not handler:
|
||||
return 0
|
||||
try:
|
||||
handler.on_request_done()
|
||||
except:
|
||||
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_REQUEST:
|
||||
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)))
|
||||
handler.cookies = None
|
||||
try:
|
||||
handler.on_headers()
|
||||
if frame.hd.flags & cnghttp2.NGHTTP2_FLAG_END_STREAM:
|
||||
handler.on_request_done()
|
||||
except:
|
||||
sys.stderr.write(traceback.format_exc())
|
||||
return http2._rst_stream(frame.hd.stream_id)
|
||||
elif frame.hd.type == cnghttp2.NGHTTP2_SETTINGS:
|
||||
if (frame.hd.flags & cnghttp2.NGHTTP2_FLAG_ACK):
|
||||
http2._stop_settings_timer()
|
||||
|
||||
return 0
|
||||
|
||||
cdef int server_on_data_chunk_recv(cnghttp2.nghttp2_session *session,
|
||||
uint8_t flags,
|
||||
int32_t stream_id, const uint8_t *data,
|
||||
size_t length, void *user_data):
|
||||
cdef http2 = <_HTTP2SessionCore>user_data
|
||||
|
||||
handler = _get_stream_user_data(session, stream_id)
|
||||
if not handler:
|
||||
return 0
|
||||
|
||||
try:
|
||||
handler.on_data(data[:length])
|
||||
except:
|
||||
sys.stderr.write(traceback.format_exc())
|
||||
return http2._rst_stream(stream_id)
|
||||
|
||||
return 0
|
||||
|
||||
cdef int server_on_frame_send(cnghttp2.nghttp2_session *session,
|
||||
const cnghttp2.nghttp2_frame *frame,
|
||||
void *user_data):
|
||||
cdef http2 = <_HTTP2SessionCore>user_data
|
||||
|
||||
if frame.hd.type == cnghttp2.NGHTTP2_PUSH_PROMISE:
|
||||
# For PUSH_PROMISE, send push response immediately
|
||||
handler = _get_stream_user_data\
|
||||
(session, frame.push_promise.promised_stream_id)
|
||||
if not handler:
|
||||
return 0
|
||||
|
||||
http2.send_response(handler)
|
||||
elif frame.hd.type == cnghttp2.NGHTTP2_SETTINGS:
|
||||
if (frame.hd.flags & cnghttp2.NGHTTP2_FLAG_ACK) == 0:
|
||||
return 0
|
||||
http2._start_settings_timer()
|
||||
|
||||
cdef int server_on_frame_not_send(cnghttp2.nghttp2_session *session,
|
||||
const cnghttp2.nghttp2_frame *frame,
|
||||
int lib_error_code,
|
||||
void *user_data):
|
||||
cdef http2 = <_HTTP2SessionCore>user_data
|
||||
|
||||
if frame.hd.type == cnghttp2.NGHTTP2_PUSH_PROMISE:
|
||||
# We have to remove handler here. Without this, it is not
|
||||
# removed until session is terminated.
|
||||
handler = _get_stream_user_data\
|
||||
(session, frame.push_promise.promised_stream_id)
|
||||
if not handler:
|
||||
return 0
|
||||
http2._remove_handler(handler)
|
||||
|
||||
cdef int server_on_stream_close(cnghttp2.nghttp2_session *session,
|
||||
int32_t stream_id,
|
||||
cnghttp2.nghttp2_error_code error_code,
|
||||
void *user_data):
|
||||
cdef http2 = <_HTTP2SessionCore>user_data
|
||||
|
||||
handler = _get_stream_user_data(session, stream_id)
|
||||
if not handler:
|
||||
return 0
|
||||
|
||||
try:
|
||||
handler.on_close(error_code)
|
||||
except:
|
||||
sys.stderr.write(traceback.format_exc())
|
||||
|
||||
http2._remove_handler(handler)
|
||||
|
||||
return 0
|
||||
|
||||
cdef ssize_t server_data_source_read(cnghttp2.nghttp2_session *session,
|
||||
int32_t stream_id,
|
||||
uint8_t *buf, size_t length,
|
||||
uint32_t *data_flags,
|
||||
cnghttp2.nghttp2_data_source *source,
|
||||
void *user_data):
|
||||
cdef http2 = <_HTTP2SessionCore>user_data
|
||||
handler = <object>source.ptr
|
||||
|
||||
try:
|
||||
data = handler.response_body.read(length)
|
||||
except:
|
||||
sys.stderr.write(traceback.format_exc())
|
||||
return cnghttp2.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
|
||||
if data:
|
||||
nread = len(data)
|
||||
memcpy(buf, <uint8_t*>data, nread)
|
||||
return nread
|
||||
|
||||
data_flags[0] = cnghttp2.NGHTTP2_DATA_FLAG_EOF
|
||||
|
||||
return 0
|
||||
|
||||
cdef class _HTTP2SessionCore:
|
||||
cdef cnghttp2.nghttp2_session *session
|
||||
cdef transport
|
||||
cdef handler_class
|
||||
cdef handlers
|
||||
cdef settings_timer
|
||||
|
||||
def __cinit__(self, transport, handler_class):
|
||||
cdef cnghttp2.nghttp2_session_callbacks callbacks
|
||||
cdef cnghttp2.nghttp2_settings_entry iv[2]
|
||||
cdef int rv
|
||||
|
||||
self.session = NULL
|
||||
|
||||
self.transport = transport
|
||||
self.handler_class = handler_class
|
||||
self.handlers = set()
|
||||
self.settings_timer = None
|
||||
|
||||
memset(&callbacks, 0, sizeof(callbacks))
|
||||
callbacks.on_header_callback = server_on_header
|
||||
callbacks.on_begin_headers_callback = server_on_begin_headers
|
||||
callbacks.on_frame_recv_callback = server_on_frame_recv
|
||||
callbacks.on_stream_close_callback = server_on_stream_close
|
||||
callbacks.on_frame_send_callback = server_on_frame_send
|
||||
callbacks.on_frame_not_send_callback = server_on_frame_not_send
|
||||
callbacks.on_data_chunk_recv_callback = server_on_data_chunk_recv
|
||||
|
||||
rv = cnghttp2.nghttp2_session_server_new(&self.session, &callbacks,
|
||||
<void*>self)
|
||||
if rv != 0:
|
||||
raise Exception('nghttp2_session_server_new failed: {}'.format\
|
||||
(_strerror(rv)))
|
||||
|
||||
iv[0].settings_id = cnghttp2.NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS
|
||||
iv[0].value = 100
|
||||
iv[1].settings_id = cnghttp2.NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE
|
||||
iv[1].value = cnghttp2.NGHTTP2_INITIAL_WINDOW_SIZE
|
||||
|
||||
rv = cnghttp2.nghttp2_submit_settings(self.session,
|
||||
cnghttp2.NGHTTP2_FLAG_NONE,
|
||||
iv, sizeof(iv) / sizeof(iv[0]))
|
||||
|
||||
if rv != 0:
|
||||
raise Exception('nghttp2_submit_settings failed: {}'.format\
|
||||
(_strerror(rv)))
|
||||
|
||||
def __dealloc__(self):
|
||||
cnghttp2.nghttp2_session_del(self.session)
|
||||
|
||||
def data_received(self, data):
|
||||
cdef ssize_t rv
|
||||
|
||||
rv = cnghttp2.nghttp2_session_mem_recv(self.session, data, len(data))
|
||||
if rv < 0:
|
||||
raise Exception('nghttp2_session_mem_recv failed: {}'.format\
|
||||
(_strerror(rv)))
|
||||
self.send_data()
|
||||
|
||||
OUTBUF_MAX = 65535
|
||||
SETTINGS_TIMEOUT = 5.0
|
||||
|
||||
def send_data(self):
|
||||
cdef ssize_t outbuflen
|
||||
cdef const uint8_t *outbuf
|
||||
|
||||
while True:
|
||||
if self.transport.get_write_buffer_size() > self.OUTBUF_MAX:
|
||||
break
|
||||
outbuflen = cnghttp2.nghttp2_session_mem_send(self.session, &outbuf)
|
||||
if outbuflen == 0:
|
||||
break
|
||||
if outbuflen < 0:
|
||||
raise Exception('nghttp2_session_mem_send faild: {}'.format\
|
||||
(_strerror(outbuflen)))
|
||||
self.transport.write(outbuf[:outbuflen])
|
||||
|
||||
if self.transport.get_write_buffer_size() == 0 and \
|
||||
cnghttp2.nghttp2_session_want_read(self.session) == 0 and \
|
||||
cnghttp2.nghttp2_session_want_write(self.session) == 0:
|
||||
self.transport.close()
|
||||
|
||||
def _make_handler(self, stream_id):
|
||||
handler = self.handler_class(self, stream_id)
|
||||
self.handlers.add(handler)
|
||||
return handler
|
||||
|
||||
def _remove_handler(self, handler):
|
||||
self.handlers.remove(handler)
|
||||
|
||||
def send_response(self, handler):
|
||||
cdef cnghttp2.nghttp2_data_provider prd
|
||||
cdef cnghttp2.nghttp2_data_provider *prd_ptr
|
||||
cdef cnghttp2.nghttp2_nv *nva
|
||||
cdef size_t nvlen
|
||||
cdef int rv
|
||||
|
||||
nva = NULL
|
||||
nvlen = _make_nva(&nva, handler.response_headers)
|
||||
|
||||
if handler.response_body:
|
||||
prd.source.ptr = <void*>handler
|
||||
prd.read_callback = server_data_source_read
|
||||
prd_ptr = &prd
|
||||
else:
|
||||
prd_ptr = NULL
|
||||
|
||||
rv = cnghttp2.nghttp2_submit_response(self.session, handler.stream_id,
|
||||
nva, nvlen, prd_ptr)
|
||||
|
||||
free(nva)
|
||||
|
||||
if rv != 0:
|
||||
# TODO Ignore return value
|
||||
self._rst_stream(handler.stream_id)
|
||||
raise Exception('nghttp2_submit_response failed: {}'.format\
|
||||
(_strerror(rv)))
|
||||
|
||||
self._log_request(handler)
|
||||
|
||||
def push(self, handler, promised_handler):
|
||||
cdef cnghttp2.nghttp2_nv *nva
|
||||
cdef size_t nvlen
|
||||
cdef int32_t promised_stream_id
|
||||
|
||||
self.handlers.add(promised_handler)
|
||||
|
||||
nva = NULL
|
||||
nvlen = _make_nva(&nva, promised_handler.headers)
|
||||
|
||||
promised_stream_id = cnghttp2.nghttp2_submit_push_promise\
|
||||
(self.session,
|
||||
cnghttp2.NGHTTP2_FLAG_NONE,
|
||||
handler.stream_id,
|
||||
nva, nvlen,
|
||||
<void*>promised_handler)
|
||||
if promised_stream_id < 0:
|
||||
raise Exception('nghttp2_submit_push_promise failed: {}'.format\
|
||||
(_strerror(promised_stream_id)))
|
||||
|
||||
promised_handler.stream_id = promised_stream_id
|
||||
|
||||
return promised_handler
|
||||
|
||||
def _rst_stream(self, stream_id,
|
||||
error_code=cnghttp2.NGHTTP2_INTERNAL_ERROR):
|
||||
cdef int rv
|
||||
|
||||
rv = cnghttp2.nghttp2_submit_rst_stream\
|
||||
(self.session, cnghttp2.NGHTTP2_FLAG_NONE,
|
||||
stream_id, error_code)
|
||||
|
||||
return rv
|
||||
|
||||
def _start_settings_timer(self):
|
||||
loop = asyncio.get_event_loop()
|
||||
self.settings_timer = loop.call_later(self.SETTINGS_TIMEOUT,
|
||||
self._settings_timeout)
|
||||
|
||||
def _stop_settings_timer(self):
|
||||
if self.settings_timer:
|
||||
self.settings_timer.cancel()
|
||||
self.settings_timer = None
|
||||
|
||||
def _settings_timeout(self):
|
||||
cdef int rv
|
||||
|
||||
self.settings_timer = None
|
||||
|
||||
rv = cnghttp2.nghttp2_session_terminate_session\
|
||||
(self.session, cnghttp2.NGHTTP2_SETTINGS_TIMEOUT)
|
||||
try:
|
||||
self.send_data()
|
||||
except Exception as err:
|
||||
sys.stderr.write(traceback.format_exc())
|
||||
self.transport.close()
|
||||
return
|
||||
|
||||
def _get_client_address(self):
|
||||
return self.transport.get_extra_info('peername')
|
||||
|
||||
def _log_request(self, handler):
|
||||
now = datetime.datetime.now()
|
||||
tv = time.mktime(now.timetuple())
|
||||
datestr = email.utils.formatdate(timeval=tv, localtime=False,
|
||||
usegmt=True)
|
||||
try:
|
||||
method = handler.method.decode('utf-8')
|
||||
except:
|
||||
method = handler.method
|
||||
try:
|
||||
path = handler.path.decode('utf-8')
|
||||
except:
|
||||
path = handler.path
|
||||
sys.stderr.write('{} - - [{}] "{} {} HTTP/2" {} - {}\n'.format\
|
||||
(handler.client_address[0],
|
||||
datestr, method, path, handler.status,
|
||||
'P' if handler.pushed else '-'))
|
||||
|
||||
if asyncio:
|
||||
|
||||
class BaseRequestHandler:
|
||||
|
||||
"""HTTP/2 request (stream) handler base class.
|
||||
|
||||
The class is used to handle the HTTP/2 stream. By default, it does
|
||||
not nothing. It must be subclassed to handle each event callback
|
||||
method.
|
||||
|
||||
The first callback method invoked is on_headers(). It is called
|
||||
when HEADERS frame, which includes request header fields, is
|
||||
arrived.
|
||||
|
||||
If request has request body, on_data(data) is invoked for each
|
||||
chunk of received data.
|
||||
|
||||
When whole request is received, on_request_done() is invoked.
|
||||
|
||||
When stream is closed, on_close(error_code) is called.
|
||||
|
||||
The application can send response using send_response() method. It
|
||||
can be used in on_headers(), on_data() or on_request_done().
|
||||
|
||||
The application can push resource using push() method. It must be
|
||||
used before send_response() call.
|
||||
|
||||
The following instance variables are available:
|
||||
|
||||
client_address
|
||||
Contains a tuple of the form (host, port) referring to the client's
|
||||
address.
|
||||
|
||||
stream_id
|
||||
Stream ID of this stream
|
||||
|
||||
scheme
|
||||
Scheme of the request URI. This is a value of :scheme header field.
|
||||
|
||||
method
|
||||
Method of this stream. This is a value of :method header field.
|
||||
|
||||
host
|
||||
This is a value of :authority or host header field.
|
||||
|
||||
path
|
||||
This is a value of :path header field.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, http2, stream_id):
|
||||
self.headers = []
|
||||
self.cookies = []
|
||||
# Stream ID. For promised stream, it is initially -1.
|
||||
self.stream_id = stream_id
|
||||
self.http2 = http2
|
||||
# address of the client
|
||||
self.client_address = self.http2._get_client_address()
|
||||
# :scheme header field in request
|
||||
self.scheme = None
|
||||
# :method header field in request
|
||||
self.method = None
|
||||
# :authority or host header field in request
|
||||
self.host = None
|
||||
# :path header field in request
|
||||
self.path = None
|
||||
# HTTP status
|
||||
self.status = None
|
||||
# True if this is a handler for pushed resource
|
||||
self.pushed = False
|
||||
|
||||
def on_headers(self):
|
||||
|
||||
'''Called when request HEADERS is arrived.
|
||||
|
||||
'''
|
||||
pass
|
||||
|
||||
def on_data(self, data):
|
||||
|
||||
'''Called when a chunk of request body is arrived. This method
|
||||
will be called multiple times until all data are received.
|
||||
|
||||
'''
|
||||
pass
|
||||
|
||||
def on_request_done(self):
|
||||
|
||||
'''Called when whole request was received
|
||||
|
||||
'''
|
||||
pass
|
||||
|
||||
def on_close(self, error_code):
|
||||
|
||||
'''Called when stream is about to close.
|
||||
|
||||
'''
|
||||
pass
|
||||
|
||||
def send_response(self, status=200, headers=None, body=None):
|
||||
|
||||
'''Send response. The status is HTTP status code. The headers is
|
||||
additional response headers. The :status header field is
|
||||
appended by the library. The body is the response body. It
|
||||
could be None if response body is empty. Or it must be
|
||||
instance of either str, bytes or io.IOBase. If instance of str
|
||||
is specified, it is encoded using UTF-8.
|
||||
|
||||
The headers is a list of tuple of the form (name,
|
||||
value). The name and value can be either unicode string or
|
||||
byte string.
|
||||
|
||||
On error, exception will be thrown.
|
||||
|
||||
'''
|
||||
if self.status is not None:
|
||||
raise Exception('response has already been sent')
|
||||
|
||||
if not status:
|
||||
raise Exception('status must not be empty')
|
||||
|
||||
body = self._wrap_body(body)
|
||||
|
||||
self._set_response_prop(status, headers, body)
|
||||
self.http2.send_response(self)
|
||||
|
||||
def push(self, path, method='GET', request_headers=None,
|
||||
status=200, headers=None, body=None):
|
||||
|
||||
'''Push a resource. The path is a path portion of request URI
|
||||
for this
|
||||
resource. The method is a method to access this
|
||||
resource. The request_headers is additional request
|
||||
headers to access this resource. The :scheme, :method,
|
||||
:authority and :path are appended by the library. The
|
||||
:scheme and :authority are inherited from the request (not
|
||||
request_headers parameter).
|
||||
|
||||
The status is HTTP status code. The headers is additional
|
||||
response headers. The :status header field is appended by the
|
||||
library. The body is the response body. It could be None if
|
||||
response body is empty. Or it must be instance of either str,
|
||||
bytes or io.IOBase. If instance of str is specified, it is
|
||||
encoded using UTF-8.
|
||||
|
||||
The headers and request_headers are a list of tuple of the
|
||||
form (name, value). The name and value can be either
|
||||
unicode string or byte string.
|
||||
|
||||
On error, exception will be thrown.
|
||||
|
||||
'''
|
||||
if not status:
|
||||
raise Exception('status must not be empty')
|
||||
|
||||
if not method:
|
||||
raise Exception('method must not be empty')
|
||||
|
||||
if not path:
|
||||
raise Exception('path must not be empty')
|
||||
|
||||
body = self._wrap_body(body)
|
||||
|
||||
promised_handler = self.http2._make_handler(-1)
|
||||
promised_handler.pushed = True
|
||||
promised_handler.scheme = self.scheme
|
||||
promised_handler.method = method.encode('utf-8')
|
||||
promised_handler.host = self.host
|
||||
promised_handler.path = path.encode('utf-8')
|
||||
promised_handler._set_response_prop(status, headers, body)
|
||||
|
||||
if request_headers is None:
|
||||
request_headers = []
|
||||
|
||||
request_headers = _encode_headers(request_headers)
|
||||
request_headers.append((b':scheme', promised_handler.scheme))
|
||||
request_headers.append((b':method', promised_handler.method))
|
||||
request_headers.append((b':authority', promised_handler.host))
|
||||
request_headers.append((b':path', promised_handler.path))
|
||||
|
||||
promised_handler.headers = request_headers
|
||||
|
||||
return self.http2.push(self, promised_handler)
|
||||
|
||||
def _set_response_prop(self, status, headers, body):
|
||||
self.status = status
|
||||
|
||||
if headers is None:
|
||||
headers = []
|
||||
|
||||
self.response_headers = _encode_headers(headers)
|
||||
self.response_headers.append((b':status', str(status)\
|
||||
.encode('utf-8')))
|
||||
|
||||
self.response_body = body
|
||||
|
||||
def _wrap_body(self, body):
|
||||
if body is None:
|
||||
return body
|
||||
elif isinstance(body, str):
|
||||
return io.BytesIO(body.encode('utf-8'))
|
||||
elif isinstance(body, bytes):
|
||||
return io.BytesIO(body)
|
||||
elif isinstance(body, io.IOBase):
|
||||
return body
|
||||
else:
|
||||
raise Exception(('body must be None or instance of str or '
|
||||
'bytes or io.IOBase'))
|
||||
|
||||
def _encode_headers(headers):
|
||||
return [(k if isinstance(k, bytes) else k.encode('utf-8'),
|
||||
v if isinstance(v, bytes) else v.encode('utf-8')) \
|
||||
for k, v in headers]
|
||||
|
||||
class _HTTP2Session(asyncio.Protocol):
|
||||
|
||||
def __init__(self, RequestHandlerClass):
|
||||
asyncio.Protocol.__init__(self)
|
||||
self.RequestHandlerClass = RequestHandlerClass
|
||||
self.http2 = None
|
||||
|
||||
def connection_made(self, transport):
|
||||
self.transport = transport
|
||||
self.connection_header = cnghttp2.NGHTTP2_CLIENT_CONNECTION_PREFACE
|
||||
sock = self.transport.get_extra_info('socket')
|
||||
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
||||
ssl_ctx = self.transport.get_extra_info('sslcontext')
|
||||
if ssl_ctx:
|
||||
if sock.selected_npn_protocol().encode('utf-8') != \
|
||||
cnghttp2.NGHTTP2_PROTO_VERSION_ID:
|
||||
self.transport.abort()
|
||||
|
||||
def connection_lost(self, exc):
|
||||
if self.http2:
|
||||
self.http2 = None
|
||||
|
||||
def data_received(self, data):
|
||||
nread = min(len(data), len(self.connection_header))
|
||||
|
||||
if self.connection_header.startswith(data[:nread]):
|
||||
data = data[nread:]
|
||||
self.connection_header = self.connection_header[nread:]
|
||||
if len(self.connection_header) == 0:
|
||||
try:
|
||||
self.http2 = _HTTP2SessionCore\
|
||||
(self.transport,
|
||||
self.RequestHandlerClass)
|
||||
except Exception as err:
|
||||
sys.stderr.write(traceback.format_exc())
|
||||
self.transport.abort()
|
||||
return
|
||||
|
||||
self.data_received = self.data_received2
|
||||
self.resume_writing = self.resume_writing2
|
||||
self.data_received(data)
|
||||
else:
|
||||
self.transport.abort()
|
||||
|
||||
def data_received2(self, data):
|
||||
try:
|
||||
self.http2.data_received(data)
|
||||
except Exception as err:
|
||||
sys.stderr.write(traceback.format_exc())
|
||||
self.transport.close()
|
||||
return
|
||||
|
||||
def resume_writing2(self):
|
||||
try:
|
||||
self.http2.send_data()
|
||||
except Exception as err:
|
||||
sys.stderr.write(traceback.format_exc())
|
||||
self.transport.close()
|
||||
return
|
||||
|
||||
class HTTP2Server:
|
||||
|
||||
'''HTTP/2 server.
|
||||
|
||||
This class builds on top of the asyncio event loop. On
|
||||
construction, RequestHandlerClass must be given, which must be a
|
||||
subclass of BaseRequestHandler class.
|
||||
|
||||
'''
|
||||
def __init__(self, address, RequestHandlerClass, ssl=None):
|
||||
|
||||
'''address is a tuple of the listening address and port (e.g.,
|
||||
('127.0.0.1', 8080)). RequestHandlerClass must be a subclass
|
||||
of BaseRequestHandler class to handle a HTTP/2 stream. The
|
||||
ssl can be ssl.SSLContext instance. If it is not None, the
|
||||
resulting server is SSL/TLS capable.
|
||||
|
||||
'''
|
||||
def session_factory():
|
||||
return _HTTP2Session(RequestHandlerClass)
|
||||
|
||||
self.loop = asyncio.get_event_loop()
|
||||
|
||||
if ssl:
|
||||
ssl.set_npn_protocols([cnghttp2.NGHTTP2_PROTO_VERSION_ID\
|
||||
.decode('utf-8')])
|
||||
|
||||
coro = self.loop.create_server(session_factory,
|
||||
host=address[0], port=address[1],
|
||||
ssl=ssl)
|
||||
self.server = self.loop.run_until_complete(coro)
|
||||
|
||||
def serve_forever(self):
|
||||
try:
|
||||
self.loop.run_forever()
|
||||
finally:
|
||||
self.server.close()
|
||||
self.loop.close()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# nghttp2 - HTTP/2.0 C Library
|
||||
# nghttp2 - HTTP/2 C Library
|
||||
|
||||
# Copyright (c) 2013 Tatsuhiro Tsujikawa
|
||||
|
||||
@@ -20,18 +20,29 @@
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
import sys
|
||||
from distutils.core import setup
|
||||
from distutils.extension import Extension
|
||||
|
||||
if sys.platform == "win32":
|
||||
LIBS = ['nghttp2_imp', 'ws2_32']
|
||||
else:
|
||||
LIBS = ['nghttp2']
|
||||
|
||||
setup(
|
||||
name = 'python-nghttp2',
|
||||
description = 'Python HTTP/2.0 library on top of nghttp2',
|
||||
description = 'Python HTTP/2 library on top of nghttp2',
|
||||
author = 'Tatsuhiro Tsujikawa',
|
||||
author_email = 'tatsuhiro.t@gmail.com',
|
||||
url = 'http://tatsuhiro-t.github.io/nghttp2/',
|
||||
keywords = [],
|
||||
ext_modules = [Extension("nghttp2",
|
||||
["nghttp2.c"],
|
||||
libraries=['nghttp2'])],
|
||||
include_dirs=['@top_srcdir@/lib',
|
||||
'@top_srcdir@/lib/includes',
|
||||
'@top_builddir@/lib/includes'],
|
||||
library_dirs=['@top_builddir@/lib/.libs'],
|
||||
libraries=LIBS)],
|
||||
long_description='TBD'
|
||||
)
|
||||
119
python/wsgi.py
Normal file
119
python/wsgi.py
Normal file
@@ -0,0 +1,119 @@
|
||||
# nghttp2 - HTTP/2.0 C Library
|
||||
|
||||
# Copyright (c) 2013 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.
|
||||
import io
|
||||
import sys
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import nghttp2
|
||||
|
||||
def _dance_decode(b):
|
||||
# TODO faster than looping through and mod-128'ing all unicode points?
|
||||
return b.decode('utf-8').encode('latin1').decode('latin1')
|
||||
|
||||
class WSGIContainer(nghttp2.BaseRequestHandler):
|
||||
|
||||
_BASE_ENVIRON = {
|
||||
'wsgi.version': (1,0),
|
||||
'wsgi.url_scheme': 'http', # FIXME
|
||||
'wsgi.multithread': True, # TODO I think?
|
||||
'wsgi.multiprocess': False, # TODO no idea
|
||||
'wsgi.run_once': True, # TODO now I'm just guessing
|
||||
'wsgi.errors': sys.stderr, # TODO will work for testing - is this even used by any frameworks?
|
||||
}
|
||||
|
||||
def __init__(self, app, *args, **kwargs):
|
||||
super(WSGIContainer, self).__init__(*args, **kwargs)
|
||||
self.app = app
|
||||
self.chunks = []
|
||||
|
||||
def on_data(self, chunk):
|
||||
self.chunks.append(chunk)
|
||||
|
||||
def on_request_done(self):
|
||||
environ = WSGIContainer._BASE_ENVIRON.copy()
|
||||
parsed = urlparse(self.path)
|
||||
|
||||
environ['wsgi.input'] = io.BytesIO(b''.join(self.chunks))
|
||||
|
||||
for name, value in self.headers:
|
||||
mangled_name = b'HTTP_' + name.replace(b'-', b'_').upper()
|
||||
environ[_dance_decode(mangled_name)] = _dance_decode(value)
|
||||
|
||||
environ.update(dict(
|
||||
REQUEST_METHOD=_dance_decode(self.method),
|
||||
# TODO SCRIPT_NAME? like APPLICATION_ROOT in Flask...
|
||||
PATH_INFO=_dance_decode(parsed.path),
|
||||
QUERY_STRING=_dance_decode(parsed.query),
|
||||
CONTENT_TYPE=environ.get('HTTP_CONTENT_TYPE', ''),
|
||||
CONTENT_LENGTH=environ.get('HTTP_CONTENT_LENGTH', ''),
|
||||
SERVER_NAME=_dance_decode(self.host),
|
||||
SERVER_PORT='', # FIXME probably requires changes in nghttp2
|
||||
SERVER_PROTOCOL='HTTP/2.0',
|
||||
))
|
||||
|
||||
response_status = [None]
|
||||
response_headers = [None]
|
||||
response_chunks = []
|
||||
|
||||
def start_response(status, headers, exc_info=None):
|
||||
if response_status[0] is not None:
|
||||
raise AssertionError('Response already started')
|
||||
exc_info = None # avoid dangling circular ref - TODO is this necessary? borrowed from snippet in WSGI spec
|
||||
|
||||
response_status[0] = status
|
||||
response_headers[0] = headers
|
||||
# TODO handle exc_info
|
||||
|
||||
return lambda chunk: response_chunks.append(chunk)
|
||||
|
||||
# TODO technically, this breaks the WSGI spec by buffering the status,
|
||||
# headers, and body until all are completely output from the app before
|
||||
# writing the response, but it looks like nghttp2 doesn't support any
|
||||
# other way for now
|
||||
|
||||
# TODO disallow yielding/returning before start_response is called
|
||||
response_chunks.extend(self.app(environ, start_response))
|
||||
response_body = b''.join(response_chunks)
|
||||
|
||||
# TODO automatically set content-length if not provided
|
||||
self.send_response(
|
||||
status=response_status[0],
|
||||
headers=response_headers[0],
|
||||
body=response_body,
|
||||
)
|
||||
|
||||
def wsgi_app(app):
|
||||
return lambda *args, **kwargs: WSGIContainer(app, *args, **kwargs)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import ssl
|
||||
from werkzeug.testapp import test_app
|
||||
|
||||
ssl_ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
|
||||
ssl_ctx.options = ssl.OP_ALL | ssl.OP_NO_SSLv2
|
||||
ssl_ctx.load_cert_chain('server.crt', 'server.key')
|
||||
|
||||
server = nghttp2.HTTP2Server(('127.0.0.1', 8443), wsgi_app(test_app),
|
||||
ssl=ssl_ctx)
|
||||
server.serve_forever()
|
||||
1
src/.gitignore
vendored
1
src/.gitignore
vendored
@@ -9,3 +9,4 @@ test-suite.log
|
||||
libnghttpx.a
|
||||
deflatehd
|
||||
inflatehd
|
||||
h2load
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
*
|
||||
@@ -103,7 +103,7 @@ void start_element_func
|
||||
if(!src_attr) {
|
||||
return;
|
||||
}
|
||||
add_link(parser_data, src_attr, REQ_PRI_MEDIUM);
|
||||
add_link(parser_data, src_attr, REQ_PRI_LOW);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
*
|
||||
|
||||
1230
src/HttpServer.cc
1230
src/HttpServer.cc
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2013 Tatsuhiro Tsujikawa
|
||||
*
|
||||
@@ -40,10 +40,19 @@
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
#include <event2/event.h>
|
||||
#include <event2/bufferevent.h>
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "nghttp2_buf.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "http2.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
@@ -54,27 +63,34 @@ struct Config {
|
||||
std::string host;
|
||||
std::string private_key_file;
|
||||
std::string cert_file;
|
||||
timeval stream_read_timeout;
|
||||
timeval stream_write_timeout;
|
||||
void *data_ptr;
|
||||
size_t output_upper_thres;
|
||||
size_t padding;
|
||||
size_t num_worker;
|
||||
ssize_t header_table_size;
|
||||
uint16_t port;
|
||||
bool verbose;
|
||||
bool daemon;
|
||||
bool verify_client;
|
||||
bool no_tls;
|
||||
bool no_flow_control;
|
||||
bool error_gzip;
|
||||
Config();
|
||||
};
|
||||
|
||||
class Sessions;
|
||||
class Http2Handler;
|
||||
|
||||
struct Request {
|
||||
struct Stream {
|
||||
Headers headers;
|
||||
std::pair<std::string, size_t> response_body;
|
||||
Http2Handler *handler;
|
||||
event *rtimer;
|
||||
event *wtimer;
|
||||
int32_t stream_id;
|
||||
int file;
|
||||
Request(int32_t stream_id);
|
||||
~Request();
|
||||
bool enable_compression;
|
||||
Stream(Http2Handler *handler, int32_t stream_id);
|
||||
~Stream();
|
||||
};
|
||||
|
||||
class Sessions;
|
||||
@@ -94,7 +110,7 @@ public:
|
||||
int recvcb(uint8_t *buf, size_t len);
|
||||
|
||||
int submit_file_response(const std::string& status,
|
||||
int32_t stream_id,
|
||||
Stream *stream,
|
||||
time_t last_modified,
|
||||
off_t file_length,
|
||||
nghttp2_data_provider *data_prd);
|
||||
@@ -109,11 +125,13 @@ public:
|
||||
const std::vector<std::pair<std::string, std::string>>& headers,
|
||||
nghttp2_data_provider *data_prd);
|
||||
|
||||
int submit_push_promise(Request *req, const std::string& push_path);
|
||||
int submit_push_promise(Stream *stream, const std::string& push_path);
|
||||
|
||||
void add_stream(int32_t stream_id, std::unique_ptr<Request> req);
|
||||
int submit_rst_stream(Stream *stream, nghttp2_error_code error_code);
|
||||
|
||||
void add_stream(int32_t stream_id, std::unique_ptr<Stream> stream);
|
||||
void remove_stream(int32_t stream_id);
|
||||
Request* get_stream(int32_t stream_id);
|
||||
Stream* get_stream(int32_t stream_id);
|
||||
int64_t session_id() const;
|
||||
Sessions* get_sessions() const;
|
||||
const Config* get_config() const;
|
||||
@@ -121,16 +139,27 @@ public:
|
||||
void set_left_connhd_len(size_t left);
|
||||
void remove_settings_timer();
|
||||
void terminate_session(nghttp2_error_code error_code);
|
||||
int tls_handshake();
|
||||
void decide_compression(const std::string& path, Stream *stream);
|
||||
private:
|
||||
std::map<int32_t, std::unique_ptr<Request>> id2req_;
|
||||
int handle_ssl_temporal_error(int err);
|
||||
int tls_write(const uint8_t *data, size_t datalen);
|
||||
int tls_write_pending();
|
||||
int wait_events();
|
||||
|
||||
std::map<int32_t, std::unique_ptr<Stream>> id2stream_;
|
||||
nghttp2_buf sendbuf_;
|
||||
int64_t session_id_;
|
||||
nghttp2_session *session_;
|
||||
Sessions *sessions_;
|
||||
bufferevent *bev_;
|
||||
SSL* ssl_;
|
||||
event *rev_, *wev_;
|
||||
event *settings_timerev_;
|
||||
const uint8_t *pending_data_;
|
||||
size_t pending_datalen_;
|
||||
size_t left_connhd_len_;
|
||||
int fd_;
|
||||
uint8_t sendbufarray_[65536];
|
||||
};
|
||||
|
||||
class HttpServer {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# nghttp2 - HTTP/2.0 C Library
|
||||
# nghttp2 - HTTP/2 C Library
|
||||
|
||||
# Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
|
||||
@@ -36,6 +36,7 @@ AM_CPPFLAGS = \
|
||||
@LIBEVENT_OPENSSL_CFLAGS@ \
|
||||
@OPENSSL_CFLAGS@ \
|
||||
@JANSSON_CFLAGS@ \
|
||||
@ZLIB_CFLAGS@ \
|
||||
@DEFS@
|
||||
AM_LDFLAGS = \
|
||||
@LIBSPDYLAY_LIBS@ \
|
||||
@@ -43,6 +44,8 @@ AM_LDFLAGS = \
|
||||
@LIBEVENT_OPENSSL_LIBS@ \
|
||||
@OPENSSL_LIBS@ \
|
||||
@JANSSON_LIBS@ \
|
||||
@ZLIB_LIBS@ \
|
||||
@JEMALLOC_LIBS@ \
|
||||
@SRC_LIBS@
|
||||
|
||||
LDADD = \
|
||||
@@ -51,10 +54,11 @@ LDADD = \
|
||||
|
||||
if ENABLE_APP
|
||||
|
||||
bin_PROGRAMS += nghttp nghttpd nghttpx
|
||||
bin_PROGRAMS += nghttp nghttpd nghttpx h2load
|
||||
|
||||
HELPER_OBJECTS = util.cc http2.cc timegm.c app_helper.cc
|
||||
HELPER_HFILES = util.h http2.h timegm.h app_helper.h nghttp2_config.h
|
||||
HELPER_OBJECTS = util.cc http2.cc timegm.c app_helper.cc nghttp2_gzip.c
|
||||
HELPER_HFILES = util.h http2.h timegm.h app_helper.h nghttp2_config.h \
|
||||
nghttp2_gzip.h
|
||||
|
||||
HTML_PARSER_OBJECTS =
|
||||
HTML_PARSER_HFILES = HtmlParser.h
|
||||
@@ -67,10 +71,23 @@ nghttp_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} nghttp.cc \
|
||||
${HTML_PARSER_OBJECTS} ${HTML_PARSER_HFILES}
|
||||
|
||||
nghttpd_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} nghttpd.cc \
|
||||
ssl.cc ssl.h \
|
||||
HttpServer.cc HttpServer.h
|
||||
|
||||
h2load_SOURCES = util.cc util.h http2.cc http2.h h2load.cc h2load.h \
|
||||
timegm.c timegm.h \
|
||||
ssl.cc ssl.h \
|
||||
h2load_session.h \
|
||||
h2load_http2_session.cc h2load_http2_session.h
|
||||
|
||||
if HAVE_SPDYLAY
|
||||
h2load_SOURCES += h2load_spdy_session.cc h2load_spdy_session.h
|
||||
endif # HAVE_SPDYLAY
|
||||
|
||||
NGHTTPX_SRCS = \
|
||||
util.cc util.h http2.cc http2.h timegm.c timegm.h base64.h \
|
||||
app_helper.cc app_helper.h \
|
||||
ssl.cc ssl.h \
|
||||
shrpx_config.cc shrpx_config.h \
|
||||
shrpx_error.h \
|
||||
shrpx_listen_handler.cc shrpx_listen_handler.h \
|
||||
@@ -110,7 +127,9 @@ nghttpx_unittest_SOURCES = shrpx-unittest.cc \
|
||||
shrpx_downstream_test.cc shrpx_downstream_test.h \
|
||||
shrpx_config_test.cc shrpx_config_test.h \
|
||||
http2_test.cc http2_test.h \
|
||||
util_test.cc util_test.h
|
||||
util_test.cc util_test.h \
|
||||
nghttp2_gzip_test.c nghttp2_gzip_test.h \
|
||||
nghttp2_gzip.c nghttp2_gzip.h
|
||||
nghttpx_unittest_CPPFLAGS = ${AM_CPPFLAGS}\
|
||||
-DNGHTTP2_TESTS_DIR=\"$(top_srcdir)/tests\"
|
||||
nghttpx_unittest_LDFLAGS = -static
|
||||
@@ -128,8 +147,8 @@ bin_PROGRAMS += inflatehd deflatehd
|
||||
|
||||
HPACK_TOOLS_COMMON_SRCS = comp_helper.c comp_helper.h
|
||||
|
||||
inflatehd_SOURCES = inflatehd.c $(HPACK_TOOLS_COMMON_SRCS)
|
||||
inflatehd_SOURCES = inflatehd.cc $(HPACK_TOOLS_COMMON_SRCS)
|
||||
|
||||
deflatehd_SOURCES = deflatehd.c $(HPACK_TOOLS_COMMON_SRCS)
|
||||
deflatehd_SOURCES = deflatehd.cc $(HPACK_TOOLS_COMMON_SRCS)
|
||||
|
||||
endif # ENABLE_HPACK_TOOLS
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
*
|
||||
@@ -43,6 +43,8 @@
|
||||
#include <iomanip>
|
||||
#include <fstream>
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
#include "app_helper.h"
|
||||
#include "util.h"
|
||||
#include "http2.h"
|
||||
@@ -77,6 +79,8 @@ const char* strstatus(nghttp2_error_code error_code)
|
||||
return "CONNECT_ERROR";
|
||||
case NGHTTP2_ENHANCE_YOUR_CALM:
|
||||
return "ENHANCE_YOUR_CALM";
|
||||
case NGHTTP2_INADEQUATE_SECURITY:
|
||||
return "INADEQUATE_SECURITY";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
@@ -95,8 +99,8 @@ const char* strsettingsid(int32_t id)
|
||||
return "SETTINGS_MAX_CONCURRENT_STREAMS";
|
||||
case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
|
||||
return "SETTINGS_INITIAL_WINDOW_SIZE";
|
||||
case NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS:
|
||||
return "SETTINGS_FLOW_CONTROL_OPTIONS";
|
||||
case NGHTTP2_SETTINGS_COMPRESS_DATA:
|
||||
return "SETTINGS_COMPRESS_DATA";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
@@ -125,19 +129,16 @@ const char* strframetype(uint8_t type)
|
||||
return "GOAWAY";
|
||||
case NGHTTP2_WINDOW_UPDATE:
|
||||
return "WINDOW_UPDATE";
|
||||
case NGHTTP2_ALTSVC:
|
||||
return "ALTSVC";
|
||||
case NGHTTP2_BLOCKED:
|
||||
return "BLOCKED";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void print_frame_attr_indent()
|
||||
{
|
||||
printf(" ");
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
bool color_output = false;
|
||||
} // namespace
|
||||
@@ -147,6 +148,22 @@ void set_color_output(bool f)
|
||||
color_output = f;
|
||||
}
|
||||
|
||||
namespace {
|
||||
FILE *outfile = stdout;
|
||||
} // namespace
|
||||
|
||||
void set_output(FILE *file)
|
||||
{
|
||||
outfile = file;
|
||||
}
|
||||
|
||||
namespace {
|
||||
void print_frame_attr_indent()
|
||||
{
|
||||
fprintf(outfile, " ");
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
const char* ansi_esc(const char *code)
|
||||
{
|
||||
@@ -162,17 +179,22 @@ const char* ansi_escend()
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void print_nv(nghttp2_nv *nva, size_t nvlen, bool indent = true)
|
||||
void print_nv(nghttp2_nv *nv)
|
||||
{
|
||||
fprintf(outfile, "%s", ansi_esc("\033[1;34m"));
|
||||
fwrite(nv->name, nv->namelen, 1, outfile);
|
||||
fprintf(outfile, "%s: ", ansi_escend());
|
||||
fwrite(nv->value, nv->valuelen, 1, outfile);
|
||||
fprintf(outfile, "\n");
|
||||
}
|
||||
} // namespace
|
||||
namespace {
|
||||
void print_nv(nghttp2_nv *nva, size_t nvlen)
|
||||
{
|
||||
for(auto& nv : http2::sort_nva(nva, nvlen)) {
|
||||
if(indent) {
|
||||
print_frame_attr_indent();
|
||||
}
|
||||
printf("%s", ansi_esc("\033[1;34m"));
|
||||
fwrite(nv.name, nv.namelen, 1, stdout);
|
||||
printf("%s: ", ansi_escend());
|
||||
fwrite(nv.value, nv.valuelen, 1, stdout);
|
||||
printf("\n");
|
||||
|
||||
print_nv(&nv);
|
||||
}
|
||||
}
|
||||
} // namelen
|
||||
@@ -180,7 +202,7 @@ void print_nv(nghttp2_nv *nva, size_t nvlen, bool indent = true)
|
||||
void print_timer()
|
||||
{
|
||||
auto millis = get_timer();
|
||||
printf("%s[%3ld.%03ld]%s",
|
||||
fprintf(outfile, "%s[%3ld.%03ld]%s",
|
||||
ansi_esc("\033[33m"),
|
||||
(long int)(millis.count()/1000), (long int)(millis.count()%1000),
|
||||
ansi_escend());
|
||||
@@ -189,7 +211,7 @@ void print_timer()
|
||||
namespace {
|
||||
void print_frame_hd(const nghttp2_frame_hd& hd)
|
||||
{
|
||||
printf("<length=%zu, flags=0x%02x, stream_id=%d>\n",
|
||||
fprintf(outfile, "<length=%zu, flags=0x%02x, stream_id=%d>\n",
|
||||
hd.length, hd.flags, hd.stream_id);
|
||||
}
|
||||
} // namespace
|
||||
@@ -203,23 +225,68 @@ void print_flags(const nghttp2_frame_hd& hd)
|
||||
if(hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||
s += "END_STREAM";
|
||||
}
|
||||
if(hd.flags & NGHTTP2_FLAG_END_SEGMENT) {
|
||||
if(!s.empty()) {
|
||||
s += " | ";
|
||||
}
|
||||
s += "END_SEGMENT";
|
||||
}
|
||||
if(hd.flags & NGHTTP2_FLAG_PAD_LOW) {
|
||||
if(!s.empty()) {
|
||||
s += " | ";
|
||||
}
|
||||
s += "PAD_LOW";
|
||||
}
|
||||
if(hd.flags & NGHTTP2_FLAG_PAD_HIGH) {
|
||||
if(!s.empty()) {
|
||||
s += " | ";
|
||||
}
|
||||
s += "PAD_HIGH";
|
||||
}
|
||||
if(hd.flags & NGHTTP2_FLAG_COMPRESSED) {
|
||||
if(!s.empty()) {
|
||||
s += " | ";
|
||||
}
|
||||
s += "COMPRESSED";
|
||||
}
|
||||
break;
|
||||
case NGHTTP2_HEADERS:
|
||||
if(hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||
s += "END_STREAM";
|
||||
}
|
||||
if(hd.flags & NGHTTP2_FLAG_END_SEGMENT) {
|
||||
if(!s.empty()) {
|
||||
s += " | ";
|
||||
}
|
||||
s += "END_SEGMENT";
|
||||
}
|
||||
if(hd.flags & NGHTTP2_FLAG_END_HEADERS) {
|
||||
if(!s.empty()) {
|
||||
s += " | ";
|
||||
}
|
||||
s += "END_HEADERS";
|
||||
}
|
||||
if(hd.flags & NGHTTP2_FLAG_PAD_LOW) {
|
||||
if(!s.empty()) {
|
||||
s += " | ";
|
||||
}
|
||||
s += "PAD_LOW";
|
||||
}
|
||||
if(hd.flags & NGHTTP2_FLAG_PAD_HIGH) {
|
||||
if(!s.empty()) {
|
||||
s += " | ";
|
||||
}
|
||||
s += "PAD_HIGH";
|
||||
}
|
||||
if(hd.flags & NGHTTP2_FLAG_PRIORITY) {
|
||||
if(!s.empty()) {
|
||||
s += " | ";
|
||||
}
|
||||
s += "PRIORITY";
|
||||
}
|
||||
|
||||
break;
|
||||
case NGHTTP2_PRIORITY:
|
||||
break;
|
||||
case NGHTTP2_SETTINGS:
|
||||
if(hd.flags & NGHTTP2_FLAG_ACK) {
|
||||
@@ -227,8 +294,20 @@ void print_flags(const nghttp2_frame_hd& hd)
|
||||
}
|
||||
break;
|
||||
case NGHTTP2_PUSH_PROMISE:
|
||||
if(hd.flags & NGHTTP2_FLAG_END_PUSH_PROMISE) {
|
||||
s += "END_PUSH_PROMISE";
|
||||
if(hd.flags & NGHTTP2_FLAG_END_HEADERS) {
|
||||
s += "END_HEADERS";
|
||||
}
|
||||
if(hd.flags & NGHTTP2_FLAG_PAD_LOW) {
|
||||
if(!s.empty()) {
|
||||
s += " | ";
|
||||
}
|
||||
s += "PAD_LOW";
|
||||
}
|
||||
if(hd.flags & NGHTTP2_FLAG_PAD_HIGH) {
|
||||
if(!s.empty()) {
|
||||
s += " | ";
|
||||
}
|
||||
s += "PAD_HIGH";
|
||||
}
|
||||
break;
|
||||
case NGHTTP2_PING:
|
||||
@@ -237,7 +316,7 @@ void print_flags(const nghttp2_frame_hd& hd)
|
||||
}
|
||||
break;
|
||||
}
|
||||
printf("; %s\n", s.c_str());
|
||||
fprintf(outfile, "; %s\n", s.c_str());
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -253,10 +332,29 @@ const char* frame_name_ansi_esc(print_type ptype)
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
std::string ascii_dump(const uint8_t *data, size_t len)
|
||||
{
|
||||
std::string res;
|
||||
|
||||
for(size_t i = 0; i < len; ++i) {
|
||||
auto c = data[i];
|
||||
|
||||
if(c >= 0x20 && c < 0x7f) {
|
||||
res += c;
|
||||
} else {
|
||||
res += ".";
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void print_frame(print_type ptype, const nghttp2_frame *frame)
|
||||
{
|
||||
printf("%s%s%s frame ",
|
||||
fprintf(outfile, "%s%s%s frame ",
|
||||
frame_name_ansi_esc(ptype),
|
||||
strframetype(frame->hd.type),
|
||||
ansi_escend());
|
||||
@@ -267,24 +365,33 @@ void print_frame(print_type ptype, const nghttp2_frame *frame)
|
||||
}
|
||||
switch(frame->hd.type) {
|
||||
case NGHTTP2_DATA:
|
||||
if(frame->data.padlen > 0) {
|
||||
print_frame_attr_indent();
|
||||
fprintf(outfile, "(padlen=%zu)\n", frame->data.padlen);
|
||||
}
|
||||
break;
|
||||
case NGHTTP2_HEADERS:
|
||||
if(frame->hd.flags & NGHTTP2_FLAG_PRIORITY) {
|
||||
print_frame_attr_indent();
|
||||
printf("(pri=%d)\n", frame->headers.pri);
|
||||
fprintf(outfile, "(padlen=%zu", frame->headers.padlen);
|
||||
if(frame->hd.flags & NGHTTP2_FLAG_PRIORITY) {
|
||||
fprintf(outfile, ", stream_id=%d, weight=%u, exclusive=%d",
|
||||
frame->headers.pri_spec.stream_id,
|
||||
frame->headers.pri_spec.weight,
|
||||
frame->headers.pri_spec.exclusive);
|
||||
}
|
||||
fprintf(outfile, ")\n");
|
||||
switch(frame->headers.cat) {
|
||||
case NGHTTP2_HCAT_REQUEST:
|
||||
print_frame_attr_indent();
|
||||
printf("; Open new stream\n");
|
||||
fprintf(outfile, "; Open new stream\n");
|
||||
break;
|
||||
case NGHTTP2_HCAT_RESPONSE:
|
||||
print_frame_attr_indent();
|
||||
printf("; First response header\n");
|
||||
fprintf(outfile, "; First response header\n");
|
||||
break;
|
||||
case NGHTTP2_HCAT_PUSH_RESPONSE:
|
||||
print_frame_attr_indent();
|
||||
printf("; First push response header\n");
|
||||
fprintf(outfile, "; First push response header\n");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -293,20 +400,26 @@ void print_frame(print_type ptype, const nghttp2_frame *frame)
|
||||
break;
|
||||
case NGHTTP2_PRIORITY:
|
||||
print_frame_attr_indent();
|
||||
printf("(pri=%d)\n", frame->priority.pri);
|
||||
|
||||
fprintf(outfile, "(stream_id=%d, weight=%u, exclusive=%d)\n",
|
||||
frame->priority.pri_spec.stream_id,
|
||||
frame->priority.pri_spec.weight,
|
||||
frame->priority.pri_spec.exclusive);
|
||||
|
||||
break;
|
||||
case NGHTTP2_RST_STREAM:
|
||||
print_frame_attr_indent();
|
||||
printf("(error_code=%s(%u))\n",
|
||||
fprintf(outfile, "(error_code=%s(%u))\n",
|
||||
strstatus(frame->rst_stream.error_code),
|
||||
frame->rst_stream.error_code);
|
||||
break;
|
||||
case NGHTTP2_SETTINGS:
|
||||
print_frame_attr_indent();
|
||||
printf("(niv=%lu)\n", static_cast<unsigned long>(frame->settings.niv));
|
||||
fprintf(outfile, "(niv=%lu)\n",
|
||||
static_cast<unsigned long>(frame->settings.niv));
|
||||
for(size_t i = 0; i < frame->settings.niv; ++i) {
|
||||
print_frame_attr_indent();
|
||||
printf("[%s(%d):%u]\n",
|
||||
fprintf(outfile, "[%s(%d):%u]\n",
|
||||
strsettingsid(frame->settings.iv[i].settings_id),
|
||||
frame->settings.iv[i].settings_id,
|
||||
frame->settings.iv[i].value);
|
||||
@@ -314,32 +427,52 @@ void print_frame(print_type ptype, const nghttp2_frame *frame)
|
||||
break;
|
||||
case NGHTTP2_PUSH_PROMISE:
|
||||
print_frame_attr_indent();
|
||||
printf("(promised_stream_id=%d)\n",
|
||||
fprintf(outfile, "(padlen=%zu, promised_stream_id=%d)\n",
|
||||
frame->push_promise.padlen,
|
||||
frame->push_promise.promised_stream_id);
|
||||
print_nv(frame->push_promise.nva, frame->push_promise.nvlen);
|
||||
break;
|
||||
case NGHTTP2_PING:
|
||||
print_frame_attr_indent();
|
||||
printf("(opaque_data=%s)\n",
|
||||
fprintf(outfile, "(opaque_data=%s)\n",
|
||||
util::format_hex(frame->ping.opaque_data, 8).c_str());
|
||||
break;
|
||||
case NGHTTP2_GOAWAY:
|
||||
print_frame_attr_indent();
|
||||
printf("(last_stream_id=%d, error_code=%s(%u), opaque_data(%u)=[%s])\n",
|
||||
fprintf(outfile,
|
||||
"(last_stream_id=%d, error_code=%s(%u), opaque_data(%u)=[%s])\n",
|
||||
frame->goaway.last_stream_id,
|
||||
strstatus(frame->goaway.error_code),
|
||||
frame->goaway.error_code,
|
||||
static_cast<unsigned int>(frame->goaway.opaque_data_len),
|
||||
util::format_hex(frame->goaway.opaque_data,
|
||||
ascii_dump(frame->goaway.opaque_data,
|
||||
frame->goaway.opaque_data_len).c_str());
|
||||
break;
|
||||
case NGHTTP2_WINDOW_UPDATE:
|
||||
print_frame_attr_indent();
|
||||
printf("(window_size_increment=%d)\n",
|
||||
fprintf(outfile, "(window_size_increment=%d)\n",
|
||||
frame->window_update.window_size_increment);
|
||||
break;
|
||||
case NGHTTP2_ALTSVC:
|
||||
print_frame_attr_indent();
|
||||
fprintf(outfile, "(max-age=%u, port=%u, protocol_id=",
|
||||
frame->altsvc.max_age, frame->altsvc.port);
|
||||
if(frame->altsvc.protocol_id_len) {
|
||||
fwrite(frame->altsvc.protocol_id, frame->altsvc.protocol_id_len, 1,
|
||||
outfile);
|
||||
}
|
||||
fprintf(outfile, ", host=");
|
||||
if(frame->altsvc.host_len) {
|
||||
fwrite(frame->altsvc.host, frame->altsvc.host_len, 1, outfile);
|
||||
}
|
||||
fprintf(outfile, ", origin=");
|
||||
if(frame->altsvc.origin_len) {
|
||||
fwrite(frame->altsvc.origin, frame->altsvc.origin_len, 1, outfile);
|
||||
}
|
||||
fprintf(outfile, ")\n");
|
||||
|
||||
break;
|
||||
default:
|
||||
printf("\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -349,15 +482,22 @@ int verbose_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)
|
||||
{
|
||||
nghttp2_nv nv = {
|
||||
nghttp2_nv nva = {
|
||||
const_cast<uint8_t*>(name), const_cast<uint8_t*>(value),
|
||||
static_cast<uint16_t>(namelen), static_cast<uint16_t>(valuelen)
|
||||
namelen, valuelen
|
||||
};
|
||||
|
||||
for(auto& nv : http2::sort_nva(&nva, 1)) {
|
||||
print_timer();
|
||||
printf(" (stream_id=%d) ", frame->hd.stream_id);
|
||||
print_nv(&nv, 1, false /* no indent */);
|
||||
fprintf(outfile, " (stream_id=%d, noind=%d) ", frame->hd.stream_id,
|
||||
(flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0);
|
||||
|
||||
print_nv(&nv);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -365,9 +505,9 @@ int verbose_on_frame_recv_callback
|
||||
(nghttp2_session *session, const nghttp2_frame *frame, void *user_data)
|
||||
{
|
||||
print_timer();
|
||||
printf(" recv ");
|
||||
fprintf(outfile, " recv ");
|
||||
print_frame(PRINT_RECV, frame);
|
||||
fflush(stdout);
|
||||
fflush(outfile);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -376,9 +516,9 @@ int verbose_on_invalid_frame_recv_callback
|
||||
nghttp2_error_code error_code, void *user_data)
|
||||
{
|
||||
print_timer();
|
||||
printf(" [INVALID; status=%s] recv ", strstatus(error_code));
|
||||
fprintf(outfile, " [INVALID; status=%s] recv ", strstatus(error_code));
|
||||
print_frame(PRINT_RECV, frame);
|
||||
fflush(stdout);
|
||||
fflush(outfile);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -387,11 +527,11 @@ void dump_header(const uint8_t *head, size_t headlen)
|
||||
{
|
||||
size_t i;
|
||||
print_frame_attr_indent();
|
||||
printf("Header dump: ");
|
||||
fprintf(outfile, "Header dump: ");
|
||||
for(i = 0; i < headlen; ++i) {
|
||||
printf("%02X ", head[i]);
|
||||
fprintf(outfile, "%02X ", head[i]);
|
||||
}
|
||||
printf("\n");
|
||||
fprintf(outfile, "\n");
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -403,9 +543,9 @@ int verbose_on_unknown_frame_recv_callback(nghttp2_session *session,
|
||||
void *user_data)
|
||||
{
|
||||
print_timer();
|
||||
printf(" recv unknown frame\n");
|
||||
fprintf(outfile, " recv unknown frame\n");
|
||||
dump_header(head, headlen);
|
||||
fflush(stdout);
|
||||
fflush(outfile);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -413,9 +553,9 @@ int verbose_on_frame_send_callback
|
||||
(nghttp2_session *session, const nghttp2_frame *frame, void *user_data)
|
||||
{
|
||||
print_timer();
|
||||
printf(" send ");
|
||||
fprintf(outfile, " send ");
|
||||
print_frame(PRINT_SEND, frame);
|
||||
fflush(stdout);
|
||||
fflush(outfile);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -438,4 +578,48 @@ std::chrono::steady_clock::time_point get_time()
|
||||
return std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
ssize_t deflate_data(uint8_t *out, size_t outlen,
|
||||
const uint8_t *in, size_t inlen)
|
||||
{
|
||||
int rv;
|
||||
z_stream zst;
|
||||
uint8_t temp_out[8192];
|
||||
auto temp_outlen = sizeof(temp_out);
|
||||
|
||||
zst.next_in = Z_NULL;
|
||||
zst.zalloc = Z_NULL;
|
||||
zst.zfree = Z_NULL;
|
||||
zst.opaque = Z_NULL;
|
||||
|
||||
rv = deflateInit2(&zst, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
|
||||
31, 9, Z_DEFAULT_STRATEGY);
|
||||
|
||||
if(rv != Z_OK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
zst.avail_in = inlen;
|
||||
zst.next_in = (uint8_t*)in;
|
||||
zst.avail_out = temp_outlen;
|
||||
zst.next_out = temp_out;
|
||||
|
||||
rv = deflate(&zst, Z_FINISH);
|
||||
|
||||
deflateEnd(&zst);
|
||||
|
||||
if(rv != Z_STREAM_END) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
temp_outlen -= zst.avail_out;
|
||||
|
||||
if(temp_outlen > outlen) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(out, temp_out, temp_outlen);
|
||||
|
||||
return temp_outlen;
|
||||
}
|
||||
|
||||
} // namespace nghttp2
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
*
|
||||
@@ -43,6 +43,7 @@ int verbose_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);
|
||||
|
||||
int verbose_on_frame_recv_callback
|
||||
@@ -86,6 +87,13 @@ void print_timer();
|
||||
// variable.
|
||||
void set_color_output(bool f);
|
||||
|
||||
// Set output file when printing HTTP2 frames. By default, stdout is
|
||||
// used.
|
||||
void set_output(FILE *file);
|
||||
|
||||
ssize_t deflate_data(uint8_t *out, size_t outlen,
|
||||
const uint8_t *in, size_t inlen);
|
||||
|
||||
} // namespace nghttp2
|
||||
|
||||
#endif // APP_HELPER_H
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2013 Tatsuhiro Tsujikawa
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2013 Tatsuhiro Tsujikawa
|
||||
*
|
||||
@@ -27,12 +27,8 @@
|
||||
|
||||
static void dump_val(json_t *jent, const char *key, uint8_t *val, size_t len)
|
||||
{
|
||||
if(val == NULL && len > 0) {
|
||||
json_object_set_new(jent, key, json_string("**DEALLOCATED**"));
|
||||
} else {
|
||||
json_object_set_new(jent, key, json_pack("s#", val, len));
|
||||
}
|
||||
}
|
||||
|
||||
json_t* dump_header_table(nghttp2_hd_context *context)
|
||||
{
|
||||
@@ -58,12 +54,6 @@ json_t* dump_header_table(nghttp2_hd_context *context)
|
||||
json_object_set_new(obj, "size", json_integer(context->hd_table_bufsize));
|
||||
json_object_set_new(obj, "max_size",
|
||||
json_integer(context->hd_table_bufsize_max));
|
||||
if(context->role == NGHTTP2_HD_ROLE_DEFLATE) {
|
||||
json_object_set_new(obj, "deflate_size",
|
||||
json_integer(context->deflate_hd_table_bufsize));
|
||||
json_object_set_new(obj, "max_deflate_size",
|
||||
json_integer(context->deflate_hd_table_bufsize_max));
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
@@ -93,13 +83,11 @@ json_t* dump_headers(const nghttp2_nv *nva, size_t nvlen)
|
||||
return headers;
|
||||
}
|
||||
|
||||
void output_json_header(int side)
|
||||
void output_json_header(void)
|
||||
{
|
||||
printf("{\n"
|
||||
" \"context\": \"%s\",\n"
|
||||
" \"cases\":\n"
|
||||
" [\n",
|
||||
(side == NGHTTP2_HD_SIDE_REQUEST ? "request" : "response"));
|
||||
" [\n");
|
||||
}
|
||||
|
||||
void output_json_footer(void)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2013 Tatsuhiro Tsujikawa
|
||||
*
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
#endif // HAVE_CONFIG_H
|
||||
|
||||
#include <jansson.h>
|
||||
|
||||
@@ -40,8 +40,8 @@ json_t* dump_header(const uint8_t *name, size_t namelen,
|
||||
|
||||
json_t* dump_headers(const nghttp2_nv *nva, size_t nvlen);
|
||||
|
||||
void output_json_header(int side);
|
||||
void output_json_header(void);
|
||||
|
||||
void output_json_footer(void);
|
||||
|
||||
#endif /* NGHTTP2_COMP_HELPER_H */
|
||||
#endif // NGHTTP2_COMP_HELPER_H
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2013 Tatsuhiro Tsujikawa
|
||||
*
|
||||
@@ -24,27 +24,33 @@
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
#endif // HAVE_CONFIG_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
#include <jansson.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
#include "nghttp2_hd.h"
|
||||
#include "nghttp2_frame.h"
|
||||
|
||||
#include "comp_helper.h"
|
||||
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
size_t table_size;
|
||||
size_t deflate_table_size;
|
||||
nghttp2_hd_side side;
|
||||
int http1text;
|
||||
int dump_header_table;
|
||||
int no_refset;
|
||||
@@ -73,31 +79,39 @@ static void to_hex(char *dest, const uint8_t *src, size_t len)
|
||||
}
|
||||
|
||||
static void output_to_json(nghttp2_hd_deflater *deflater,
|
||||
const uint8_t *buf, size_t len, size_t inputlen,
|
||||
nghttp2_nv *nva, size_t nvlen,
|
||||
nghttp2_bufs *bufs, size_t inputlen,
|
||||
const std::vector<nghttp2_nv> nva,
|
||||
int seq)
|
||||
{
|
||||
json_t *obj;
|
||||
char *hex = NULL;
|
||||
auto len = nghttp2_bufs_len(bufs);
|
||||
auto hex = std::vector<char>(len * 2);
|
||||
auto obj = json_object();
|
||||
auto comp_ratio = inputlen == 0 ? 0.0 : (double)len / inputlen * 100;
|
||||
|
||||
if(len > 0) {
|
||||
hex = malloc(len * 2);
|
||||
}
|
||||
obj = json_object();
|
||||
json_object_set_new(obj, "seq", json_integer(seq));
|
||||
json_object_set_new(obj, "input_length", json_integer(inputlen));
|
||||
json_object_set_new(obj, "output_length", json_integer(len));
|
||||
json_object_set_new(obj, "percentage_of_original_size",
|
||||
json_real((double)len / inputlen * 100));
|
||||
to_hex(hex, buf, len);
|
||||
json_real(comp_ratio));
|
||||
|
||||
auto hexp = hex.data();
|
||||
for(auto ci = bufs->head; ci; ci = ci->next) {
|
||||
auto buf = &ci->buf;
|
||||
to_hex(hexp, buf->pos, nghttp2_buf_len(buf));
|
||||
hexp += nghttp2_buf_len(buf);
|
||||
}
|
||||
|
||||
if(len == 0) {
|
||||
json_object_set_new(obj, "wire", json_string(""));
|
||||
} else {
|
||||
json_object_set_new(obj, "wire", json_pack("s#", hex, len * 2));
|
||||
json_object_set_new(obj, "wire", json_pack("s#", hex.data(), hex.size()));
|
||||
}
|
||||
json_object_set_new(obj, "headers", dump_headers(nva, nvlen));
|
||||
json_object_set_new(obj, "headers", dump_headers(nva.data(), nva.size()));
|
||||
if(seq == 0) {
|
||||
// We only change the header table size only once at the beginning
|
||||
json_object_set_new(obj, "header_table_size",
|
||||
json_integer(config.table_size));
|
||||
}
|
||||
if(config.dump_header_table) {
|
||||
json_object_set_new(obj, "header_table",
|
||||
dump_header_table(&deflater->ctx));
|
||||
@@ -105,36 +119,37 @@ static void output_to_json(nghttp2_hd_deflater *deflater,
|
||||
json_dumpf(obj, stdout, JSON_PRESERVE_ORDER | JSON_INDENT(2));
|
||||
printf("\n");
|
||||
json_decref(obj);
|
||||
free(hex);
|
||||
}
|
||||
|
||||
static void deflate_hd(nghttp2_hd_deflater *deflater,
|
||||
nghttp2_nv *nva, size_t nvlen, size_t inputlen, int seq)
|
||||
const std::vector<nghttp2_nv>& nva,
|
||||
size_t inputlen, int seq)
|
||||
{
|
||||
ssize_t rv;
|
||||
uint8_t *buf = NULL;
|
||||
size_t buflen = 0;
|
||||
rv = nghttp2_hd_deflate_hd(deflater, &buf, &buflen, 0, nva, nvlen);
|
||||
nghttp2_bufs bufs;
|
||||
|
||||
nghttp2_bufs_init2(&bufs, 4096, 16, 0);
|
||||
|
||||
rv = nghttp2_hd_deflate_hd_bufs(deflater, &bufs,
|
||||
(nghttp2_nv*)nva.data(), nva.size());
|
||||
if(rv < 0) {
|
||||
fprintf(stderr, "deflate failed with error code %zd at %d\n", rv, seq);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
input_sum += inputlen;
|
||||
output_sum += rv;
|
||||
output_to_json(deflater, buf, rv, inputlen, nva, nvlen, seq);
|
||||
free(buf);
|
||||
output_sum += nghttp2_bufs_len(&bufs);
|
||||
|
||||
output_to_json(deflater, &bufs, inputlen, nva, seq);
|
||||
nghttp2_bufs_free(&bufs);
|
||||
}
|
||||
|
||||
static int deflate_hd_json(json_t *obj, nghttp2_hd_deflater *deflater, int seq)
|
||||
{
|
||||
json_t *js;
|
||||
nghttp2_nv nva[128];
|
||||
size_t len;
|
||||
size_t i;
|
||||
size_t inputlen = 0;
|
||||
|
||||
js = json_object_get(obj, "headers");
|
||||
if(js == NULL) {
|
||||
auto js = json_object_get(obj, "headers");
|
||||
if(js == nullptr) {
|
||||
fprintf(stderr, "'headers' key is missing at %d\n", seq);
|
||||
return -1;
|
||||
}
|
||||
@@ -143,20 +158,20 @@ static int deflate_hd_json(json_t *obj, nghttp2_hd_deflater *deflater, int seq)
|
||||
"The value of 'headers' key must be an array at %d\n", seq);
|
||||
return -1;
|
||||
}
|
||||
len = json_array_size(js);
|
||||
if(len > sizeof(nva)/sizeof(nva[0])) {
|
||||
fprintf(stderr, "Too many headers (> %zu) at %d\n",
|
||||
sizeof(nva)/sizeof(nva[0]), seq);
|
||||
return -1;
|
||||
}
|
||||
for(i = 0; i < len; ++i) {
|
||||
json_t *nv_pair = json_array_get(js, i);
|
||||
|
||||
auto len = json_array_size(js);
|
||||
auto nva = std::vector<nghttp2_nv>(len);
|
||||
|
||||
for(size_t i = 0; i < len; ++i) {
|
||||
auto nv_pair = json_array_get(js, i);
|
||||
const char *name;
|
||||
json_t *value;
|
||||
|
||||
if(!json_is_object(nv_pair) || json_object_size(nv_pair) != 1) {
|
||||
fprintf(stderr, "bad formatted name/value pair object at %d\n", seq);
|
||||
return -1;
|
||||
}
|
||||
|
||||
json_object_foreach(nv_pair, name, value) {
|
||||
nva[i].name = (uint8_t*)name;
|
||||
nva[i].namelen = strlen(name);
|
||||
@@ -165,20 +180,26 @@ static int deflate_hd_json(json_t *obj, nghttp2_hd_deflater *deflater, int seq)
|
||||
fprintf(stderr, "value is not string at %d\n", seq);
|
||||
return -1;
|
||||
}
|
||||
|
||||
nva[i].value = (uint8_t*)json_string_value(value);
|
||||
nva[i].valuelen = strlen(json_string_value(value));
|
||||
|
||||
nva[i].flags = NGHTTP2_NV_FLAG_NONE;
|
||||
}
|
||||
|
||||
inputlen += nva[i].namelen + nva[i].valuelen;
|
||||
}
|
||||
deflate_hd(deflater, nva, len, inputlen, seq);
|
||||
|
||||
deflate_hd(deflater, nva, inputlen, seq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void init_deflater(nghttp2_hd_deflater *deflater, nghttp2_hd_side side)
|
||||
static void init_deflater(nghttp2_hd_deflater *deflater)
|
||||
{
|
||||
nghttp2_hd_deflate_init2(deflater, side, config.deflate_table_size);
|
||||
nghttp2_hd_deflate_init2(deflater, config.deflate_table_size);
|
||||
nghttp2_hd_deflate_set_no_refset(deflater, config.no_refset);
|
||||
nghttp2_hd_change_table_size(&deflater->ctx, config.table_size);
|
||||
nghttp2_hd_deflate_change_table_size(deflater, config.table_size);
|
||||
}
|
||||
|
||||
static void deinit_deflater(nghttp2_hd_deflater *deflater)
|
||||
@@ -188,38 +209,34 @@ static void deinit_deflater(nghttp2_hd_deflater *deflater)
|
||||
|
||||
static int perform(void)
|
||||
{
|
||||
size_t i;
|
||||
json_t *json, *cases;
|
||||
json_error_t error;
|
||||
size_t len;
|
||||
nghttp2_hd_deflater deflater;
|
||||
nghttp2_hd_side side;
|
||||
|
||||
json = json_loadf(stdin, 0, &error);
|
||||
if(json == NULL) {
|
||||
auto json = json_loadf(stdin, 0, &error);
|
||||
|
||||
if(json == nullptr) {
|
||||
fprintf(stderr, "JSON loading failed\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if(strcmp("request", json_string_value(json_object_get(json, "context")))
|
||||
== 0) {
|
||||
side = NGHTTP2_HD_SIDE_REQUEST;
|
||||
} else {
|
||||
side = NGHTTP2_HD_SIDE_RESPONSE;
|
||||
}
|
||||
cases = json_object_get(json, "cases");
|
||||
if(cases == NULL) {
|
||||
|
||||
auto cases = json_object_get(json, "cases");
|
||||
|
||||
if(cases == nullptr) {
|
||||
fprintf(stderr, "Missing 'cases' key in root object\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if(!json_is_array(cases)) {
|
||||
fprintf(stderr, "'cases' must be JSON array\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
init_deflater(&deflater, side);
|
||||
output_json_header(side);
|
||||
len = json_array_size(cases);
|
||||
for(i = 0; i < len; ++i) {
|
||||
json_t *obj = json_array_get(cases, i);
|
||||
|
||||
init_deflater(&deflater);
|
||||
output_json_header();
|
||||
auto len = json_array_size(cases);
|
||||
|
||||
for(size_t i = 0; i < len; ++i) {
|
||||
auto obj = json_array_get(cases, i);
|
||||
if(!json_is_object(obj)) {
|
||||
fprintf(stderr, "Unexpected JSON type at %zu. It should be object.\n",
|
||||
i);
|
||||
@@ -241,11 +258,11 @@ static int perform(void)
|
||||
static int perform_from_http1text(void)
|
||||
{
|
||||
char line[1 << 14];
|
||||
nghttp2_nv nva[256];
|
||||
std::vector<nghttp2_nv> nva;
|
||||
int seq = 0;
|
||||
nghttp2_hd_deflater deflater;
|
||||
init_deflater(&deflater, config.side);
|
||||
output_json_header(config.side);
|
||||
init_deflater(&deflater);
|
||||
output_json_header();
|
||||
for(;;) {
|
||||
size_t nvlen = 0;
|
||||
int end = 0;
|
||||
@@ -255,16 +272,18 @@ static int perform_from_http1text(void)
|
||||
nghttp2_nv *nv;
|
||||
char *rv = fgets(line, sizeof(line), stdin);
|
||||
char *val, *val_end;
|
||||
if(rv == NULL) {
|
||||
if(rv == nullptr) {
|
||||
end = 1;
|
||||
break;
|
||||
} else if(line[0] == '\n') {
|
||||
break;
|
||||
}
|
||||
assert(nvlen < sizeof(nva)/sizeof(nva[0]));
|
||||
|
||||
nva.resize(nvlen);
|
||||
|
||||
nv = &nva[nvlen];
|
||||
val = strchr(line+1, ':');
|
||||
if(val == NULL) {
|
||||
if(val == nullptr) {
|
||||
fprintf(stderr, "Bad HTTP/1 header field format at %d.\n", seq);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
@@ -274,11 +293,13 @@ static int perform_from_http1text(void)
|
||||
for(val_end = val; *val_end && (*val_end != '\r' && *val_end != '\n');
|
||||
++val_end);
|
||||
*val_end = '\0';
|
||||
/* printf("[%s] : [%s]\n", line, val); */
|
||||
|
||||
nv->namelen = strlen(line);
|
||||
nv->valuelen = strlen(val);
|
||||
nv->name = (uint8_t*)strdup(line);
|
||||
nv->value = (uint8_t*)strdup(val);
|
||||
nv->flags = NGHTTP2_NV_FLAG_NONE;
|
||||
|
||||
++nvlen;
|
||||
inputlen += nv->namelen + nv->valuelen;
|
||||
}
|
||||
@@ -287,7 +308,7 @@ static int perform_from_http1text(void)
|
||||
if(seq > 0) {
|
||||
printf(",\n");
|
||||
}
|
||||
deflate_hd(&deflater, nva, nvlen, inputlen, seq);
|
||||
deflate_hd(&deflater, nva, inputlen, seq);
|
||||
}
|
||||
|
||||
for(i = 0; i < nvlen; ++i) {
|
||||
@@ -304,93 +325,88 @@ static int perform_from_http1text(void)
|
||||
|
||||
static void print_help(void)
|
||||
{
|
||||
printf("HPACK HTTP/2.0 header encoder\n"
|
||||
"Usage: deflatehd [OPTIONS] < INPUT\n"
|
||||
"\n"
|
||||
"Reads JSON data or HTTP/1-style header fields from stdin and\n"
|
||||
"outputs deflated header block in JSON array.\n"
|
||||
"\n"
|
||||
"For the JSON input, the root JSON object must contain \"context\"\n"
|
||||
"key, which indicates which compression context is used. If it is\n"
|
||||
"\"request\", request compression context is used. Otherwise,\n"
|
||||
"response compression context is used. The value of \"cases\" key\n"
|
||||
"contains the sequence of input header set. They share the same\n"
|
||||
"compression context and are processed in the order they appear.\n"
|
||||
"Each item in the sequence is a JSON object and it must have at\n"
|
||||
"least \"headers\" key. Its value is an array of a JSON object\n"
|
||||
"containing exactly one name/value pair.\n"
|
||||
"\n"
|
||||
"Example:\n"
|
||||
"{\n"
|
||||
" \"context\": \"request\",\n"
|
||||
" \"cases\":\n"
|
||||
" [\n"
|
||||
" {\n"
|
||||
" \"headers\": [\n"
|
||||
" { \":method\": \"GET\" },\n"
|
||||
" { \":path\": \"/\" }\n"
|
||||
" ]\n"
|
||||
" },\n"
|
||||
" {\n"
|
||||
" \"headers\": [\n"
|
||||
" { \":method\": \"POST\" },\n"
|
||||
" { \":path\": \"/\" }\n"
|
||||
" ]\n"
|
||||
" }\n"
|
||||
" ]\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"With -t option, the program can accept more familiar HTTP/1 style\n"
|
||||
"header field block. Each header set must be followed by one empty\n"
|
||||
"line:\n"
|
||||
"\n"
|
||||
"Example:\n"
|
||||
":method: GET\n"
|
||||
":scheme: https\n"
|
||||
":path: /\n"
|
||||
"\n"
|
||||
":method: POST\n"
|
||||
"user-agent: nghttp2\n"
|
||||
"\n"
|
||||
"The output of this program can be used as input for inflatehd.\n"
|
||||
"\n"
|
||||
"OPTIONS:\n"
|
||||
" -r, --response Use response compression context instead of\n"
|
||||
" request if -t is used. For JSON input, it is\n"
|
||||
" determined by inspecting \"context\" key in\n"
|
||||
" root JSON object.\n"
|
||||
" -t, --http1text Use HTTP/1 style header field text as input.\n"
|
||||
" Each header set is delimited by single empty\n"
|
||||
" line.\n"
|
||||
" -s, --table-size=<N>\n"
|
||||
" Set dynamic table size. In the HPACK\n"
|
||||
" specification, this value is denoted by\n"
|
||||
" SETTINGS_HEADER_TABLE_SIZE.\n"
|
||||
" Default: 4096\n"
|
||||
" -S, --deflate-table-size=<N>\n"
|
||||
" Use first N bytes of dynamic header table\n"
|
||||
" buffer.\n"
|
||||
" Default: 4096\n"
|
||||
" -d, --dump-header-table\n"
|
||||
" Output dynamic header table.\n"
|
||||
" -c, --no-refset Don't use reference set.\n");
|
||||
std::cout << R"(HPACK HTTP/2 header encoder
|
||||
Usage: deflatehd [OPTIONS] < INPUT
|
||||
|
||||
Reads JSON data or HTTP/1-style header fields from stdin and outputs
|
||||
deflated header block in JSON array.
|
||||
|
||||
For the JSON input, the root JSON object must contain "context" key,
|
||||
which indicates which compression context is used. If it is
|
||||
"request", request compression context is used. Otherwise, response
|
||||
compression context is used. The value of "cases" key contains the
|
||||
sequence of input header set. They share the same compression context
|
||||
and are processed in the order they appear. Each item in the sequence
|
||||
is a JSON object and it must have at least "headers" key. Its value
|
||||
is an array of a JSON object containing exactly one name/value pair.
|
||||
|
||||
Example:
|
||||
{
|
||||
"context": "request",
|
||||
"cases":
|
||||
[
|
||||
{
|
||||
"headers": [
|
||||
{ ":method": "GET" },
|
||||
{ ":path": "/" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"headers": [
|
||||
{ ":method": "POST" },
|
||||
{ ":path": "/" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
With -t option, the program can accept more familiar HTTP/1 style
|
||||
header field block. Each header set must be followed by one empty
|
||||
line:
|
||||
|
||||
Example:
|
||||
|
||||
:method: GET
|
||||
:scheme: https
|
||||
:path: /
|
||||
|
||||
:method: POST
|
||||
user-agent: nghttp2
|
||||
|
||||
The output of this program can be used as input for inflatehd.
|
||||
|
||||
OPTIONS:
|
||||
-t, --http1text Use HTTP/1 style header field text as input.
|
||||
Each header set is delimited by single empty
|
||||
line.
|
||||
-s, --table-size=<N>
|
||||
Set dynamic table size. In the HPACK
|
||||
specification, this value is denoted by
|
||||
SETTINGS_HEADER_TABLE_SIZE.
|
||||
Default: 4096
|
||||
-S, --deflate-table-size=<N>
|
||||
Use first N bytes of dynamic header table
|
||||
buffer.
|
||||
Default: 4096
|
||||
-d, --dump-header-table
|
||||
Output dynamic header table.
|
||||
-c, --no-refset Don't use reference set.)"
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
static struct option long_options[] = {
|
||||
{"response", no_argument, NULL, 'r'},
|
||||
{"http1text", no_argument, NULL, 't'},
|
||||
{"table-size", required_argument, NULL, 's'},
|
||||
{"deflate-table-size", required_argument, NULL, 'S'},
|
||||
{"dump-header-table", no_argument, NULL, 'd'},
|
||||
{"no-refset", no_argument, NULL, 'c'},
|
||||
{NULL, 0, NULL, 0 }
|
||||
{"http1text", no_argument, nullptr, 't'},
|
||||
{"table-size", required_argument, nullptr, 's'},
|
||||
{"deflate-table-size", required_argument, nullptr, 'S'},
|
||||
{"dump-header-table", no_argument, nullptr, 'd'},
|
||||
{"no-refset", no_argument, nullptr, 'c'},
|
||||
{nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char *end;
|
||||
|
||||
config.side = NGHTTP2_HD_SIDE_REQUEST;
|
||||
config.table_size = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE;
|
||||
config.deflate_table_size = NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE;
|
||||
config.http1text = 0;
|
||||
@@ -398,24 +414,20 @@ int main(int argc, char **argv)
|
||||
config.no_refset = 0;
|
||||
while(1) {
|
||||
int option_index = 0;
|
||||
int c = getopt_long(argc, argv, "S:cdhrs:t", long_options, &option_index);
|
||||
int c = getopt_long(argc, argv, "S:cdhs:t", long_options, &option_index);
|
||||
if(c == -1) {
|
||||
break;
|
||||
}
|
||||
switch(c) {
|
||||
case 'r':
|
||||
/* --response */
|
||||
config.side = NGHTTP2_HD_SIDE_RESPONSE;
|
||||
break;
|
||||
case 'h':
|
||||
print_help();
|
||||
exit(EXIT_SUCCESS);
|
||||
case 't':
|
||||
/* --http1text */
|
||||
// --http1text
|
||||
config.http1text = 1;
|
||||
break;
|
||||
case 's':
|
||||
/* --table-size */
|
||||
// --table-size
|
||||
errno = 0;
|
||||
config.table_size = strtoul(optarg, &end, 10);
|
||||
if(errno == ERANGE || *end != '\0') {
|
||||
@@ -424,7 +436,7 @@ int main(int argc, char **argv)
|
||||
}
|
||||
break;
|
||||
case 'S':
|
||||
/* --deflate-table-size */
|
||||
// --deflate-table-size
|
||||
errno = 0;
|
||||
config.deflate_table_size = strtoul(optarg, &end, 10);
|
||||
if(errno == ERANGE || *end != '\0') {
|
||||
@@ -433,11 +445,11 @@ int main(int argc, char **argv)
|
||||
}
|
||||
break;
|
||||
case 'd':
|
||||
/* --dump-header-table */
|
||||
// --dump-header-table
|
||||
config.dump_header_table = 1;
|
||||
break;
|
||||
case 'c':
|
||||
/* --no-refset */
|
||||
// --no-refset
|
||||
config.no_refset = 1;
|
||||
break;
|
||||
case '?':
|
||||
@@ -451,7 +463,10 @@ int main(int argc, char **argv)
|
||||
} else {
|
||||
perform();
|
||||
}
|
||||
|
||||
auto comp_ratio = input_sum == 0 ? 0.0 : (double)output_sum / input_sum;
|
||||
|
||||
fprintf(stderr, "Overall: input=%zu output=%zu ratio=%.02f\n",
|
||||
input_sum, output_sum, (double)output_sum / input_sum);
|
||||
input_sum, output_sum, comp_ratio);
|
||||
return 0;
|
||||
}
|
||||
992
src/h2load.cc
Normal file
992
src/h2load.cc
Normal file
@@ -0,0 +1,992 @@
|
||||
/*
|
||||
* 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 "h2load.h"
|
||||
|
||||
#include <getopt.h>
|
||||
#include <signal.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#ifdef HAVE_SPDYLAY
|
||||
#include <spdylay/spdylay.h>
|
||||
#endif // HAVE_SPDYLAY
|
||||
|
||||
#include <event2/bufferevent_ssl.h>
|
||||
|
||||
#include <openssl/err.h>
|
||||
|
||||
#include "http-parser/http_parser.h"
|
||||
|
||||
#include "h2load_http2_session.h"
|
||||
#ifdef HAVE_SPDYLAY
|
||||
#include "h2load_spdy_session.h"
|
||||
#endif // HAVE_SPDYLAY
|
||||
#include "ssl.h"
|
||||
#include "http2.h"
|
||||
#include "util.h"
|
||||
|
||||
using namespace nghttp2;
|
||||
|
||||
namespace h2load {
|
||||
|
||||
Config::Config()
|
||||
: addrs(nullptr),
|
||||
nreqs(1),
|
||||
nclients(1),
|
||||
nthreads(1),
|
||||
max_concurrent_streams(-1),
|
||||
window_bits(16),
|
||||
connection_window_bits(16),
|
||||
no_tls_proto(PROTO_HTTP2),
|
||||
port(0),
|
||||
verbose(false)
|
||||
{}
|
||||
|
||||
Config::~Config()
|
||||
{
|
||||
freeaddrinfo(addrs);
|
||||
}
|
||||
|
||||
Config config;
|
||||
|
||||
namespace {
|
||||
void eventcb(bufferevent *bev, short events, void *ptr);
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void readcb(bufferevent *bev, void *ptr);
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void writecb(bufferevent *bev, void *ptr);
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void debug(const char *format, ...)
|
||||
{
|
||||
if(config.verbose) {
|
||||
fprintf(stderr, "[DEBUG] ");
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
vfprintf(stderr, format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Stream::Stream()
|
||||
: status_success(-1)
|
||||
{}
|
||||
|
||||
Client::Client(Worker *worker)
|
||||
: worker(worker),
|
||||
ssl(nullptr),
|
||||
bev(nullptr),
|
||||
next_addr(config.addrs),
|
||||
reqidx(0),
|
||||
state(CLIENT_IDLE)
|
||||
{}
|
||||
|
||||
Client::~Client()
|
||||
{
|
||||
disconnect();
|
||||
}
|
||||
|
||||
int Client::connect()
|
||||
{
|
||||
if(config.scheme == "https") {
|
||||
ssl = SSL_new(worker->ssl_ctx);
|
||||
|
||||
auto config = worker->config;
|
||||
|
||||
if(!util::numeric_host(config->host.c_str())) {
|
||||
SSL_set_tlsext_host_name(ssl, config->host.c_str());
|
||||
}
|
||||
|
||||
bev = bufferevent_openssl_socket_new(worker->evbase, -1, ssl,
|
||||
BUFFEREVENT_SSL_CONNECTING,
|
||||
BEV_OPT_DEFER_CALLBACKS);
|
||||
} else {
|
||||
bev = bufferevent_socket_new(worker->evbase, -1,
|
||||
BEV_OPT_DEFER_CALLBACKS);
|
||||
}
|
||||
|
||||
int rv = -1;
|
||||
while(next_addr) {
|
||||
rv = bufferevent_socket_connect(bev, next_addr->ai_addr,
|
||||
next_addr->ai_addrlen);
|
||||
next_addr = next_addr->ai_next;
|
||||
if(rv == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
bufferevent_enable(bev, EV_READ);
|
||||
bufferevent_setcb(bev, readcb, writecb, eventcb, this);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Client::disconnect()
|
||||
{
|
||||
process_abandoned_streams();
|
||||
if(worker->stats.req_done == worker->stats.req_todo) {
|
||||
worker->schedule_terminate();
|
||||
}
|
||||
int fd = -1;
|
||||
streams.clear();
|
||||
session.reset();
|
||||
state = CLIENT_IDLE;
|
||||
if(ssl) {
|
||||
fd = SSL_get_fd(ssl);
|
||||
SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);
|
||||
SSL_shutdown(ssl);
|
||||
}
|
||||
if(bev) {
|
||||
bufferevent_disable(bev, EV_READ | EV_WRITE);
|
||||
bufferevent_free(bev);
|
||||
bev = nullptr;
|
||||
}
|
||||
if(ssl) {
|
||||
SSL_free(ssl);
|
||||
ssl = nullptr;
|
||||
}
|
||||
if(fd != -1) {
|
||||
shutdown(fd, SHUT_WR);
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
void Client::submit_request()
|
||||
{
|
||||
session->submit_request();
|
||||
++worker->stats.req_started;
|
||||
}
|
||||
|
||||
void Client::process_abandoned_streams()
|
||||
{
|
||||
worker->stats.req_failed += streams.size();
|
||||
worker->stats.req_error += streams.size();
|
||||
worker->stats.req_done += streams.size();
|
||||
}
|
||||
|
||||
void Client::report_progress()
|
||||
{
|
||||
if(worker->id == 0 &&
|
||||
worker->stats.req_done % worker->progress_interval == 0) {
|
||||
std::cout << "progress: "
|
||||
<< worker->stats.req_done * 100 / worker->stats.req_todo
|
||||
<< "% done"
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void Client::terminate_session()
|
||||
{
|
||||
session->terminate();
|
||||
}
|
||||
|
||||
void Client::on_request(int32_t stream_id)
|
||||
{
|
||||
streams[stream_id] = Stream();
|
||||
}
|
||||
|
||||
void Client::on_header(int32_t stream_id,
|
||||
const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen)
|
||||
{
|
||||
auto itr = streams.find(stream_id);
|
||||
if(itr == std::end(streams)) {
|
||||
return;
|
||||
}
|
||||
auto& stream = (*itr).second;
|
||||
if(stream.status_success == -1 &&
|
||||
namelen == 7 && util::streq(":status", 7, name, namelen)) {
|
||||
int status = 0;
|
||||
for(size_t i = 0; i < valuelen; ++i) {
|
||||
if('0' <= value[i] && value[i] <= '9') {
|
||||
status *= 10;
|
||||
status += value[i] - '0';
|
||||
if(status > 999) {
|
||||
stream.status_success = 0;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(status >= 200 && status < 300) {
|
||||
++worker->stats.status[2];
|
||||
stream.status_success = 1;
|
||||
} else if(status < 400) {
|
||||
++worker->stats.status[3];
|
||||
stream.status_success = 1;
|
||||
} else if(status < 600) {
|
||||
++worker->stats.status[status / 100];
|
||||
stream.status_success = 0;
|
||||
} else {
|
||||
stream.status_success = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Client::on_stream_close(int32_t stream_id, bool success)
|
||||
{
|
||||
++worker->stats.req_done;
|
||||
if(success && streams[stream_id].status_success == 1) {
|
||||
++worker->stats.req_success;
|
||||
} else {
|
||||
++worker->stats.req_failed;
|
||||
}
|
||||
report_progress();
|
||||
streams.erase(stream_id);
|
||||
if(worker->stats.req_done == worker->stats.req_todo) {
|
||||
worker->schedule_terminate();
|
||||
return;
|
||||
}
|
||||
|
||||
if(worker->stats.req_started < worker->stats.req_todo) {
|
||||
submit_request();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int Client::on_connect()
|
||||
{
|
||||
session->on_connect();
|
||||
|
||||
auto nreq = std::min(worker->stats.req_todo - worker->stats.req_started,
|
||||
std::min(worker->stats.req_todo / worker->clients.size(),
|
||||
(size_t)config.max_concurrent_streams));
|
||||
for(; nreq > 0; --nreq) {
|
||||
submit_request();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Client::on_read()
|
||||
{
|
||||
ssize_t rv = session->on_read();
|
||||
if(rv < 0) {
|
||||
return -1;
|
||||
}
|
||||
worker->stats.bytes_total += rv;
|
||||
|
||||
return on_write();
|
||||
}
|
||||
|
||||
int Client::on_write()
|
||||
{
|
||||
return session->on_write();
|
||||
}
|
||||
|
||||
Worker::Worker(uint32_t id, SSL_CTX *ssl_ctx, size_t req_todo, size_t nclients,
|
||||
Config *config)
|
||||
: stats{0}, evbase(event_base_new()), ssl_ctx(ssl_ctx), config(config),
|
||||
id(id), term_timer_started(false)
|
||||
{
|
||||
stats.req_todo = req_todo;
|
||||
progress_interval = std::max((size_t)1, req_todo / 10);
|
||||
for(size_t i = 0; i < nclients; ++i) {
|
||||
clients.push_back(util::make_unique<Client>(this));
|
||||
}
|
||||
}
|
||||
|
||||
Worker::~Worker()
|
||||
{
|
||||
event_base_free(evbase);
|
||||
}
|
||||
|
||||
void Worker::run()
|
||||
{
|
||||
for(auto& client : clients) {
|
||||
if(client->connect() != 0) {
|
||||
std::cerr << "client could not connect to host" << std::endl;
|
||||
client->disconnect();
|
||||
}
|
||||
}
|
||||
event_base_loop(evbase, 0);
|
||||
}
|
||||
|
||||
namespace {
|
||||
void term_timeout_cb(evutil_socket_t fd, short what, void *arg)
|
||||
{
|
||||
auto worker = static_cast<Worker*>(arg);
|
||||
worker->terminate_session();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void Worker::schedule_terminate()
|
||||
{
|
||||
if(term_timer_started) {
|
||||
return;
|
||||
}
|
||||
term_timer_started = true;
|
||||
auto term_timer = evtimer_new(evbase, term_timeout_cb, this);
|
||||
timeval timeout = { 0, 0 };
|
||||
evtimer_add(term_timer, &timeout);
|
||||
}
|
||||
|
||||
void Worker::terminate_session()
|
||||
{
|
||||
for(auto& client : clients) {
|
||||
if(client->session == nullptr) {
|
||||
client->disconnect();
|
||||
continue;
|
||||
}
|
||||
client->terminate_session();
|
||||
if(client->on_write() != 0) {
|
||||
client->disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
void debug_nextproto_error()
|
||||
{
|
||||
#ifdef HAVE_SPDYLAY
|
||||
debug("no supported protocol was negotiated, expected: %s, "
|
||||
"spdy/2, spdy/3, spdy/3.1\n", NGHTTP2_PROTO_VERSION_ID);
|
||||
#else // !HAVE_SPDYLAY
|
||||
debug("no supported protocol was negotiated, expected: %s\n",
|
||||
NGHTTP2_PROTO_VERSION_ID);
|
||||
#endif // !HAVE_SPDYLAY
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void eventcb(bufferevent *bev, short events, void *ptr)
|
||||
{
|
||||
int rv;
|
||||
auto client = static_cast<Client*>(ptr);
|
||||
if(events & BEV_EVENT_CONNECTED) {
|
||||
if(client->ssl) {
|
||||
const unsigned char *next_proto = nullptr;
|
||||
unsigned int next_proto_len;
|
||||
SSL_get0_next_proto_negotiated(client->ssl,
|
||||
&next_proto, &next_proto_len);
|
||||
|
||||
if(!next_proto) {
|
||||
debug_nextproto_error();
|
||||
client->disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
if(next_proto_len == NGHTTP2_PROTO_VERSION_ID_LEN &&
|
||||
memcmp(NGHTTP2_PROTO_VERSION_ID, next_proto, next_proto_len) == 0) {
|
||||
client->session = util::make_unique<Http2Session>(client);
|
||||
} else {
|
||||
#ifdef HAVE_SPDYLAY
|
||||
auto spdy_version = spdylay_npn_get_version(next_proto,
|
||||
next_proto_len);
|
||||
if(spdy_version) {
|
||||
client->session = util::make_unique<SpdySession>(client,
|
||||
spdy_version);
|
||||
} else {
|
||||
debug_nextproto_error();
|
||||
client->disconnect();
|
||||
return;
|
||||
}
|
||||
#else // !HAVE_SPDYLAY
|
||||
debug_nextproto_error();
|
||||
client->disconnect();
|
||||
return;
|
||||
#endif // !HAVE_SPDYLAY
|
||||
}
|
||||
} else {
|
||||
switch(config.no_tls_proto) {
|
||||
case Config::PROTO_HTTP2:
|
||||
client->session = util::make_unique<Http2Session>(client);
|
||||
break;
|
||||
#ifdef HAVE_SPDYLAY
|
||||
case Config::PROTO_SPDY2:
|
||||
client->session = util::make_unique<SpdySession>
|
||||
(client, SPDYLAY_PROTO_SPDY2);
|
||||
break;
|
||||
case Config::PROTO_SPDY3:
|
||||
client->session = util::make_unique<SpdySession>
|
||||
(client, SPDYLAY_PROTO_SPDY3);
|
||||
break;
|
||||
case Config::PROTO_SPDY3_1:
|
||||
client->session = util::make_unique<SpdySession>
|
||||
(client, SPDYLAY_PROTO_SPDY3_1);
|
||||
break;
|
||||
#endif // HAVE_SPDYLAY
|
||||
default:
|
||||
// unreachable
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
int fd = bufferevent_getfd(bev);
|
||||
int val = 1;
|
||||
(void)setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
|
||||
reinterpret_cast<char *>(&val), sizeof(val));
|
||||
client->state = CLIENT_CONNECTED;
|
||||
client->on_connect();
|
||||
return;
|
||||
}
|
||||
if(events & BEV_EVENT_EOF) {
|
||||
client->disconnect();
|
||||
return;
|
||||
}
|
||||
if(events & (BEV_EVENT_ERROR | BEV_EVENT_TIMEOUT)) {
|
||||
if(client->state == CLIENT_IDLE) {
|
||||
client->disconnect();
|
||||
rv = client->connect();
|
||||
if(rv == 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
debug("error/eof\n");
|
||||
client->disconnect();
|
||||
return;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void readcb(bufferevent *bev, void *ptr)
|
||||
{
|
||||
int rv;
|
||||
auto client = static_cast<Client*>(ptr);
|
||||
rv = client->on_read();
|
||||
if(rv != 0) {
|
||||
client->disconnect();
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void writecb(bufferevent *bev, void *ptr)
|
||||
{
|
||||
if(evbuffer_get_length(bufferevent_get_output(bev)) > 0) {
|
||||
return;
|
||||
}
|
||||
int rv;
|
||||
auto client = static_cast<Client*>(ptr);
|
||||
rv = client->on_write();
|
||||
if(rv != 0) {
|
||||
client->disconnect();
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void resolve_host()
|
||||
{
|
||||
int rv;
|
||||
addrinfo hints, *res;
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = 0;
|
||||
hints.ai_flags = AI_ADDRCONFIG;
|
||||
|
||||
rv = getaddrinfo(config.host.c_str(), util::utos(config.port).c_str(),
|
||||
&hints, &res);
|
||||
if(rv != 0) {
|
||||
std::cerr << "getaddrinfo() failed: "
|
||||
<< gai_strerror(rv) << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if(res == nullptr) {
|
||||
std::cerr << "No address returned" << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
config.addrs = res;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
std::string get_reqline(const char *uri, const http_parser_url& u)
|
||||
{
|
||||
std::string reqline;
|
||||
|
||||
if(util::has_uri_field(u, UF_PATH)) {
|
||||
reqline = util::get_uri_field(uri, u, UF_PATH);
|
||||
} else {
|
||||
reqline = "/";
|
||||
}
|
||||
|
||||
if(util::has_uri_field(u, UF_QUERY)) {
|
||||
reqline += "?";
|
||||
reqline += util::get_uri_field(uri, u, UF_QUERY);
|
||||
}
|
||||
|
||||
return reqline;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
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(nghttp2_select_next_protocol(out, outlen, in, inlen) > 0) {
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
#ifdef HAVE_SPDYLAY
|
||||
else if(spdylay_select_next_protocol(out, outlen, in, inlen) > 0) {
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
#endif
|
||||
return SSL_TLSEXT_ERR_NOACK;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void print_version(std::ostream& out)
|
||||
{
|
||||
out << "h2load nghttp2/" NGHTTP2_VERSION << std::endl;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void print_usage(std::ostream& out)
|
||||
{
|
||||
out << R"(Usage: h2load [OPTIONS]... <URI>...
|
||||
benchmarking tool for HTTP/2 and SPDY server)" << std::endl;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void print_help(std::ostream& out)
|
||||
{
|
||||
print_usage(out);
|
||||
|
||||
out << R"(
|
||||
<URI> Specify URI to access. Multiple URIs can be
|
||||
specified. URIs are used in this order for each
|
||||
client. All URIs are used, then first URI is
|
||||
used and then 2nd URI, and so on. The scheme,
|
||||
host and port in the subsequent URIs, if present,
|
||||
are ignored. Those in the first URI are used
|
||||
solely.
|
||||
Options:
|
||||
-n, --requests=<N> Number of requests. Default: )"
|
||||
<< config.nreqs << R"(
|
||||
-c, --clients=<N> Number of concurrent clients. Default: )"
|
||||
<< config.nclients << R"(
|
||||
-t, --threads=<N> Number of native threads. Default: )"
|
||||
<< config.nthreads << R"(
|
||||
-m, --max-concurrent-streams=(auto|<N>)
|
||||
Max concurrent streams to issue per session. If
|
||||
"auto" is given, the number of given URIs is
|
||||
used. Default: auto
|
||||
-w, --window-bits=<N>
|
||||
Sets the stream level initial window size to
|
||||
(2**<N>)-1. For SPDY, 2**<N> is used instead.
|
||||
-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.
|
||||
-p, --no-tls-proto=<PROTOID>
|
||||
Specify ALPN identifier of the protocol to be
|
||||
used when accessing http URI without SSL/TLS.)";
|
||||
#ifdef HAVE_SPDYLAY
|
||||
out << R"(
|
||||
Available protocols: spdy/2, spdy/3, spdy/3.1 and
|
||||
)";
|
||||
#else // !HAVE_SPDYLAY
|
||||
out << R"(
|
||||
Available protocol: )";
|
||||
#endif // !HAVE_SPDYLAY
|
||||
out << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"(
|
||||
Default: )"
|
||||
<< NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"(
|
||||
-v, --verbose Output debug information.
|
||||
--version Display version information and exit.
|
||||
-h, --help Display this help and exit.)"
|
||||
<< std::endl;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
while(1) {
|
||||
static int flag = 0;
|
||||
static option long_options[] = {
|
||||
{"requests", required_argument, nullptr, 'n'},
|
||||
{"clients", required_argument, nullptr, 'c'},
|
||||
{"threads", required_argument, nullptr, 't'},
|
||||
{"max-concurrent-streams", required_argument, nullptr, 'm'},
|
||||
{"window-bits", required_argument, nullptr, 'w'},
|
||||
{"connection-window-bits", required_argument, nullptr, 'W'},
|
||||
{"no-tls-proto", required_argument, nullptr, 'p'},
|
||||
{"verbose", no_argument, nullptr, 'v'},
|
||||
{"help", no_argument, nullptr, 'h'},
|
||||
{"version", no_argument, &flag, 1},
|
||||
{nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
int option_index = 0;
|
||||
auto c = getopt_long(argc, argv, "hvW:c:m:n:p:t:w:", long_options,
|
||||
&option_index);
|
||||
if(c == -1) {
|
||||
break;
|
||||
}
|
||||
switch(c) {
|
||||
case 'n':
|
||||
config.nreqs = strtoul(optarg, nullptr, 10);
|
||||
break;
|
||||
case 'c':
|
||||
config.nclients = strtoul(optarg, nullptr, 10);
|
||||
break;
|
||||
case 't':
|
||||
#ifdef NOTHREADS
|
||||
std::cerr << "-t: WARNING: Threading disabled at build time, " <<
|
||||
"no threads created." << std::endl;
|
||||
#else
|
||||
config.nthreads = strtoul(optarg, nullptr, 10);
|
||||
#endif // NOTHREADS
|
||||
break;
|
||||
case 'm':
|
||||
if(util::strieq("auto", optarg)) {
|
||||
config.max_concurrent_streams = -1;
|
||||
} else {
|
||||
config.max_concurrent_streams = strtoul(optarg, nullptr, 10);
|
||||
}
|
||||
break;
|
||||
case 'w':
|
||||
case 'W': {
|
||||
errno = 0;
|
||||
char *endptr = nullptr;
|
||||
auto n = strtoul(optarg, &endptr, 10);
|
||||
if(errno == 0 && *endptr == '\0' && n < 31) {
|
||||
if(c == 'w') {
|
||||
config.window_bits = n;
|
||||
} else {
|
||||
config.connection_window_bits = n;
|
||||
}
|
||||
} else {
|
||||
std::cerr << "-" << static_cast<char>(c)
|
||||
<< ": specify the integer in the range [0, 30], inclusive"
|
||||
<< std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'p':
|
||||
if(util::strieq(NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, optarg)) {
|
||||
config.no_tls_proto = Config::PROTO_HTTP2;
|
||||
#ifdef HAVE_SPDYLAY
|
||||
} else if(util::strieq("spdy/2", optarg)) {
|
||||
config.no_tls_proto = Config::PROTO_SPDY2;
|
||||
} else if(util::strieq("spdy/3", optarg)) {
|
||||
config.no_tls_proto = Config::PROTO_SPDY3;
|
||||
} else if(util::strieq("spdy/3.1", optarg)) {
|
||||
config.no_tls_proto = Config::PROTO_SPDY3_1;
|
||||
#endif // HAVE_SPDYLAY
|
||||
} else {
|
||||
std::cerr << "-p: unsupported protocol " << optarg << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
case 'v':
|
||||
config.verbose = true;
|
||||
break;
|
||||
case 'h':
|
||||
print_help(std::cout);
|
||||
exit(EXIT_SUCCESS);
|
||||
case '?':
|
||||
util::show_candidates(argv[optind - 1], long_options);
|
||||
exit(EXIT_FAILURE);
|
||||
case 0:
|
||||
switch(flag) {
|
||||
case 1:
|
||||
// version option
|
||||
print_version(std::cout);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(argc == optind) {
|
||||
std::cerr << "no URI given" << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if(config.nreqs == 0) {
|
||||
std::cerr << "-n: the number of requests must be strictly greater than 0."
|
||||
<< std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if(config.max_concurrent_streams == 0) {
|
||||
std::cerr << "-m: the max concurrent streams must be strictly greater "
|
||||
<< "than 0."
|
||||
<< std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if(config.nthreads == 0) {
|
||||
std::cerr << "-t: the number of threads must be strictly greater than 0."
|
||||
<< std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if(config.nreqs < config.nclients) {
|
||||
std::cerr << "-n, -c: the number of requests must be greater than or "
|
||||
<< "equal to the concurrent clients."
|
||||
<< std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if(config.nthreads > std::thread::hardware_concurrency()) {
|
||||
std::cerr << "-t: warning: the number of threads is greater than hardware "
|
||||
<< "cores."
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
struct sigaction act;
|
||||
memset(&act, 0, sizeof(struct sigaction));
|
||||
act.sa_handler = SIG_IGN;
|
||||
sigaction(SIGPIPE, &act, nullptr);
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
|
||||
#ifndef NOTHREADS
|
||||
ssl::LibsslGlobalLock();
|
||||
#endif // NOTHREADS
|
||||
|
||||
auto ssl_ctx = SSL_CTX_new(SSLv23_client_method());
|
||||
if(!ssl_ctx) {
|
||||
std::cerr << "Failed to create SSL_CTX: "
|
||||
<< ERR_error_string(ERR_get_error(), nullptr) << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
SSL_CTX_set_next_proto_select_cb(ssl_ctx,
|
||||
client_select_next_proto_cb, nullptr);
|
||||
|
||||
// First URI is treated specially. We use scheme, host and port of
|
||||
// this URI and ignore those in the remaining URIs if present.
|
||||
http_parser_url u;
|
||||
memset(&u, 0, sizeof(u));
|
||||
auto uri = argv[optind];
|
||||
if(http_parser_parse_url(uri, strlen(uri), 0, &u) != 0 ||
|
||||
!util::has_uri_field(u, UF_SCHEMA) || !util::has_uri_field(u, UF_HOST)) {
|
||||
std::cerr << "invalid URI: " << uri << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
config.scheme = util::get_uri_field(uri, u, UF_SCHEMA);
|
||||
config.host = util::get_uri_field(uri, u, UF_HOST);
|
||||
if(util::has_uri_field(u, UF_PORT)) {
|
||||
config.port = u.port;
|
||||
} else {
|
||||
config.port = util::get_default_port(uri, u);
|
||||
}
|
||||
|
||||
std::vector<std::string> reqlines;
|
||||
|
||||
reqlines.push_back(get_reqline(uri, u));
|
||||
|
||||
++optind;
|
||||
for(int i = optind; i < argc; ++i) {
|
||||
memset(&u, 0, sizeof(u));
|
||||
|
||||
auto uri = argv[i];
|
||||
|
||||
if(http_parser_parse_url(uri, strlen(uri), 0, &u) != 0) {
|
||||
std::cerr << "invalid URI: " << uri << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
reqlines.push_back(get_reqline(uri, u));
|
||||
}
|
||||
|
||||
if(config.max_concurrent_streams == -1) {
|
||||
config.max_concurrent_streams = reqlines.size();
|
||||
}
|
||||
|
||||
Headers shared_nva;
|
||||
shared_nva.emplace_back(":scheme", config.scheme);
|
||||
if(config.port != util::get_default_port(uri, u)) {
|
||||
shared_nva.emplace_back(":authority",
|
||||
config.host + ":" + util::utos(config.port));
|
||||
} else {
|
||||
shared_nva.emplace_back(":authority", config.host);
|
||||
}
|
||||
shared_nva.emplace_back(":method", "GET");
|
||||
|
||||
for(auto& req : reqlines) {
|
||||
// For nghttp2
|
||||
std::vector<nghttp2_nv> nva;
|
||||
|
||||
nva.push_back(http2::make_nv_ls(":path", req));
|
||||
|
||||
for(auto& nv : shared_nva) {
|
||||
nva.push_back(http2::make_nv(nv.name, nv.value, false));
|
||||
}
|
||||
|
||||
config.nva.push_back(std::move(nva));
|
||||
|
||||
// For spdylay
|
||||
std::vector<const char*> cva;
|
||||
|
||||
cva.push_back(":path");
|
||||
cva.push_back(req.c_str());
|
||||
|
||||
for(auto& nv : shared_nva) {
|
||||
if(nv.name == ":authority") {
|
||||
cva.push_back(":host");
|
||||
} else {
|
||||
cva.push_back(nv.name.c_str());
|
||||
}
|
||||
cva.push_back(nv.value.c_str());
|
||||
}
|
||||
cva.push_back(":version");
|
||||
cva.push_back("HTTP/1.1");
|
||||
cva.push_back(nullptr);
|
||||
|
||||
config.nv.push_back(std::move(cva));
|
||||
}
|
||||
|
||||
resolve_host();
|
||||
|
||||
size_t nreqs_per_thread = config.nreqs / config.nthreads;
|
||||
ssize_t nreqs_rem = config.nreqs % config.nthreads;
|
||||
|
||||
size_t nclients_per_thread = config.nclients / config.nthreads;
|
||||
ssize_t nclients_rem = config.nclients % config.nthreads;
|
||||
|
||||
std::cout << "starting benchmark..." << std::endl;
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
|
||||
std::vector<std::unique_ptr<Worker>> workers;
|
||||
for(size_t i = 0; i < config.nthreads - 1; ++i) {
|
||||
auto nreqs = nreqs_per_thread + (nreqs_rem-- > 0);
|
||||
auto nclients = nclients_per_thread + (nclients_rem-- > 0);
|
||||
std::cout << "spawning thread #" << i << ": "
|
||||
<< nclients << " concurrent clients, "
|
||||
<< nreqs << " total requests"
|
||||
<< std::endl;
|
||||
workers.push_back(util::make_unique<Worker>(i, ssl_ctx, nreqs, nclients,
|
||||
&config));
|
||||
threads.emplace_back(&Worker::run, workers.back().get());
|
||||
}
|
||||
auto nreqs_last = nreqs_per_thread + (nreqs_rem-- > 0);
|
||||
auto nclients_last = nclients_per_thread + (nclients_rem-- > 0);
|
||||
std::cout << "spawning thread #" << (config.nthreads - 1) << ": "
|
||||
<< nclients_last << " concurrent clients, "
|
||||
<< nreqs_last << " total requests"
|
||||
<< std::endl;
|
||||
Worker worker(config.nthreads - 1, ssl_ctx, nreqs_last, nclients_last,
|
||||
&config);
|
||||
worker.run();
|
||||
|
||||
for(size_t i = 0; i < config.nthreads - 1; ++i) {
|
||||
threads[i].join();
|
||||
worker.stats.req_todo += workers[i]->stats.req_todo;
|
||||
worker.stats.req_started += workers[i]->stats.req_started;
|
||||
worker.stats.req_done += workers[i]->stats.req_done;
|
||||
worker.stats.req_success += workers[i]->stats.req_success;
|
||||
worker.stats.req_failed += workers[i]->stats.req_failed;
|
||||
worker.stats.req_error += workers[i]->stats.req_error;
|
||||
worker.stats.bytes_total += workers[i]->stats.bytes_total;
|
||||
worker.stats.bytes_head += workers[i]->stats.bytes_head;
|
||||
worker.stats.bytes_body += workers[i]->stats.bytes_body;
|
||||
for(size_t j = 0; j < 6; ++j) {
|
||||
worker.stats.status[j] += workers[i]->stats.status[j];
|
||||
}
|
||||
}
|
||||
|
||||
auto end = std::chrono::steady_clock::now();
|
||||
auto duration =
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
|
||||
|
||||
// Requests which have not been issued due to connection errors, are
|
||||
// counted towards req_failed and req_error.
|
||||
auto req_not_issued = worker.stats.req_todo
|
||||
- worker.stats.req_success - worker.stats.req_failed;
|
||||
worker.stats.req_failed += req_not_issued;
|
||||
worker.stats.req_error += req_not_issued;
|
||||
|
||||
// UI is heavily inspired by weighttp
|
||||
// https://github.com/lighttpd/weighttp
|
||||
size_t rps;
|
||||
int64_t kbps;
|
||||
if(duration > 0) {
|
||||
auto secd = static_cast<double>(duration) / (1000 * 1000);
|
||||
rps = worker.stats.req_todo / secd;
|
||||
kbps = worker.stats.bytes_total / secd / 1024;
|
||||
} else {
|
||||
rps = 0;
|
||||
kbps = 0;
|
||||
}
|
||||
|
||||
auto sec = duration / (1000 * 1000);
|
||||
auto millisec = (duration / 1000) % 1000;
|
||||
auto microsec = duration % 1000;
|
||||
|
||||
std::cout << "\n"
|
||||
<< "finished in "
|
||||
<< sec << " sec, "
|
||||
<< millisec << " millisec and "
|
||||
<< microsec << " microsec, "
|
||||
<< rps << " req/s, "
|
||||
<< kbps << " kbytes/s\n"
|
||||
<< "requests: "
|
||||
<< worker.stats.req_todo << " total, "
|
||||
<< worker.stats.req_started << " started, "
|
||||
<< worker.stats.req_done << " done, "
|
||||
<< worker.stats.req_success << " succeeded, "
|
||||
<< worker.stats.req_failed << " failed, "
|
||||
<< worker.stats.req_error << " errored\n"
|
||||
<< "status codes: "
|
||||
<< worker.stats.status[2] << " 2xx, "
|
||||
<< worker.stats.status[3] << " 3xx, "
|
||||
<< worker.stats.status[4] << " 4xx, "
|
||||
<< worker.stats.status[5] << " 5xx\n"
|
||||
<< "traffic: "
|
||||
<< worker.stats.bytes_total << " bytes total, "
|
||||
<< worker.stats.bytes_head << " bytes headers, "
|
||||
<< worker.stats.bytes_body << " bytes data"
|
||||
<< std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace h2load
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
return h2load::main(argc, argv);
|
||||
}
|
||||
163
src/h2load.h
Normal file
163
src/h2load.h
Normal file
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* 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 H2LOAD_H
|
||||
#define H2LOAD_H
|
||||
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
#include <event.h>
|
||||
#include <event2/event.h>
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
namespace h2load {
|
||||
|
||||
class Session;
|
||||
|
||||
struct Config {
|
||||
std::vector<std::vector<nghttp2_nv>> nva;
|
||||
std::vector<std::vector<const char*>> nv;
|
||||
std::string scheme;
|
||||
std::string host;
|
||||
addrinfo *addrs;
|
||||
size_t nreqs;
|
||||
size_t nclients;
|
||||
size_t nthreads;
|
||||
// The maximum number of concurrent streams per session.
|
||||
ssize_t max_concurrent_streams;
|
||||
size_t window_bits;
|
||||
size_t connection_window_bits;
|
||||
enum {
|
||||
PROTO_HTTP2,
|
||||
PROTO_SPDY2,
|
||||
PROTO_SPDY3,
|
||||
PROTO_SPDY3_1
|
||||
} no_tls_proto;
|
||||
uint16_t port;
|
||||
bool verbose;
|
||||
|
||||
Config();
|
||||
~Config();
|
||||
};
|
||||
|
||||
struct Stats {
|
||||
// The total number of requests
|
||||
size_t req_todo;
|
||||
// The number of requests issued so far
|
||||
size_t req_started;
|
||||
// The number of requests finished
|
||||
size_t req_done;
|
||||
// The number of requests marked as success. This is subset of
|
||||
// req_done.
|
||||
size_t req_success;
|
||||
// The number of requests failed. This is subset of req_done.
|
||||
size_t req_failed;
|
||||
// The number of requests failed due to network errors. This is
|
||||
// subset of req_failed.
|
||||
size_t req_error;
|
||||
// The number of bytes received on the "wire". If SSL/TLS is used,
|
||||
// this is the number of decrypted bytes the application received.
|
||||
int64_t bytes_total;
|
||||
// The number of bytes received in HEADERS frame payload.
|
||||
int64_t bytes_head;
|
||||
// The number of bytes received in DATA frame.
|
||||
int64_t bytes_body;
|
||||
// The number of each HTTP status category, status[i] is status code
|
||||
// in the range [i*100, (i+1)*100).
|
||||
size_t status[6];
|
||||
};
|
||||
|
||||
enum ClientState {
|
||||
CLIENT_IDLE,
|
||||
CLIENT_CONNECTED
|
||||
};
|
||||
|
||||
struct Client;
|
||||
|
||||
struct Worker {
|
||||
std::vector<std::unique_ptr<Client>> clients;
|
||||
Stats stats;
|
||||
event_base *evbase;
|
||||
SSL_CTX *ssl_ctx;
|
||||
Config *config;
|
||||
size_t progress_interval;
|
||||
uint32_t id;
|
||||
bool term_timer_started;
|
||||
|
||||
Worker(uint32_t id, SSL_CTX *ssl_ctx, size_t nreq_todo, size_t nclients,
|
||||
Config *config);
|
||||
~Worker();
|
||||
void run();
|
||||
void schedule_terminate();
|
||||
void terminate_session();
|
||||
};
|
||||
|
||||
struct Stream {
|
||||
int status_success;
|
||||
Stream();
|
||||
};
|
||||
|
||||
struct Client {
|
||||
std::unordered_map<int32_t, Stream> streams;
|
||||
std::unique_ptr<Session> session;
|
||||
Worker *worker;
|
||||
SSL *ssl;
|
||||
bufferevent *bev;
|
||||
addrinfo *next_addr;
|
||||
size_t reqidx;
|
||||
ClientState state;
|
||||
|
||||
Client(Worker *worker);
|
||||
~Client();
|
||||
int connect();
|
||||
void disconnect();
|
||||
void submit_request();
|
||||
void process_abandoned_streams();
|
||||
void report_progress();
|
||||
void terminate_session();
|
||||
int on_connect();
|
||||
int on_read();
|
||||
int on_write();
|
||||
void on_request(int32_t stream_id);
|
||||
void on_header(int32_t stream_id,
|
||||
const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen);
|
||||
void on_stream_close(int32_t stream_id, bool success);
|
||||
};
|
||||
|
||||
} // namespace h2load
|
||||
|
||||
#endif // H2LOAD_H
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user