Compare commits

..

390 Commits

Author SHA1 Message Date
Tatsuhiro Tsujikawa
453e12cd1f Update man pages 2014-05-16 23:54:09 +09:00
Tatsuhiro Tsujikawa
de5c821530 Bump up version number to 0.4.0, LT revision to 3:0:0 2014-05-16 23:51:32 +09:00
Tatsuhiro Tsujikawa
1ac028e166 Take into account that pending_local_max_concurrent_stream could be too large
pending_local_max_concurrent_stream is, once local settings applied,
becomes NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS, which is very large
number.  When adjusting number of streams, we have to take min of
local effective SETTINGS_MAX_CONCURRENT_STREAMS and pending one.
2014-05-16 22:32:08 +09:00
Tatsuhiro Tsujikawa
2778e4aafc Remove useless debug code in nghttp2_stream_roots_add() 2014-05-16 22:30:59 +09:00
Tatsuhiro Tsujikawa
3f80472e0a nghttpx: Minor tweak to buffer size
Simplified when to send WINDOW_UPDATE to the backend, that is we send
WINDOW_UPDATE when input buffer is empty.
2014-05-16 21:42:30 +09:00
Tatsuhiro Tsujikawa
ec30af9117 Update sphinx_rtd_theme to 54a48b1726ed602c9b5416ee46c639462ec315fe 2014-05-16 00:36:06 +09:00
Tatsuhiro Tsujikawa
3b5b5ce254 Update http-parser to 8d9e5db981b623fffc93657abacdc80270cbee58 2014-05-16 00:23:03 +09:00
Tatsuhiro Tsujikawa
694cd07f1d nghttpx: Remove Connection: close header field from CONNECT response
It appears that some Android client does not like Connection: close in
200 CONNECT response.  Browsers fine with this header field.  It is
better to remove it.  Squid does not emit it too.
2014-05-15 23:45:17 +09:00
Tatsuhiro Tsujikawa
5e71f293e5 Update nghttp and nghttpd example output 2014-05-15 00:27:24 +09:00
Tatsuhiro Tsujikawa
d4ec542107 Update doc 2014-05-15 00:11:40 +09:00
Tatsuhiro Tsujikawa
ffdc764d85 Update doc 2014-05-15 00:10:27 +09:00
Tatsuhiro Tsujikawa
2ae1da113e src: Use C++ style comments for C++ source code 2014-05-14 23:22:23 +09:00
Tatsuhiro Tsujikawa
5b4f02dfe0 src: Rewrite util::format_hex 2014-05-14 23:09:33 +09:00
Tatsuhiro Tsujikawa
e47b976691 src: Eliminate use of snprintf 2014-05-14 22:39:28 +09:00
Tatsuhiro Tsujikawa
be4c75a7e9 src: Use gmtime_r instead of gmtime 2014-05-14 21:23:21 +09:00
Tatsuhiro Tsujikawa
7b9a8acc22 Add HPACK deflation API 2014-05-13 23:42:55 +09:00
Tatsuhiro Tsujikawa
03e2dabea9 doc: Add Python API doc 2014-05-13 21:46:39 +09:00
Tatsuhiro Tsujikawa
2411a08b09 python: Return Handler object for pushed resource from BaseRequestHandler.push
Also update doc
2014-05-13 21:42:58 +09:00
Tatsuhiro Tsujikawa
062d6a8398 Remove outdated comment 2014-05-12 22:58:05 +09:00
Tatsuhiro Tsujikawa
ad4a4ee567 Add missing library error code to nghttp2_error() 2014-05-12 22:58:04 +09:00
Tatsuhiro Tsujikawa
ab76468971 Return NGHTTP2_ERR_BUFFER_ERROR from nghttp2_hd_{deflate,inflate}_hd
It is generally useful to know what is the cause of the error.  Since
we expose HPACK API, it is friendly to tell application the
insufficient buffer size is a culprit.
2014-05-12 22:58:04 +09:00
Tatsuhiro Tsujikawa
bc6d952361 Check max SETTINGS_HEADER_TABLE_SIZE in nghttp2_iv_check()
Hide NGHTTP2_MAX_HEADER_TABLE_SIZE from public API.  Now it is defined
as ((1u << 31) - 1) in nghttp2_frame.h, which is sufficiently big
enough.
2014-05-12 21:28:49 +09:00
Tatsuhiro Tsujikawa
f85c592818 Fix compile error with clang -Wunreachable-code 2014-05-12 21:11:27 +09:00
Tatsuhiro Tsujikawa
c9c9beddeb Merge branch 'add_check_compile_flag' of https://github.com/alagoutte/nghttp2 into alagoutte-add_check_compile_flag 2014-05-12 21:05:18 +09:00
Tatsuhiro Tsujikawa
d3fa938f1f nghttpd: Fix compiler warning 2014-05-11 21:51:25 +09:00
Tatsuhiro Tsujikawa
9683f88e6a Add NGHTTP2_DEFAULT_HEADER_TABLE_SIZE macro
Use this macro in Python sources.  Python module constant
HD_DEFLATE_HD_TABLE_BUFSIZE_MAX was renamed as
DEFLATE_MAX_HEADER_TABLE_SIZE since the previous name was awkward.
2014-05-11 21:38:30 +09:00
Tatsuhiro Tsujikawa
2e5c7f598f Fix bug HPACK deflater does not send context update after table size change 2014-05-11 21:25:27 +09:00
Alexis La Goutte
88234cbac0 Add some news CFLAGS:
* -Wpragmas
* -Wunreachable-code
* -Wpedantic
* -Waddress
* -Wattributes
* -Wdiv-by-zero
* -Wheader-guard
2014-05-11 11:28:29 +02:00
Alexis La Goutte
5b208c6277 Add AX_CHECK_COMPILE_FLAG (from Autotools packages)
It's fix the build with Clang and --enable-werror, don't support -WClobberred and display error about unknown warning option
error: unknown warning option '-Wclobbered'; did you mean '-Wconsumed'? [-Werror,-Wunknown-warning-option]
2014-05-11 11:27:52 +02:00
Tatsuhiro Tsujikawa
4fffd23dd3 Code cleanup 2014-05-11 13:53:42 +09:00
Tatsuhiro Tsujikawa
b187895e1d nghttp2_bufs_remove: Prevent NULL from being passed to memcpy 2014-05-11 13:51:46 +09:00
Tatsuhiro Tsujikawa
6e7d0286e3 python: Utilize return value of nghttp2_submit_push_promise 2014-05-10 23:45:05 +09:00
Tatsuhiro Tsujikawa
f2bb7947ee Update doc 2014-05-10 23:22:06 +09:00
Tatsuhiro Tsujikawa
dc791a641d Update doc 2014-05-10 21:43:16 +09:00
Tatsuhiro Tsujikawa
a93e04c6f8 tests: Add failmalloc HPACK test 2014-05-10 21:14:25 +09:00
Tatsuhiro Tsujikawa
d46e13ae52 tests: Fix failmalloc tests 2014-05-10 19:40:23 +09:00
Tatsuhiro Tsujikawa
0707720b11 Code cleanup 2014-05-10 18:27:53 +09:00
Tatsuhiro Tsujikawa
74ad10c355 nghttpx: Log :authority for CONNECT request in accesslog 2014-05-10 00:55:15 +09:00
Tatsuhiro Tsujikawa
f131705ba4 Add h2load.1 dev version for now to make happy 2014-05-10 00:34:56 +09:00
Tatsuhiro Tsujikawa
6a70584459 doc: Add h2load man page 2014-05-10 00:19:57 +09:00
Tatsuhiro Tsujikawa
4c8d4f8a85 Code cleanup 2014-05-10 00:13:40 +09:00
Tatsuhiro Tsujikawa
3ebb3faf32 Remove nghttp2_ prefix from static function, part 2 2014-05-08 23:54:07 +09:00
Tatsuhiro Tsujikawa
65bbdf56cd Remove nghttp2_ prefix from static function 2014-05-08 23:37:56 +09:00
Tatsuhiro Tsujikawa
3e3d51842b Interleave stream DATA more naturally
We simulate resource sharing by decreasing weight.  The thing is if
weight is wrapped, that item continues to send DATA until its weight
gets lowered under the other items.  This commits fix this issue.
2014-05-08 23:07:29 +09:00
Tatsuhiro Tsujikawa
b041218a2a python: Fix function include path 2014-05-08 01:03:28 +09:00
Tatsuhiro Tsujikawa
29d2386f13 Update doc 2014-05-08 00:59:06 +09:00
Tatsuhiro Tsujikawa
1bd43e094a nghttp: Remove streams from HttpClient 2014-05-08 00:29:46 +09:00
Tatsuhiro Tsujikawa
48fc0c04bc doc: Update tutorial-client.rst 2014-05-08 00:11:36 +09:00
Tatsuhiro Tsujikawa
d2890dfb91 Remove adjust_priority_callback
Since we have stream ID immediately available from nghttp2_submit_*,
we don't need adjust_priority_callback.
2014-05-07 23:43:45 +09:00
Tatsuhiro Tsujikawa
e8de437d5c Return new stream ID from nghttp2_submit_{request, headers, push_promise}
Previously stream ID was assigned just before HEADERS or PUSH_PROMISE
was serialized and nghttp2_submit_{request, headers, push_promise} did
not return stream ID.  The application has to check assigned stream ID
using before_frame_send_callback.  Now it is apparent that priority is
meant to DATA transfer only.  Also application can reorder the
requests if it wants. Therefore we can assign stream ID in
nghttp2_submit_* functions and return stream ID from them.  With this
change, now application does not have to check stream ID using
before_frame_send_callback and its code will be simplified.
2014-05-07 23:24:07 +09:00
Tatsuhiro Tsujikawa
51e79c5a3d Remove debug output 2014-05-07 23:23:43 +09:00
Tatsuhiro Tsujikawa
d11cac48f1 Merge branch 'typo' of https://github.com/alagoutte/nghttp2 into alagoutte-typo 2014-05-07 00:35:07 +09:00
Alexis La Goutte
ed63674b88 Fix GCC build with -Werror
timegm.h:30:8: error: C++ style comments are not allowed in ISO C90 [-Werror]
2014-05-06 17:06:40 +02:00
Alexis La Goutte
8e22eadc76 Fix typo 2014-05-06 17:05:47 +02:00
Tatsuhiro Tsujikawa
9228e223fa Remove NGHTTP2_ERR_GZIP error code 2014-05-06 23:42:57 +09:00
Tatsuhiro Tsujikawa
43fb7f707f Fix unittest build error 2014-05-06 23:42:32 +09:00
Tatsuhiro Tsujikawa
f88599197e Update doc 2014-05-06 23:18:12 +09:00
Tatsuhiro Tsujikawa
4f027c1562 libnghttp2: Remove dependency to zlib
We inherited gzip compression API from spdylay codebase.  In spdylay,
the cost of having such API is almost free because spdylay requires
zlib for header compression.  nghttp2 no longer uses gzip to header
compression.  zlib dependency exists just for gzip compression API,
which is not an essential.  So we decided to move gzip code to under
src and remove zlib dependency from libnghttp2 itself.  As nghttp2
package, we depend on zlib to compile tools under src.
2014-05-06 23:10:50 +09:00
Tatsuhiro Tsujikawa
704f362804 nghttp2.h: Use hex style flag definition 2014-05-06 18:49:24 +09:00
Tatsuhiro Tsujikawa
e2535df505 Code cleanup 2014-05-06 18:39:04 +09:00
Tatsuhiro Tsujikawa
f207089604 Update doc 2014-05-06 18:34:48 +09:00
Tatsuhiro Tsujikawa
7b7b0ebcca Update doc 2014-05-06 18:08:41 +09:00
Tatsuhiro Tsujikawa
8289943a58 nghttp2_frame: Add assertion to available buffer size in first buffer 2014-05-06 18:06:46 +09:00
Tatsuhiro Tsujikawa
56d6784d8d nghttp2_frame: Return NGHTTP2_ERR_FRAME_SIZE_ERROR instead of .._BUFFER_ERROR
Also updates docs
2014-05-06 18:02:26 +09:00
Tatsuhiro Tsujikawa
1d26678934 Enable silent-rules by default 2014-05-05 18:48:26 +09:00
Tatsuhiro Tsujikawa
9125499dd0 src: Implement per-frame DATA compression
Currently, nghttpd server only compresses files whose extensions are
one of .html, .js, .css and .txt.  nghttp advertises its support of
per-frame compression in SETTINGS frame.  To implement this feature,
we added 2 public API: nghttp2_session_get_remote_settings() and
nghttp2_gzip_inflate_finished().
2014-05-03 00:02:17 +09:00
Nicholas Hurley
f3f9210dae Add --disable-threads option for configure
This allows users of OS X 10.9 to run nghttpd (and friends) with
threading entirely disabled, to avoid crashes on startup related to
std::mutex.
2014-05-01 17:18:29 -07:00
Tatsuhiro Tsujikawa
9ca63de9e8 examples: Zero clear callbacks 2014-05-01 10:46:08 +09:00
Tatsuhiro Tsujikawa
813c750c12 Merge branch 'clang' of https://github.com/alagoutte/nghttp2 into alagoutte-clang 2014-05-01 10:45:38 +09:00
Tatsuhiro Tsujikawa
3fc1d2dfaa Merge branch 'gcc' of https://github.com/alagoutte/nghttp2 into alagoutte-gcc 2014-05-01 10:29:40 +09:00
Tatsuhiro Tsujikawa
855f39743a Fix crash when indexed repr index=0 2014-05-01 09:06:54 +09:00
Tatsuhiro Tsujikawa
3c431da6aa Fix bug that server treats reception of ENABLE_PUSH=0 as connection error 2014-05-01 08:38:28 +09:00
Alexis La Goutte
66ed7f6a59 Fix build when use Clang
libevent-server.c:552:43: error: missing field 'recv_callback' initializer [-Werror,-Wmissing-field-initializers]
2014-04-30 17:31:33 +02:00
Alexis La Goutte
8ca2f6aa92 Fix build when use Clang
libevent-client.c:355:43: error: missing field 'recv_callback' initializer [-Werror,-Wmissing-field-initializers]
2014-04-30 17:31:29 +02:00
Alexis La Goutte
fa2fbe944f Fix GCC build with -Werror
libevent-server.c:691:8: error: C++ style comments are not allowed in ISO C90
2014-04-30 17:09:06 +02:00
Alexis La Goutte
33a6851abe Fix GCC build with -Werror
nghttp2_session.c:1615:9: error: C++ style comments are not allowed in ISO C90 [-Werror]
2014-04-30 17:09:06 +02:00
Alexis La Goutte
763cdc3499 Fix GCC when use -Wpedantic
nghttp2_stream.h:94:43: error: comma at end of enumerator list [-Werror=pedantic]
2014-04-30 17:09:05 +02:00
Alexis La Goutte
0f5c28ac46 Fix GCC when use -Wpedantic
nghttp2_hd.h:115:30: error: comma at end of enumerator list [-Werror=pedantic]
2014-04-30 17:09:05 +02:00
Alexis La Goutte
941236948f Fix GCC when use -Wpedantic
nghttp2_int.h:50:33: error: comma at end of enumerator list [-Werror=pedantic]
2014-04-30 17:09:05 +02:00
Tatsuhiro Tsujikawa
660c536275 Extend namelen and valuelen in nghttp2_nv to size_t 2014-04-30 23:08:34 +09:00
Tatsuhiro Tsujikawa
abe74f869f Ditto 7730b13e5a 2014-04-30 22:44:51 +09:00
Tatsuhiro Tsujikawa
52b74144ee Fix 0 size malloc, part 2 2014-04-30 22:40:43 +09:00
Tatsuhiro Tsujikawa
1b79114d2d Fix compiler warnings 2014-04-30 22:16:21 +09:00
Tatsuhiro Tsujikawa
ab634853df Fix 0 size malloc 2014-04-30 22:09:02 +09:00
Tatsuhiro Tsujikawa
d2e64317ba Code cleanup, include nghttp2_int.h explicitly to ensure debug macro 2014-04-30 22:08:32 +09:00
Alexis La Goutte
7730b13e5a Fix Address of stack memory associated with local variable 'flag' is still
referred to by the global variable 'long_options' upon returning to the caller.
This will be a dangling reference

