Compare commits

..

141 Commits

Author SHA1 Message Date
Tatsuhiro Tsujikawa
92a20c76e6 Update bash_completion 2015-05-15 23:01:33 +09:00
Tatsuhiro Tsujikawa
42ccea806c Update man pages 2015-05-15 23:01:19 +09:00
Tatsuhiro Tsujikawa
805f36d134 Bump up version number to 0.7.15, LT revision to 13:4:8 2015-05-15 22:59:00 +09:00
Tatsuhiro Tsujikawa
e2c0a3e43b Retry finding jemalloc lib by je_malloc_stats_print
Fixes GH-233
2015-05-15 22:40:49 +09:00
Tatsuhiro Tsujikawa
3572e7c634 inflatehd: Fix crash if 'wire' value is not string
Fixes GH-235
2015-05-15 22:29:57 +09:00
Tatsuhiro Tsujikawa
0479f833fc Revert "nghttpx: Remove last write/read fields for TLS"
This reverts commit 585af93828.
2015-05-15 22:20:15 +09:00
Tatsuhiro Tsujikawa
252aeb43e1 Add test for GH-232 2015-05-15 01:00:09 +09:00
Tatsuhiro Tsujikawa
24fe24b37d Merge branch 'etcimon-patch-1' 2015-05-15 00:57:07 +09:00
Etienne Cimon
32603d7eff Access violation in buffers
When adding a large amount of data that spans to multiple chunks, the pointer is incremented by the wrong value.
2015-05-14 10:45:17 -04:00
Tatsuhiro Tsujikawa
53bfc70c9e Include inttypes.h (or cintypes for C++) instead of stdint.h
From autoconf manual, section 5.6.1 Portability of Headers, says:

"""
The C99 standard says that inttypes.h includes stdint.h, so there's no
need to include stdint.h separately in a standard environment. Some
implementations have inttypes.h but not stdint.h (e.g., Solaris 7),
but we don't know of any implementation that has stdint.h but not
inttypes.h.
"""
2015-05-14 00:17:45 +09:00
Tatsuhiro Tsujikawa
c4068cd404 Fix travis build error
Remove AC_PROG_RANLIB because it is rendered obsolete by LT_INIT.
Remove AC_CHECK_HEADER_STDBOOl, because travis does not have one.
2015-05-13 23:48:31 +09:00
Tatsuhiro Tsujikawa
0aa17f64c1 doc: Remove 'NGHTTP2_EXTERN' from api doc 2015-05-13 23:39:48 +09:00
Tatsuhiro Tsujikawa
38cfc5c47c Check more headers and funcs 2015-05-13 23:29:20 +09:00
Tatsuhiro Tsujikawa
91c8f085ef Update man pages 2015-05-13 00:44:37 +09:00
Tatsuhiro Tsujikawa
5da49989f8 nghttpd: Add --echo-upload option to send back request body 2015-05-13 00:38:28 +09:00
Tatsuhiro Tsujikawa
260131966d help2rst.py: Add comment to tell that this is a generated file 2015-05-12 23:30:17 +09:00
Tatsuhiro Tsujikawa
6862f66c23 Merge branch 'syohex-misspelling' 2015-05-12 23:25:33 +09:00
Tatsuhiro Tsujikawa
2f2a7ace81 Fix corresponding upstream source reported in previous commits accordingly 2015-05-12 23:24:18 +09:00
Syohei YOSHIDA
132719f752 Correct misspellings in document 2015-05-12 10:49:42 +09:00
Tatsuhiro Tsujikawa
a8625e15f0 clang-format 2015-05-08 19:24:17 +09:00
Tatsuhiro Tsujikawa
7f60de0c51 Bump up version number to 0.7.15-DEV 2015-05-08 18:36:58 +09:00
Tatsuhiro Tsujikawa
3a46a2c0a4 Update man pages 2015-05-08 17:55:15 +09:00
Tatsuhiro Tsujikawa
fbff101165 Update bash_completion 2015-05-08 17:54:51 +09:00
Tatsuhiro Tsujikawa
526d2c727d Bump up version number to 0.7.14, LT revision to 13:3:8 2015-05-08 17:51:54 +09:00
Tatsuhiro Tsujikawa
6c232da679 Merge branch 'alagoutte-misc' 2015-05-07 23:40:22 +09:00
Alexis La Goutte
1241c951d6 rv is only used in an assert.
The assert only evaluates to code if NDEBUG is undefined. Protect rv and its use accordingly

Issue reported by Joerg Mayer
https://code.wireshark.org/review/8260
2015-05-07 15:20:24 +02:00
Tatsuhiro Tsujikawa
73e79130d1 Update doc 2015-05-07 21:14:21 +09:00
Tatsuhiro Tsujikawa
1a1902350b Update sphinx_rtd_theme 2015-05-07 21:09:57 +09:00
Tatsuhiro Tsujikawa
eec65826cf Update man pages 2015-05-07 20:55:10 +09:00
Tatsuhiro Tsujikawa
fc17c0a618 Fix global-buffer-overflow 2015-05-07 19:37:43 +09:00
Tatsuhiro Tsujikawa
2620992003 Document about time for connect and time for 1st byte in h2load man page 2015-05-07 18:58:32 +09:00
Tatsuhiro Tsujikawa
5a2069b55c Merge branch 'wzyboy-patch-upstart' 2015-05-06 15:56:57 +09:00
Tatsuhiro Tsujikawa
14adcb2d81 Substitute bindir in nghttpx-upstart.conf 2015-05-06 15:56:18 +09:00
Tatsuhiro Tsujikawa
13660edef2 Merge branch 'patch-upstart' of https://github.com/wzyboy/nghttp2 into wzyboy-patch-upstart 2015-05-06 15:54:42 +09:00
Tatsuhiro Tsujikawa
98034286ac Substitute bindir in nghttpx.service 2015-05-06 15:53:12 +09:00
Tatsuhiro Tsujikawa
2a37a28d72 Revert "Substitute bindir for nghttpx.service"
This reverts commit 7bb154f768.
2015-05-06 15:37:56 +09:00
Tatsuhiro Tsujikawa
7bb154f768 Substitute bindir for nghttpx.service
Use static pattern rules to use same recipe for nghttpx-init and
nghttpx.service.

Also rewrite how to produce nghttpx
2015-05-06 12:02:12 +09:00
Zhuoyun Wei
eb96aa261f Add Upstart configuration file 2015-05-06 10:38:24 +08:00
Tatsuhiro Tsujikawa
232d359cbb Add nghttpx.service to distribution 2015-05-06 11:02:51 +09:00
Tatsuhiro Tsujikawa
2d5d9d5d04 nghttpd: Add -m, --max-concurrent-streams option 2015-05-06 10:42:43 +09:00
Tatsuhiro Tsujikawa
7ecca39025 nghttpx: Fix heap-use-after-free bug in http/1 frontend
This is a regression introduced in
4be4d875f3
2015-05-05 23:45:39 +09:00
Tatsuhiro Tsujikawa
bc0190c19f Merge branch 'wzyboy-patch-systemd-service' 2015-05-05 22:37:40 +09:00
Tatsuhiro Tsujikawa
9a162b81f0 Merge branch 'patch-systemd-service' of https://github.com/wzyboy/nghttp2 into wzyboy-patch-systemd-service 2015-05-05 22:37:21 +09:00
Tatsuhiro Tsujikawa
a67a8fabff Merge branch 'wzyboy-patch-logrotate' 2015-05-05 22:36:06 +09:00
Zhuoyun Wei
989d381aab Add systemd service file 2015-05-04 23:56:09 +08:00
Zhuoyun Wei
1d65d82cb5 Improve logrotate configuration file
- Avoid changing file permissions
- Make sure all log files are reopened
- Remove non-sense prerotate script
- Send SIGUSR1 to all nghttpx processes
2015-05-04 23:17:27 +08:00
Tatsuhiro Tsujikawa
4be4d875f3 nghttpx: Log absolute URI for HTTP/2 or client proxy request 2015-05-04 23:24:33 +09:00
Tatsuhiro Tsujikawa
1ab707713f nghttpx: Accept reference instead of pointer by upstream_accesslog 2015-05-04 22:45:34 +09:00
Tatsuhiro Tsujikawa
cc46d363c5 h2load: Refactor statistics hanlding to scale more upcoming new metrics 2015-05-04 22:36:21 +09:00
Tatsuhiro Tsujikawa
fa082cbdd0 Merge branch 'alex-nalivko-include_pull_request' 2015-05-03 16:52:27 +09:00
Tatsuhiro Tsujikawa
016d40ea0f Merge branch 'include_pull_request' of https://github.com/alex-nalivko/nghttp2 into alex-nalivko-include_pull_request 2015-05-03 16:50:52 +09:00
Tatsuhiro Tsujikawa
fe6d065bb4 Merge branch 'ericcarlschwartz-finer_stats' 2015-05-03 16:47:52 +09:00
Tatsuhiro Tsujikawa
b4e8bea4b5 clang-format 2015-05-03 16:47:32 +09:00
Tatsuhiro Tsujikawa
555d5abac9 Merge branch 'finer_stats' of https://github.com/ericcarlschwartz/nghttp2 into ericcarlschwartz-finer_stats 2015-05-03 16:37:02 +09:00
Alex Nalivko
3137dc4a70 h2load_spdy_session errno include 2015-05-02 19:33:04 +00:00
es
4bba4bf66c update h2load to give connect time and ttfb stats
finer statistics for h2load: update per comments from tatsuhiro-t

finer stats for h2load: fixed formatting
2015-05-01 10:30:09 -07:00
Tatsuhiro Tsujikawa
7b3a33a313 README.rst: Fix formatting and remove trailing spaces 2015-05-01 13:29:43 +09:00
Tatsuhiro Tsujikawa
ee52290de7 Merge branch 'brk0v-typo' 2015-05-01 00:41:48 +09:00
Viacheslav Biriukov
8f0899a190 mages to images typo 2015-04-30 18:22:51 +03:00
Tatsuhiro Tsujikawa
f6cfd082c7 Merge branch 'alagoutte-misc' 2015-04-30 18:43:41 +09:00
Tatsuhiro Tsujikawa
9d81be4b35 Merge branch 'misc' of https://github.com/alagoutte/nghttp2 into alagoutte-misc 2015-04-30 18:42:17 +09:00
Alexis La Goutte
a62778d6b0 fix comma at end of enumerator list [-Wpedantic] 2015-04-30 07:49:55 +02:00
Tatsuhiro Tsujikawa
ea612a2dce Merge branch 'nghttpx-headers-size-option' 2015-04-29 22:55:30 +09:00
Tatsuhiro Tsujikawa
026521b097 integration: Add tests for --header-field-buffer and --max-header-fields 2015-04-29 22:54:25 +09:00
Tatsuhiro Tsujikawa
9dc5259593 nghttpx: Take into account request URI in header size in https frontend 2015-04-29 22:23:25 +09:00
Tatsuhiro Tsujikawa
ea8a566d98 nghttpx: Send 431 if header field size exceeded the configuration limit 2015-04-29 21:39:46 +09:00
Tatsuhiro Tsujikawa
8c6f9e899f nghttpx: Enforce header field buffer limit for SPDY frontend 2015-04-29 21:27:36 +09:00
Tatsuhiro Tsujikawa
552f675466 nghttpx: Add --header-field-buffer and --max-header-fields options 2015-04-29 21:10:59 +09:00
Tatsuhiro Tsujikawa
f9a50333d2 Fix doc for nghttp2_select_next_protocol
We have to return this error code to notify OpenSSL that we have not
filled any values in |*out| and |*outlen|.
2015-04-28 23:12:27 +09:00
Tatsuhiro Tsujikawa
f05a4830c5 nghttp: Fix assertion error if very large value is given to -t 2015-04-28 21:51:28 +09:00
Tatsuhiro Tsujikawa
9e1b068a4b Fix bug that promised stream was not reset on decompression error 2015-04-28 21:38:52 +09:00
Tatsuhiro Tsujikawa
54bff91762 Bump up version number to 0.7.14-DEV 2015-04-28 00:01:09 +09:00
Tatsuhiro Tsujikawa
63630690a8 Fix make -j3 distcheck error 2015-04-27 23:52:36 +09:00
Tatsuhiro Tsujikawa
dbc613e0d0 Update man pages 2015-04-27 23:09:00 +09:00
Tatsuhiro Tsujikawa
ee354ee6c8 Bump up version number to 0.7.13, LT revision to 13:2:8 2015-04-27 23:06:14 +09:00
Tatsuhiro Tsujikawa
df707df21b Update man pages 2015-04-27 22:36:34 +09:00
Tatsuhiro Tsujikawa
2436acbd23 Update doc about h2load flow control 2015-04-27 22:36:03 +09:00
Tatsuhiro Tsujikawa
b41835f19b h2load: Effectively disable flow control by setting large window size
Previously h2load used default flow control window as described in
HTTP/2 and SPDY specification.  The window size is 64KiB, which is a
bit small, and cannot utilize full server performance when response
size is not too small.  Basically, we do this kind of benchmarking
test to measure server's throughput, and optimal performance.  Smaller
window certainly degrades performance even in local testing because
server is so fast that it has to wait for WINDOW_UPDATE from h2load.
To make default behaviour suitable for peak performance test, we
decided to disable flow control in h2load by setting large enough
window size.

Most users used h2load without -w or -W options, so they were
implicitly throttled by flow control and the result was affected by
that negatively.  Now flow control is disabled by default, the result
may improve depending on the implementations.
2015-04-27 21:23:01 +09:00
Tatsuhiro Tsujikawa
42b2430fe1 Remove unnecessary assignment to item->cycle 2015-04-26 22:32:54 +09:00
Tatsuhiro Tsujikawa
c8f67788e0 Remove unused local variable 2015-04-26 19:47:14 +09:00
Tatsuhiro Tsujikawa
bbdff112a3 Make huffman decoding slightly faster using evil looking macro 2015-04-26 19:39:33 +09:00
Tatsuhiro Tsujikawa
02468b1ca1 Allocate field name and value in the same buffer if indname to dynamic table 2015-04-26 18:43:24 +09:00
Tatsuhiro Tsujikawa
c41f413978 Fix compile error with --enable-werror 2015-04-25 02:23:01 +09:00
Tatsuhiro Tsujikawa
e38dd37667 Update doc 2015-04-25 01:00:02 +09:00
Tatsuhiro Tsujikawa
f2cf2b625c Replace priority queue with linear queue where possible
After reviewing codebase, only queue for DATA frames requires
priorities.  Other frames can be replaced multiple linear queues.
Replacing priority queue with linear queue allows us to simplify
codebase a bit; for example, now nghttp2_session.next_seq is gone.
2015-04-25 00:56:46 +09:00
Tatsuhiro Tsujikawa
eb05777d88 clang-format 2015-04-24 00:17:13 +09:00
Tatsuhiro Tsujikawa
6b0b8ea7d5 Update doc 2015-04-24 00:05:10 +09:00
Tatsuhiro Tsujikawa
c925c32233 Fix bug that promised stream is not reset on temporal failure from on_header_callback 2015-04-23 23:57:39 +09:00
Tatsuhiro Tsujikawa
514558afc0 Allow NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE from nghttp2_on_begin_headers_callback
Since application most likely allocates the stream object in
nghttp2_on_begin_headers_callback, it is desirable to handle its
failure as stream error.  But previously it only signals success or
fatal error.  Submitting RST_STREAM does not prevent
nghttp2_on_header_callback from being invoked.  This commit improves
this situation by allowing NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE from
nghttp2_on_begin_headers_callback.  If that value is returned, library
submits RST_STREAM with error code INTERNAL_ERROR, and
nghttp2_on_header_callback and nghttp2_on_frame_recv_callback for that
frame are not invoked.  Note that for PUSH_PROMISE frame, the stream
to be reset is promised stream.
2015-04-23 23:43:30 +09:00
Tatsuhiro Tsujikawa
b9f602b9a2 Update doc 2015-04-23 21:14:09 +09:00
Tatsuhiro Tsujikawa
1a99bcc860 Merge branch 'travis-container' 2015-04-23 00:00:06 +09:00
Tatsuhiro Tsujikawa
6b8aa36c98 travis: Use gcc-4.9 instead of gcc-4.8 2015-04-22 23:59:23 +09:00
Tatsuhiro Tsujikawa
86ddda5c0e tiny-nghttpd: Fix compile error with travis gcc-4.8 2015-04-22 23:59:23 +09:00
Tatsuhiro Tsujikawa
58b7f4a096 Fix inline error with travis gcc-4.8 2015-04-22 23:59:23 +09:00
Tatsuhiro Tsujikawa
4069aab4f7 travis: Attempt to enable gcc 2015-04-22 23:59:23 +09:00
Tatsuhiro Tsujikawa
5595ba643e travis: Use container-based infrastructure 2015-04-22 23:59:23 +09:00
Tatsuhiro Tsujikawa
77c556901c nghttpx: Increase maximum header field set size 2015-04-22 22:27:48 +09:00
Tatsuhiro Tsujikawa
4928959213 asio: Document asynchronous parameter for listen_and_serve 2015-04-22 22:25:16 +09:00
Tatsuhiro Tsujikawa
a200bb1084 Merge branch 'sunxiaoguang-asio_graceful_shutdown' 2015-04-22 21:43:52 +09:00
Xiaoguang Sun
92a1ca5917 Graceful shutdown and joinable server 2015-04-22 17:51:28 +08:00
Tatsuhiro Tsujikawa
97648d257f mkhufftbl.py: Refactor 2015-04-21 23:48:45 +09:00
Tatsuhiro Tsujikawa
787d40129b Bump up version number to 0.7.13-DEV 2015-04-19 18:32:58 +09:00
Tatsuhiro Tsujikawa
91ad7e150e Never indexing still can use header field name in dynamic table 2015-04-19 18:21:26 +09:00
Tatsuhiro Tsujikawa
28bde2cef0 Update bash_completion 2015-04-19 18:11:11 +09:00
Tatsuhiro Tsujikawa
a9b54a1bfa Update man pages 2015-04-19 18:10:54 +09:00
Tatsuhiro Tsujikawa
80f0e99f00 Bump up version number to 0.7.12, LT revision to 13:1:8 2015-04-19 18:07:57 +09:00
Tatsuhiro Tsujikawa
102ea7c0bb nghttpd: Cache fd
Implement fd caching for static files.  The response body for such as
404 was dynamically generated previously, but now it is written in
temporally file and its fd is cached.  Currently, cache is reference
counted and expired when count becomes 0.  This makes caching is not
effective other than "busy" period, but we don't need this feature if
we are not busy.
2015-04-19 17:38:06 +09:00
Tatsuhiro Tsujikawa
85671a69bf Update doc 2015-04-18 16:49:34 +09:00
Tatsuhiro Tsujikawa
a3fa257473 Fix compile error with Android NDK r10d + --enable-werror 2015-04-17 23:46:19 +09:00
Tatsuhiro Tsujikawa
c4e994c97d nghttp: Add --no-push option to disable server push 2015-04-17 23:35:16 +09:00
Tatsuhiro Tsujikawa
0b41e20d54 nghttp: Show stream ID in statistics output 2015-04-17 23:35:16 +09:00
Tatsuhiro Tsujikawa
cfabce6e70 Update man pages 2015-04-17 23:14:23 +09:00
Tatsuhiro Tsujikawa
b948c5457d Specify program directive 2015-04-17 23:13:42 +09:00
Tatsuhiro Tsujikawa
3bdf78e8af Document nghttp's dependency based priority 2015-04-17 23:06:07 +09:00
Tatsuhiro Tsujikawa
436595df98 nghttp: Remove --dep-idle option
In this commit, we made --dep-idle behaviour by default.  This is
because the previous default behaviour is not reflect current usage of
dependency priority and never will be because of fragility of tree due
to stream closure.
2015-04-17 22:24:06 +09:00
Tatsuhiro Tsujikawa
d3561a63b1 nghttp: Depend on "leader" anchor if js is linked inside head element 2015-04-17 21:25:31 +09:00
Tatsuhiro Tsujikawa
1a12a9b397 Penalize cycle according to effective weight 2015-04-17 21:17:22 +09:00
Tatsuhiro Tsujikawa
57644e0256 Effectively revert 03c4092862
This is not mandated by spec.  Also it may work badly with Firefox
style dependency tree usage.
2015-04-17 21:04:17 +09:00
Tatsuhiro Tsujikawa
7323d4c639 Fix bug that nghttp2_session_set_next_stream_id accepts invalid stream_id 2015-04-17 00:12:47 +09:00
Tatsuhiro Tsujikawa
e23225689f nghttp: Use same priority anchor nodes as Firefox does 2015-04-16 23:56:37 +09:00
Tatsuhiro Tsujikawa
e6ad2eb14f We can assert this 2015-04-16 22:58:25 +09:00
Tatsuhiro Tsujikawa
d4a22edeb3 Don't do fancy stuff yet 2015-04-16 22:58:25 +09:00
Tatsuhiro Tsujikawa
8f4e2d941f Revert accidental change in nghttp.cc 2015-04-16 22:58:25 +09:00
Tatsuhiro Tsujikawa
1a8da6caec Update doc 2015-04-16 21:40:39 +09:00
Tatsuhiro Tsujikawa
dc335b9025 Improve weight handling a bit 2015-04-16 21:38:13 +09:00
Tatsuhiro Tsujikawa
93afbc7d2f Rewrite static header table handling
We rewrite static header table handling in nghttp2_hd.c.  We expand
nghttp2_token to include all static header table entries, and fully
use them in header compression and decompression.  The lookup function
is now located in nghttp2_hd.c.  We add new nghttp2_hd_inflate_hd2()
function to export token value for header name, then we pass it to
nghttp2_http_on_header function, so that we don't have to look up
token there.  We carefully set enum value of token to static table
index, so looking up static table is now O(1), assuming we have token.
2015-04-15 23:58:56 +09:00
Tatsuhiro Tsujikawa
82e2c5bd22 Never index authorization and small cookie header field
nghttp2 library now use Literal Header Field never Indexed for
"authorization" header field and small "cookie" header field,
regardless of nghttp2_nv.flags.
2015-04-15 23:58:56 +09:00
Tatsuhiro Tsujikawa
53bcafb39f Merge branch 'cubicdaiya-alloc-error-handling' 2015-04-15 22:45:10 +09:00
Tatsuhiko Kubo
59f8397659 Use nullptr instead of NULL in C++. 2015-04-15 21:18:39 +09:00
Tatsuhiko Kubo
061732adf0 improved malloc error handlings. 2015-04-15 09:20:45 +09:00
Tatsuhiro Tsujikawa
5c2ca28706 asio: client: Call error_cb on error occurred in do_read and do_write
Fixes GH-207
2015-04-13 21:33:43 +09:00
Tatsuhiro Tsujikawa
a8ea86cfe5 src: constexpr 2015-04-12 17:51:23 +09:00
Tatsuhiro Tsujikawa
7451a73def nghttpx: Don't push resource if link header has non empty loadpolicy 2015-04-12 17:42:25 +09:00
Tatsuhiro Tsujikawa
889e705f35 nghttpx: Add logging for somewhat important events (logs, tickets, and ocsp) 2015-04-11 00:08:28 +09:00
Tatsuhiro Tsujikawa
14d4979c54 Don't install libnghttp2_asio headers if they are disabled 2015-04-10 23:11:40 +09:00
Tatsuhiro Tsujikawa
095bc178f3 nghttpx: Robust HTTP/1 backend CL and TE handling
We should ignore Content-Length and Transfer-Encoding for upgraded
response, and reset content-length if this is a non-final response.
2015-04-10 22:30:20 +09:00
Tatsuhiro Tsujikawa
308738025c nghttpx: Don't set response content-length if HTTP/2 response upgraded 2015-04-10 22:24:17 +09:00
Tatsuhiro Tsujikawa
97366bf55c nghttpx: Set content-length after complete request/response headers 2015-04-10 22:10:51 +09:00
Tatsuhiro Tsujikawa
87cadca3d8 integration: Add HTTP Upgrade test 2015-04-10 21:28:12 +09:00
Tatsuhiro Tsujikawa
9803f92e9c nghttpx: Set Downstream to stream user data on HTTP Upgrade to h2 2015-04-10 02:40:09 +09:00
Tatsuhiro Tsujikawa
cbfa021095 Bump up version number to 0.7.12-DEV 2015-04-10 00:38:17 +09:00
144 changed files with 4243 additions and 1932 deletions

View File

@@ -1,31 +1,31 @@
language: cpp
compiler:
- clang
#Disable gcc build for the moment...
# - gcc
- gcc
sudo: false
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.9
- libstdc++-4.9-dev
- autoconf
- automake
- autotools-dev
- libtool
- pkg-config
- zlib1g-dev
- libcunit1-dev
- libssl-dev
- libxml2-dev
- libev-dev
- libevent-dev
- libjansson-dev
- libjemalloc-dev
before_install:
- $CC --version
- sudo add-apt-repository --yes ppa:ubuntu-toolchain-r/test
- sudo apt-get update -qq
#Install and use gcc-4.8 (don't build with gcc-4.6)
#libstdc++-4.8 is needed by Clang to build too
- sudo apt-get -qq install g++-4.8 libstdc++-4.8-dev
- >
sudo apt-get install --no-install-recommends -qq
autoconf
automake
autotools-dev
libtool
pkg-config
zlib1g-dev
libcunit1-dev
libssl-dev
libxml2-dev
libev-dev
libevent-dev
libjansson-dev
libjemalloc-dev
- if [ "$CXX" = "g++" ]; then export CXX="g++-4.8" CC="gcc-4.8"; fi
- if [ "$CXX" = "g++" ]; then export CXX="g++-4.9" CC="gcc-4.9"; fi
- $CC --version
before_script:
- autoreconf -i

View File

@@ -13,7 +13,7 @@ An HPACK encoder and decoder are available as a public API.
An experimental high level C++ library is also available.
We have Python bindings of this libary, but we do not have full
We have Python bindings of this library, but we do not have full
code coverage yet.
Development Status
@@ -187,7 +187,7 @@ https://nghttp2.org/documentation/
Unit tests
----------
Unit tests are done by simply running `make check`.
Unit tests are done by simply running ``make check``.
Integration tests
-----------------
@@ -490,7 +490,7 @@ disabled in the frontend and incoming HTTP/1.1 connections can be
upgraded to HTTP/2 through HTTP Upgrade.
The ``--http2-bridge``, ``--client`` and ``--client-proxy`` modes use
SSL/TLS in the backend connection by deafult. To disable SSL/TLS, use
SSL/TLS in the backend connection by default. To disable SSL/TLS, use
the ``--backend-no-tls`` option.
``nghttpx`` supports a configuration file. See the ``--conf`` option and
@@ -785,7 +785,7 @@ max_deflate_size
The maximum header table size the encoder uses. This can be smaller
than ``max_size``. In this case, the encoder only uses up to first
``max_deflate_size`` buffer. Since the header table size is still
``max_size``, the encoder has to keep track of entries ouside the
``max_size``, the encoder has to keep track of entries outside the
``max_deflate_size`` but inside the ``max_size`` and make sure
that they are no longer referenced.
@@ -1273,7 +1273,7 @@ original creator(s) or those who have been assigned copyright by the
original author(s).
By submitting a patch to the nghttp2 project, you (or your employer, as
the case may be) agree to assign the copyright of your submission to us.
the case may be) agree to assign the copyright of your submission to us.
.. the above really needs to be reworded to pass legal muster.
We will credit you for your
changes as far as possible, to give credit but also to keep a trace

View File