Found by Clang Analyzer
2014-04-30 22:06:42 +09:00
Alexis La Goutte
ed5339953e Fix Dead Store (Dead assignement/Dead increment) warning found by Clang Analyzer 2014-04-30 22:06:42 +09:00
Alexis La Goutte
b2f07b1d8c Fix Dead Store (Dead assignement/Dead increment) warning found by Clang Analyzer 2014-04-30 22:06:42 +09:00
Alexis La Goutte
eff5c7d0d0 Fix Dead Store (Dead assignement/Dead increment) warning found by Clang Analyzer 2014-04-30 22:06:42 +09:00
Alexis La Goutte
e00b8f1f73 Fix Dead Store (Dead assignement/Dead increment) warning found by Clang Analyzer 2014-04-30 22:06:42 +09:00
Tatsuhiro Tsujikawa
fe6b541233 Handle hd inflate buffer allocation failure 2014-04-30 11:32:05 +09:00
Tatsuhiro Tsujikawa
167a1102e0 Remove unused NGHTTP2_HD_MAX_BUFFER_LENGTH macro 2014-04-30 10:55:43 +09:00
Tatsuhiro Tsujikawa
d61208b394 Update doc 2014-04-30 10:49:19 +09:00
Tatsuhiro Tsujikawa
4cf023d94c Update doc 2014-04-30 10:28:50 +09:00
Tatsuhiro Tsujikawa
b5d793dee6 app_helper: Remove redundant line separator after frame debug output 2014-04-30 10:01:19 +09:00
Tatsuhiro Tsujikawa
4caddec9ba nghttp: Align NULL separated header fields 2014-04-30 09:56:33 +09:00
Tatsuhiro Tsujikawa
3b4aedd566 Add HPACK decoder public API 2014-04-29 15:53:46 +09:00
Tatsuhiro Tsujikawa
bc50062964 nghttp: Fix crash on PUSH_PROMISE 2014-04-29 15:08:57 +09:00
Tatsuhiro Tsujikawa
c69f6f4186 Use AC_DEFINE for DEBUG macro instead of adding it to CFLAGS 2014-04-29 12:58:52 +09:00
Tatsuhiro Tsujikawa
8c5db539b3 Change NGHTTP2_MAX_HEADER_TABLE_SIZE to 256MiB 2014-04-28 22:50:43 +09:00
Tatsuhiro Tsujikawa
fa8b310cfd nghttpx: Return SSL_TLSEXT_ERR_OK from servername_callback 2014-04-27 23:17:19 +09:00
Tatsuhiro Tsujikawa
6d5f402380 Add nghttp2_adjust_priority_callback
Callback function invoked to adjust priority value for request
HEADERS.

Since the application doesn’t know stream ID when it submits
requests, it may not be able to add correct priority value to HEADERS
frame and forced to use follwing PRIORITY frame. The purpose of this
callback is give the chance to the application to adjust priority
value with the latest information it has just before transmission so
that correct priority is included in HEADERS frame and it doesn’t
have to send additional PRIORITY frame.
2014-04-27 14:48:43 +09:00
Tatsuhiro Tsujikawa
cc7929bdcc Fix possible SIGFPE 2014-04-27 14:40:10 +09:00
Tatsuhiro Tsujikawa
a82b7f09c8 nghttpx: Drop HTTP/2 backend connection unless TLSv1.2 or TLSv1.1 was negotiated 2014-04-26 23:00:58 +09:00
Tatsuhiro Tsujikawa
75bfbc94dd nghttpx: Require TLSv1.2 or TLSv1.1 for NPN as well 2014-04-26 22:51:39 +09:00
Tatsuhiro Tsujikawa
6c66bd5c7c ALPN: Do not negotiate HTTP/2 unless TLSv1.2 or TLSv1.1 was used 2014-04-26 22:37:48 +09:00
Tatsuhiro Tsujikawa
cd69ed20c3 nghttpx: Select SPDY protocol in ALPN 2014-04-26 19:36:35 +09:00
Tatsuhiro Tsujikawa
a8a2236da9 nghttpx: Add --add-response-header option 2014-04-26 14:56:08 +09:00
Tatsuhiro Tsujikawa
293b717b04 nghttp: Fix bug that -H does not allow single letter header name 2014-04-26 14:53:03 +09:00
Tatsuhiro Tsujikawa
078b1de12e nghttp: Fix uninitialized pri_spec 2014-04-25 21:23:47 +09:00
Tatsuhiro Tsujikawa
d84d0b8c5c h2load: Check return of nghttp2_submit_request 2014-04-25 21:23:31 +09:00
Tatsuhiro Tsujikawa
cb6a3cf4e7 Update README.rst 2014-04-25 20:04:15 +09:00
Tatsuhiro Tsujikawa
59e42c1c69 Update doc 2014-04-25 01:39:40 +09:00
Tatsuhiro Tsujikawa
a5f715963e Update README.rst 2014-04-25 01:37:00 +09:00
Tatsuhiro Tsujikawa
d49733a5c9 Declare h2-12 for now 2014-04-25 01:33:29 +09:00
Tatsuhiro Tsujikawa
052be3296c Implement compressed DATA
The library interface supports compressed DATA.  The library does not
deflate nor inflate data payload.  When sending data, an application
has to compress data and set NGHTTP2_DATA_FLAG_COMPRESSED to
data_flags parameter in nghttp2_data_source_read_callback.  On
receiving, flags parameter in nghttp2_on_data_chunk_recv_callback
includes NGHTTP2_FLAG_COMPRESSED.  An application should check the
flags and inflate data as necessary.  Since compression context is per
frame, when DATA is seen in nghttp2_on_frame_recv_callback, an
application should reset compression context.
2014-04-25 01:27:18 +09:00
Tatsuhiro Tsujikawa
6bb410d603 Implement BLOCKED frame 2014-04-25 00:38:24 +09:00
Tatsuhiro Tsujikawa
2d4b92fc2b Merge branch 'priority' 2014-04-24 23:48:37 +09:00
Tatsuhiro Tsujikawa
ee26469cd9 Handle circular dependency
Handle the situation if a stream is told to depend on its descendant.
This is what
http://tools.ietf.org/html/draft-ietf-httpbis-http2-12#section-5.3.3
says.
2014-04-24 23:44:34 +09:00
Tatsuhiro Tsujikawa
853c9888d9 Distribute effective weight among only streams with marked as top
If stream with dpri value of no_data, we check any its descendant has
stream with dpri value of top.  If so, we have to distribute of its
portion of weight to its descendants.
2014-04-24 23:37:40 +09:00
Tatsuhiro Tsujikawa
85190f3677 Update README.rst 2014-04-24 00:00:54 +09:00
Tatsuhiro Tsujikawa
efd90c349d python: Output seqno in hpackmake.py, utilize it in hpackcheck.py 2014-04-23 00:55:54 +09:00
Tatsuhiro Tsujikawa
5aa0a0d099 Check protocol length so that scanner don't overrun buffer 2014-04-22 23:20:33 +09:00
Tatsuhiro Tsujikawa
80eb988511 h2load: Add -p, --no-tls-proto option to support SPDY without SSL/TLS
Previously h2load supports SPDY only for https URI.  This is because
SPDY has no mechanism to negotiate its protocol version without NPN.
With this change, user can specify the exact protocol version to use
when http URI (without SSL/TLS) is used.
2014-04-21 21:35:45 +09:00
Tatsuhiro Tsujikawa
a8525a131a Update .gitignore 2014-04-21 00:57:27 +09:00
Tatsuhiro Tsujikawa
6a598d8fb8 python: Fix parellel make distcheck and not distribute nghttp2.c
It seems that setup.py gets deleted before python executes it in
clean-local.  To prevent this situation, we just use .NOTPARALLEL.

Previously we distribute nghttp2.c in python, which is cythonized C
source code from nghttp2.pyx.  Since it is distributed in archive it
exists in source directory.  But we use python distutils which
operates in build directory and does not support C source files in out
of tree directory (i.e., not under the build directory).  Copying C
source file to build directory is a bit dirty, so we just decided not
to ship nghttp2.c.
2014-04-21 00:51:12 +09:00
Tatsuhiro Tsujikawa
1d5a1b895b doc: Use autoconf template nghttpx-howto.rst.in properly 2014-04-20 23:42:15 +09:00
Tatsuhiro Tsujikawa
e6fdb3418d doc: Add nghttpx-howto.rst 2014-04-20 23:35:07 +09:00
Tatsuhiro Tsujikawa
5240f8ad7e python: Update to draft version to 7 in hpackmake.py 2014-04-20 23:02:56 +09:00
Tatsuhiro Tsujikawa
91b616dd6f Update doc 2014-04-18 22:55:21 +09:00
Tatsuhiro Tsujikawa
1c1843297c priority: Add tests 2014-04-17 21:34:44 +09:00
Tatsuhiro Tsujikawa
aa4d43f31e Allow exclusive dependency to stream 0 2014-04-17 21:18:18 +09:00
Tatsuhiro Tsujikawa
ac86b51e37 Implement simplified dependency based priority 2014-04-15 22:55:07 +09:00
Tatsuhiro Tsujikawa
27a91fc30e Allow NGHTTP2_PRIORITY_TYPE_NONE in nghttp2_submit_{request,headers} 2014-04-10 23:29:56 +09:00
Tatsuhiro Tsujikawa
ece6521d26 Check stream availability when sending ALTSVC with stream_id != 0 2014-04-10 23:27:10 +09:00
Tatsuhiro Tsujikawa
792938d410 Update doc 2014-04-09 00:16:04 +09:00
Tatsuhiro Tsujikawa
9b3d5a8be5 Harden check for submit functions
nghttp2_submit_{headers,request}: Return NGHTTP2_ERR_INVAILD_ARGUMENT
if pri_spec->type is invalid.

nghttp2_submit_push_promise: Return NGHTTP2_ERR_PROTO if issued by
client.

nghttp2_submit_altsvc: Return NGHTTP2_ERR_PROTO instead of
NGHTTP2_ERR_INVALID_STATE if issued by client.
2014-04-09 00:13:11 +09:00
Tatsuhiro Tsujikawa
8658163aac Update doc 2014-04-08 23:18:59 +09:00
Tatsuhiro Tsujikawa
6326aec089 nghttpx: Return std::unique_ptr from parse_config_str_list 2014-04-08 22:44:30 +09:00
Tatsuhiro Tsujikawa
f9f6cdc93d nghttpx: Specify altsvc info in one option and allow multiple occurrences 2014-04-08 22:28:50 +09:00
Tatsuhiro Tsujikawa
5b3deec186 Fix python build on windows
Patch from Gisle Vanem