@@ -25,13 +25,30 @@ dnl Do not change user variables!
dnl http://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html
AC_PREREQ(2.61)
AC_INIT([nghttp2], [0.7.11], [t-tujikawa@users.sourceforge.net])
AC_INIT([nghttp2], [0.7.15], [t-tujikawa@users.sourceforge.net])
AC_USE_SYSTEM_EXTENSIONS
LT_PREREQ([2.2.6])
LT_INIT()
AC_CANONICAL_BUILD
AC_CANONICAL_HOST
AC_CANONICAL_TARGET
AM_INIT_AUTOMAKE([subdir-objects])
# comment out for now since this requires automake 1.13 or higher and
# travis has older one.
# AM_EXTRA_RECURSIVE_TARGETS([it])
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_HEADERS([config.h])
dnl See versioning rule:
dnl http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
AC_SUBST(LT_CURRENT, 13)
AC_SUBST(LT_REVISION, 0)
AC_SUBST(LT_REVISION, 4)
AC_SUBST(LT_AGE, 8)
major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"`
@@ -42,21 +59,6 @@ PACKAGE_VERSION_NUM=`printf "0x%02x%02x%02x" "$major" "$minor" "$patch"`
AC_SUBST(PACKAGE_VERSION_NUM)
AC_CANONICAL_BUILD
AC_CANONICAL_HOST
AC_CANONICAL_TARGET
AC_CONFIG_MACRO_DIR([m4])
AM_INIT_AUTOMAKE([subdir-objects])
# comment out for now since this requires automake 1.13 or higher and
# travis has older one.
# AM_EXTRA_RECURSIVE_TARGETS([it])
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
AC_CONFIG_HEADERS([config.h])
dnl Checks for command-line options
AC_ARG_ENABLE([werror],
[AS_HELP_STRING([--enable-werror],
@@ -129,10 +131,12 @@ AC_ARG_VAR([CYTHON], [the Cython executable])
dnl Checks for programs
AC_PROG_CC
AC_PROG_CXX
AC_PROG_CPP
AC_PROG_INSTALL
AC_PROG_LN_S
AC_PROG_MAKE_SET
AM_PROG_CC_C_O
AC_PROG_MKDIR_P
PKG_PROG_PKG_CONFIG([0.20])
if [test "x$request_python_bindings" != "xno"]; then
@@ -358,9 +362,23 @@ if test "x${request_jemalloc}" != "xno"; then
AC_SEARCH_LIBS([malloc_stats_print], [jemalloc], [have_jemalloc=yes], [],
[$PTHREAD_LDFLAGS])
LIBS=$LIBS_OLD
if test "x${have_jemalloc}" = "xyes"; then
jemalloc_libs=${ac_cv_search_malloc_stats_print}
else
# On Darwin, malloc_stats_print is je_malloc_stats_print
AC_SEARCH_LIBS([je_malloc_stats_print], [jemalloc], [have_jemalloc=yes], [],
[$PTHREAD_LDFLAGS])
LIBS=$LIBS_OLD
if test "x${have_jemalloc}" = "xyes"; then
jemalloc_libs=${ac_cv_search_je_malloc_stats_print}
fi
fi
if test "x${have_jemalloc}" = "xyes" &&
test "x${ac_cv_search_malloc_stats_print}" != "xnone required"; then
JEMALLOC_LIBS=${ac_cv_search_malloc_stats_print}
test "x${jemalloc_libs}" != "xnone required"; then
JEMALLOC_LIBS=${jemalloc_libs}
AC_SUBST([JEMALLOC_LIBS])
fi
fi
@@ -498,12 +516,19 @@ AM_CONDITIONAL([ENABLE_FAILMALLOC], [ test "x${enable_failmalloc}" = "xyes" ])
AC_HEADER_ASSERT
AC_CHECK_HEADERS([ \
arpa/inet.h \
fcntl.h \
inttypes.h \
limits.h \
netdb.h \
netinet/in.h \
pwd.h \
stddef.h \
stdint.h \
stdlib.h \
string.h \
sys/socket.h \
sys/time.h \
syslog.h \
time.h \
unistd.h \
])
@@ -515,8 +540,16 @@ AC_TYPE_UINT8_T
AC_TYPE_UINT16_T
AC_TYPE_UINT32_T
AC_TYPE_UINT64_T
AC_TYPE_INT8_T
AC_TYPE_INT16_T
AC_TYPE_INT32_T
AC_TYPE_INT64_T
AC_TYPE_OFF_T
AC_TYPE_PID_T
AC_TYPE_UID_T
AC_CHECK_TYPES([ptrdiff_t])
AC_C_BIGENDIAN
AC_C_INLINE
AC_SYS_LARGEFILE
AC_CHECK_MEMBER([struct tm.tm_gmtoff], [have_struct_tm_tm_gmtoff=yes],
@@ -535,12 +568,34 @@ AC_CHECK_SIZEOF([int *])
if test "x$cross_compiling" != "xyes"; then
AC_FUNC_MALLOC
fi
AC_FUNC_CHOWN
AC_FUNC_ERROR_AT_LINE
AC_FUNC_FORK
# Don't check realloc, since LeakSanitizer detects memory leak during check
# AC_FUNC_REALLOC
AC_FUNC_STRERROR_R
AC_FUNC_STRNLEN
AC_CHECK_FUNCS([ \
_Exit \
accept4 \
dup2 \
getcwd \
getpwnam \
localtime_r \
memchr \
memmove \
memset \
socket \
sqrt \
strchr \
strdup \
strerror \
strndup \
strstr \
strtol \
strtoul \
timegm \
])

2
contrib/.gitignore vendored
View File

@@ -1 +1,3 @@
nghttpx-init
nghttpx.service
nghttpx-upstart.conf

View File

@@ -21,19 +21,24 @@
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
EXTRA_DIST = nghttpx-init.in nghttpx-logrotate
configfiles = nghttpx-init nghttpx.service nghttpx-upstart.conf
EXTRA_DIST = $(configfiles:%=%.in) nghttpx-logrotate
edit = sed -e 's|@bindir[@]|$(bindir)|g'
nghttpx-init: Makefile
nghttpx-init: %: $(srcdir)/%.in
rm -f $@ $@.tmp
$(edit) $(srcdir)/$@.in > $@.tmp
$(edit) $< > $@.tmp
chmod +x $@.tmp
mv $@.tmp $@
nghttpx-init: $(srcdir)/nghttpx-init.in
nghttpx.service nghttpx-upstart.conf: %: $(srcdir)/%.in
$(edit) $< > $@
all-local: nghttpx-init
$(configfiles): Makefile
all-local: $(configfiles)
clean-local:
-rm -f nghttpx-init nghttpx-init.tmp
-rm -f nghttpx-init.tmp $(configfiles)

View File

@@ -1,18 +1,11 @@
/var/log/nghttpx/*.log {
weekly
missingok
rotate 52
compress
delaycompress
notifempty
create 0640 www-data adm
sharedscripts
prerotate
if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
run-parts /etc/logrotate.d/httpd-prerotate; \
fi \
endscript
postrotate
[ -s /run/nghttpx.pid ] && kill -USR1 `cat /run/nghttpx.pid`
endscript
weekly
rotate 52
missingok
compress
delaycompress
notifempty
postrotate
killall -USR1 nghttpx 2> /dev/null || true
endscript
}

View File

@@ -0,0 +1,8 @@
# vim: ft=upstart:
description "HTTP/2 reverse proxy"
start on runlevel [2]
stop on runlevel [016]
exec @bindir@/nghttpx

View File

@@ -0,0 +1,10 @@
[Unit]
Description=HTTP/2 experimental proxy
After=network.target
[Service]
Type=simple
ExecStart=@bindir@/nghttpx --errorlog-syslog
[Install]
WantedBy=multi-user.target

View File

@@ -201,11 +201,28 @@ help:
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
apiref.rst: $(top_builddir)/lib/includes/nghttp2/nghttp2ver.h \
apiref.rst macros.rst enums.rst types.rst: \
$(top_builddir)/lib/includes/nghttp2/nghttp2ver.h \
$(top_builddir)/lib/includes/nghttp2/nghttp2.h
$(PYTHON) $(top_srcdir)/doc/mkapiref.py \
$@ macros.rst enums.rst types.rst . $^
# Inspired by
# http://www.gnu.org/savannah-checkouts/gnu/automake/manual/html_node/Multiple-Outputs.html
apidoc.stamp: $(top_builddir)/lib/includes/nghttp2/nghttp2ver.h \
$(top_builddir)/lib/includes/nghttp2/nghttp2.h
@rm -f apidoc.tmp
@touch apidoc.tmp
$(PYTHON) $(top_srcdir)/doc/mkapiref.py \
$@ macros.rst enums.rst types.rst . $^
@mv -f apidoc.tmp $@
$(APIDOC): apidoc.stamp
## Recover from the removal of $@
@if test -f $@; then :; else \
rm -f apidoc.stamp; \
$(MAKE) $(AM_MAKEFLAGS) apidoc.stamp; \
fi
clean-local:
-rm $(APIDOCS)
-rm -rf $(BUILDDIR)/*

View File

@@ -5,7 +5,7 @@ From https://github.com/ryan-roemer/sphinx-bootstrap-theme.
"""
import os
VERSION = (0, 1, 7)
VERSION = (0, 1, 8)
__version__ = ".".join(str(v) for v in VERSION)
__version_full__ = __version__

View File

@@ -6,14 +6,16 @@
{% endfor %}
<li>{{ title }}</li>
<li class="wy-breadcrumbs-aside">
{% if display_github %}
<a href="https://{{ github_host|default("github.com") }}/{{ github_user }}/{{ github_repo }}/blob/{{ github_version }}{{ conf_py_path }}{{ pagename }}{{ source_suffix }}" class="fa fa-github"> Edit on GitHub</a>
{% elif display_bitbucket %}
<a href="https://bitbucket.org/{{ bitbucket_user }}/{{ bitbucket_repo }}/src/{{ bitbucket_version}}{{ conf_py_path }}{{ pagename }}{{ source_suffix }}" class="fa fa-bitbucket"> Edit on Bitbucket</a>
{% elif show_source and source_url_prefix %}
<a href="{{ source_url_prefix }}{{ pagename }}{{ source_suffix }}">View page source</a>
{% elif show_source and has_source and sourcename %}
<a href="{{ pathto('_sources/' + sourcename, true)|e }}" rel="nofollow"> View page source</a>
{% if pagename != "search" %}
{% if display_github %}
<a href="https://{{ github_host|default("github.com") }}/{{ github_user }}/{{ github_repo }}/blob/{{ github_version }}{{ conf_py_path }}{{ pagename }}{{ source_suffix }}" class="fa fa-github"> Edit on GitHub</a>
{% elif display_bitbucket %}
<a href="https://bitbucket.org/{{ bitbucket_user }}/{{ bitbucket_repo }}/src/{{ bitbucket_version}}{{ conf_py_path }}{{ pagename }}{{ source_suffix }}" class="fa fa-bitbucket"> Edit on Bitbucket</a>
{% elif show_source and source_url_prefix %}
<a href="{{ source_url_prefix }}{{ pagename }}{{ source_suffix }}">View page source</a>
{% elif show_source and has_source and sourcename %}
<a href="{{ pathto('_sources/' + sourcename, true)|e }}" rel="nofollow"> View page source</a>
{% endif %}
{% endif %}
</li>
</ul>

View File

@@ -2,10 +2,10 @@
{% if next or prev %}
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
{% if next %}
<a href="{{ next.link|e }}" class="btn btn-neutral float-right" title="{{ next.title|striptags|e }}">Next <span class="fa fa-arrow-circle-right"></span></a>
<a href="{{ next.link|e }}" class="btn btn-neutral float-right" title="{{ next.title|striptags|e }}" accesskey="n">Next <span class="fa fa-arrow-circle-right"></span></a>
{% endif %}
{% if prev %}
<a href="{{ prev.link|e }}" class="btn btn-neutral" title="{{ prev.title|striptags|e }}"><span class="fa fa-arrow-circle-left"></span> Previous</a>
<a href="{{ prev.link|e }}" class="btn btn-neutral" title="{{ prev.title|striptags|e }}" accesskey="p"><span class="fa fa-arrow-circle-left"></span> Previous</a>
{% endif %}
</div>
{% endif %}

View File

@@ -107,7 +107,7 @@
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
{% block menu %}
{% set toctree = toctree(maxdepth=2, collapse=False, includehidden=True) %}
{% set toctree = toctree(maxdepth=4, collapse=False, includehidden=True) %}
{% if toctree %}
{{ toctree }}
{% else %}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,50 +1,113 @@
$( document ).ready(function() {
function toggleCurrent (elem) {
var parent_li = elem.closest('li');
parent_li.siblings('li.current').removeClass('current');
parent_li.siblings().find('li.current').removeClass('current');
parent_li.find('> ul li.current').removeClass('current');
parent_li.toggleClass('current');
}
$(document).ready(function() {
// Shift nav in mobile when clicking the menu.
$(document).on('click', "[data-toggle='wy-nav-top']", function() {
$("[data-toggle='wy-nav-shift']").toggleClass("shift");
$("[data-toggle='rst-versions']").toggleClass("shift");
$("[data-toggle='wy-nav-shift']").toggleClass("shift");
$("[data-toggle='rst-versions']").toggleClass("shift");
});
// Close menu when you click a link.
// Nav menu link click operations
$(document).on('click', ".wy-menu-vertical .current ul li a", function() {
$("[data-toggle='wy-nav-shift']").removeClass("shift");
$("[data-toggle='rst-versions']").toggleClass("shift");
var target = $(this);
// Close menu when you click a link.
$("[data-toggle='wy-nav-shift']").removeClass("shift");
$("[data-toggle='rst-versions']").toggleClass("shift");
// Handle dynamic display of l3 and l4 nav lists
toggleCurrent(target);
if (typeof(window.SphinxRtdTheme) != 'undefined') {
window.SphinxRtdTheme.StickyNav.hashChange();
}
});
$(document).on('click', "[data-toggle='rst-current-version']", function() {
$("[data-toggle='rst-versions']").toggleClass("shift-up");
});
$("[data-toggle='rst-versions']").toggleClass("shift-up");
});
// Make tables responsive
$("table.docutils:not(.field-list)").wrap("<div class='wy-table-responsive'></div>");
// Add expand links to all parents of nested ul
$('.wy-menu-vertical ul').siblings('a').each(function () {
var link = $(this);
expand = $('<span class="toctree-expand"></span>');
expand.on('click', function (ev) {
toggleCurrent(link);
ev.stopPropagation();
return false;
});
link.prepend(expand);
});
});
// Sphinx theme state
window.SphinxRtdTheme = (function (jquery) {
var stickyNav = (function () {
var navBar,
win,
stickyNavCssClass = 'stickynav',
winScroll = false,
linkScroll = false,
winPosition = 0,
enable = function () {
navBar.addClass(stickyNavCssClass);
win.on('scroll', function() { // set flag on scroll event
winScroll = true;
init();
reset();
win.on('hashchange', reset);
// Set scrolling
win.on('scroll', function () {
if (!linkScroll) {
winScroll = true;
}
});
// use setInterval to only handle a subset of scroll events so we don't kill scroll performance
setInterval(function() {
setInterval(function () {
if (winScroll) {
winScroll = false;
navBar.scrollTop(win.scrollTop());
var newWinPosition = win.scrollTop(),
navPosition = navBar.scrollTop(),
newNavPosition = navPosition + (newWinPosition - winPosition);
navBar.scrollTop(newNavPosition);
winPosition = newWinPosition;
}
}, 100);
}, 25);
},
init = function () {
navBar = jquery('nav.wy-nav-side:first');
win = jquery(window);
win = jquery(window);
},
reset = function () {
// Get anchor from URL and open up nested nav
var anchor = encodeURI(window.location.hash);
if (anchor) {
try {
var link = $('.wy-menu-vertical')
.find('[href="' + anchor + '"]');
$('.wy-menu-vertical li.toctree-l1 li.current')
.removeClass('current');
link.closest('li.toctree-l2').addClass('current');
link.closest('li.toctree-l3').addClass('current');
link.closest('li.toctree-l4').addClass('current');
}
catch (err) {
console.log("Error expanding nav for anchor", err);
}
}
},
hashChange = function () {
linkScroll = true;
win.one('hashchange', function () {
linkScroll = false;
});
};
jquery(init);
return {
enable : enable
enable: enable,
hashChange: hashChange
};
}());
return {
StickyNav : stickyNav
StickyNav: stickyNav
};
}($));

View File

@@ -8,7 +8,7 @@ _nghttp()
_get_comp_words_by_ref cur prev
case $cur in
-*)
COMPREPLY=( $( compgen -W '--verbose --no-dep --get-assets --har --header-table-size --multiply --padding --hexdump --dep-idle --continuation --connection-window-bits --peer-max-concurrent-streams --timeout --data --no-content-length --version --color --cert --upgrade --remote-name --trailer --weight --help --key --null-out --window-bits --stat --header ' -- "$cur" ) )
COMPREPLY=( $( compgen -W '--no-push --verbose --no-dep --get-assets --har --header-table-size --multiply --padding --hexdump --continuation --connection-window-bits --peer-max-concurrent-streams --timeout --data --no-content-length --version --color --cert --upgrade --remote-name --trailer --weight --help --key --null-out --window-bits --stat --header ' -- "$cur" ) )
;;
*)
_filedir

View File

@@ -8,7 +8,7 @@ _nghttpd()
_get_comp_words_by_ref cur prev
case $cur in
-*)
COMPREPLY=( $( compgen -W '--error-gzip --push --header-table-size --trailer --htdocs --address --padding --verbose --version --help --hexdump --daemon --verify-client --workers --no-tls --color --early-response --dh-param-file ' -- "$cur" ) )
COMPREPLY=( $( compgen -W '--error-gzip --push --header-table-size --trailer --htdocs --address --padding --verbose --version --help --hexdump --dh-param-file --daemon --verify-client --echo-upload --workers --no-tls --color --early-response --max-concurrent-streams ' -- "$cur" ) )
;;
*)
_filedir

View File

@@ -8,7 +8,7 @@ _nghttpx()
_get_comp_words_by_ref cur prev
case $cur in
-*)
COMPREPLY=( $( compgen -W '--frontend-http2-connection-window-bits --worker-read-rate --frontend-no-tls --frontend-http2-dump-request-header --daemon --write-rate --altsvc --frontend-http2-dump-response-header --backend-http1-connections-per-frontend --tls-ticket-key-file --ciphers --verify-client-cacert --backend-keep-alive-timeout --strip-incoming-x-forwarded-for --errorlog-file --private-key-passwd-file --version --backlog --backend-http-proxy-uri --add-response-header --backend-write-timeout --backend-request-buffer --add-x-forwarded-for --write-burst --backend-http2-connection-window-bits --insecure --rlimit-nofile --backend-http2-window-bits --tls-proto-list --no-location-rewrite --padding --conf --accesslog-syslog --backend-http2-connections-per-worker --http2-max-concurrent-streams --client-proxy --worker-frontend-connections --ocsp-update-interval --cacert --frontend-read-timeout --worker-write-burst --npn-list --syslog-facility --backend-http1-connections-per-host --no-server-push --client --http2-bridge --fetch-ocsp-response-file --no-via --user --stream-write-timeout --no-ocsp --backend-response-buffer --http2-no-cookie-crumbling --backend-read-timeout --stream-read-timeout --workers --worker-read-burst --dh-param-file --errorlog-syslog --frontend --accesslog-file --http2-proxy --frontend-http2-read-timeout --accesslog-format --frontend-http2-window-bits --backend-no-tls --client-private-key-file --pid-file --client-cert-file --no-host-rewrite --log-level --worker-write-rate --help --backend-tls-sni-field --subcert --frontend-frame-debug --frontend-write-timeout --verify-client --read-rate --read-burst --backend-ipv4 --listener-disable-timeout --backend-ipv6 --backend ' -- "$cur" ) )
COMPREPLY=( $( compgen -W '--worker-read-rate --frontend-no-tls --frontend-http2-dump-response-header --backend-http1-connections-per-frontend --tls-ticket-key-file --verify-client-cacert --backend-request-buffer --backend-http2-connection-window-bits --conf --worker-write-burst --npn-list --fetch-ocsp-response-file --stream-read-timeout --accesslog-syslog --frontend-http2-read-timeout --listener-disable-timeout --frontend-http2-connection-window-bits --ciphers --strip-incoming-x-forwarded-for --daemon --backend-keep-alive-timeout --backend-http-proxy-uri --backend-http1-connections-per-host --rlimit-nofile --no-via --ocsp-update-interval --backend-write-timeout --client --http2-no-cookie-crumbling --worker-read-burst --client-proxy --http2-bridge --accesslog-format --errorlog-syslog --errorlog-file --http2-max-concurrent-streams --frontend-write-timeout --read-burst --backend-ipv4 --backend-ipv6 --backend --insecure --log-level --tls-proto-list --backend-http2-connections-per-worker --dh-param-file --worker-frontend-connections --header-field-buffer --no-server-push --no-location-rewrite --no-ocsp --backend-response-buffer --workers --frontend-http2-window-bits --no-host-rewrite --worker-write-rate --backend-tls-sni-field --subcert --help --frontend-frame-debug --pid-file --frontend-http2-dump-request-header --private-key-passwd-file --write-rate --altsvc --user --add-x-forwarded-for --syslog-facility --frontend-read-timeout --backlog --write-burst --backend-http2-window-bits --padding --stream-write-timeout --cacert --version --verify-client --backend-read-timeout --frontend --accesslog-file --http2-proxy --max-header-fields --backend-no-tls --client-private-key-file --client-cert-file --add-response-header --read-rate ' -- "$cur" ) )
;;
*)
_filedir

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "H2LOAD" "1" "April 10, 2015" "0.7.11" "nghttp2"
.TH "H2LOAD" "1" "May 15, 2015" "0.7.15" "nghttp2"
.SH NAME
h2load \- HTTP/2 benchmarking tool
.
@@ -71,7 +71,7 @@ Default: \fB1\fP
.INDENT 0.0
.TP
.B \-i, \-\-input\-file=<FILE>
Path of a file with multiple URIs are seperated by EOLs.
Path of a file with multiple URIs are separated by EOLs.
This option will disable URIs getting from command\-line.
If \(aq\-\(aq is given as <FILE>, URIs will be read from stdin.
URIs are used in this order for each client. All URIs
@@ -93,6 +93,8 @@ Default: \fBauto\fP
.B \-w, \-\-window\-bits=<N>
Sets the stream level initial window size to (2**<N>)\-1.
For SPDY, 2**<N> is used instead.
.sp
Default: \fB30\fP
.UNINDENT
.INDENT 0.0
.TP
@@ -101,6 +103,8 @@ Sets the connection level initial window size to
(2**<N>)\-1. For SPDY, if <N> is strictly less than 16,
this option is ignored. Otherwise 2**<N> is used for
SPDY.
.sp
Default: \fB30\fP
.UNINDENT
.INDENT 0.0
.TP
@@ -201,13 +205,63 @@ The maximum time taken for request and response.
The mean time taken for request and response.
.TP
.B sd
The standard deviation of the time for request and response.
The standard deviation of the time taken for request and response.
.TP
.B +/\- sd
The fraction of the number of requests within standard deviation
range (mean +/\- sd) against total number of successful requests.
.UNINDENT
.TP
.B time for connect
.INDENT 7.0
.TP
.B min
The minimum time taken to connect to a server.
.TP
.B max
The maximum time taken to connect to a server.
.TP
.B mean
The mean time taken to connect to a server.
.TP
.B sd
The standard deviation of the time taken to connect to a server.
.TP
.B +/\- sd
The fraction of the number of connections within standard
deviation range (mean +/\- sd) against total number of successful
connections.
.UNINDENT
.TP
.B time for 1st byte (of (decrypted in case of TLS) application data)
.INDENT 7.0
.TP
.B min
The minimum time taken to get 1st byte from a server.
.TP
.B max
The maximum time taken to get 1st byte from a server.
.TP
.B mean
The mean time taken to get 1st byte from a server.
.TP
.B sd
The standard deviation of the time taken to get 1st byte from a
server.
.TP
.B +/\- sd
The fraction of the number of connections within standard
deviation range (mean +/\- sd) against total number of successful
connections.
.UNINDENT
.UNINDENT
.SH FLOW CONTROL
.sp
h2load sets large flow control window by default, and effectively
disables flow control to avoid under utilization of server
performance. To set smaller flow control window, use \fI\%\-w\fP and
\fI\%\-W\fP options. For example, use \fB\-w16 \-W16\fP to set default
window size described in HTTP/2 and SPDY protocol specification.
.SH SEE ALSO
.sp
\fInghttp(1)\fP, \fInghttpd(1)\fP, \fInghttpx(1)\fP

View File

@@ -1,4 +1,8 @@
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
.. program:: h2load
h2load(1)
=========
@@ -44,7 +48,7 @@ OPTIONS
.. option:: -i, --input-file=<FILE>
Path of a file with multiple URIs are seperated by EOLs.
Path of a file with multiple URIs are separated by EOLs.
This option will disable URIs getting from command-line.
If '-' is given as <FILE>, URIs will be read from stdin.
URIs are used in this order for each client. All URIs
@@ -65,6 +69,8 @@ OPTIONS
Sets the stream level initial window size to (2\*\*<N>)-1.
For SPDY, 2**<N> is used instead.
Default: ``30``
.. option:: -W, --connection-window-bits=<N>
Sets the connection level initial window size to
@@ -72,6 +78,8 @@ OPTIONS
this option is ignored. Otherwise 2\*\*<N> is used for
SPDY.
Default: ``30``
.. option:: -H, --header=<HEADER>
Add/Override a header to the requests.
@@ -147,11 +155,49 @@ time for request
mean
The mean time taken for request and response.
sd
The standard deviation of the time for request and response.
The standard deviation of the time taken for request and response.
+/- sd
The fraction of the number of requests within standard deviation
range (mean +/- sd) against total number of successful requests.
time for connect
min
The minimum time taken to connect to a server.
max
The maximum time taken to connect to a server.
mean
The mean time taken to connect to a server.
sd
The standard deviation of the time taken to connect to a server.
+/- sd
The fraction of the number of connections within standard
deviation range (mean +/- sd) against total number of successful
connections.
time for 1st byte (of (decrypted in case of TLS) application data)
min
The minimum time taken to get 1st byte from a server.
max
The maximum time taken to get 1st byte from a server.
mean
The mean time taken to get 1st byte from a server.
sd
The standard deviation of the time taken to get 1st byte from a
server.
+/- sd
The fraction of the number of connections within standard
deviation range (mean +/- sd) against total number of successful
connections.
FLOW CONTROL
------------
h2load sets large flow control window by default, and effectively
disables flow control to avoid under utilization of server
performance. To set smaller flow control window, use :option:`-w` and
:option:`-W` options. For example, use ``-w16 -W16`` to set default
window size described in HTTP/2 and SPDY protocol specification.
SEE ALSO
--------

View File

@@ -44,11 +44,49 @@ time for request
mean
The mean time taken for request and response.
sd
The standard deviation of the time for request and response.
The standard deviation of the time taken for request and response.
+/- sd
The fraction of the number of requests within standard deviation
range (mean +/- sd) against total number of successful requests.
time for connect
min
The minimum time taken to connect to a server.
max
The maximum time taken to connect to a server.
mean
The mean time taken to connect to a server.
sd
The standard deviation of the time taken to connect to a server.
+/- sd
The fraction of the number of connections within standard
deviation range (mean +/- sd) against total number of successful
connections.
time for 1st byte (of (decrypted in case of TLS) application data)
min
The minimum time taken to get 1st byte from a server.
max
The maximum time taken to get 1st byte from a server.
mean
The mean time taken to get 1st byte from a server.
sd
The standard deviation of the time taken to get 1st byte from a
server.
+/- sd
The fraction of the number of connections within standard
deviation range (mean +/- sd) against total number of successful
connections.
FLOW CONTROL
------------
h2load sets large flow control window by default, and effectively
disables flow control to avoid under utilization of server
performance. To set smaller flow control window, use :option:`-w` and
:option:`-W` options. For example, use ``-w16 -W16`` to set default
window size described in HTTP/2 and SPDY protocol specification.
SEE ALSO
--------

View File

@@ -222,6 +222,7 @@ def process_function(domain, infile):
func_proto = ''.join(func_proto)
func_proto = re.sub(r';\n$', '', func_proto)
func_proto = re.sub(r'\s+', ' ', func_proto)
func_proto = re.sub(r'NGHTTP2_EXTERN ', '', func_proto)
return FunctionDoc(func_proto, content, domain)
def read_content(infile):

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "NGHTTP" "1" "April 10, 2015" "0.7.11" "nghttp2"
.TH "NGHTTP" "1" "May 15, 2015" "0.7.15" "nghttp2"
.SH NAME
nghttp \- HTTP/2 experimental client
.
@@ -64,8 +64,9 @@ yet.
.UNINDENT
.INDENT 0.0
.TP
.B \-t, \-\-timeout=<SEC>
Timeout each request after <SEC> seconds.
.B \-t, \-\-timeout=<DURATION>
Timeout each request after <DURATION>. Set 0 to disable
timeout.
.UNINDENT
.INDENT 0.0
.TP
@@ -104,8 +105,8 @@ Add a header to the requests. Example: \fI\%\-H\fP\(aq:method: PUT\(aq
.B \-\-trailer=<HEADER>
Add a trailer header to the requests. <HEADER> must not
include pseudo header field (header field name starting
with \(aq:\(aq). To send trailer, one must use \fI\-d\fP option to
send request body. Example: \fI\-\-trailer\fP \(aqfoo: bar\(aq.
with \(aq:\(aq). To send trailer, one must use \fI\%\-d\fP option to
send request body. Example: \fI\%\-\-trailer\fP \(aqfoo: bar\(aq.
.UNINDENT
.INDENT 0.0
.TP
@@ -135,7 +136,7 @@ requested twice. This option disables it too.
.TP
.B \-u, \-\-upgrade
Perform HTTP Upgrade for HTTP/2. This option is ignored
if the request URI has https scheme. If \fI\-d\fP is used, the
if the request URI has https scheme. If \fI\%\-d\fP is used, the
HTTP upgrade request is performed with OPTIONS method.
.UNINDENT
.INDENT 0.0
@@ -192,11 +193,6 @@ Don\(aqt send dependency based priority hint to server.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-dep\-idle
Use idle streams as anchor nodes to express priority.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-hexdump
Display the incoming traffic in hexadecimal (Canonical
hex+ASCII display). If SSL/TLS is used, decrypted data
@@ -204,6 +200,11 @@ are used.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-no\-push
Disable server push.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-version
Display version information and exit.
.UNINDENT
@@ -215,6 +216,68 @@ Display this help and exit.
.sp
The <SIZE> argument is an integer and an optional unit (e.g., 10K is
10 * 1024). Units are K, M and G (powers of 1024).
.sp
The <DURATION> argument is an integer and an optional unit (e.g., 1s
is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms
(hours, minutes, seconds and milliseconds, respectively). If a unit
is omitted, a second is used as unit.
.SH DEPENDENCY BASED PRIORITY
.sp
nghttp sends priority hints to server by default unless
\fI\%\-\-no\-dep\fP is used. nghttp mimics the way Firefox employs to
manages dependency using idle streams. We follows the behaviour of
Firefox Nightly as of April, 2015, and nghttp\(aqs behaviour is very
static and could be different from Firefox in detail. But reproducing
the same behaviour of Firefox is not our goal. The goal is provide
the easy way to test out the dependency priority in server
implementation.
.sp
When connection is established, nghttp sends 5 PRIORITY frames to idle
streams 3, 5, 7, 9 and 11 to create "anchor" nodes in dependency
tree:
.INDENT 0.0
.INDENT 3.5
.sp
.nf
.ft C
+\-\-\-\-\-+
|id=0 |
+\-\-\-\-\-+
^ ^ ^
w=201 / | \e w=1
/ | \e
/ w=101| \e
+\-\-\-\-\-+ +\-\-\-\-\-+ +\-\-\-\-\-+
|id=3 | |id=5 | |id=7 |
+\-\-\-\-\-+ +\-\-\-\-\-+ +\-\-\-\-\-+
^ ^
w=1 | w=1 |
| |
+\-\-\-\-\-+ +\-\-\-\-\-+
|id=11| |id=9 |
+\-\-\-\-\-+ +\-\-\-\-\-+
.ft P
.fi
.UNINDENT
.UNINDENT
.sp
In the above figure, \fBid\fP means stream ID, and \fBw\fP means weight.
The stream 0 is non\-existence stream, and forms the root of the tree.
The stream 7 and 9 are not used for now.
.sp
The URIs given in the command\-line depend on stream 11 with the weight
given in \fI\%\-p\fP option, which defaults to 16.
.sp
If \fI\%\-a\fP option is used, nghttp parses the resource pointed by
URI given in command\-line as html, and extracts resource links from
it. When requesting those resources, nghttp uses dependency according
to its resource type.
.sp
For CSS, and Javascript files inside "head" element, they depend on
stream 3 with the weight 2. The Javascript files outside "head"
element depend on stream 5 with the weight 2. The mages depend on
stream 11 with the weight 12. The other resources (e.g., icon) depend
on stream 11 with the weight 2.
.SH SEE ALSO
.sp
\fInghttpd(1)\fP, \fInghttpx(1)\fP, \fIh2load(1)\fP

View File

@@ -1,4 +1,8 @@
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
.. program:: nghttp
nghttp(1)
=========
@@ -36,9 +40,10 @@ OPTIONS
'index.html' is used as a filename. Not implemented
yet.
.. option:: -t, --timeout=<SEC>
.. option:: -t, --timeout=<DURATION>
Timeout each request after <SEC> seconds.
Timeout each request after <DURATION>. Set 0 to disable
timeout.
.. option:: -w, --window-bits=<N>
@@ -143,16 +148,16 @@ OPTIONS
Don't send dependency based priority hint to server.
.. option:: --dep-idle
Use idle streams as anchor nodes to express priority.
.. option:: --hexdump
Display the incoming traffic in hexadecimal (Canonical
hex+ASCII display). If SSL/TLS is used, decrypted data
are used.
.. option:: --no-push
Disable server push.
.. option:: --version
Display version information and exit.
@@ -166,6 +171,62 @@ OPTIONS
The <SIZE> argument is an integer and an optional unit (e.g., 10K is
10 * 1024). Units are K, M and G (powers of 1024).
The <DURATION> argument is an integer and an optional unit (e.g., 1s
is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms
(hours, minutes, seconds and milliseconds, respectively). If a unit
is omitted, a second is used as unit.
DEPENDENCY BASED PRIORITY
-------------------------
nghttp sends priority hints to server by default unless
:option:`--no-dep` is used. nghttp mimics the way Firefox employs to
manages dependency using idle streams. We follows the behaviour of
Firefox Nightly as of April, 2015, and nghttp's behaviour is very
static and could be different from Firefox in detail. But reproducing
the same behaviour of Firefox is not our goal. The goal is provide
the easy way to test out the dependency priority in server
implementation.
When connection is established, nghttp sends 5 PRIORITY frames to idle
streams 3, 5, 7, 9 and 11 to create "anchor" nodes in dependency
tree::
+-----+
|id=0 |
+-----+
^ ^ ^
w=201 / | \ w=1
/ | \
/ w=101| \
+-----+ +-----+ +-----+
|id=3 | |id=5 | |id=7 |
+-----+ +-----+ +-----+
^ ^
w=1 | w=1 |
| |
+-----+ +-----+
|id=11| |id=9 |
+-----+ +-----+
In the above figure, ``id`` means stream ID, and ``w`` means weight.
The stream 0 is non-existence stream, and forms the root of the tree.
The stream 7 and 9 are not used for now.
The URIs given in the command-line depend on stream 11 with the weight
given in :option:`-p` option, which defaults to 16.
If :option:`-a` option is used, nghttp parses the resource pointed by
URI given in command-line as html, and extracts resource links from
it. When requesting those resources, nghttp uses dependency according
to its resource type.
For CSS, and Javascript files inside "head" element, they depend on
stream 3 with the weight 2. The Javascript files outside "head"
element depend on stream 5 with the weight 2. The mages depend on
stream 11 with the weight 12. The other resources (e.g., icon) depend
on stream 11 with the weight 2.
SEE ALSO
--------

View File

@@ -1,3 +1,54 @@
DEPENDENCY BASED PRIORITY
-------------------------
nghttp sends priority hints to server by default unless
:option:`--no-dep` is used. nghttp mimics the way Firefox employs to
manages dependency using idle streams. We follows the behaviour of
Firefox Nightly as of April, 2015, and nghttp's behaviour is very
static and could be different from Firefox in detail. But reproducing
the same behaviour of Firefox is not our goal. The goal is provide
the easy way to test out the dependency priority in server
implementation.
When connection is established, nghttp sends 5 PRIORITY frames to idle
streams 3, 5, 7, 9 and 11 to create "anchor" nodes in dependency
tree::
+-----+
|id=0 |
+-----+
^ ^ ^
w=201 / | \ w=1
/ | \
/ w=101| \
+-----+ +-----+ +-----+
|id=3 | |id=5 | |id=7 |
+-----+ +-----+ +-----+
^ ^
w=1 | w=1 |
| |
+-----+ +-----+
|id=11| |id=9 |
+-----+ +-----+
In the above figure, ``id`` means stream ID, and ``w`` means weight.
The stream 0 is non-existence stream, and forms the root of the tree.
The stream 7 and 9 are not used for now.
The URIs given in the command-line depend on stream 11 with the weight
given in :option:`-p` option, which defaults to 16.
If :option:`-a` option is used, nghttp parses the resource pointed by
URI given in command-line as html, and extracts resource links from
it. When requesting those resources, nghttp uses dependency according
to its resource type.
For CSS, and Javascript files inside "head" element, they depend on
stream 3 with the weight 2. The Javascript files outside "head"
element depend on stream 5 with the weight 2. The mages depend on
stream 11 with the weight 12. The other resources (e.g., icon) depend
on stream 11 with the weight 2.
SEE ALSO
--------

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "NGHTTPD" "1" "April 10, 2015" "0.7.11" "nghttp2"
.TH "NGHTTPD" "1" "May 15, 2015" "0.7.15" "nghttp2"
.SH NAME
nghttpd \- HTTP/2 experimental server
.
@@ -63,7 +63,7 @@ address determined by getaddrinfo is used.
.INDENT 0.0
.TP
.B \-D, \-\-daemon
Run in a background. If \fI\-D\fP is used, the current working
Run in a background. If \fI\%\-D\fP is used, the current working
directory is changed to \(aq\fI/\fP\(aq. Therefore if this option
is used, \fI\%\-d\fP option must be specified.
.UNINDENT
@@ -109,7 +109,7 @@ Push resources <PUSH_PATH>s when <PATH> is requested.
This option can be used repeatedly to specify multiple
push configurations. <PATH> and <PUSH_PATH>s are
relative to document root. See \fI\%\-\-htdocs\fP option.
Example: \fI\-p\fP/=/foo.png \fI\-p\fP/doc=/bar.css
Example: \fI\%\-p\fP/=/foo.png \fI\%\-p\fP/doc=/bar.css
.UNINDENT
.INDENT 0.0
.TP
@@ -119,6 +119,14 @@ Specify 0 to disable padding.
.UNINDENT
.INDENT 0.0
.TP
.B \-m, \-\-max\-concurrent\-streams=<N>
Set the maximum number of the concurrent streams in one
HTTP/2 session.
.sp
Default: \fB100\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-n, \-\-workers=<N>
Set the number of worker threads.
.sp
@@ -159,6 +167,11 @@ are used.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-echo\-upload
Send back uploaded content if method is POST or PUT.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-version
Display version information and exit.
.UNINDENT

View File

@@ -1,4 +1,8 @@
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
.. program:: nghttpd
nghttpd(1)
==========
@@ -83,6 +87,13 @@ OPTIONS
Add at most <N> bytes to a frame payload as padding.
Specify 0 to disable padding.
.. option:: -m, --max-concurrent-streams=<N>
Set the maximum number of the concurrent streams in one
HTTP/2 session.
Default: ``100``
.. option:: -n, --workers=<N>
Set the number of worker threads.
@@ -117,6 +128,10 @@ OPTIONS
hex+ASCII display). If SSL/TLS is used, decrypted data
are used.
.. option:: --echo-upload
Send back uploaded content if method is POST or PUT.
.. option:: --version
Display version information and exit.

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "NGHTTPX" "1" "April 10, 2015" "0.7.11" "nghttp2"
.TH "NGHTTPX" "1" "May 15, 2015" "0.7.15" "nghttp2"
.SH NAME
nghttpx \- HTTP/2 experimental proxy
.
@@ -704,6 +704,23 @@ won\(aqt replace anything already set. This option can be
used several times to specify multiple header fields.
Example: \fI\%\-\-add\-response\-header\fP="foo: bar"
.UNINDENT
.INDENT 0.0
.TP
.B \-\-header\-field\-buffer=<SIZE>
Set maximum buffer size for incoming HTTP header field
list. This is the sum of header name and value in
bytes.
.sp
Default: \fB64K\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-max\-header\-fields=<N>
Set maximum number of incoming HTTP header fields, which
appear in one request or response header field list.
.sp
Default: \fB100\fP
.UNINDENT
.SS Debug
.INDENT 0.0
.TP
@@ -795,7 +812,7 @@ argument in the configuration file. Specify \fByes\fP as an argument
ignored.
.sp
To specify private key and certificate file which are given as
positional arguments in commnad\-line, use \fBprivate\-key\-file\fP and
positional arguments in command\-line, use \fBprivate\-key\-file\fP and
\fBcertificate\-file\fP\&.
.sp
\fI\%\-\-conf\fP option cannot be used in the configuration file and

View File

@@ -1,4 +1,8 @@
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
.. program:: nghttpx
nghttpx(1)
==========
@@ -617,6 +621,21 @@ HTTP
used several times to specify multiple header fields.
Example: :option:`--add-response-header`\="foo: bar"
.. option:: --header-field-buffer=<SIZE>
Set maximum buffer size for incoming HTTP header field
list. This is the sum of header name and value in
bytes.
Default: ``64K``
.. option:: --max-header-fields=<N>
Set maximum number of incoming HTTP header fields, which
appear in one request or response header field list.
Default: ``100``
Debug
~~~~~
@@ -710,7 +729,7 @@ FILES
ignored.
To specify private key and certificate file which are given as
positional arguments in commnad-line, use ``private-key-file`` and
positional arguments in command-line, use ``private-key-file`` and
``certificate-file``.
:option:`--conf` option cannot be used in the configuration file and

View File

@@ -19,7 +19,7 @@ FILES
ignored.
To specify private key and certificate file which are given as
positional arguments in commnad-line, use ``private-key-file`` and
positional arguments in command-line, use ``private-key-file`` and
``certificate-file``.
:option:`--conf` option cannot be used in the configuration file and

View File

@@ -50,8 +50,9 @@ Flow Control
------------
HTTP/2 and SPDY/3 or later employ flow control and it may affect
benchmarking results. To adjust receiver flow control window size,
there is following options:
benchmarking results. By default, h2load uses large enough flow
control window, which effectively disables flow control. To adjust
receiver flow control window size, there are following options:
``-w``
Sets the stream level initial window size to
@@ -86,5 +87,5 @@ If multiple URIs are specified, they are used in round robin manner.
.. note::
Please note that h2load uses sheme, host and port in the first URI
Please note that h2load uses scheme, host and port in the first URI
and ignores those parts in the rest of the URIs.

View File

@@ -80,7 +80,7 @@ status code, in the above example, which is 200. The second argument,
which is omitted in the above example, is additional header fields to
send.
``nghttp2::asio_http2::server::response::end`` sends responde body.
``nghttp2::asio_http2::server::response::end`` sends response body.
In the above example, we send string "hello, world".
The life time of req and res object ends after the callback set by
@@ -277,7 +277,7 @@ response header fields and response body to the console screen:
``boost::asio::io_service`` object and remote server address. When
connection is made, the callback function passed to
``nghttp2::asio_http2::client::on_connect`` is invoked with connected
address as its paramter. After this callback call, use
address as its parameter. After this callback call, use
``nghttp2::asio_http2::session::submit`` to send request to the
server. You can submit multiple requests at once without waiting for
the completion of previous request.

View File

@@ -21,7 +21,7 @@ SSL/TLS, the frontend also supports SPDY protocol.
By default, this mode's frontend connection is encrypted using
SSL/TLS. So server's private key and certificate must be supplied to
the command line (or through configuration file). In this case, the
fontend protocol selection will is done via ALPN or NPN.
frontend protocol selection will 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
@@ -243,7 +243,7 @@ Read/write rate limit
---------------------
nghttpx supports transfer rate limiting on frontend connections. You
can do rate limit per frontend connection for reading and writeing
can do rate limit per frontend connection for reading and writing
individually.
To perform rate limit for reading, use ``--read-rate`` and

View File

@@ -267,7 +267,7 @@ HTTP/2 servers
(byte string could be ``None``), :py:data:`DATA_EOF` must be
returned as flag. If there is no data available right now, but
additional data are anticipated, return tuple (``None``,
:py:data:`DATA_DEFERRD`). When data arrived, call
:py:data:`DATA_DEFERRED`). When data arrived, call
:py:meth:`resume()` and restart response body transmission.
Only the body generator can pause response body generation;

View File

@@ -65,11 +65,11 @@ its stream specific data in ``http2_stream_data`` structure and the
defined as follows::
typedef struct {
/* The NULL-terminated URI string to retreive. */
/* The NULL-terminated URI string to retrieve. */
const char *uri;
/* Parsed result of the |uri| */
struct http_parser_url *u;
/* The authroity portion of the |uri|, not NULL-terminated */
/* The authority portion of the |uri|, not NULL-terminated */
char *authority;
/* The path portion of the |uri|, including query, not
NULL-terminated */
@@ -204,7 +204,7 @@ transmission of client connection header is done in
}
Here we specify SETTINGS_MAX_CONCURRENT_STREAMS to 100, which is
really not needed for this tiny example progoram, but we are
really not needed for this tiny example program, but we are
demonstrating the use of SETTINGS frame. To queue the SETTINGS frame
for the transmission, we use `nghttp2_submit_settings()`. Note that
`nghttp2_submit_settings()` function only queues the frame and not
@@ -388,7 +388,7 @@ After all name/value pairs are emitted for a frame,
}
In this tutorial, we are just interested in the HTTP response
HEADERS. We check te frame type and its category (it should be
HEADERS. We check the frame type and its category (it should be
:macro:`NGHTTP2_HCAT_RESPONSE` for HTTP response HEADERS). Also check
its stream ID.
@@ -407,7 +407,7 @@ of data is received from the remote peer::
}
In our case, a chunk of data is response body. After checking stream
ID, we just write the recieved data to the stdout. Note that the
ID, we just write the received data to the stdout. Note that the
output in the terminal may be corrupted if the response body contains
some binary data.

View File

@@ -51,7 +51,7 @@ bound of encoded result, use `nghttp2_hd_deflate_bound()` function::
size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater,
const nghttp2_nv *nva, size_t nvlen);
Pass this function with the same paramters *deflater*, *nva* and
Pass this function with the same parameters *deflater*, *nva* and
*nvlen* which will be passed to `nghttp2_hd_deflate_hd()`.
The subsequent call of `nghttp2_hd_deflate_hd()` will use current

View File

@@ -35,8 +35,12 @@
//
#include <sys/types.h>
#include <sys/stat.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif // HAVE_UNISTD_H
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif // HAVE_FCNTL_H
#include <iostream>
#include <string>

View File

@@ -28,16 +28,26 @@
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /* !HAVE_CONFIG_H */
#endif /* HAVE_CONFIG_H */
#include <stdint.h>
#include <inttypes.h>
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif /* HAVE_UNISTD_H */
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif /* HAVE_FCNTL_H */
#include <sys/types.h>
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif /* HAVE_SYS_SOCKET_H */
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif /* HAVE_NETDB_H */
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif /* HAVE_NETINET_IN_H */
#include <netinet/tcp.h>
#include <poll.h>
#include <signal.h>

View File

@@ -24,12 +24,18 @@
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /* !HAVE_CONFIG_H */
#endif /* HAVE_CONFIG_H */
#include <sys/types.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif /* HAVE_UNISTD_H */
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif /* HAVE_SYS_SOCKET_H */
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif /* HAVE_NETINET_IN_H */
#include <netinet/tcp.h>
#include <err.h>
#include <signal.h>
@@ -50,11 +56,11 @@
#define ARRLEN(x) (sizeof(x) / sizeof(x[0]))
typedef struct {
/* The NULL-terminated URI string to retreive. */
/* The NULL-terminated URI string to retrieve. */
const char *uri;
/* Parsed result of the |uri| */
struct http_parser_url *u;
/* The authroity portion of the |uri|, not NULL-terminated */
/* The authority portion of the |uri|, not NULL-terminated */
char *authority;
/* The path portion of the |uri|, including query, not
NULL-terminated */
@@ -258,8 +264,7 @@ static int on_data_chunk_recv_callback(nghttp2_session *session _U_,
stream), if it is closed, we send GOAWAY and tear down the
session */
static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
uint32_t error_code,
void *user_data) {
uint32_t error_code, void *user_data) {
http2_session_data *session_data = (http2_session_data *)user_data;
int rv;

View File

@@ -24,17 +24,27 @@
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /* !HAVE_CONFIG_H */
#endif /* HAVE_CONFIG_H */
#include <sys/types.h>
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif /* HAVE_SYS_SOCKET_H */
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif /* HAVE_NETDB_H */
#include <signal.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <sys/stat.h>
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif /* HAVE_FCNTL_H */
#include <ctype.h>
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif /* HAVE_NETINET_IN_H */
#include <netinet/tcp.h>
#include <err.h>

View File

@@ -30,18 +30,30 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /* !HAVE_CONFIG_H */
#endif /* HAVE_CONFIG_H */
#include <sys/types.h>
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif /* HAVE_SYS_SOCKET_H */
#include <sys/stat.h>
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif /* HAVE_FCNTL_H */
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif /* HAVE_NETDB_H */
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif /* HAVE_NETINET_IN_H */
#include <netinet/tcp.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <stdlib.h>
#ifdef HAVE_TIME_H
#include <time.h>
#endif /* HAVE_TIME_H */
#include <string.h>
#include <stdio.h>
#include <errno.h>
@@ -190,7 +202,7 @@ typedef enum {
NGHTTP2_TOKEN__METHOD,
NGHTTP2_TOKEN__PATH,
NGHTTP2_TOKEN__SCHEME,
NGHTTP2_TOKEN_HOST,
NGHTTP2_TOKEN_HOST
} nghttp2_token;
/* Inspired by h2o header lookup. https://github.com/h2o/h2o */
@@ -699,7 +711,8 @@ static ssize_t fd_read_callback(nghttp2_session *session _U_,
nghttp2_data_source *source,
void *user_data _U_) {
stream *strm = source->ptr;
ssize_t nread = (int64_t)length < strm->fileleft ? length : strm->fileleft;
ssize_t nread =
(int64_t)length < strm->fileleft ? (int64_t)length : strm->fileleft;
*data_flags |= NGHTTP2_DATA_FLAG_NO_COPY;

View File

@@ -1,19 +1,72 @@
#!/usr/bin/env python
HEADERS = [
':authority',
':method',
':path',
':scheme',
':status',
"content-length",
"host",
"te",
'connection',
'keep-alive',
'proxy-connection',
'transfer-encoding',
'upgrade'
(':authority', 0),
(':method', 1),
(':method', 2),
(':path', 3),
(':path', 4),
(':scheme', 5),
(':scheme', 6),
(':status', 7),
(':status', 8),
(':status', 9),
(':status', 10),
(':status', 11),
(':status', 12),
(':status', 13),
('accept-charset', 14),
('accept-encoding', 15),
('accept-language', 16),
('accept-ranges', 17),
('accept', 18),
('access-control-allow-origin', 19),
('age', 20),
('allow', 21),
('authorization', 22),
('cache-control', 23),
('content-disposition', 24),
('content-encoding', 25),
('content-language', 26),
('content-length', 27),
('content-location', 28),
('content-range', 29),
('content-type', 30),
('cookie', 31),
('date', 32),
('etag', 33),
('expect', 34),
('expires', 35),
('from', 36),
('host', 37),
('if-match', 38),
('if-modified-since', 39),
('if-none-match', 40),
('if-range', 41),
('if-unmodified-since', 42),
('last-modified', 43),
('link', 44),
('location', 45),
('max-forwards', 46),
('proxy-authenticate', 47),
('proxy-authorization', 48),
('range', 49),
('referer', 50),
('refresh', 51),
('retry-after', 52),
('server', 53),
('set-cookie', 54),
('strict-transport-security', 55),
('transfer-encoding', 56),
('user-agent', 57),
('vary', 58),
('via', 59),
('www-authenticate', 60),
('te', None),
('connection', None),
('keep-alive',None),
('proxy-connection', None),
('upgrade', None),
]
def to_enum_hd(k):
@@ -27,7 +80,7 @@ def to_enum_hd(k):
def build_header(headers):
res = {}
for k in headers:
for k, _ in headers:
size = len(k)
if size not in res:
res[size] = {}
@@ -40,18 +93,20 @@ def build_header(headers):
return res
def gen_enum():
print '''\
typedef enum {'''
for k in sorted(HEADERS):
print '''\
{},'''.format(to_enum_hd(k))
print '''\
NGHTTP2_TOKEN_MAXIDX,
} nghttp2_token;'''
name = ''
print 'typedef enum {'
for k, token in HEADERS:
if token is None:
print ' {},'.format(to_enum_hd(k))
else:
if name != k:
name = k
print ' {} = {},'.format(to_enum_hd(k), token)
print '} nghttp2_token;'
def gen_index_header():
print '''\
static int lookup_token(const uint8_t *name, size_t namelen) {
static inline int lookup_token(const uint8_t *name, size_t namelen) {
switch (namelen) {'''
b = build_header(HEADERS)
for size in sorted(b.keys()):
@@ -66,7 +121,7 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
case '{}':'''.format(c)
for k in headers:
print '''\
if (streq("{}", name, {})) {{
if (lstreq("{}", name, {})) {{
return {};
}}'''.format(k[:-1], size - 1, to_enum_hd(k))
print '''\

View File

@@ -61,6 +61,10 @@ def help2man(infile):
description.append(line)
print '''
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
.. program:: {cmdname}
{cmdname}(1)
{cmdnameunderline}

View File

@@ -246,6 +246,72 @@ func TestH1H1RequestTrailer(t *testing.T) {
}
}
// TestH1H1HeaderFieldBufferPath tests that request with request path
// larger than configured buffer size is rejected.
func TestH1H1HeaderFieldBufferPath(t *testing.T) {
// The value 100 is chosen so that sum of header fields bytes
// does not exceed it. We use > 100 bytes URI to exceed this
// limit.
st := newServerTester([]string{"--header-field-buffer=100"}, t, func(w http.ResponseWriter, r *http.Request) {
t.Fatal("execution path should not be here")
})
defer st.Close()
res, err := st.http1(requestParam{
name: "TestH1H1HeaderFieldBufferPath",
path: "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
})
if err != nil {
t.Fatalf("Error st.http1() = %v", err)
}
if got, want := res.status, 431; got != want {
t.Errorf("status: %v; want %v", got, want)
}
}
// TestH1H1HeaderFieldBuffer tests that request with header fields
// larger than configured buffer size is rejected.
func TestH1H1HeaderFieldBuffer(t *testing.T) {
st := newServerTester([]string{"--header-field-buffer=10"}, t, func(w http.ResponseWriter, r *http.Request) {
t.Fatal("execution path should not be here")
})
defer st.Close()
res, err := st.http1(requestParam{
name: "TestH1H1HeaderFieldBuffer",
})
if err != nil {
t.Fatalf("Error st.http1() = %v", err)
}
if got, want := res.status, 431; got != want {
t.Errorf("status: %v; want %v", got, want)
}
}
// TestH1H1HeaderFields tests that request with header fields more
// than configured number is rejected.
func TestH1H1HeaderFields(t *testing.T) {
st := newServerTester([]string{"--max-header-fields=1"}, t, func(w http.ResponseWriter, r *http.Request) {
t.Fatal("execution path should not be here")
})
defer st.Close()
res, err := st.http1(requestParam{
name: "TestH1H1HeaderFields",
header: []hpack.HeaderField{
// Add extra header field to ensure that
// header field limit exceeds
pair("Connection", "close"),
},
})
if err != nil {
t.Fatalf("Error st.http1() = %v", err)
}
if got, want := res.status, 431; got != want {
t.Errorf("status: %v; want %v", got, want)
}
}
// TestH1H2ConnectFailure tests that server handles the situation that
// connection attempt to HTTP/2 backend failed.
func TestH1H2ConnectFailure(t *testing.T) {

View File

@@ -558,6 +558,79 @@ func TestH2H1RequestTrailer(t *testing.T) {
}
}
// TestH2H1HeaderFieldBuffer tests that request with header fields
// larger than configured buffer size is rejected.
func TestH2H1HeaderFieldBuffer(t *testing.T) {
st := newServerTester([]string{"--header-field-buffer=10"}, t, func(w http.ResponseWriter, r *http.Request) {
t.Fatal("execution path should not be here")
})
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H1HeaderFieldBuffer",
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
if got, want := res.status, 431; got != want {
t.Errorf("status: %v; want %v", got, want)
}
}
// TestH2H1HeaderFields tests that request with header fields more
// than configured number is rejected.
func TestH2H1HeaderFields(t *testing.T) {
st := newServerTester([]string{"--max-header-fields=1"}, t, func(w http.ResponseWriter, r *http.Request) {
t.Fatal("execution path should not be here")
})
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H1HeaderFields",
// we have at least 4 pseudo-header fields sent, and
// that ensures that buffer limit exceeds.
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
if got, want := res.status, 431; got != want {
t.Errorf("status: %v; want %v", got, want)
}
}
// TestH2H1Upgrade tests HTTP Upgrade to HTTP/2
func TestH2H1Upgrade(t *testing.T) {
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {})
defer st.Close()
res, err := st.http1(requestParam{
name: "TestH2H1Upgrade",
header: []hpack.HeaderField{
pair("Connection", "Upgrade, HTTP2-Settings"),
pair("Upgrade", "h2c-14"),
pair("HTTP2-Settings", "AAMAAABkAAQAAP__"),
},
})
if err != nil {
t.Fatalf("Error st.http1() = %v", err)
}
if got, want := res.status, 101; got != want {
t.Errorf("res.status: %v; want %v", got, want)
}
res, err = st.http2(requestParam{
httpUpgrade: true,
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
if got, want := res.status, 200; got != want {
t.Errorf("res.status: %v; want %v", got, want)
}
}
// TestH2H1GracefulShutdown tests graceful shutdown.
func TestH2H1GracefulShutdown(t *testing.T) {
st := newServerTester(nil, t, noopHandler)

View File

@@ -170,6 +170,46 @@ func TestS3H1NoVia(t *testing.T) {
}
}
// TestS3H1HeaderFieldBuffer tests that request with header fields
// larger than configured buffer size is rejected.
func TestS3H1HeaderFieldBuffer(t *testing.T) {
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--header-field-buffer=10"}, t, func(w http.ResponseWriter, r *http.Request) {
t.Fatal("execution path should not be here")
})
defer st.Close()
res, err := st.spdy(requestParam{
name: "TestS3H1HeaderFieldBuffer",
})
if err != nil {
t.Fatalf("Error st.spdy() = %v", err)
}
if got, want := res.spdyRstErrCode, spdy.InternalError; got != want {
t.Errorf("res.spdyRstErrCode: %v; want %v", got, want)
}
}
// TestS3H1HeaderFields tests that request with header fields more
// than configured number is rejected.
func TestS3H1HeaderFields(t *testing.T) {
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--max-header-fields=1"}, t, func(w http.ResponseWriter, r *http.Request) {
t.Fatal("execution path should not be here")
})
defer st.Close()
res, err := st.spdy(requestParam{
name: "TestS3H1HeaderFields",
// we have at least 5 pseudo-header fields sent, and
// that ensures that buffer limit exceeds.
})
if err != nil {
t.Fatalf("Error st.spdy() = %v", err)
}
if got, want := res.spdyRstErrCode, spdy.InternalError; got != want {
t.Errorf("res.spdyRstErrCode: %v; want %v", got, want)
}
}
// TestS3H2ConnectFailure tests that server handles the situation that
// connection attempt to HTTP/2 backend failed.
func TestS3H2ConnectFailure(t *testing.T) {

View File

@@ -247,15 +247,16 @@ func (st *serverTester) readSpdyFrame() (spdy.Frame, error) {
}
type requestParam struct {
name string // name for this request to identify the request in log easily
streamID uint32 // stream ID, automatically assigned if 0
method string // method, defaults to GET
scheme string // scheme, defaults to http
authority string // authority, defaults to backend server address
path string // path, defaults to /
header []hpack.HeaderField // additional request header fields
body []byte // request body
trailer []hpack.HeaderField // trailer part
name string // name for this request to identify the request in log easily
streamID uint32 // stream ID, automatically assigned if 0
method string // method, defaults to GET
scheme string // scheme, defaults to http
authority string // authority, defaults to backend server address
path string // path, defaults to /
header []hpack.HeaderField // additional request header fields
body []byte // request body
trailer []hpack.HeaderField // trailer part
httpUpgrade bool // true if upgraded to HTTP/2 through HTTP Upgrade
}
// wrapper for request body to set trailer part
@@ -296,7 +297,19 @@ func (st *serverTester) http1(rp requestParam) (*serverResponse, error) {
body = cbr
}
}
req, err := http.NewRequest(method, st.url, body)
reqURL := st.url
if rp.path != "" {
u, err := url.Parse(st.url)
if err != nil {
st.t.Fatalf("Error parsing URL from st.url %v: %v", st.url, err)
}
u.Path = rp.path
reqURL = u.String()
}
req, err := http.NewRequest(method, reqURL, body)
if err != nil {
return nil, err
}
@@ -478,69 +491,70 @@ func (st *serverTester) http2(rp requestParam) (*serverResponse, error) {
streams := make(map[uint32]*serverResponse)
streams[id] = res
method := "GET"
if rp.method != "" {
method = rp.method
}
_ = st.enc.WriteField(pair(":method", method))
scheme := "http"
if rp.scheme != "" {
scheme = rp.scheme
}
_ = st.enc.WriteField(pair(":scheme", scheme))
authority := st.authority
if rp.authority != "" {
authority = rp.authority
}
_ = st.enc.WriteField(pair(":authority", authority))
path := "/"
if rp.path != "" {
path = rp.path
}
_ = st.enc.WriteField(pair(":path", path))
_ = st.enc.WriteField(pair("test-case", rp.name))
for _, h := range rp.header {
_ = st.enc.WriteField(h)
}
err := st.fr.WriteHeaders(http2.HeadersFrameParam{
StreamID: id,
EndStream: len(rp.body) == 0 && len(rp.trailer) == 0,
EndHeaders: true,
BlockFragment: st.headerBlkBuf.Bytes(),
})
if err != nil {
return nil, err
}
if len(rp.body) != 0 {
// TODO we assume rp.body fits in 1 frame
if err := st.fr.WriteData(id, len(rp.trailer) == 0, rp.body); err != nil {
return nil, err
if !rp.httpUpgrade {
method := "GET"
if rp.method != "" {
method = rp.method
}
}
_ = st.enc.WriteField(pair(":method", method))
if len(rp.trailer) != 0 {
st.headerBlkBuf.Reset()
for _, h := range rp.trailer {
scheme := "http"
if rp.scheme != "" {
scheme = rp.scheme
}
_ = st.enc.WriteField(pair(":scheme", scheme))
authority := st.authority
if rp.authority != "" {
authority = rp.authority
}
_ = st.enc.WriteField(pair(":authority", authority))
path := "/"
if rp.path != "" {
path = rp.path
}
_ = st.enc.WriteField(pair(":path", path))
_ = st.enc.WriteField(pair("test-case", rp.name))
for _, h := range rp.header {
_ = st.enc.WriteField(h)
}
err := st.fr.WriteHeaders(http2.HeadersFrameParam{
StreamID: id,
EndStream: true,
EndStream: len(rp.body) == 0 && len(rp.trailer) == 0,
EndHeaders: true,
BlockFragment: st.headerBlkBuf.Bytes(),
})
if err != nil {
return nil, err
}
}
if len(rp.body) != 0 {
// TODO we assume rp.body fits in 1 frame
if err := st.fr.WriteData(id, len(rp.trailer) == 0, rp.body); err != nil {
return nil, err
}
}
if len(rp.trailer) != 0 {
st.headerBlkBuf.Reset()
for _, h := range rp.trailer {
_ = st.enc.WriteField(h)
}
err := st.fr.WriteHeaders(http2.HeadersFrameParam{
StreamID: id,
EndStream: true,
EndHeaders: true,
BlockFragment: st.headerBlkBuf.Bytes(),
})
if err != nil {
return nil, err
}
}
}
loop:
for {
fr, err := st.readFrame()

View File

@@ -36,7 +36,7 @@ extern "C" {
#endif
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <sys/types.h>
#include <nghttp2/nghttp2ver.h>
@@ -241,8 +241,9 @@ typedef enum {
*/
NGHTTP2_ERR_UNSUPPORTED_VERSION = -503,
/**
* Used as a return value from :type:`nghttp2_send_callback` and
* :type:`nghttp2_recv_callback` to indicate that the operation
* Used as a return value from :type:`nghttp2_send_callback`,
* :type:`nghttp2_recv_callback` and
* :type:`nghttp2_send_data_callback` to indicate that the operation
* would block.
*/
NGHTTP2_ERR_WOULDBLOCK = -504,
@@ -1275,10 +1276,10 @@ typedef ssize_t (*nghttp2_recv_callback)(nghttp2_session *session, uint8_t *buf,
/**
* @functypedef
*
* Callback function invoked by `nghttp2_session_recv()` when a frame
* is received. The |user_data| pointer is the third argument passed
* in to the call to `nghttp2_session_client_new()` or
* `nghttp2_session_server_new()`.
* Callback function invoked by `nghttp2_session_recv()` and
* `nghttp2_session_mem_recv()` when a frame is received. The
* |user_data| pointer is the third argument passed in to the call to
* `nghttp2_session_client_new()` or `nghttp2_session_server_new()`.
*
* If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen``
* member of their data structure are always ``NULL`` and 0
@@ -1313,14 +1314,14 @@ typedef int (*nghttp2_on_frame_recv_callback)(nghttp2_session *session,
/**
* @functypedef
*
* Callback function invoked by `nghttp2_session_recv()` when an
* invalid non-DATA frame is received. The |error_code| indicates the
* error. It is usually one of the :enum:`nghttp2_error_code` but
* that is not guaranteed. When this callback function is invoked,
* the library automatically submits either RST_STREAM or GOAWAY
* frame. The |user_data| pointer is the third argument passed in to
* the call to `nghttp2_session_client_new()` or
* `nghttp2_session_server_new()`.
* Callback function invoked by `nghttp2_session_recv()` and
* `nghttp2_session_mem_recv()` when an invalid non-DATA frame is
* received. The |error_code| indicates the error. It is usually one
* of the :enum:`nghttp2_error_code` but that is not guaranteed. When
* this callback function is invoked, the library automatically
* submits either RST_STREAM or GOAWAY frame. The |user_data| pointer
* is the third argument passed in to the call to
* `nghttp2_session_client_new()` or `nghttp2_session_server_new()`.
*
* If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen``
* member of their data structure are always ``NULL`` and 0
@@ -1328,7 +1329,7 @@ typedef int (*nghttp2_on_frame_recv_callback)(nghttp2_session *session,
*
* The implementation of this function must return 0 if it succeeds.
* If nonzero is returned, it is treated as fatal error and
* `nghttp2_session_recv()` and `nghttp2_session_send()` functions
* `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions
* immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
*
* To set this callback to :type:`nghttp2_session_callbacks`, use
@@ -1361,7 +1362,7 @@ typedef int (*nghttp2_on_invalid_frame_recv_callback)(
* region included in the input bytes.
*
* The implementation of this function must return 0 if it succeeds.
* If nonzero is returned, it is treated as fatal error and
* If nonzero is returned, it is treated as fatal error, and
* `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions
* immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
*
@@ -1384,7 +1385,7 @@ typedef int (*nghttp2_on_data_chunk_recv_callback)(nghttp2_session *session,
*
* The implementation of this function must return 0 if it succeeds.
* If nonzero is returned, it is treated as fatal error and
* `nghttp2_session_recv()` and `nghttp2_session_send()` functions
* `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions
* immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
*
* To set this callback to :type:`nghttp2_session_callbacks`, use
@@ -1403,7 +1404,7 @@ typedef int (*nghttp2_before_frame_send_callback)(nghttp2_session *session,
*
* The implementation of this function must return 0 if it succeeds.
* If nonzero is returned, it is treated as fatal error and
* `nghttp2_session_recv()` and `nghttp2_session_send()` functions
* `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions
* immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
*
* To set this callback to :type:`nghttp2_session_callbacks`, use
@@ -1425,7 +1426,7 @@ typedef int (*nghttp2_on_frame_send_callback)(nghttp2_session *session,
*
* The implementation of this function must return 0 if it succeeds.
* If nonzero is returned, it is treated as fatal error and
* `nghttp2_session_recv()` and `nghttp2_session_send()` functions
* `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions
* immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
*
* `nghttp2_session_get_stream_user_data()` can be used to get
@@ -1455,8 +1456,9 @@ typedef int (*nghttp2_on_frame_not_send_callback)(nghttp2_session *session,
*
* The implementation of this function must return 0 if it succeeds.
* If nonzero is returned, it is treated as fatal error and
* `nghttp2_session_recv()` and `nghttp2_session_send()` functions
* immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
* `nghttp2_session_recv()`, `nghttp2_session_mem_recv()`,
* `nghttp2_session_send()`, and `nghttp2_session_mem_send()`
* functions immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
*
* To set this callback to :type:`nghttp2_session_callbacks`, use
* `nghttp2_session_callbacks_set_on_stream_close_callback()`.
@@ -1498,13 +1500,26 @@ typedef int (*nghttp2_on_stream_close_callback)(nghttp2_session *session,
* frame with ``frame->headers.cat == NGHTTP2_HCAT_HEADERS``
* containing final response headers (non-1xx status code). The
* trailer headers also has ``frame->headers.cat ==
* NGHTTP2_HCAT_HEADERS`` which does not containg any status code.
* NGHTTP2_HCAT_HEADERS`` which does not contain any status code.
*
* The implementation of this function must return 0 if it succeeds or
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. If nonzero value other than
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned, it is treated as
* if :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned. If
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned,
* Returning :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close
* the stream (promised stream if frame is PUSH_PROMISE) by issuing
* RST_STREAM with :enum:`NGHTTP2_INTERNAL_ERROR`. In this case,
* :type:`nghttp2_on_header_callback` and
* :type:`nghttp2_on_frame_recv_callback` will not be invoked. If a
* different error code is desirable, use
* `nghttp2_submit_rst_stream()` with a desired error code and then
* return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. Again, use
* ``frame->push_promise.promised_stream_id`` as stream_id parameter
* in `nghttp2_submit_rst_stream()` if frame is PUSH_PROMISE.
*
* The implementation of this function must return 0 if it succeeds.
* It can return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` to
* reset the stream (promised stream if frame is PUSH_PROMISE). For
* critical errors, it must return
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. If the other value is
* returned, it is treated as if :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`
* is returned. If :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned,
* `nghttp2_session_mem_recv()` function will immediately return
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
*
@@ -1559,12 +1574,15 @@ typedef int (*nghttp2_on_begin_headers_callback)(nghttp2_session *session,
* included in the input bytes.
*
* Returning :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close
* the stream by issuing RST_STREAM with
* :enum:`NGHTTP2_INTERNAL_ERROR`. In this case,
* the stream (promised stream if frame is PUSH_PROMISE) by issuing
* RST_STREAM with :enum:`NGHTTP2_INTERNAL_ERROR`. In this case,
* :type:`nghttp2_on_header_callback` and
* :type:`nghttp2_on_frame_recv_callback` will not be invoked. If a
* different error code is desirable, use
* `nghttp2_submit_rst_stream()` with a desired error code and then
* return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`.
* return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. Again, use
* ``frame->push_promise.promised_stream_id`` as stream_id parameter
* in `nghttp2_submit_rst_stream()` if frame is PUSH_PROMISE.
*
* The implementation of this function must return 0 if it succeeds.
* It may return :enum:`NGHTTP2_ERR_PAUSE` or
@@ -1596,8 +1614,8 @@ typedef int (*nghttp2_on_header_callback)(nghttp2_session *session,
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. Returning
* ``frame->hd.length`` means no padding is added. Returning
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` will make
* `nghttp2_session_send()` function immediately return
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
* `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions
* immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
*
* To set this callback to :type:`nghttp2_session_callbacks`, use
* `nghttp2_session_callbacks_set_select_padding_callback()`.
@@ -1726,8 +1744,8 @@ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_recv_callback(
/**
* @function
*
* Sets callback function invoked by `nghttp2_session_recv()` when a
* frame is received.
* Sets callback function invoked by `nghttp2_session_recv()` and
* `nghttp2_session_mem_recv()` when a frame is received.
*/
NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_recv_callback(
nghttp2_session_callbacks *cbs,
@@ -1736,8 +1754,9 @@ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_recv_callback(
/**
* @function
*
* Sets callback function invoked by `nghttp2_session_recv()` when an
* invalid non-DATA frame is received.
* Sets callback function invoked by `nghttp2_session_recv()` and
* `nghttp2_session_mem_recv()` when an invalid non-DATA frame is
* received.
*/
NGHTTP2_EXTERN void
nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(
@@ -2707,7 +2726,9 @@ NGHTTP2_EXTERN uint32_t
*
* :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
* The |next_stream_id| is strictly less than the value
* `nghttp2_session_get_next_stream_id()` returns.
* `nghttp2_session_get_next_stream_id()` returns; or
* |next_stream_id| is invalid (e.g., even integer for client, or
* odd integer for server).
*/
NGHTTP2_EXTERN int nghttp2_session_set_next_stream_id(nghttp2_session *session,
int32_t next_stream_id);
@@ -2928,13 +2949,13 @@ nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec);
* If |data_prd| is not ``NULL``, it provides data which will be sent
* in subsequent DATA frames. In this case, a method that allows
* request message bodies
* (http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9) must
* be specified with ``:method`` key in |nva| (e.g. ``POST``). This
* function does not take ownership of the |data_prd|. The function
* copies the members of the |data_prd|. If |data_prd| is ``NULL``,
* HEADERS have END_STREAM set. The |stream_user_data| is data
* associated to the stream opened by this request and can be an
* arbitrary pointer, which can be retrieved later by
* (https://tools.ietf.org/html/rfc7231#section-4) must be specified
* with ``:method`` key in |nva| (e.g. ``POST``). This function does
* not take ownership of the |data_prd|. The function copies the
* members of the |data_prd|. If |data_prd| is ``NULL``, HEADERS have
* END_STREAM set. The |stream_user_data| is data associated to the
* stream opened by this request and can be an arbitrary pointer,
* which can be retrieved later by
* `nghttp2_session_get_stream_user_data()`.
*
* This function returns assigned stream ID if it succeeds, or one of
@@ -3541,7 +3562,10 @@ NGHTTP2_EXTERN int nghttp2_nv_compare_name(const nghttp2_nv *lhs,
* {
* int rv;
* rv = nghttp2_select_next_protocol(out, outlen, in, inlen);
* if(rv == 1) {
* if (rv == -1) {
* return SSL_TLSEXT_ERR_NOACK;
* }
* if (rv == 1) {
* ((MyType*)arg)->http2_selected = 1;
* }
* return SSL_TLSEXT_ERR_OK;

View File

@@ -320,7 +320,7 @@ int nghttp2_bufs_add(nghttp2_bufs *bufs, const void *data, size_t len) {
}
buf->last = nghttp2_cpymem(buf->last, p, nwrite);
p += len;
p += nwrite;
len -= nwrite;
}
@@ -432,6 +432,24 @@ ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out) {
return (ssize_t)len;
}
size_t nghttp2_bufs_remove_copy(nghttp2_bufs *bufs, uint8_t *out) {
size_t len;
nghttp2_buf_chain *chain;
nghttp2_buf *buf;
nghttp2_buf resbuf;
len = nghttp2_bufs_len(bufs);
nghttp2_buf_wrap_init(&resbuf, out, len);
for (chain = bufs->head; chain; chain = chain->next) {
buf = &chain->buf;
resbuf.last = nghttp2_cpymem(resbuf.last, buf->pos, nghttp2_buf_len(buf));
}
return len;
}
void nghttp2_bufs_reset(nghttp2_bufs *bufs) {
nghttp2_buf_chain *chain, *ci;
size_t k;

View File

@@ -324,6 +324,17 @@ int nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b);
*/
ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out);
/*
* Copies all data stored in |bufs| to |out|. This function assumes
* that the buffer space pointed by |out| has at least
* nghttp2_bufs(bufs) bytes.
*
* The contents of |bufs| is left unchanged.
*
* This function returns the length of copied data.
*/
size_t nghttp2_bufs_remove_copy(nghttp2_bufs *bufs, uint8_t *out);
/*
* Resets |bufs| and makes the buffers empty.
*/

File diff suppressed because it is too large Load Diff

View File

@@ -49,7 +49,68 @@
#define NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE (1 << 12)
/* Exported for unit test */
extern const size_t NGHTTP2_STATIC_TABLE_LENGTH;
#define NGHTTP2_STATIC_TABLE_LENGTH 61
/* Generated by genlibtokenlookup.py */
typedef enum {
NGHTTP2_TOKEN__AUTHORITY = 0,
NGHTTP2_TOKEN__METHOD = 1,
NGHTTP2_TOKEN__PATH = 3,
NGHTTP2_TOKEN__SCHEME = 5,
NGHTTP2_TOKEN__STATUS = 7,
NGHTTP2_TOKEN_ACCEPT_CHARSET = 14,
NGHTTP2_TOKEN_ACCEPT_ENCODING = 15,
NGHTTP2_TOKEN_ACCEPT_LANGUAGE = 16,
NGHTTP2_TOKEN_ACCEPT_RANGES = 17,
NGHTTP2_TOKEN_ACCEPT = 18,
NGHTTP2_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN = 19,
NGHTTP2_TOKEN_AGE = 20,
NGHTTP2_TOKEN_ALLOW = 21,
NGHTTP2_TOKEN_AUTHORIZATION = 22,
NGHTTP2_TOKEN_CACHE_CONTROL = 23,
NGHTTP2_TOKEN_CONTENT_DISPOSITION = 24,
NGHTTP2_TOKEN_CONTENT_ENCODING = 25,
NGHTTP2_TOKEN_CONTENT_LANGUAGE = 26,
NGHTTP2_TOKEN_CONTENT_LENGTH = 27,
NGHTTP2_TOKEN_CONTENT_LOCATION = 28,
NGHTTP2_TOKEN_CONTENT_RANGE = 29,
NGHTTP2_TOKEN_CONTENT_TYPE = 30,
NGHTTP2_TOKEN_COOKIE = 31,
NGHTTP2_TOKEN_DATE = 32,
NGHTTP2_TOKEN_ETAG = 33,
NGHTTP2_TOKEN_EXPECT = 34,
NGHTTP2_TOKEN_EXPIRES = 35,
NGHTTP2_TOKEN_FROM = 36,
NGHTTP2_TOKEN_HOST = 37,
NGHTTP2_TOKEN_IF_MATCH = 38,
NGHTTP2_TOKEN_IF_MODIFIED_SINCE = 39,
NGHTTP2_TOKEN_IF_NONE_MATCH = 40,
NGHTTP2_TOKEN_IF_RANGE = 41,
NGHTTP2_TOKEN_IF_UNMODIFIED_SINCE = 42,
NGHTTP2_TOKEN_LAST_MODIFIED = 43,
NGHTTP2_TOKEN_LINK = 44,
NGHTTP2_TOKEN_LOCATION = 45,
NGHTTP2_TOKEN_MAX_FORWARDS = 46,
NGHTTP2_TOKEN_PROXY_AUTHENTICATE = 47,
NGHTTP2_TOKEN_PROXY_AUTHORIZATION = 48,
NGHTTP2_TOKEN_RANGE = 49,
NGHTTP2_TOKEN_REFERER = 50,
NGHTTP2_TOKEN_REFRESH = 51,
NGHTTP2_TOKEN_RETRY_AFTER = 52,
NGHTTP2_TOKEN_SERVER = 53,
NGHTTP2_TOKEN_SET_COOKIE = 54,
NGHTTP2_TOKEN_STRICT_TRANSPORT_SECURITY = 55,
NGHTTP2_TOKEN_TRANSFER_ENCODING = 56,
NGHTTP2_TOKEN_USER_AGENT = 57,
NGHTTP2_TOKEN_VARY = 58,
NGHTTP2_TOKEN_VIA = 59,
NGHTTP2_TOKEN_WWW_AUTHENTICATE = 60,
NGHTTP2_TOKEN_TE,
NGHTTP2_TOKEN_CONNECTION,
NGHTTP2_TOKEN_KEEP_ALIVE,
NGHTTP2_TOKEN_PROXY_CONNECTION,
NGHTTP2_TOKEN_UPGRADE
} nghttp2_token;
typedef enum {
NGHTTP2_HD_FLAG_NONE = 0,
@@ -67,18 +128,14 @@ typedef enum {
typedef struct {
nghttp2_nv nv;
uint32_t name_hash;
uint32_t value_hash;
/* nghttp2_token value for nv.name. It could be -1 if we have no
token for that header field name. */
int token;
/* Reference count */
uint8_t ref;
uint8_t flags;
} nghttp2_hd_entry;
typedef struct {
nghttp2_hd_entry ent;
size_t index;
} nghttp2_hd_static_entry;
typedef struct {
nghttp2_hd_entry **buffer;
size_t mask;
@@ -107,6 +164,12 @@ typedef enum {
NGHTTP2_HD_STATE_READ_VALUE
} nghttp2_hd_inflate_state;
typedef enum {
NGHTTP2_HD_WITH_INDEXING,
NGHTTP2_HD_WITHOUT_INDEXING,
NGHTTP2_HD_NEVER_INDEXING
} nghttp2_hd_indexing_mode;
typedef struct {
/* dynamic header table */
nghttp2_hd_ringbuf hd_table;
@@ -176,9 +239,8 @@ struct nghttp2_hd_inflater {
* set in the |flags|, the content pointed by the |name| with length
* |namelen| is copied. Likewise, if NGHTTP2_HD_FLAG_VALUE_ALLOC bit
* set in the |flags|, the content pointed by the |value| with length
* |valuelen| is copied. The |name_hash| and |value_hash| are hash
* value for |name| and |value| respectively. The hash function is
* defined in nghttp2_hd.c.
* |valuelen| is copied. The |token| is enum number looked up by
* |name|. It could be -1 if we don't have that enum value.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
@@ -188,8 +250,7 @@ struct nghttp2_hd_inflater {
*/
int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t flags, uint8_t *name,
size_t namelen, uint8_t *value, size_t valuelen,
uint32_t name_hash, uint32_t value_hash,
nghttp2_mem *mem);
int token, nghttp2_mem *mem);
void nghttp2_hd_entry_free(nghttp2_hd_entry *ent, nghttp2_mem *mem);
@@ -271,13 +332,25 @@ int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater, nghttp2_mem *mem);
*/
void nghttp2_hd_inflate_free(nghttp2_hd_inflater *inflater);
/*
* Similar to nghttp2_hd_inflate_hd(), but this takes additional
* output parameter |token|. On successful header emission, it
* contains nghttp2_token value for nv_out->name. It could be -1 if
* we don't have enum value for the name. Other than that return
* values and semantics are the same as nghttp2_hd_inflate_hd().
*/
ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater,
nghttp2_nv *nv_out, int *inflate_flags,
int *token, uint8_t *in, size_t inlen,
int in_final);
/* For unittesting purpose */
int nghttp2_hd_emit_indname_block(nghttp2_bufs *bufs, size_t index,
nghttp2_nv *nv, int inc_indexing);
nghttp2_nv *nv, int indexing_mode);
/* For unittesting purpose */
int nghttp2_hd_emit_newname_block(nghttp2_bufs *bufs, nghttp2_nv *nv,
int inc_indexing);
int indexing_mode);
/* For unittesting purpose */
int nghttp2_hd_emit_table_size(nghttp2_bufs *bufs, size_t table_size);

View File

@@ -168,10 +168,27 @@ void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx) {
ctx->accept = 1;
}
/* Use macro to make the code simpler..., but error case is tricky.
We spent most of the CPU in decoding, so we are doing this
thing. */
#define hd_huff_decode_sym_emit(bufs, sym, avail) \
do { \
if ((avail)) { \
nghttp2_bufs_fast_addb((bufs), (sym)); \
--(avail); \
} else { \
rv = nghttp2_bufs_addb((bufs), (sym)); \
if (rv != 0) { \
return rv; \
} \
(avail) = nghttp2_bufs_cur_avail((bufs)); \
} \
} while (0)
ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx,
nghttp2_bufs *bufs, const uint8_t *src,
size_t srclen, int final) {
size_t i, j;
size_t i;
int rv;
size_t avail;
@@ -180,30 +197,28 @@ ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx,
/* We use the decoding algorithm described in
http://graphics.ics.uci.edu/pub/Prefix.pdf */
for (i = 0; i < srclen; ++i) {
uint8_t in = src[i] >> 4;
for (j = 0; j < 2; ++j) {
const nghttp2_huff_decode *t;
const nghttp2_huff_decode *t;
t = &huff_decode_table[ctx->state][in];
if (t->flags & NGHTTP2_HUFF_FAIL) {
return NGHTTP2_ERR_HEADER_COMP;
}
if (t->flags & NGHTTP2_HUFF_SYM) {
if (avail) {
nghttp2_bufs_fast_addb(bufs, t->sym);
--avail;
} else {
rv = nghttp2_bufs_addb(bufs, t->sym);
if (rv != 0) {
return rv;
}
avail = nghttp2_bufs_cur_avail(bufs);
}
}
ctx->state = t->state;
ctx->accept = (t->flags & NGHTTP2_HUFF_ACCEPTED) != 0;
in = src[i] & 0xf;
t = &huff_decode_table[ctx->state][src[i] >> 4];
if (t->flags & NGHTTP2_HUFF_FAIL) {
return NGHTTP2_ERR_HEADER_COMP;
}
if (t->flags & NGHTTP2_HUFF_SYM) {
/* this is macro, and may return from this function on error */
hd_huff_decode_sym_emit(bufs, t->sym, avail);
}
t = &huff_decode_table[t->state][src[i] & 0xf];
if (t->flags & NGHTTP2_HUFF_FAIL) {
return NGHTTP2_ERR_HEADER_COMP;
}
if (t->flags & NGHTTP2_HUFF_SYM) {
/* this is macro, and may return from this function on error */
hd_huff_decode_sym_emit(bufs, t->sym, avail);
}
ctx->state = t->state;
ctx->accept = (t->flags & NGHTTP2_HUFF_ACCEPTED) != 0;
}
if (final && !ctx->accept) {
return NGHTTP2_ERR_HEADER_COMP;

View File

@@ -29,12 +29,16 @@
#include <config.h>
#endif /* HAVE_CONFIG_H */
#include <string.h>
#include <nghttp2/nghttp2.h>
#include "nghttp2_mem.h"
#define nghttp2_min(A, B) ((A) < (B) ? (A) : (B))
#define nghttp2_max(A, B) ((A) > (B) ? (A) : (B))
#define lstreq(A, B, N) ((sizeof((A)) - 1) == (N) && memcmp((A), (B), (N)) == 0)
/*
* Copies 2 byte unsigned integer |n| in host byte order to |buf| in
* network byte order.

View File

@@ -28,11 +28,8 @@
#include <assert.h>
#include <stdio.h>
static int memeq(const void *a, const void *b, size_t n) {
return memcmp(a, b, n) == 0;
}
#define streq(A, B, N) ((sizeof((A)) - 1) == (N) && memeq((A), (B), (N)))
#include "nghttp2_hd.h"
#include "nghttp2_helper.h"
static char downcase(char c) {
return 'A' <= c && c <= 'Z' ? (c - 'A' + 'a') : c;
@@ -50,129 +47,7 @@ static int memieq(const void *a, const void *b, size_t n) {
return 1;
}
#define strieq(A, B, N) ((sizeof((A)) - 1) == (N) && memieq((A), (B), (N)))
typedef enum {
NGHTTP2_TOKEN__AUTHORITY,
NGHTTP2_TOKEN__METHOD,
NGHTTP2_TOKEN__PATH,
NGHTTP2_TOKEN__SCHEME,
NGHTTP2_TOKEN__STATUS,
NGHTTP2_TOKEN_CONNECTION,
NGHTTP2_TOKEN_CONTENT_LENGTH,
NGHTTP2_TOKEN_HOST,
NGHTTP2_TOKEN_KEEP_ALIVE,
NGHTTP2_TOKEN_PROXY_CONNECTION,
NGHTTP2_TOKEN_TE,
NGHTTP2_TOKEN_TRANSFER_ENCODING,
NGHTTP2_TOKEN_UPGRADE,
NGHTTP2_TOKEN_MAXIDX,
} nghttp2_token;
/*
* This function was generated by genlibtokenlookup.py. Inspired by
* h2o header lookup. https://github.com/h2o/h2o
*/
static int lookup_token(const uint8_t *name, size_t namelen) {
switch (namelen) {
case 2:
switch (name[1]) {
case 'e':
if (streq("t", name, 1)) {
return NGHTTP2_TOKEN_TE;
}
break;
}
break;
case 4:
switch (name[3]) {
case 't':
if (streq("hos", name, 3)) {
return NGHTTP2_TOKEN_HOST;
}
break;
}
break;
case 5:
switch (name[4]) {
case 'h':
if (streq(":pat", name, 4)) {
return NGHTTP2_TOKEN__PATH;
}
break;
}
break;
case 7:
switch (name[6]) {
case 'd':
if (streq(":metho", name, 6)) {
return NGHTTP2_TOKEN__METHOD;
}
break;
case 'e':
if (streq(":schem", name, 6)) {
return NGHTTP2_TOKEN__SCHEME;
}
if (streq("upgrad", name, 6)) {
return NGHTTP2_TOKEN_UPGRADE;
}
break;
case 's':
if (streq(":statu", name, 6)) {
return NGHTTP2_TOKEN__STATUS;
}
break;
}
break;
case 10:
switch (name[9]) {
case 'e':
if (streq("keep-aliv", name, 9)) {
return NGHTTP2_TOKEN_KEEP_ALIVE;
}
break;
case 'n':
if (streq("connectio", name, 9)) {
return NGHTTP2_TOKEN_CONNECTION;
}
break;
case 'y':
if (streq(":authorit", name, 9)) {
return NGHTTP2_TOKEN__AUTHORITY;
}
break;
}
break;
case 14:
switch (name[13]) {
case 'h':
if (streq("content-lengt", name, 13)) {
return NGHTTP2_TOKEN_CONTENT_LENGTH;
}
break;
}
break;
case 16:
switch (name[15]) {
case 'n':
if (streq("proxy-connectio", name, 15)) {
return NGHTTP2_TOKEN_PROXY_CONNECTION;
}
break;
}
break;
case 17:
switch (name[16]) {
case 'g':
if (streq("transfer-encodin", name, 16)) {
return NGHTTP2_TOKEN_TRANSFER_ENCODING;
}
break;
}
break;
}
return -1;
}
#define lstrieq(A, B, N) ((sizeof((A)) - 1) == (N) && memieq((A), (B), (N)))
static int64_t parse_uint(const uint8_t *s, size_t len) {
int64_t n = 0;
@@ -238,9 +113,7 @@ static int check_path(nghttp2_stream *stream) {
}
static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
int trailer) {
int token;
int token, int trailer) {
if (nv->name[0] == ':') {
if (trailer ||
(stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
@@ -248,8 +121,6 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
}
}
token = lookup_token(nv->name, nv->namelen);
switch (token) {
case NGHTTP2_TOKEN__AUTHORITY:
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__AUTHORITY)) {
@@ -262,14 +133,14 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
}
switch (nv->valuelen) {
case 4:
if (streq("HEAD", nv->value, nv->valuelen)) {
if (lstreq("HEAD", nv->value, nv->valuelen)) {
stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
}
break;
case 7:
switch (nv->value[6]) {
case 'T':
if (streq("CONNECT", nv->value, nv->valuelen)) {
if (lstreq("CONNECT", nv->value, nv->valuelen)) {
if (stream->stream_id % 2 == 0) {
/* we won't allow CONNECT for push */
return NGHTTP2_ERR_HTTP_HEADER;
@@ -282,7 +153,7 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
}
break;
case 'S':
if (streq("OPTIONS", nv->value, nv->valuelen)) {
if (lstreq("OPTIONS", nv->value, nv->valuelen)) {
stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_OPTIONS;
}
break;
@@ -338,7 +209,7 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
case NGHTTP2_TOKEN_UPGRADE:
return NGHTTP2_ERR_HTTP_HEADER;
case NGHTTP2_TOKEN_TE:
if (!strieq("trailers", nv->value, nv->valuelen)) {
if (!lstrieq("trailers", nv->value, nv->valuelen)) {
return NGHTTP2_ERR_HTTP_HEADER;
}
break;
@@ -356,9 +227,7 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
}
static int http_response_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
int trailer) {
int token;
int token, int trailer) {
if (nv->name[0] == ':') {
if (trailer ||
(stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
@@ -366,8 +235,6 @@ static int http_response_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
}
}
token = lookup_token(nv->name, nv->namelen);
switch (token) {
case NGHTTP2_TOKEN__STATUS: {
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__STATUS)) {
@@ -400,7 +267,7 @@ static int http_response_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
case NGHTTP2_TOKEN_UPGRADE:
return NGHTTP2_ERR_HTTP_HEADER;
case NGHTTP2_TOKEN_TE:
if (!strieq("trailers", nv->value, nv->valuelen)) {
if (!lstrieq("trailers", nv->value, nv->valuelen)) {
return NGHTTP2_ERR_HTTP_HEADER;
}
break;
@@ -418,7 +285,8 @@ static int http_response_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
}
int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
nghttp2_frame *frame, nghttp2_nv *nv, int trailer) {
nghttp2_frame *frame, nghttp2_nv *nv, int token,
int trailer) {
/* We are strict for pseudo header field. One bad character should
lead to fail. OTOH, we should be a bit forgiving for regular
headers, since existing public internet has so much illegal
@@ -458,10 +326,10 @@ int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
}
if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
return http_request_on_header(stream, nv, trailer);
return http_request_on_header(stream, nv, token, trailer);
}
return http_response_on_header(stream, nv, trailer);
return http_response_on_header(stream, nv, token, trailer);
}
int nghttp2_http_on_request_headers(nghttp2_stream *stream,
@@ -574,14 +442,15 @@ void nghttp2_http_record_request_method(nghttp2_stream *stream,
/* TODO we should do this strictly. */
for (i = 0; i < nvlen; ++i) {
const nghttp2_nv *nv = &nva[i];
if (lookup_token(nv->name, nv->namelen) != NGHTTP2_TOKEN__METHOD) {
if (!(nv->namelen == 7 && nv->name[6] == 'd' &&
memcmp(":metho", nv->name, nv->namelen - 1) == 0)) {
continue;
}
if (streq("CONNECT", nv->value, nv->valuelen)) {
if (lstreq("CONNECT", nv->value, nv->valuelen)) {
stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
return;
}
if (streq("HEAD", nv->value, nv->valuelen)) {
if (lstreq("HEAD", nv->value, nv->valuelen)) {
stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
return;
}

View File

@@ -36,7 +36,8 @@
/*
* This function is called when HTTP header field |nv| in |frame| is
* received for |stream|. This function will validate |nv| against
* the current state of stream.
* the current state of stream. The |token| is nghttp2_token value
* for nv->name, or -1 if we don't have enum value for the name.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
@@ -48,7 +49,8 @@
* if it was not received because of compatibility reasons.
*/
int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
nghttp2_frame *frame, nghttp2_nv *nv, int trailer);
nghttp2_frame *frame, nghttp2_nv *nv, int token,
int trailer);
/*
* This function is called when request header is received. This

View File

@@ -39,7 +39,8 @@
} while (0)
#endif
typedef int (*nghttp2_compar)(const void *lhs, const void *rhs);
/* "less" function, return nonzero if |lhs| is less than |rhs|. */
typedef int (*nghttp2_less)(const void *lhs, const void *rhs);
/* Internal error code. They must be in the range [-499, -100],
inclusive. */

View File

@@ -58,7 +58,7 @@ typedef enum {
*/
NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS = 1 << 1,
NGHTTP2_OPT_RECV_CLIENT_PREFACE = 1 << 2,
NGHTTP2_OPT_NO_HTTP_MESSAGING = 1 << 3,
NGHTTP2_OPT_NO_HTTP_MESSAGING = 1 << 3
} nghttp2_option_flag;
/**

View File

@@ -25,6 +25,15 @@
#include "nghttp2_outbound_item.h"
#include <assert.h>
#include <string.h>
void nghttp2_outbound_item_init(nghttp2_outbound_item *item) {
item->cycle = 0;
item->qnext = NULL;
item->queued = 0;
memset(&item->aux_data, 0, sizeof(nghttp2_aux_data));
}
void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem) {
nghttp2_frame *frame;
@@ -65,3 +74,32 @@ void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem) {
break;
}
}
void nghttp2_outbound_queue_init(nghttp2_outbound_queue *q) {
q->head = q->tail = NULL;
q->n = 0;
}
void nghttp2_outbound_queue_push(nghttp2_outbound_queue *q,
nghttp2_outbound_item *item) {
if (q->tail) {
q->tail = q->tail->qnext = item;
} else {
q->head = q->tail = item;
}
++q->n;
}
void nghttp2_outbound_queue_pop(nghttp2_outbound_queue *q) {
nghttp2_outbound_item *item;
if (!q->head) {
return;
}
item = q->head;
q->head = q->head->qnext;
item->qnext = NULL;
if (!q->head) {
q->tail = NULL;
}
--q->n;
}

View File

@@ -33,13 +33,6 @@
#include "nghttp2_frame.h"
#include "nghttp2_mem.h"
/* 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
/* struct used for HEADERS and PUSH_PROMISE frame */
typedef struct {
nghttp2_data_provider data_prd;
@@ -88,7 +81,7 @@ typedef enum {
/* indicates that this GOAWAY is just a notification for graceful
shutdown. No nghttp2_session.goaway_flags should be updated on
the reaction to this frame. */
NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE = 0x2,
NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE = 0x2
} nghttp2_goaway_aux_flag;
/* struct used for GOAWAY frame */
@@ -104,19 +97,31 @@ typedef union {
nghttp2_goaway_aux_data goaway;
} nghttp2_aux_data;
typedef struct {
struct nghttp2_outbound_item;
typedef struct nghttp2_outbound_item nghttp2_outbound_item;
struct nghttp2_outbound_item {
nghttp2_frame frame;
nghttp2_aux_data aux_data;
int64_t seq;
/* Reset count of weight. See comment for last_cycle in
nghttp2_session.h */
/* The priority used in priority comparion. Smaller is served
ealier. For PING, SETTINGS and non-DATA frames (excluding
response HEADERS frame) have dedicated cycle value defined above.
For DATA frame, cycle is computed by taking into account of
effective weight and frame payload length previously sent, so
that the amount of transmission is distributed across streams
proportional to effective weight (inside a tree). */
uint64_t cycle;
/* The priority used in priority comparion. Larger is served
ealier. */
int32_t weight;
nghttp2_outbound_item *qnext;
/* nonzero if this object is queued. */
uint8_t queued;
} nghttp2_outbound_item;
};
/*
* Initializes |item|. No memory allocation is done in this function.
* Don't call nghttp2_outbound_item_free() until frame member is
* initialized.
*/
void nghttp2_outbound_item_init(nghttp2_outbound_item *item);
/*
* Deallocates resource for |item|. If |item| is NULL, this function
@@ -124,4 +129,29 @@ typedef struct {
*/
void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem);
/*
* queue for nghttp2_outbound_item.
*/
typedef struct {
nghttp2_outbound_item *head, *tail;
/* number of items in this queue. */
size_t n;
} nghttp2_outbound_queue;
void nghttp2_outbound_queue_init(nghttp2_outbound_queue *q);
/* Pushes |item| into |q| */
void nghttp2_outbound_queue_push(nghttp2_outbound_queue *q,
nghttp2_outbound_item *item);
/* Pops |item| at the top from |q|. If |q| is empty, nothing
happens. */
void nghttp2_outbound_queue_pop(nghttp2_outbound_queue *q);
/* Returns the top item. */
#define nghttp2_outbound_queue_top(Q) ((Q)->head)
/* Returns the size of the queue */
#define nghttp2_outbound_queue_size(Q) ((Q)->n)
#endif /* NGHTTP2_OUTBOUND_ITEM_H */

View File

@@ -24,7 +24,7 @@
*/
#include "nghttp2_pq.h"
int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_compar compar, nghttp2_mem *mem) {
int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_less less, nghttp2_mem *mem) {
pq->mem = mem;
pq->capacity = 128;
pq->q = nghttp2_mem_malloc(mem, pq->capacity * sizeof(void *));
@@ -32,7 +32,7 @@ int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_compar compar, nghttp2_mem *mem) {
return NGHTTP2_ERR_NOMEM;
}
pq->length = 0;
pq->compar = compar;
pq->less = less;
return 0;
}
@@ -52,7 +52,7 @@ static void bubble_up(nghttp2_pq *pq, size_t index) {
return;
} else {
size_t parent = (index - 1) / 2;
if (pq->compar(pq->q[parent], pq->q[index]) > 0) {
if (pq->less(pq->q[index], pq->q[parent])) {
swap(pq, parent, index);
bubble_up(pq, parent);
}
@@ -93,7 +93,7 @@ static void bubble_down(nghttp2_pq *pq, size_t index) {
if (j >= pq->length) {
break;
}
if (pq->compar(pq->q[minindex], pq->q[j]) > 0) {
if (pq->less(pq->q[j], pq->q[minindex])) {
minindex = j;
}
}

View File

@@ -45,8 +45,8 @@ typedef struct {
/* The maximum number of items this pq can store. This is
automatically extended when length is reached to this value. */
size_t capacity;
/* The compare function between items */
nghttp2_compar compar;
/* The less function between items */
nghttp2_less less;
} nghttp2_pq;
/*
@@ -58,7 +58,7 @@ typedef struct {
* NGHTTP2_ERR_NOMEM
* Out of memory.
*/
int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_compar cmp, nghttp2_mem *mem);
int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_less less, nghttp2_mem *mem);
/*
* Deallocates any resources allocated for |pq|. The stored items are

View File

@@ -221,22 +221,13 @@ nghttp2_stream *nghttp2_session_get_stream_raw(nghttp2_session *session,
return (nghttp2_stream *)nghttp2_map_find(&session->streams, stream_id);
}
static int outbound_item_compar(const void *lhsx, const void *rhsx) {
static int outbound_item_less(const void *lhsx, const void *rhsx) {
const nghttp2_outbound_item *lhs, *rhs;
lhs = (const nghttp2_outbound_item *)lhsx;
rhs = (const nghttp2_outbound_item *)rhsx;
if (lhs->cycle == rhs->cycle) {
if (lhs->weight == rhs->weight) {
return (lhs->seq < rhs->seq) ? -1 : ((lhs->seq > rhs->seq) ? 1 : 0);
}
/* Larger weight has higher precedence */
return rhs->weight - lhs->weight;
}
return (lhs->cycle < rhs->cycle) ? -1 : 1;
return (lhs->cycle < rhs->cycle) ? 1 : 0;
}
static void session_inbound_frame_reset(nghttp2_session *session) {
@@ -338,15 +329,7 @@ static int session_new(nghttp2_session **session_ptr,
/* next_stream_id is initialized in either
nghttp2_session_client_new2 or nghttp2_session_server_new2 */
rv = nghttp2_pq_init(&(*session_ptr)->ob_pq, outbound_item_compar, mem);
if (rv != 0) {
goto fail_ob_pq;
}
rv = nghttp2_pq_init(&(*session_ptr)->ob_ss_pq, outbound_item_compar, mem);
if (rv != 0) {
goto fail_ob_ss_pq;
}
rv = nghttp2_pq_init(&(*session_ptr)->ob_da_pq, outbound_item_compar, mem);
rv = nghttp2_pq_init(&(*session_ptr)->ob_da_pq, outbound_item_less, mem);
if (rv != 0) {
goto fail_ob_da_pq;
}
@@ -366,9 +349,6 @@ static int session_new(nghttp2_session **session_ptr,
nghttp2_stream_roots_init(&(*session_ptr)->roots);
(*session_ptr)->next_seq = 0;
(*session_ptr)->last_cycle = 1;
(*session_ptr)->remote_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE;
(*session_ptr)->recv_window_size = 0;
(*session_ptr)->consumed_size = 0;
@@ -457,10 +437,6 @@ fail_hd_inflater:
fail_hd_deflater:
nghttp2_pq_free(&(*session_ptr)->ob_da_pq);
fail_ob_da_pq:
nghttp2_pq_free(&(*session_ptr)->ob_ss_pq);
fail_ob_ss_pq:
nghttp2_pq_free(&(*session_ptr)->ob_pq);
fail_ob_pq:
nghttp2_mem_free(mem, *session_ptr);
fail_session:
return rv;
@@ -566,6 +542,16 @@ static void ob_pq_free(nghttp2_pq *pq, nghttp2_mem *mem) {
nghttp2_pq_free(pq);
}
static void ob_q_free(nghttp2_outbound_queue *q, nghttp2_mem *mem) {
nghttp2_outbound_item *item, *next;
for (item = q->head; item;) {
next = item->qnext;
nghttp2_outbound_item_free(item, mem);
nghttp2_mem_free(mem, item);
item = next;
}
}
void nghttp2_session_del(nghttp2_session *session) {
nghttp2_mem *mem;
@@ -584,8 +570,9 @@ void nghttp2_session_del(nghttp2_session *session) {
nghttp2_map_each_free(&session->streams, free_streams, session);
nghttp2_map_free(&session->streams);
ob_pq_free(&session->ob_pq, mem);
ob_pq_free(&session->ob_ss_pq, mem);
ob_q_free(&session->ob_urgent, mem);
ob_q_free(&session->ob_reg, mem);
ob_q_free(&session->ob_syn, mem);
ob_pq_free(&session->ob_da_pq, mem);
active_outbound_item_reset(&session->aob, mem);
session_inbound_frame_reset(session);
@@ -691,17 +678,6 @@ nghttp2_session_reprioritize_stream(nghttp2_session *session,
return 0;
}
void nghttp2_session_outbound_item_init(nghttp2_session *session,
nghttp2_outbound_item *item) {
item->seq = session->next_seq++;
/* We use cycle for DATA only */
item->cycle = 0;
item->weight = NGHTTP2_OB_EX_WEIGHT;
item->queued = 0;
memset(&item->aux_data, 0, sizeof(nghttp2_aux_data));
}
int nghttp2_session_add_item(nghttp2_session *session,
nghttp2_outbound_item *item) {
/* TODO Return error if stream is not found for the frame requiring
@@ -716,66 +692,45 @@ int nghttp2_session_add_item(nghttp2_session *session,
if (frame->hd.type != NGHTTP2_DATA) {
switch (frame->hd.type) {
case NGHTTP2_RST_STREAM:
if (stream) {
stream->state = NGHTTP2_STREAM_CLOSING;
}
break;
case NGHTTP2_SETTINGS:
item->weight = NGHTTP2_OB_SETTINGS_WEIGHT;
break;
case NGHTTP2_PING:
/* Ping has highest priority. */
item->weight = NGHTTP2_OB_PING_WEIGHT;
break;
default:
break;
}
if (frame->hd.type == NGHTTP2_HEADERS) {
case NGHTTP2_HEADERS:
/* We push request HEADERS and push response HEADERS to
dedicated queue because their transmission is affected by
SETTINGS_MAX_CONCURRENT_STREAMS */
/* TODO If 2 HEADERS are submitted for reserved stream, then
both of them are queued into ob_ss_pq, which is not
both of them are queued into ob_syn, which is not
desirable. */
if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
rv = nghttp2_pq_push(&session->ob_ss_pq, item);
if (rv != 0) {
return rv;
}
nghttp2_outbound_queue_push(&session->ob_syn, item);
item->queued = 1;
} else if (stream && (stream->state == NGHTTP2_STREAM_RESERVED ||
item->aux_data.headers.attach_stream)) {
item->weight = stream->effective_weight;
item->cycle = session->last_cycle;
break;
}
if (stream && (stream->state == NGHTTP2_STREAM_RESERVED ||
item->aux_data.headers.attach_stream)) {
rv = nghttp2_stream_attach_item(stream, item, session);
if (rv != 0) {
return rv;
}
} else {
rv = nghttp2_pq_push(&session->ob_pq, item);
if (rv != 0) {
return rv;
}
item->queued = 1;
}
} else {
rv = nghttp2_pq_push(&session->ob_pq, item);
if (rv != 0) {
return rv;
break;
}
nghttp2_outbound_queue_push(&session->ob_reg, item);
item->queued = 1;
break;
case NGHTTP2_SETTINGS:
case NGHTTP2_PING:
nghttp2_outbound_queue_push(&session->ob_urgent, item);
item->queued = 1;
break;
case NGHTTP2_RST_STREAM:
if (stream) {
stream->state = NGHTTP2_STREAM_CLOSING;
}
/* fall through */
default:
nghttp2_outbound_queue_push(&session->ob_reg, item);
item->queued = 1;
}
@@ -790,9 +745,6 @@ int nghttp2_session_add_item(nghttp2_session *session,
return NGHTTP2_ERR_DATA_EXIST;
}
item->weight = stream->effective_weight;
item->cycle = session->last_cycle;
rv = nghttp2_stream_attach_item(stream, item, session);
if (rv != 0) {
@@ -802,30 +754,6 @@ int nghttp2_session_add_item(nghttp2_session *session,
return 0;
}
typedef struct {
int32_t stream_id;
uint32_t error_code;
} nghttp2_rst_target;
static int cancel_pending_request(void *pq_item, void *arg) {
nghttp2_outbound_item *item;
nghttp2_rst_target *t;
nghttp2_headers_aux_data *aux_data;
item = pq_item;
t = arg;
aux_data = &item->aux_data.headers;
if (item->frame.hd.stream_id != t->stream_id || aux_data->canceled) {
return 0;
}
aux_data->error_code = t->error_code;
aux_data->canceled = 1;
return 1;
}
int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id,
uint32_t error_code) {
int rv;
@@ -833,7 +761,6 @@ int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id,
nghttp2_frame *frame;
nghttp2_stream *stream;
nghttp2_mem *mem;
nghttp2_rst_target t = {stream_id, error_code};
mem = &session->mem;
stream = nghttp2_session_get_stream(session, stream_id);
@@ -841,21 +768,35 @@ int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id,
return 0;
}
/* Cancel pending request HEADERS in ob_ss_pq if this RST_STREAM
/* Cancel pending request HEADERS in ob_syn if this RST_STREAM
refers to that stream. */
if (!session->server && nghttp2_session_is_my_stream_id(session, stream_id) &&
nghttp2_pq_top(&session->ob_ss_pq)) {
nghttp2_outbound_item *top;
nghttp2_outbound_queue_top(&session->ob_syn)) {
nghttp2_headers_aux_data *aux_data;
nghttp2_frame *headers_frame;
top = nghttp2_pq_top(&session->ob_ss_pq);
headers_frame = &top->frame;
headers_frame = &nghttp2_outbound_queue_top(&session->ob_syn)->frame;
assert(headers_frame->hd.type == NGHTTP2_HEADERS);
if (headers_frame->hd.stream_id <= stream_id &&
(uint32_t)stream_id < session->next_stream_id) {
if (nghttp2_pq_each(&session->ob_ss_pq, cancel_pending_request, &t)) {
for (item = session->ob_syn.head; item; item = item->qnext) {
aux_data = &item->aux_data.headers;
if (item->frame.hd.stream_id < stream_id) {
continue;
}
/* stream_id in ob_syn queue must be strictly increasing. If
we found larger ID, then we can break here. */
if (item->frame.hd.stream_id > stream_id || aux_data->canceled) {
break;
}
aux_data->error_code = error_code;
aux_data->canceled = 1;
return 0;
}
}
@@ -866,7 +807,7 @@ int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id,
return NGHTTP2_ERR_NOMEM;
}
nghttp2_session_outbound_item_init(session, item);
nghttp2_outbound_item_init(item);
frame = &item->frame;
@@ -1986,7 +1927,8 @@ static int session_prep_frame(nghttp2_session *session,
}
rv = nghttp2_session_pack_data(session, &session->aob.framebufs,
next_readmax, frame, &item->aux_data.data);
next_readmax, frame, &item->aux_data.data,
stream);
if (rv == NGHTTP2_ERR_DEFERRED) {
rv = nghttp2_stream_defer_item(stream, NGHTTP2_STREAM_FLAG_DEFERRED_USER,
session);
@@ -2028,125 +1970,66 @@ static int session_prep_frame(nghttp2_session *session,
}
}
/* Used only for tests */
nghttp2_outbound_item *nghttp2_session_get_ob_pq_top(nghttp2_session *session) {
return (nghttp2_outbound_item *)nghttp2_pq_top(&session->ob_pq);
}
nghttp2_outbound_item *
nghttp2_session_get_next_ob_item(nghttp2_session *session) {
nghttp2_outbound_item *item, *headers_item;
if (nghttp2_outbound_queue_top(&session->ob_urgent)) {
return nghttp2_outbound_queue_top(&session->ob_urgent);
}
if (nghttp2_pq_empty(&session->ob_pq)) {
if (nghttp2_pq_empty(&session->ob_ss_pq)) {
if (session->remote_window_size == 0 ||
nghttp2_pq_empty(&session->ob_da_pq)) {
return NULL;
}
if (nghttp2_outbound_queue_top(&session->ob_reg)) {
return nghttp2_outbound_queue_top(&session->ob_reg);
}
return nghttp2_pq_top(&session->ob_da_pq);
if (!session_is_outgoing_concurrent_streams_max(session)) {
if (nghttp2_outbound_queue_top(&session->ob_syn)) {
return nghttp2_outbound_queue_top(&session->ob_syn);
}
/* Return item only when concurrent connection limit is not
reached */
if (session_is_outgoing_concurrent_streams_max(session)) {
if (session->remote_window_size == 0 ||
nghttp2_pq_empty(&session->ob_da_pq)) {
return NULL;
}
return nghttp2_pq_top(&session->ob_da_pq);
}
return nghttp2_pq_top(&session->ob_ss_pq);
}
if (nghttp2_pq_empty(&session->ob_ss_pq)) {
return nghttp2_pq_top(&session->ob_pq);
if (session->remote_window_size > 0 &&
!nghttp2_pq_empty(&session->ob_da_pq)) {
return nghttp2_pq_top(&session->ob_da_pq);
}
item = nghttp2_pq_top(&session->ob_pq);
headers_item = nghttp2_pq_top(&session->ob_ss_pq);
if (session_is_outgoing_concurrent_streams_max(session) ||
item->weight > headers_item->weight ||
(item->weight == headers_item->weight && item->seq < headers_item->seq)) {
return item;
}
return headers_item;
return NULL;
}
nghttp2_outbound_item *
nghttp2_session_pop_next_ob_item(nghttp2_session *session) {
nghttp2_outbound_item *item, *headers_item;
nghttp2_outbound_item *item;
if (nghttp2_pq_empty(&session->ob_pq)) {
if (nghttp2_pq_empty(&session->ob_ss_pq)) {
if (session->remote_window_size == 0 ||
nghttp2_pq_empty(&session->ob_da_pq)) {
return NULL;
}
item = nghttp2_outbound_queue_top(&session->ob_urgent);
if (item) {
nghttp2_outbound_queue_pop(&session->ob_urgent);
item->queued = 0;
return item;
}
item = nghttp2_pq_top(&session->ob_da_pq);
nghttp2_pq_pop(&session->ob_da_pq);
item = nghttp2_outbound_queue_top(&session->ob_reg);
if (item) {
nghttp2_outbound_queue_pop(&session->ob_reg);
item->queued = 0;
return item;
}
if (!session_is_outgoing_concurrent_streams_max(session)) {
item = nghttp2_outbound_queue_top(&session->ob_syn);
if (item) {
nghttp2_outbound_queue_pop(&session->ob_syn);
item->queued = 0;
return item;
}
}
/* Pop item only when concurrent connection limit is not
reached */
if (session_is_outgoing_concurrent_streams_max(session)) {
if (session->remote_window_size == 0 ||
nghttp2_pq_empty(&session->ob_da_pq)) {
return NULL;
}
item = nghttp2_pq_top(&session->ob_da_pq);
nghttp2_pq_pop(&session->ob_da_pq);
item->queued = 0;
return item;
}
item = nghttp2_pq_top(&session->ob_ss_pq);
nghttp2_pq_pop(&session->ob_ss_pq);
if (session->remote_window_size > 0 &&
!nghttp2_pq_empty(&session->ob_da_pq)) {
item = nghttp2_pq_top(&session->ob_da_pq);
nghttp2_pq_pop(&session->ob_da_pq);
item->queued = 0;
return item;
}
if (nghttp2_pq_empty(&session->ob_ss_pq)) {
item = nghttp2_pq_top(&session->ob_pq);
nghttp2_pq_pop(&session->ob_pq);
item->queued = 0;
return item;
}
item = nghttp2_pq_top(&session->ob_pq);
headers_item = nghttp2_pq_top(&session->ob_ss_pq);
if (session_is_outgoing_concurrent_streams_max(session) ||
item->weight > headers_item->weight ||
(item->weight == headers_item->weight && item->seq < headers_item->seq)) {
nghttp2_pq_pop(&session->ob_pq);
item->queued = 0;
return item;
}
nghttp2_pq_pop(&session->ob_ss_pq);
headers_item->queued = 0;
return headers_item;
return NULL;
}
static int session_call_before_frame_send(nghttp2_session *session,
@@ -2249,21 +2132,21 @@ static int session_close_stream_on_goaway(nghttp2_session *session,
return 0;
}
static void session_outbound_item_cycle_weight(nghttp2_session *session,
nghttp2_outbound_item *item,
int32_t ini_weight) {
if (item->weight == NGHTTP2_MIN_WEIGHT || item->weight > ini_weight) {
static void session_outbound_item_schedule(nghttp2_session *session,
nghttp2_outbound_item *item,
int32_t weight) {
/* Schedule next write. Offset proportional to the write size.
Stream with heavier weight is scheduled earlier. */
size_t delta = item->frame.hd.length * NGHTTP2_MAX_WEIGHT / weight;
item->weight = ini_weight;
if (item->cycle == session->last_cycle) {
item->cycle = ++session->last_cycle;
} else {
item->cycle = session->last_cycle;
}
} else {
--item->weight;
if (session->last_cycle < item->cycle) {
session->last_cycle = item->cycle;
}
/* We pretend to ignore overflow given that the value range of
item->cycle, which is uint64_t. nghttp2 won't explode even when
overflow occurs, there might be some disturbance of priority. */
item->cycle = session->last_cycle + delta;
}
/*
@@ -2583,20 +2466,12 @@ static int session_after_frame_sent2(nghttp2_session *session) {
assert(stream);
next_item = nghttp2_session_get_next_ob_item(session);
/* Imagine we hit connection window size limit while sending DATA
frame. If we decrement weight here, its stream might get
inferior share because the other streams' weight is not
decremented because of flow control. */
if (session->remote_window_size > 0 || stream->remote_window_size <= 0) {
session_outbound_item_cycle_weight(session, aob->item,
stream->effective_weight);
}
/* If priority of this stream is higher or equal to other stream
waiting at the top of the queue, we continue to send this
data. */
if (stream->dpri == NGHTTP2_STREAM_DPRI_TOP &&
(next_item == NULL || outbound_item_compar(item, next_item) < 0)) {
(next_item == NULL || (next_item->frame.hd.type == NGHTTP2_DATA &&
outbound_item_less(item, next_item)))) {
size_t next_readmax;
next_readmax = nghttp2_session_next_data_read(session, stream);
@@ -2634,7 +2509,7 @@ static int session_after_frame_sent2(nghttp2_session *session) {
nghttp2_bufs_reset(framebufs);
rv = nghttp2_session_pack_data(session, framebufs, next_readmax, frame,
aux_data);
aux_data, stream);
if (nghttp2_is_fatal(rv)) {
return rv;
}
@@ -3086,6 +2961,9 @@ static int session_call_on_begin_headers(nghttp2_session *session,
if (session->callbacks.on_begin_headers_callback) {
rv = session->callbacks.on_begin_headers_callback(session, frame,
session->user_data);
if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
return rv;
}
if (rv != 0) {
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
@@ -3230,6 +3108,7 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame,
nghttp2_stream *stream;
nghttp2_stream *subject_stream;
int trailer = 0;
int token;
*readlen_ptr = 0;
stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
@@ -3245,19 +3124,19 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame,
DEBUGF(fprintf(stderr, "recv: decoding header block %zu bytes\n", inlen));
for (;;) {
inflate_flags = 0;
proclen = nghttp2_hd_inflate_hd(&session->hd_inflater, &nv, &inflate_flags,
in, inlen, final);
proclen = nghttp2_hd_inflate_hd2(&session->hd_inflater, &nv, &inflate_flags,
&token, in, inlen, final);
if (nghttp2_is_fatal((int)proclen)) {
return (int)proclen;
}
if (proclen < 0) {
if (session->iframe.state == NGHTTP2_IB_READ_HEADER_BLOCK) {
if (stream && stream->state != NGHTTP2_STREAM_CLOSING) {
if (subject_stream && subject_stream->state != NGHTTP2_STREAM_CLOSING) {
/* Adding RST_STREAM here is very important. It prevents
from invoking subsequent callbacks for the same stream
ID. */
rv = nghttp2_session_add_rst_stream(session, frame->hd.stream_id,
NGHTTP2_COMPRESSION_ERROR);
rv = nghttp2_session_add_rst_stream(
session, subject_stream->stream_id, NGHTTP2_COMPRESSION_ERROR);
if (nghttp2_is_fatal(rv)) {
return rv;
@@ -3281,7 +3160,7 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame,
if (call_header_cb && (inflate_flags & NGHTTP2_HD_INFLATE_EMIT)) {
rv = 0;
if (subject_stream && session_enforce_http_messaging(session)) {
rv = nghttp2_http_on_header(session, subject_stream, frame, &nv,
rv = nghttp2_http_on_header(session, subject_stream, frame, &nv, token,
trailer);
if (rv == NGHTTP2_ERR_HTTP_HEADER) {
DEBUGF(fprintf(
@@ -5101,6 +4980,16 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
busy = 1;
if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
rv = nghttp2_session_add_rst_stream(
session, iframe->frame.hd.stream_id, NGHTTP2_INTERNAL_ERROR);
if (nghttp2_is_fatal(rv)) {
return rv;
}
iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
break;
}
if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) {
iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
break;
@@ -5345,6 +5234,16 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
busy = 1;
if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
rv = nghttp2_session_add_rst_stream(
session, iframe->frame.hd.stream_id, NGHTTP2_INTERNAL_ERROR);
if (nghttp2_is_fatal(rv)) {
return rv;
}
iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
break;
}
if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) {
iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
break;
@@ -5409,6 +5308,17 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
busy = 1;
if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
rv = nghttp2_session_add_rst_stream(
session, iframe->frame.push_promise.promised_stream_id,
NGHTTP2_INTERNAL_ERROR);
if (nghttp2_is_fatal(rv)) {
return rv;
}
iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
break;
}
if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) {
iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
break;
@@ -5515,8 +5425,12 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
in += hd_proclen;
iframe->payloadleft -= hd_proclen;
/* Use promised stream ID for PUSH_PROMISE */
rv = nghttp2_session_add_rst_stream(
session, iframe->frame.hd.stream_id, NGHTTP2_INTERNAL_ERROR);
session, iframe->frame.hd.type == NGHTTP2_PUSH_PROMISE
? iframe->frame.push_promise.promised_stream_id
: iframe->frame.hd.stream_id,
NGHTTP2_INTERNAL_ERROR);
if (nghttp2_is_fatal(rv)) {
return rv;
}
@@ -5999,10 +5913,12 @@ int nghttp2_session_want_write(nghttp2_session *session) {
* want to write them.
*/
if (session->aob.item == NULL && nghttp2_pq_empty(&session->ob_pq) &&
if (session->aob.item == NULL &&
nghttp2_outbound_queue_top(&session->ob_urgent) == NULL &&
nghttp2_outbound_queue_top(&session->ob_reg) == NULL &&
(nghttp2_pq_empty(&session->ob_da_pq) ||
session->remote_window_size == 0) &&
(nghttp2_pq_empty(&session->ob_ss_pq) ||
(nghttp2_outbound_queue_top(&session->ob_syn) == NULL ||
session_is_outgoing_concurrent_streams_max(session))) {
return 0;
}
@@ -6026,7 +5942,7 @@ int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags,
return NGHTTP2_ERR_NOMEM;
}
nghttp2_session_outbound_item_init(session, item);
nghttp2_outbound_item_init(item);
frame = &item->frame;
@@ -6075,7 +5991,7 @@ int nghttp2_session_add_goaway(nghttp2_session *session, int32_t last_stream_id,
return NGHTTP2_ERR_NOMEM;
}
nghttp2_session_outbound_item_init(session, item);
nghttp2_outbound_item_init(item);
frame = &item->frame;
@@ -6112,7 +6028,7 @@ int nghttp2_session_add_window_update(nghttp2_session *session, uint8_t flags,
return NGHTTP2_ERR_NOMEM;
}
nghttp2_session_outbound_item_init(session, item);
nghttp2_outbound_item_init(item);
frame = &item->frame;
@@ -6183,7 +6099,7 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags,
session->inflight_niv = niv;
}
nghttp2_session_outbound_item_init(session, item);
nghttp2_outbound_item_init(item);
frame = &item->frame;
@@ -6228,7 +6144,8 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags,
int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs,
size_t datamax, nghttp2_frame *frame,
nghttp2_data_aux_data *aux_data) {
nghttp2_data_aux_data *aux_data,
nghttp2_stream *stream) {
int rv;
uint32_t data_flags;
ssize_t payloadlen;
@@ -6241,12 +6158,6 @@ int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs,
buf = &bufs->cur->buf;
if (session->callbacks.read_length_callback) {
nghttp2_stream *stream;
stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
if (!stream) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
payloadlen = session->callbacks.read_length_callback(
session, frame->hd.type, stream->stream_id, session->remote_window_size,
@@ -6360,6 +6271,9 @@ int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs,
return rv;
}
session_outbound_item_schedule(session, stream->item,
stream->effective_weight);
return 0;
}
@@ -6405,8 +6319,9 @@ int nghttp2_session_resume_data(nghttp2_session *session, int32_t stream_id) {
}
size_t nghttp2_session_get_outbound_queue_size(nghttp2_session *session) {
return nghttp2_pq_size(&session->ob_pq) +
nghttp2_pq_size(&session->ob_ss_pq) +
return nghttp2_outbound_queue_size(&session->ob_urgent) +
nghttp2_outbound_queue_size(&session->ob_reg) +
nghttp2_outbound_queue_size(&session->ob_syn) +
nghttp2_pq_size(&session->ob_da_pq);
}
@@ -6646,11 +6561,19 @@ int nghttp2_session_consume_stream(nghttp2_session *session, int32_t stream_id,
int nghttp2_session_set_next_stream_id(nghttp2_session *session,
int32_t next_stream_id) {
if (next_stream_id < 0 ||
if (next_stream_id <= 0 ||
session->next_stream_id > (uint32_t)next_stream_id) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
if (session->server) {
if (next_stream_id % 2) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
} else if (next_stream_id % 2 == 0) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
session->next_stream_id = next_stream_id;
return 0;
}

View File

@@ -47,7 +47,7 @@
typedef enum {
NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE = 1 << 0,
NGHTTP2_OPTMASK_RECV_CLIENT_PREFACE = 1 << 1,
NGHTTP2_OPTMASK_NO_HTTP_MESSAGING = 1 << 2,
NGHTTP2_OPTMASK_NO_HTTP_MESSAGING = 1 << 2
} nghttp2_optmask;
typedef enum {
@@ -84,7 +84,7 @@ typedef enum {
NGHTTP2_IB_READ_PAD_DATA,
NGHTTP2_IB_READ_DATA,
NGHTTP2_IB_IGN_DATA,
NGHTTP2_IB_IGN_ALL,
NGHTTP2_IB_IGN_ALL
} nghttp2_inbound_state;
#define NGHTTP2_INBOUND_NUM_IV 7
@@ -136,18 +136,21 @@ typedef enum {
/* Flag means GOAWAY was sent */
NGHTTP2_GOAWAY_SENT = 0x4,
/* Flag means GOAWAY was received */
NGHTTP2_GOAWAY_RECV = 0x8,
NGHTTP2_GOAWAY_RECV = 0x8
} nghttp2_goaway_flag;
struct nghttp2_session {
nghttp2_map /* <nghttp2_stream*> */ streams;
nghttp2_stream_roots roots;
/* Queue for outbound frames other than stream-creating HEADERS and
DATA */
nghttp2_pq /* <nghttp2_outbound_item*> */ ob_pq;
/* Queue for outbound stream-creating HEADERS frame */
nghttp2_pq /* <nghttp2_outbound_item*> */ ob_ss_pq;
/* QUeue for DATA frame */
/* Queue for outbound urgent frames (PING and SETTINGS) */
nghttp2_outbound_queue ob_urgent;
/* Queue for non-DATA frames */
nghttp2_outbound_queue ob_reg;
/* Queue for outbound stream-creating HEADERS (request or push
response) frame, which are subject to
SETTINGS_MAX_CONCURRENT_STREAMS limit. */
nghttp2_outbound_queue ob_syn;
/* Queue for DATA frame */
nghttp2_pq /* <nghttp2_outbound_item*> */ ob_da_pq;
nghttp2_active_outbound_item aob;
nghttp2_inbound_frame iframe;
@@ -156,22 +159,8 @@ struct nghttp2_session {
nghttp2_session_callbacks callbacks;
/* Memory allocator */
nghttp2_mem mem;
/* Sequence number of outbound frame to maintain the order of
enqueue if priority is equal. */
int64_t next_seq;
/* Reset count of nghttp2_outbound_item's weight. We decrements
weight each time DATA is sent to simulate resource sharing. We
use priority queue and larger weight has the precedence. If
weight is reached to lowest weight, it resets to its initial
weight. If this happens, other items which have the lower weight
currently but same initial weight cannot send DATA until item
having large weight is decreased. To avoid this, we use this
cycle variable. Initally, this is set to 1. If weight gets
lowest weight, and if item's cycle == last_cycle, we increments
last_cycle and assigns it to item's cycle. Otherwise, just
assign last_cycle. In priority queue comparator, we first
compare items' cycle value. Lower cycle value has the
precedence. */
/* Base value when we schedule next DATA frame write. This is
updated when one frame was written. */
uint64_t last_cycle;
void *user_data;
/* Points to the latest closed stream. NULL if there is no closed
@@ -291,14 +280,6 @@ typedef struct {
int nghttp2_session_is_my_stream_id(nghttp2_session *session,
int32_t stream_id);
/*
* Initializes |item|. No memory allocation is done in this function.
* Don't call nghttp2_outbound_item_free() until frame member is
* initialized.
*/
void nghttp2_session_outbound_item_init(nghttp2_session *session,
nghttp2_outbound_item *item);
/*
* Adds |item| to the outbound queue in |session|. When this function
* succeeds, it takes ownership of |item|. So caller must not free it
@@ -704,13 +685,8 @@ nghttp2_stream *nghttp2_session_get_stream_raw(nghttp2_session *session,
*/
int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs,
size_t datamax, nghttp2_frame *frame,
nghttp2_data_aux_data *aux_data);
/*
* Returns top of outbound frame queue. This function returns NULL if
* queue is empty.
*/
nghttp2_outbound_item *nghttp2_session_get_ob_pq_top(nghttp2_session *session);
nghttp2_data_aux_data *aux_data,
nghttp2_stream *stream);
/*
* Pops and returns next item to send. If there is no such item,

View File

@@ -63,7 +63,6 @@ void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
stream->effective_weight = stream->weight;
stream->sum_dep_weight = 0;
stream->sum_norest_weight = 0;
stream->sum_top_weight = 0;
stream->roots = roots;
stream->root_prev = NULL;
@@ -102,21 +101,26 @@ static int stream_push_item(nghttp2_stream *stream, nghttp2_session *session) {
return 0;
}
if (item->weight > stream->effective_weight) {
item->weight = stream->effective_weight;
}
item->cycle = session->last_cycle;
switch (item->frame.hd.type) {
case NGHTTP2_DATA:
/* Penalize item by delaying scheduling according to effective
weight. This will delay low priority stream, which is good.
OTOH, this may incur delay for high priority item. Will
see. */
item->cycle =
session->last_cycle +
NGHTTP2_DATA_PAYLOADLEN * NGHTTP2_MAX_WEIGHT / stream->effective_weight;
rv = nghttp2_pq_push(&session->ob_da_pq, item);
if (rv != 0) {
return rv;
}
break;
case NGHTTP2_HEADERS:
if (stream->state == NGHTTP2_STREAM_RESERVED) {
rv = nghttp2_pq_push(&session->ob_ss_pq, item);
nghttp2_outbound_queue_push(&session->ob_syn, item);
} else {
rv = nghttp2_pq_push(&session->ob_pq, item);
nghttp2_outbound_queue_push(&session->ob_reg, item);
}
break;
default:
@@ -124,10 +128,6 @@ static int stream_push_item(nghttp2_stream *stream, nghttp2_session *session) {
assert(0);
}
if (rv != 0) {
return rv;
}
item->queued = 1;
return 0;
@@ -178,18 +178,6 @@ int32_t nghttp2_stream_dep_distributed_effective_weight(nghttp2_stream *stream,
return nghttp2_max(1, weight);
}
static int32_t
stream_dep_distributed_top_effective_weight(nghttp2_stream *stream,
int32_t weight) {
if (stream->sum_top_weight == 0) {
return stream->effective_weight;
}
weight = stream->effective_weight * weight / stream->sum_top_weight;
return nghttp2_max(1, weight);
}
static void stream_update_dep_set_rest(nghttp2_stream *stream);
/* Updates effective_weight of descendant streams in subtree of
@@ -199,10 +187,9 @@ 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, "
"sum_top_weight=%d\n",
"stream(%p)=%d, weight=%d, sum_norest_weight=%d\n",
stream, stream->stream_id, stream->weight,
stream->sum_norest_weight, stream->sum_top_weight));
stream->sum_norest_weight));
/* stream->sum_norest_weight == 0 means there is no
NGHTTP2_STREAM_DPRI_TOP under stream */
@@ -211,47 +198,13 @@ static void stream_update_dep_effective_weight(nghttp2_stream *stream) {
return;
}
/* If there is no direct descendant whose dpri is
NGHTTP2_STREAM_DPRI_TOP, indirect descendants have the chance to
send data, so recursively set weight for descendants. */
if (stream->sum_top_weight == 0) {
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);
}
return;
}
/* If there is at least one direct descendant whose dpri is
NGHTTP2_STREAM_DPRI_TOP, we won't give a chance to indirect
descendants, since closed or blocked stream's weight is
distributed among its siblings */
for (si = stream->dep_next; si; si = si->sib_next) {
if (si->dpri == NGHTTP2_STREAM_DPRI_TOP) {
if (si->dpri != NGHTTP2_STREAM_DPRI_REST) {
si->effective_weight =
stream_dep_distributed_top_effective_weight(stream, si->weight);
DEBUGF(fprintf(stderr, "stream: stream=%d top eweight=%d\n",
si->stream_id, si->effective_weight));
continue;
nghttp2_stream_dep_distributed_effective_weight(stream, si->weight);
}
if (si->dpri == NGHTTP2_STREAM_DPRI_NO_ITEM) {
DEBUGF(fprintf(stderr, "stream: stream=%d no_item, ignored\n",
si->stream_id));
/* Since we marked NGHTTP2_STREAM_DPRI_TOP under si, we make
them NGHTTP2_STREAM_DPRI_REST again. */
stream_update_dep_set_rest(si->dep_next);
} else {
DEBUGF(
fprintf(stderr, "stream: stream=%d rest, ignored\n", si->stream_id));
}
stream_update_dep_effective_weight(si);
}
}
@@ -347,25 +300,20 @@ static int stream_update_dep_queue_top(nghttp2_stream *stream,
}
/*
* Updates stream->sum_norest_weight and stream->sum_top_weight
* recursively. We have to gather effective sum of weight of
* descendants. If stream->dpri == NGHTTP2_STREAM_DPRI_NO_ITEM, 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.
*
* Calculating stream->sum_top-weight is very simple compared to
* stream->sum_norest_weight. It just adds up the weight of direct
* descendants whose dpri is NGHTTP2_STREAM_DPRI_TOP.
* Updates stream->sum_norest_weight recursively. We have to gather
* effective sum of weight of descendants. If stream->dpri ==
* NGHTTP2_STREAM_DPRI_NO_ITEM, 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;
stream->sum_top_weight = 0;
if (stream->dpri == NGHTTP2_STREAM_DPRI_TOP) {
return 1;
@@ -383,10 +331,6 @@ static int stream_update_dep_sum_norest_weight(nghttp2_stream *stream) {
rv = 1;
stream->sum_norest_weight += si->weight;
}
if (si->dpri == NGHTTP2_STREAM_DPRI_TOP) {
stream->sum_top_weight += si->weight;
}
}
return rv;

View File

@@ -133,7 +133,7 @@ typedef enum {
/* "http" or "https" scheme */
NGHTTP2_HTTP_FLAG_SCHEME_HTTP = 1 << 12,
/* set if final response is expected */
NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE = 1 << 13,
NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE = 1 << 13
} nghttp2_http_flag;
typedef enum {
@@ -217,9 +217,6 @@ struct nghttp2_stream {
descendant with dpri == NGHTTP2_STREAM_DPRI_TOP. We use this
value to calculate effective weight. */
int32_t sum_norest_weight;
/* sum of weight of direct descendants whose dpri value is
NGHTTP2_STREAM_DPRI_TOP */
int32_t sum_top_weight;
nghttp2_stream_state state;
/* status code from remote server */
int16_t status_code;

View File

@@ -62,7 +62,7 @@ static int32_t submit_headers_shared(nghttp2_session *session, uint8_t flags,
goto fail;
}
nghttp2_session_outbound_item_init(session, item);
nghttp2_outbound_item_init(item);
if (data_prd != NULL && data_prd->read_callback != NULL) {
item->aux_data.headers.data_prd = *data_prd;
@@ -212,7 +212,7 @@ int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags _U_,
return NGHTTP2_ERR_NOMEM;
}
nghttp2_session_outbound_item_init(session, item);
nghttp2_outbound_item_init(item);
frame = &item->frame;
@@ -299,7 +299,7 @@ int32_t nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags _U_,
return NGHTTP2_ERR_NOMEM;
}
nghttp2_session_outbound_item_init(session, item);
nghttp2_outbound_item_init(item);
item->aux_data.headers.stream_user_data = promised_stream_user_data;
@@ -453,7 +453,7 @@ int nghttp2_submit_data(nghttp2_session *session, uint8_t flags,
return NGHTTP2_ERR_NOMEM;
}
nghttp2_session_outbound_item_init(session, item);
nghttp2_outbound_item_init(item);
frame = &item->frame;
aux_data = &item->aux_data.data;

View File

@@ -10,8 +10,271 @@
from __future__ import unicode_literals
import re
import sys
import StringIO
# From [1]
HUFFMAN_CODE_TABLE = """\
( 0) |11111111|11000 1ff8 [13]
( 1) |11111111|11111111|1011000 7fffd8 [23]
( 2) |11111111|11111111|11111110|0010 fffffe2 [28]
( 3) |11111111|11111111|11111110|0011 fffffe3 [28]
( 4) |11111111|11111111|11111110|0100 fffffe4 [28]
( 5) |11111111|11111111|11111110|0101 fffffe5 [28]
( 6) |11111111|11111111|11111110|0110 fffffe6 [28]
( 7) |11111111|11111111|11111110|0111 fffffe7 [28]
( 8) |11111111|11111111|11111110|1000 fffffe8 [28]
( 9) |11111111|11111111|11101010 ffffea [24]
( 10) |11111111|11111111|11111111|111100 3ffffffc [30]
( 11) |11111111|11111111|11111110|1001 fffffe9 [28]
( 12) |11111111|11111111|11111110|1010 fffffea [28]
( 13) |11111111|11111111|11111111|111101 3ffffffd [30]
( 14) |11111111|11111111|11111110|1011 fffffeb [28]
( 15) |11111111|11111111|11111110|1100 fffffec [28]
( 16) |11111111|11111111|11111110|1101 fffffed [28]
( 17) |11111111|11111111|11111110|1110 fffffee [28]
( 18) |11111111|11111111|11111110|1111 fffffef [28]
( 19) |11111111|11111111|11111111|0000 ffffff0 [28]
( 20) |11111111|11111111|11111111|0001 ffffff1 [28]
( 21) |11111111|11111111|11111111|0010 ffffff2 [28]
( 22) |11111111|11111111|11111111|111110 3ffffffe [30]
( 23) |11111111|11111111|11111111|0011 ffffff3 [28]
( 24) |11111111|11111111|11111111|0100 ffffff4 [28]
( 25) |11111111|11111111|11111111|0101 ffffff5 [28]
( 26) |11111111|11111111|11111111|0110 ffffff6 [28]
( 27) |11111111|11111111|11111111|0111 ffffff7 [28]
( 28) |11111111|11111111|11111111|1000 ffffff8 [28]
( 29) |11111111|11111111|11111111|1001 ffffff9 [28]
( 30) |11111111|11111111|11111111|1010 ffffffa [28]
( 31) |11111111|11111111|11111111|1011 ffffffb [28]
' ' ( 32) |010100 14 [ 6]
'!' ( 33) |11111110|00 3f8 [10]
'"' ( 34) |11111110|01 3f9 [10]
'#' ( 35) |11111111|1010 ffa [12]
'$' ( 36) |11111111|11001 1ff9 [13]
'%' ( 37) |010101 15 [ 6]
'&' ( 38) |11111000 f8 [ 8]
''' ( 39) |11111111|010 7fa [11]
'(' ( 40) |11111110|10 3fa [10]
')' ( 41) |11111110|11 3fb [10]
'*' ( 42) |11111001 f9 [ 8]
'+' ( 43) |11111111|011 7fb [11]
',' ( 44) |11111010 fa [ 8]
'-' ( 45) |010110 16 [ 6]
'.' ( 46) |010111 17 [ 6]
'/' ( 47) |011000 18 [ 6]
'0' ( 48) |00000 0 [ 5]
'1' ( 49) |00001 1 [ 5]
'2' ( 50) |00010 2 [ 5]
'3' ( 51) |011001 19 [ 6]
'4' ( 52) |011010 1a [ 6]
'5' ( 53) |011011 1b [ 6]
'6' ( 54) |011100 1c [ 6]
'7' ( 55) |011101 1d [ 6]
'8' ( 56) |011110 1e [ 6]
'9' ( 57) |011111 1f [ 6]
':' ( 58) |1011100 5c [ 7]
';' ( 59) |11111011 fb [ 8]
'<' ( 60) |11111111|1111100 7ffc [15]
'=' ( 61) |100000 20 [ 6]
'>' ( 62) |11111111|1011 ffb [12]
'?' ( 63) |11111111|00 3fc [10]
'@' ( 64) |11111111|11010 1ffa [13]
'A' ( 65) |100001 21 [ 6]
'B' ( 66) |1011101 5d [ 7]
'C' ( 67) |1011110 5e [ 7]
'D' ( 68) |1011111 5f [ 7]
'E' ( 69) |1100000 60 [ 7]
'F' ( 70) |1100001 61 [ 7]
'G' ( 71) |1100010 62 [ 7]
'H' ( 72) |1100011 63 [ 7]
'I' ( 73) |1100100 64 [ 7]
'J' ( 74) |1100101 65 [ 7]
'K' ( 75) |1100110 66 [ 7]
'L' ( 76) |1100111 67 [ 7]
'M' ( 77) |1101000 68 [ 7]
'N' ( 78) |1101001 69 [ 7]
'O' ( 79) |1101010 6a [ 7]
'P' ( 80) |1101011 6b [ 7]
'Q' ( 81) |1101100 6c [ 7]
'R' ( 82) |1101101 6d [ 7]
'S' ( 83) |1101110 6e [ 7]
'T' ( 84) |1101111 6f [ 7]
'U' ( 85) |1110000 70 [ 7]
'V' ( 86) |1110001 71 [ 7]
'W' ( 87) |1110010 72 [ 7]
'X' ( 88) |11111100 fc [ 8]
'Y' ( 89) |1110011 73 [ 7]
'Z' ( 90) |11111101 fd [ 8]
'[' ( 91) |11111111|11011 1ffb [13]
'\' ( 92) |11111111|11111110|000 7fff0 [19]
']' ( 93) |11111111|11100 1ffc [13]
'^' ( 94) |11111111|111100 3ffc [14]
'_' ( 95) |100010 22 [ 6]
'`' ( 96) |11111111|1111101 7ffd [15]
'a' ( 97) |00011 3 [ 5]
'b' ( 98) |100011 23 [ 6]
'c' ( 99) |00100 4 [ 5]
'd' (100) |100100 24 [ 6]
'e' (101) |00101 5 [ 5]
'f' (102) |100101 25 [ 6]
'g' (103) |100110 26 [ 6]
'h' (104) |100111 27 [ 6]
'i' (105) |00110 6 [ 5]
'j' (106) |1110100 74 [ 7]
'k' (107) |1110101 75 [ 7]
'l' (108) |101000 28 [ 6]
'm' (109) |101001 29 [ 6]
'n' (110) |101010 2a [ 6]
'o' (111) |00111 7 [ 5]
'p' (112) |101011 2b [ 6]
'q' (113) |1110110 76 [ 7]
'r' (114) |101100 2c [ 6]
's' (115) |01000 8 [ 5]
't' (116) |01001 9 [ 5]
'u' (117) |101101 2d [ 6]
'v' (118) |1110111 77 [ 7]
'w' (119) |1111000 78 [ 7]
'x' (120) |1111001 79 [ 7]
'y' (121) |1111010 7a [ 7]
'z' (122) |1111011 7b [ 7]
'{' (123) |11111111|1111110 7ffe [15]
'|' (124) |11111111|100 7fc [11]
'}' (125) |11111111|111101 3ffd [14]
'~' (126) |11111111|11101 1ffd [13]
(127) |11111111|11111111|11111111|1100 ffffffc [28]
(128) |11111111|11111110|0110 fffe6 [20]
(129) |11111111|11111111|010010 3fffd2 [22]
(130) |11111111|11111110|0111 fffe7 [20]
(131) |11111111|11111110|1000 fffe8 [20]
(132) |11111111|11111111|010011 3fffd3 [22]
(133) |11111111|11111111|010100 3fffd4 [22]
(134) |11111111|11111111|010101 3fffd5 [22]
(135) |11111111|11111111|1011001 7fffd9 [23]
(136) |11111111|11111111|010110 3fffd6 [22]
(137) |11111111|11111111|1011010 7fffda [23]
(138) |11111111|11111111|1011011 7fffdb [23]
(139) |11111111|11111111|1011100 7fffdc [23]
(140) |11111111|11111111|1011101 7fffdd [23]
(141) |11111111|11111111|1011110 7fffde [23]
(142) |11111111|11111111|11101011 ffffeb [24]
(143) |11111111|11111111|1011111 7fffdf [23]
(144) |11111111|11111111|11101100 ffffec [24]
(145) |11111111|11111111|11101101 ffffed [24]
(146) |11111111|11111111|010111 3fffd7 [22]
(147) |11111111|11111111|1100000 7fffe0 [23]
(148) |11111111|11111111|11101110 ffffee [24]
(149) |11111111|11111111|1100001 7fffe1 [23]
(150) |11111111|11111111|1100010 7fffe2 [23]
(151) |11111111|11111111|1100011 7fffe3 [23]
(152) |11111111|11111111|1100100 7fffe4 [23]
(153) |11111111|11111110|11100 1fffdc [21]
(154) |11111111|11111111|011000 3fffd8 [22]
(155) |11111111|11111111|1100101 7fffe5 [23]
(156) |11111111|11111111|011001 3fffd9 [22]
(157) |11111111|11111111|1100110 7fffe6 [23]
(158) |11111111|11111111|1100111 7fffe7 [23]
(159) |11111111|11111111|11101111 ffffef [24]
(160) |11111111|11111111|011010 3fffda [22]
(161) |11111111|11111110|11101 1fffdd [21]
(162) |11111111|11111110|1001 fffe9 [20]
(163) |11111111|11111111|011011 3fffdb [22]
(164) |11111111|11111111|011100 3fffdc [22]
(165) |11111111|11111111|1101000 7fffe8 [23]
(166) |11111111|11111111|1101001 7fffe9 [23]
(167) |11111111|11111110|11110 1fffde [21]
(168) |11111111|11111111|1101010 7fffea [23]
(169) |11111111|11111111|011101 3fffdd [22]
(170) |11111111|11111111|011110 3fffde [22]
(171) |11111111|11111111|11110000 fffff0 [24]
(172) |11111111|11111110|11111 1fffdf [21]
(173) |11111111|11111111|011111 3fffdf [22]
(174) |11111111|11111111|1101011 7fffeb [23]
(175) |11111111|11111111|1101100 7fffec [23]
(176) |11111111|11111111|00000 1fffe0 [21]
(177) |11111111|11111111|00001 1fffe1 [21]
(178) |11111111|11111111|100000 3fffe0 [22]
(179) |11111111|11111111|00010 1fffe2 [21]
(180) |11111111|11111111|1101101 7fffed [23]
(181) |11111111|11111111|100001 3fffe1 [22]
(182) |11111111|11111111|1101110 7fffee [23]
(183) |11111111|11111111|1101111 7fffef [23]
(184) |11111111|11111110|1010 fffea [20]
(185) |11111111|11111111|100010 3fffe2 [22]
(186) |11111111|11111111|100011 3fffe3 [22]
(187) |11111111|11111111|100100 3fffe4 [22]
(188) |11111111|11111111|1110000 7ffff0 [23]
(189) |11111111|11111111|100101 3fffe5 [22]
(190) |11111111|11111111|100110 3fffe6 [22]
(191) |11111111|11111111|1110001 7ffff1 [23]
(192) |11111111|11111111|11111000|00 3ffffe0 [26]
(193) |11111111|11111111|11111000|01 3ffffe1 [26]
(194) |11111111|11111110|1011 fffeb [20]
(195) |11111111|11111110|001 7fff1 [19]
(196) |11111111|11111111|100111 3fffe7 [22]
(197) |11111111|11111111|1110010 7ffff2 [23]
(198) |11111111|11111111|101000 3fffe8 [22]
(199) |11111111|11111111|11110110|0 1ffffec [25]
(200) |11111111|11111111|11111000|10 3ffffe2 [26]
(201) |11111111|11111111|11111000|11 3ffffe3 [26]
(202) |11111111|11111111|11111001|00 3ffffe4 [26]
(203) |11111111|11111111|11111011|110 7ffffde [27]
(204) |11111111|11111111|11111011|111 7ffffdf [27]
(205) |11111111|11111111|11111001|01 3ffffe5 [26]
(206) |11111111|11111111|11110001 fffff1 [24]
(207) |11111111|11111111|11110110|1 1ffffed [25]
(208) |11111111|11111110|010 7fff2 [19]
(209) |11111111|11111111|00011 1fffe3 [21]
(210) |11111111|11111111|11111001|10 3ffffe6 [26]
(211) |11111111|11111111|11111100|000 7ffffe0 [27]
(212) |11111111|11111111|11111100|001 7ffffe1 [27]
(213) |11111111|11111111|11111001|11 3ffffe7 [26]
(214) |11111111|11111111|11111100|010 7ffffe2 [27]
(215) |11111111|11111111|11110010 fffff2 [24]
(216) |11111111|11111111|00100 1fffe4 [21]
(217) |11111111|11111111|00101 1fffe5 [21]
(218) |11111111|11111111|11111010|00 3ffffe8 [26]
(219) |11111111|11111111|11111010|01 3ffffe9 [26]
(220) |11111111|11111111|11111111|1101 ffffffd [28]
(221) |11111111|11111111|11111100|011 7ffffe3 [27]
(222) |11111111|11111111|11111100|100 7ffffe4 [27]
(223) |11111111|11111111|11111100|101 7ffffe5 [27]
(224) |11111111|11111110|1100 fffec [20]
(225) |11111111|11111111|11110011 fffff3 [24]
(226) |11111111|11111110|1101 fffed [20]
(227) |11111111|11111111|00110 1fffe6 [21]
(228) |11111111|11111111|101001 3fffe9 [22]
(229) |11111111|11111111|00111 1fffe7 [21]
(230) |11111111|11111111|01000 1fffe8 [21]
(231) |11111111|11111111|1110011 7ffff3 [23]
(232) |11111111|11111111|101010 3fffea [22]
(233) |11111111|11111111|101011 3fffeb [22]
(234) |11111111|11111111|11110111|0 1ffffee [25]
(235) |11111111|11111111|11110111|1 1ffffef [25]
(236) |11111111|11111111|11110100 fffff4 [24]
(237) |11111111|11111111|11110101 fffff5 [24]
(238) |11111111|11111111|11111010|10 3ffffea [26]
(239) |11111111|11111111|1110100 7ffff4 [23]
(240) |11111111|11111111|11111010|11 3ffffeb [26]
(241) |11111111|11111111|11111100|110 7ffffe6 [27]
(242) |11111111|11111111|11111011|00 3ffffec [26]
(243) |11111111|11111111|11111011|01 3ffffed [26]
(244) |11111111|11111111|11111100|111 7ffffe7 [27]
(245) |11111111|11111111|11111101|000 7ffffe8 [27]
(246) |11111111|11111111|11111101|001 7ffffe9 [27]
(247) |11111111|11111111|11111101|010 7ffffea [27]
(248) |11111111|11111111|11111101|011 7ffffeb [27]
(249) |11111111|11111111|11111111|1110 ffffffe [28]
(250) |11111111|11111111|11111101|100 7ffffec [27]
(251) |11111111|11111111|11111101|101 7ffffed [27]
(252) |11111111|11111111|11111101|110 7ffffee [27]
(253) |11111111|11111111|11111101|111 7ffffef [27]
(254) |11111111|11111111|11111110|000 7fffff0 [27]
(255) |11111111|11111111|11111011|10 3ffffee [26]
EOS (256) |11111111|11111111|11111111|111111 3fffffff [30]
"""
class Node:
def __init__(self, term = None):
self.term = term
self.left = None
@@ -20,21 +283,18 @@ class Node:
self.id = None
self.accept = False
def to_bin(s):
res = []
for i in range(0, len(s), 8):
x = s[i:i+8]
x += '0'*(8 - len(x))
a = 0
for j in range(8):
a *= 2
a += ord(x[j]) - ord('0')
res.append(a) #chr(a))
return res
class Context:
nodes = []
def __init__(self):
self.next_id_ = 0
self.root = Node()
def insert(node, sym, bits):
def next_id(self):
id = self.next_id_
self.next_id_ += 1
return id
def _add(node, sym, bits):
if len(bits) == 0:
node.term = sym
return
@@ -47,67 +307,71 @@ def insert(node, sym, bits):
if node.right is None:
node.right = Node()
child = node.right
insert(child, sym, bits[1:])
_add(child, sym, bits[1:])
def traverse(node, bits, syms, start_node, root, depth):
if depth == 4:
if 256 in syms:
syms = []
def huffman_tree_add(ctx, sym, bits):
_add(ctx.root, sym, bits)
def _set_node_id(ctx, node, prefix):
if node.term is not None:
return
if len(prefix) <= 7 and [1] * len(prefix) == prefix:
node.accept = True
node.id = ctx.next_id()
_set_node_id(ctx, node.left, prefix + [0])
_set_node_id(ctx, node.right, prefix + [1])
def huffman_tree_set_node_id(ctx):
_set_node_id(ctx, ctx.root, [])
def _traverse(node, sym, start_node, root, left):
if left == 0:
if sym == 256:
sym = None
node = None
start_node.trans.append((node, bits, syms))
start_node.trans.append((node, sym))
return
if node.term is not None:
node = root
def go(node, bit):
nbits = list(bits)
nbits.append(bit)
nsyms = list(syms)
def go(node):
if node.term is not None:
nsyms.append(node.term)
traverse(node, nbits, nsyms, start_node, root, depth + 1)
assert sym is None
nsym = node.term
else:
nsym = sym
go(node.left, 0)
go(node.right, 1)
_traverse(node, nsym, start_node, root, left - 1)
idseed = 0
go(node.left)
go(node.right)
def dfs_setid(node, prefix):
if node.term is not None:
return
if len(prefix) <= 7 and [1] * len(prefix) == prefix:
node.accept = True
global idseed
node.id = idseed
idseed += 1
dfs_setid(node.left, prefix + [0])
dfs_setid(node.right, prefix + [1])
def dfs(node, root):
def _build_transition_table(ctx, node):
if node is None:
return
traverse(node, [], [], node, root, 0)
dfs(node.left, root)
dfs(node.right, root)
_traverse(node, None, node, ctx.root, 4)
_build_transition_table(ctx, node.left)
_build_transition_table(ctx, node.right)
def huffman_tree_build_transition_table(ctx):
_build_transition_table(ctx, ctx.root)
NGHTTP2_HUFF_ACCEPTED = 1
NGHTTP2_HUFF_SYM = 1 << 1
NGHTTP2_HUFF_FAIL = 1 << 2
def dfs_print(node):
def _print_transition_table(node):
if node.term is not None:
return
print '/* {} */'.format(node.id)
print '{'
for nd, bits, syms in node.trans:
outlen = len(syms)
for nd, sym in node.trans:
flags = 0
if outlen == 0:
if sym is None:
out = 0
else:
assert(outlen == 1)
out = syms[0]
out = sym
flags |= NGHTTP2_HUFF_SYM
if nd is None:
id = 0
@@ -122,52 +386,50 @@ def dfs_print(node):
flags |= NGHTTP2_HUFF_ACCEPTED
print ' {{{}, 0x{:02x}, {}}},'.format(id, flags, out)
print '},'
dfs_print(node.left)
dfs_print(node.right)
_print_transition_table(node.left)
_print_transition_table(node.right)
symbol_tbl = [(None, 0) for i in range(257)]
tables = {}
def huffman_tree_print_transition_table(ctx):
_print_transition_table(ctx.root)
root = Node()
if __name__ == '__main__':
ctx = Context()
symbol_tbl = [(None, 0) for i in range(257)]
for line in sys.stdin:
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(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(4))
assert(len(bits) == nbits)
binpat = to_bin(bits)
assert(len(binpat) == (nbits+7)/8)
symbol_tbl[sym] = (binpat, nbits, m.group(3))
#print "Inserting", sym
insert(root, sym, bits)
for line in StringIO.StringIO(HUFFMAN_CODE_TABLE):
m = re.match(
r'.*\(\s*(\d+)\)\s+([|01]+)\s+(\S+)\s+\[\s*(\d+)\].*', line)
if m:
sym = int(m.group(1))
bits = re.sub(r'\|', '', m.group(2))
code = m.group(3)
nbits = int(m.group(4))
if len(code) > 8:
raise Error('Code is more than 4 bytes long')
assert(len(bits) == nbits)
symbol_tbl[sym] = (nbits, code)
huffman_tree_add(ctx, sym, bits)
dfs_setid(root, [])
dfs(root, root)
huffman_tree_set_node_id(ctx)
huffman_tree_build_transition_table(ctx)
print '''\
print '''\
typedef struct {
uint32_t nbits;
uint32_t code;
} nghttp2_huff_sym;
'''
print '''\
const nghttp2_huff_sym huff_sym_table[] = {'''
for i in range(257):
pat = list(symbol_tbl[i][0])
pat += [0]*(4 - len(pat))
print '''\
const nghttp2_huff_sym huff_sym_table[] = {'''
for i in range(257):
print '''\
{{ {}, 0x{}u }}{}\
'''.format(symbol_tbl[i][1], symbol_tbl[i][2], ',' if i < 256 else '')
print '};'
print ''
'''.format(symbol_tbl[i][0], symbol_tbl[i][1], ',' if i < 256 else '')
print '};'
print ''
print '''\
print '''\
enum {{
NGHTTP2_HUFF_ACCEPTED = {},
NGHTTP2_HUFF_SYM = {},
@@ -175,7 +437,7 @@ enum {{
}} nghttp2_huff_decode_flag;
'''.format(NGHTTP2_HUFF_ACCEPTED, NGHTTP2_HUFF_SYM, NGHTTP2_HUFF_FAIL)
print '''\
print '''\
typedef struct {
uint8_t state;
uint8_t flags;
@@ -183,7 +445,7 @@ typedef struct {
} nghttp2_huff_decode;
'''
print '''\
print '''\
const nghttp2_huff_decode huff_decode_table[][16] = {'''
dfs_print(root)
print '};'
huffman_tree_print_transition_table(ctx)
print '};'

View File

@@ -10,39 +10,17 @@
from __future__ import unicode_literals
import re, sys
def hash(s):
h = 0
for c in s:
h = h * 31 + ord(c)
return h & ((1 << 32) - 1)
entries = []
for line in sys.stdin:
m = re.match(r'(\d+)\s+(\S+)\s+(\S.*)?', line)
val = m.group(3).strip() if m.group(3) else ''
entries.append((hash(m.group(2)), int(m.group(1)), m.group(2), val))
entries.sort()
print '/* Sorted by hash(name) and its table index */'
print 'static nghttp2_hd_static_entry static_table[] = {'
for ent in entries:
print 'MAKE_STATIC_ENT({}, "{}", "{}", {}u, {}u),'\
.format(ent[1] - 1, ent[2], ent[3], ent[0], hash(ent[3]))
print '};'
print ''
print '/* Index to the position in static_table */'
print 'const size_t static_table_index[] = {'
for i in range(len(entries)):
for j, ent in enumerate(entries):
if ent[1] - 1 == i:
sys.stdout.write('{: <2d},'.format(j))
break
if (i + 1) % 16 == 0:
sys.stdout.write('\n')
else:
sys.stdout.write(' ')
entries.append((int(m.group(1)), m.group(2), val))
print 'static nghttp2_hd_entry static_table[] = {'
idx = 0
for i, ent in enumerate(entries):
if entries[idx][1] != ent[1]:
idx = i
print 'MAKE_STATIC_ENT("{}", "{}", {}),'\
.format(ent[1], ent[2], entries[idx][0] - 1)
print '};'

View File

@@ -30,7 +30,8 @@
namespace nghttp2 {
ParserData::ParserData(const std::string &base_uri) : base_uri(base_uri) {}
ParserData::ParserData(const std::string &base_uri)
: base_uri(base_uri), inside_head(0) {}
HtmlParser::HtmlParser(const std::string &base_uri)
: base_uri_(base_uri), parser_ctx_(nullptr), parser_data_(base_uri) {}
@@ -52,13 +53,13 @@ const char *get_attr(const xmlChar **attrs, const char *name) {
} // namespace
namespace {
void add_link(ParserData *parser_data, const char *uri, RequestPriority pri) {
void add_link(ParserData *parser_data, const char *uri, ResourceType res_type) {
auto u = xmlBuildURI(
reinterpret_cast<const xmlChar *>(uri),
reinterpret_cast<const xmlChar *>(parser_data->base_uri.c_str()));
if (u) {
parser_data->links.push_back(
std::make_pair(reinterpret_cast<char *>(u), pri));
std::make_pair(reinterpret_cast<char *>(u), res_type));
free(u);
}
}
@@ -68,6 +69,9 @@ namespace {
void start_element_func(void *user_data, const xmlChar *name,
const xmlChar **attrs) {
auto parser_data = static_cast<ParserData *>(user_data);
if (util::strieq(reinterpret_cast<const char *>(name), "head")) {
++parser_data->inside_head;
}
if (util::strieq(reinterpret_cast<const char *>(name), "link")) {
auto rel_attr = get_attr(attrs, "rel");
auto href_attr = get_attr(attrs, "href");
@@ -75,22 +79,35 @@ void start_element_func(void *user_data, const xmlChar *name,
return;
}
if (util::strieq(rel_attr, "shortcut icon")) {
add_link(parser_data, href_attr, REQ_PRI_LOWEST);
add_link(parser_data, href_attr, REQ_OTHERS);
} else if (util::strieq(rel_attr, "stylesheet")) {
add_link(parser_data, href_attr, REQ_PRI_MEDIUM);
add_link(parser_data, href_attr, REQ_CSS);
}
} else if (util::strieq(reinterpret_cast<const char *>(name), "img")) {
auto src_attr = get_attr(attrs, "src");
if (!src_attr) {
return;
}
add_link(parser_data, src_attr, REQ_PRI_LOWEST);
add_link(parser_data, src_attr, REQ_IMG);
} else if (util::strieq(reinterpret_cast<const char *>(name), "script")) {
auto src_attr = get_attr(attrs, "src");
if (!src_attr) {
return;
}
add_link(parser_data, src_attr, REQ_PRI_LOW);
if (parser_data->inside_head) {
add_link(parser_data, src_attr, REQ_JS);
} else {
add_link(parser_data, src_attr, REQ_UNBLOCK_JS);
}
}
}
} // namespace
namespace {
void end_element_func(void *user_data, const xmlChar *name) {
auto parser_data = static_cast<ParserData *>(user_data);
if (util::strieq(reinterpret_cast<const char *>(name), "head")) {
--parser_data->inside_head;
}
}
} // namespace
@@ -112,7 +129,7 @@ xmlSAXHandler saxHandler = {
nullptr, // startDocumentSAXFunc
nullptr, // endDocumentSAXFunc
&start_element_func, // startElementSAXFunc
nullptr, // endElementSAXFunc
&end_element_func, // endElementSAXFunc
nullptr, // referenceSAXFunc
nullptr, // charactersSAXFunc
nullptr, // ignorableWhitespaceSAXFunc
@@ -160,7 +177,7 @@ int HtmlParser::parse_chunk_internal(const char *chunk, size_t size, int fin) {
}
}
const std::vector<std::pair<std::string, RequestPriority>> &
const std::vector<std::pair<std::string, ResourceType>> &
HtmlParser::get_links() const {
return parser_data_.links;
}

View File

@@ -38,16 +38,19 @@
namespace nghttp2 {
enum RequestPriority {
REQ_PRI_HIGH = 0,
REQ_PRI_MEDIUM = 1,
REQ_PRI_LOW = 2,
REQ_PRI_LOWEST = 3
enum ResourceType {
REQ_CSS = 1,
REQ_JS,
REQ_UNBLOCK_JS,
REQ_IMG,
REQ_OTHERS,
};
struct ParserData {
std::string base_uri;
std::vector<std::pair<std::string, RequestPriority>> links;
std::vector<std::pair<std::string, ResourceType>> links;
// > 0 if we are inside "head" element.
int inside_head;
ParserData(const std::string &base_uri);
};
@@ -58,7 +61,7 @@ public:
HtmlParser(const std::string &base_uri);
~HtmlParser();
int parse_chunk(const char *chunk, size_t size, int fin);
const std::vector<std::pair<std::string, RequestPriority>> &get_links() const;
const std::vector<std::pair<std::string, ResourceType>> &get_links() const;
void clear_links();
private:
@@ -75,14 +78,13 @@ class HtmlParser {
public:
HtmlParser(const std::string &base_uri) {}
int parse_chunk(const char *chunk, size_t size, int fin) { return 0; }
const std::vector<std::pair<std::string, RequestPriority>> &
get_links() const {
const std::vector<std::pair<std::string, ResourceType>> &get_links() const {
return links_;
}
void clear_links() {}
private:
std::vector<std::pair<std::string, RequestPriority>> links_;
std::vector<std::pair<std::string, ResourceType>> links_;
};
#endif // !HAVE_LIBXML2

View File

@@ -25,13 +25,25 @@
#include "HttpServer.h"
#include <sys/stat.h>
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif // HAVE_SYS_SOCKET_H
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif // HAVE_NETDB_H
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif // HAVE_UNISTD_H
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif // HAVE_FCNTL_H
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif // HAVE_NETINET_IN_H
#include <netinet/tcp.h>
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif // HAVE_ARPA_INET_H
#include <cassert>
#include <set>
@@ -57,11 +69,6 @@
namespace nghttp2 {
namespace {
const std::string STATUS_200 = "200";
const std::string STATUS_301 = "301";
const std::string STATUS_304 = "304";
const std::string STATUS_400 = "400";
const std::string STATUS_404 = "404";
const std::string DEFAULT_HTML = "index.html";
const std::string NGHTTPD_SERVER = "nghttpd nghttp2/" NGHTTP2_VERSION;
} // namespace
@@ -94,9 +101,10 @@ template <typename Array> void append_nv(Stream *stream, const Array &nva) {
Config::Config()
: stream_read_timeout(60.), stream_write_timeout(60.),
session_option(nullptr), data_ptr(nullptr), padding(0), num_worker(1),
header_table_size(-1), port(0), verbose(false), daemon(false),
verify_client(false), no_tls(false), error_gzip(false),
early_response(false), hexdump(false) {
max_concurrent_streams(100), header_table_size(-1), port(0),
verbose(false), daemon(false), verify_client(false), no_tls(false),
error_gzip(false), early_response(false), hexdump(false),
echo_upload(false) {
nghttp2_option_new(&session_option);
nghttp2_option_set_recv_client_preface(session_option, 1);
}
@@ -171,9 +179,10 @@ void fill_callback(nghttp2_session_callbacks *callbacks, const Config *config);
class Sessions {
public:
Sessions(struct ev_loop *loop, const Config *config, SSL_CTX *ssl_ctx)
: loop_(loop), config_(config), ssl_ctx_(ssl_ctx), callbacks_(nullptr),
next_session_id_(1), tstamp_cached_(ev_now(loop)),
Sessions(HttpServer *sv, struct ev_loop *loop, const Config *config,
SSL_CTX *ssl_ctx)
: sv_(sv), loop_(loop), config_(config), ssl_ctx_(ssl_ctx),
callbacks_(nullptr), next_session_id_(1), tstamp_cached_(ev_now(loop)),
cached_date_(util::http_date(tstamp_cached_)) {
nghttp2_session_callbacks_new(&callbacks_);
@@ -244,9 +253,37 @@ public:
}
return cached_date_;
}
FileEntry *get_cached_fd(const std::string &path) {
auto i = fd_cache_.find(path);
if (i == std::end(fd_cache_)) {
return nullptr;
}
auto &ent = (*i).second;
++ent.usecount;
return &ent;
}
FileEntry *cache_fd(const std::string &path, const FileEntry &ent) {
auto rv = fd_cache_.emplace(path, ent);
return &(*rv.first).second;
}
void release_fd(const std::string &path) {
auto i = fd_cache_.find(path);
if (i == std::end(fd_cache_)) {
return;
}
auto &ent = (*i).second;
if (--ent.usecount == 0) {
close(ent.fd);
fd_cache_.erase(i);
}
}
const HttpServer *get_server() const { return sv_; }
private:
std::set<Http2Handler *> handlers_;
// cache for file descriptors to read file.
std::map<std::string, FileEntry> fd_cache_;
HttpServer *sv_;
struct ev_loop *loop_;
const Config *config_;
SSL_CTX *ssl_ctx_;
@@ -257,7 +294,8 @@ private:
};
Stream::Stream(Http2Handler *handler, int32_t stream_id)
: handler(handler), body_left(0), stream_id(stream_id), file(-1) {
: handler(handler), file_ent(nullptr), body_length(0), body_offset(0),
stream_id(stream_id), echo_upload(false) {
auto config = handler->get_config();
ev_timer_init(&rtimer, stream_timeout_cb, 0., config->stream_read_timeout);
ev_timer_init(&wtimer, stream_timeout_cb, 0., config->stream_write_timeout);
@@ -270,8 +308,9 @@ Stream::Stream(Http2Handler *handler, int32_t stream_id)
}
Stream::~Stream() {
if (file != -1) {
close(file);
if (file_ent != nullptr) {
auto sessions = handler->get_sessions();
sessions->release_fd(file_ent->path);
}
auto loop = handler->get_loop();
@@ -634,8 +673,10 @@ int Http2Handler::on_write() { return write_(*this); }
int Http2Handler::connection_made() {
int r;
auto config = sessions_->get_config();
r = nghttp2_session_server_new2(&session_, sessions_->get_callbacks(), this,
sessions_->get_config()->session_option);
config->session_option);
if (r != 0) {
return r;
}
@@ -643,11 +684,11 @@ int Http2Handler::connection_made() {
size_t niv = 1;
entry[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
entry[0].value = 100;
entry[0].value = config->max_concurrent_streams;
if (sessions_->get_config()->header_table_size >= 0) {
if (config->header_table_size >= 0) {
entry[niv].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
entry[niv].value = sessions_->get_config()->header_table_size;
entry[niv].value = config->header_table_size;
++niv;
}
r = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, entry.data(), niv);
@@ -835,12 +876,12 @@ ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
auto hd = static_cast<Http2Handler *>(user_data);
auto stream = hd->get_stream(stream_id);
size_t nread = std::min(stream->body_left, static_cast<int64_t>(length));
auto nread = std::min(stream->body_length - stream->body_offset,
static_cast<int64_t>(length));
*data_flags |= NGHTTP2_DATA_FLAG_NO_COPY;
stream->body_left -= nread;
if (nread == 0 || stream->body_left <= 0) {
if (nread == 0 || stream->body_length == stream->body_offset + nread) {
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
auto config = hd->get_config();
@@ -872,59 +913,70 @@ ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
}
namespace {
void prepare_status_response(Stream *stream, Http2Handler *hd,
const std::string &status) {
int pipefd[2];
if (status == STATUS_304 || pipe(pipefd) == -1) {
hd->submit_response(status, stream->stream_id, 0);
return;
}
std::string body;
body.reserve(256);
body = "<html><head><title>";
body += status;
body += "</title></head><body><h1>";
body += status;
body += "</h1><hr><address>";
body += NGHTTPD_SERVER;
body += " at port ";
body += util::utos(hd->get_config()->port);
body += "</address>";
body += "</body></html>";
void prepare_status_response(Stream *stream, Http2Handler *hd, int status) {
auto sessions = hd->get_sessions();
auto status_page = sessions->get_server()->get_status_page(status);
auto file_ent = &status_page->file_ent;
// we don't set stream->file_ent since we don't want to expire it.
stream->body_length = file_ent->length;
nghttp2_data_provider data_prd;
data_prd.source.fd = file_ent->fd;
data_prd.read_callback = file_read_callback;
Headers headers;
if (hd->get_config()->error_gzip) {
gzFile write_fd = gzdopen(pipefd[1], "w");
gzwrite(write_fd, body.c_str(), body.size());
gzclose(write_fd);
headers.emplace_back("content-encoding", "gzip");
} else {
ssize_t rv;
while ((rv = write(pipefd[1], body.c_str(), body.size())) == -1 &&
errno == EINTR)
;
if (rv != static_cast<ssize_t>(body.size())) {
std::cerr << "Could not write all response body: " << rv << std::endl;
}
}
close(pipefd[1]);
stream->file = pipefd[0];
stream->body_left = body.size();
nghttp2_data_provider data_prd;
data_prd.source.fd = pipefd[0];
data_prd.read_callback = file_read_callback;
headers.emplace_back("content-type", "text/html; charset=UTF-8");
hd->submit_response(status, stream->stream_id, headers, &data_prd);
hd->submit_response(status_page->status, stream->stream_id, headers,
&data_prd);
}
} // namespace
namespace {
void prepare_echo_response(Stream *stream, Http2Handler *hd) {
auto length = lseek(stream->file_ent->fd, 0, SEEK_END);
if (length == -1) {
hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR);
return;
}
stream->body_length = length;
if (lseek(stream->file_ent->fd, 0, SEEK_SET) == -1) {
hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR);
return;
}
nghttp2_data_provider data_prd;
data_prd.source.fd = stream->file_ent->fd;
data_prd.read_callback = file_read_callback;
Headers headers;
headers.emplace_back("nghttpd-response", "echo");
headers.emplace_back("content-length", util::utos(length));
hd->submit_response("200", stream->stream_id, headers, &data_prd);
}
} // namespace
namespace {
bool prepare_upload_temp_store(Stream *stream, Http2Handler *hd) {
auto sessions = hd->get_sessions();
char tempfn[] = "/tmp/nghttpd.temp.XXXXXX";
auto fd = mkstemp(tempfn);
if (fd == -1) {
return false;
}
unlink(tempfn);
// Ordinary request never start with "echo:". The length is 0 for
// now. We will update it when we get whole request body.
stream->file_ent = sessions->cache_fd(std::string("echo:") + tempfn,
FileEntry(tempfn, 0, 0, fd));
stream->echo_upload = true;
return true;
}
} // namespace
namespace {
void prepare_redirect_response(Stream *stream, Http2Handler *hd,
const std::string &path,
const std::string &status) {
const std::string &path, int status) {
auto scheme =
http2::get_header(stream->hdidx, http2::HD__SCHEME, stream->headers);
auto authority =
@@ -941,7 +993,10 @@ void prepare_redirect_response(Stream *stream, Http2Handler *hd,
auto headers = Headers{{"location", redirect_url}};
hd->submit_response(status, stream->stream_id, headers, nullptr);
auto sessions = hd->get_sessions();
auto status_page = sessions->get_server()->get_status_page(status);
hd->submit_response(status_page->status, stream->stream_id, headers, nullptr);
}
} // namespace
@@ -973,9 +1028,15 @@ void prepare_response(Stream *stream, Http2Handler *hd,
url = reqpath;
}
url = util::percentDecode(url.begin(), url.end());
auto sessions = hd->get_sessions();
url = util::percentDecode(std::begin(url), std::end(url));
if (!util::check_path(url)) {
prepare_status_response(stream, hd, STATUS_404);
if (stream->file_ent) {
sessions->release_fd(stream->file_ent->path);
stream->file_ent = nullptr;
}
prepare_status_response(stream, hd, 404);
return;
}
auto push_itr = hd->get_config()->push.find(url);
@@ -988,55 +1049,76 @@ void prepare_response(Stream *stream, Http2Handler *hd,
}
}
}
std::string path = hd->get_config()->htdocs + url;
if (path[path.size() - 1] == '/') {
path += DEFAULT_HTML;
}
int file = open(path.c_str(), O_RDONLY | O_BINARY);
if (file == -1) {
prepare_status_response(stream, hd, STATUS_404);
if (stream->echo_upload) {
assert(stream->file_ent);
prepare_echo_response(stream, hd);
return;
}
struct stat buf;
auto file_ent = sessions->get_cached_fd(path);
if (fstat(file, &buf) == -1) {
close(file);
prepare_status_response(stream, hd, STATUS_404);
if (file_ent == nullptr) {
int file = open(path.c_str(), O_RDONLY | O_BINARY);
if (file == -1) {
prepare_status_response(stream, hd, 404);
return;
}
if (buf.st_mode & S_IFDIR) {
close(file);
if (query_pos == std::string::npos) {
reqpath += "/";
} else {
reqpath.insert(query_pos, "/");
return;
}
prepare_redirect_response(stream, hd, reqpath, STATUS_301);
struct stat buf;
if (fstat(file, &buf) == -1) {
close(file);
prepare_status_response(stream, hd, 404);
return;
}
if (buf.st_mode & S_IFDIR) {
close(file);
if (query_pos == std::string::npos) {
reqpath += "/";
} else {
reqpath.insert(query_pos, "/");
}
prepare_redirect_response(stream, hd, reqpath, 301);
return;
}
if (last_mod_found && buf.st_mtime <= last_mod) {
close(file);
prepare_status_response(stream, hd, 304);
return;
}
file_ent = sessions->cache_fd(
path, FileEntry(path, buf.st_size, buf.st_mtime, file));
} else if (last_mod_found && file_ent->mtime <= last_mod) {
sessions->release_fd(file_ent->path);
prepare_status_response(stream, hd, 304);
return;
}
stream->file = file;
stream->body_left = buf.st_size;
stream->file_ent = file_ent;
stream->body_length = file_ent->length;
nghttp2_data_provider data_prd;
data_prd.source.fd = file;
data_prd.source.fd = file_ent->fd;
data_prd.read_callback = file_read_callback;
if (last_mod_found && buf.st_mtime <= last_mod) {
prepare_status_response(stream, hd, STATUS_304);
return;
}
hd->submit_file_response(STATUS_200, stream, buf.st_mtime, buf.st_size,
hd->submit_file_response("200", stream, file_ent->mtime, file_ent->length,
&data_prd);
}
} // namespace
@@ -1108,7 +1190,7 @@ int hd_on_frame_recv_callback(nghttp2_session *session,
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
remove_stream_read_timeout(stream);
if (!hd->get_config()->early_response) {
if (stream->echo_upload || !hd->get_config()->early_response) {
prepare_response(stream, hd);
}
} else {
@@ -1132,14 +1214,22 @@ int hd_on_frame_recv_callback(nghttp2_session *session,
hd->submit_non_final_response("100", frame->hd.stream_id);
}
if (hd->get_config()->early_response) {
auto &method = http2::get_header(stream->hdidx, http2::HD__METHOD,
stream->headers)->value;
if (hd->get_config()->echo_upload &&
(method == "POST" || method == "PUT")) {
if (!prepare_upload_temp_store(stream, hd)) {
hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR);
return 0;
}
} else if (hd->get_config()->early_response) {
prepare_response(stream, hd);
}
}
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
remove_stream_read_timeout(stream);
if (!hd->get_config()->early_response) {
if (stream->echo_upload || !hd->get_config()->early_response) {
prepare_response(stream, hd);
}
} else {
@@ -1220,6 +1310,7 @@ int send_data_callback(nghttp2_session *session, nghttp2_frame *frame,
auto hd = static_cast<Http2Handler *>(user_data);
auto wb = hd->get_wb();
auto padlen = frame->data.padlen;
auto stream = hd->get_stream(frame->hd.stream_id);
if (wb->wleft() < 9 + length + padlen) {
return NGHTTP2_ERR_WOULDBLOCK;
@@ -1237,17 +1328,18 @@ int send_data_callback(nghttp2_session *session, nghttp2_frame *frame,
while (length) {
ssize_t nread;
while ((nread = read(fd, p, length)) == -1 && errno == EINTR)
while ((nread = pread(fd, p, length, stream->body_offset)) == -1 &&
errno == EINTR)
;
if (nread == -1) {
auto stream = hd->get_stream(frame->hd.stream_id);
remove_stream_read_timeout(stream);
remove_stream_write_timeout(stream);
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
stream->body_offset += nread;
length -= nread;
p += nread;
}
@@ -1283,6 +1375,21 @@ int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
return 0;
}
if (stream->echo_upload) {
assert(stream->file_ent);
while (len) {
ssize_t n;
while ((n = write(stream->file_ent->fd, data, len)) == -1 &&
errno == EINTR)
;
if (n == -1) {
hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR);
return 0;
}
len -= n;
data += n;
}
}
// TODO Handle POST
add_stream_read_timeout(stream);
@@ -1380,7 +1487,7 @@ void run_worker(Worker *worker) {
class AcceptHandler {
public:
AcceptHandler(Sessions *sessions, const Config *config)
AcceptHandler(HttpServer *sv, Sessions *sessions, const Config *config)
: sessions_(sessions), config_(config), next_worker_(0) {
if (config_->num_worker == 1) {
return;
@@ -1392,7 +1499,7 @@ public:
auto worker = make_unique<Worker>();
auto loop = ev_loop_new(0);
worker->sessions =
make_unique<Sessions>(loop, config_, sessions_->get_ssl_ctx());
make_unique<Sessions>(sv, loop, config_, sessions_->get_ssl_ctx());
ev_async_init(&worker->w, worker_acceptcb);
worker->w.data = worker.get();
ev_async_start(loop, &worker->w);
@@ -1476,7 +1583,61 @@ void acceptcb(struct ev_loop *loop, ev_io *w, int revents) {
}
} // namespace
HttpServer::HttpServer(const Config *config) : config_(config) {}
namespace {
FileEntry make_status_body(int status, uint16_t port) {
std::string body;
body = "<html><head><title>";
body += http2::get_status_string(status);
body += "</title></head><body><h1>";
body += http2::get_status_string(status);
body += "</h1><hr><address>";
body += NGHTTPD_SERVER;
body += " at port ";
body += util::utos(port);
body += "</address>";
body += "</body></html>";
char tempfn[] = "/tmp/nghttpd.temp.XXXXXX";
int fd = mkstemp(tempfn);
if (fd == -1) {
auto error = errno;
std::cerr << "Could not open status response body file: errno=" << error;
assert(0);
}
unlink(tempfn);
ssize_t nwrite;
while ((nwrite = write(fd, body.c_str(), body.size())) == -1 &&
errno == EINTR)
;
if (nwrite == -1) {
auto error = errno;
std::cerr << "Could not write status response body into file: errno="
<< error;
assert(0);
}
return FileEntry(util::utos(status), nwrite, 0, fd);
}
} // namespace
// index into HttpServer::status_pages_
enum {
IDX_200,
IDX_301,
IDX_304,
IDX_400,
IDX_404,
};
HttpServer::HttpServer(const Config *config) : config_(config) {
status_pages_ = std::vector<StatusPage>{
{"200", make_status_body(200, config_->port)},
{"301", make_status_body(301, config_->port)},
{"304", make_status_body(304, config_->port)},
{"400", make_status_body(400, config_->port)},
{"404", make_status_body(404, config_->port)},
};
}
namespace {
int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len,
@@ -1497,14 +1658,14 @@ int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) {
} // namespace
namespace {
int start_listen(struct ev_loop *loop, Sessions *sessions,
int start_listen(HttpServer *sv, struct ev_loop *loop, Sessions *sessions,
const Config *config) {
addrinfo hints;
int r;
bool ok = false;
const char *addr = nullptr;
auto acceptor = std::make_shared<AcceptHandler>(sessions, config);
auto acceptor = std::make_shared<AcceptHandler>(sv, sessions, config);
auto service = util::utos(config->port);
memset(&hints, 0, sizeof(addrinfo));
@@ -1699,8 +1860,8 @@ int HttpServer::run() {
auto loop = EV_DEFAULT;
Sessions sessions(loop, config_, ssl_ctx);
if (start_listen(loop, &sessions, config_) != 0) {
Sessions sessions(this, loop, config_, ssl_ctx);
if (start_listen(this, loop, &sessions, config_) != 0) {
std::cerr << "Could not listen" << std::endl;
return -1;
}
@@ -1711,4 +1872,22 @@ int HttpServer::run() {
const Config *HttpServer::get_config() const { return config_; }
const StatusPage *HttpServer::get_status_page(int status) const {
switch (status) {
case 200:
return &status_pages_[IDX_200];
case 301:
return &status_pages_[IDX_301];
case 304:
return &status_pages_[IDX_304];
case 400:
return &status_pages_[IDX_400];
case 404:
return &status_pages_[IDX_404];
default:
assert(0);
}
return nullptr;
}
} // namespace nghttp2

View File

@@ -27,9 +27,9 @@
#include "nghttp2_config.h"
#include <stdint.h>
#include <sys/types.h>
#include <cinttypes>
#include <cstdlib>
#include <string>
@@ -63,6 +63,7 @@ struct Config {
void *data_ptr;
size_t padding;
size_t num_worker;
size_t max_concurrent_streams;
ssize_t header_table_size;
uint16_t port;
bool verbose;
@@ -72,21 +73,36 @@ struct Config {
bool error_gzip;
bool early_response;
bool hexdump;
bool echo_upload;
Config();
~Config();
};
class Http2Handler;
struct FileEntry {
FileEntry(std::string path, int64_t length, int64_t mtime, int fd)
: path(std::move(path)), length(length), mtime(mtime), dlprev(nullptr),
dlnext(nullptr), fd(fd), usecount(1) {}
std::string path;
int64_t length;
int64_t mtime;
FileEntry *dlprev, *dlnext;
int fd;
int usecount;
};
struct Stream {
Headers headers;
Http2Handler *handler;
FileEntry *file_ent;
ev_timer rtimer;
ev_timer wtimer;
int64_t body_left;
int64_t body_length;
int64_t body_offset;
int32_t stream_id;
int file;
http2::HeaderIndex hdidx;
bool echo_upload;
Stream(Http2Handler *handler, int32_t stream_id);
~Stream();
};
@@ -160,14 +176,21 @@ private:
int fd_;
};
struct StatusPage {
std::string status;
FileEntry file_ent;
};
class HttpServer {
public:
HttpServer(const Config *config);
int listen();
int run();
const Config *get_config() const;
const StatusPage *get_status_page(int status) const;
private:
std::vector<StatusPage> status_pages_;
const Config *config_;
};

View File

@@ -23,11 +23,21 @@
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <sys/types.h>
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif // HAVE_SYS_SOCKET_H
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif // HAVE_NETDB_H
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif // HAVE_UNISTD_H
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif // HAVE_FCNTL_H
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif // HAVE_NETINET_IN_H
#include <netinet/tcp.h>
#include <poll.h>

View File

@@ -27,9 +27,11 @@
#include "nghttp2_config.h"
#include <stdint.h>
#include <cinttypes>
#include <cstdlib>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif // HAVE_SYS_TIME_H
#include <poll.h>
#include <map>

View File

@@ -89,10 +89,7 @@ void session_impl::connected(tcp::resolver::iterator endpoint_it) {
}
void session_impl::not_connected(const boost::system::error_code &ec) {
auto &error_cb = on_error();
if (error_cb) {
error_cb(ec);
}
call_error_cb(ec);
}
void session_impl::on_connect(connect_cb cb) { connect_cb_ = std::move(cb); }
@@ -103,6 +100,14 @@ const connect_cb &session_impl::on_connect() const { return connect_cb_; }
const error_cb &session_impl::on_error() const { return error_cb_; }
void session_impl::call_error_cb(const boost::system::error_code &ec) {
auto &error_cb = on_error();
if (!error_cb) {
return;
}
error_cb(ec);
}
namespace {
int on_begin_headers_callback(nghttp2_session *session,
const nghttp2_frame *frame, void *user_data) {
@@ -308,10 +313,7 @@ bool session_impl::setup_session() {
auto rv = nghttp2_session_client_new(&session_, callbacks, this);
if (rv != 0) {
auto &error_cb = on_error();
if (error_cb) {
error_cb(make_error_code(static_cast<nghttp2_error>(rv)));
}
call_error_cb(make_error_code(static_cast<nghttp2_error>(rv)));
return false;
}
@@ -528,6 +530,7 @@ void session_impl::do_read() {
std::size_t bytes_transferred) {
if (ec) {
if (ec.value() == boost::asio::error::operation_aborted) {
call_error_cb(ec);
shutdown_socket();
}
return;
@@ -540,6 +543,8 @@ void session_impl::do_read() {
nghttp2_session_mem_recv(session_, rb_.data(), bytes_transferred);
if (rv != static_cast<ssize_t>(bytes_transferred)) {
call_error_cb(make_error_code(
static_cast<nghttp2_error>(rv < 0 ? rv : NGHTTP2_ERR_PROTO)));
shutdown_socket();
return;
}
@@ -577,6 +582,7 @@ void session_impl::do_write() {
const uint8_t *data;
auto n = nghttp2_session_mem_send(session_, &data);
if (n < 0) {
call_error_cb(make_error_code(static_cast<nghttp2_error>(n)));
shutdown_socket();
return;
}
@@ -606,6 +612,8 @@ void session_impl::do_write() {
write_socket([this](const boost::system::error_code &ec, std::size_t n) {
if (ec) {
call_error_cb(ec);
shutdown_socket();
return;
}

View File

@@ -97,6 +97,7 @@ protected:
private:
bool should_stop() const;
bool setup_session();
void call_error_cb(const boost::system::error_code &ec);
boost::asio::io_service &io_service_;
tcp::resolver resolver_;

View File

@@ -35,8 +35,6 @@
//
#include "asio_io_service_pool.h"
#include <future>
namespace nghttp2 {
namespace asio_http2 {
@@ -56,19 +54,23 @@ io_service_pool::io_service_pool(std::size_t pool_size) : next_io_service_(0) {
}
}
void io_service_pool::run() {
void io_service_pool::run(bool asynchronous) {
// Create a pool of threads to run all of the io_services.
auto futs = std::vector<std::future<std::size_t>>();
for (std::size_t i = 0; i < io_services_.size(); ++i) {
futs.push_back(std::async(std::launch::async,
(size_t (boost::asio::io_service::*)(void)) &
boost::asio::io_service::run,
io_services_[i]));
futures_.push_back(std::async(std::launch::async,
(size_t (boost::asio::io_service::*)(void)) &
boost::asio::io_service::run,
io_services_[i]));
}
if (!asynchronous) {
join();
}
}
void io_service_pool::join() {
// Wait for all threads in the pool to exit.
for (auto &fut : futs) {
for (auto &fut : futures_) {
fut.get();
}
}

View File

@@ -41,6 +41,7 @@
#include <vector>
#include <memory>
#include <future>
#include <boost/noncopyable.hpp>
#include <boost/thread.hpp>
@@ -58,11 +59,14 @@ public:
explicit io_service_pool(std::size_t pool_size);
/// Run all io_service objects in the pool.
void run();
void run(bool asynchronous = false);
/// Stop all io_service objects in the pool.
void stop();
/// Join on all io_service objects in the pool.
void join();
/// Get an io_service to use.
boost::asio::io_service &get_io_service();
@@ -75,6 +79,9 @@ private:
/// The next io_service to use for a connection.
std::size_t next_io_service_;
/// Futures to all the io_service objects
std::vector<std::future<std::size_t>> futures_;
};
} // namespace asio_http2

View File

@@ -50,7 +50,7 @@ boost::system::error_code
server::listen_and_serve(boost::system::error_code &ec,
boost::asio::ssl::context *tls_context,
const std::string &address, const std::string &port,
int backlog, serve_mux &mux) {
int backlog, serve_mux &mux, bool asynchronous) {
ec.clear();
if (bind_and_listen(ec, address, port, backlog)) {
@@ -65,7 +65,7 @@ server::listen_and_serve(boost::system::error_code &ec,
}
}
io_service_pool_.run();
io_service_pool_.run(asynchronous);
return ec;
}
@@ -156,6 +156,10 @@ void server::start_accept(tcp::acceptor &acceptor, serve_mux &mux) {
});
}
void server::stop() { io_service_pool_.stop(); }
void server::join() { io_service_pool_.join(); }
} // namespace server
} // namespace asio_http2
} // namespace nghttp2

View File

@@ -69,7 +69,9 @@ public:
listen_and_serve(boost::system::error_code &ec,
boost::asio::ssl::context *tls_context,
const std::string &address, const std::string &port,
int backlog, serve_mux &mux);
int backlog, serve_mux &mux, bool asynchronous = false);
void join();
void stop();
private:
/// Initiate an asynchronous accept operation.

View File

@@ -54,15 +54,15 @@ http2 &http2::operator=(http2 &&other) noexcept {
boost::system::error_code http2::listen_and_serve(boost::system::error_code &ec,
const std::string &address,
const std::string &port) {
return impl_->listen_and_serve(ec, nullptr, address, port);
const std::string &port,
bool asynchronous) {
return impl_->listen_and_serve(ec, nullptr, address, port, asynchronous);
}
boost::system::error_code
http2::listen_and_serve(boost::system::error_code &ec,
boost::asio::ssl::context &tls_context,
const std::string &address, const std::string &port) {
return impl_->listen_and_serve(ec, &tls_context, address, port);
boost::system::error_code http2::listen_and_serve(
boost::system::error_code &ec, boost::asio::ssl::context &tls_context,
const std::string &address, const std::string &port, bool asynchronous) {
return impl_->listen_and_serve(ec, &tls_context, address, port, asynchronous);
}
void http2::num_threads(size_t num_threads) { impl_->num_threads(num_threads); }
@@ -73,6 +73,10 @@ bool http2::handle(std::string pattern, request_cb cb) {
return impl_->handle(std::move(pattern), std::move(cb));
}
void http2::stop() { impl_->stop(); }
void http2::join() { return impl_->join(); }
} // namespace server
} // namespace asio_http2

View File

@@ -41,9 +41,10 @@ http2_impl::http2_impl() : num_threads_(1), backlog_(-1) {}
boost::system::error_code http2_impl::listen_and_serve(
boost::system::error_code &ec, boost::asio::ssl::context *tls_context,
const std::string &address, const std::string &port) {
return server(num_threads_)
.listen_and_serve(ec, tls_context, address, port, backlog_, mux_);
const std::string &address, const std::string &port, bool asynchronous) {
server_.reset(new server(num_threads_));
return server_->listen_and_serve(ec, tls_context, address, port, backlog_,
mux_, asynchronous);
}
void http2_impl::num_threads(size_t num_threads) { num_threads_ = num_threads; }
@@ -54,6 +55,10 @@ bool http2_impl::handle(std::string pattern, request_cb cb) {
return mux_.handle(std::move(pattern), std::move(cb));
}
void http2_impl::stop() { return server_->stop(); }
void http2_impl::join() { return server_->join(); }
} // namespace server
} // namespace asio_http2

View File

@@ -42,13 +42,14 @@ class server;
class http2_impl {
public:
http2_impl();
boost::system::error_code
listen_and_serve(boost::system::error_code &ec,
boost::asio::ssl::context *tls_context,
const std::string &address, const std::string &port);
boost::system::error_code listen_and_serve(
boost::system::error_code &ec, boost::asio::ssl::context *tls_context,
const std::string &address, const std::string &port, bool asynchronous);
void num_threads(size_t num_threads);
void backlog(int backlog);
bool handle(std::string pattern, request_cb cb);
void stop();
void join();
private:
std::unique_ptr<server> server_;

View File

@@ -25,6 +25,10 @@
#ifndef BUFFER_TEST_H
#define BUFFER_TEST_H
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif // HAVE_CONFIG_H
namespace nghttp2 {
void test_buffer_write(void);

View File

@@ -57,6 +57,9 @@ json_t *dump_header(const uint8_t *name, size_t namelen, const uint8_t *value,
size_t valuelen) {
json_t *nv_pair = json_object();
char *cname = malloc(namelen + 1);
if (cname == NULL) {
return NULL;
}
memcpy(cname, name, namelen);
cname[namelen] = '\0';
json_object_set_new(nv_pair, cname, json_pack("s#", value, valuelen));

View File

@@ -26,7 +26,9 @@
#include <config.h>
#endif // HAVE_CONFIG_H
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif // HAVE_UNISTD_H
#include <getopt.h>
#include <cstdio>

View File

@@ -26,10 +26,14 @@
#include <getopt.h>
#include <signal.h>
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif // HAVE_NETINET_IN_H
#include <netinet/tcp.h>
#include <sys/stat.h>
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif // HAVE_FCNTL_H
#include <cstdio>
#include <cassert>
@@ -69,7 +73,7 @@ namespace h2load {
Config::Config()
: data_length(-1), addrs(nullptr), nreqs(1), nclients(1), nthreads(1),
max_concurrent_streams(-1), window_bits(16), connection_window_bits(16),
max_concurrent_streams(-1), window_bits(30), connection_window_bits(30),
no_tls_proto(PROTO_HTTP2), data_fd(-1), port(0), default_port(0),
verbose(false) {}
@@ -150,8 +154,8 @@ void readcb(struct ev_loop *loop, ev_io *w, int revents) {
Client::Client(Worker *worker, size_t req_todo)
: worker(worker), ssl(nullptr), next_addr(config.addrs), reqidx(0),
state(CLIENT_IDLE), req_todo(req_todo), req_started(0), req_done(0),
fd(-1) {
state(CLIENT_IDLE), first_byte_received(false), req_todo(req_todo),
req_started(0), req_done(0), fd(-1) {
ev_io_init(&wev, writecb, 0, EV_WRITE);
ev_io_init(&rev, readcb, 0, EV_READ);
@@ -165,6 +169,8 @@ int Client::do_read() { return readfn(*this); }
int Client::do_write() { return writefn(*this); }
int Client::connect() {
record_start_time(&worker->stats);
while (next_addr) {
auto addr = next_addr;
next_addr = next_addr->ai_next;
@@ -469,6 +475,8 @@ int Client::connection_made() {
session->on_connect();
record_connect_time(&worker->stats);
auto nreq =
std::min(req_todo - req_started, (size_t)config.max_concurrent_streams);
@@ -519,6 +527,11 @@ int Client::read_clear() {
if (on_read(buf, nread) != 0) {
return -1;
}
if (!first_byte_received) {
first_byte_received = true;
record_ttfb(&worker->stats);
}
}
return 0;
@@ -641,6 +654,11 @@ int Client::read_tls() {
if (on_read(buf, rv) != 0) {
return -1;
}
if (!first_byte_received) {
first_byte_received = true;
record_ttfb(&worker->stats);
}
}
}
@@ -691,6 +709,18 @@ void Client::record_request_time(RequestStat *req_stat) {
req_stat->request_time = std::chrono::steady_clock::now();
}
void Client::record_start_time(Stats *stat) {
stat->start_times.push_back(std::chrono::steady_clock::now());
}
void Client::record_connect_time(Stats *stat) {
stat->connect_times.push_back(std::chrono::steady_clock::now());
}
void Client::record_ttfb(Stats *stat) {
stat->ttfbs.push_back(std::chrono::steady_clock::now());
}
void Client::signal_write() { ev_io_start(worker->loop, &wev); }
Worker::Worker(uint32_t id, SSL_CTX *ssl_ctx, size_t req_todo, size_t nclients,
@@ -731,70 +761,99 @@ void Worker::run() {
}
namespace {
double within_sd(const std::vector<std::unique_ptr<Worker>> &workers,
const std::chrono::microseconds &mean,
const std::chrono::microseconds &sd, size_t n) {
auto upper = mean.count() + sd.count();
auto lower = mean.count() - sd.count();
size_t m = 0;
for (const auto &w : workers) {
for (const auto &req_stat : w->stats.req_stats) {
if (!req_stat.completed) {
continue;
}
auto t = std::chrono::duration_cast<std::chrono::microseconds>(
req_stat.stream_close_time - req_stat.request_time);
if (lower <= t.count() && t.count() <= upper) {
++m;
}
}
// Returns percentage of number of samples within mean +/- sd.
template <typename Duration>
double within_sd(const std::vector<Duration> &samples, const Duration &mean,
const Duration &sd) {
if (samples.size() == 0) {
return 0.0;
}
return (m / static_cast<double>(n)) * 100;
auto lower = mean - sd;
auto upper = mean + sd;
auto m = std::count_if(
std::begin(samples), std::end(samples),
[&lower, &upper](const Duration &t) { return lower <= t && t <= upper; });
return (m / static_cast<double>(samples.size())) * 100;
}
} // namespace
namespace {
// Computes statistics using |samples|. The min, max, mean, sd, and
// percentage of number of samples within mean +/- sd are computed.
template <typename Duration>
TimeStat<Duration> compute_time_stat(const std::vector<Duration> &samples) {
if (samples.size() == 0) {
return {Duration::zero(), Duration::zero(), Duration::zero(),
Duration::zero(), 0.0};
}
// standard deviation calculated using Rapid calculation method:
// http://en.wikipedia.org/wiki/Standard_deviation#Rapid_calculation_methods
double a = 0, q = 0;
size_t n = 0;
int64_t sum = 0;
auto res = TimeStat<Duration>{Duration::max(), Duration::min()};
for (const auto &t : samples) {
++n;
res.min = std::min(res.min, t);
res.max = std::max(res.max, t);
sum += t.count();
auto na = a + (t.count() - a) / n;
q += (t.count() - a) * (t.count() - na);
a = na;
}
res.mean = Duration(sum / n);
res.sd = Duration(static_cast<typename Duration::rep>(sqrt(q / n)));
res.within_sd = within_sd(samples, res.mean, res.sd);
return res;
}
} // namespace
namespace {
TimeStats
process_time_stats(const std::vector<std::unique_ptr<Worker>> &workers) {
auto ts = TimeStats();
int64_t sum = 0;
size_t n = 0;
size_t nrequest_times = 0, nttfb_times = 0;
for (const auto &w : workers) {
nrequest_times += w->stats.req_stats.size();
nttfb_times += w->stats.ttfbs.size();
}
ts.time_min = std::chrono::microseconds::max();
ts.time_max = std::chrono::microseconds::min();
ts.within_sd = 0.;
std::vector<std::chrono::microseconds> request_times;
request_times.reserve(nrequest_times);
std::vector<std::chrono::microseconds> connect_times, ttfb_times;
connect_times.reserve(nttfb_times);
ttfb_times.reserve(nttfb_times);
// standard deviation calculated using Rapid calculation method:
// http://en.wikipedia.org/wiki/Standard_deviation#Rapid_calculation_methods
double a = 0, q = 0;
for (const auto &w : workers) {
for (const auto &req_stat : w->stats.req_stats) {
if (!req_stat.completed) {
continue;
}
++n;
auto t = std::chrono::duration_cast<std::chrono::microseconds>(
req_stat.stream_close_time - req_stat.request_time);
ts.time_min = std::min(ts.time_min, t);
ts.time_max = std::max(ts.time_max, t);
sum += t.count();
request_times.push_back(
std::chrono::duration_cast<std::chrono::microseconds>(
req_stat.stream_close_time - req_stat.request_time));
}
auto na = a + (t.count() - a) / n;
q = q + (t.count() - a) * (t.count() - na);
a = na;
const auto &stat = w->stats;
// rule out cases where we started but didn't connect or get the
// first byte (errors). We will get connect event before FFTB.
assert(stat.start_times.size() >= stat.ttfbs.size());
assert(stat.connect_times.size() >= stat.ttfbs.size());
for (size_t i = 0; i < stat.ttfbs.size(); ++i) {
connect_times.push_back(
std::chrono::duration_cast<std::chrono::microseconds>(
stat.connect_times[i] - stat.start_times[i]));
ttfb_times.push_back(
std::chrono::duration_cast<std::chrono::microseconds>(
stat.ttfbs[i] - stat.start_times[i]));
}
}
if (n == 0) {
ts.time_max = ts.time_min = std::chrono::microseconds::zero();
return ts;
}
ts.time_mean = std::chrono::microseconds(sum / n);
ts.time_sd = std::chrono::microseconds(
static_cast<std::chrono::microseconds::rep>(sqrt(q / n)));
ts.within_sd = within_sd(workers, ts.time_mean, ts.time_sd, n);
return ts;
return {compute_time_stat(request_times), compute_time_stat(connect_times),
compute_time_stat(ttfb_times)};
}
} // namespace
@@ -959,7 +1018,7 @@ Options:
Number of native threads.
Default: )" << config.nthreads << R"(
-i, --input-file=<FILE>
Path of a file with multiple URIs are seperated by EOLs.
Path of a file with multiple URIs are separated by EOLs.
This option will disable URIs getting from command-line.
If '-' is given as <FILE>, URIs will be read from stdin.
URIs are used in this order for each client. All URIs
@@ -974,11 +1033,13 @@ Options:
-w, --window-bits=<N>
Sets the stream level initial window size to (2**<N>)-1.
For SPDY, 2**<N> is used instead.
Default: )" << config.window_bits << R"(
-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.
Default: )" << config.connection_window_bits << R"(
-H, --header=<HEADER>
Add/Override a header to the requests.
-p, --no-tls-proto=<PROTOID>
@@ -1406,7 +1467,7 @@ int main(int argc, char **argv) {
}
}
auto time_stats = process_time_stats(workers);
auto ts = process_time_stats(workers);
// Requests which have not been issued due to connection errors, are
// counted towards req_failed and req_error.
@@ -1439,14 +1500,23 @@ status codes: )" << stats.status[2] << " 2xx, " << stats.status[3] << " 3xx, "
traffic: )" << stats.bytes_total << " bytes total, " << stats.bytes_head
<< " bytes headers, " << stats.bytes_body << R"( bytes data
min max mean sd +/- sd
time for request: )" << std::setw(10)
<< util::format_duration(time_stats.time_min) << " "
<< std::setw(10) << util::format_duration(time_stats.time_max)
<< " " << std::setw(10)
<< util::format_duration(time_stats.time_mean) << " "
<< std::setw(10) << util::format_duration(time_stats.time_sd)
<< std::setw(9) << util::dtos(time_stats.within_sd) << "%"
<< std::endl;
time for request: )" << std::setw(10) << util::format_duration(ts.request.min)
<< " " << std::setw(10) << util::format_duration(ts.request.max)
<< " " << std::setw(10) << util::format_duration(ts.request.mean)
<< " " << std::setw(10) << util::format_duration(ts.request.sd)
<< std::setw(9) << util::dtos(ts.request.within_sd) << "%"
<< "\ntime for connect: " << std::setw(10)
<< util::format_duration(ts.connect.min) << " " << std::setw(10)
<< util::format_duration(ts.connect.max) << " " << std::setw(10)
<< util::format_duration(ts.connect.mean) << " " << std::setw(10)
<< util::format_duration(ts.connect.sd) << std::setw(9)
<< util::dtos(ts.connect.within_sd) << "%"
<< "\ntime to 1st byte: " << std::setw(10)
<< util::format_duration(ts.ttfb.min) << " " << std::setw(10)
<< util::format_duration(ts.ttfb.max) << " " << std::setw(10)
<< util::format_duration(ts.ttfb.mean) << " " << std::setw(10)
<< util::format_duration(ts.ttfb.sd) << std::setw(9)
<< util::dtos(ts.ttfb.within_sd) << "%" << std::endl;
SSL_CTX_free(ssl_ctx);

View File

@@ -28,8 +28,12 @@
#include "nghttp2_config.h"
#include <sys/types.h>
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif // HAVE_SYS_SOCKET_H
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif // HAVE_NETDB_H
#include <vector>
#include <string>
@@ -94,13 +98,24 @@ struct RequestStat {
bool completed;
};
struct TimeStats {
// time for request: max, min, mean and sd (standard deviation)
std::chrono::microseconds time_max, time_min, time_mean, time_sd;
// percentage of number of requests inside mean -/+ sd
template <typename Duration> struct TimeStat {
// min, max, mean and sd (standard deviation)
Duration min, max, mean, sd;
// percentage of samples inside mean -/+ sd
double within_sd;
};
struct TimeStats {
// time for request
TimeStat<std::chrono::microseconds> request;
// time for connect
TimeStat<std::chrono::microseconds> connect;
// time to first byte (TTFB)
TimeStat<std::chrono::microseconds> ttfb;
};
enum TimeStatType { STAT_REQUEST, STAT_CONNECT, STAT_FIRST_BYTE };
struct Stats {
Stats(size_t req_todo);
// The total number of requests
@@ -132,6 +147,12 @@ struct Stats {
std::array<size_t, 6> status;
// The statistics per request
std::vector<RequestStat> req_stats;
// time connect starts
std::vector<std::chrono::steady_clock::time_point> start_times;
// time to connect
std::vector<std::chrono::steady_clock::time_point> connect_times;
// time to first byte (TTFB)
std::vector<std::chrono::steady_clock::time_point> ttfbs;
};
enum ClientState { CLIENT_IDLE, CLIENT_CONNECTED };
@@ -171,6 +192,7 @@ struct Client {
addrinfo *next_addr;
size_t reqidx;
ClientState state;
bool first_byte_received;
// The number of requests this client has to issue.
size_t req_todo;
// The number of requests this client has issued so far.
@@ -215,6 +237,9 @@ struct Client {
void on_stream_close(int32_t stream_id, bool success, RequestStat *req_stat);
void record_request_time(RequestStat *req_stat);
void record_start_time(Stats *stat);
void record_connect_time(Stats *stat);
void record_ttfb(Stats *stat);
void signal_write();
};

View File

@@ -28,7 +28,8 @@
#include "nghttp2_config.h"
#include <sys/types.h>
#include <stdint.h>
#include <cinttypes>
#include "h2load.h"

View File

@@ -25,6 +25,7 @@
#include "h2load_spdy_session.h"
#include <cassert>
#include <cerrno>
#include "h2load.h"
#include "util.h"

View File

@@ -718,6 +718,30 @@ InputIt skip_to_right_dquote(InputIt first, InputIt last) {
}
} // namespace
namespace {
// Returns true if link-param does not match pattern |pat| of length
// |patlen| or it has empty value (""). |pat| should be parmname
// followed by "=".
bool check_link_param_empty(const char *first, const char *last,
const char *pat, size_t patlen) {
if (first + patlen <= last) {
if (std::equal(pat, pat + patlen, first, util::CaseCmp())) {
// we only accept URI if pat is followd by "" (e.g.,
// loadpolicy="") here.
if (first + patlen + 2 <= last) {
if (*(first + patlen) != '"' || *(first + patlen + 1) != '"') {
return false;
}
} else {
// here we got invalid production (anchor=") or anchor=?
return false;
}
}
}
return true;
}
} // namespace
namespace {
std::pair<LinkHeader, const char *>
parse_next_link_header_once(const char *first, const char *last) {
@@ -755,11 +779,11 @@ parse_next_link_header_once(const char *first, const char *last) {
// we expect link-param
// rel can take several relations using quoted form.
static const char PLP[] = "rel=\"";
static const size_t PLPLEN = sizeof(PLP) - 1;
static constexpr char PLP[] = "rel=\"";
static constexpr size_t PLPLEN = sizeof(PLP) - 1;
static const char PLT[] = "preload";
static const size_t PLTLEN = sizeof(PLT) - 1;
static constexpr char PLT[] = "preload";
static constexpr size_t PLTLEN = sizeof(PLT) - 1;
if (first + PLPLEN < last && *(first + PLPLEN - 1) == '"' &&
std::equal(PLP, PLP + PLPLEN, first, util::CaseCmp())) {
// we have to search preload in whitespace separated list:
@@ -804,8 +828,8 @@ parse_next_link_header_once(const char *first, const char *last) {
}
// we are only interested in rel=preload parameter. Others are
// simply skipped.
static const char PL[] = "rel=preload";
static const size_t PLLEN = sizeof(PL) - 1;
static constexpr char PL[] = "rel=preload";
static constexpr size_t PLLEN = sizeof(PL) - 1;
if (first + PLLEN == last) {
if (std::equal(PL, PL + PLLEN, first, util::CaseCmp())) {
ok = true;
@@ -834,20 +858,19 @@ parse_next_link_header_once(const char *first, const char *last) {
}
}
// we have to reject URI if we have nonempty anchor parameter.
static const char ANCHOR[] = "anchor=";
static const size_t ANCHORLEN = sizeof(ANCHOR) - 1;
if (!ign && first + ANCHORLEN <= last) {
if (std::equal(ANCHOR, ANCHOR + ANCHORLEN, first, util::CaseCmp())) {
// we only accept URI if anchor="" here.
if (first + ANCHORLEN + 2 <= last) {
if (*(first + ANCHORLEN) != '"' || *(first + ANCHORLEN + 1) != '"') {
ign = true;
}
} else {
// here we got invalid production (anchor=") or anchor=?
ign = true;
}
}
static constexpr char ANCHOR[] = "anchor=";
static constexpr size_t ANCHORLEN = sizeof(ANCHOR) - 1;
if (!ign && !check_link_param_empty(first, last, ANCHOR, ANCHORLEN)) {
ign = true;
}
// reject URI if we have non-empty loadpolicy. This could be
// tightened up to just pick up "next" or "insert".
static constexpr char LOADPOLICY[] = "loadpolicy=";
static constexpr size_t LOADPOLICYLEN = sizeof(LOADPOLICY) - 1;
if (!ign &&
!check_link_param_empty(first, last, LOADPOLICY, LOADPOLICYLEN)) {
ign = true;
}
auto param_first = first;

View File

@@ -627,6 +627,19 @@ void test_http2_parse_link_header(void) {
CU_ASSERT(1 == res.size());
CU_ASSERT(std::make_pair(&s[36], &s[39]) == res[0].uri);
}
{
// With loadpolicy="next", url should be ignored
const char s[] = R"(<url>; rel=preload; loadpolicy="next")";
auto res = http2::parse_link_header(s, sizeof(s) - 1);
CU_ASSERT(0 == res.size());
}
{
// url should be picked up if empty loadpolicy is specified
const char s[] = R"(<url>; rel=preload; loadpolicy="")";
auto res = http2::parse_link_header(s, sizeof(s) - 1);
CU_ASSERT(1 == res.size());
CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri);
}
{
// case-insensitive match
const char s[] = R"(<url>; rel=preload; ANCHOR="#foo", <url>; )"

View File

@@ -25,6 +25,10 @@
#ifndef SHRPX_HTTP2_TEST_H
#define SHRPX_HTTP2_TEST_H
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif // HAVE_CONFIG_H
namespace shrpx {
void test_http2_add_header(void);

View File

@@ -20,5 +20,8 @@
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
if ENABLE_ASIO_LIB
nobase_include_HEADERS = nghttp2/asio_http2.h nghttp2/asio_http2_client.h \
nghttp2/asio_http2_server.h
endif # ENABLE_ASIO_LIB

View File

@@ -136,17 +136,25 @@ public:
http2 &operator=(http2 &&other) noexcept;
// Starts listening connection on given address and port and serves
// incoming requests in cleartext TCP connection.
// incoming requests in cleartext TCP connection. If |asynchronous|
// is false, this function blocks forever unless there is an error.
// If it is true, after server has started, this function returns
// immediately, and the caller should call join() to shutdown server
// gracefully.
boost::system::error_code listen_and_serve(boost::system::error_code &ec,
const std::string &address,
const std::string &port);
const std::string &port,
bool asynchronous = false);
// Starts listening connection on given address and port and serves
// incoming requests in SSL/TLS encrypted connection.
// incoming requests in SSL/TLS encrypted connection. For
// |asynchronous| parameter, see cleartext version
// |listen_and_serve|.
boost::system::error_code
listen_and_serve(boost::system::error_code &ec,
boost::asio::ssl::context &tls_context,
const std::string &address, const std::string &port);
const std::string &address, const std::string &port,
bool asynchronous = false);
// Registers request handler |cb| with path pattern |pattern|. This
// function will fail and returns false if same pattern has been
@@ -187,6 +195,12 @@ public:
// connections.
void backlog(int backlog);
// Gracefully stop http2 server
void stop();
// Join on http2 server and wait for it to fully stop
void join();
private:
std::unique_ptr<http2_impl> impl_;
};

View File

@@ -26,7 +26,9 @@
#include <config.h>
#endif // HAVE_CONFIG_H
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif // HAVE_UNISTD_H
#include <getopt.h>
#include <cstdio>
@@ -99,6 +101,11 @@ static int inflate_hd(json_t *obj, nghttp2_hd_inflater *inflater, int seq) {
return -1;
}
if (!json_is_string(wire)) {
fprintf(stderr, "'wire' value is not string at %d\n", seq);
return -1;
}
auto table_size = json_object_get(obj, "header_table_size");
if (table_size) {

View File

@@ -25,6 +25,10 @@
#ifndef MEMCHUNK_TEST_H
#define MEMCHUNK_TEST_H
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif // HAVE_CONFIG_H
namespace nghttp2 {
void test_pool_recycle(void);

View File

@@ -25,9 +25,15 @@
#include "nghttp.h"
#include <sys/stat.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif // HAVE_UNISTD_H
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif // HAVE_FCNTL_H
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif // HAVE_NETINET_IN_H
#include <netinet/tcp.h>
#include <getopt.h>
@@ -61,17 +67,33 @@
namespace nghttp2 {
// stream ID of anchor stream node when --dep-idle is enabled. These
// * portion of ANCHOR_ID_* matches RequestPriority in HtmlParser.h.
// The stream ID = 1 is excluded since it is used as first stream in
// upgrade case.
enum {
ANCHOR_ID_HIGH = 3,
ANCHOR_ID_MEDIUM = 5,
ANCHOR_ID_LOW = 7,
ANCHOR_ID_LOWEST = 9,
// The anchor stream nodes when --no-dep is not used. The stream ID =
// 1 is excluded since it is used as first stream in upgrade case. We
// follows the same dependency anchor nodes as Firefox does.
struct Anchor {
int32_t stream_id;
// stream ID this anchor depends on
int32_t dep_stream_id;
// .. with this weight.
int32_t weight;
};
// This is index into anchors. Firefox uses ANCHOR_FOLLOWERS for html
// file.
enum {
ANCHOR_LEADERS,
ANCHOR_UNBLOCKED,
ANCHOR_BACKGROUND,
ANCHOR_SPECULATIVE,
ANCHOR_FOLLOWERS,
};
namespace {
auto anchors = std::array<Anchor, 5>{{
{3, 0, 201}, {5, 0, 101}, {7, 0, 1}, {9, 7, 1}, {11, 3, 1},
}};
} // namespace
Config::Config()
: output_upper_thres(1024 * 1024), padding(0),
peer_max_concurrent_streams(NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS),
@@ -79,7 +101,7 @@ Config::Config()
timeout(0.), window_bits(-1), connection_window_bits(-1), verbose(0),
null_out(false), remote_name(false), get_assets(false), stat(false),
upgrade(false), continuation(false), no_content_length(false),
no_dep(false), dep_idle(false), hexdump(false) {
no_dep(false), hexdump(false), no_push(false) {
nghttp2_option_new(&http2_option);
nghttp2_option_set_peer_max_concurrent_streams(http2_option,
peer_max_concurrent_streams);
@@ -111,12 +133,10 @@ std::string strip_fragment(const char *raw_uri) {
Request::Request(const std::string &uri, const http_parser_url &u,
const nghttp2_data_provider *data_prd, int64_t data_length,
const nghttp2_priority_spec &pri_spec,
std::shared_ptr<Dependency> dep, int pri, int level)
: uri(uri), u(u), dep(std::move(dep)), pri_spec(pri_spec),
data_length(data_length), data_offset(0), response_len(0),
inflater(nullptr), html_parser(nullptr), data_prd(data_prd),
stream_id(-1), status(0), level(level), pri(pri),
const nghttp2_priority_spec &pri_spec, int level)
: uri(uri), u(u), pri_spec(pri_spec), data_length(data_length),
data_offset(0), response_len(0), inflater(nullptr), html_parser(nullptr),
data_prd(data_prd), stream_id(-1), status(0), level(level),
expect_final_response(false) {
http2::init_hdidx(res_hdidx);
http2::init_hdidx(req_hdidx);
@@ -155,77 +175,41 @@ std::string Request::make_reqpath() const {
return path;
}
int32_t Request::find_dep_stream_id(int start) {
for (auto i = start; i >= 0; --i) {
for (auto req : dep->deps[i]) {
return req->stream_id;
}
}
return -1;
}
nghttp2_priority_spec Request::resolve_dep(int32_t pri) {
namespace {
nghttp2_priority_spec resolve_dep(int res_type) {
nghttp2_priority_spec pri_spec;
int exclusive = 0;
int32_t stream_id = -1;
nghttp2_priority_spec_default_init(&pri_spec);
if (config.no_dep) {
nghttp2_priority_spec_default_init(&pri_spec);
return pri_spec;
}
if (config.dep_idle) {
int32_t anchor_id = 0;
switch (pri) {
case REQ_PRI_HIGH:
anchor_id = ANCHOR_ID_HIGH;
break;
case REQ_PRI_MEDIUM:
anchor_id = ANCHOR_ID_MEDIUM;
break;
case REQ_PRI_LOW:
anchor_id = ANCHOR_ID_LOW;
break;
case REQ_PRI_LOWEST:
anchor_id = ANCHOR_ID_LOWEST;
break;
}
nghttp2_priority_spec_init(&pri_spec, anchor_id, NGHTTP2_DEFAULT_WEIGHT, 0);
return pri_spec;
int32_t anchor_id;
int32_t weight;
switch (res_type) {
case REQ_CSS:
case REQ_JS:
anchor_id = anchors[ANCHOR_LEADERS].stream_id;
weight = 2;
break;
case REQ_UNBLOCK_JS:
anchor_id = anchors[ANCHOR_UNBLOCKED].stream_id;
weight = 2;
break;
case REQ_IMG:
anchor_id = anchors[ANCHOR_FOLLOWERS].stream_id;
weight = 12;
break;
default:
anchor_id = anchors[ANCHOR_FOLLOWERS].stream_id;
weight = 2;
}
if (pri == 0) {
return pri_spec;
}
auto start = std::min(pri, (int)dep->deps.size() - 1);
for (auto i = start; i >= 0; --i) {
if (dep->deps[i][0]->pri < pri) {
stream_id = find_dep_stream_id(i);
if (i != (int)dep->deps.size() - 1) {
exclusive = 1;
}
break;
} else if (dep->deps[i][0]->pri == pri) {
stream_id = find_dep_stream_id(i - 1);
break;
}
}
if (stream_id == -1) {
return pri_spec;
}
nghttp2_priority_spec_init(&pri_spec, stream_id, NGHTTP2_DEFAULT_WEIGHT,
exclusive);
nghttp2_priority_spec_init(&pri_spec, anchor_id, weight, 0);
return pri_spec;
}
} // namespace
bool Request::is_ipv6_literal_addr() const {
if (util::has_uri_field(u, UF_HOST)) {
@@ -759,6 +743,13 @@ size_t populate_settings(nghttp2_settings_entry *iv) {
iv[niv].value = config.header_table_size;
++niv;
}
if (config.no_push) {
iv[niv].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
iv[niv].value = 0;
++niv;
}
return niv;
}
} // namespace
@@ -767,7 +758,7 @@ int HttpClient::on_upgrade_connect() {
ssize_t rv;
record_connect_end_time();
assert(!reqvec.empty());
std::array<nghttp2_settings_entry, 32> iv;
std::array<nghttp2_settings_entry, 16> iv;
size_t niv = populate_settings(iv.data());
assert(settings_payload.size() >= 8 * niv);
rv = nghttp2_pack_settings_payload(settings_payload.data(),
@@ -977,26 +968,22 @@ int HttpClient::connection_made() {
return -1;
}
}
if (!config.no_dep && config.dep_idle) {
if (!config.no_dep) {
// Create anchor stream nodes
nghttp2_priority_spec pri_spec;
int32_t dep_stream_id = 0;
for (auto stream_id :
{ANCHOR_ID_HIGH, ANCHOR_ID_MEDIUM, ANCHOR_ID_LOW, ANCHOR_ID_LOWEST}) {
nghttp2_priority_spec_init(&pri_spec, dep_stream_id,
NGHTTP2_DEFAULT_WEIGHT, 0);
rv = nghttp2_submit_priority(session, NGHTTP2_FLAG_NONE, stream_id,
for (auto &anchor : anchors) {
nghttp2_priority_spec_init(&pri_spec, anchor.dep_stream_id, anchor.weight,
0);
rv = nghttp2_submit_priority(session, NGHTTP2_FLAG_NONE, anchor.stream_id,
&pri_spec);
if (rv != 0) {
return -1;
}
dep_stream_id = stream_id;
}
rv = nghttp2_session_set_next_stream_id(session, ANCHOR_ID_LOWEST + 2);
rv = nghttp2_session_set_next_stream_id(
session, anchors[ANCHOR_FOLLOWERS].stream_id + 2);
if (rv != 0) {
return -1;
}
@@ -1004,7 +991,8 @@ int HttpClient::connection_made() {
if (need_upgrade()) {
// Amend the priority because we cannot send priority in
// HTTP/1.1 Upgrade.
nghttp2_priority_spec_init(&pri_spec, ANCHOR_ID_HIGH, config.weight, 0);
auto &anchor = anchors[ANCHOR_FOLLOWERS];
nghttp2_priority_spec_init(&pri_spec, anchor.stream_id, config.weight, 0);
rv = nghttp2_submit_priority(session, NGHTTP2_FLAG_NONE, 1, &pri_spec);
if (rv != 0) {
@@ -1237,9 +1225,7 @@ void HttpClient::update_hostport() {
bool HttpClient::add_request(const std::string &uri,
const nghttp2_data_provider *data_prd,
int64_t data_length,
const nghttp2_priority_spec &pri_spec,
std::shared_ptr<Dependency> dep, int pri,
int level) {
const nghttp2_priority_spec &pri_spec, int level) {
http_parser_url u;
memset(&u, 0, sizeof(u));
if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) {
@@ -1253,8 +1239,8 @@ bool HttpClient::add_request(const std::string &uri,
path_cache.insert(uri);
}
reqvec.push_back(make_unique<Request>(uri, u, data_prd, data_length, pri_spec,
std::move(dep), pri, level));
reqvec.push_back(
make_unique<Request>(uri, u, data_prd, data_length, pri_spec, level));
return true;
}
@@ -1272,37 +1258,9 @@ void HttpClient::record_connect_end_time() {
}
void HttpClient::request_done(Request *req) {
if (req->pri == 0 && req->dep) {
assert(req->dep->deps.empty());
req->dep->deps.push_back(std::vector<Request *>{req});
return;
}
if (req->stream_id % 2 == 0) {
return;
}
auto itr = std::begin(req->dep->deps);
for (; itr != std::end(req->dep->deps); ++itr) {
if ((*itr)[0]->pri == req->pri) {
(*itr).push_back(req);
break;
}
if ((*itr)[0]->pri > req->pri) {
auto v = std::vector<Request *>{req};
req->dep->deps.insert(itr, std::move(v));
break;
}
}
if (itr == std::end(req->dep->deps)) {
req->dep->deps.push_back(std::vector<Request *>{req});
}
}
#ifdef HAVE_JANSSON
@@ -1485,7 +1443,7 @@ void update_html_parser(HttpClient *client, Request *req, const uint8_t *data,
for (auto &p : req->html_parser->get_links()) {
auto uri = strip_fragment(p.first.c_str());
auto pri = p.second;
auto res_type = p.second;
http_parser_url u;
memset(&u, 0, sizeof(u));
@@ -1494,10 +1452,9 @@ void update_html_parser(HttpClient *client, Request *req, const uint8_t *data,
util::fieldeq(uri.c_str(), u, req->uri.c_str(), req->u, UF_HOST) &&
util::porteq(uri.c_str(), u, req->uri.c_str(), req->u)) {
// No POST data for assets
auto pri_spec = req->resolve_dep(pri);
auto pri_spec = resolve_dep(res_type);
if (client->add_request(uri, nullptr, 0, pri_spec, req->dep, pri,
req->level + 1)) {
if (client->add_request(uri, nullptr, 0, pri_spec, req->level + 1)) {
submit_request(client, config.headers, client->reqvec.back().get());
}
@@ -1660,7 +1617,7 @@ int on_begin_headers_callback(nghttp2_session *session,
nghttp2_priority_spec_default_init(&pri_spec);
auto req = make_unique<Request>("", u, nullptr, 0, pri_spec, nullptr);
auto req = make_unique<Request>("", u, nullptr, 0, pri_spec);
req->stream_id = stream_id;
nghttp2_session_set_stream_user_data(session, stream_id, req.get());
@@ -1932,7 +1889,7 @@ see http://www.w3.org/TR/resource-timing/#processing-model
sorted by 'complete'
responseEnd requestStart process code size request path)" << std::endl;
id responseEnd requestStart process code size request path)" << std::endl;
const auto &base = client.timing.connect_end_time;
for (const auto &req : reqs) {
@@ -1944,8 +1901,9 @@ responseEnd requestStart process code size request path)" << std::endl;
req->timing.response_end_time - req->timing.request_start_time);
auto pushed = req->stream_id % 2 == 0;
std::cout << std::setw(11) << ("+" + util::format_duration(response_end))
<< " " << (pushed ? "*" : " ") << std::setw(11)
std::cout << std::setw(3) << req->stream_id << " " << std::setw(11)
<< ("+" + util::format_duration(response_end)) << " "
<< (pushed ? "*" : " ") << std::setw(11)
<< ("+" + util::format_duration(request_start)) << " "
<< std::setw(8) << util::format_duration(total) << " "
<< std::setw(4) << req->status << " " << std::setw(4)
@@ -2058,17 +2016,16 @@ int communicate(
nghttp2_priority_spec pri_spec;
int32_t dep_stream_id = 0;
if (!config.no_dep && config.dep_idle) {
dep_stream_id = ANCHOR_ID_HIGH;
if (!config.no_dep) {
dep_stream_id = anchors[ANCHOR_FOLLOWERS].stream_id;
}
nghttp2_priority_spec_init(&pri_spec, dep_stream_id, config.weight, 0);
for (auto req : requests) {
for (int i = 0; i < config.multiply; ++i) {
auto dep = std::make_shared<Dependency>();
client.add_request(std::get<0>(req), std::get<1>(req), std::get<2>(req),
pri_spec, std::move(dep));
pri_spec);
}
}
client.update_hostport();
@@ -2364,8 +2321,9 @@ Options:
filename is dereived from URI. If URI ends with '/',
'index.html' is used as a filename. Not implemented
yet.
-t, --timeout=<SEC>
Timeout each request after <SEC> seconds.
-t, --timeout=<DURATION>
Timeout each request after <DURATION>. Set 0 to disable
timeout.
-w, --window-bits=<N>
Sets the stream level initial window size to 2**<N>-1.
-W, --connection-window-bits=<N>
@@ -2425,17 +2383,22 @@ Options:
--no-content-length
Don't send content-length header field.
--no-dep Don't send dependency based priority hint to server.
--dep-idle Use idle streams as anchor nodes to express priority.
--hexdump Display the incoming traffic in hexadecimal (Canonical
hex+ASCII display). If SSL/TLS is used, decrypted data
are used.
--no-push Disable server push.
--version Display version information and exit.
-h, --help Display this help and exit.
--
The <SIZE> argument is an integer and an optional unit (e.g., 10K is
10 * 1024). Units are K, M and G (powers of 1024).)" << std::endl;
10 * 1024). Units are K, M and G (powers of 1024).
The <DURATION> argument is an integer and an optional unit (e.g., 1s
is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms
(hours, minutes, seconds and milliseconds, respectively). If a unit
is omitted, a second is used as unit.)" << std::endl;
}
} // namespace
@@ -2469,9 +2432,9 @@ int main(int argc, char **argv) {
{"version", no_argument, &flag, 5},
{"no-content-length", no_argument, &flag, 6},
{"no-dep", no_argument, &flag, 7},
{"dep-idle", no_argument, &flag, 8},
{"trailer", required_argument, &flag, 9},
{"hexdump", no_argument, &flag, 10},
{"no-push", no_argument, &flag, 11},
{nullptr, 0, nullptr, 0}};
int option_index = 0;
int c = getopt_long(argc, argv, "M:Oab:c:d:gm:np:r:hH:vst:uw:W:",
@@ -2521,7 +2484,11 @@ int main(int argc, char **argv) {
++config.verbose;
break;
case 't':
config.timeout = atoi(optarg);
config.timeout = util::parse_duration_with_unit(optarg);
if (config.timeout == std::numeric_limits<double>::infinity()) {
std::cerr << "-t: bad timeout value: " << optarg << std::endl;
exit(EXIT_FAILURE);
}
break;
case 'u':
config.upgrade = true;
@@ -2565,10 +2532,7 @@ int main(int argc, char **argv) {
<< std::endl;
exit(EXIT_FAILURE);
}
// To test "never index" repr, don't index authorization header
// field unconditionally.
auto no_index = util::strieq_l("authorization", header);
config.headers.emplace_back(header, value, no_index);
config.headers.emplace_back(header, value, false);
util::inp_strlower(config.headers.back().name);
break;
}
@@ -2630,10 +2594,6 @@ int main(int argc, char **argv) {
// no-dep option
config.no_dep = true;
break;
case 8:
// dep-idle option
config.dep_idle = true;
break;
case 9: {
// trailer option
auto header = optarg;
@@ -2662,6 +2622,10 @@ int main(int argc, char **argv) {
// hexdump option
config.hexdump = true;
break;
case 11:
// no-push option
config.no_push = true;
break;
}
break;
default:

Some files were not shown because too many files have changed in this diff Show More