"""
I tried to build this extension on Windows, but failed since
ws2_32.lib is needed in libraries
"""
2014-04-07 22:15:15 +09:00
Tatsuhiro Tsujikawa
f3f031f94c Merge branch 'wsgi' of https://github.com/alekstorm/nghttp2 into alekstorm-wsgi 2014-04-07 21:16:24 +09:00
Alek Storm
be7aa8f53a Add experimental WSGI server implementation 2014-04-06 21:33:32 -07:00
Tatsuhiro Tsujikawa
7563839756 Update doc 2014-04-06 21:13:44 +09:00
Tatsuhiro Tsujikawa
ffcbffc28b Revert f763d76110 2014-04-06 17:56:48 +09:00
Tatsuhiro Tsujikawa
d998e79549 h2load: Link with timegm 2014-04-06 17:35:00 +09:00
Tatsuhiro Tsujikawa
1aa69e334d Fix compile error on 32-bit systems 2014-04-06 17:34:44 +09:00
Tatsuhiro Tsujikawa
1c00e715a3 Update doc 2014-04-06 17:23:42 +09:00
Tatsuhiro Tsujikawa
59c9c4511c nghttpx: Use move to insert crumbled cookies 2014-04-05 23:45:07 +09:00
Tatsuhiro Tsujikawa
f763d76110 Revert NGHTTP2_DATA_PAYLOADLEN to 4086 2014-04-05 23:42:37 +09:00
Tatsuhiro Tsujikawa
5b55874d4d Fix static analysis error 2014-04-05 20:04:09 +09:00
Tatsuhiro Tsujikawa
c9f3de5f6b python: Fix compile error 2014-04-05 19:16:40 +09:00
Tatsuhiro Tsujikawa
c2bb9c01a6 nghttp: Update doc for -p option and improve error handling for it 2014-04-05 19:15:45 +09:00
Tatsuhiro Tsujikawa
0a527f16f5 nghttpx: Log when connection was upgraded to HTTP/2 2014-04-05 18:59:22 +09:00
Tatsuhiro Tsujikawa
8f23c0c38b Name unnamed union in nghttp2_priority_spec so that we can be C90 compatible 2014-04-05 18:40:44 +09:00
Tatsuhiro Tsujikawa
c1060f0d48 Announce h2-11 2014-04-05 18:26:48 +09:00
Tatsuhiro Tsujikawa
15e8d0de7b Update tutorial 2014-04-05 18:04:21 +09:00
Tatsuhiro Tsujikawa
e7ad3633c7 nghttp2_data_source_read_callback: Replace eof with uint32_t *data_flags
Replace int *eof with uint32_t *data_flags so that we can easily
extend functionality if we have to (but we don't do if possible).
2014-04-05 17:59:24 +09:00
Tatsuhiro Tsujikawa
a0d93e7744 Fix unit test 2014-04-05 17:55:14 +09:00
Tatsuhiro Tsujikawa
124da7720f Fix compile error 2014-04-05 17:45:06 +09:00
Tatsuhiro Tsujikawa
d668d2448b Hide session option from public API
To make adding new option easier, we decided to make the details of
option struct private and hide it from public API.  We provide
functions to set individual option value.
2014-04-04 21:57:47 +09:00
Tatsuhiro Tsujikawa
21ab2f135b Connection error if client changes SETTINGS_ENABLE_PUSH to nonzero 2014-04-04 20:36:09 +09:00
Tatsuhiro Tsujikawa
1e38ceb1cd Allow empty SETTINGS in upgrade 2014-04-04 20:23:46 +09:00
Tatsuhiro Tsujikawa
2a49e164c8 nghttpx: Fix crash with HTTP/2 downstream 2014-04-03 19:14:05 +09:00
Tatsuhiro Tsujikawa
22c88af1ab nghttpx: Resume deferred DATA after complete DATA frame arrived on backend
If SPDY or HTTP/2 ustream is used and HTTP/2 downstream is used, only
call {spdylay,nghttp2}_resume_data when complete DATA frame was read
in backend to avoid to transmit too small DATA frame to the upstream.
2014-04-03 18:54:15 +09:00
Tatsuhiro Tsujikawa
5d80d18f31 Cosmetic change 2014-04-03 16:06:03 +09:00
Tatsuhiro Tsujikawa
37e1626478 Update doc 2014-04-03 16:01:30 +09:00
Tatsuhiro Tsujikawa
6cddfaf263 nghttp2_stream_resume_deferred_data: Call stream_update_dep_on_attach_data
.. instead of nghttp2_stream_attach_data() internal API
2014-04-03 15:52:21 +09:00
Tatsuhiro Tsujikawa
ac2a8ef4a2 Fix bug that transfer stuck when stream marked as top is deferred 2014-04-03 15:48:51 +09:00
Tatsuhiro Tsujikawa
b671375abc doc: Update tutorial 2014-04-03 15:22:22 +09:00
Tatsuhiro Tsujikawa
db6c41a219 nghttpx: Add altsvc related options
To advertise alternative serive, at least --altsvc-port and
--altsvc-protocol-id must be specified.
2014-04-03 13:20:50 +09:00
Tatsuhiro Tsujikawa
1d38df0a31 nghttp: Don't index authorization header field for debugging purpose 2014-04-03 11:33:15 +09:00
Tatsuhiro Tsujikawa
b1edb1f3ae Don't index name/value pair bearing NO_INDEX flag when forwarding it 2014-04-03 11:22:11 +09:00
Tatsuhiro Tsujikawa
c53c1dc669 nghttp2_session_resume_data: Return error if no deferred data exist 2014-04-03 00:01:35 +09:00
Tatsuhiro Tsujikawa
580a19e097 nghttp2_stream_detach_deferred_data -> nghttp2_stream_resume_deferred_data 2014-04-02 22:58:06 +09:00
Tatsuhiro Tsujikawa
ef40879b5f Refactor nghttp2_stream
Combine deferred_data and data into data_item and merge deferred_flags
into flags.
2014-04-02 22:55:49 +09:00
Tatsuhiro Tsujikawa
35a45f9d47 src: Initialize nghttp2_nv flags 2014-04-02 20:37:33 +09:00
Tatsuhiro Tsujikawa
2685e3405f Rename NGHTTP2_DATA_PAYLOAD_LENGTH as NGHTTP2_DATA_PAYLOADLEN 2014-04-02 20:35:07 +09:00
Tatsuhiro Tsujikawa
9c4c99bf96 Adjust transmission frame buffer size to support maximum payload size 2014-04-02 20:33:01 +09:00
Tatsuhiro Tsujikawa
9d9eb48258 python: Update according to HPACK change, but flags are not used yet 2014-04-02 02:16:39 +09:00
Tatsuhiro Tsujikawa
c9f90924a9 Add flags parameter to nghttp2_on_header_callback 2014-04-02 02:10:35 +09:00
Tatsuhiro Tsujikawa
e5b0303481 Update huffman codes 2014-04-02 01:44:39 +09:00
Tatsuhiro Tsujikawa
b1722cbe28 Update static table 2014-04-02 01:30:50 +09:00
Tatsuhiro Tsujikawa
7877b676e3 Honor NGHTTP2_NV_FLAG_NO_INDEX in deflater and inflater 2014-04-02 01:25:44 +09:00
Tatsuhiro Tsujikawa
c55df4a30b Fix compile error with --enable-werror 2014-04-02 01:18:27 +09:00
Tatsuhiro Tsujikawa
24cb90806d Add flags to nghttp2_nv structure
This is preliminary change for upcoming HPACK updates.  The flags are
used to determine the name/value pair is indexable or not.
2014-04-01 23:17:50 +09:00
Tatsuhiro Tsujikawa
da5db205ca Make group weight range [1, 256], inclusive
We do -+1 on serialization and deserialization since the field is 1
byte.  This change also parameterized range so that we can change it
easily.
2014-04-01 22:54:20 +09:00
Tatsuhiro Tsujikawa
f2d945734e Rename framebuflen as framerv, cause it is not a length 2014-04-01 21:59:26 +09:00
Tatsuhiro Tsujikawa
f5ead55f0e Check payload length when submitting GOAWAY and ALTSVC 2014-04-01 21:55:29 +09:00
Tatsuhiro Tsujikawa
f785e56dba Implement ALTSVC frame 2014-04-01 21:47:51 +09:00
Tatsuhiro Tsujikawa
b143039b60 src: Output debug data in GOAWAY in printable ascii
Non printable ascii is printed as ".".
2014-03-31 23:17:51 +09:00
Tatsuhiro Tsujikawa
6afb7442b5 Adjust weight when pushing data to queue 2014-03-31 23:01:05 +09:00
Tatsuhiro Tsujikawa
b85e2ab7f7 Share stream_group weight among streams marked as top 2014-03-31 22:51:33 +09:00
Tatsuhiro Tsujikawa
f011bda377 src: Compile defaltehd and inflatehd with c++
This commit also fixes defaltehd always reports output length is 0.
2014-03-30 22:41:02 +09:00
Tatsuhiro Tsujikawa
34581d830d Define NGHTTP2_CLEARTEXT_PROTO_VERSION_ID
This identifier string is used if HTTP/2 is used over cleartext TCP.
2014-03-30 21:30:47 +09:00
Tatsuhiro Tsujikawa
334658044e Update tutorial 2014-03-30 21:08:04 +09:00
Tatsuhiro Tsujikawa
60a2c260a5 Define NGHTTP2_CLIENT_CONNECTION_PREFACE macro
NGHTTP2_CLIENT_CONNECTION_PREFACE has the same content with
NGHTTP2_CLIENT_CONNECTION_HEADER, which is now obsoleted by
NGHTTP2_CLIENT_CONNECTION_PREFACE.
2014-03-30 21:02:25 +09:00
Tatsuhiro Tsujikawa
ab2dc5967d Replace HTTP/2.0 with HTTP/2 2014-03-30 19:26:37 +09:00
Tatsuhiro Tsujikawa
705051ceb7 Update doc 2014-03-30 19:00:42 +09:00
Tatsuhiro Tsujikawa
d3962becf4 Ignore priority request if resultant tree has cycle 2014-03-30 18:48:32 +09:00
Tatsuhiro Tsujikawa
21d5986157 Fail nghttp2_submit_settings if there is pending SETTINGS frame in-flight
pending_local_max_concurrent_stream is now set in
nghttp2_session_add_settings, rather than after frame was sent.
2014-03-30 18:07:52 +09:00
Tatsuhiro Tsujikawa
74daa16a1c Retain incoming closed streams for dependency tree
The number of closed stream to keep is limited by
MAX_CONCURRENT_STREAMS - current active stream.
2014-03-30 17:41:54 +09:00
Tatsuhiro Tsujikawa
a9d97d9d35 Update doc 2014-03-30 01:34:13 +09:00
Tatsuhiro Tsujikawa
bd7d335d9a src: Print padlen first 2014-03-30 01:24:17 +09:00
Tatsuhiro Tsujikawa
c12b6bc360 Call on_stream_close_callback for a stream in reserved state
This is useful because application may allocate resources for it and
wants to free the resources if they are not used anymore.
2014-03-30 01:24:16 +09:00
Tatsuhiro Tsujikawa
58da463ad6 Make deflater bad state if parsing HEADERS/PUSH_PROMISE failed 2014-03-30 01:24:16 +09:00
Tatsuhiro Tsujikawa
36c8de9da5 Limit the number of streams in one dependency tree 2014-03-30 01:24:16 +09:00
Tatsuhiro Tsujikawa
f7162ab702 Implement dependency based priority 2014-03-30 01:24:16 +09:00
Tatsuhiro Tsujikawa
8ccb6e463d nghttpx: Use raw-string iteral to output help 2014-03-22 22:03:13 +09:00
Tatsuhiro Tsujikawa
e4dacb2f6f nghttpd: Use raw-string literal to output help 2014-03-22 21:35:00 +09:00
Tatsuhiro Tsujikawa
dbb82b0f9c Make opaque_data parameter in nghttp2_submit_goaway const 2014-03-22 19:05:58 +09:00
Tatsuhiro Tsujikawa
e1eebf08fb Support DEBUG_DATA in GOAWAY again 2014-03-22 18:59:59 +09:00
Tatsuhiro Tsujikawa
01586f473d Wrap small inbound buffer by nghttp_buf 2014-03-22 18:27:38 +09:00
Tatsuhiro Tsujikawa
3c631b5625 Code cleanup 2014-03-22 17:16:25 +09:00
Tatsuhiro Tsujikawa
774cf88f68 Don't add RST_STREAM frame if stream is in NGHTTP2_STREAM_CLOSING 2014-03-22 00:51:40 +09:00
Tatsuhiro Tsujikawa
0a80b0c1aa nghttpd: Set write timeout for stream blocked by flow controll only
This change also reset read timeout when we have sent HEADERS,
PUSH_PROMISE or DATA.
2014-03-22 00:41:01 +09:00
Tatsuhiro Tsujikawa
1dfe2f8670 Add nghttp2_session_get_stream_remote_window_size public API function 2014-03-22 00:34:25 +09:00
Tatsuhiro Tsujikawa
fac42788bc nghttpd: Rename Request as Stream 2014-03-21 23:26:53 +09:00
Tatsuhiro Tsujikawa
464fef7c6e nghttpd: Add HTTP/2 stream read/write timeout 2014-03-21 23:07:20 +09:00
Tatsuhiro Tsujikawa
334656b704 nghttpx: Announce 2.0 in via header field 2014-03-21 19:25:46 +09:00
Tatsuhiro Tsujikawa
ba5d9d3352 nghttpx: Add x-forwarded-proto header field to downstream HTTP/2 request 2014-03-21 18:57:57 +09:00
Tatsuhiro Tsujikawa
d0fbbe6932 Less cryptic debug message 2014-03-20 00:27:39 +09:00
Tatsuhiro Tsujikawa
c945d4ebbe Fix compile error with --enable-debug 2014-03-19 23:24:46 +09:00
Tatsuhiro Tsujikawa
c048ac5eff nghttpd: Avoid to use bufferevent for connection 2014-03-18 00:09:59 +09:00
Tatsuhiro Tsujikawa
68b392817b nghttp2_bufs: Add chunk_keep to specify the number of buffers to keep on reset 2014-03-16 21:38:13 +09:00
Tatsuhiro Tsujikawa
fbfa3adc42 h2load: traffic bytes data should be based on all traffic 2014-03-16 19:36:41 +09:00
Tatsuhiro Tsujikawa
775d07ace4 EvbufferBuffer: Optimize a bit 2014-03-15 16:23:12 +09:00
Tatsuhiro Tsujikawa
fef01a3c39 nghttpd: Honor server's cipher preferece order 2014-03-15 16:11:30 +09:00
Tatsuhiro Tsujikawa
a7eb6502a9 src: Use large transmission buffer to reduce SSL/TLS overhead 2014-03-15 16:10:42 +09:00
Tatsuhiro Tsujikawa
a457d2a138 h2load, nghttp: Use SNI field for non-numeric host 2014-03-15 15:32:38 +09:00
Tatsuhiro Tsujikawa
843ecd8cc1 h2load: Support multiple URIs
Supplying multiple URIs can simulate more real life situation on
server side.  For example, we can supply URIs of html, css and js and
benchmark the server.  The -m option is updated so that it defaults to
the number of supplied URIs.
2014-03-14 23:15:01 +09:00
Tatsuhiro Tsujikawa
5b81f7c713 Don't show PAD_HIGH and PAD_LOW flags to user callback 2014-03-14 21:53:03 +09:00
Tatsuhiro Tsujikawa
2ec4b10805 Add nghttp2_buf tests 2014-03-14 21:40:14 +09:00
Tatsuhiro Tsujikawa
781d1a2b70 Code cleanup 2014-03-14 02:23:50 +09:00
Tatsuhiro Tsujikawa
7ffa594d4c nghttp: Use raw-literal string for help output 2014-03-14 01:53:28 +09:00
Tatsuhiro Tsujikawa
136d997596 h2load: Support -W option for SPDY, if the value >= 16 2014-03-14 01:40:41 +09:00
Tatsuhiro Tsujikawa
2b7627f70c h2load: Use raw-string literal for help output 2014-03-14 01:06:47 +09:00
Tatsuhiro Tsujikawa
0620052f50 src: Use std::numeric_limits<>::max() instead of INT64_MAX 2014-03-14 00:54:10 +09:00
Tatsuhiro Tsujikawa
142b433533 nghttp2_hd: Treat prematurely ended compressed header block as error properly 2014-03-14 00:52:44 +09:00
Tatsuhiro Tsujikawa
b464cb78ac Fix tests to reflect their intent 2014-03-14 00:25:43 +09:00
Tatsuhiro Tsujikawa
344d663e90 deflate_hd: Fix mishandled error return from emit_indexed_block 2014-03-14 00:25:03 +09:00
Tatsuhiro Tsujikawa
d48eca60cf Fix broken nghttp2_bufs_avail 2014-03-14 00:24:34 +09:00
Tatsuhiro Tsujikawa
aefc0d1ebb Use calloc instead of malloc + memset; remove useless memset 2014-03-13 23:27:14 +09:00
Tatsuhiro Tsujikawa
1be8d1b797 inflate_header_block: Issue RST_STREAM if header decompression failed 2014-03-13 23:02:33 +09:00
Tatsuhiro Tsujikawa
0fa4779d38 Don't call on_frame_recv_callback after stream close or being closed 2014-03-13 22:49:37 +09:00
Tatsuhiro Tsujikawa
d07bb1ddff Rework outbound frame buffers 2014-03-13 22:11:02 +09:00
Tatsuhiro Tsujikawa
0666a73e10 Remove nghttp2_buffer 2014-03-11 21:20:51 +09:00
Tatsuhiro Tsujikawa
3f56c938d8 nghttp2_hd: Use nghttp2_bufs, avoiding realloc() 2014-03-11 21:18:28 +09:00
Tatsuhiro Tsujikawa
7b2d585896 Return 0 if nghttp2_session_prep_frame succeeds rather than frame length 2014-03-11 02:15:05 +09:00
Tatsuhiro Tsujikawa
74f899fc01 Replace NGHTTP2_MAX_FRAME_LENGTH with NGHTTP2_MAX_PAYLOADLEN 2014-03-11 02:01:08 +09:00
Tatsuhiro Tsujikawa
e803c6b65e Replace NGHTTP2_FRAME_HEAD_LENGTH with NGHTTP2_FRAME_HDLEN 2014-03-11 01:55:42 +09:00
Tatsuhiro Tsujikawa
358b4386d3 Introduce nghttp2_buf to ease buffer management 2014-03-11 01:47:38 +09:00
Tatsuhiro Tsujikawa
d074cb611f nghttpx: Add rate limit per worker thread
The existing options --{read,write}-{rate,burst} are per connection.
The new options --worker-{read,write}-{rate,burst} are per worker
thread, which is overall rate limit of all connections worker handles.
2014-03-09 14:53:28 +09:00
Tatsuhiro Tsujikawa
54dab50015 Support END_SEGMENT in nghttp2_submit_data() 2014-03-06 00:19:02 +09:00
Tatsuhiro Tsujikawa
b60679808b Filter supported flags in received frame 2014-03-05 23:25:42 +09:00
Tatsuhiro Tsujikawa
547d6d1fb5 Use 4086 as max DATA payload size to make DATA frame fit into 4K buffer 2014-03-05 22:48:17 +09:00
Tatsuhiro Tsujikawa
382024a180 Update README.rst 2014-03-05 21:46:26 +09:00
Tatsuhiro Tsujikawa
12d92a621d Add --with-spdylay configure option 2014-03-05 21:44:28 +09:00
Tatsuhiro Tsujikawa
7f18eced0b src: Use jemalloc if available 2014-03-05 21:38:33 +09:00
Tatsuhiro Tsujikawa
27e161dc31 src: Add EvbufferBuffer class to simplify the code base 2014-03-05 00:23:33 +09:00
Tatsuhiro Tsujikawa
3ca4539f99 nghttpd: Only emit content-encoding: gzip when error-gzip is on 2014-03-04 23:37:42 +09:00
Tatsuhiro Tsujikawa
ddfa93ff5b nghttpx: Make socketpair in internal use non-block 2014-03-04 23:34:48 +09:00
Tatsuhiro Tsujikawa
a61ca763df nghttpd: Add -e option to toggle gzipped error response 2014-03-04 23:29:30 +09:00
Tatsuhiro Tsujikawa
749cc08f52 Update README.rst 2014-03-04 23:16:46 +09:00
Tatsuhiro Tsujikawa
caeeba681f nghttpd: Add multi threading support 2014-03-04 23:14:26 +09:00
Tatsuhiro Tsujikawa
d4ea2418d8 nghttpx: Use LibsslGlobalLock 2014-03-04 21:33:43 +09:00
Tatsuhiro Tsujikawa
73f55e7b7a h2load: Fix crash with multi threading 2014-03-04 21:32:57 +09:00
Tatsuhiro Tsujikawa
13be30e582 Update src/.gitignore 2014-03-04 01:16:42 +09:00
Tatsuhiro Tsujikawa
60fc117c9c Update README.rst 2014-03-04 01:16:15 +09:00
Tatsuhiro Tsujikawa
5cfb51c881 h2load: Support parellel native threads execution with -t option 2014-03-04 01:12:11 +09:00
Tatsuhiro Tsujikawa
f3183efe04 nghttpd: Emit protocol id error only when verbose output is enabled 2014-03-03 23:51:46 +09:00
Tatsuhiro Tsujikawa
b5341ebac6 nghttpd: More SSL_CTX options and support EDCHE cipher 2014-03-03 23:49:13 +09:00
Tatsuhiro Tsujikawa
e34b8ac7fb src: Avoid to call costly evbuffer_add() repeatedly
The profiler and benchmarking showed that calling evbuffer_add()
repeatedly is very costly. To avoid this, we buffer up small writes
into one large chunk and call evbuffer_add() less times.
2014-03-03 23:45:57 +09:00
antbryan
58485bd1d8 Update README.rst
some small additions for clarity. please check to ensure meaning is preserved.
2014-03-02 13:52:08 -05:00
Tatsuhiro Tsujikawa
3d211e1cfd h2load: Fix compiler warning 2014-03-03 00:53:31 +09:00
Tatsuhiro Tsujikawa
79c5032708 nghttp2_hd: Fix crash with multiple threads execution 2014-03-03 00:53:04 +09:00
Tatsuhiro Tsujikawa
e06cc13edb Update README.rst 2014-03-02 23:45:47 +09:00
Tatsuhiro Tsujikawa
1337fa8bfe src: Add h2load, benchmarking tool for HTTP/2 and SPDY 2014-03-02 23:45:47 +09:00
Tatsuhiro Tsujikawa
c1e1a1be5a nghttpx: Cast uint8_t to uint32_t to display it as numeric value 2014-03-02 23:22:28 +09:00
Tatsuhiro Tsujikawa
979feaecc6 Mitigate heap fragmentation when lots of concurrent http2 sessions run 2014-03-02 16:34:23 +09:00
Tatsuhiro Tsujikawa
6f0b9128b4 tests: Fix leak 2014-02-28 03:07:19 +09:00
Tatsuhiro Tsujikawa
a85455ed0b src: Zero clear http_parser_url struct 2014-02-27 22:11:31 +09:00
Tatsuhiro Tsujikawa
227a48cea1 src: Move http_parser_url related functions to util 2014-02-27 21:53:52 +09:00
Tatsuhiro Tsujikawa
ddf6162528 python: Use distutils to build/install python bindings
automake version is a bit picky about installation location and uses
different compiler flags from distutils. We prefer official distutils.
Now nghttp2.c, which is generated by cython, is distributed in
archive.
2014-02-27 00:58:21 +09:00
Tatsuhiro Tsujikawa
abdbd29d5f Update doc 2014-02-27 00:01:35 +09:00
Tatsuhiro Tsujikawa
1fee4fd2df nghttp2_hd: Fail inflate immediately if ctx.bad is nonzero
Doing inflation after error produces invalid results, especially, if
it is in NGHTTP2_HD_STATE_READ_INDEX, the inflater->left could be 0,
which causes assertion error.  Add sanity assertion for index
2014-02-26 23:20:52 +09:00
Tatsuhiro Tsujikawa
18357512ed nghttp2_hd: Fix integer decoding bug 2014-02-26 21:44:48 +09:00
Tatsuhiro Tsujikawa
52cec35906 python: Fix NameError if asyncio is not available 2014-02-26 00:54:16 +09:00
Tatsuhiro Tsujikawa
13cc3f2fe9 src, python: Only produce header_table_size when it is changed 2014-02-25 23:55:06 +09:00
Tatsuhiro Tsujikawa
7cf574d0d8 python: Update doc 2014-02-25 23:29:50 +09:00
Tatsuhiro Tsujikawa
57af995fd0 Update README.rst 2014-02-25 23:29:50 +09:00
Tatsuhiro Tsujikawa
911ffb24fa python: Set NPN in HTTP2Server.__init__ 2014-02-25 21:25:31 +09:00
Tatsuhiro Tsujikawa
add07c4303 python: Encode headers in UTF-8 if they are given in unicode 2014-02-25 21:21:35 +09:00
Tatsuhiro Tsujikawa
4dcb68d128 Update README.rst 2014-02-25 01:47:09 +09:00
Tatsuhiro Tsujikawa
98715f4374 python: Add HTTP/2 server using asyncio 2014-02-25 01:47:09 +09:00
Tatsuhiro Tsujikawa
9cc7f9fb36 nghttp: Wait for pushed resources to complete
The statistics of pushed resources are also calculated.
2014-02-25 01:16:47 +09:00
Tatsuhiro Tsujikawa
d1c1deaf03 Add promised_stream_user_data parameter to nghttp2_submit_push_promise
This is very useful to associate application specific data to promised
stream.

nghttp2_nv_array_copy now does not complain the header field is large.
2014-02-25 00:26:12 +09:00
Tatsuhiro Tsujikawa
86aa905c10 nghttpx: Code cleanup 2014-02-22 11:26:32 +09:00
Tatsuhiro Tsujikawa
09fd95ac5c Revert 6c51bd0979 2014-02-21 23:04:40 +09:00
Tatsuhiro Tsujikawa
5e88be0b2c Update doc 2014-02-21 21:29:23 +09:00
Tatsuhiro Tsujikawa
fc25143418 Remove END_PUSH_PROMISE in favor of END_HEADERS 2014-02-21 21:23:51 +09:00
Tatsuhiro Tsujikawa
a21175398d Add test to verify that deflater can handle initial buf smaller than default 2014-02-21 21:18:46 +09:00
Tatsuhiro Tsujikawa
6c51bd0979 src: Disable strip http-parser mode
Add --enable-strict-http-parser mode to enable it.
2014-02-21 00:20:27 +09:00
Tatsuhiro Tsujikawa
dc82a6026e Add --enable-debug configure option to define DEBUGBUILD macro 2014-02-20 23:19:55 +09:00
Tatsuhiro Tsujikawa
f152dd8881 Rename --enable-maintainer-mode configure option as --enable-werror 2014-02-20 23:17:04 +09:00
Tatsuhiro Tsujikawa
9703c5de5c Code cleanup 2014-02-20 23:12:42 +09:00
Tatsuhiro Tsujikawa
3395f7158f Strict handling of max concurrent streams
Exceeding ACKed max concurrent streams results in connection error.
This change fixes the bug that num_{incoming,outgoing}_streams
is decremented wrongly if a stream is in reserved state and
RST_STREAM is send and its state is changed to NGHTTP2_STREAM_CLOSING.
This change also fixes the bug that transmission of push response
HEADERS does not increase num_outgoing_streams.
2014-02-20 23:10:32 +09:00
Tatsuhiro Tsujikawa
659c3b0aa0 nghttpx: Initialize response_htp_ with 0 2014-02-20 21:30:25 +09:00
Tatsuhiro Tsujikawa
46f5d4b1c4 examples: Check stream_data is null 2014-02-20 21:30:05 +09:00
Tatsuhiro Tsujikawa
bbc09b005b nghttpd: Use nghttp2_session_mem_recv and nghttp2_session_mem_send 2014-02-19 23:27:53 +09:00
Tatsuhiro Tsujikawa
1e1e77ad5e nghttp: Use nghttp2_session_mem_recv and nghttp2_session_mem_send 2014-02-19 23:27:52 +09:00
Tatsuhiro Tsujikawa
cdf5d5402b nghttpx: Code cleanup 2014-02-19 23:11:26 +09:00
Tatsuhiro Tsujikawa
3c96041c43 nghttpx: Fix typo 2014-02-19 22:53:33 +09:00
Tatsuhiro Tsujikawa
6320bd8926 nghttpx: Fix priority bits 2014-02-19 22:52:00 +09:00
Tatsuhiro Tsujikawa
30b3855194 nghttp2_session_mem_send tiny doc fix
Patch from Dave Beckett
2014-02-19 21:18:24 +09:00
Tatsuhiro Tsujikawa
4ced1c1622 Code cleanup 2014-02-19 01:08:52 +09:00
Tatsuhiro Tsujikawa
2966ad2d15 Update doc 2014-02-19 00:16:25 +09:00
Tatsuhiro Tsujikawa
649586fff6 Add nghttp2_session_mem_send() API function
This function behaves like nghttp2_session_send(), but it does not
use nghttp2_send_callback to send data. Instead, it returns the
serialized data to trasmit and its length to the caller.
2014-02-18 23:23:11 +09:00
Tatsuhiro Tsujikawa
a9991133af Add man page generation script using help2man 2014-02-16 19:44:28 +09:00
Tatsuhiro Tsujikawa
f5342494f4 src: Format help message and add --version to make man page generation easier 2014-02-16 19:39:41 +09:00
Tatsuhiro Tsujikawa
1fd5fdd54a src: Remove redundant SETTINGS_ENABLE_PUSH from server side 2014-02-16 16:05:26 +09:00
Tatsuhiro Tsujikawa
88607f09e5 Update doc 2014-02-16 15:50:36 +09:00
Tatsuhiro Tsujikawa
c7c496b029 Update doc 2014-02-16 15:30:46 +09:00
Tatsuhiro Tsujikawa
5cc24cb7c2 Merge branch 'draft-10' 2014-02-16 15:29:21 +09:00
Tatsuhiro Tsujikawa
a5353c22a6 doc: Add link to 0.3.1 release 2014-02-16 15:23:02 +09:00
Tatsuhiro Tsujikawa
b2ab5178a3 Bump up version number to 0.4.0-DEV 2014-02-16 15:20:56 +09:00
Tatsuhiro Tsujikawa
27b3091ab6 Update README.rst 2014-02-15 20:00:51 +09:00
Tatsuhiro Tsujikawa
62b73133e5 Code cleanup 2014-02-15 18:56:20 +09:00
Tatsuhiro Tsujikawa
66832e9f4e Allow NGHTTP2_FLAG_END_SEGMENT in nghttp2_submit_headers() 2014-02-15 18:55:52 +09:00
Tatsuhiro Tsujikawa
53302406d3 Merge branch 'master' into draft-10 2014-02-15 18:43:16 +09:00
Tatsuhiro Tsujikawa
bc0ce40dc2 Update README.rst 2014-02-15 17:20:47 +09:00
Tatsuhiro Tsujikawa
652dc250fd Update README.rst 2014-02-15 17:19:49 +09:00
Tatsuhiro Tsujikawa
0da79865b8 Don't set PAD_HIGH and PAD_LOW flags to HEADERS/PUSH_PROMISE object to user cb 2014-02-15 17:12:17 +09:00
Tatsuhiro Tsujikawa
7504d89f9b src: Add at most N bytes as padding if --padding option is used 2014-02-15 16:40:32 +09:00
Tatsuhiro Tsujikawa
3f3f258cd6 Add padding to PUSH_PROMISE 2014-02-15 16:30:43 +09:00
Tatsuhiro Tsujikawa
1e95c8b313 Allow always max 1024 padding for HEADERS
We need paddings regardless of payload and frame boundary to mitigate
certain attacks.

Since we handles CONTINUATION internally, we don't show FLAG_PAD_HIGH
and PAD_LOW flags of HEADERS in nghttp/nghttpd. We just show the
total paddings in HEADERS + CONTINUATION.
2014-02-15 01:34:04 +09:00
Tatsuhiro Tsujikawa
622f783675 Disallow PUSH_PROMISE from client side 2014-02-14 16:12:04 +09:00
Tatsuhiro Tsujikawa
7ab4206269 Tear down connection if SETTINGS makes window size overflow 2014-02-14 16:08:39 +09:00
Tatsuhiro Tsujikawa
e74fbdf6b4 Merge branch 'master' into draft-10 2014-02-13 23:45:41 +09:00
Tatsuhiro Tsujikawa
fd88c6160d HPACK post -05 updates
* Use 1 Huffman code table for both request and response
* Remove complicated deflater side table size management
* Add encoding context update
* Fix memory leak in inflater
2014-02-13 23:22:52 +09:00
Tatsuhiro Tsujikawa
082876d92d Contribute flow control window for each byte PAD_HIGH and _LOW in DATA
This may help the pathological situation where window is too small.
2014-02-12 21:35:40 +09:00
Tatsuhiro Tsujikawa
c7a17093cb Fix compile errors with --enable-maintainer-mode 2014-02-11 21:39:35 +09:00
Tatsuhiro Tsujikawa
bac31e844a Add more debug output 2014-02-11 21:35:41 +09:00
Tatsuhiro Tsujikawa
067a4d05c3 Merge branch 'master' into draft-10 2014-02-11 21:33:49 +09:00
Tatsuhiro Tsujikawa
7822bbd7e8 Fix PAD_HIGH and PAD_LOW are not counted in flow control 2014-02-11 21:30:44 +09:00
Tatsuhiro Tsujikawa
cd3eae3dd2 src: Fix select_padding_callback which returns value greater than max_payload 2014-02-11 19:19:00 +09:00
Tatsuhiro Tsujikawa
dbb131d13d Simplify framebufmark calculation for DATA frame 2014-02-11 18:55:22 +09:00
Tatsuhiro Tsujikawa
6364ae1a98 Fix nghttp2_active_outbound_item is not reset on DATA deferred 2014-02-11 18:43:45 +09:00
Tatsuhiro Tsujikawa
16b5e99e88 Bitwise-OR last CONTINUATION flags to first HEADERS flags 2014-02-11 17:30:38 +09:00
Tatsuhiro Tsujikawa
c280cc7c4d nghttpx: Add --padding option for debugging purpose 2014-02-11 17:23:22 +09:00
Tatsuhiro Tsujikawa
788072af9b Fix HEADERS padding is not added 2014-02-11 17:23:08 +09:00
Tatsuhiro Tsujikawa
cf0b880b15 Error if undefined SETTINGS ID is detected in nghttp2_iv_check 2014-02-11 16:53:08 +09:00
Tatsuhiro Tsujikawa
39fe7a5cfa Don't set select_padding_callback if padding_boundary is 0 or not set 2014-02-11 16:48:27 +09:00
Tatsuhiro Tsujikawa
3144bcbe20 Remove unused iframe->error_code 2014-02-11 16:34:42 +09:00
Tatsuhiro Tsujikawa
eb2856f3df Add inbound_frame_reset_left() not to forget to reset iframe->buflen 2014-02-11 16:33:07 +09:00
Tatsuhiro Tsujikawa
9865b46905 Don't change state in inbound_frame_handle_pad 2014-02-11 16:24:21 +09:00
Tatsuhiro Tsujikawa
6c40928fed Merge branch 'master' into draft-10
Conflicts:
	lib/nghttp2_session.c
2014-02-11 16:18:22 +09:00
Tatsuhiro Tsujikawa
109b8cedde Fix compile error and test failures 2014-02-11 16:12:26 +09:00
Tatsuhiro Tsujikawa
e78a2100ec Merge branch 'master' into draft-10 2014-02-11 16:03:42 +09:00
Tatsuhiro Tsujikawa
78d202ac30 Callback based padding from application
Now previous padding options are removed and instead we added
select_padding_callback to select padding length for each frame
by application. If this callback is not implemented by application,
no padding is added.

This change also fixes the broken session_detect_idle_stream()
if stream_id is our side.
2014-02-11 15:28:44 +09:00
Tatsuhiro Tsujikawa
118ed09da5 Allow disabling padding 2014-02-09 23:53:53 +09:00
Tatsuhiro Tsujikawa
5b58b4ace5 Add padding if payload length is zero 2014-02-09 22:01:07 +09:00
Tatsuhiro Tsujikawa
68b5ffc1dc Rename padding related names 2014-02-09 21:46:15 +09:00
Tatsuhiro Tsujikawa
ce53d7bd9e src: Don't output priority if NGHTTP2_FLAG_PRIORITY is not set 2014-02-09 21:34:28 +09:00
Tatsuhiro Tsujikawa
c7c283f3a9 nghttpx: Add --frontend-frame-debug option to debug HTTP/2 frame in upstream
The output format is the same one with nghttp/nghttpd. The output
is made into stderr to make it sync with logging.
2014-02-09 18:47:26 +09:00
Tatsuhiro Tsujikawa
909b79e69b Fix test 2014-02-09 18:33:27 +09:00
Tatsuhiro Tsujikawa
256c97d89b Change default padding size to 16 2014-02-09 18:27:34 +09:00
Tatsuhiro Tsujikawa
ba95cd936d Fix flow control error because padding is excluded 2014-02-09 18:26:46 +09:00
Tatsuhiro Tsujikawa
72e2e145c5 Revert 748f6e65bd 2014-02-09 17:42:20 +09:00
Tatsuhiro Tsujikawa
2c4dc08aee Merge branch 'master' into draft-10 2014-02-09 17:40:53 +09:00
Tatsuhiro Tsujikawa
5884794a7f Merge branch 'master' into draft-10 2014-02-09 17:14:07 +09:00
Tatsuhiro Tsujikawa
dffa078c11 Update doc 2014-02-09 16:49:24 +09:00
Tatsuhiro Tsujikawa
945c57c335 Add test for nghttp2_frame_pack_headers with padding 2014-02-09 15:17:26 +09:00
Tatsuhiro Tsujikawa
1db2195389 Implement padding for HEADERS and CONTINUATION 2014-02-09 15:17:26 +09:00
Tatsuhiro Tsujikawa
10feab02e8 Fix bufoff_ptr if no padding is made 2014-02-09 12:39:43 +09:00
Tatsuhiro Tsujikawa
9c30ed1a64 Update flags 2014-02-09 12:39:43 +09:00
Tatsuhiro Tsujikawa
e9d1ba2539 Handle incoming PAD_HIGH and PAD_LOW in inbound_frame_handle_padding 2014-02-09 12:39:43 +09:00
Tatsuhiro Tsujikawa
2ff3d97b2e Add nghttp2_frame_add_pad to deal with adding pads 2014-02-09 12:39:43 +09:00
Tatsuhiro Tsujikawa
748f6e65bd Run test after doc so that we can see test result in terminal 2014-02-08 17:44:34 +09:00
Tatsuhiro Tsujikawa
52a1f56d14 Merge branch 'master' into draft-10 2014-02-08 16:44:09 +09:00
Tatsuhiro Tsujikawa
b6a0eff8a8 Add more DEBUGFs 2014-02-08 00:32:50 +09:00
Tatsuhiro Tsujikawa
be9d5efa4c nghttp: Add --continuation option to test CONTINUATION 2014-02-08 00:23:18 +09:00
Tatsuhiro Tsujikawa
814d0f76f3 Implement DATA frame padding 2014-02-08 00:23:18 +09:00
Tatsuhiro Tsujikawa
f26270b5b4 Change SETTINGS payload format according to the spec 2014-02-06 22:06:42 +09:00
Tatsuhiro Tsujikawa
d584888601 Renumber frame types, flags and error codes 2014-02-06 21:49:16 +09:00
Tatsuhiro Tsujikawa
40a5756564 Terminate connection if unknown frame type is received 2014-02-06 21:42:49 +09:00
Tatsuhiro Tsujikawa
f2c654f898 Fix reception of ENABLE_PUSH ignored; strict check for SETTINGS value 2014-02-06 21:39:58 +09:00
Tatsuhiro Tsujikawa
112b49cb9a Renumber SETTINGS 2014-02-06 00:26:12 +09:00
Tatsuhiro Tsujikawa
c79adf6997 Remove flow control disabling feature 2014-02-06 00:23:20 +09:00
Tatsuhiro Tsujikawa
196406da0e Change protocol identifier to h2-10 2014-02-05 23:37:27 +09:00
211 changed files with 27689 additions and 18021 deletions

1
.gitignore vendored
View File

@@ -35,3 +35,4 @@ doc/nghttp2ver.h.rst
doc/package_README.rst
doc/tutorial-client.rst
doc/tutorial-server.rst
doc/nghttpx-howto.rst

View File

@@ -1,4 +1,4 @@
# nghttp2 - HTTP/2.0 C Library
# nghttp2 - HTTP/2 C Library
# Copyright (c) 2012 Tatsuhiro Tsujikawa

View File

@@ -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()

View File

@@ -1,6 +1,6 @@
#!/bin/sh
#
# nghttp2 - HTTP/2.0 C Library
# nghttp2 - HTTP/2 C Library
#
# Copyright (c) 2013 Tatsuhiro Tsujikawa
#

View File

@@ -1,6 +1,6 @@
#!/bin/sh
#
# nghttp2 - HTTP/2.0 C Library
# nghttp2 - HTTP/2 C Library
#
# Copyright (c) 2013 Tatsuhiro Tsujikawa
#

View File

@@ -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)
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.])
# 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 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}

View File

@@ -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 \

View File

@@ -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 %}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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

View File

@@ -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.

View File

@@ -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
View 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
View File

@@ -0,0 +1,3 @@
[SEE ALSO]
nghttp(1), nghttpd(1), nghttpx(1)

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env python
# nghttp2 - HTTP/2.0 C Library
# nghttp2 - HTTP/2 C Library
# Copyright (c) 2012 Tatsuhiro Tsujikawa

View File

@@ -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
View File

@@ -0,0 +1,3 @@
[SEE ALSO]
nghttpd(1), nghttpx(1), h2load(1)

View File

@@ -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
View File

@@ -0,0 +1,3 @@
[SEE ALSO]
nghttp(1), nghttpx(1), h2load(1)

1
doc/nghttpx-howto.rst.in Normal file
View File

@@ -0,0 +1 @@
.. include:: @top_srcdir@/doc/sources/nghttpx-howto.rst

View File

@@ -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
View File

@@ -0,0 +1,3 @@
[SEE ALSO]
nghttp(1), nghttpd(1), h2load(1)

1
doc/python-apiref.rst Normal file
View File

@@ -0,0 +1 @@
.. include:: ../doc/sources/python-apiref.rst

View File

@@ -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

View 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.

View 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()

View File

@@ -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.

View File

@@ -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;

View File

@@ -1,4 +1,4 @@
# nghttp2 - HTTP/2.0 C Library
# nghttp2 - HTTP/2 C Library
# Copyright (c) 2012 Tatsuhiro Tsujikawa

View File

@@ -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);

View File

@@ -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

View File

@@ -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) {

View File

@@ -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 \

View File

@@ -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

View File

@@ -1,5 +1,5 @@
/*
* nghttp2 - HTTP/2.0 C Library
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa
*

View File

@@ -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
View 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
View 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 */

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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 */

File diff suppressed because it is too large Load Diff

View File

@@ -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 */

View File

@@ -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;
}
}
return 0;
}
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)
{
if(side == NGHTTP2_HD_SIDE_REQUEST) {
ctx->huff_decode_table = req_huff_decode_table;
} else {
ctx->huff_decode_table = res_huff_decode_table;
}
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;

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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;) {

View File

@@ -1,5 +1,5 @@
/*
* nghttp2 - HTTP/2.0 C Library
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2012 Tatsuhiro Tsujikawa
*

View File

@@ -1,5 +1,5 @@
/*
* nghttp2 - HTTP/2.0 C Library
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2012 Tatsuhiro Tsujikawa
*

View File

@@ -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 */
}
}

View File

@@ -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
View 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
View 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 */

View File

@@ -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;

View File

@@ -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;
/*

View File

@@ -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;

View File

@@ -1,5 +1,5 @@
/*
* nghttp2 - HTTP/2.0 C Library
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2012 Tatsuhiro Tsujikawa
*

View 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;
}

View File

@@ -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 */

View File

@@ -1,5 +1,5 @@
/*
* nghttp2 - HTTP/2.0 C Library
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2012 Tatsuhiro Tsujikawa
*

View File

@@ -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

View File

@@ -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 */

View File

@@ -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;
}
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;
}
void nghttp2_stream_detach_deferred_data(nghttp2_stream *stream)
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;
}

View File

@@ -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 */

View File

@@ -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(&copy_pri_spec);
} else {
nghttp2_priority_spec_default_init(&copy_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,
&copy_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(&copy_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, &copy_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);
}

View File

@@ -1,5 +1,5 @@
/*
* nghttp2 - HTTP/2.0 C Library
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2012 Tatsuhiro Tsujikawa
*

View File

@@ -1,5 +1,5 @@
/*
* nghttp2 - HTTP/2.0 C Library
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa
*

View 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
View 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

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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))

View File

@@ -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:

View File

@@ -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()

View File

@@ -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
View 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
View File

@@ -9,3 +9,4 @@ test-suite.log
libnghttpx.a
deflatehd
inflatehd
h2load

View File

@@ -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

View File

@@ -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

View File

@@ -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 {

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -1,5 +1,5 @@
/*
* nghttp2 - HTTP/2.0 C Library
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2013 Tatsuhiro Tsujikawa
*

View File

@@ -1,5 +1,5 @@
/*
* nghttp2 - HTTP/2.0 C Library
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2013 Tatsuhiro Tsujikawa
*
@@ -27,11 +27,7 @@
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)

View File

@@ -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

View File

@@ -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
View 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
View 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