Compare commits

..

180 Commits

Author SHA1 Message Date
Tatsuhiro Tsujikawa
d7fdcbb3d6 Update man pages 2015-03-27 00:37:12 +09:00
Tatsuhiro Tsujikawa
de2c2ad65c src: Update hexdump usage output so that help2rst.py can produce good output 2015-03-27 00:36:19 +09:00
Tatsuhiro Tsujikawa
862a0ee66b Bump up version number to 0.7.9, LT revision to 12:2:7 2015-03-27 00:27:49 +09:00
Tatsuhiro Tsujikawa
c2510a01a5 examples: Place AM_CPPFLAGS first to use in-package header files first
Fixes GH-192
2015-03-26 23:09:39 +09:00
Tatsuhiro Tsujikawa
dc85623060 nghttpx: Fix PUSH_PROMISE header field corruption
Fixes GH-194
2015-03-26 22:52:51 +09:00
Tatsuhiro Tsujikawa
8afbb6ca26 h2load: Fix crash if -t > -c 2015-03-26 19:57:37 +09:00
Tatsuhiro Tsujikawa
ed79637737 h2load: Add -d option to upload data to server 2015-03-26 19:53:42 +09:00
Tatsuhiro Tsujikawa
faa2c4467a Merge branch 'wzyboy-patch-1' 2015-03-25 23:26:20 +09:00
Zhuoyun Wei
c87fae463e Fix comments, too 2015-03-25 21:04:49 +08:00
Zhuoyun Wei
29b4b11e78 Change --spdy-proxy to --http2-proxy 2015-03-25 20:42:12 +08:00
Tatsuhiro Tsujikawa
3b24be3bcd src: Fix compile error with clang-3.6 2015-03-25 01:27:18 +09:00
Tatsuhiro Tsujikawa
ece8289aaf nghttpx: Forward only "trailers" keyword in te when forwarding HTTP/2 backend 2015-03-25 01:20:41 +09:00
Tatsuhiro Tsujikawa
4042ff0fc4 nghttpx: Fix te header field is duplicated when forwarding HTTP/2 backend 2015-03-25 01:17:06 +09:00
Tatsuhiro Tsujikawa
d3d6c5e314 Fix bug that inflater->nvbufs is not reset 2015-03-24 21:54:05 +09:00
Tatsuhiro Tsujikawa
125e32eb56 src: Refactor a bit 2015-03-24 21:45:59 +09:00
Tatsuhiro Tsujikawa
94bf8dcd4e src: Refactor util::hexdump 2015-03-24 21:43:28 +09:00
Tatsuhiro Tsujikawa
89b8039466 nghttp, nghttpd: Add --hexdump option to hexdump incoming traffic
The output format is similar to `hexdump -C`
2015-03-24 02:30:51 +09:00
Tatsuhiro Tsujikawa
72843b33d0 Bump up version number to 0.7.9-DEV 2015-03-24 00:14:56 +09:00
Tatsuhiro Tsujikawa
03e699e013 Update man pages 2015-03-24 00:04:07 +09:00
Tatsuhiro Tsujikawa
fcf99fa8fc Bump up version number to 0.7.8, LT revision to 12:1:7 2015-03-24 00:00:22 +09:00
Tatsuhiro Tsujikawa
661fb2eb0e NULL-terminate name and value in nghttp2_nv
Guaranteeing NULL-termination is very useful when name or value are
used with C functions which requires NULL-terminated string.
2015-03-23 23:25:57 +09:00
Tatsuhiro Tsujikawa
42496d638b Merge branch 'Lekensteyn-documentation' 2015-03-22 17:03:31 +09:00
Peter Wu
c0a6a0a6d1 README.rst: OpenSSL 1.0.2 has already been released
OpenSSL 1.0.2 is already released. Avoid the confusing wording that
seems to suggest that a development version of OpenSSL 1.0.2 provides
ALPN support.
2015-03-21 23:16:56 +01:00
Tatsuhiro Tsujikawa
b7bda783c5 Update doc 2015-03-21 23:09:17 +09:00
Tatsuhiro Tsujikawa
6893608ae2 Use literal instead of computed value in token lookup 2015-03-21 23:03:37 +09:00
Tatsuhiro Tsujikawa
ef913bc929 Validate :path header field
For "http" or "https" URIs, :path header field must start with "/".
The only exception is OPTIONS method, which can contain "*" to
represent system-wide OPTIONS request.
2015-03-21 23:03:37 +09:00
Tatsuhiro Tsujikawa
a5ed70bcfe Merge branch 'rasa-patch-1' 2015-03-21 19:47:27 +09:00
Tatsuhiro Tsujikawa
9bf2ca6916 Merge branch 'patch-1' of https://github.com/rasa/nghttp2 into rasa-patch-1 2015-03-21 19:46:22 +09:00
Ross Smith II
57729d0a23 Removed errant markdown 2015-03-20 08:37:06 -07:00
Tatsuhiro Tsujikawa
e86b81ec10 Merge branch 'icing-h2load-reserver-fix' 2015-03-20 23:32:43 +09:00
mod-h2-dev
076eefbed6 fix for segfault by reserving correct worker count 2015-03-16 17:42:22 +02:00
Ross Smith II
58a735dc68 Update README.rst 2015-03-15 23:42:19 -07:00
Tatsuhiro Tsujikawa
948d4d43d5 Bump up version number to 0.7.8-DEV 2015-03-14 18:53:43 +09:00
Tatsuhiro Tsujikawa
ef581e94bb Update man pages 2015-03-14 18:52:50 +09:00
Tatsuhiro Tsujikawa
ad84af2b2b Bump up version number to 0.7.7 2015-03-14 18:50:12 +09:00
Tatsuhiro Tsujikawa
0e65e1254d Bump up version number to 0.7.7-DEV 2015-03-14 18:36:18 +09:00
Tatsuhiro Tsujikawa
08ec5b3fc0 nghttp: Restore same message displayed when some requests failed 2015-03-14 18:32:53 +09:00
Tatsuhiro Tsujikawa
8c491d5917 Bump up version number to 0.7.6, LT revision to 12:0:7 2015-03-14 18:20:23 +09:00
Tatsuhiro Tsujikawa
4219fe7822 Bump up libnghttp2_asio LT revision to 1:0:0 2015-03-14 18:20:00 +09:00
Tatsuhiro Tsujikawa
d4eb2b2c75 Update doc 2015-03-14 17:59:30 +09:00
Tatsuhiro Tsujikawa
8ea26fddfd Fix compile error "chosen constructor is explicit in copy-initialization" 2015-03-14 15:54:55 +09:00
Tatsuhiro Tsujikawa
98add63cdf nghttp: Treat stream as success if we see END_STREAM from peer 2015-03-14 00:09:10 +09:00
Tatsuhiro Tsujikawa
7b90404072 nghttpx: Omit well-known port from hostport in downstream request 2015-03-14 00:09:10 +09:00
Tatsuhiro Tsujikawa
de0543f684 nghttpx: Refactor a bit 2015-03-14 00:09:10 +09:00
Tatsuhiro Tsujikawa
46e3be7b5b nghttpx: Simplify backend request line construction
It turns out that the cause of complication in backend request line
construction is a absolute-form in HTTP/1 request.  In HTTP/2, we have
separated pseudo-header fields and no problem at all.  In this commit,
we parse request URI in HTTP/1 frontend and extract values from it to
make backend logic simpler.  This patch removes host header field
emission in HTTP/2 backend if :authority is emitted.  It also rewrites
host header field with authority part in absolute-form URI as per RFC
7230.
2015-03-14 00:09:10 +09:00
Tatsuhiro Tsujikawa
7c675b1505 Merge branch 'bagder-fix-trailing-comma' 2015-03-13 23:10:38 +09:00
Daniel Stenberg
d287ea986f nghttp2.h: remove trailing comma last in enum
... since gcc -pedantic warns on it.
2015-03-13 09:05:30 +01:00
Tatsuhiro Tsujikawa
399328cb49 Depend on spdylay >= 1.3.2 for spdylay_session_set_stream_user_data 2015-03-12 01:18:04 +09:00
Tatsuhiro Tsujikawa
d46e50b112 nghttpx: Refactor DownstreamQueue to avoid expensive std::map 2015-03-12 01:13:55 +09:00
Tatsuhiro Tsujikawa
0f87cedc2d nghttpx: Use doubly linked list for dconns_ and streams_ 2015-03-11 21:35:47 +09:00
Tatsuhiro Tsujikawa
d34095cf49 nghttpx: Pin HTTP/2 upstream to one Http2Session to improve performance 2015-03-11 21:14:55 +09:00
Tatsuhiro Tsujikawa
6039bacb1b Update doc 2015-03-11 00:56:15 +09:00
Tatsuhiro Tsujikawa
4877f72a75 nghttpx: Optimize a bit 2015-03-11 00:42:18 +09:00
Tatsuhiro Tsujikawa
274b3a2296 nghttpx: Reset connection check timer on successful write while not checking 2015-03-11 00:27:51 +09:00
Tatsuhiro Tsujikawa
93013f4205 nghttpx: Remove --backend-http2-connection-check option, enable it by default 2015-03-11 00:22:05 +09:00
Tatsuhiro Tsujikawa
5789987ca3 Update doc 2015-03-11 00:16:29 +09:00
Tatsuhiro Tsujikawa
a0524ef05d Fix busy loop 2015-03-11 00:11:51 +09:00
Tatsuhiro Tsujikawa
0e3ae63965 nghttpx: Add --backend-http2-connections-per-worker 2015-03-10 23:43:25 +09:00
Tatsuhiro Tsujikawa
3e14261ebf nghttpx: Setting failure mode in on_connect() may affect other backends 2015-03-10 23:21:48 +09:00
Tatsuhiro Tsujikawa
446de923f3 nghttpx: Support multiple HTTP/2 session per worker
Currently, we use same number of HTTP/2 sessions per worker with given
backend addresses.  New option to specify the number of HTTP/2 session
per worker will follow.
2015-03-10 23:20:21 +09:00
Tatsuhiro Tsujikawa
c5860fc6f4 nghttpx: Support multiple -b option for HTTP/2 backend 2015-03-10 21:54:29 +09:00
Tatsuhiro Tsujikawa
6b714030dd nghttpx: Disable acceptor temporarily when process runs out of fd 2015-03-10 21:25:20 +09:00
Tatsuhiro Tsujikawa
8483225839 nghttpx: Don't rewrite host for CONNECT method 2015-03-10 00:44:35 +09:00
Tatsuhiro Tsujikawa
585af93828 nghttpx: Remove last write/read fields for TLS
It seems that we don't care about this since we don't change buffer
pointer between would-block write/read and next write/read.  Somehow
we decided we need these fields.  As a precaution, we set
SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER in SSL_set_mode() for both server
and client contexts.
2015-03-10 00:11:11 +09:00
Tatsuhiro Tsujikawa
41e266181e nghttpx: Attempt to improve HTTP/2 backend connection check
It turns out that writing successfully to network is not enough.
After apparently successful network write, read fails and then we
first know network has been lost (at least my android mobile network).
In this change, we say connection check is successful only when
successful read.  We already send PING in this case, so we just wait
PING ACK with short timeout.  If timeout has expired, drop connection.
Since waiting for PING ACK could degrade performance for fast reliably
connected network, we decided to disable connection check by default.
Use --backend-http2-connection-check to enable it.
2015-03-09 23:37:54 +09:00
Tatsuhiro Tsujikawa
6b7e6166f9 Merge branch 'kazuho-kazuho/issues/177' 2015-03-09 21:11:10 +09:00
Kazuho Oku
2a4f347dbc do not send pseudo-headers when in HTTP/1 2015-03-09 11:40:13 +09:00
Tatsuhiro Tsujikawa
bff5a28d12 Merge branch 'nghttpx-trailer' 2015-03-08 19:32:49 +09:00
Tatsuhiro Tsujikawa
5c31c130bd integration: Add test case for trailer part 2015-03-08 19:31:43 +09:00
Tatsuhiro Tsujikawa
b9d6fff962 nghttpx: Allow accepting trailer part in h1 frontend
Downstream's headers mutation functions have been rewritten to share
code.
2015-03-08 18:39:45 +09:00
Tatsuhiro Tsujikawa
9ffbc45ba6 nghttpx: Allow sending trailer part in h1 backend link 2015-03-08 17:58:00 +09:00
Tatsuhiro Tsujikawa
961f41d7ce nghttp, nghttpd: Add trailer header field when sending trailer part 2015-03-08 17:52:36 +09:00
Tatsuhiro Tsujikawa
928d3e5f3f nghttpx: Allow sending trailer header field 2015-03-08 17:51:52 +09:00
Tatsuhiro Tsujikawa
42eeebc7f6 nghttpx: Add function to send trailer part in h1 frontend 2015-03-08 17:32:38 +09:00
Tatsuhiro Tsujikawa
991baf9e69 nghttpx: Use http2::copy_headers_to_nva in trailer part handling 2015-03-08 17:32:01 +09:00
Tatsuhiro Tsujikawa
6ad63a06b0 nghttpx: Support response trailer part handling in h1 backend 2015-03-08 17:20:38 +09:00
Tatsuhiro Tsujikawa
60c2fe5a2e nghttpx: Support trailer-part in h2 <- h2 path 2015-03-08 16:48:45 +09:00
Tatsuhiro Tsujikawa
41b5077330 nghttpx: Support trailer part in h2 -> h2 path 2015-03-08 16:33:40 +09:00
Tatsuhiro Tsujikawa
d10dc4bb9b nghttp, nghttpd: Clean up around NGHTTP2_DATA_FLAG_NO_END_STREAM 2015-03-08 16:28:23 +09:00
Tatsuhiro Tsujikawa
c4b487be05 asio: Add client::request::write_trailer() 2015-03-07 23:04:31 +09:00
Tatsuhiro Tsujikawa
c976a0fcd1 asio: Add server::response::write_trailer() 2015-03-07 19:26:42 +09:00
Tatsuhiro Tsujikawa
5b5034f19c Merge branch 'trailer' 2015-03-07 18:02:50 +09:00
Tatsuhiro Tsujikawa
dd9e829ee1 Document NGHTTP2_DATA_FLAG_NO_END_STREAM in nghttp2_data_source_read_callback 2015-03-07 17:59:58 +09:00
Tatsuhiro Tsujikawa
80a7523b49 nghttpd: Add --trailer to send trailer header fields 2015-03-07 17:50:03 +09:00
Tatsuhiro Tsujikawa
4c55a2340b nghttp: Add --trailer optiont to send trailer header fields with -d option 2015-03-07 17:45:46 +09:00
Tatsuhiro Tsujikawa
2f2a535113 Add a way to send trailer with nghttp2_submit_request/nghttp2_submit_response
nghttp2_submit_request and nghttp2_submit_response will set
NGHTTP2_FLAG_END_STREAM after all given data is sent (data could be
0).  This means we have no way to send trailers.  In this commit, we
added NGHTTP2_DATA_FLAG_NO_END_STREAM flag.  The application can set
this flag in *data_flags inside nghttp2_data_source_read_callback.  If
NGHTTP2_DATA_FLAG_EOF is set, library automatically set
NGHTTP2_FLAG_END_STREAM.  But if both NGHTTP2_DATA_FLAG_EOF and
NGHTTP2_DATA_FLAG_NO_END_STREAM are set, NGHTTP2_FLAG_END_STREAM will
not set by library.  Then application can use new
nghttp2_submit_trailer() to send trailers.  nghttp2_submit_trailer()
will set NGHTTP2_FLAG_END_STREAM and it is actually thing wrapper of
nghttp2_submit_headers().
2015-03-07 17:09:29 +09:00
Tatsuhiro Tsujikawa
505a300d93 Refuse PUSH_PROMISE while unacked local ENABLE_PUSH is 0
After we sent SETTINGS including ENABLE_PUSH = 0, peer may already
issue PUSH_PROMISE before receiving our SETTINGS and react it to
SETTINGS ACK.  Previously we accept this PUSH_PROMISE.  In this
commit, we check the pending ENABLE_PUSH value and if it means
disabling push, we refuse PUSH_PROMISE with RST_STREAM of error
REFUSED_STREAM.
2015-03-07 16:17:40 +09:00
Tatsuhiro Tsujikawa
4236fa603f Document asio linking 2015-03-07 15:55:47 +09:00
Tatsuhiro Tsujikawa
196d7da53f nghttpx: Use http2::expect_response_body() 2015-03-07 15:50:52 +09:00
Tatsuhiro Tsujikawa
6824319fe6 asio: Link with http-parser 2015-03-07 15:48:38 +09:00
Tatsuhiro Tsujikawa
66d00954a5 asio: Quote ServeMux documents in http2::handle() 2015-03-07 03:17:13 +09:00
Tatsuhiro Tsujikawa
76eb3193ab Update documents using updated libnghttp2_asio API, including client API 2015-03-07 03:12:13 +09:00
Tatsuhiro Tsujikawa
66f5438dc9 asio: Remove eof check in asio-cl.cc 2015-03-07 03:10:37 +09:00
Tatsuhiro Tsujikawa
45164b6761 asio: asio-cl: Shutdown session on closure of first stream 2015-03-07 01:42:01 +09:00
Tatsuhiro Tsujikawa
ceefddd332 asio: Remove unused captured variable 2015-03-07 01:39:25 +09:00
Tatsuhiro Tsujikawa
a043230371 asio: Fix crach in client code 2015-03-07 01:38:59 +09:00
Tatsuhiro Tsujikawa
c2f49fa478 nghttpd: Fix bug that date header field value is not updated 2015-03-07 01:10:49 +09:00
Tatsuhiro Tsujikawa
fe17a20f84 Update README.rst 2015-03-07 01:10:49 +09:00
Tatsuhiro Tsujikawa
773b108eeb asio: Add noexcept for move constructor and move assignment operator 2015-03-07 01:10:49 +09:00
Tatsuhiro Tsujikawa
36a9cbf41f asio: Use large window size for client 2015-03-07 01:10:49 +09:00
Tatsuhiro Tsujikawa
f8182333b4 asio: Improve date header field precision 2015-03-07 01:10:49 +09:00
Tatsuhiro Tsujikawa
c3265de625 asio: Remove signal handling from server 2015-03-07 01:10:49 +09:00
Tatsuhiro Tsujikawa
b24bd3d8cb asio: Use host_service_from_uri in asio-cl2.cc 2015-03-07 01:10:49 +09:00
Tatsuhiro Tsujikawa
fa21b95274 asio: Provide move constructor and move assignment operator to server::http2 2015-03-07 01:10:49 +09:00
Tatsuhiro Tsujikawa
5dccc88a7c asio: Add host_service_from_uri() to extract remote address from URI 2015-03-07 01:10:49 +09:00
Tatsuhiro Tsujikawa
2cadd38b6b asio: Fix path matching in server 2015-03-07 01:10:49 +09:00
Tatsuhiro Tsujikawa
d7cfe464a2 asio: client::configure_tls_context takes error_code 2015-03-07 01:10:49 +09:00
Tatsuhiro Tsujikawa
018e9eaf6d asio: Clean up server API to explicitly set error code 2015-03-07 01:10:49 +09:00
Tatsuhiro Tsujikawa
94ca9705ef asio: Rewrite response body handling if response body is not expected 2015-03-06 21:36:40 +09:00
Tatsuhiro Tsujikawa
d6f810d91a examples: Update .gitignore 2015-03-06 03:14:26 +09:00
Tatsuhiro Tsujikawa
d8cf29c202 examples: Add less complicated asio client example 2015-03-06 03:13:22 +09:00
Tatsuhiro Tsujikawa
bf46539d10 python: Fix bug push response header fields are not passed to callback 2015-03-06 03:03:55 +09:00
Tatsuhiro Tsujikawa
2ed47cdd19 Merge branch 'asio-client' 2015-03-06 02:46:06 +09:00
Tatsuhiro Tsujikawa
a3f79232c6 asio: Use proper cookie syntax 2015-03-06 02:41:07 +09:00
Tatsuhiro Tsujikawa
421bf85808 Update README.rst 2015-03-06 02:36:50 +09:00
Tatsuhiro Tsujikawa
05a847b6b8 asio: Use passed URI for request 2015-03-06 02:35:44 +09:00
Tatsuhiro Tsujikawa
1e3afe0646 Update README for asio server example 2015-03-06 02:23:17 +09:00
Tatsuhiro Tsujikawa
38788d707b asio: Don't send response body if it is not expected by status code or method 2015-03-06 02:15:34 +09:00
Tatsuhiro Tsujikawa
44c0d32a1b asio: Fix compile error with gcc 2015-03-06 02:05:01 +09:00
Tatsuhiro Tsujikawa
36aae8c310 asio: Move server::http2 to its own file 2015-03-06 01:58:56 +09:00
Tatsuhiro Tsujikawa
34ac90443f asio: Rename http2::listen as http2::listen_and_serve 2015-03-06 01:54:28 +09:00
Tatsuhiro Tsujikawa
992c14e354 asio: Rename *_reader as *_generator and read_cb as generator_cb 2015-03-06 01:47:55 +09:00
Tatsuhiro Tsujikawa
529fc937dc asio: Document public APIs 2015-03-06 01:41:06 +09:00
Tatsuhiro Tsujikawa
42c174e803 asio: Add example to use timer to achieve delayed response 2015-03-06 01:04:46 +09:00
Tatsuhiro Tsujikawa
05b8d8c5b7 asio: Start sending response header when write_head is called 2015-03-06 00:06:53 +09:00
Tatsuhiro Tsujikawa
a1c937a007 src: Add missing dquote in HTML 2015-03-05 23:34:42 +09:00
Tatsuhiro Tsujikawa
8baec366f0 asio: Make redirect_handler and status_handler part of public API 2015-03-05 23:32:21 +09:00
Tatsuhiro Tsujikawa
c64bb62ffe asio: Make redirect_handler use passed uri as is and percent-encode path part 2015-03-05 23:23:17 +09:00
Tatsuhiro Tsujikawa
6020b727d8 asio: Add signal_write() to server::http2_handler to avoid scattered ifs 2015-03-05 22:00:14 +09:00
Tatsuhiro Tsujikawa
7f04968950 asio: Add dtor to API classes 2015-03-05 21:51:44 +09:00
Tatsuhiro Tsujikawa
ca1f43dfca asio: Export session_impl* from stream rather than delegating everything 2015-03-05 21:48:37 +09:00
Tatsuhiro Tsujikawa
9efb62f40b asio: Move server classes to dedicated files 2015-03-05 21:42:48 +09:00
Tatsuhiro Tsujikawa
f1c7d3edfd asio: Rename server::http2_stream as server::stream 2015-03-05 21:03:03 +09:00
Tatsuhiro Tsujikawa
7ff38eeb2e asio: Start writing on cancel 2015-03-05 03:00:18 +09:00
Tatsuhiro Tsujikawa
e4ce595ebb asio: Add serve_mux class to route incoming requet by path
serve_mux is direct port of ServeMux from go
2015-03-05 02:12:32 +09:00
Tatsuhiro Tsujikawa
8accf3898a asio: Add client::request::resume() member function 2015-03-05 02:12:32 +09:00
Tatsuhiro Tsujikawa
690a1622aa asio: Add const qualifier to client::session member functions 2015-03-05 02:12:32 +09:00
Tatsuhiro Tsujikawa
aaef798566 asio: Add server::response::io_service() 2015-03-05 02:12:32 +09:00
Tatsuhiro Tsujikawa
0753fcd6e5 asio: Add error_code parameter to cancel() 2015-03-05 02:12:32 +09:00
Tatsuhiro Tsujikawa
a14029744c asio:: Add cancel() and on_close() to server::response 2015-03-05 02:12:31 +09:00
Tatsuhiro Tsujikawa
7e46d0b142 asio: Call signal_write() on session_impl::cancel() 2015-03-05 02:12:31 +09:00
Tatsuhiro Tsujikawa
726e6c087d asio: server: Move push member function to response object 2015-03-05 02:12:31 +09:00
Tatsuhiro Tsujikawa
ff0eaf8399 asio: Indicate EOF by passing 0 to the second parameter to data_cb 2015-03-05 02:12:31 +09:00
Tatsuhiro Tsujikawa
7fb7575f78 asio: read_cb: Use similar semantics with nghttp2_data_source_read_callback 2015-03-05 02:12:31 +09:00
Tatsuhiro Tsujikawa
44642af227 asio: Remove unused struct header 2015-03-05 02:12:31 +09:00
Tatsuhiro Tsujikawa
e7d052100c asio: Utilize aggregate or value-initialization for header_value 2015-03-05 02:12:31 +09:00
Tatsuhiro Tsujikawa
fc2170e488 asio: Use uri_ref in server code 2015-03-05 02:12:31 +09:00
Tatsuhiro Tsujikawa
0cda2282dd asio: Assign values to uri_ref directly 2015-03-05 02:12:31 +09:00
Tatsuhiro Tsujikawa
566baab307 asio: asio-cl: Disable peer verification to make development easier
Don't do this in practice
2015-03-05 02:12:31 +09:00
Tatsuhiro Tsujikawa
6753b6d61e asio: Use header_map in server code 2015-03-05 02:12:31 +09:00
Tatsuhiro Tsujikawa
35817876fe asio: Move server API to asio_http2_server.h 2015-03-05 02:12:31 +09:00
Tatsuhiro Tsujikawa
7d753d779e asio: Fix resource leak (socket not closed) in server code 2015-03-05 02:12:31 +09:00
Tatsuhiro Tsujikawa
f6f908a541 asio: Make impl() const 2015-03-05 02:12:31 +09:00
Tatsuhiro Tsujikawa
b0c1986a46 asio: Avoid shared_ptr for request and response 2015-03-05 02:12:31 +09:00
Tatsuhiro Tsujikawa
9671eaa850 asio: Set stream pointer to request and response in stream ctor 2015-03-05 02:12:31 +09:00
Tatsuhiro Tsujikawa
062b42918c asio: Remove threaded task runner
This is too complicated and not suitable for event driven server.  We
plan to expose io_service instead.
2015-03-05 02:12:31 +09:00
Tatsuhiro Tsujikawa
2fa28e790d asio: Separate client API to asio_http2_client.h 2015-03-05 02:12:31 +09:00
Tatsuhiro Tsujikawa
76eab3faa0 asio: Pass connected address to connect_cb 2015-03-05 02:12:31 +09:00
Tatsuhiro Tsujikawa
70ea774f23 asio: Clear up TLS peer verification 2015-03-05 02:12:31 +09:00
Tatsuhiro Tsujikawa
e15d302985 asio: Introduce uri_ref 2015-03-05 02:12:31 +09:00
Tatsuhiro Tsujikawa
b2196f215a asio: Use boost::system::error_code for on_error callback 2015-03-05 02:12:31 +09:00
Tatsuhiro Tsujikawa
8afd75ca47 Make request, response const 2015-03-05 02:12:31 +09:00
Tatsuhiro Tsujikawa
0676535caf Do async-resove in ctor 2015-03-05 02:12:31 +09:00
Tatsuhiro Tsujikawa
dd741a58ae Use header_map instead of wrapping it 2015-03-05 02:12:31 +09:00
Tatsuhiro Tsujikawa
26304546c4 [WIP] Add asio client interface 2015-03-05 02:12:31 +09:00
Tatsuhiro Tsujikawa
838fb33892 Merge branch 'icing-pthread-getspecific' 2015-03-05 02:07:11 +09:00
Tatsuhiro Tsujikawa
7f802b623d Remove thread_local check, since we use pthread_* directly for now 2015-03-05 02:06:31 +09:00
Tatsuhiro Tsujikawa
c2426bc732 Merge branch 'pthread-getspecific' of https://github.com/icing/nghttp2 into icing-pthread-getspecific 2015-03-05 02:04:56 +09:00
Tatsuhiro Tsujikawa
08d0cdfd31 Fix typo 2015-03-04 02:48:20 +09:00
Stefan Eissing
1fd44b1567 replacing thread_local, which does not exist on OS X, with pthread_getspecific call 2015-03-03 17:09:15 +01:00
Tatsuhiro Tsujikawa
a2a9f15307 tests: Use nghttp2_mem instead of raw malloc()/free()
Fixes GH-170
2015-03-03 23:23:43 +09:00
Tatsuhiro Tsujikawa
291c27c940 Merge branch 'acesso-patch-2' 2015-03-03 22:45:46 +09:00
Tatsuhiro Tsujikawa
f86c11f07d Merge branch 'patch-2' of https://github.com/acesso/nghttp2 into acesso-patch-2 2015-03-03 22:45:13 +09:00
Tatsuhiro Tsujikawa
dbd0f032ce Fix -Werror=cast-align error on 32 bit platform
Fixes GH-172
2015-03-03 22:36:24 +09:00
acesso
6a2e6b744f Update shrpx_config.cc
declaration of make_socket_closeonexec need the proper scope here, it was there per request at https://github.com/tatsuhiro-t/nghttp2/pull/142 , not sure why is was removed.
2015-03-02 17:14:09 -03:00
Tatsuhiro Tsujikawa
4de8db523f nghttpx: Check frame type in HTTP/2 backend on_begin_headers_callback 2015-03-02 00:53:48 +09:00
Tatsuhiro Tsujikawa
cf5ac0f363 nghttpx: Fix broken server push after HTTP upgrade 2015-03-01 11:03:48 +09:00
Tatsuhiro Tsujikawa
185ebd7b79 nghttpx: Fix crash when upgrading HTTP/2 failed 2015-03-01 10:11:45 +09:00
Tatsuhiro Tsujikawa
38153e0f6e Update README.rst 2015-02-27 01:03:37 +09:00
Tatsuhiro Tsujikawa
f36f3ae1fa Bump up version number to 0.7.6-DEV 2015-02-27 01:01:29 +09:00
158 changed files with 7932 additions and 3019 deletions

2
.gitignore vendored
View File

@@ -42,6 +42,8 @@ doc/tutorial-hpack.rst
doc/python-apiref.rst doc/python-apiref.rst
doc/building-android-binary.rst doc/building-android-binary.rst
doc/asio_http2.h.rst doc/asio_http2.h.rst
doc/asio_http2_server.h.rst
doc/asio_http2_client.h.rst
doc/libnghttp2_asio.rst doc/libnghttp2_asio.rst
doc/contribute.rst doc/contribute.rst
python/setup.py python/setup.py

View File

@@ -1,30 +1,30 @@
nghttp2 - HTTP/2 C Library nghttp2 - HTTP/2 C Library
========================== ==========================
This is an implementation of Hypertext Transfer Protocol version 2 This is an implementation of the Hypertext Transfer Protocol version 2
in C. in C.
The framing layer of HTTP/2 is implemented as a form of reusable C The framing layer of HTTP/2 is implemented as a reusable C
library. On top of that, we have implemented HTTP/2 client, server library. On top of that, we have implemented an HTTP/2 client, server
and proxy. We have also developed load test and benchmarking tool for and proxy. We have also developed load test and benchmarking tools for
HTTP/2 and SPDY. HTTP/2 and SPDY.
HPACK encoder and decoder are available as public API. An HPACK encoder and decoder are available as a public API.
The experimental high level C++ library is also available. An experimental high level C++ library is also available.
We have Python binding of this libary, but we have not covered We have Python bindings of this libary, but we do not have full
everything yet. code coverage yet.
Development Status Development Status
------------------ ------------------
We started to implement h2-14 We started to implement h2-14
(http://tools.ietf.org/html/draft-ietf-httpbis-http2-14), the header (http://tools.ietf.org/html/draft-ietf-httpbis-http2-14), and header
compression compression
(http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-09). (http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-09).
The nghttp2 code base was forked from spdylay project. The nghttp2 code base was forked from the spdylay project.
=========================== ======= =========================== =======
HTTP/2 Features Support HTTP/2 Features Support
@@ -37,14 +37,14 @@ Large header (CONTINUATION) Yes
Public Test Server Public Test Server
------------------ ------------------
The following endpoints are available to try out nghttp2 The following endpoints are available to try out our nghttp2
implementation. implementation.
* https://nghttp2.org/ (TLS + ALPN/NPN) * https://nghttp2.org/ (TLS + ALPN/NPN)
NPN offer ``h2-14``, ``spdy/3.1`` and ``http/1.1``. This endpoint supports ``h2``, ``h2-16``, ``h2-14``, ``spdy/3.1``
and ``http/1.1`` via ALPN/NPN and requires TLSv1.2 for HTTP/2
This endpoint requires TLSv1.2 for HTTP/2 connection. connection.
* http://nghttp2.org/ (Upgrade / Direct) * http://nghttp2.org/ (Upgrade / Direct)
@@ -67,16 +67,16 @@ To build the documentation, you need to install:
* sphinx (http://sphinx-doc.org/) * sphinx (http://sphinx-doc.org/)
To build and run the application programs (``nghttp``, ``nghttpd`` and To build and run the application programs (``nghttp``, ``nghttpd`` and
``nghttpx``) in ``src`` directory, the following packages are ``nghttpx``) in the ``src`` directory, the following packages are
required: required:
* OpenSSL >= 1.0.1 * OpenSSL >= 1.0.1
* libev >= 4.15 * libev >= 4.15
* zlib >= 1.2.3 * zlib >= 1.2.3
ALPN support requires unreleased version OpenSSL >= 1.0.2. ALPN support requires OpenSSL >= 1.0.2 (released 22 January 2015).
To enable SPDY protocol in the application program ``nghttpx`` and To enable the SPDY protocol in the application program ``nghttpx`` and
``h2load``, the following package is required: ``h2load``, the following package is required:
* spdylay >= 1.3.0 * spdylay >= 1.3.0
@@ -90,7 +90,7 @@ The HPACK tools require the following package:
* jansson >= 2.5 * jansson >= 2.5
To build sources under examples directory, libevent is required: To build sources under the examples directory, libevent is required:
* libevent-openssl >= 2.0.8 * libevent-openssl >= 2.0.8
@@ -109,16 +109,17 @@ The Python bindings require the following packages:
* cython >= 0.19 * cython >= 0.19
* python >= 2.7 * python >= 2.7
If you are using Ubuntu 14.04 LTS, you need the following packages If you are using Ubuntu 14.04 LTS (trusty), run the following to install the needed packages::
installed::
apt-get install make binutils autoconf automake autotools-dev libtool pkg-config zlib1g-dev libcunit1-dev libssl-dev libxml2-dev libev-dev libevent-dev libjansson-dev libjemalloc-dev cython python3.4-dev sudo apt-get install make binutils autoconf automake autotools-dev libtool pkg-config \
zlib1g-dev libcunit1-dev libssl-dev libxml2-dev libev-dev libevent-dev libjansson-dev \
libjemalloc-dev cython python3.4-dev
spdylay is not packaged in Ubuntu, so you need to build it yourself: spdylay is not packaged in Ubuntu, so you need to build it yourself:
http://tatsuhiro-t.github.io/spdylay/ http://tatsuhiro-t.github.io/spdylay/
Build from git Building from git
-------------- -----------------
Building from git is easy, but please be sure that at least autoconf 2.68 is Building from git is easy, but please be sure that at least autoconf 2.68 is
used:: used::
@@ -129,23 +130,23 @@ used::
$ ./configure $ ./configure
$ make $ make
To compile source code, gcc >= 4.8.3 or clang >= 3.4 is required. To compile the source code, gcc >= 4.8.3 or clang >= 3.4 is required.
.. note:: .. note::
Mac OS X users may need ``--disable-threads`` configure option to Mac OS X users may need the ``--disable-threads`` configure option to
disable multi threading in nghttpd, nghttpx and h2load to prevent disable multi-threading in nghttpd, nghttpx and h2load to prevent
them from crashing. Patch is welcome to make multi threading work them from crashing. A patch is welcome to make multi threading work
on Mac OS X platform. on Mac OS X platform.
Building documentation Building the documentation
---------------------- --------------------------
.. note:: .. note::
Documentation is still incomplete. Documentation is still incomplete.
To build documentation, run:: To build the documentation, run::
$ make html $ make html
@@ -164,8 +165,8 @@ Unit tests are done by simply running `make check`.
Integration tests Integration tests
----------------- -----------------
We have the integration tests for nghttpx proxy server. The tests are We have the integration tests for the nghttpx proxy server. The tests are
written in `Go programming language <http://golang.org/>`_ and uses written in the `Go programming language <http://golang.org/>`_ and uses
its testing framework. We depend on the following libraries: its testing framework. We depend on the following libraries:
* https://github.com/bradfitz/http2 * https://github.com/bradfitz/http2
@@ -182,12 +183,12 @@ To run the tests, run the following command under
$ make it $ make it
Inside the tests, we use port 3009 to run test subject server. Inside the tests, we use port 3009 to run the test subject server.
Client, Server and Proxy programs Client, Server and Proxy programs
--------------------------------- ---------------------------------
The src directory contains HTTP/2 client, server and proxy programs. The ``src`` directory contains the HTTP/2 client, server and proxy programs.
nghttp - client nghttp - client
+++++++++++++++ +++++++++++++++
@@ -334,7 +335,7 @@ The HTTP Upgrade is performed like this::
[ 0.038] send GOAWAY frame <length=8, flags=0x00, stream_id=0> [ 0.038] send GOAWAY frame <length=8, flags=0x00, stream_id=0>
(last_stream_id=0, error_code=NO_ERROR(0), opaque_data(0)=[]) (last_stream_id=0, error_code=NO_ERROR(0), opaque_data(0)=[])
With ``-s`` option, ``nghttp`` prints out some timing information for Using the ``-s`` option, ``nghttp`` prints out some timing information for
requests, sorted by completion time:: requests, sorted by completion time::
$ nghttp -nas https://nghttp2.org/ $ nghttp -nas https://nghttp2.org/
@@ -360,8 +361,8 @@ requests, sorted by completion time::
+76.14ms +11.17ms 64.97ms 200 171K /images/posts/with-pri-blog.png +76.14ms +11.17ms 64.97ms 200 171K /images/posts/with-pri-blog.png
+88.52ms +11.17ms 77.36ms 200 174K /images/posts/without-pri-blog.png +88.52ms +11.17ms 77.36ms 200 174K /images/posts/without-pri-blog.png
With ``-r`` option, ``nghttp`` writes more detailed timing data to Using the ``-r`` option, ``nghttp`` writes more detailed timing data to
given file in HAR format. the given file in HAR format.
nghttpd - server nghttpd - server
++++++++++++++++ ++++++++++++++++
@@ -371,13 +372,13 @@ nghttpd - server
By default, it uses SSL/TLS connection. Use ``--no-tls`` option to By default, it uses SSL/TLS connection. Use ``--no-tls`` option to
disable it. disable it.
``nghttpd`` only accepts the HTTP/2 connection via NPN/ALPN or direct ``nghttpd`` only accepts HTTP/2 connections via NPN/ALPN or direct
HTTP/2 connection. No HTTP Upgrade is supported. HTTP/2 connections. No HTTP Upgrade is supported.
``-p`` option allows users to configure server push. The ``-p`` option allows users to configure server push.
Just like ``nghttp``, it has verbose output mode for framing Just like ``nghttp``, it has a verbose output mode for framing
information. Here is sample output from ``nghttpd`` server:: information. Here is sample output from ``nghttpd``::
$ nghttpd --no-tls -v 8080 $ nghttpd --no-tls -v 8080
IPv4: listen on port 8080 IPv4: listen on port 8080
@@ -431,8 +432,8 @@ nghttpx - proxy
+++++++++++++++ +++++++++++++++
``nghttpx`` is a multi-threaded reverse proxy for ``h2-14``, SPDY and ``nghttpx`` is a multi-threaded reverse proxy for ``h2-14``, SPDY and
HTTP/1.1 and powers nghttp2.org site and supports HTTP/2 server push. HTTP/1.1, and powers http://nghttp2.org and supports HTTP/2 server push.
It has several operation modes: It has several operational modes:
================== ============================ ============== ============= ================== ============================ ============== =============
Mode option Frontend Backend Note Mode option Frontend Backend Note
@@ -446,23 +447,21 @@ default mode HTTP/2, SPDY, HTTP/1.1 (TLS) HTTP/1.1 Reverse proxy
The interesting mode at the moment is the default mode. It works like The interesting mode at the moment is the default mode. It works like
a reverse proxy and listens for ``h2-14``, SPDY and HTTP/1.1 and can a reverse proxy and listens for ``h2-14``, SPDY and HTTP/1.1 and can
be deployed SSL/TLS terminator for existing web server. be deployed as a SSL/TLS terminator for existing web server.
The default mode, ``--http2-proxy`` and ``--http2-bridge`` modes use The default mode, ``--http2-proxy`` and ``--http2-bridge`` modes use
SSL/TLS in the frontend connection by default. To disable SSL/TLS, SSL/TLS in the frontend connection by default. To disable SSL/TLS,
use ``--frontend-no-tls`` option. If that option is used, SPDY is use the ``--frontend-no-tls`` option. If that option is used, SPDY is
disabled in the frontend and incoming HTTP/1.1 connection can be disabled in the frontend and incoming HTTP/1.1 connections can be
upgraded to HTTP/2 through HTTP Upgrade. upgraded to HTTP/2 through HTTP Upgrade.
The ``--http2-bridge``, ``--client`` and ``--client-proxy`` modes use 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 deafult. To disable SSL/TLS, use
``--backend-no-tls`` option. the ``--backend-no-tls`` option.
``nghttpx`` supports configuration file. See ``--conf`` option and ``nghttpx`` supports a configuration file. See the ``--conf`` option and
sample configuration file ``nghttpx.conf.sample``. sample configuration file ``nghttpx.conf.sample``.
``nghttpx`` does not support server push.
In the default mode, (without any of ``--http2-proxy``, In the default mode, (without any of ``--http2-proxy``,
``--http2-bridge``, ``--client-proxy`` and ``--client`` options), ``--http2-bridge``, ``--client-proxy`` and ``--client`` options),
``nghttpx`` works as reverse proxy to the backend server:: ``nghttpx`` works as reverse proxy to the backend server::
@@ -470,18 +469,18 @@ In the default mode, (without any of ``--http2-proxy``,
Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/1.1) --> Web Server Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/1.1) --> Web Server
[reverse proxy] [reverse proxy]
With ``--http2-proxy`` option, it works as so called secure proxy (aka With the ``--http2-proxy`` option, it works as a so called secure proxy (aka
SPDY proxy):: SPDY proxy)::
Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/1.1) --> Proxy Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/1.1) --> Proxy
[secure proxy] (e.g., Squid, ATS) [secure proxy] (e.g., Squid, ATS)
The ``Client`` in the above needs to be configured to use The ``Client`` in the above example needs to be configured to use
``nghttpx`` as secure proxy. ``nghttpx`` as secure proxy.
At the time of this writing, Chrome is the only browser which supports At the time of this writing, Chrome is the only browser which supports
secure proxy. The one way to configure Chrome to use secure proxy is secure proxy. One way to configure Chrome to use a secure proxy is
create proxy.pac script like this: to create a proxy.pac script like this:
.. code-block:: javascript .. code-block:: javascript
@@ -490,7 +489,7 @@ create proxy.pac script like this:
} }
``SERVERADDR`` and ``PORT`` is the hostname/address and port of the ``SERVERADDR`` and ``PORT`` is the hostname/address and port of the
machine nghttpx is running. Please note that Chrome requires valid machine nghttpx is running on. Please note that Chrome requires a valid
certificate for secure proxy. certificate for secure proxy.
Then run Chrome with the following arguments:: Then run Chrome with the following arguments::
@@ -498,24 +497,24 @@ Then run Chrome with the following arguments::
$ google-chrome --proxy-pac-url=file:///path/to/proxy.pac --use-npn $ google-chrome --proxy-pac-url=file:///path/to/proxy.pac --use-npn
With ``--http2-bridge``, it accepts HTTP/2, SPDY and HTTP/1.1 With ``--http2-bridge``, it accepts HTTP/2, SPDY and HTTP/1.1
connections and communicates with backend in HTTP/2:: connections and communicates with the backend in HTTP/2::
Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/2) --> Web or HTTP/2 Proxy etc Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/2) --> Web or HTTP/2 Proxy etc
(e.g., nghttpx -s) (e.g., nghttpx -s)
With ``--client-proxy`` option, it works as forward proxy and expects With ``--client-proxy``, it works as a forward proxy and expects
that the backend is HTTP/2 proxy:: that the backend is an HTTP/2 proxy::
Client <-- (HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/2) --> HTTP/2 Proxy Client <-- (HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/2) --> HTTP/2 Proxy
[forward proxy] (e.g., nghttpx -s) [forward proxy] (e.g., nghttpx -s)
The ``Client`` needs to be configured to use nghttpx as forward The ``Client`` needs to be configured to use nghttpx as a forward
proxy. The frontend HTTP/1.1 connection can be upgraded to HTTP/2 proxy. The frontend HTTP/1.1 connection can be upgraded to HTTP/2
through HTTP Upgrade. With the above configuration, one can use through HTTP Upgrade. With the above configuration, one can use
HTTP/1.1 client to access and test their HTTP/2 servers. HTTP/1.1 client to access and test their HTTP/2 servers.
With ``--client`` option, it works as reverse proxy and expects that With ``--client``, it works as a reverse proxy and expects that
the backend is HTTP/2 Web server:: the backend is an HTTP/2 Web server::
Client <-- (HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/2) --> Web Server Client <-- (HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/2) --> Web Server
[reverse proxy] [reverse proxy]
@@ -524,11 +523,11 @@ The frontend HTTP/1.1 connection can be upgraded to HTTP/2
through HTTP Upgrade. through HTTP Upgrade.
For the operation modes which talk to the backend in HTTP/2 over For the operation modes which talk to the backend in HTTP/2 over
SSL/TLS, the backend connections can be tunneled through HTTP proxy. SSL/TLS, the backend connections can be tunneled through an HTTP proxy.
The proxy is specified using ``--backend-http-proxy-uri`` option. The The proxy is specified using ``--backend-http-proxy-uri``. The
following figure illustrates the example of ``--http2-bridge`` and following figure illustrates the example of the ``--http2-bridge`` and
``--backend-http-proxy-uri`` options to talk to the outside HTTP/2 ``--backend-http-proxy-uri`` options to talk to the outside HTTP/2
proxy through HTTP proxy:: proxy through an HTTP proxy::
Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/2) -- Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/2) --
@@ -539,7 +538,7 @@ Benchmarking tool
----------------- -----------------
The ``h2load`` program is a benchmarking tool for HTTP/2 and SPDY. The ``h2load`` program is a benchmarking tool for HTTP/2 and SPDY.
The SPDY support is enabled if the program was built with spdylay The SPDY support is enabled if the program was built with the spdylay
library. The UI of ``h2load`` is heavily inspired by ``weighttp`` library. The UI of ``h2load`` is heavily inspired by ``weighttp``
(https://github.com/lighttpd/weighttp). The typical usage is as (https://github.com/lighttpd/weighttp). The typical usage is as
follows:: follows::
@@ -567,38 +566,38 @@ follows::
min max mean sd +/- sd min max mean sd +/- sd
time for request: 283.86ms 1.46s 659.70ms 150.87ms 84.68% time for request: 283.86ms 1.46s 659.70ms 150.87ms 84.68%
The above example issued total 100000 requests, using 100 concurrent The above example issued total 100,000 requests, using 100 concurrent
clients (in other words, 100 HTTP/2 sessions), and maximum 100 streams clients (in other words, 100 HTTP/2 sessions), and a maximum of 100 streams
per client. With ``-t`` option, ``h2load`` will use multiple native per client. With the ``-t`` option, ``h2load`` will use multiple native
threads to avoid saturating single core on client side. threads to avoid saturating a single core on client side.
.. warning:: .. warning::
**Don't use this tool against publicly available servers.** That is **Don't use this tool against publicly available servers.** That is
considered a DOS attack. Please only use against your private considered a DOS attack. Please only use it against your private
servers. servers.
HPACK tools HPACK tools
----------- -----------
The ``src`` directory contains HPACK tools. The ``deflatehd`` is a The ``src`` directory contains the HPACK tools. The ``deflatehd`` program is a
command-line header compression tool. The ``inflatehd`` is command-line header compression tool. The ``inflatehd`` program is a
command-line header decompression tool. Both tools read input from command-line header decompression tool. Both tools read input from
stdin and write output to stdout. The errors are written to stderr. stdin and write output to stdout. Errors are written to stderr.
They take JSON as input and output. We use (mostly) same JSON data They take JSON as input and output. We (mostly) use the same JSON data
format described at https://github.com/http2jp/hpack-test-case format described at https://github.com/http2jp/hpack-test-case.
deflatehd - header compressor deflatehd - header compressor
+++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++
The ``deflatehd`` reads JSON data or HTTP/1-style header fields from The ``deflatehd`` program reads JSON data or HTTP/1-style header fields from
stdin and outputs compressed header block in JSON. stdin and outputs compressed header block in JSON.
For the JSON input, the root JSON object must include ``cases`` key. For the JSON input, the root JSON object must include a ``cases`` key.
Its value has to include the sequence of input header set. They share Its value has to include the sequence of input header set. They share
the same compression context and are processed in the order they the same compression context and are processed in the order they
appear. Each item in the sequence is a JSON object and it must appear. Each item in the sequence is a JSON object and it must
include ``headers`` key. Its value is an array of a JSON object, include a ``headers`` key. Its value is an array of JSON objects,
which includes exactly one name/value pair. which includes exactly one name/value pair.
Example: Example:
@@ -624,8 +623,8 @@ Example:
} }
With ``-t`` option, the program can accept more familiar HTTP/1 style With the ``-t`` option, the program can accept more familiar HTTP/1 style
header field block. Each header set is delimited by empty line: header field blocks. Each header set is delimited by an empty line:
Example:: Example::
@@ -636,29 +635,29 @@ Example::
:method: POST :method: POST
user-agent: nghttp2 user-agent: nghttp2
The output is JSON object. It should include ``cases`` key and its The output is in JSON object. It should include a ``cases`` key and its
value is an array of JSON object, which has at least following keys: value is an array of JSON objects, which has at least the following keys:
seq seq
The index of header set in the input. The index of header set in the input.
input_length input_length
The sum of length of name/value pair in the input. The sum of the length of the name/value pairs in the input.
output_length output_length
The length of compressed header block. The length of the compressed header block.
percentage_of_original_size percentage_of_original_size
``input_length`` / ``output_length`` * 100 ``input_length`` / ``output_length`` * 100
wire wire
The compressed header block in hex string. The compressed header block as a hex string.
headers headers
The input header set. The input header set.
header_table_size header_table_size
The header table size adjusted before deflating header set. The header table size adjusted before deflating the header set.
Examples: Examples:
@@ -725,7 +724,7 @@ Examples:
The output can be used as the input for ``inflatehd`` and The output can be used as the input for ``inflatehd`` and
``deflatehd``. ``deflatehd``.
With ``-d`` option, the extra ``header_table`` key is added and its With the ``-d`` option, the extra ``header_table`` key is added and its
associated value includes the state of dynamic header table after the associated value includes the state of dynamic header table after the
corresponding header set was processed. The value includes at least corresponding header set was processed. The value includes at least
the following keys: the following keys:
@@ -749,8 +748,8 @@ deflate_size
``max_deflate_size``. ``max_deflate_size``.
max_deflate_size max_deflate_size
The maximum header table size encoder uses. This can be smaller The maximum header table size the encoder uses. This can be smaller
than ``max_size``. In this case, encoder only uses up to first 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_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 ouside the
``max_deflate_size`` but inside the ``max_size`` and make sure ``max_deflate_size`` but inside the ``max_size`` and make sure
@@ -913,14 +912,14 @@ Example:
inflatehd - header decompressor inflatehd - header decompressor
+++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++
The ``inflatehd`` reads JSON data from stdin and outputs decompressed The ``inflatehd`` program reads JSON data from stdin and outputs decompressed
name/value pairs in JSON. name/value pairs in JSON.
The root JSON object must include ``cases`` key. Its value has to The root JSON object must include the ``cases`` key. Its value has to
include the sequence of compressed header block. They share the same include the sequence of compressed header blocks. They share the same
compression context and are processed in the order they appear. Each compression context and are processed in the order they appear. Each
item in the sequence is a JSON object and it must have at least item in the sequence is a JSON object and it must have at least a
``wire`` key. Its value is a compressed header block in hex string. ``wire`` key. Its value is a compressed header block as a hex string.
Example: Example:
@@ -934,17 +933,17 @@ Example:
] ]
} }
The output is JSON object. It should include ``cases`` key and its The output is a JSON object. It should include a ``cases`` key and its
value is an array of JSON object, which has at least following keys: value is an array of JSON objects, which has at least following keys:
seq seq
The index of header set in the input. The index of the header set in the input.
headers headers
The JSON array includes decompressed name/value pairs. A JSON array that includes decompressed name/value pairs.
wire wire
The compressed header block in hex string. The compressed header block as a hex string.
header_table_size header_table_size
The header table size adjusted before inflating compressed header The header table size adjusted before inflating compressed header
@@ -1008,8 +1007,8 @@ Example:
The output can be used as the input for ``deflatehd`` and The output can be used as the input for ``deflatehd`` and
``inflatehd``. ``inflatehd``.
With ``-d`` option, the extra ``header_table`` key is added and its With the ``-d`` option, the extra ``header_table`` key is added and its
associated value includes the state of dynamic header table after the associated value includes the state of the dynamic header table after the
corresponding header set was processed. The format is the same as corresponding header set was processed. The format is the same as
``deflatehd``. ``deflatehd``.
@@ -1018,10 +1017,10 @@ libnghttp2_asio: High level HTTP/2 C++ library
libnghttp2_asio is C++ library built on top of libnghttp2 and provides libnghttp2_asio is C++ library built on top of libnghttp2 and provides
high level abstraction API to build HTTP/2 applications. It depends high level abstraction API to build HTTP/2 applications. It depends
on Boost::ASIO library and OpenSSL. Currently libnghttp2_asio on the Boost::ASIO library and OpenSSL. Currently libnghttp2_asio
provides server side API. provides both client and server APIs.
libnghttp2_asio is not built by default. Use ``--enable-asio-lib`` libnghttp2_asio is not built by default. Use the ``--enable-asio-lib``
configure flag to build libnghttp2_asio. The required Boost libraries configure flag to build libnghttp2_asio. The required Boost libraries
are: are:
@@ -1029,25 +1028,81 @@ are:
* Boost::System * Boost::System
* Boost::Thread * Boost::Thread
Server API is designed to build HTTP/2 server very easily to utilize The server API is designed to build an HTTP/2 server very easily to utilize
C++11 anonymous function and closure. The bare minimum example of C++11 anonymous functions and closures. The bare minimum example of
HTTP/2 server looks like this: an HTTP/2 server looks like this:
.. code-block:: cpp .. code-block:: cpp
#include <nghttp2/asio_http2.h> #include <nghttp2/asio_http2_server.h>
using namespace nghttp2::asio_http2; using namespace nghttp2::asio_http2;
using namespace nghttp2::asio_http2::server; using namespace nghttp2::asio_http2::server;
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
boost::system::error_code ec;
http2 server; http2 server;
server.listen("*", 3000, [](const std::shared_ptr<request> &req, server.handle("/", [](const request &req, const response &res) {
const std::shared_ptr<response> &res) { res.write_head(200);
res->write_head(200); res.end("hello, world\n");
res->end("hello, world");
}); });
if (server.listen_and_serve(ec, "localhost", "3000")) {
std::cerr << "error: " << ec.message() << std::endl;
}
}
Here is sample code to use the client API:
.. code-block:: cpp
#include <iostream>
#include <nghttp2/asio_http2_client.h>
using boost::asio::ip::tcp;
using namespace nghttp2::asio_http2;
using namespace nghttp2::asio_http2::client;
int main(int argc, char *argv[]) {
boost::system::error_code ec;
boost::asio::io_service io_service;
// connect to localhost:3000
session sess(io_service, "localhost", "3000");
sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) {
boost::system::error_code ec;
auto req = sess.submit(ec, "GET", "http://localhost:3000/");
req->on_response([](const response &res) {
// print status code and response header fields.
std::cerr << "HTTP/2 " << res.status_code() << std::endl;
for (auto &kv : res.header()) {
std::cerr << kv.first << ": " << kv.second.value << "\n";
}
std::cerr << std::endl;
res.on_data([](const uint8_t *data, std::size_t len) {
std::cerr.write(reinterpret_cast<const char *>(data), len);
std::cerr << std::endl;
});
});
req->on_close([&sess](uint32_t error_code) {
// shutdown session after first request was done.
sess.shutdown();
});
});
sess.on_error([](const boost::system::error_code &ec) {
std::cerr << "error: " << ec.message() << std::endl;
});
io_service.run();
} }
For more details, see the documentation of libnghttp2_asio. For more details, see the documentation of libnghttp2_asio.
@@ -1055,19 +1110,19 @@ For more details, see the documentation of libnghttp2_asio.
Python bindings Python bindings
--------------- ---------------
This ``python`` directory contains nghttp2 Python bindings. The The ``python`` directory contains nghttp2 Python bindings. The
bindings currently provide HPACK compressor and decompressor classes bindings currently provide HPACK compressor and decompressor classes
and HTTP/2 server. and an HTTP/2 server.
The extension module is called ``nghttp2``. The extension module is called ``nghttp2``.
``make`` will build the bindings and target Python version is ``make`` will build the bindings and target Python version is
determined by configure script. If the detected Python version is not determined by the ``configure`` script. If the detected Python version is not
what you expect, specify a path to Python executable in ``PYTHON`` what you expect, specify a path to Python executable in a ``PYTHON``
variable as an argument to configure script (e.g., ``./configure variable as an argument to configure script (e.g., ``./configure
PYTHON=/usr/bin/python3.4``). PYTHON=/usr/bin/python3.4``).
The following example code illustrates basic usage of HPACK compressor The following example code illustrates basic usage of the HPACK compressor
and decompressor in Python: and decompressor in Python:
.. code-block:: python .. code-block:: python
@@ -1094,21 +1149,21 @@ By default, it does nothing. It must be subclassed to handle each
event callback method. event callback method.
The first callback method invoked is ``on_headers()``. It is called The first callback method invoked is ``on_headers()``. It is called
when HEADERS frame, which includes request header fields, has arrived. when HEADERS frame, which includes the request header fields, has arrived.
If request has request body, ``on_data(data)`` is invoked for each If the request has a request body, ``on_data(data)`` is invoked for each
chunk of received data. chunk of received data.
When whole request is received, ``on_request_done()`` is invoked. Once the entire request is received, ``on_request_done()`` is invoked.
When stream is closed, ``on_close(error_code)`` is called. When the stream is closed, ``on_close(error_code)`` is called.
The application can send response using ``send_response()`` method. The application can send a response using ``send_response()`` method.
It can be used in ``on_headers()``, ``on_data()`` or It can be used in ``on_headers()``, ``on_data()`` or
``on_request_done()``. ``on_request_done()``.
The application can push resource using ``push()`` method. It must be The application can push resources using the ``push()`` method. It must be
used before ``send_response()`` call. used before the ``send_response()`` call.
The following instance variables are available: The following instance variables are available:
@@ -1178,14 +1233,15 @@ When contributing with code, you agree to put your changes and new
code under the same license nghttp2 is already using unless stated and code under the same license nghttp2 is already using unless stated and
agreed otherwise. agreed otherwise.
When changing existing source code, you do not alter the copyright of When changing existing source code, do not alter the copyright of
the original file(s). The copyright will still be owned by the the original file(s). The copyright will still be owned by the
original creator(s) or those who have been assigned copyright by the original creator(s) or those who have been assigned copyright by the
original author(s). original author(s).
By submitting a patch to the nghttp2 project, you are assumed to have By submitting a patch to the nghttp2 project, you (or your employer, as
the right to the code and to be allowed by your employer or whatever the case may be) agree to assign the copyright of your submission to us.
to hand over that patch/code to us. We will credit you for your .. 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 changes as far as possible, to give credit but also to keep a trace
back to who made what changes. Please always provide us with your back to who made what changes. Please always provide us with your
full real name when contributing! full real name when contributing!

View File

@@ -39,6 +39,7 @@ PATH=$TOOLCHAIN/bin:$PATH
--without-libxml2 \ --without-libxml2 \
--disable-python-bindings \ --disable-python-bindings \
--disable-examples \ --disable-examples \
--enable-werror \
CC=clang \ CC=clang \
CXX=clang++ \ CXX=clang++ \
CPPFLAGS="-I$PREFIX/include" \ CPPFLAGS="-I$PREFIX/include" \

View File

@@ -25,14 +25,14 @@ dnl Do not change user variables!
dnl http://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html dnl http://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html
AC_PREREQ(2.61) AC_PREREQ(2.61)
AC_INIT([nghttp2], [0.7.5], [t-tujikawa@users.sourceforge.net]) AC_INIT([nghttp2], [0.7.9], [t-tujikawa@users.sourceforge.net])
LT_PREREQ([2.2.6]) LT_PREREQ([2.2.6])
LT_INIT() LT_INIT()
dnl See versioning rule: dnl See versioning rule:
dnl http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html dnl http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
AC_SUBST(LT_CURRENT, 11) AC_SUBST(LT_CURRENT, 12)
AC_SUBST(LT_REVISION, 0) AC_SUBST(LT_REVISION, 2)
AC_SUBST(LT_AGE, 6) AC_SUBST(LT_AGE, 7)
major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"` major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"`
minor=`echo $PACKAGE_VERSION |cut -d. -f2 | sed -e "s/[^0-9]//g"` minor=`echo $PACKAGE_VERSION |cut -d. -f2 | sed -e "s/[^0-9]//g"`
@@ -200,21 +200,6 @@ std::vector<std::future<int>> v;
[have_std_future=no [have_std_future=no
AC_MSG_RESULT([no])]) AC_MSG_RESULT([no])])
# Check that thread_local is available.
AC_MSG_CHECKING([whether thread_local is available])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
[[
]],
[[
thread_local int n;
]])],
[AC_DEFINE([HAVE_THREAD_LOCAL], [1],
[Define to 1 if you have the `thread_local`.])
have_thread_local=yes
AC_MSG_RESULT([yes])],
[have_thread_local=no
AC_MSG_RESULT([no])])
# Check that std::map::emplace is available for g++-4.7. # Check that std::map::emplace is available for g++-4.7.
AC_MSG_CHECKING([whether std::map::emplace is available]) AC_MSG_CHECKING([whether std::map::emplace is available])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM( AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
@@ -388,7 +373,7 @@ fi
# spdylay (for src/nghttpx and src/h2load) # spdylay (for src/nghttpx and src/h2load)
have_spdylay=no have_spdylay=no
if test "x${request_spdylay}" != "xno"; then if test "x${request_spdylay}" != "xno"; then
PKG_CHECK_MODULES([LIBSPDYLAY], [libspdylay >= 1.3.0], PKG_CHECK_MODULES([LIBSPDYLAY], [libspdylay >= 1.3.2],
[have_spdylay=yes], [have_spdylay=no]) [have_spdylay=yes], [have_spdylay=no])
if test "x${have_spdylay}" = "xyes"; then if test "x${have_spdylay}" = "xyes"; then
AC_DEFINE([HAVE_SPDYLAY], [1], [Define to 1 if you have `spdylay` library.]) AC_DEFINE([HAVE_SPDYLAY], [1], [Define to 1 if you have `spdylay` library.])
@@ -549,6 +534,10 @@ if test "x$have_struct_tm_tm_gmtoff" = "xyes"; then
[Define to 1 if you have `struct tm.tm_gmtoff` member.]) [Define to 1 if you have `struct tm.tm_gmtoff` member.])
fi fi
# Check size of pointer to decide we need 8 bytes alignment
# adjustment.
AC_CHECK_SIZEOF([int *])
# Checks for library functions. # Checks for library functions.
if test "x$cross_compiling" != "xyes"; then if test "x$cross_compiling" != "xyes"; then
AC_FUNC_MALLOC AC_FUNC_MALLOC
@@ -627,11 +616,10 @@ if test "x$debug" != "xno"; then
fi fi
enable_threads=yes enable_threads=yes
# Some platform does not have working std::future or thread_local. We # Some platform does not have working std::future. We disable
# disable threading for those platforms. # threading for those platforms.
if test "x$threads" != "xyes" || if test "x$threads" != "xyes" ||
test "x$have_std_future" != "xyes" || test "x$have_std_future" != "xyes"; then
test "x$have_thread_local" != "xyes"; then
enable_threads=no enable_threads=no
AC_DEFINE([NOTHREADS], [1], [Define to 1 if you want to disable threads.]) AC_DEFINE([NOTHREADS], [1], [Define to 1 if you want to disable threads.])
fi fi
@@ -672,6 +660,8 @@ AC_CONFIG_FILES([
doc/nghttp2.h.rst doc/nghttp2.h.rst
doc/nghttp2ver.h.rst doc/nghttp2ver.h.rst
doc/asio_http2.h.rst doc/asio_http2.h.rst
doc/asio_http2_server.h.rst
doc/asio_http2_client.h.rst
doc/contribute.rst doc/contribute.rst
contrib/Makefile contrib/Makefile
]) ])

View File

@@ -84,6 +84,11 @@ are floating around in existing internet and resetting stream just
because of this may break many web sites. This is especially true if because of this may break many web sites. This is especially true if
we forward to or translate from HTTP/1 traffic. we forward to or translate from HTTP/1 traffic.
For "http" or "https" URIs, ":path" pseudo header fields must start
with "/". The only exception is OPTIONS request, in that case, "*" is
allowed in ":path" pseudo header field to represent system-wide
OPTIONS request.
With the above validations, nghttp2 library guarantees that header With the above validations, nghttp2 library guarantees that header
field name passed to `nghttp2_on_header_callback()` is not empty. field name passed to `nghttp2_on_header_callback()` is not empty.
Also required pseudo headers are all present and not empty. Also required pseudo headers are all present and not empty.

View File

@@ -0,0 +1,5 @@
asio_http2_client.h
===================
.. literalinclude:: @top_srcdir@/src/includes/nghttp2/asio_http2_client.h
:language: cpp

View File

@@ -0,0 +1,5 @@
asio_http2_server.h
===================
.. literalinclude:: @top_srcdir@/src/includes/nghttp2/asio_http2_server.h
:language: cpp

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText. .\" Man page generated from reStructuredText.
. .
.TH "H2LOAD" "1" "February 27, 2015" "0.7.5" "nghttp2" .TH "H2LOAD" "1" "March 27, 2015" "0.7.9" "nghttp2"
.SH NAME .SH NAME
h2load \- HTTP/2 benchmarking tool h2load \- HTTP/2 benchmarking tool
. .
@@ -118,6 +118,12 @@ Default: \fBh2c\-14\fP
.UNINDENT .UNINDENT
.INDENT 0.0 .INDENT 0.0
.TP .TP
.B \-d, \-\-data=<FILE>
Post FILE to server. The request method is changed to
POST.
.UNINDENT
.INDENT 0.0
.TP
.B \-v, \-\-verbose .B \-v, \-\-verbose
Output debug information. Output debug information.
.UNINDENT .UNINDENT

View File

@@ -84,6 +84,11 @@ OPTIONS
Default: ``h2c-14`` Default: ``h2c-14``
.. option:: -d, --data=<FILE>
Post FILE to server. The request method is changed to
POST.
.. option:: -v, --verbose .. option:: -v, --verbose
Output debug information. Output debug information.

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText. .\" Man page generated from reStructuredText.
. .
.TH "NGHTTP" "1" "February 27, 2015" "0.7.5" "nghttp2" .TH "NGHTTP" "1" "March 27, 2015" "0.7.9" "nghttp2"
.SH NAME .SH NAME
nghttp \- HTTP/2 experimental client nghttp \- HTTP/2 experimental client
. .
@@ -101,6 +101,14 @@ Add a header to the requests. Example: \fI\%\-H\fP\(aq:method: PUT\(aq
.UNINDENT .UNINDENT
.INDENT 0.0 .INDENT 0.0
.TP .TP
.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.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-cert=<CERT> .B \-\-cert=<CERT>
Use the specified client certificate file. The file Use the specified client certificate file. The file
must be in PEM format. must be in PEM format.
@@ -189,6 +197,13 @@ Use idle streams as anchor nodes to express priority.
.UNINDENT .UNINDENT
.INDENT 0.0 .INDENT 0.0
.TP .TP
.B \-\-hexdump
Display the incoming traffic in hexadecimal (Canonical
hex+ASCII display). If SSL/TLS is used, decrypted data
are used.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-version .B \-\-version
Display version information and exit. Display version information and exit.
.UNINDENT .UNINDENT

View File

@@ -67,6 +67,13 @@ OPTIONS
Add a header to the requests. Example: :option:`-H`\':method: PUT' Add a header to the requests. Example: :option:`-H`\':method: PUT'
.. option:: --trailer=<HEADER>
Add a trailer header to the requests. <HEADER> must not
include pseudo header field (header field name starting
with ':'). To send trailer, one must use :option:`-d` option to
send request body. Example: :option:`--trailer` 'foo: bar'.
.. option:: --cert=<CERT> .. option:: --cert=<CERT>
Use the specified client certificate file. The file Use the specified client certificate file. The file
@@ -140,6 +147,12 @@ OPTIONS
Use idle streams as anchor nodes to express priority. 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:: --version .. option:: --version
Display version information and exit. Display version information and exit.

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText. .\" Man page generated from reStructuredText.
. .
.TH "NGHTTPD" "1" "February 27, 2015" "0.7.5" "nghttp2" .TH "NGHTTPD" "1" "March 27, 2015" "0.7.9" "nghttp2"
.SH NAME .SH NAME
nghttpd \- HTTP/2 experimental server nghttpd \- HTTP/2 experimental server
. .
@@ -144,6 +144,21 @@ rather than complete request is received.
.UNINDENT .UNINDENT
.INDENT 0.0 .INDENT 0.0
.TP .TP
.B \-\-trailer=<HEADER>
Add a trailer header to a response. <HEADER> must not
include pseudo header field (header field name starting
with \(aq:\(aq). The trailer is sent only if a response has
body part. Example: \fI\%\-\-trailer\fP \(aqfoo: bar\(aq.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-hexdump
Display the incoming traffic in hexadecimal (Canonical
hex+ASCII display). If SSL/TLS is used, decrypted data
are used.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-version .B \-\-version
Display version information and exit. Display version information and exit.
.UNINDENT .UNINDENT

View File

@@ -104,6 +104,19 @@ OPTIONS
Start sending response when request HEADERS is received, Start sending response when request HEADERS is received,
rather than complete request is received. rather than complete request is received.
.. option:: --trailer=<HEADER>
Add a trailer header to a response. <HEADER> must not
include pseudo header field (header field name starting
with ':'). The trailer is sent only if a response has
body part. Example: :option:`--trailer` 'foo: bar'.
.. option:: --hexdump
Display the incoming traffic in hexadecimal (Canonical
hex+ASCII display). If SSL/TLS is used, decrypted data
are used.
.. option:: --version .. option:: --version
Display version information and exit. Display version information and exit.

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText. .\" Man page generated from reStructuredText.
. .
.TH "NGHTTPX" "1" "February 27, 2015" "0.7.5" "nghttp2" .TH "NGHTTPX" "1" "March 27, 2015" "0.7.9" "nghttp2"
.SH NAME .SH NAME
nghttpx \- HTTP/2 experimental proxy nghttpx \- HTTP/2 experimental proxy
. .
@@ -55,13 +55,10 @@ The options are categorized into several groups.
.INDENT 0.0 .INDENT 0.0
.TP .TP
.B \-b, \-\-backend=<HOST,PORT> .B \-b, \-\-backend=<HOST,PORT>
Set backend host and port. For HTTP/1 backend, multiple Set backend host and port. The multiple backend
backend addresses are accepted by repeating this option. addresses are accepted by repeating this option. UNIX
HTTP/2 backend does not support multiple backend domain socket can be specified by prefixing path name
addresses and the first occurrence of this option is with "unix:" (e.g., unix:/var/run/backend.sock)
used. UNIX domain socket can be specified by prefixing
path name with "unix:" (e.g.,
unix:/var/run/backend.sock)
.sp .sp
Default: \fB127.0.0.1,80\fP Default: \fB127.0.0.1,80\fP
.UNINDENT .UNINDENT
@@ -196,6 +193,13 @@ Default: \fB0\fP
.UNINDENT .UNINDENT
.INDENT 0.0 .INDENT 0.0
.TP .TP
.B \-\-backend\-http2\-connections\-per\-worker=<N>
Set maximum number of HTTP/2 connections per worker.
The default value is 0, which means the number of
backend addresses specified by \fI\%\-b\fP option.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-backend\-http1\-connections\-per\-host=<N> .B \-\-backend\-http1\-connections\-per\-host=<N>
Set maximum number of backend concurrent HTTP/1 Set maximum number of backend concurrent HTTP/1
connections per host. This option is meaningful when \fI\%\-s\fP connections per host. This option is meaningful when \fI\%\-s\fP

View File

@@ -34,13 +34,10 @@ Connections
.. option:: -b, --backend=<HOST,PORT> .. option:: -b, --backend=<HOST,PORT>
Set backend host and port. For HTTP/1 backend, multiple Set backend host and port. The multiple backend
backend addresses are accepted by repeating this option. addresses are accepted by repeating this option. UNIX
HTTP/2 backend does not support multiple backend domain socket can be specified by prefixing path name
addresses and the first occurrence of this option is with "unix:" (e.g., unix:/var/run/backend.sock)
used. UNIX domain socket can be specified by prefixing
path name with "unix:" (e.g.,
unix:/var/run/backend.sock)
Default: ``127.0.0.1,80`` Default: ``127.0.0.1,80``
@@ -161,6 +158,12 @@ Performance
Default: ``0`` Default: ``0``
.. option:: --backend-http2-connections-per-worker=<N>
Set maximum number of HTTP/2 connections per worker.
The default value is 0, which means the number of
backend addresses specified by :option:`-b` option.
.. option:: --backend-http1-connections-per-host=<N> .. option:: --backend-http1-connections-per-host=<N>
Set maximum number of backend concurrent HTTP/1 Set maximum number of backend concurrent HTTP/1

View File

@@ -33,6 +33,8 @@ Contents:
python-apiref python-apiref
nghttp2.h nghttp2.h
nghttp2ver.h nghttp2ver.h
asio_http2_server.h
asio_http2_client.h
asio_http2.h asio_http2.h
Source <https://github.com/tatsuhiro-t/nghttp2> Source <https://github.com/tatsuhiro-t/nghttp2>
Issues <https://github.com/tatsuhiro-t/nghttp2/issues> Issues <https://github.com/tatsuhiro-t/nghttp2/issues>

View File

@@ -4,7 +4,7 @@ libnghttp2_asio: High level HTTP/2 C++ library
libnghttp2_asio is C++ library built on top of libnghttp2 and provides libnghttp2_asio is C++ library built on top of libnghttp2 and provides
high level abstraction API to build HTTP/2 applications. It depends high level abstraction API to build HTTP/2 applications. It depends
on Boost::ASIO library and OpenSSL. Currently libnghttp2_asio on Boost::ASIO library and OpenSSL. Currently libnghttp2_asio
provides server side API. provides server and client side API.
libnghttp2_asio is not built by default. Use ``--enable-asio-lib`` libnghttp2_asio is not built by default. Use ``--enable-asio-lib``
configure flag to build libnghttp2_asio. The required Boost libraries configure flag to build libnghttp2_asio. The required Boost libraries
@@ -14,42 +14,64 @@ are:
* Boost::System * Boost::System
* Boost::Thread * Boost::Thread
To use libnghttp2_asio, first include following header file: We have 3 header files for this library:
.. code-block:: cpp * :doc:`asio_http2_server.h`
* :doc:`asio_http2_client.h`
* :doc:`asio_http2.h`
#include <nghttp2/asio_http2.h> asio_http2.h is included from the other two files.
Also take a look at that header file :doc:`asio_http2.h`. To build a program with libnghttp2_asio, link to the following
libraries::
-lnghttp2_asio -lboost_system
If ``boost::asio::ssl`` is used in application code, OpenSSL is also
required in link line::
-lnghttp2_asio -lboost_system -lssl -lcrypto
Server API Server API
---------- ----------
To use server API, first include following header file:
.. code-block:: cpp
#include <nghttp2/asio_http2_server.h>
Also take a look at that header file :doc:`asio_http2_server.h`.
Server API is designed to build HTTP/2 server very easily to utilize Server API is designed to build HTTP/2 server very easily to utilize
C++11 anonymous function and closure. The bare minimum example of C++11 anonymous function and closure. The bare minimum example of
HTTP/2 server looks like this: HTTP/2 server looks like this:
.. code-block:: cpp .. code-block:: cpp
#include <nghttp2/asio_http2.h>
using namespace nghttp2::asio_http2; using namespace nghttp2::asio_http2;
using namespace nghttp2::asio_http2::server; using namespace nghttp2::asio_http2::server;
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
boost::system::error_code ec;
http2 server; http2 server;
server.listen("*", 3000, [](const std::shared_ptr<request> &req, server.handle("/", [](const request &req, const response &res) {
const std::shared_ptr<response> &res) { res.write_head(200);
res->write_head(200); res.end("hello, world\n");
res->end("hello, world");
}); });
if (server.listen_and_serve(ec, "localhost", "3000")) {
std::cerr << "error: " << ec.message() << std::endl;
}
} }
First we instantiate ``nghttp2::asio_http2::server::http2`` object. First we instantiate ``nghttp2::asio_http2::server::http2`` object.
Then call ``nghttp2::asio_http2::server::http2::listen`` function with ``nghttp2::asio_http2::server::http2::handle`` function registers
address and port to listen to and callback function, namely "request pattern and its handler function. In this example, we register "/" as
callback", invoked when request arrives. pattern, which matches all requests. Then call
``nghttp2::asio_http2::server::http2::listen_and_serve`` function with
address and port to listen to.
The ``req`` and ``res`` represent HTTP request and response The ``req`` and ``res`` represent HTTP request and response
respectively. ``nghttp2::asio_http2_::server::response::write_head`` respectively. ``nghttp2::asio_http2_::server::response::write_head``
@@ -61,6 +83,10 @@ send.
``nghttp2::asio_http2::server::response::end`` sends responde body. ``nghttp2::asio_http2::server::response::end`` sends responde body.
In the above example, we send string "hello, world". In the above example, we send string "hello, world".
The life time of req and res object ends after the callback set by
``nghttp2::asio_http2::server::response::on_close`` function.
Application must not use those objects after this call.
Serving static files and enabling SSL/TLS Serving static files and enabling SSL/TLS
+++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++++++++++++
@@ -69,40 +95,47 @@ SSL/TLS.
.. code-block:: cpp .. code-block:: cpp
#include <nghttp2/asio_http2.h> #include <nghttp2/asio_http2_server.h>
using namespace nghttp2::asio_http2; using namespace nghttp2::asio_http2;
using namespace nghttp2::asio_http2::server; using namespace nghttp2::asio_http2::server;
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
boost::system::error_code ec;
boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23);
tls.use_private_key_file("server.key", boost::asio::ssl::context::pem);
tls.use_certificate_chain_file("server.crt");
configure_tls_context_easy(ec, tls);
http2 server; http2 server;
server.tls("server.key", "server.crt"); server.handle("/index.html", [](const request &req, const response &res) {
res.write_head(200);
server.listen("*", 3000, [](const std::shared_ptr<request> &req, res.end(file_generator("index.html"));
const std::shared_ptr<response> &res) {
if (req->path() == "/" || req->path() == "/index.html") {
res->write_head(200);
res->end(file_reader("index.html"));
} else {
res->write_head(404);
res->end("<html><head><title>404</title></head>"
"<body>404 Not Found</body></html>");
}
}); });
if (server.listen_and_serve(ec, tls, "localhost", "3000")) {
std::cerr << "error: " << ec.message() << std::endl;
}
} }
Specifying path to private key file and certificate file in We first create ``boost::asio::ssl::context`` object and set path to
``nghttp2::asio_http2::server::http2::tls`` will enable SSL/TLS. Both private key file and certificate file.
files must be in PEM format. ``nghttp2::asio_http2::server::configure_tls_context_easy`` function
configures SSL/TLS context object for HTTP/2 server use, including NPN
callbacks.
In the above example, if request path is either "/" or "/index.html", In the above example, if request path is "/index.html", we serve
we serve index.html file in the current working directory. index.html file in the current working directory.
``nghttp2::asio_http2::server::response::end`` has overload to take ``nghttp2::asio_http2::server::response::end`` has overload to take
function of type ``nghttp2::asio_http2::read_cb`` and application pass function of type ``nghttp2::asio_http2::generator_cb`` and application
its implementation to generate response body. For the convenience, pass its implementation to generate response body. For the
libnghttp2_asio library provides ``nghttp2::asio_http2::file_reader`` convenience, libnghttp2_asio library provides
function to generate function to server static file. ``nghttp2::asio_http2::file_generator`` function to generate function
to server static file. If other resource is requested, server
automatically responds with 404 status code.
Server push Server push
+++++++++++ +++++++++++
@@ -111,44 +144,56 @@ Server push is also supported.
.. code-block:: cpp .. code-block:: cpp
#include <nghttp2/asio_http2.h> #include <nghttp2/asio_http2_server.h>
using namespace nghttp2::asio_http2; using namespace nghttp2::asio_http2;
using namespace nghttp2::asio_http2::server; using namespace nghttp2::asio_http2::server;
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
boost::system::error_code ec;
boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23);
tls.use_private_key_file("server.key", boost::asio::ssl::context::pem);
tls.use_certificate_chain_file("server.crt");
configure_tls_context_easy(ec, tls);
http2 server; http2 server;
server.tls("server.key", "server.crt"); std::string style_css = "h1 { color: green; }";
server.listen("*", 3000, [](const std::shared_ptr<request> &req, server.handle("/", [&style_css](const request &req, const response &res) {
const std::shared_ptr<response> &res) { boost::system::error_code ec;
if (req->path() == "/") { auto push = res.push(ec, "GET", "/style.css");
req->push("GET", "/my.css"); push->write_head(200);
push->end(style_css);
res->write_head(200); res.write_head(200);
res->end(file_reader("index.html")); res.end(R"(
<!DOCTYPE html><html lang="en">
return; <title>HTTP/2 FTW</title><body>
} <link href="/style.css" rel="stylesheet" type="text/css">
<h1>This should be green</h1>
if (req->path() == "/my.css") { </body></html>
res->write_head(200); )");
res->end(file_reader("my.css"));
return;
}
res->write_head(404);
res->end("<html><head><title>404</title></head>"
"<body>404 Not Found</body></html>");
}); });
server.handle("/style.css",
[&style_css](const request &req, const response &res) {
res.write_head(200);
res.end(style_css);
});
if (server.listen_and_serve(ec, tls, "localhost", "3000")) {
std::cerr << "error: " << ec.message() << std::endl;
}
} }
When client requested "/", we push "/my.css". To push resource, call When client requested any resource other than "/style.css", we push
``nghttp2::asio_http2::server::request::push`` function with desired "/style.css". To push resource, call
method and path. Later, the callback will be called with the pushed ``nghttp2::asio_http2::server::response::push`` function with desired
resource "/my.css". method and path. It returns another response object and use its
functions to send push response.
Enable multi-threading Enable multi-threading
++++++++++++++++++++++ ++++++++++++++++++++++
@@ -164,65 +209,225 @@ desired number of threads:
// Use 4 native threads // Use 4 native threads
server.num_threads(4); server.num_threads(4);
Run blocking tasks in background thread Client API
+++++++++++++++++++++++++++++++++++++++ ----------
The request callback is called in the same thread where HTTP request To use client API, first include following header file:
is handled. And many connections shares the same thread, we cannot
directly run blocking tasks in request callback.
To run blocking tasks, use
``nghttp2::asio_http2::server::request::run_task``. The passed
callback will be executed in the different thread from the thread
where request callback was executed. So application can perform
blocking task there. The example follows:
.. code-block:: cpp .. code-block:: cpp
#include <unistd.h> #include <nghttp2/asio_http2_client.h>
#include <nghttp2/asio_http2.h>
Also take a look at that header file :doc:`asio_http2_client.h`.
Here is the sample client code to access HTTP/2 server and print out
response header fields and response body to the console screen:
.. code-block:: cpp
#include <iostream>
#include <nghttp2/asio_http2_client.h>
using boost::asio::ip::tcp;
using namespace nghttp2::asio_http2; using namespace nghttp2::asio_http2;
using namespace nghttp2::asio_http2::server; using namespace nghttp2::asio_http2::client;
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
http2 server; boost::system::error_code ec;
boost::asio::io_service io_service;
server.num_concurrent_tasks(16); // connect to localhost:3000
session sess(io_service, "localhost", "3000");
server.listen("*", 3000, [](const std::shared_ptr<request> &req, sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) {
const std::shared_ptr<response> &res) { boost::system::error_code ec;
req->run_task([res](channel &channel) {
// executed in different thread than the thread where
// request callback was executed.
// using res directly here is not safe. Capturing it by auto req = sess.submit(ec, "GET", "http://localhost:3000/");
// value is safe because it is std::shared_ptr.
sleep(1); req->on_response([](const response &res) {
// print status code and response header fields.
std::cerr << "HTTP/2 " << res.status_code() << std::endl;
for (auto &kv : res.header()) {
std::cerr << kv.first << ": " << kv.second.value << "\n";
}
std::cerr << std::endl;
channel.post([res]() { res.on_data([](const uint8_t *data, std::size_t len) {
// executed in the same thread where request callback std::cerr.write(reinterpret_cast<const char *>(data), len);
// was executed. std::cerr << std::endl;
res->write_head(200); });
res->end("hello, world"); });
});
}); req->on_close([&sess](uint32_t error_code) {
// shutdown session after first request was done.
sess.shutdown();
});
}); });
sess.on_error([](const boost::system::error_code &ec) {
std::cerr << "error: " << ec.message() << std::endl;
});
io_service.run();
} }
First we set the number of background threads which run tasks. By ``nghttp2::asio_http2::client::session`` object takes
default it is set to 1. In this example, we set it to 16, so at most ``boost::asio::io_service`` object and remote server address. When
16 tasks can be executed concurrently without blocking handling new connection is made, the callback function passed to
requests. ``nghttp2::asio_http2::client::on_connect`` is invoked with connected
address as its paramter. After this callback call, use
``nghttp2::asio_http2::session::submit`` to send request to the
server. You can submit multiple requests at once without waiting for
the completion of previous request.
We call ``req->run_task()`` to execute task in background thread. In The life time of req and res object ends after the callback set by
the passed callback, we just simply sleeps 1 second. After sleep is ``nghttp2::asio_http2::server::request::on_close`` function.
over, we schedule another callback to send response to the client. Application must not use those objects after this call.
Since the callback passed to ``req->run_task()`` is executed in the
different thread from the thread where request callback is called, Normally, client does not stop even after all requests are done unless
using ``req`` or ``res`` object directly there may cause undefined connection is lost. To stop client, call
behaviour. To avoid this issue, we can use ``nghttp2::asio_http2::server::session::shutdown()``.
``nghttp2::asio_http2::channel::post`` by supplying a callback which
in turn get called in the same thread where request callback was Recieve server push and enable SSL/TLS
called. ++++++++++++++++++++++++++++++++++++++
.. code-block:: cpp
#include <iostream>
#include <nghttp2/asio_http2_client.h>
using boost::asio::ip::tcp;
using namespace nghttp2::asio_http2;
using namespace nghttp2::asio_http2::client;
int main(int argc, char *argv[]) {
boost::system::error_code ec;
boost::asio::io_service io_service;
boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23);
tls.set_default_verify_paths();
// disabled to make development easier...
// tls_ctx.set_verify_mode(boost::asio::ssl::verify_peer);
configure_tls_context(ec, tls);
// connect to localhost:3000
session sess(io_service, tls, "localhost", "3000");
sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) {
boost::system::error_code ec;
auto req = sess.submit(ec, "GET", "http://localhost:3000/");
req->on_response([&sess](const response &res) {
std::cerr << "response received!" << std::endl;
res.on_data([&sess](const uint8_t *data, std::size_t len) {
std::cerr.write(reinterpret_cast<const char *>(data), len);
std::cerr << std::endl;
});
});
req->on_push([](const request &push) {
std::cerr << "push request received!" << std::endl;
push.on_response([](const response &res) {
std::cerr << "push response received!" << std::endl;
res.on_data([](const uint8_t *data, std::size_t len) {
std::cerr.write(reinterpret_cast<const char *>(data), len);
std::cerr << std::endl;
});
});
});
});
sess.on_error([](const boost::system::error_code &ec) {
std::cerr << "error: " << ec.message() << std::endl;
});
io_service.run();
}
The above sample code demonstrates how to enable SSL/TLS and receive
server push. Currently,
``nghttp2::asio_http2::client::configure_tls_context`` function setups
NPN callbacks for SSL/TLS context for HTTP/2 use.
To receive server push, use
``nghttp2::asio_http2::client::request::on_push`` function to set
callback function which is invoked when server push request is
arrived. The callback function takes
``nghttp2::asio_http2::client::request`` object, which contains the
pushed request. To get server push response, set callback using
``nghttp2::asio_http2::client::request::on_response``.
As stated in the previous section, client does not stop automatically
as long as HTTP/2 session is fine and connection is alive. We don't
call ``nghttp2::asio_http2::client::session::shutdown`` in this
example, so the program does not terminate after all responses are
received. Hit Ctrl-C to terminate the program.
Multiple concurrent requests
++++++++++++++++++++++++++++
.. code-block:: cpp
#include <iostream>
#include <nghttp2/asio_http2_client.h>
using boost::asio::ip::tcp;
using namespace nghttp2::asio_http2;
using namespace nghttp2::asio_http2::client;
int main(int argc, char *argv[]) {
boost::system::error_code ec;
boost::asio::io_service io_service;
// connect to localhost:3000
session sess(io_service, "localhost", "3000");
sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) {
boost::system::error_code ec;
auto printer = [](const response &res) {
res.on_data([](const uint8_t *data, std::size_t len) {
std::cerr.write(reinterpret_cast<const char *>(data), len);
std::cerr << std::endl;
});
};
std::size_t num = 3;
auto count = std::make_shared<int>(num);
for (std::size_t i = 0; i < num; ++i) {
auto req = sess.submit(ec, "GET",
"http://localhost:3000/" + std::to_string(i + 1));
req->on_response(printer);
req->on_close([&sess, count](uint32_t error_code) {
if (--*count == 0) {
// shutdown session after |num| requests were done.
sess.shutdown();
}
});
}
});
sess.on_error([](const boost::system::error_code &ec) {
std::cerr << "error: " << ec.message() << std::endl;
});
io_service.run();
}
Here is the sample to send 3 requests at once. Depending on the
server settings, these requests are processed out-of-order. In this
example, we have a trick to shutdown session after all requests were
done. We made ``count`` object which is shared pointer to int and is
initialized to 3. On each request closure (the invocation of the
callback set by ``nghttp2::asio_http2::client::request::on_close``),
we decrement the count. If count becomes 0, we are sure that all
requests have been done and initiate shutdown.

View File

@@ -59,11 +59,11 @@ SPDY protocols and it works as so called SPDY proxy.
With ``--frontend-no-tls`` option, SSL/TLS is turned off in frontend With ``--frontend-no-tls`` option, SSL/TLS is turned off in frontend
connection, so the connection gets insecure. connection, so the connection gets insecure.
The backend must be HTTP/1 proxy server. nghttpx only supports The backend must be HTTP/1 proxy server. nghttpx supports multiple
multiple backend server addresses. It translates incoming requests to backend server addresses. It translates incoming requests to HTTP/1
HTTP/1 request to backend server. The backend server performs real request to backend server. The backend server performs real proxy
proxy work for each request, for example, dispatching requests to the work for each request, for example, dispatching requests to the origin
origin server and caching contents. server and caching contents.
For example, to make nghttpx listen to encrypted HTTP/2 requests at For example, to make nghttpx listen to encrypted HTTP/2 requests at
port 8443, and a backend HTTP/1 proxy server is configured to listen port 8443, and a backend HTTP/1 proxy server is configured to listen
@@ -124,7 +124,9 @@ HTTP/1 frontend connection can be upgraded to HTTP/2 using HTTP
Upgrade. To disable SSL/TLS in backend connection, use Upgrade. To disable SSL/TLS in backend connection, use
``--backend-no-tls`` option. ``--backend-no-tls`` option.
The backend connection is created one per worker (thread). By default, the number of backend HTTP/2 connections per worker
(thread) is determined by number of ``-b`` option. To adjust this
value, use ``--backend-http2-connections-per-worker`` option.
The backend server is supporsed to be a HTTP/2 web server (e.g., The backend server is supporsed to be a HTTP/2 web server (e.g.,
nghttpd). The one use-case of this mode is utilize existing HTTP/1 nghttpd). The one use-case of this mode is utilize existing HTTP/1
@@ -156,7 +158,9 @@ HTTP/1 frontend connection can be upgraded to HTTP/2 using HTTP
Upgrade. To disable SSL/TLS in backend connection, use Upgrade. To disable SSL/TLS in backend connection, use
``--backend-no-tls`` option. ``--backend-no-tls`` option.
The backend connection is created one per worker (thread). By default, the number of backend HTTP/2 connections per worker
(thread) is determined by number of ``-b`` option. To adjust this
value, use ``--backend-http2-connections-per-worker`` option.
The backend server must be a HTTP/2 proxy. You can use nghttpx in The backend server must be a HTTP/2 proxy. You can use nghttpx in
`HTTP/2 proxy mode`_ as backend server. The one use-case of this mode `HTTP/2 proxy mode`_ as backend server. The one use-case of this mode
@@ -196,6 +200,10 @@ With ``--frontend-no-tls`` option, SSL/TLS is turned off in frontend
connection, so the connection gets insecure. To disable SSL/TLS in connection, so the connection gets insecure. To disable SSL/TLS in
backend connection, use ``--backend-no-tls`` option. backend connection, use ``--backend-no-tls`` option.
By default, the number of backend HTTP/2 connections per worker
(thread) is determined by number of ``-b`` option. To adjust this
value, use ``--backend-http2-connections-per-worker`` option.
The backend server is supporsed to be a HTTP/2 web server or HTTP/2 The backend server is supporsed to be a HTTP/2 web server or HTTP/2
proxy. If backend server is HTTP/2 proxy, use proxy. If backend server is HTTP/2 proxy, use
``--no-location-rewrite`` and ``--no-host-rewrite`` options to disable ``--no-location-rewrite`` and ``--no-host-rewrite`` options to disable
@@ -285,11 +293,11 @@ re-open log files, send USR1 signal to nghttpx process. It will
re-open files specified by ``--accesslog-file`` and re-open files specified by ``--accesslog-file`` and
``--errorlog-file`` options. ``--errorlog-file`` options.
Multiple HTTP/1 backend addresses Multiple backend addresses
--------------------------------- --------------------------
nghttpx supports multiple HTTP/1 backend addresses. To specify them, nghttpx supports multiple backend addresses. To specify them, just
just use ``-b`` option repeatedly. For example, to use backend1:8080 use ``-b`` option repeatedly. For example, to use backend1:8080 and
and backend2:8080, use command-line like this: ``-bbackend1,8080 backend2:8080, use command-line like this: ``-bbackend1,8080
-bbackend2,8080``. Please note that HTTP/2 backend only supports 1 -bbackend2,8080``. For HTTP/2 backend, see also
backend address. ``--backend-http2-connections-per-worker`` option.

5
examples/.gitignore vendored
View File

@@ -2,7 +2,8 @@ client
libevent-client libevent-client
libevent-server libevent-server
deflate deflate
asio-sv
tiny-nghttpd tiny-nghttpd
asio-sv
asio-sv2 asio-sv2
asio-sv3 asio-cl
asio-cl2

View File

@@ -58,10 +58,21 @@ endif # ENABLE_TINY_NGHTTPD
if ENABLE_ASIO_LIB if ENABLE_ASIO_LIB
noinst_PROGRAMS += asio-sv asio-sv2 asio-sv3 noinst_PROGRAMS += asio-sv asio-sv2 asio-cl asio-cl2
ASIOCPPFLAGS = ${BOOST_CPPFLAGS} ${AM_CPPFLAGS} # AM_CPPFLAGS must be placed first, so that header file (e.g.,
ASIOLDADD = $(top_builddir)/src/libnghttp2_asio.la @JEMALLOC_LIBS@ # nghttp2/nghttp2.h) in this package is used rather than installed
# one.
ASIOCPPFLAGS = ${AM_CPPFLAGS} ${BOOST_CPPFLAGS}
ASIOLDADD = $(top_builddir)/lib/libnghttp2.la \
$(top_builddir)/src/libnghttp2_asio.la @JEMALLOC_LIBS@ \
$(top_builddir)/third-party/libhttp-parser.la \
${BOOST_LDFLAGS} \
${BOOST_ASIO_LIB} \
${BOOST_THREAD_LIB} \
${BOOST_SYSTEM_LIB} \
@OPENSSL_LIBS@ \
@APPLDFLAGS@
asio_sv_SOURCES = asio-sv.cc asio_sv_SOURCES = asio-sv.cc
asio_sv_CPPFLAGS = ${ASIOCPPFLAGS} asio_sv_CPPFLAGS = ${ASIOCPPFLAGS}
@@ -71,9 +82,13 @@ asio_sv2_SOURCES = asio-sv2.cc
asio_sv2_CPPFLAGS = ${ASIOCPPFLAGS} asio_sv2_CPPFLAGS = ${ASIOCPPFLAGS}
asio_sv2_LDADD = ${ASIOLDADD} asio_sv2_LDADD = ${ASIOLDADD}
asio_sv3_SOURCES = asio-sv3.cc asio_cl_SOURCES = asio-cl.cc
asio_sv3_CPPFLAGS = ${ASIOCPPFLAGS} asio_cl_CPPFLAGS = ${ASIOCPPFLAGS}
asio_sv3_LDADD = ${ASIOLDADD} asio_cl_LDADD = ${ASIOLDADD}
asio_cl2_SOURCES = asio-cl2.cc
asio_cl2_CPPFLAGS = ${ASIOCPPFLAGS}
asio_cl2_LDADD = ${ASIOLDADD}
endif # ENABLE_ASIO_LIB endif # ENABLE_ASIO_LIB

96
examples/asio-cl.cc Normal file
View File

@@ -0,0 +1,96 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <iostream>
#include <nghttp2/asio_http2_client.h>
using boost::asio::ip::tcp;
using namespace nghttp2::asio_http2;
using namespace nghttp2::asio_http2::client;
int main(int argc, char *argv[]) {
try {
if (argc < 2) {
std::cerr << "Usage: asio-cl URI" << std::endl;
return 1;
}
boost::system::error_code ec;
boost::asio::io_service io_service;
std::string uri = argv[1];
std::string scheme, host, service;
if (host_service_from_uri(ec, scheme, host, service, uri)) {
std::cerr << "error: bad URI: " << ec.message() << std::endl;
return 1;
}
boost::asio::ssl::context tls_ctx(boost::asio::ssl::context::sslv23);
tls_ctx.set_default_verify_paths();
// disabled to make development easier...
// tls_ctx.set_verify_mode(boost::asio::ssl::verify_peer);
configure_tls_context(ec, tls_ctx);
auto sess = scheme == "https" ? session(io_service, tls_ctx, host, service)
: session(io_service, host, service);
sess.on_connect([&sess, &uri](tcp::resolver::iterator endpoint_it) {
boost::system::error_code ec;
auto req = sess.submit(ec, "GET", uri);
if (ec) {
std::cerr << "error: " << ec.message() << std::endl;
return;
}
req->on_response([&sess](const response &res) {
std::cerr << "HTTP/2 " << res.status_code() << std::endl;
for (auto &kv : res.header()) {
std::cerr << kv.first << ": " << kv.second.value << "\n";
}
std::cerr << std::endl;
res.on_data([&sess](const uint8_t *data, std::size_t len) {
std::cerr.write(reinterpret_cast<const char *>(data), len);
std::cerr << std::endl;
});
});
req->on_close([&sess](uint32_t error_code) { sess.shutdown(); });
});
sess.on_error([](const boost::system::error_code &ec) {
std::cerr << "error: " << ec.message() << std::endl;
});
io_service.run();
} catch (std::exception &e) {
std::cerr << "exception: " << e.what() << "\n";
}
return 0;
}

134
examples/asio-cl2.cc Normal file
View File

@@ -0,0 +1,134 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <iostream>
#include <string>
#include <nghttp2/asio_http2_client.h>
using boost::asio::ip::tcp;
using namespace nghttp2::asio_http2;
using namespace nghttp2::asio_http2::client;
void print_header(const header_map &h) {
for (auto &kv : h) {
std::cerr << kv.first << ": " << kv.second.value << "\n";
}
std::cerr << std::endl;
}
void print_header(const response &res) {
std::cerr << "HTTP/2 " << res.status_code() << "\n";
print_header(res.header());
}
void print_header(const request &req) {
auto &uri = req.uri();
std::cerr << req.method() << " " << uri.scheme << "://" << uri.host
<< uri.path;
if (!uri.raw_query.empty()) {
std::cerr << "?" << uri.raw_query;
}
std::cerr << " HTTP/2\n";
print_header(req.header());
}
int main(int argc, char *argv[]) {
try {
if (argc < 2) {
std::cerr << "Usage: asio-cl URI" << std::endl;
return 1;
}
boost::system::error_code ec;
boost::asio::io_service io_service;
std::string uri = argv[1];
std::string scheme, host, service;
if (host_service_from_uri(ec, scheme, host, service, uri)) {
std::cerr << "error: bad URI: " << ec.message() << std::endl;
return 1;
}
boost::asio::ssl::context tls_ctx(boost::asio::ssl::context::sslv23);
tls_ctx.set_default_verify_paths();
// disabled to make development easier...
// tls_ctx.set_verify_mode(boost::asio::ssl::verify_peer);
configure_tls_context(ec, tls_ctx);
auto sess = scheme == "https" ? session(io_service, tls_ctx, host, service)
: session(io_service, host, service);
sess.on_connect([&sess, &uri](tcp::resolver::iterator endpoint_it) {
std::cerr << "connected to " << (*endpoint_it).endpoint() << std::endl;
boost::system::error_code ec;
auto req = sess.submit(ec, "GET", uri, {{"cookie", {"foo=bar", true}}});
if (ec) {
std::cerr << "error: " << ec.message() << std::endl;
return;
}
req->on_response([&sess, req](const response &res) {
std::cerr << "response header was received" << std::endl;
print_header(res);
res.on_data([&sess](const uint8_t *data, std::size_t len) {
std::cerr.write(reinterpret_cast<const char *>(data), len);
std::cerr << std::endl;
});
});
req->on_close([&sess](uint32_t error_code) {
std::cerr << "request done with error_code=" << error_code << std::endl;
});
req->on_push([](const request &push_req) {
std::cerr << "push request was received" << std::endl;
print_header(push_req);
push_req.on_response([](const response &res) {
std::cerr << "push response header was received" << std::endl;
res.on_data([](const uint8_t *data, std::size_t len) {
std::cerr.write(reinterpret_cast<const char *>(data), len);
std::cerr << std::endl;
});
});
});
});
sess.on_error([](const boost::system::error_code &ec) {
std::cerr << "error: " << ec.message() << std::endl;
});
io_service.run();
} catch (std::exception &e) {
std::cerr << "exception: " << e.what() << "\n";
}
return 0;
}

View File

@@ -37,7 +37,7 @@
#include <iostream> #include <iostream>
#include <string> #include <string>
#include <nghttp2/asio_http2.h> #include <nghttp2/asio_http2_server.h>
using namespace nghttp2::asio_http2; using namespace nghttp2::asio_http2;
using namespace nghttp2::asio_http2::server; using namespace nghttp2::asio_http2::server;
@@ -45,28 +45,102 @@ using namespace nghttp2::asio_http2::server;
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
try { try {
// Check command line arguments. // Check command line arguments.
if (argc < 3) { if (argc < 4) {
std::cerr << "Usage: asio-sv <port> <threads> <private-key-file> " std::cerr
<< "<cert-file>\n"; << "Usage: asio-sv <address> <port> <threads> [<private-key-file> "
<< "<cert-file>]\n";
return 1; return 1;
} }
uint16_t port = std::stoi(argv[1]); boost::system::error_code ec;
std::size_t num_threads = std::stoi(argv[2]);
std::string addr = argv[1];
std::string port = argv[2];
std::size_t num_threads = std::stoi(argv[3]);
http2 server; http2 server;
server.num_threads(num_threads); server.num_threads(num_threads);
if (argc >= 5) { server.handle("/", [](const request &req, const response &res) {
server.tls(argv[3], argv[4]); res.write_head(200, {{"foo", {"bar"}}});
} res.end("hello, world\n");
server.listen("*", port, [](const std::shared_ptr<request> &req,
const std::shared_ptr<response> &res) {
res->write_head(200, {header{"foo", "bar"}});
res->end("hello, world");
}); });
server.handle("/secret/", [](const request &req, const response &res) {
res.write_head(200);
res.end("under construction!\n");
});
server.handle("/push", [](const request &req, const response &res) {
boost::system::error_code ec;
auto push = res.push(ec, "GET", "/push/1");
if (!ec) {
push->write_head(200);
push->end("server push FTW!\n");
}
res.write_head(200);
res.end("you'll receive server push!\n");
});
server.handle("/delay", [](const request &req, const response &res) {
res.write_head(200);
auto timer = std::make_shared<boost::asio::deadline_timer>(
res.io_service(), boost::posix_time::seconds(3));
auto closed = std::make_shared<bool>();
res.on_close([timer, closed](uint32_t error_code) {
timer->cancel();
*closed = true;
});
timer->async_wait([&res, closed](const boost::system::error_code &ec) {
if (ec || *closed) {
return;
}
res.end("finally!\n");
});
});
server.handle("/trailer", [](const request &req, const response &res) {
// send trailer part.
res.write_head(200, {{"trailers", {"digest"}}});
std::string body = "nghttp2 FTW!\n";
auto left = std::make_shared<size_t>(body.size());
res.end([&res, body, left](uint8_t *dst, std::size_t len,
uint32_t *data_flags) {
auto n = std::min(len, *left);
std::copy_n(body.c_str() + (body.size() - *left), n, dst);
*left -= n;
if (*left == 0) {
*data_flags |=
NGHTTP2_DATA_FLAG_EOF | NGHTTP2_DATA_FLAG_NO_END_STREAM;
// RFC 3230 Instance Digests in HTTP. The digest value is
// SHA-256 message digest of body.
res.write_trailer(
{{"digest",
{"SHA-256=qqXqskW7F3ueBSvmZRCiSwl2ym4HRO0M/pvQCBlSDis="}}});
}
return n;
});
});
if (argc >= 6) {
boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23);
tls.use_private_key_file(argv[4], boost::asio::ssl::context::pem);
tls.use_certificate_chain_file(argv[5]);
configure_tls_context_easy(ec, tls);
if (server.listen_and_serve(ec, tls, addr, port)) {
std::cerr << "error: " << ec.message() << std::endl;
}
} else {
if (server.listen_and_serve(ec, addr, port)) {
std::cerr << "error: " << ec.message() << std::endl;
}
}
} catch (std::exception &e) { } catch (std::exception &e) {
std::cerr << "exception: " << e.what() << "\n"; std::cerr << "exception: " << e.what() << "\n";
} }

View File

@@ -40,7 +40,7 @@
#include <iostream> #include <iostream>
#include <string> #include <string>
#include <nghttp2/asio_http2.h> #include <nghttp2/asio_http2_server.h>
using namespace nghttp2::asio_http2; using namespace nghttp2::asio_http2;
using namespace nghttp2::asio_http2::server; using namespace nghttp2::asio_http2::server;
@@ -48,30 +48,28 @@ using namespace nghttp2::asio_http2::server;
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
try { try {
// Check command line arguments. // Check command line arguments.
if (argc < 4) { if (argc < 5) {
std::cerr << "Usage: asio-sv2 <port> <threads> <doc-root> " std::cerr << "Usage: asio-sv2 <address> <port> <threads> <doc-root> "
<< "<private-key-file> <cert-file>\n"; << "[<private-key-file> <cert-file>]\n";
return 1; return 1;
} }
uint16_t port = std::stoi(argv[1]); boost::system::error_code ec;
std::size_t num_threads = std::stoi(argv[2]);
std::string docroot = argv[3]; std::string addr = argv[1];
std::string port = argv[2];
std::size_t num_threads = std::stoi(argv[3]);
std::string docroot = argv[4];
http2 server; http2 server;
server.num_threads(num_threads); server.num_threads(num_threads);
if (argc >= 6) { server.handle("/", [&docroot](const request &req, const response &res) {
server.tls(argv[4], argv[5]); auto path = percent_decode(req.uri().path);
}
server.listen("*", port, [&docroot](const std::shared_ptr<request> &req,
const std::shared_ptr<response> &res) {
auto path = percent_decode(req->path());
if (!check_path(path)) { if (!check_path(path)) {
res->write_head(404); res.write_head(404);
res->end(); res.end();
return; return;
} }
@@ -82,22 +80,39 @@ int main(int argc, char *argv[]) {
path = docroot + path; path = docroot + path;
auto fd = open(path.c_str(), O_RDONLY); auto fd = open(path.c_str(), O_RDONLY);
if (fd == -1) { if (fd == -1) {
res->write_head(404); res.write_head(404);
res->end(); res.end();
return; return;
} }
auto headers = std::vector<header>(); auto header = header_map();
struct stat stbuf; struct stat stbuf;
if (stat(path.c_str(), &stbuf) == 0) { if (stat(path.c_str(), &stbuf) == 0) {
headers.push_back( header.emplace("content-length",
header{"content-length", std::to_string(stbuf.st_size)}); header_value{std::to_string(stbuf.st_size)});
headers.push_back(header{"last-modified", http_date(stbuf.st_mtime)}); header.emplace("last-modified",
header_value{http_date(stbuf.st_mtime)});
} }
res->write_head(200, std::move(headers)); res.write_head(200, std::move(header));
res->end(file_reader_from_fd(fd)); res.end(file_generator_from_fd(fd));
}); });
if (argc >= 7) {
boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23);
tls.use_private_key_file(argv[5], boost::asio::ssl::context::pem);
tls.use_certificate_chain_file(argv[6]);
configure_tls_context_easy(ec, tls);
if (server.listen_and_serve(ec, tls, addr, port)) {
std::cerr << "error: " << ec.message() << std::endl;
}
} else {
if (server.listen_and_serve(ec, addr, port)) {
std::cerr << "error: " << ec.message() << std::endl;
}
}
} catch (std::exception &e) { } catch (std::exception &e) {
std::cerr << "exception: " << e.what() << "\n"; std::cerr << "exception: " << e.what() << "\n";
} }

View File

@@ -1,142 +0,0 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2014 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
// We wrote this code based on the original code which has the
// following license:
//
// main.cpp
// ~~~~~~~~
//
// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include <unistd.h>
#include <iostream>
#include <string>
#include <deque>
#include <nghttp2/asio_http2.h>
using namespace nghttp2::asio_http2;
using namespace nghttp2::asio_http2::server;
int main(int argc, char *argv[]) {
try {
// Check command line arguments.
if (argc < 4) {
std::cerr << "Usage: asio-sv3 <port> <threads> <tasks> "
<< " <private-key-file> <cert-file>\n";
return 1;
}
uint16_t port = std::stoi(argv[1]);
std::size_t num_threads = std::stoi(argv[2]);
std::size_t num_concurrent_tasks = std::stoi(argv[3]);
http2 server;
server.num_threads(num_threads);
if (argc >= 5) {
server.tls(argv[4], argv[5]);
}
server.num_concurrent_tasks(num_concurrent_tasks);
server.listen("*", port, [](const std::shared_ptr<request> &req,
const std::shared_ptr<response> &res) {
res->write_head(200);
auto msgq = std::make_shared<std::deque<std::string>>();
res->end([msgq](uint8_t * buf, std::size_t len)
-> std::pair<ssize_t, bool> {
if (msgq->empty()) {
// if msgq is empty, tells the library that don't call
// this callback until we call res->resume(). This is
// done by returing std::make_pair(0, false).
return std::make_pair(0, false);
}
auto msg = std::move(msgq->front());
msgq->pop_front();
if (msg.empty()) {
// The empty message signals the end of response in
// this simple protocol.
return std::make_pair(0, true);
}
auto nwrite = std::min(len, msg.size());
std::copy(std::begin(msg), std::begin(msg) + nwrite, buf);
if (msg.size() > nwrite) {
msgq->push_front(msg.substr(nwrite));
}
return std::make_pair(nwrite, false);
});
req->run_task([res, msgq](channel &channel) {
// executed in different thread from request callback
// was called.
// Using res and msgq is not safe inside this callback.
// But using them in callback passed to channel::post is
// safe.
// We just emit simple message "message N\n" in every 1
// second and 3 times in total.
for (std::size_t i = 0; i < 3; ++i) {
msgq->push_back("message " + std::to_string(i + 1) + "\n");
channel.post([res]() {
// executed in same thread where
// request callback was called.
// Tells library we have new message.
res->resume();
});
sleep(1);
}
// Send empty message to signal the end of response
// body.
msgq->push_back("");
channel.post([res]() {
// executed in same thread where request
// callback was called.
res->resume();
});
});
});
} catch (std::exception &e) {
std::cerr << "exception: " << e.what() << "\n";
}
return 0;
}

View File

@@ -77,7 +77,7 @@ int lookup_token(const uint8_t *name, size_t namelen) {
print '''\ print '''\
case {}:'''.format(size) case {}:'''.format(size)
print '''\ print '''\
switch (name[namelen - 1]) {''' switch (name[{}]) {{'''.format(size - 1)
for c in sorted(ents.keys()): for c in sorted(ents.keys()):
headers = sorted(ents[c]) headers = sorted(ents[c])
print '''\ print '''\

View File

@@ -59,7 +59,7 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
print '''\ print '''\
case {}:'''.format(size) case {}:'''.format(size)
print '''\ print '''\
switch (name[namelen - 1]) {''' switch (name[{}]) {{'''.format(size - 1)
for c in sorted(ents.keys()): for c in sorted(ents.keys()):
headers = sorted(ents[c]) headers = sorted(ents[c])
print '''\ print '''\

View File

@@ -211,6 +211,41 @@ func TestH1H1HTTP10NoHostRewrite(t *testing.T) {
} }
} }
// TestH1H1RequestTrailer tests request trailer part is forwarded to
// backend.
func TestH1H1RequestTrailer(t *testing.T) {
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
buf := make([]byte, 4096)
for {
_, err := r.Body.Read(buf)
if err == io.EOF {
break
}
if err != nil {
t.Fatalf("r.Body.Read() = %v", err)
}
}
if got, want := r.Trailer.Get("foo"), "bar"; got != want {
t.Errorf("r.Trailer.Get(foo): %v; want %v", got, want)
}
})
defer st.Close()
res, err := st.http1(requestParam{
name: "TestH1H1RequestTrailer",
body: []byte("1"),
trailer: []hpack.HeaderField{
pair("foo", "bar"),
},
})
if err != nil {
t.Fatalf("Error st.http1() = %v", err)
}
if got, want := res.status, 200; got != want {
t.Errorf("res.status: %v; want %v", got, want)
}
}
// TestH1H2ConnectFailure tests that server handles the situation that // TestH1H2ConnectFailure tests that server handles the situation that
// connection attempt to HTTP/2 backend failed. // connection attempt to HTTP/2 backend failed.
func TestH1H2ConnectFailure(t *testing.T) { func TestH1H2ConnectFailure(t *testing.T) {

View File

@@ -520,6 +520,41 @@ func TestH2H1ServerPush(t *testing.T) {
} }
} }
// TestH2H1RequestTrailer tests request trailer part is forwarded to
// backend.
func TestH2H1RequestTrailer(t *testing.T) {
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
buf := make([]byte, 4096)
for {
_, err := r.Body.Read(buf)
if err == io.EOF {
break
}
if err != nil {
t.Fatalf("r.Body.Read() = %v", err)
}
}
if got, want := r.Trailer.Get("foo"), "bar"; got != want {
t.Errorf("r.Trailer.Get(foo): %v; want %v", got, want)
}
})
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H1RequestTrailer",
body: []byte("1"),
trailer: []hpack.HeaderField{
pair("foo", "bar"),
},
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
if got, want := res.status, 200; got != want {
t.Errorf("res.status: %v; want %v", got, want)
}
}
// TestH2H1GracefulShutdown tests graceful shutdown. // TestH2H1GracefulShutdown tests graceful shutdown.
func TestH2H1GracefulShutdown(t *testing.T) { func TestH2H1GracefulShutdown(t *testing.T) {
st := newServerTester(nil, t, noopHandler) st := newServerTester(nil, t, noopHandler)

View File

@@ -255,6 +255,27 @@ type requestParam struct {
path string // path, defaults to / path string // path, defaults to /
header []hpack.HeaderField // additional request header fields header []hpack.HeaderField // additional request header fields
body []byte // request body body []byte // request body
trailer []hpack.HeaderField // trailer part
}
// wrapper for request body to set trailer part
type chunkedBodyReader struct {
trailer []hpack.HeaderField
trailerWritten bool
body io.Reader
req *http.Request
}
func (cbr *chunkedBodyReader) Read(p []byte) (n int, err error) {
// document says that we have to set http.Request.Trailer
// after request was sent and before body returns EOF.
if !cbr.trailerWritten {
cbr.trailerWritten = true
for _, h := range cbr.trailer {
cbr.req.Trailer.Set(h.Name, h.Value)
}
}
return cbr.body.Read(p)
} }
func (st *serverTester) http1(rp requestParam) (*serverResponse, error) { func (st *serverTester) http1(rp requestParam) (*serverResponse, error) {
@@ -264,8 +285,16 @@ func (st *serverTester) http1(rp requestParam) (*serverResponse, error) {
} }
var body io.Reader var body io.Reader
var cbr *chunkedBodyReader
if rp.body != nil { if rp.body != nil {
body = bytes.NewBuffer(rp.body) body = bytes.NewBuffer(rp.body)
if len(rp.trailer) != 0 {
cbr = &chunkedBodyReader{
trailer: rp.trailer,
body: body,
}
body = cbr
}
} }
req, err := http.NewRequest(method, st.url, body) req, err := http.NewRequest(method, st.url, body)
if err != nil { if err != nil {
@@ -275,7 +304,15 @@ func (st *serverTester) http1(rp requestParam) (*serverResponse, error) {
req.Header.Add(h.Name, h.Value) req.Header.Add(h.Name, h.Value)
} }
req.Header.Add("Test-Case", rp.name) req.Header.Add("Test-Case", rp.name)
if cbr != nil {
cbr.req = req
// this makes request use chunked encoding
req.ContentLength = -1
req.Trailer = make(http.Header)
for _, h := range cbr.trailer {
req.Trailer.Set(h.Name, "")
}
}
if err := req.Write(st.conn); err != nil { if err := req.Write(st.conn); err != nil {
return nil, err return nil, err
} }
@@ -473,7 +510,7 @@ func (st *serverTester) http2(rp requestParam) (*serverResponse, error) {
err := st.fr.WriteHeaders(http2.HeadersFrameParam{ err := st.fr.WriteHeaders(http2.HeadersFrameParam{
StreamID: id, StreamID: id,
EndStream: len(rp.body) == 0, EndStream: len(rp.body) == 0 && len(rp.trailer) == 0,
EndHeaders: true, EndHeaders: true,
BlockFragment: st.headerBlkBuf.Bytes(), BlockFragment: st.headerBlkBuf.Bytes(),
}) })
@@ -483,7 +520,23 @@ func (st *serverTester) http2(rp requestParam) (*serverResponse, error) {
if len(rp.body) != 0 { if len(rp.body) != 0 {
// TODO we assume rp.body fits in 1 frame // TODO we assume rp.body fits in 1 frame
if err := st.fr.WriteData(id, true, rp.body); err != nil { 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 return nil, err
} }
} }

View File

@@ -402,21 +402,27 @@ typedef enum {
*/ */
typedef struct { typedef struct {
/** /**
* The |name| byte string, which is not necessarily ``NULL`` * The |name| byte string. If this struct is presented from library
* terminated. * (e.g., :type:`nghttp2_on_frame_recv_callback`), |name| is
* guaranteed to be NULL-terminated. When application is
* constructing this struct, |name| is not required to be
* NULL-terminated.
*/ */
uint8_t *name; uint8_t *name;
/** /**
* The |value| byte string, which is not necessarily ``NULL`` * The |value| byte string. If this struct is presented from
* terminated. * library (e.g., :type:`nghttp2_on_frame_recv_callback`), |value|
* is guaranteed to be NULL-terminated. When application is
* constructing this struct, |value| is not required to be
* NULL-terminated.
*/ */
uint8_t *value; uint8_t *value;
/** /**
* The length of the |name|. * The length of the |name|, excluding terminating NULL.
*/ */
size_t namelen; size_t namelen;
/** /**
* The length of the |value|. * The length of the |value|, excluding terminating NULL.
*/ */
size_t valuelen; size_t valuelen;
/** /**
@@ -468,7 +474,9 @@ typedef enum {
*/ */
NGHTTP2_WINDOW_UPDATE = 0x08, NGHTTP2_WINDOW_UPDATE = 0x08,
/** /**
* The CONTINUATION frame. * The CONTINUATION frame. This frame type won't be passed to any
* callbacks because the library processes this frame type and its
* preceding HEADERS/PUSH_PROMISE as a single frame.
*/ */
NGHTTP2_CONTINUATION = 0x09 NGHTTP2_CONTINUATION = 0x09
} nghttp2_frame_type; } nghttp2_frame_type;
@@ -682,7 +690,14 @@ typedef enum {
/** /**
* Indicates EOF was sensed. * Indicates EOF was sensed.
*/ */
NGHTTP2_DATA_FLAG_EOF = 0x01 NGHTTP2_DATA_FLAG_EOF = 0x01,
/**
* Indicates that END_STREAM flag must not be set even if
* NGHTTP2_DATA_FLAG_EOF is set. Usually this flag is used to send
* trailer header fields with `nghttp2_submit_request()` or
* `nghttp2_submit_response()`.
*/
NGHTTP2_DATA_FLAG_NO_END_STREAM = 0x02
} nghttp2_data_flag; } nghttp2_data_flag;
/** /**
@@ -695,6 +710,21 @@ typedef enum {
* them in |buf| and return number of data stored in |buf|. If EOF is * them in |buf| and return number of data stored in |buf|. If EOF is
* reached, set :enum:`NGHTTP2_DATA_FLAG_EOF` flag in |*data_flags|. * reached, set :enum:`NGHTTP2_DATA_FLAG_EOF` flag in |*data_flags|.
* *
* If this callback is set by `nghttp2_submit_request()`,
* `nghttp2_submit_response()` or `nghttp2_submit_headers()` and
* `nghttp2_submit_data()` with flag parameter
* :enum:`NGHTTP2_FLAG_END_STREAM` set, and
* :enum:`NGHTTP2_DATA_FLAG_EOF` flag is set to |*data_flags|, DATA
* frame will have END_STREAM flag set. Usually, this is expected
* behaviour and all are fine. One exception is send trailer header
* fields. You cannot send trailers after sending frame with
* END_STREAM set. To avoid this problem, one can set
* :enum:`NGHTTP2_DATA_FLAG_NO_END_STREAM` along with
* :enum:`NGHTTP2_DATA_FLAG_EOF` to signal the library not to set
* END_STREAM in DATA frame. Then application can use
* `nghttp2_submit_trailer()` to send trailers.
* `nghttp2_submit_trailer()` can be called inside this callback.
*
* If the application wants to postpone DATA frames (e.g., * If the application wants to postpone DATA frames (e.g.,
* asynchronous I/O, or reading data blocks for long time), it is * asynchronous I/O, or reading data blocks for long time), it is
* achieved by returning :enum:`NGHTTP2_ERR_DEFERRED` without reading * achieved by returning :enum:`NGHTTP2_ERR_DEFERRED` without reading
@@ -1437,7 +1467,11 @@ typedef int (*nghttp2_on_begin_headers_callback)(nghttp2_session *session,
* :type:`nghttp2_on_frame_recv_callback` for the |frame| will not be * :type:`nghttp2_on_frame_recv_callback` for the |frame| will not be
* invoked. * invoked.
* *
* The |value| may be ``NULL`` if the |valuelen| is 0. * Both |name| and |value| are guaranteed to be NULL-terminated. The
* |namelen| and |valuelen| do not include terminal NULL. If
* `nghttp2_option_set_no_http_messaging()` is used with nonzero
* value, NULL character may be included in |name| or |value| before
* terminating NULL.
* *
* Please note that unless `nghttp2_option_set_no_http_messaging()` is * Please note that unless `nghttp2_option_set_no_http_messaging()` is
* used, nghttp2 library does perform validation against the |name| * used, nghttp2 library does perform validation against the |name|
@@ -2834,6 +2868,53 @@ int nghttp2_submit_response(nghttp2_session *session, int32_t stream_id,
const nghttp2_nv *nva, size_t nvlen, const nghttp2_nv *nva, size_t nvlen,
const nghttp2_data_provider *data_prd); const nghttp2_data_provider *data_prd);
/**
* @function
*
* Submits trailer HEADERS against the stream |stream_id|.
*
* The |nva| is an array of name/value pair :type:`nghttp2_nv` with
* |nvlen| elements. The application is responsible not to include
* required pseudo-header fields (header field whose name starts with
* ":") in |nva|.
*
* This function creates copies of all name/value pairs in |nva|. It
* also lower-cases all names in |nva|. The order of elements in
* |nva| is preserved.
*
* For server, trailer must be followed by response HEADERS or
* response DATA. The library does not check that response HEADERS
* has already sent and if `nghttp2_submit_trailer()` is called before
* any response HEADERS submission (usually by
* `nghttp2_submit_response()`), the content of |nva| will be sent as
* reponse headers, which will result in error.
*
* This function has the same effect with `nghttp2_submit_headers()`,
* with flags = :enum:`NGHTTP2_FLAG_END_HEADERS` and both pri_spec and
* stream_user_data to NULL.
*
* To submit trailer after `nghttp2_submit_response()` is called, the
* application has to specify :type:`nghttp2_data_provider` to
* `nghttp2_submit_response()`. In side
* :type:`nghttp2_data_source_read_callback`, when setting
* :enum:`NGHTTP2_DATA_FLAG_EOF`, also set
* :enum:`NGHTTP2_DATA_FLAG_NO_END_STREAM`. After that, the
* application can send trailer using `nghttp2_submit_trailer()`.
* `nghttp2_submit_trailer()` can be used inside
* :type:`nghttp2_data_source_read_callback`.
*
* This function returns 0 if it succeeds and |stream_id| is -1.
* Otherwise, this function returns 0 if it succeeds, or one of the
* following negative error codes:
*
* :enum:`NGHTTP2_ERR_NOMEM`
* Out of memory.
* :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
* The |stream_id| is 0.
*/
int nghttp2_submit_trailer(nghttp2_session *session, int32_t stream_id,
const nghttp2_nv *nva, size_t nvlen);
/** /**
* @function * @function
* *

View File

@@ -410,31 +410,23 @@ ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out) {
len += nghttp2_buf_len(&chain->buf); len += nghttp2_buf_len(&chain->buf);
} }
if (!len) { if (len == 0) {
res = NULL; res = NULL;
} else { return 0;
res = nghttp2_mem_malloc(bufs->mem, len); }
if (res == NULL) { res = nghttp2_mem_malloc(bufs->mem, len);
return NGHTTP2_ERR_NOMEM; if (res == NULL) {
} return NGHTTP2_ERR_NOMEM;
} }
nghttp2_buf_wrap_init(&resbuf, res, len); nghttp2_buf_wrap_init(&resbuf, res, len);
for (chain = bufs->head; chain; chain = chain->next) { for (chain = bufs->head; chain; chain = chain->next) {
buf = &chain->buf; buf = &chain->buf;
resbuf.last = nghttp2_cpymem(resbuf.last, buf->pos, nghttp2_buf_len(buf));
if (resbuf.last) {
resbuf.last = nghttp2_cpymem(resbuf.last, buf->pos, nghttp2_buf_len(buf));
}
nghttp2_buf_reset(buf);
nghttp2_buf_shift_right(&chain->buf, bufs->offset);
} }
bufs->cur = bufs->head;
*out = res; *out = res;
return (ssize_t)len; return (ssize_t)len;

View File

@@ -313,9 +313,8 @@ int nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b);
* function allocates the contagious memory to store all data in * function allocates the contagious memory to store all data in
* |bufs| and assigns it to |*out|. * |bufs| and assigns it to |*out|.
* *
* On successful return, nghttp2_bufs_len(bufs) returns 0, just like * The contents of |bufs| is left unchanged.
* after calling nghttp2_bufs_reset(). *
* This function returns the length of copied data and assigns the * This function returns the length of copied data and assigns the
* pointer to copied data to |*out| if it succeeds, or one of the * pointer to copied data to |*out| if it succeeds, or one of the
* following negative error codes: * following negative error codes:

View File

@@ -739,7 +739,8 @@ int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, const nghttp2_nv *nva,
nghttp2_nv *p; nghttp2_nv *p;
for (i = 0; i < nvlen; ++i) { for (i = 0; i < nvlen; ++i) {
buflen += nva[i].namelen + nva[i].valuelen; /* + 2 for null-termination */
buflen += nva[i].namelen + nva[i].valuelen + 2;
} }
if (nvlen == 0) { if (nvlen == 0) {
@@ -765,12 +766,14 @@ int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, const nghttp2_nv *nva,
memcpy(data, nva[i].name, nva[i].namelen); memcpy(data, nva[i].name, nva[i].namelen);
p->name = data; p->name = data;
p->namelen = nva[i].namelen; p->namelen = nva[i].namelen;
data[p->namelen] = '\0';
nghttp2_downcase(p->name, p->namelen); nghttp2_downcase(p->name, p->namelen);
data += nva[i].namelen; data += nva[i].namelen + 1;
memcpy(data, nva[i].value, nva[i].valuelen); memcpy(data, nva[i].value, nva[i].valuelen);
p->value = data; p->value = data;
p->valuelen = nva[i].valuelen; p->valuelen = nva[i].valuelen;
data += nva[i].valuelen; data[p->valuelen] = '\0';
data += nva[i].valuelen + 1;
++p; ++p;
} }
return 0; return 0;

View File

@@ -471,7 +471,9 @@ void nghttp2_nv_array_sort(nghttp2_nv *nva, size_t nvlen);
/* /*
* Copies name/value pairs from |nva|, which contains |nvlen| pairs, * Copies name/value pairs from |nva|, which contains |nvlen| pairs,
* to |*nva_ptr|, which is dynamically allocated so that all items can * to |*nva_ptr|, which is dynamically allocated so that all items can
* be stored. * be stored. The resultant name and value in nghttp2_nv are
* guaranteed to be NULL-terminated even if the input is not
* null-terminated.
* *
* The |*nva_ptr| must be freed using nghttp2_nv_array_del(). * The |*nva_ptr| must be freed using nghttp2_nv_array_del().
* *

View File

@@ -152,10 +152,11 @@ int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t flags, uint8_t *name,
if ((flags & NGHTTP2_HD_FLAG_NAME_ALLOC) && if ((flags & NGHTTP2_HD_FLAG_NAME_ALLOC) &&
(flags & NGHTTP2_HD_FLAG_NAME_GIFT) == 0) { (flags & NGHTTP2_HD_FLAG_NAME_GIFT) == 0) {
if (namelen == 0) { if (namelen == 0) {
/* We should not allow empty header field name */ flags &= ~NGHTTP2_HD_FLAG_NAME_ALLOC;
ent->nv.name = NULL; ent->nv.name = (uint8_t *)"";
} else { } else {
ent->nv.name = nghttp2_memdup(name, namelen, mem); /* copy including terminating NULL byte */
ent->nv.name = nghttp2_memdup(name, namelen + 1, mem);
if (ent->nv.name == NULL) { if (ent->nv.name == NULL) {
rv = NGHTTP2_ERR_NOMEM; rv = NGHTTP2_ERR_NOMEM;
goto fail; goto fail;
@@ -167,9 +168,11 @@ int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t flags, uint8_t *name,
if ((flags & NGHTTP2_HD_FLAG_VALUE_ALLOC) && if ((flags & NGHTTP2_HD_FLAG_VALUE_ALLOC) &&
(flags & NGHTTP2_HD_FLAG_VALUE_GIFT) == 0) { (flags & NGHTTP2_HD_FLAG_VALUE_GIFT) == 0) {
if (valuelen == 0) { if (valuelen == 0) {
ent->nv.value = NULL; flags &= ~NGHTTP2_HD_FLAG_VALUE_ALLOC;
ent->nv.value = (uint8_t *)"";
} else { } else {
ent->nv.value = nghttp2_memdup(value, valuelen, mem); /* copy including terminating NULL byte */
ent->nv.value = nghttp2_memdup(value, valuelen + 1, mem);
if (ent->nv.value == NULL) { if (ent->nv.value == NULL) {
rv = NGHTTP2_ERR_NOMEM; rv = NGHTTP2_ERR_NOMEM;
goto fail2; goto fail2;
@@ -404,11 +407,8 @@ static size_t entry_room(size_t namelen, size_t valuelen) {
} }
static int emit_indexed_header(nghttp2_nv *nv_out, nghttp2_hd_entry *ent) { static int emit_indexed_header(nghttp2_nv *nv_out, nghttp2_hd_entry *ent) {
DEBUGF(fprintf(stderr, "inflatehd: header emission: ")); DEBUGF(fprintf(stderr, "inflatehd: header emission: %s: %s\n", ent->nv.name,
DEBUGF(fwrite(ent->nv.name, ent->nv.namelen, 1, stderr)); ent->nv.value));
DEBUGF(fprintf(stderr, ": "));
DEBUGF(fwrite(ent->nv.value, ent->nv.valuelen, 1, stderr));
DEBUGF(fprintf(stderr, "\n"));
/* ent->ref may be 0. This happens if the encoder emits literal /* ent->ref may be 0. This happens if the encoder emits literal
block larger than header table capacity with indexing. */ block larger than header table capacity with indexing. */
*nv_out = ent->nv; *nv_out = ent->nv;
@@ -416,11 +416,8 @@ static int emit_indexed_header(nghttp2_nv *nv_out, nghttp2_hd_entry *ent) {
} }
static int emit_literal_header(nghttp2_nv *nv_out, nghttp2_nv *nv) { static int emit_literal_header(nghttp2_nv *nv_out, nghttp2_nv *nv) {
DEBUGF(fprintf(stderr, "inflatehd: header emission: ")); DEBUGF(fprintf(stderr, "inflatehd: header emission: %s: %s\n", nv->name,
DEBUGF(fwrite(nv->name, nv->namelen, 1, stderr)); nv->value));
DEBUGF(fprintf(stderr, ": "));
DEBUGF(fwrite(nv->value, nv->valuelen, 1, stderr));
DEBUGF(fprintf(stderr, "\n"));
*nv_out = *nv; *nv_out = *nv;
return 0; return 0;
} }
@@ -750,11 +747,9 @@ static nghttp2_hd_entry *add_hd_table_incremental(nghttp2_hd_context *context,
context->hd_table_bufsize -= entry_room(ent->nv.namelen, ent->nv.valuelen); context->hd_table_bufsize -= entry_room(ent->nv.namelen, ent->nv.valuelen);
DEBUGF(fprintf(stderr, "hpack: remove item from header table: ")); DEBUGF(fprintf(stderr, "hpack: remove item from header table: %s: %s\n",
DEBUGF(fwrite(ent->nv.name, ent->nv.namelen, 1, stderr)); ent->nv.name, ent->nv.value));
DEBUGF(fprintf(stderr, ": "));
DEBUGF(fwrite(ent->nv.value, ent->nv.valuelen, 1, stderr));
DEBUGF(fprintf(stderr, "\n"));
hd_ringbuf_pop_back(&context->hd_table); hd_ringbuf_pop_back(&context->hd_table);
if (--ent->ref == 0) { if (--ent->ref == 0) {
nghttp2_hd_entry_free(ent, mem); nghttp2_hd_entry_free(ent, mem);
@@ -975,11 +970,7 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs,
uint32_t value_hash = hash(nv->value, nv->valuelen); uint32_t value_hash = hash(nv->value, nv->valuelen);
nghttp2_mem *mem; nghttp2_mem *mem;
DEBUGF(fprintf(stderr, "deflatehd: deflating ")); DEBUGF(fprintf(stderr, "deflatehd: deflating %s: %s\n", nv->name, nv->value));
DEBUGF(fwrite(nv->name, nv->namelen, 1, stderr));
DEBUGF(fprintf(stderr, ": "));
DEBUGF(fwrite(nv->value, nv->valuelen, 1, stderr));
DEBUGF(fprintf(stderr, "\n"));
mem = deflater->ctx.mem; mem = deflater->ctx.mem;
@@ -1337,18 +1328,24 @@ static int hd_inflate_remove_bufs(nghttp2_hd_inflater *inflater, nghttp2_nv *nv,
return NGHTTP2_ERR_NOMEM; return NGHTTP2_ERR_NOMEM;
} }
nghttp2_bufs_reset(&inflater->nvbufs);
buflen = rv; buflen = rv;
if (value_only) { if (value_only) {
/* we don't use this value, so no need to NULL-terminate */
nv->name = NULL; nv->name = NULL;
nv->namelen = 0; nv->namelen = 0;
nv->value = buf;
nv->valuelen = buflen - 1;
} else { } else {
nv->name = buf; nv->name = buf;
nv->namelen = inflater->newnamelen; nv->namelen = inflater->newnamelen;
}
nv->value = buf + nv->namelen; nv->value = buf + nv->namelen + 1;
nv->valuelen = buflen - nv->namelen; nv->valuelen = buflen - nv->namelen - 2;
}
return 0; return 0;
} }
@@ -1360,15 +1357,19 @@ static int hd_inflate_remove_bufs(nghttp2_hd_inflater *inflater, nghttp2_nv *nv,
pbuf = &inflater->nvbufs.head->buf; pbuf = &inflater->nvbufs.head->buf;
if (value_only) { if (value_only) {
/* we don't use this value, so no need to NULL-terminate */
nv->name = NULL; nv->name = NULL;
nv->namelen = 0; nv->namelen = 0;
nv->value = pbuf->pos;
nv->valuelen = nghttp2_buf_len(pbuf) - 1;
} else { } else {
nv->name = pbuf->pos; nv->name = pbuf->pos;
nv->namelen = inflater->newnamelen; nv->namelen = inflater->newnamelen;
}
nv->value = pbuf->pos + nv->namelen; nv->value = pbuf->pos + nv->namelen + 1;
nv->valuelen = nghttp2_buf_len(pbuf) - nv->namelen; nv->valuelen = nghttp2_buf_len(pbuf) - nv->namelen - 2;
}
/* Resetting does not change the content of first buffer */ /* Resetting does not change the content of first buffer */
nghttp2_bufs_reset(&inflater->nvbufs); nghttp2_bufs_reset(&inflater->nvbufs);
@@ -1528,6 +1529,7 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out,
uint8_t *first = in; uint8_t *first = in;
uint8_t *last = in + inlen; uint8_t *last = in + inlen;
int rfin = 0; int rfin = 0;
int busy = 0;
if (inflater->ctx.bad) { if (inflater->ctx.bad) {
return NGHTTP2_ERR_HEADER_COMP; return NGHTTP2_ERR_HEADER_COMP;
@@ -1536,7 +1538,8 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out,
DEBUGF(fprintf(stderr, "inflatehd: start state=%d\n", inflater->state)); DEBUGF(fprintf(stderr, "inflatehd: start state=%d\n", inflater->state));
hd_inflate_keep_free(inflater); hd_inflate_keep_free(inflater);
*inflate_flags = NGHTTP2_HD_INFLATE_NONE; *inflate_flags = NGHTTP2_HD_INFLATE_NONE;
for (; in != last;) { for (; in != last || busy;) {
busy = 0;
switch (inflater->state) { switch (inflater->state) {
case NGHTTP2_HD_STATE_OPCODE: case NGHTTP2_HD_STATE_OPCODE:
if ((*in & 0xe0u) == 0x20u) { if ((*in & 0xe0u) == 0x20u) {
@@ -1688,6 +1691,11 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out,
inflater->newnamelen = nghttp2_bufs_len(&inflater->nvbufs); inflater->newnamelen = nghttp2_bufs_len(&inflater->nvbufs);
rv = nghttp2_bufs_addb(&inflater->nvbufs, '\0');
if (rv != 0) {
goto fail;
}
inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN; inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN;
break; break;
@@ -1709,6 +1717,11 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out,
inflater->newnamelen = nghttp2_bufs_len(&inflater->nvbufs); inflater->newnamelen = nghttp2_bufs_len(&inflater->nvbufs);
rv = nghttp2_bufs_addb(&inflater->nvbufs, '\0');
if (rv != 0) {
goto fail;
}
inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN; inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN;
break; break;
@@ -1734,19 +1747,6 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out,
} }
DEBUGF(fprintf(stderr, "inflatehd: valuelen=%zu\n", inflater->left)); DEBUGF(fprintf(stderr, "inflatehd: valuelen=%zu\n", inflater->left));
if (inflater->left == 0) {
if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) {
rv = hd_inflate_commit_newname(inflater, nv_out);
} else {
rv = hd_inflate_commit_indname(inflater, nv_out);
}
if (rv != 0) {
goto fail;
}
inflater->state = NGHTTP2_HD_STATE_OPCODE;
*inflate_flags |= NGHTTP2_HD_INFLATE_EMIT;
return (ssize_t)(in - first);
}
if (inflater->huffman_encoded) { if (inflater->huffman_encoded) {
nghttp2_hd_huff_decode_context_init(&inflater->huff_decode_ctx); nghttp2_hd_huff_decode_context_init(&inflater->huff_decode_ctx);
@@ -1755,6 +1755,9 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out,
} else { } else {
inflater->state = NGHTTP2_HD_STATE_READ_VALUE; inflater->state = NGHTTP2_HD_STATE_READ_VALUE;
} }
busy = 1;
break; break;
case NGHTTP2_HD_STATE_READ_VALUEHUFF: case NGHTTP2_HD_STATE_READ_VALUEHUFF:
rv = hd_inflate_read_huff(inflater, &inflater->nvbufs, in, last); rv = hd_inflate_read_huff(inflater, &inflater->nvbufs, in, last);
@@ -1773,6 +1776,11 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out,
goto almost_ok; goto almost_ok;
} }
rv = nghttp2_bufs_addb(&inflater->nvbufs, '\0');
if (rv != 0) {
goto fail;
}
if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) { if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) {
rv = hd_inflate_commit_newname(inflater, nv_out); rv = hd_inflate_commit_newname(inflater, nv_out);
} else { } else {
@@ -1805,6 +1813,11 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out,
goto almost_ok; goto almost_ok;
} }
rv = nghttp2_bufs_addb(&inflater->nvbufs, '\0');
if (rv != 0) {
goto fail;
}
if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) { if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) {
rv = hd_inflate_commit_newname(inflater, nv_out); rv = hd_inflate_commit_newname(inflater, nv_out);
} else { } else {

View File

@@ -324,8 +324,7 @@ void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx);
* be initialized by nghttp2_hd_huff_decode_context_init(). The result * be initialized by nghttp2_hd_huff_decode_context_init(). The result
* will be added to |dest|. This function may expand |dest| as * will be added to |dest|. This function may expand |dest| as
* needed. The caller is responsible to release the memory of |dest| * needed. The caller is responsible to release the memory of |dest|
* by calling nghttp2_bufs_free() or export its content using * by calling nghttp2_bufs_free().
* nghttp2_bufs_remove().
* *
* The caller must set the |final| to nonzero if the given input is * The caller must set the |final| to nonzero if the given input is
* the final block. * the final block.

View File

@@ -76,7 +76,7 @@ typedef enum {
static int lookup_token(const uint8_t *name, size_t namelen) { static int lookup_token(const uint8_t *name, size_t namelen) {
switch (namelen) { switch (namelen) {
case 2: case 2:
switch (name[namelen - 1]) { switch (name[1]) {
case 'e': case 'e':
if (streq("t", name, 1)) { if (streq("t", name, 1)) {
return NGHTTP2_TOKEN_TE; return NGHTTP2_TOKEN_TE;
@@ -85,7 +85,7 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
} }
break; break;
case 4: case 4:
switch (name[namelen - 1]) { switch (name[3]) {
case 't': case 't':
if (streq("hos", name, 3)) { if (streq("hos", name, 3)) {
return NGHTTP2_TOKEN_HOST; return NGHTTP2_TOKEN_HOST;
@@ -94,7 +94,7 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
} }
break; break;
case 5: case 5:
switch (name[namelen - 1]) { switch (name[4]) {
case 'h': case 'h':
if (streq(":pat", name, 4)) { if (streq(":pat", name, 4)) {
return NGHTTP2_TOKEN__PATH; return NGHTTP2_TOKEN__PATH;
@@ -103,7 +103,7 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
} }
break; break;
case 7: case 7:
switch (name[namelen - 1]) { switch (name[6]) {
case 'd': case 'd':
if (streq(":metho", name, 6)) { if (streq(":metho", name, 6)) {
return NGHTTP2_TOKEN__METHOD; return NGHTTP2_TOKEN__METHOD;
@@ -125,7 +125,7 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
} }
break; break;
case 10: case 10:
switch (name[namelen - 1]) { switch (name[9]) {
case 'e': case 'e':
if (streq("keep-aliv", name, 9)) { if (streq("keep-aliv", name, 9)) {
return NGHTTP2_TOKEN_KEEP_ALIVE; return NGHTTP2_TOKEN_KEEP_ALIVE;
@@ -144,7 +144,7 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
} }
break; break;
case 14: case 14:
switch (name[namelen - 1]) { switch (name[13]) {
case 'h': case 'h':
if (streq("content-lengt", name, 13)) { if (streq("content-lengt", name, 13)) {
return NGHTTP2_TOKEN_CONTENT_LENGTH; return NGHTTP2_TOKEN_CONTENT_LENGTH;
@@ -153,7 +153,7 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
} }
break; break;
case 16: case 16:
switch (name[namelen - 1]) { switch (name[15]) {
case 'n': case 'n':
if (streq("proxy-connectio", name, 15)) { if (streq("proxy-connectio", name, 15)) {
return NGHTTP2_TOKEN_PROXY_CONNECTION; return NGHTTP2_TOKEN_PROXY_CONNECTION;
@@ -162,7 +162,7 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
} }
break; break;
case 17: case 17:
switch (name[namelen - 1]) { switch (name[16]) {
case 'g': case 'g':
if (streq("transfer-encodin", name, 16)) { if (streq("transfer-encodin", name, 16)) {
return NGHTTP2_TOKEN_TRANSFER_ENCODING; return NGHTTP2_TOKEN_TRANSFER_ENCODING;
@@ -225,6 +225,18 @@ static int expect_response_body(nghttp2_stream *stream) {
stream->status_code != 204; stream->status_code != 204;
} }
/* For "http" or "https" URIs, OPTIONS request may have "*" in :path
header field to represent system-wide OPTIONS request. Otherwise,
:path header field value must start with "/". This function must
be called after ":method" header field was received. This function
returns nonzero if path is valid.*/
static int check_path(nghttp2_stream *stream) {
return (stream->http_flags & NGHTTP2_HTTP_FLAG_SCHEME_HTTP) == 0 ||
((stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_REGULAR) ||
((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_OPTIONS) &&
(stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_ASTERISK)));
}
static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv, static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
int trailer) { int trailer) {
int token; int token;
@@ -248,18 +260,34 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__METHOD)) { if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__METHOD)) {
return NGHTTP2_ERR_HTTP_HEADER; return NGHTTP2_ERR_HTTP_HEADER;
} }
if (streq("HEAD", nv->value, nv->valuelen)) { switch (nv->valuelen) {
stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD; case 4:
} else if (streq("CONNECT", nv->value, nv->valuelen)) { if (streq("HEAD", nv->value, nv->valuelen)) {
if (stream->stream_id % 2 == 0) { stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
/* we won't allow CONNECT for push */
return NGHTTP2_ERR_HTTP_HEADER;
} }
stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT; break;
if (stream->http_flags & case 7:
(NGHTTP2_HTTP_FLAG__PATH | NGHTTP2_HTTP_FLAG__SCHEME)) { switch (nv->value[6]) {
return NGHTTP2_ERR_HTTP_HEADER; case 'T':
if (streq("CONNECT", nv->value, nv->valuelen)) {
if (stream->stream_id % 2 == 0) {
/* we won't allow CONNECT for push */
return NGHTTP2_ERR_HTTP_HEADER;
}
stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
if (stream->http_flags &
(NGHTTP2_HTTP_FLAG__PATH | NGHTTP2_HTTP_FLAG__SCHEME)) {
return NGHTTP2_ERR_HTTP_HEADER;
}
}
break;
case 'S':
if (streq("OPTIONS", nv->value, nv->valuelen)) {
stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_OPTIONS;
}
break;
} }
break;
} }
break; break;
case NGHTTP2_TOKEN__PATH: case NGHTTP2_TOKEN__PATH:
@@ -269,6 +297,11 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) { if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) {
return NGHTTP2_ERR_HTTP_HEADER; return NGHTTP2_ERR_HTTP_HEADER;
} }
if (nv->value[0] == '/') {
stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_REGULAR;
} else if (nv->valuelen == 1 && nv->value[0] == '*') {
stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_ASTERISK;
}
break; break;
case NGHTTP2_TOKEN__SCHEME: case NGHTTP2_TOKEN__SCHEME:
if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) { if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) {
@@ -277,6 +310,10 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) { if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) {
return NGHTTP2_ERR_HTTP_HEADER; return NGHTTP2_ERR_HTTP_HEADER;
} }
if ((nv->valuelen == 4 && memieq("http", nv->value, 4)) ||
(nv->valuelen == 5 && memieq("https", nv->value, 5))) {
stream->http_flags |= NGHTTP2_HTTP_FLAG_SCHEME_HTTP;
}
break; break;
case NGHTTP2_TOKEN_HOST: case NGHTTP2_TOKEN_HOST:
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG_HOST)) { if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG_HOST)) {
@@ -434,11 +471,16 @@ int nghttp2_http_on_request_headers(nghttp2_stream *stream,
return -1; return -1;
} }
stream->content_length = -1; stream->content_length = -1;
} else if ((stream->http_flags & NGHTTP2_HTTP_FLAG_REQ_HEADERS) != } else {
NGHTTP2_HTTP_FLAG_REQ_HEADERS || if ((stream->http_flags & NGHTTP2_HTTP_FLAG_REQ_HEADERS) !=
(stream->http_flags & NGHTTP2_HTTP_FLAG_REQ_HEADERS ||
(NGHTTP2_HTTP_FLAG__AUTHORITY | NGHTTP2_HTTP_FLAG_HOST)) == 0) { (stream->http_flags &
return -1; (NGHTTP2_HTTP_FLAG__AUTHORITY | NGHTTP2_HTTP_FLAG_HOST)) == 0) {
return -1;
}
if (!check_path(stream)) {
return -1;
}
} }
if (frame->hd.type == NGHTTP2_PUSH_PROMISE) { if (frame->hd.type == NGHTTP2_PUSH_PROMISE) {

View File

@@ -40,6 +40,10 @@ typedef uint32_t key_type;
typedef struct nghttp2_map_entry { typedef struct nghttp2_map_entry {
struct nghttp2_map_entry *next; struct nghttp2_map_entry *next;
key_type key; key_type key;
#if SIZEOF_INT_P == 4
/* we requires 8 bytes aligment */
int64_t pad;
#endif
} nghttp2_map_entry; } nghttp2_map_entry;
typedef struct { typedef struct {

View File

@@ -383,6 +383,7 @@ static int session_new(nghttp2_session **session_ptr,
(*session_ptr)->pending_local_max_concurrent_stream = (*session_ptr)->pending_local_max_concurrent_stream =
NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS; NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS;
(*session_ptr)->pending_enable_push = 1;
if (server) { if (server) {
(*session_ptr)->server = 1; (*session_ptr)->server = 1;
@@ -3901,6 +3902,7 @@ int nghttp2_session_update_local_settings(nghttp2_session *session,
session->pending_local_max_concurrent_stream = session->pending_local_max_concurrent_stream =
NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS; NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS;
session->pending_enable_push = 1;
return 0; return 0;
} }
@@ -4129,7 +4131,8 @@ int nghttp2_session_on_push_promise_received(nghttp2_session *session,
} }
session->last_recv_stream_id = frame->push_promise.promised_stream_id; session->last_recv_stream_id = frame->push_promise.promised_stream_id;
stream = nghttp2_session_get_stream(session, frame->hd.stream_id); stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
if (!stream || stream->state == NGHTTP2_STREAM_CLOSING) { if (!stream || stream->state == NGHTTP2_STREAM_CLOSING ||
!session->pending_enable_push) {
if (!stream) { if (!stream) {
if (session_detect_idle_stream(session, frame->hd.stream_id)) { if (session_detect_idle_stream(session, frame->hd.stream_id)) {
return session_inflate_handle_invalid_connection( return session_inflate_handle_invalid_connection(
@@ -6105,8 +6108,10 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags,
return rv; return rv;
} }
/* Extract NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS here and use /* Extract NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS and ENABLE_PUSH
it to refuse the incoming streams with RST_STREAM. */ here. We use it to refuse the incoming stream and PUSH_PROMISE
with RST_STREAM. */
for (i = niv; i > 0; --i) { for (i = niv; i > 0; --i) {
if (iv[i - 1].settings_id == NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS) { if (iv[i - 1].settings_id == NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS) {
session->pending_local_max_concurrent_stream = iv[i - 1].value; session->pending_local_max_concurrent_stream = iv[i - 1].value;
@@ -6114,6 +6119,13 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags,
} }
} }
for (i = niv; i > 0; --i) {
if (iv[i - 1].settings_id == NGHTTP2_SETTINGS_ENABLE_PUSH) {
session->pending_enable_push = iv[i - 1].value;
break;
}
}
return 0; return 0;
} }
@@ -6210,7 +6222,10 @@ int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs,
if (data_flags & NGHTTP2_DATA_FLAG_EOF) { if (data_flags & NGHTTP2_DATA_FLAG_EOF) {
aux_data->eof = 1; aux_data->eof = 1;
if (aux_data->flags & NGHTTP2_FLAG_END_STREAM) { /* If NGHTTP2_DATA_FLAG_NO_END_STREAM is set, don't set
NGHTTP2_FLAG_END_STREAM */
if ((aux_data->flags & NGHTTP2_FLAG_END_STREAM) &&
(data_flags & NGHTTP2_DATA_FLAG_NO_END_STREAM) == 0) {
frame->hd.flags |= NGHTTP2_FLAG_END_STREAM; frame->hd.flags |= NGHTTP2_FLAG_END_STREAM;
} }
} }

View File

@@ -253,6 +253,9 @@ struct nghttp2_session {
/* Unacked local SETTINGS_MAX_CONCURRENT_STREAMS value. We use this /* Unacked local SETTINGS_MAX_CONCURRENT_STREAMS value. We use this
to refuse the incoming stream if it exceeds this value. */ to refuse the incoming stream if it exceeds this value. */
uint32_t pending_local_max_concurrent_stream; uint32_t pending_local_max_concurrent_stream;
/* Unacked local ENABLE_PUSH value. We use this to refuse
PUSH_PROMISE before SETTINGS ACK is received. */
uint8_t pending_enable_push;
/* Nonzero if the session is server side. */ /* Nonzero if the session is server side. */
uint8_t server; uint8_t server;
/* Flags indicating GOAWAY is sent and/or recieved. The flags are /* Flags indicating GOAWAY is sent and/or recieved. The flags are

View File

@@ -119,6 +119,7 @@ static int stream_push_item(nghttp2_stream *stream, nghttp2_session *session) {
} }
break; break;
default: default:
rv = 0;
/* should not reach here */ /* should not reach here */
assert(0); assert(0);
} }

View File

@@ -120,10 +120,20 @@ typedef enum {
/* HTTP method flags */ /* HTTP method flags */
NGHTTP2_HTTP_FLAG_METH_CONNECT = 1 << 7, NGHTTP2_HTTP_FLAG_METH_CONNECT = 1 << 7,
NGHTTP2_HTTP_FLAG_METH_HEAD = 1 << 8, NGHTTP2_HTTP_FLAG_METH_HEAD = 1 << 8,
NGHTTP2_HTTP_FLAG_METH_ALL = NGHTTP2_HTTP_FLAG_METH_OPTIONS = 1 << 9,
NGHTTP2_HTTP_FLAG_METH_CONNECT | NGHTTP2_HTTP_FLAG_METH_HEAD, NGHTTP2_HTTP_FLAG_METH_ALL = NGHTTP2_HTTP_FLAG_METH_CONNECT |
NGHTTP2_HTTP_FLAG_METH_HEAD |
NGHTTP2_HTTP_FLAG_METH_OPTIONS,
/* :path category */
/* path starts with "/" */
NGHTTP2_HTTP_FLAG_PATH_REGULAR = 1 << 10,
/* path "*" */
NGHTTP2_HTTP_FLAG_PATH_ASTERISK = 1 << 11,
/* scheme */
/* "http" or "https" scheme */
NGHTTP2_HTTP_FLAG_SCHEME_HTTP = 1 << 12,
/* set if final response is expected */ /* set if final response is expected */
NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE = 1 << 9, NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE = 1 << 13,
} nghttp2_http_flag; } nghttp2_http_flag;
typedef enum { typedef enum {

View File

@@ -155,6 +155,12 @@ static int32_t submit_headers_shared_nva(nghttp2_session *session,
attach_stream); attach_stream);
} }
int32_t nghttp2_submit_trailer(nghttp2_session *session, int32_t stream_id,
const nghttp2_nv *nva, size_t nvlen) {
return submit_headers_shared_nva(session, NGHTTP2_FLAG_END_STREAM, stream_id,
NULL, nva, nvlen, NULL, NULL, 0);
}
int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags, int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags,
int32_t stream_id, int32_t stream_id,
const nghttp2_priority_spec *pri_spec, const nghttp2_priority_spec *pri_spec,

View File

@@ -10,7 +10,7 @@
# #
# * The options which do not take argument in the command-line *take* # * The options which do not take argument in the command-line *take*
# argument in the configuration file. Specify 'yes' as argument # argument in the configuration file. Specify 'yes' as argument
# (e.g., spdy-proxy=yes). If other string is given, it disables the # (e.g., http2-proxy=yes). If other string is given, it disables the
# option. # option.
# #
# * To specify private key and certificate file, use private-key-file # * To specify private key and certificate file, use private-key-file
@@ -25,5 +25,5 @@
# backend=127.0.0.1,80 # backend=127.0.0.1,80
# private-key-file=/path/to/server.key # private-key-file=/path/to/server.key
# certificate-file=/path/to/server.crt # certificate-file=/path/to/server.crt
# spdy-proxy=no # http2-proxy=no
# workers=1 # workers=1

View File

@@ -322,8 +322,6 @@ cdef int client_on_header(cnghttp2.nghttp2_session *session,
logging.debug('client_on_header, type:%s, stream_id:%s', frame.hd.type, frame.hd.stream_id) logging.debug('client_on_header, type:%s, stream_id:%s', frame.hd.type, frame.hd.stream_id)
if frame.hd.type == cnghttp2.NGHTTP2_HEADERS: if frame.hd.type == cnghttp2.NGHTTP2_HEADERS:
if frame.headers.cat == cnghttp2.NGHTTP2_HCAT_REQUEST:
return 0
handler = _get_stream_user_data(session, frame.hd.stream_id) handler = _get_stream_user_data(session, frame.hd.stream_id)
elif frame.hd.type == cnghttp2.NGHTTP2_PUSH_PROMISE: elif frame.hd.type == cnghttp2.NGHTTP2_PUSH_PROMISE:
handler = _get_stream_user_data(session, frame.push_promise.promised_stream_id) handler = _get_stream_user_data(session, frame.push_promise.promised_stream_id)
@@ -545,7 +543,7 @@ cdef int client_on_frame_recv(cnghttp2.nghttp2_session *session,
sys.stderr.write(traceback.format_exc()) sys.stderr.write(traceback.format_exc())
return http2._rst_stream(frame.hd.stream_id) return http2._rst_stream(frame.hd.stream_id)
elif frame.hd.type == cnghttp2.NGHTTP2_HEADERS: elif frame.hd.type == cnghttp2.NGHTTP2_HEADERS:
if frame.headers.cat == cnghttp2.NGHTTP2_HCAT_RESPONSE: if frame.headers.cat == cnghttp2.NGHTTP2_HCAT_RESPONSE or frame.headers.cat == cnghttp2.NGHTTP2_HCAT_PUSH_RESPONSE:
handler = _get_stream_user_data(session, frame.hd.stream_id) handler = _get_stream_user_data(session, frame.hd.stream_id)
if not handler: if not handler:
@@ -970,7 +968,6 @@ cdef class _HTTP2ClientSessionCore(_HTTP2SessionCoreBase):
handler.method = push_promise.method handler.method = push_promise.method
handler.host = push_promise.host handler.host = push_promise.host
handler.path = push_promise.path handler.path = push_promise.path
handler.headers = push_promise.headers
handler.cookies = push_promise.cookies handler.cookies = push_promise.cookies
handler.stream_id = push_promise.stream_id handler.stream_id = push_promise.stream_id
handler.http2 = self handler.http2 = self
@@ -1033,6 +1030,9 @@ if asyncio:
path path
This is a value of :path header field. This is a value of :path header field.
headers
Request header fields
""" """
def __init__(self, http2, stream_id): def __init__(self, http2, stream_id):
@@ -1345,6 +1345,11 @@ if asyncio:
path path
This is a value of :path header field. This is a value of :path header field.
headers
Response header fields. There is a special exception. If this
object is passed to push_promise(), this instance variable contains
pushed request header fields.
""" """
def __init__(self, http2=None, stream_id=-1): def __init__(self, http2=None, stream_id=-1):

View File

@@ -96,7 +96,7 @@ Config::Config()
session_option(nullptr), data_ptr(nullptr), padding(0), num_worker(1), session_option(nullptr), data_ptr(nullptr), padding(0), num_worker(1),
header_table_size(-1), port(0), verbose(false), daemon(false), header_table_size(-1), port(0), verbose(false), daemon(false),
verify_client(false), no_tls(false), error_gzip(false), verify_client(false), no_tls(false), error_gzip(false),
early_response(false) { early_response(false), hexdump(false) {
nghttp2_option_new(&session_option); nghttp2_option_new(&session_option);
nghttp2_option_set_recv_client_preface(session_option, 1); nghttp2_option_set_recv_client_preface(session_option, 1);
} }
@@ -173,7 +173,8 @@ class Sessions {
public: public:
Sessions(struct ev_loop *loop, const Config *config, SSL_CTX *ssl_ctx) Sessions(struct ev_loop *loop, const Config *config, SSL_CTX *ssl_ctx)
: loop_(loop), config_(config), ssl_ctx_(ssl_ctx), callbacks_(nullptr), : loop_(loop), config_(config), ssl_ctx_(ssl_ctx), callbacks_(nullptr),
next_session_id_(1) { next_session_id_(1), tstamp_cached_(ev_now(loop)),
cached_date_(util::http_date(tstamp_cached_)) {
nghttp2_session_callbacks_new(&callbacks_); nghttp2_session_callbacks_new(&callbacks_);
fill_callback(callbacks_, config_); fill_callback(callbacks_, config_);
@@ -234,17 +235,25 @@ public:
} }
add_handler(handler.release()); add_handler(handler.release());
} }
void update_cached_date() { cached_date_ = util::http_date(time(nullptr)); } void update_cached_date() { cached_date_ = util::http_date(tstamp_cached_); }
const std::string &get_cached_date() const { return cached_date_; } const std::string &get_cached_date() {
auto t = ev_now(loop_);
if (t != tstamp_cached_) {
tstamp_cached_ = t;
update_cached_date();
}
return cached_date_;
}
private: private:
std::set<Http2Handler *> handlers_; std::set<Http2Handler *> handlers_;
std::string cached_date_;
struct ev_loop *loop_; struct ev_loop *loop_;
const Config *config_; const Config *config_;
SSL_CTX *ssl_ctx_; SSL_CTX *ssl_ctx_;
nghttp2_session_callbacks *callbacks_; nghttp2_session_callbacks *callbacks_;
int64_t next_session_id_; int64_t next_session_id_;
ev_tstamp tstamp_cached_;
std::string cached_date_;
}; };
Stream::Stream(Http2Handler *handler, int32_t stream_id) Stream::Stream(Http2Handler *handler, int32_t stream_id)
@@ -417,6 +426,11 @@ int Http2Handler::read_clear() {
if (nread == 0) { if (nread == 0) {
return -1; return -1;
} }
if (get_config()->hexdump) {
util::hexdump(stdout, buf.data(), nread);
}
rv = nghttp2_session_mem_recv(session_, buf.data(), nread); rv = nghttp2_session_mem_recv(session_, buf.data(), nread);
if (rv < 0) { if (rv < 0) {
if (rv != NGHTTP2_ERR_BAD_PREFACE) { if (rv != NGHTTP2_ERR_BAD_PREFACE) {
@@ -539,6 +553,11 @@ int Http2Handler::read_tls() {
} }
auto nread = rv; auto nread = rv;
if (get_config()->hexdump) {
util::hexdump(stdout, buf.data(), nread);
}
rv = nghttp2_session_mem_recv(session_, buf.data(), nread); rv = nghttp2_session_mem_recv(session_, buf.data(), nread);
if (rv < 0) { if (rv < 0) {
if (rv != NGHTTP2_ERR_BAD_PREFACE) { if (rv != NGHTTP2_ERR_BAD_PREFACE) {
@@ -681,12 +700,22 @@ int Http2Handler::submit_file_response(const std::string &status,
http2::make_nv_ls("content-length", content_length), http2::make_nv_ls("content-length", content_length),
http2::make_nv_ll("cache-control", "max-age=3600"), http2::make_nv_ll("cache-control", "max-age=3600"),
http2::make_nv_ls("date", sessions_->get_cached_date()), http2::make_nv_ls("date", sessions_->get_cached_date()),
http2::make_nv_ll("", "")); http2::make_nv_ll("", ""), http2::make_nv_ll("", ""));
size_t nvlen = 5; size_t nvlen = 5;
if (last_modified != 0) { if (last_modified != 0) {
last_modified_str = util::http_date(last_modified); last_modified_str = util::http_date(last_modified);
nva[nvlen++] = http2::make_nv_ls("last-modified", last_modified_str); nva[nvlen++] = http2::make_nv_ls("last-modified", last_modified_str);
} }
auto &trailer = get_config()->trailer;
std::string trailer_names;
if (!trailer.empty()) {
trailer_names = trailer[0].name;
for (size_t i = 1; i < trailer.size(); ++i) {
trailer_names += ", ";
trailer_names += trailer[i].name;
}
nva[nvlen++] = http2::make_nv_ls("trailer", trailer_names);
}
return nghttp2_submit_response(session_, stream->stream_id, nva.data(), nvlen, return nghttp2_submit_response(session_, stream->stream_id, nva.data(), nvlen,
data_prd); data_prd);
} }
@@ -800,6 +829,7 @@ void Http2Handler::terminate_session(uint32_t error_code) {
ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id, ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
uint8_t *buf, size_t length, uint32_t *data_flags, uint8_t *buf, size_t length, uint32_t *data_flags,
nghttp2_data_source *source, void *user_data) { nghttp2_data_source *source, void *user_data) {
int rv;
auto hd = static_cast<Http2Handler *>(user_data); auto hd = static_cast<Http2Handler *>(user_data);
auto stream = hd->get_stream(stream_id); auto stream = hd->get_stream(stream_id);
@@ -820,6 +850,23 @@ ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
if (nread == 0 || stream->body_left <= 0) { if (nread == 0 || stream->body_left <= 0) {
*data_flags |= NGHTTP2_DATA_FLAG_EOF; *data_flags |= NGHTTP2_DATA_FLAG_EOF;
auto config = hd->get_config();
if (!config->trailer.empty()) {
std::vector<nghttp2_nv> nva;
nva.reserve(config->trailer.size());
for (auto &kv : config->trailer) {
nva.push_back(http2::make_nv(kv.name, kv.value, kv.no_index));
}
rv = nghttp2_submit_trailer(session, stream_id, nva.data(), nva.size());
if (rv != 0) {
if (nghttp2_is_fatal(rv)) {
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
} else {
*data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM;
}
}
if (nghttp2_session_get_stream_remote_close(session, stream_id) == 0) { if (nghttp2_session_get_stream_remote_close(session, stream_id) == 0) {
remove_stream_read_timeout(stream); remove_stream_read_timeout(stream);
remove_stream_write_timeout(stream); remove_stream_write_timeout(stream);
@@ -1280,7 +1327,6 @@ void worker_acceptcb(struct ev_loop *loop, ev_async *w, int revents) {
namespace { namespace {
void run_worker(Worker *worker) { void run_worker(Worker *worker) {
auto loop = worker->sessions->get_loop(); auto loop = worker->sessions->get_loop();
worker->sessions->update_cached_date();
ev_run(loop, 0); ev_run(loop, 0);
} }
@@ -1361,10 +1407,7 @@ public:
auto fd = accept(fd_, nullptr, nullptr); auto fd = accept(fd_, nullptr, nullptr);
#endif // !HAVE_ACCEPT4 #endif // !HAVE_ACCEPT4
if (fd == -1) { if (fd == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) { break;
break;
}
continue;
} }
#ifndef HAVE_ACCEPT4 #ifndef HAVE_ACCEPT4
util::make_socket_nonblocking(fd); util::make_socket_nonblocking(fd);
@@ -1616,10 +1659,6 @@ int HttpServer::run() {
return -1; return -1;
} }
if (config_->num_worker == 1) {
sessions.update_cached_date();
}
ev_run(loop, 0); ev_run(loop, 0);
return 0; return 0;
} }

View File

@@ -50,6 +50,7 @@ namespace nghttp2 {
struct Config { struct Config {
std::map<std::string, std::vector<std::string>> push; std::map<std::string, std::vector<std::string>> push;
Headers trailer;
std::string htdocs; std::string htdocs;
std::string host; std::string host;
std::string private_key_file; std::string private_key_file;
@@ -70,6 +71,7 @@ struct Config {
bool no_tls; bool no_tls;
bool error_gzip; bool error_gzip;
bool early_response; bool early_response;
bool hexdump;
Config(); Config();
~Config(); ~Config();
}; };

View File

@@ -173,18 +173,39 @@ DISTCLEANFILES = $(pkgconfig_DATA)
lib_LTLIBRARIES = libnghttp2_asio.la lib_LTLIBRARIES = libnghttp2_asio.la
libnghttp2_asio_la_SOURCES = \ libnghttp2_asio_la_SOURCES = \
asio_connection.h \
asio_server.cc asio_server.h \
asio_io_service_pool.cc asio_io_service_pool.h \
asio_http2_handler.cc asio_http2_handler.h \
asio_http2_impl.cc asio_http2_impl.h \
util.cc util.h http2.cc http2.h \ util.cc util.h http2.cc http2.h \
ssl.cc ssl.h ssl.cc ssl.h \
asio_common.cc asio_common.h \
asio_io_service_pool.cc asio_io_service_pool.h \
asio_server_http2.cc \
asio_server_http2_impl.cc asio_server_http2_impl.h \
asio_server.cc asio_server.h \
asio_server_http2_handler.cc asio_server_http2_handler.h \
asio_server_connection.h \
asio_server_request.cc \
asio_server_request_impl.cc asio_server_request_impl.h \
asio_server_response.cc \
asio_server_response_impl.cc asio_server_response_impl.h \
asio_server_stream.cc asio_server_stream.h \
asio_server_serve_mux.cc asio_server_serve_mux.h \
asio_server_request_handler.cc asio_server_request_handler.h \
asio_server_tls_context.cc asio_server_tls_context.h \
asio_client_session.cc \
asio_client_session_impl.cc asio_client_session_impl.h \
asio_client_session_tcp_impl.cc asio_client_session_tcp_impl.h \
asio_client_session_tls_impl.cc asio_client_session_tls_impl.h \
asio_client_response.cc \
asio_client_response_impl.cc asio_client_response_impl.h \
asio_client_request.cc \
asio_client_request_impl.cc asio_client_request_impl.h \
asio_client_stream.cc asio_client_stream.h \
asio_client_tls_context.cc asio_client_tls_context.h
libnghttp2_asio_la_CPPFLAGS = ${AM_CPPFLAGS} ${BOOST_CPPFLAGS} libnghttp2_asio_la_CPPFLAGS = ${AM_CPPFLAGS} ${BOOST_CPPFLAGS}
libnghttp2_asio_la_LDFLAGS = -no-undefined -version-info 0:0:0 libnghttp2_asio_la_LDFLAGS = -no-undefined -version-info 1:0:0
libnghttp2_asio_la_LIBADD = \ libnghttp2_asio_la_LIBADD = \
$(top_builddir)/lib/libnghttp2.la \ $(top_builddir)/lib/libnghttp2.la \
$(top_builddir)/third-party/libhttp-parser.la \
${BOOST_LDFLAGS} \ ${BOOST_LDFLAGS} \
${BOOST_ASIO_LIB} \ ${BOOST_ASIO_LIB} \
${BOOST_THREAD_LIB} \ ${BOOST_THREAD_LIB} \

View File

@@ -164,11 +164,8 @@ const char *ansi_escend() { return color_output ? "\033[0m" : ""; }
namespace { namespace {
void print_nv(nghttp2_nv *nv) { void print_nv(nghttp2_nv *nv) {
fprintf(outfile, "%s", ansi_esc("\033[1;34m")); fprintf(outfile, "%s%s%s: %s\n", ansi_esc("\033[1;34m"), nv->name,
fwrite(nv->name, nv->namelen, 1, outfile); ansi_escend(), nv->value);
fprintf(outfile, "%s: ", ansi_escend());
fwrite(nv->value, nv->valuelen, 1, outfile);
fprintf(outfile, "\n");
} }
} // namespace } // namespace
namespace { namespace {

View File

@@ -0,0 +1,67 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "nghttp2_config.h"
#include <nghttp2/asio_http2_client.h>
#include "asio_client_request_impl.h"
#include "template.h"
namespace nghttp2 {
namespace asio_http2 {
namespace client {
request::request() : impl_(make_unique<request_impl>()) {}
request::~request() {}
void request::write_trailer(header_map h) const {
impl_->write_trailer(std::move(h));
}
void request::cancel(uint32_t error_code) const { impl_->cancel(error_code); }
void request::on_response(response_cb cb) const {
impl_->on_response(std::move(cb));
}
void request::on_push(request_cb cb) const { impl_->on_push(std::move(cb)); }
void request::on_close(close_cb cb) const { impl_->on_close(std::move(cb)); }
const uri_ref &request::uri() const { return impl_->uri(); }
const std::string &request::method() const { return impl_->method(); }
const header_map &request::header() const { return impl_->header(); }
void request::resume() const { impl_->resume(); }
request_impl &request::impl() const { return *impl_; }
} // namespace client
} // namespace asio_http2
} // namespace nghttp2

View File

@@ -0,0 +1,110 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "asio_client_request_impl.h"
#include "asio_client_stream.h"
#include "asio_client_session_impl.h"
#include "template.h"
namespace nghttp2 {
namespace asio_http2 {
namespace client {
request_impl::request_impl() : strm_(nullptr) {}
void request_impl::write_trailer(header_map h) {
auto sess = strm_->session();
sess->write_trailer(*strm_, std::move(h));
}
void request_impl::cancel(uint32_t error_code) {
auto sess = strm_->session();
sess->cancel(*strm_, error_code);
}
void request_impl::on_response(response_cb cb) { response_cb_ = std::move(cb); }
void request_impl::call_on_response(response &res) {
if (response_cb_) {
response_cb_(res);
}
}
void request_impl::on_push(request_cb cb) { push_request_cb_ = std::move(cb); }
void request_impl::call_on_push(request &push_req) {
if (push_request_cb_) {
push_request_cb_(push_req);
}
};
void request_impl::on_close(close_cb cb) { close_cb_ = std::move(cb); }
void request_impl::call_on_close(uint32_t error_code) {
if (close_cb_) {
close_cb_(error_code);
}
}
void request_impl::on_read(generator_cb cb) { generator_cb_ = std::move(cb); }
generator_cb::result_type request_impl::call_on_read(uint8_t *buf,
std::size_t len,
uint32_t *data_flags) {
if (generator_cb_) {
return generator_cb_(buf, len, data_flags);
}
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
return 0;
}
void request_impl::resume() {
auto sess = strm_->session();
sess->resume(*strm_);
}
void request_impl::header(header_map h) { header_ = std::move(h); }
header_map &request_impl::header() { return header_; }
const header_map &request_impl::header() const { return header_; }
void request_impl::stream(class stream *strm) { strm_ = strm; }
void request_impl::uri(uri_ref uri) { uri_ = std::move(uri); }
const uri_ref &request_impl::uri() const { return uri_; }
uri_ref &request_impl::uri() { return uri_; }
void request_impl::method(std::string s) { method_ = std::move(s); }
const std::string &request_impl::method() const { return method_; }
} // namespace client
} // namespace asio_http2
} // namespace nghttp2

View File

@@ -0,0 +1,93 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef ASIO_CLIENT_REQUEST_IMPL_H
#define ASIO_CLIENT_REQUEST_IMPL_H
#include "nghttp2_config.h"
#include <nghttp2/asio_http2_client.h>
namespace nghttp2 {
namespace asio_http2 {
namespace client {
class response;
class stream;
class request_impl {
public:
request_impl();
request_impl(const request_impl &) = delete;
request_impl &operator=(const request_impl &) = delete;
void write_trailer(header_map h);
void cancel(uint32_t error_code);
void on_response(response_cb cb);
void call_on_response(response &res);
void on_push(request_cb cb);
void call_on_push(request &push_req);
void on_close(close_cb cb);
void call_on_close(uint32_t error_code);
void on_read(generator_cb cb);
generator_cb::result_type call_on_read(uint8_t *buf, std::size_t len,
uint32_t *data_flags);
void resume();
void header(header_map h);
header_map &header();
const header_map &header() const;
void stream(class stream *strm);
void uri(uri_ref uri);
const uri_ref &uri() const;
uri_ref &uri();
void method(std::string s);
const std::string &method() const;
private:
header_map header_;
response_cb response_cb_;
request_cb push_request_cb_;
close_cb close_cb_;
generator_cb generator_cb_;
class stream *strm_;
uri_ref uri_;
std::string method_;
};
} // namespace client
} // namespace asio_http2
} // namespace nghttp2
#endif // ASIO_CLIENT_REQUEST_IMPL_H

View File

@@ -0,0 +1,53 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "nghttp2_config.h"
#include <nghttp2/asio_http2_client.h>
#include "asio_client_response_impl.h"
#include "template.h"
namespace nghttp2 {
namespace asio_http2 {
namespace client {
response::response() : impl_(make_unique<response_impl>()) {}
response::~response() {}
void response::on_data(data_cb cb) const { impl_->on_data(std::move(cb)); }
int response::status_code() const { return impl_->status_code(); }
int64_t response::content_length() const { return impl_->content_length(); }
const header_map &response::header() const { return impl_->header(); }
response_impl &response::impl() const { return *impl_; }
} // namespace client
} // namespace asio_http2
} // namespace nghttp2

View File

@@ -0,0 +1,57 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "asio_client_response_impl.h"
#include "template.h"
namespace nghttp2 {
namespace asio_http2 {
namespace client {
response_impl::response_impl() : content_length_(-1), status_code_(0) {}
void response_impl::on_data(data_cb cb) { data_cb_ = std::move(cb); }
void response_impl::call_on_data(const uint8_t *data, std::size_t len) {
if (data_cb_) {
data_cb_(data, len);
}
}
void response_impl::status_code(int sc) { status_code_ = sc; }
int response_impl::status_code() const { return status_code_; }
void response_impl::content_length(int64_t n) { content_length_ = n; }
int64_t response_impl::content_length() const { return content_length_; }
header_map &response_impl::header() { return header_; }
const header_map &response_impl::header() const { return header_; }
} // namespace client
} // namespace asio_http2
} // namespace nghttp2

View File

@@ -0,0 +1,69 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef ASIO_CLIENT_RESPONSE_IMPL_H
#define ASIO_CLIENT_RESPONSE_IMPL_H
#include "nghttp2_config.h"
#include <nghttp2/asio_http2_client.h>
namespace nghttp2 {
namespace asio_http2 {
namespace client {
class response_impl {
public:
response_impl();
response_impl(const response_impl &) = delete;
response_impl &operator=(const response_impl &) = delete;
void on_data(data_cb cb);
void call_on_data(const uint8_t *data, std::size_t len);
void status_code(int sc);
int status_code() const;
void content_length(int64_t n);
int64_t content_length() const;
header_map &header();
const header_map &header() const;
private:
data_cb data_cb_;
header_map header_;
int64_t content_length_;
int status_code_;
};
} // namespace client
} // namespace asio_http2
} // namespace nghttp2
#endif // ASIO_CLIENT_RESPONSE_IMPL_H

View File

@@ -0,0 +1,98 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "nghttp2_config.h"
#include <nghttp2/asio_http2_client.h>
#include "asio_client_session_tcp_impl.h"
#include "asio_client_session_tls_impl.h"
#include "asio_common.h"
#include "template.h"
namespace nghttp2 {
namespace asio_http2 {
namespace client {
using boost::asio::ip::tcp;
session::session(boost::asio::io_service &io_service, const std::string &host,
const std::string &service)
: impl_(make_unique<session_tcp_impl>(io_service, host, service)) {}
session::session(boost::asio::io_service &io_service,
boost::asio::ssl::context &tls_ctx, const std::string &host,
const std::string &service)
: impl_(make_unique<session_tls_impl>(io_service, tls_ctx, host, service)) {
}
session::~session() {}
session::session(session &&other) noexcept : impl_(std::move(other.impl_)) {}
session &session::operator=(session &&other) noexcept {
if (this == &other) {
return *this;
}
impl_ = std::move(other.impl_);
return *this;
}
void session::on_connect(connect_cb cb) const {
impl_->on_connect(std::move(cb));
}
void session::on_error(error_cb cb) const { impl_->on_error(std::move(cb)); }
void session::shutdown() const { impl_->shutdown(); }
boost::asio::io_service &session::io_service() const {
return impl_->io_service();
}
const request *session::submit(boost::system::error_code &ec,
const std::string &method,
const std::string &uri, header_map h) const {
return impl_->submit(ec, method, uri, generator_cb(), std::move(h));
}
const request *session::submit(boost::system::error_code &ec,
const std::string &method,
const std::string &uri, std::string data,
header_map h) const {
return impl_->submit(ec, method, uri, string_generator(std::move(data)),
std::move(h));
}
const request *session::submit(boost::system::error_code &ec,
const std::string &method,
const std::string &uri, generator_cb cb,
header_map h) const {
return impl_->submit(ec, method, uri, std::move(cb), std::move(h));
}
} // namespace client
} // namespace asio_http2
} // nghttp2

View File

@@ -0,0 +1,621 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "asio_client_session_impl.h"
#include <iostream>
#include "asio_client_stream.h"
#include "asio_client_request_impl.h"
#include "asio_client_response_impl.h"
#include "asio_common.h"
#include "template.h"
#include "util.h"
#include "http2.h"
namespace nghttp2 {
namespace asio_http2 {
namespace client {
session_impl::session_impl(boost::asio::io_service &io_service)
: wblen_(0), io_service_(io_service), resolver_(io_service),
session_(nullptr), data_pending_(nullptr), data_pendinglen_(0),
writing_(false), inside_callback_(false) {}
session_impl::~session_impl() {
// finish up all active stream
for (auto &p : streams_) {
auto &strm = p.second;
auto &req = strm->request().impl();
req.call_on_close(NGHTTP2_INTERNAL_ERROR);
}
nghttp2_session_del(session_);
}
void session_impl::start_resolve(const std::string &host,
const std::string &service) {
resolver_.async_resolve({host, service},
[this](const boost::system::error_code &ec,
tcp::resolver::iterator endpoint_it) {
if (ec) {
not_connected(ec);
return;
}
start_connect(endpoint_it);
});
}
void session_impl::connected(tcp::resolver::iterator endpoint_it) {
if (!setup_session()) {
return;
}
socket().set_option(boost::asio::ip::tcp::no_delay(true));
std::copy_n(NGHTTP2_CLIENT_CONNECTION_PREFACE,
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN, std::begin(wb_));
wblen_ = NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN;
do_write();
do_read();
auto &connect_cb = on_connect();
if (connect_cb) {
connect_cb(endpoint_it);
}
}
void session_impl::not_connected(const boost::system::error_code &ec) {
auto &error_cb = on_error();
if (error_cb) {
error_cb(ec);
}
}
void session_impl::on_connect(connect_cb cb) { connect_cb_ = std::move(cb); }
void session_impl::on_error(error_cb cb) { error_cb_ = std::move(cb); }
const connect_cb &session_impl::on_connect() const { return connect_cb_; }
const error_cb &session_impl::on_error() const { return error_cb_; }
namespace {
int on_begin_headers_callback(nghttp2_session *session,
const nghttp2_frame *frame, void *user_data) {
if (frame->hd.type != NGHTTP2_PUSH_PROMISE) {
return 0;
}
auto sess = static_cast<session_impl *>(user_data);
sess->create_push_stream(frame->push_promise.promised_stream_id);
return 0;
}
} // namespace
namespace {
int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen, uint8_t flags,
void *user_data) {
auto sess = static_cast<session_impl *>(user_data);
stream *strm;
switch (frame->hd.type) {
case NGHTTP2_HEADERS: {
strm = sess->find_stream(frame->hd.stream_id);
if (!strm) {
return 0;
}
// ignore trailers
if (frame->headers.cat == NGHTTP2_HCAT_HEADERS &&
!strm->expect_final_response()) {
return 0;
}
auto token = http2::lookup_token(name, namelen);
auto &res = strm->response().impl();
if (token == http2::HD__STATUS) {
res.status_code(util::parse_uint(value, valuelen));
} else {
if (token == http2::HD_CONTENT_LENGTH) {
res.content_length(util::parse_uint(value, valuelen));
}
res.header().emplace(
std::string(name, name + namelen),
header_value{std::string(value, value + valuelen),
(flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0});
}
break;
}
case NGHTTP2_PUSH_PROMISE: {
strm = sess->find_stream(frame->push_promise.promised_stream_id);
if (!strm) {
return 0;
}
auto &req = strm->request().impl();
auto &uri = req.uri();
switch (http2::lookup_token(name, namelen)) {
case http2::HD__METHOD:
req.method(std::string(value, value + valuelen));
break;
case http2::HD__SCHEME:
uri.scheme.assign(value, value + valuelen);
break;
case http2::HD__PATH:
split_path(uri, value, value + valuelen);
break;
case http2::HD__AUTHORITY:
uri.host.assign(value, value + valuelen);
break;
case http2::HD_HOST:
if (uri.host.empty()) {
uri.host.assign(value, value + valuelen);
}
// fall through
default:
req.header().emplace(
std::string(name, name + namelen),
header_value{std::string(value, value + valuelen),
(flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0});
}
break;
}
default:
return 0;
}
return 0;
}
} // namespace
namespace {
int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
void *user_data) {
auto sess = static_cast<session_impl *>(user_data);
auto strm = sess->find_stream(frame->hd.stream_id);
switch (frame->hd.type) {
case NGHTTP2_DATA: {
if (!strm) {
return 0;
}
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
strm->response().impl().call_on_data(nullptr, 0);
}
break;
}
case NGHTTP2_HEADERS: {
if (!strm) {
return 0;
}
// ignore trailers
if (frame->headers.cat == NGHTTP2_HCAT_HEADERS &&
!strm->expect_final_response()) {
return 0;
}
if (strm->expect_final_response()) {
// wait for final response
return 0;
}
auto &req = strm->request().impl();
req.call_on_response(strm->response());
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
strm->response().impl().call_on_data(nullptr, 0);
}
break;
}
case NGHTTP2_PUSH_PROMISE: {
if (!strm) {
return 0;
}
auto push_strm = sess->find_stream(frame->push_promise.promised_stream_id);
if (!push_strm) {
return 0;
}
strm->request().impl().call_on_push(push_strm->request());
break;
}
}
return 0;
}
} // namespace
namespace {
int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
int32_t stream_id, const uint8_t *data,
size_t len, void *user_data) {
auto sess = static_cast<session_impl *>(user_data);
auto strm = sess->find_stream(stream_id);
if (!strm) {
return 0;
}
auto &res = strm->response().impl();
res.call_on_data(data, len);
return 0;
}
} // namespace
namespace {
int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
uint32_t error_code, void *user_data) {
auto sess = static_cast<session_impl *>(user_data);
auto strm = sess->pop_stream(stream_id);
if (!strm) {
return 0;
}
strm->request().impl().call_on_close(error_code);
return 0;
}
} // namespace
bool session_impl::setup_session() {
nghttp2_session_callbacks *callbacks;
nghttp2_session_callbacks_new(&callbacks);
auto cb_del = defer(nghttp2_session_callbacks_del, callbacks);
nghttp2_session_callbacks_set_on_begin_headers_callback(
callbacks, on_begin_headers_callback);
nghttp2_session_callbacks_set_on_header_callback(callbacks,
on_header_callback);
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
on_frame_recv_callback);
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
callbacks, on_data_chunk_recv_callback);
nghttp2_session_callbacks_set_on_stream_close_callback(
callbacks, on_stream_close_callback);
auto rv = nghttp2_session_client_new(&session_, callbacks, this);
if (rv != 0) {
auto &error_cb = on_error();
if (error_cb) {
error_cb(make_error_code(static_cast<nghttp2_error>(rv)));
}
return false;
}
const uint32_t window_size = 256 * 1024 * 1024;
std::array<nghttp2_settings_entry, 2> iv{
{{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100},
// typically client is just a *sink* and just process data as
// much as possible. Use large window size by default.
{NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, window_size}}};
nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, iv.data(), iv.size());
// increase connection window size up to window_size
nghttp2_submit_window_update(session_, NGHTTP2_FLAG_NONE, 0,
window_size -
NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE);
return true;
}
int session_impl::write_trailer(stream &strm, header_map h) {
int rv;
auto nva = std::vector<nghttp2_nv>();
nva.reserve(h.size());
for (auto &hd : h) {
nva.push_back(nghttp2::http2::make_nv(hd.first, hd.second.value,
hd.second.sensitive));
}
rv = nghttp2_submit_trailer(session_, strm.stream_id(), nva.data(),
nva.size());
if (rv != 0) {
return -1;
}
signal_write();
return 0;
}
void session_impl::cancel(stream &strm, uint32_t error_code) {
nghttp2_submit_rst_stream(session_, NGHTTP2_FLAG_NONE, strm.stream_id(),
error_code);
signal_write();
}
void session_impl::resume(stream &strm) {
nghttp2_session_resume_data(session_, strm.stream_id());
signal_write();
}
stream *session_impl::find_stream(int32_t stream_id) {
auto it = streams_.find(stream_id);
if (it == std::end(streams_)) {
return nullptr;
}
return (*it).second.get();
}
std::unique_ptr<stream> session_impl::pop_stream(int32_t stream_id) {
auto it = streams_.find(stream_id);
if (it == std::end(streams_)) {
return nullptr;
}
auto strm = std::move((*it).second);
streams_.erase(it);
return strm;
}
stream *session_impl::create_push_stream(int32_t stream_id) {
auto strm = create_stream();
strm->stream_id(stream_id);
auto p = streams_.emplace(stream_id, std::move(strm));
assert(p.second);
return (*p.first).second.get();
}
std::unique_ptr<stream> session_impl::create_stream() {
return make_unique<stream>(this);
}
const request *session_impl::submit(boost::system::error_code &ec,
const std::string &method,
const std::string &uri, generator_cb cb,
header_map h) {
ec.clear();
http_parser_url u{};
// TODO Handle CONNECT method
if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) {
ec = make_error_code(boost::system::errc::invalid_argument);
return nullptr;
}
if ((u.field_set & (1 << UF_SCHEMA)) == 0 ||
(u.field_set & (1 << UF_HOST)) == 0) {
ec = make_error_code(boost::system::errc::invalid_argument);
return nullptr;
}
auto strm = create_stream();
auto &req = strm->request().impl();
auto &uref = req.uri();
http2::copy_url_component(uref.scheme, &u, UF_SCHEMA, uri.c_str());
http2::copy_url_component(uref.host, &u, UF_HOST, uri.c_str());
http2::copy_url_component(uref.raw_path, &u, UF_PATH, uri.c_str());
http2::copy_url_component(uref.raw_query, &u, UF_QUERY, uri.c_str());
if (util::ipv6_numeric_addr(uref.host.c_str())) {
uref.host = "[" + uref.host;
uref.host += "]";
}
if (u.field_set & (1 << UF_PORT)) {
uref.host += ":";
uref.host += util::utos(u.port);
}
if (uref.raw_path.empty()) {
uref.raw_path = "/";
}
uref.path = percent_decode(uref.raw_path);
auto path = uref.raw_path;
if (u.field_set & (1 << UF_QUERY)) {
path += "?";
path += uref.raw_query;
}
auto nva = std::vector<nghttp2_nv>();
nva.reserve(3 + h.size());
nva.push_back(http2::make_nv_ls(":method", method));
nva.push_back(http2::make_nv_ls(":scheme", uref.scheme));
nva.push_back(http2::make_nv_ls(":path", path));
nva.push_back(http2::make_nv_ls(":authority", uref.host));
for (auto &kv : h) {
nva.push_back(
http2::make_nv(kv.first, kv.second.value, kv.second.sensitive));
}
req.header(std::move(h));
nghttp2_data_provider *prdptr = nullptr;
nghttp2_data_provider prd;
if (cb) {
strm->request().impl().on_read(std::move(cb));
prd.source.ptr = strm.get();
prd.read_callback =
[](nghttp2_session *session, int32_t stream_id, uint8_t *buf,
size_t length, uint32_t *data_flags, nghttp2_data_source *source,
void *user_data) -> ssize_t {
auto strm = static_cast<stream *>(source->ptr);
return strm->request().impl().call_on_read(buf, length, data_flags);
};
prdptr = &prd;
}
auto stream_id = nghttp2_submit_request(session_, nullptr, nva.data(),
nva.size(), prdptr, strm.get());
if (stream_id < 0) {
ec = make_error_code(static_cast<nghttp2_error>(stream_id));
return nullptr;
}
signal_write();
strm->stream_id(stream_id);
auto p = streams_.emplace(stream_id, std::move(strm));
assert(p.second);
return &(*p.first).second->request();
}
void session_impl::shutdown() {
nghttp2_session_terminate_session(session_, NGHTTP2_NO_ERROR);
signal_write();
}
boost::asio::io_service &session_impl::io_service() { return io_service_; }
void session_impl::signal_write() {
if (!inside_callback_) {
do_write();
}
}
bool session_impl::should_stop() const {
return !writing_ && !nghttp2_session_want_read(session_) &&
!nghttp2_session_want_write(session_);
}
namespace {
struct callback_guard {
callback_guard(session_impl &sess) : sess(sess) { sess.enter_callback(); }
~callback_guard() { sess.leave_callback(); }
session_impl &sess;
};
} // namespace
void session_impl::enter_callback() {
assert(!inside_callback_);
inside_callback_ = true;
}
void session_impl::leave_callback() {
assert(inside_callback_);
inside_callback_ = false;
}
void session_impl::do_read() {
read_socket([this](const boost::system::error_code &ec,
std::size_t bytes_transferred) {
if (ec) {
if (ec.value() == boost::asio::error::operation_aborted) {
shutdown_socket();
}
return;
}
{
callback_guard cg(*this);
auto rv =
nghttp2_session_mem_recv(session_, rb_.data(), bytes_transferred);
if (rv != static_cast<ssize_t>(bytes_transferred)) {
shutdown_socket();
return;
}
}
do_write();
if (should_stop()) {
shutdown_socket();
return;
}
do_read();
});
}
void session_impl::do_write() {
if (writing_) {
return;
}
if (data_pending_) {
std::copy_n(data_pending_, data_pendinglen_, std::begin(wb_) + wblen_);
wblen_ += data_pendinglen_;
data_pending_ = nullptr;
data_pendinglen_ = 0;
}
{
callback_guard cg(*this);
for (;;) {
const uint8_t *data;
auto n = nghttp2_session_mem_send(session_, &data);
if (n < 0) {
shutdown_socket();
return;
}
if (n == 0) {
break;
}
if (wblen_ + n > wb_.size()) {
data_pending_ = data;
data_pendinglen_ = n;
break;
}
std::copy_n(data, n, std::begin(wb_) + wblen_);
wblen_ += n;
}
}
if (wblen_ == 0) {
return;
}
writing_ = true;
write_socket([this](const boost::system::error_code &ec, std::size_t n) {
if (ec) {
return;
}
wblen_ = 0;
writing_ = false;
do_write();
});
}
} // namespace client
} // namespace asio_http2
} // nghttp2

View File

@@ -0,0 +1,122 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef ASIO_CLIENT_SESSION_IMPL_H
#define ASIO_CLIENT_SESSION_IMPL_H
#include "nghttp2_config.h"
#include <boost/array.hpp>
#include <nghttp2/asio_http2_client.h>
namespace nghttp2 {
namespace asio_http2 {
namespace client {
class stream;
using boost::asio::ip::tcp;
class session_impl {
public:
session_impl(boost::asio::io_service &io_service);
virtual ~session_impl();
void start_resolve(const std::string &host, const std::string &service);
void connected(tcp::resolver::iterator endpoint_it);
void not_connected(const boost::system::error_code &ec);
void on_connect(connect_cb cb);
void on_error(error_cb cb);
const connect_cb &on_connect() const;
const error_cb &on_error() const;
int write_trailer(stream &strm, header_map h);
void cancel(stream &strm, uint32_t error_code);
void resume(stream &strm);
std::unique_ptr<stream> create_stream();
std::unique_ptr<stream> pop_stream(int32_t stream_id);
stream *create_push_stream(int32_t stream_id);
stream *find_stream(int32_t stream_id);
const request *submit(boost::system::error_code &ec,
const std::string &method, const std::string &uri,
generator_cb cb, header_map h);
virtual void start_connect(tcp::resolver::iterator endpoint_it) = 0;
virtual tcp::socket &socket() = 0;
virtual void read_socket(std::function<
void(const boost::system::error_code &ec, std::size_t n)> h) = 0;
virtual void write_socket(std::function<
void(const boost::system::error_code &ec, std::size_t n)> h) = 0;
virtual void shutdown_socket() = 0;
void shutdown();
boost::asio::io_service &io_service();
void signal_write();
void enter_callback();
void leave_callback();
void do_read();
void do_write();
protected:
boost::array<uint8_t, 8192> rb_;
boost::array<uint8_t, 65536> wb_;
std::size_t wblen_;
private:
bool should_stop() const;
bool setup_session();
boost::asio::io_service &io_service_;
tcp::resolver resolver_;
std::map<int32_t, std::unique_ptr<stream>> streams_;
connect_cb connect_cb_;
error_cb error_cb_;
nghttp2_session *session_;
const uint8_t *data_pending_;
std::size_t data_pendinglen_;
bool writing_;
bool inside_callback_;
};
} // namespace client
} // namespace asio_http2
} // namespace nghttp2
#endif // ASIO_CLIENT_SESSION_IMPL_H

View File

@@ -0,0 +1,69 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "asio_client_session_tcp_impl.h"
namespace nghttp2 {
namespace asio_http2 {
namespace client {
session_tcp_impl::session_tcp_impl(boost::asio::io_service &io_service,
const std::string &host,
const std::string &service)
: session_impl(io_service), socket_(io_service) {
start_resolve(host, service);
}
session_tcp_impl::~session_tcp_impl() {}
void session_tcp_impl::start_connect(tcp::resolver::iterator endpoint_it) {
boost::asio::async_connect(socket_, endpoint_it,
[this](const boost::system::error_code &ec,
tcp::resolver::iterator endpoint_it) {
if (ec) {
not_connected(ec);
return;
}
connected(endpoint_it);
});
}
tcp::socket &session_tcp_impl::socket() { return socket_; }
void session_tcp_impl::read_socket(
std::function<void(const boost::system::error_code &ec, std::size_t n)> h) {
socket_.async_read_some(boost::asio::buffer(rb_), h);
}
void session_tcp_impl::write_socket(
std::function<void(const boost::system::error_code &ec, std::size_t n)> h) {
boost::asio::async_write(socket_, boost::asio::buffer(wb_, wblen_), h);
}
void session_tcp_impl::shutdown_socket() { socket_.close(); }
} // namespace client
} // namespace asio_http2
} // namespace nghttp2

View File

@@ -0,0 +1,60 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef ASIO_CLIENT_SESSION_TCP_IMPL_H
#define ASIO_CLIENT_SESSION_TCP_IMPL_H
#include "asio_client_session_impl.h"
#include <nghttp2/asio_http2_client.h>
namespace nghttp2 {
namespace asio_http2 {
namespace client {
using boost::asio::ip::tcp;
class session_tcp_impl : public session_impl {
public:
session_tcp_impl(boost::asio::io_service &io_service, const std::string &host,
const std::string &service);
virtual ~session_tcp_impl();
virtual void start_connect(tcp::resolver::iterator endpoint_it);
virtual tcp::socket &socket();
virtual void read_socket(std::function<
void(const boost::system::error_code &ec, std::size_t n)> h);
virtual void write_socket(std::function<
void(const boost::system::error_code &ec, std::size_t n)> h);
virtual void shutdown_socket();
private:
tcp::socket socket_;
};
} // namespace client
} // namespace asio_http2
} // namespace nghttp2
#endif // ASIO_CLIENT_SESSION_TCP_IMPL_H

View File

@@ -0,0 +1,85 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "asio_client_session_tls_impl.h"
namespace nghttp2 {
namespace asio_http2 {
namespace client {
session_tls_impl::session_tls_impl(boost::asio::io_service &io_service,
boost::asio::ssl::context &tls_ctx,
const std::string &host,
const std::string &service)
: session_impl(io_service), socket_(io_service, tls_ctx) {
// this callback setting is no effect is
// ssl::context::set_verify_mode(boost::asio::ssl::verify_peer) is
// not used, which is what we want.
socket_.set_verify_callback(boost::asio::ssl::rfc2818_verification(host));
start_resolve(host, service);
}
session_tls_impl::~session_tls_impl() {}
void session_tls_impl::start_connect(tcp::resolver::iterator endpoint_it) {
boost::asio::async_connect(socket(), endpoint_it,
[this](const boost::system::error_code &ec,
tcp::resolver::iterator endpoint_it) {
if (ec) {
not_connected(ec);
return;
}
socket_.async_handshake(
boost::asio::ssl::stream_base::client,
[this, endpoint_it](const boost::system::error_code &ec) {
if (ec) {
not_connected(ec);
return;
}
connected(endpoint_it);
});
});
}
tcp::socket &session_tls_impl::socket() { return socket_.next_layer(); }
void session_tls_impl::read_socket(
std::function<void(const boost::system::error_code &ec, std::size_t n)> h) {
socket_.async_read_some(boost::asio::buffer(rb_), h);
}
void session_tls_impl::write_socket(
std::function<void(const boost::system::error_code &ec, std::size_t n)> h) {
boost::asio::async_write(socket_, boost::asio::buffer(wb_, wblen_), h);
}
void session_tls_impl::shutdown_socket() {
socket_.async_shutdown([](const boost::system::error_code &ec) {});
}
} // namespace client
} // namespace asio_http2
} // namespace nghttp2

View File

@@ -0,0 +1,63 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef ASIO_CLIENT_SESSION_TLS_IMPL_H
#define ASIO_CLIENT_SESSION_TLS_IMPL_H
#include "asio_client_session_impl.h"
#include <nghttp2/asio_http2_client.h>
namespace nghttp2 {
namespace asio_http2 {
namespace client {
using boost::asio::ip::tcp;
using ssl_socket = boost::asio::ssl::stream<tcp::socket>;
class session_tls_impl : public session_impl {
public:
session_tls_impl(boost::asio::io_service &io_service,
boost::asio::ssl::context &tls_ctx, const std::string &host,
const std::string &service);
virtual ~session_tls_impl();
virtual void start_connect(tcp::resolver::iterator endpoint_it);
virtual tcp::socket &socket();
virtual void read_socket(std::function<
void(const boost::system::error_code &ec, std::size_t n)> h);
virtual void write_socket(std::function<
void(const boost::system::error_code &ec, std::size_t n)> h);
virtual void shutdown_socket();
private:
ssl_socket socket_;
};
} // namespace client
} // namespace asio_http2
} // namespace nghttp2
#endif // ASIO_CLIENT_SESSION_TLS_IMPL_H

59
src/asio_client_stream.cc Normal file
View File

@@ -0,0 +1,59 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "asio_client_stream.h"
#include "asio_client_request_impl.h"
#include "asio_client_response_impl.h"
#include "asio_client_session_impl.h"
namespace nghttp2 {
namespace asio_http2 {
namespace client {
stream::stream(session_impl *sess) : sess_(sess), stream_id_(0) {
request_.impl().stream(this);
}
void stream::stream_id(int32_t stream_id) { stream_id_ = stream_id; }
int32_t stream::stream_id() const { return stream_id_; }
class request &stream::request() {
return request_;
}
class response &stream::response() {
return response_;
}
session_impl *stream::session() const { return sess_; }
bool stream::expect_final_response() const {
return response_.status_code() / 100 == 1;
}
} // namespace client
} // namespace asio_http2
} // namespace nghttp2

68
src/asio_client_stream.h Normal file
View File

@@ -0,0 +1,68 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef ASIO_CLIENT_STREAM_H
#define ASIO_CLIENT_STREAM_H
#include "nghttp2_config.h"
#include <nghttp2/asio_http2_client.h>
namespace nghttp2 {
namespace asio_http2 {
namespace client {
class request;
class response;
class session_impl;
class stream {
public:
stream(session_impl *sess);
stream(const stream &) = delete;
stream &operator=(const stream &) = delete;
void stream_id(int32_t stream_id);
int32_t stream_id() const;
class request &request();
class response &response();
session_impl *session() const;
bool expect_final_response() const;
private:
nghttp2::asio_http2::client::request request_;
nghttp2::asio_http2::client::response response_;
session_impl *sess_;
uint32_t stream_id_;
};
} // namespace client
} // namespace asio_http2
} // namespace nghttp2
#endif // ASIO_CLIENT_STREAM_H

View File

@@ -0,0 +1,64 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "asio_client_tls_context.h"
#include <openssl/ssl.h>
#include <boost/asio/ssl.hpp>
#include "ssl.h"
#include "util.h"
namespace nghttp2 {
namespace asio_http2 {
namespace client {
namespace {
int client_select_next_proto_cb(SSL *ssl, unsigned char **out,
unsigned char *outlen, const unsigned char *in,
unsigned int inlen, void *arg) {
if (!util::select_h2(const_cast<const unsigned char **>(out), outlen, in,
inlen)) {
return SSL_TLSEXT_ERR_NOACK;
}
return SSL_TLSEXT_ERR_OK;
}
} // namespace
boost::system::error_code
configure_tls_context(boost::system::error_code &ec,
boost::asio::ssl::context &tls_ctx) {
ec.clear();
auto ctx = tls_ctx.native_handle();
SSL_CTX_set_next_proto_select_cb(ctx, client_select_next_proto_cb, nullptr);
return ec;
}
} // namespace client
} // namespace asio_http2
} // namespace nghttp2

View File

@@ -0,0 +1,32 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef ASIO_CLIENT_TLS_CONTEXT_H
#define ASIO_CLIENT_TLS_CONTEXT_H
#include "nghttp2_config.h"
#include <nghttp2/asio_http2_client.h>
#endif // ASIO_CLIENT_TLS_CONTEXT_H

148
src/asio_common.cc Normal file
View File

@@ -0,0 +1,148 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "asio_common.h"
#include <memory>
#include "util.h"
#include "template.h"
#include "http2.h"
namespace nghttp2 {
namespace asio_http2 {
class nghttp2_category_impl : public boost::system::error_category {
public:
const char *name() const noexcept { return "nghttp2"; }
std::string message(int ev) const { return nghttp2_strerror(ev); }
};
const boost::system::error_category &nghttp2_category() noexcept {
static nghttp2_category_impl cat;
return cat;
}
boost::system::error_code make_error_code(nghttp2_error ev) {
return boost::system::error_code(static_cast<int>(ev), nghttp2_category());
}
generator_cb string_generator(std::string data) {
auto strio = std::make_shared<std::pair<std::string, size_t>>(std::move(data),
data.size());
return [strio](uint8_t *buf, size_t len, uint32_t *data_flags) {
auto &data = strio->first;
auto &left = strio->second;
auto n = std::min(len, left);
std::copy_n(data.c_str() + data.size() - left, n, buf);
left -= n;
if (left == 0) {
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
}
return n;
};
}
generator_cb deferred_generator() {
return [](uint8_t *buf, size_t len,
uint32_t *data_flags) { return NGHTTP2_ERR_DEFERRED; };
}
template <typename F, typename... T>
std::shared_ptr<Defer<F, T...>> defer_shared(F &&f, T &&... t) {
return std::make_shared<Defer<F, T...>>(std::forward<F>(f),
std::forward<T>(t)...);
}
generator_cb file_generator(const std::string &path) {
auto fd = open(path.c_str(), O_RDONLY);
if (fd == -1) {
return generator_cb();
}
return file_generator_from_fd(fd);
}
generator_cb file_generator_from_fd(int fd) {
auto d = defer_shared(close, fd);
return [fd, d](uint8_t *buf, size_t len, uint32_t *data_flags)
-> generator_cb::result_type {
ssize_t n;
while ((n = read(fd, buf, len)) == -1 && errno == EINTR)
;
if (n == -1) {
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
if (n == 0) {
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
}
return n;
};
}
bool check_path(const std::string &path) { return util::check_path(path); }
std::string percent_decode(const std::string &s) {
return util::percentDecode(std::begin(s), std::end(s));
}
std::string http_date(int64_t t) { return util::http_date(t); }
boost::system::error_code host_service_from_uri(boost::system::error_code &ec,
std::string &scheme,
std::string &host,
std::string &service,
const std::string &uri) {
ec.clear();
http_parser_url u{};
if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) {
ec = make_error_code(boost::system::errc::invalid_argument);
return ec;
}
if ((u.field_set & (1 << UF_SCHEMA)) == 0 ||
(u.field_set & (1 << UF_HOST)) == 0) {
ec = make_error_code(boost::system::errc::invalid_argument);
return ec;
}
http2::copy_url_component(scheme, &u, UF_SCHEMA, uri.c_str());
http2::copy_url_component(host, &u, UF_HOST, uri.c_str());
if (u.field_set & (1 << UF_PORT)) {
http2::copy_url_component(service, &u, UF_PORT, uri.c_str());
} else {
service = scheme;
}
return ec;
}
} // namespace asio_http2
} // namespace nghttp2

65
src/asio_common.h Normal file
View File

@@ -0,0 +1,65 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef ASIO_COMMON_H
#define ASIO_COMMON_H
#include "nghttp2_config.h"
#include <string>
#include <nghttp2/asio_http2.h>
#include "util.h"
namespace nghttp2 {
namespace asio_http2 {
boost::system::error_code make_error_code(nghttp2_error ev);
generator_cb string_generator(std::string data);
// Returns generator_cb, which just returns NGHTTP2_ERR_DEFERRED
generator_cb deferred_generator();
template <typename InputIt>
void split_path(uri_ref &dst, InputIt first, InputIt last) {
auto path_last = std::find(first, last, '?');
InputIt query_first;
if (path_last == last) {
query_first = path_last = last;
} else {
query_first = path_last + 1;
}
dst.path = util::percentDecode(first, path_last);
dst.raw_path.assign(first, path_last);
dst.raw_query.assign(query_first, last);
}
} // namespace asio_http2
} // namespace nghttp2
#endif // ASIO_COMMON_H

View File

@@ -1,711 +0,0 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2014 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "asio_http2_handler.h"
#include <iostream>
#include "http2.h"
#include "util.h"
#include "template.h"
namespace nghttp2 {
namespace asio_http2 {
channel::channel() : impl_(make_unique<channel_impl>()) {}
void channel::post(void_cb cb) { impl_->post(std::move(cb)); }
channel_impl &channel::impl() { return *impl_; }
channel_impl::channel_impl() : strand_(nullptr) {}
void channel_impl::post(void_cb cb) { strand_->post(std::move(cb)); }
void channel_impl::strand(boost::asio::io_service::strand *strand) {
strand_ = strand;
}
namespace server {
extern std::shared_ptr<std::string> cached_date;
request::request() : impl_(make_unique<request_impl>()) {}
const std::vector<header> &request::headers() const { return impl_->headers(); }
const std::string &request::method() const { return impl_->method(); }
const std::string &request::scheme() const { return impl_->scheme(); }
const std::string &request::authority() const { return impl_->authority(); }
const std::string &request::host() const { return impl_->host(); }
const std::string &request::path() const { return impl_->path(); }
bool request::push(std::string method, std::string path,
std::vector<header> headers) {
return impl_->push(std::move(method), std::move(path), std::move(headers));
}
bool request::pushed() const { return impl_->pushed(); }
bool request::closed() const { return impl_->closed(); }
void request::on_data(data_cb cb) { return impl_->on_data(std::move(cb)); }
void request::on_end(void_cb cb) { return impl_->on_end(std::move(cb)); }
bool request::run_task(thread_cb start) {
return impl_->run_task(std::move(start));
}
request_impl &request::impl() { return *impl_; }
response::response() : impl_(make_unique<response_impl>()) {}
void response::write_head(unsigned int status_code,
std::vector<header> headers) {
impl_->write_head(status_code, std::move(headers));
}
void response::end(std::string data) { impl_->end(std::move(data)); }
void response::end(read_cb cb) { impl_->end(std::move(cb)); }
void response::resume() { impl_->resume(); }
unsigned int response::status_code() const { return impl_->status_code(); }
bool response::started() const { return impl_->started(); }
response_impl &response::impl() { return *impl_; }
request_impl::request_impl() : pushed_(false) {}
const std::vector<header> &request_impl::headers() const { return headers_; }
const std::string &request_impl::method() const { return method_; }
const std::string &request_impl::scheme() const { return scheme_; }
const std::string &request_impl::authority() const { return authority_; }
const std::string &request_impl::host() const { return host_; }
const std::string &request_impl::path() const { return path_; }
void request_impl::set_header(std::vector<header> headers) {
headers_ = std::move(headers);
}
void request_impl::add_header(std::string name, std::string value) {
headers_.push_back(header{std::move(name), std::move(value)});
}
void request_impl::method(std::string arg) { method_ = std::move(arg); }
void request_impl::scheme(std::string arg) { scheme_ = std::move(arg); }
void request_impl::authority(std::string arg) { authority_ = std::move(arg); }
void request_impl::host(std::string arg) { host_ = std::move(arg); }
void request_impl::path(std::string arg) { path_ = std::move(arg); }
bool request_impl::push(std::string method, std::string path,
std::vector<header> headers) {
if (closed()) {
return false;
}
auto handler = handler_.lock();
auto stream = stream_.lock();
auto rv = handler->push_promise(*stream, std::move(method), std::move(path),
std::move(headers));
return rv == 0;
}
bool request_impl::pushed() const { return pushed_; }
void request_impl::pushed(bool f) { pushed_ = f; }
bool request_impl::closed() const {
return handler_.expired() || stream_.expired();
}
void request_impl::on_data(data_cb cb) { on_data_cb_ = std::move(cb); }
void request_impl::on_end(void_cb cb) { on_end_cb_ = std::move(cb); }
bool request_impl::run_task(thread_cb start) {
if (closed()) {
return false;
}
auto handler = handler_.lock();
return handler->run_task(std::move(start));
}
void request_impl::handler(std::weak_ptr<http2_handler> h) {
handler_ = std::move(h);
}
void request_impl::stream(std::weak_ptr<http2_stream> s) {
stream_ = std::move(s);
}
void request_impl::call_on_data(const uint8_t *data, std::size_t len) {
if (on_data_cb_) {
on_data_cb_(data, len);
}
}
void request_impl::call_on_end() {
if (on_end_cb_) {
on_end_cb_();
}
}
response_impl::response_impl() : status_code_(200), started_(false) {}
unsigned int response_impl::status_code() const { return status_code_; }
void response_impl::write_head(unsigned int status_code,
std::vector<header> headers) {
status_code_ = status_code;
headers_ = std::move(headers);
}
void response_impl::end(std::string data) {
if (started_) {
return;
}
auto strio = std::make_shared<std::pair<std::string, size_t>>(std::move(data),
data.size());
auto read_cb = [strio](uint8_t *buf, size_t len) {
auto nread = std::min(len, strio->second);
memcpy(buf, strio->first.c_str(), nread);
strio->second -= nread;
if (strio->second == 0) {
return std::make_pair(nread, true);
}
return std::make_pair(nread, false);
};
end(std::move(read_cb));
}
void response_impl::end(read_cb cb) {
if (started_ || closed()) {
return;
}
read_cb_ = std::move(cb);
started_ = true;
auto handler = handler_.lock();
auto stream = stream_.lock();
if (handler->start_response(*stream) != 0) {
handler->stream_error(stream->get_stream_id(), NGHTTP2_INTERNAL_ERROR);
return;
}
if (!handler->inside_callback()) {
handler->initiate_write();
}
}
bool response_impl::closed() const {
return handler_.expired() || stream_.expired();
}
void response_impl::resume() {
if (closed()) {
return;
}
auto handler = handler_.lock();
auto stream = stream_.lock();
handler->resume(*stream);
if (!handler->inside_callback()) {
handler->initiate_write();
}
}
bool response_impl::started() const { return started_; }
const std::vector<header> &response_impl::headers() const { return headers_; }
void response_impl::handler(std::weak_ptr<http2_handler> h) {
handler_ = std::move(h);
}
void response_impl::stream(std::weak_ptr<http2_stream> s) {
stream_ = std::move(s);
}
std::pair<ssize_t, bool> response_impl::call_read(uint8_t *data,
std::size_t len) {
if (read_cb_) {
return read_cb_(data, len);
}
return std::make_pair(0, true);
}
http2_stream::http2_stream(int32_t stream_id)
: request_(std::make_shared<request>()),
response_(std::make_shared<response>()), stream_id_(stream_id) {}
int32_t http2_stream::get_stream_id() const { return stream_id_; }
const std::shared_ptr<request> &http2_stream::get_request() { return request_; }
const std::shared_ptr<response> &http2_stream::get_response() {
return response_;
}
namespace {
int stream_error(nghttp2_session *session, int32_t stream_id,
uint32_t error_code) {
return nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id,
error_code);
}
} // namespace
namespace {
int on_begin_headers_callback(nghttp2_session *session,
const nghttp2_frame *frame, void *user_data) {
auto handler = static_cast<http2_handler *>(user_data);
if (frame->hd.type != NGHTTP2_HEADERS ||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
return 0;
}
handler->create_stream(frame->hd.stream_id);
return 0;
}
} // namespace
namespace {
int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen, uint8_t flags,
void *user_data) {
auto handler = static_cast<http2_handler *>(user_data);
auto stream_id = frame->hd.stream_id;
if (frame->hd.type != NGHTTP2_HEADERS ||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
return 0;
}
auto stream = handler->find_stream(stream_id);
if (!stream) {
return 0;
}
auto &req = stream->get_request()->impl();
switch (nghttp2::http2::lookup_token(name, namelen)) {
case nghttp2::http2::HD__METHOD:
req.method(std::string(value, value + valuelen));
break;
case nghttp2::http2::HD__SCHEME:
req.scheme(std::string(value, value + valuelen));
break;
case nghttp2::http2::HD__AUTHORITY:
req.authority(std::string(value, value + valuelen));
break;
case nghttp2::http2::HD__PATH:
req.path(std::string(value, value + valuelen));
break;
case nghttp2::http2::HD_HOST:
req.host(std::string(value, value + valuelen));
// fall through
default:
req.add_header(std::string(name, name + namelen),
std::string(value, value + valuelen));
}
return 0;
}
} // namespace
namespace {
int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
void *user_data) {
auto handler = static_cast<http2_handler *>(user_data);
auto stream = handler->find_stream(frame->hd.stream_id);
switch (frame->hd.type) {
case NGHTTP2_DATA:
if (!stream) {
break;
}
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
stream->get_request()->impl().call_on_end();
}
break;
case NGHTTP2_HEADERS: {
if (!stream || frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
break;
}
auto &req = stream->get_request()->impl();
if (req.host().empty()) {
req.host(req.authority());
}
handler->call_on_request(*stream);
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
stream->get_request()->impl().call_on_end();
}
break;
}
}
return 0;
}
} // namespace
namespace {
int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
int32_t stream_id, const uint8_t *data,
size_t len, void *user_data) {
auto handler = static_cast<http2_handler *>(user_data);
auto stream = handler->find_stream(stream_id);
if (!stream) {
return 0;
}
stream->get_request()->impl().call_on_data(data, len);
return 0;
}
} // namespace
namespace {
int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
uint32_t error_code, void *user_data) {
auto handler = static_cast<http2_handler *>(user_data);
handler->close_stream(stream_id);
return 0;
}
} // namespace
namespace {
int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame,
void *user_data) {
auto handler = static_cast<http2_handler *>(user_data);
if (frame->hd.type != NGHTTP2_PUSH_PROMISE) {
return 0;
}
auto stream = handler->find_stream(frame->push_promise.promised_stream_id);
if (!stream) {
return 0;
}
handler->call_on_request(*stream);
return 0;
}
} // namespace
namespace {
int on_frame_not_send_callback(nghttp2_session *session,
const nghttp2_frame *frame, int lib_error_code,
void *user_data) {
if (frame->hd.type != NGHTTP2_HEADERS) {
return 0;
}
// Issue RST_STREAM so that stream does not hang around.
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id,
NGHTTP2_INTERNAL_ERROR);
return 0;
}
} // namespace
http2_handler::http2_handler(boost::asio::io_service &io_service,
boost::asio::io_service &task_io_service_,
connection_write writefun, request_cb cb)
: writefun_(writefun), request_cb_(std::move(cb)), io_service_(io_service),
task_io_service_(task_io_service_),
strand_(std::make_shared<boost::asio::io_service::strand>(io_service_)),
session_(nullptr), buf_(nullptr), buflen_(0), inside_callback_(false) {}
http2_handler::~http2_handler() { nghttp2_session_del(session_); }
int http2_handler::start() {
int rv;
nghttp2_session_callbacks *callbacks;
rv = nghttp2_session_callbacks_new(&callbacks);
if (rv != 0) {
return -1;
}
auto cb_del = defer(nghttp2_session_callbacks_del, callbacks);
nghttp2_session_callbacks_set_on_begin_headers_callback(
callbacks, on_begin_headers_callback);
nghttp2_session_callbacks_set_on_header_callback(callbacks,
on_header_callback);
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
on_frame_recv_callback);
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
callbacks, on_data_chunk_recv_callback);
nghttp2_session_callbacks_set_on_stream_close_callback(
callbacks, on_stream_close_callback);
nghttp2_session_callbacks_set_on_frame_send_callback(callbacks,
on_frame_send_callback);
nghttp2_session_callbacks_set_on_frame_not_send_callback(
callbacks, on_frame_not_send_callback);
nghttp2_option *option;
rv = nghttp2_option_new(&option);
if (rv != 0) {
return -1;
}
auto opt_del = defer(nghttp2_option_del, option);
nghttp2_option_set_recv_client_preface(option, 1);
rv = nghttp2_session_server_new2(&session_, callbacks, this, option);
if (rv != 0) {
return -1;
}
nghttp2_settings_entry ent{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100};
nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, &ent, 1);
return 0;
}
std::shared_ptr<http2_stream> http2_handler::create_stream(int32_t stream_id) {
auto stream = std::make_shared<http2_stream>(stream_id);
streams_.emplace(stream_id, stream);
auto self = shared_from_this();
auto &req = stream->get_request()->impl();
auto &res = stream->get_response()->impl();
req.handler(self);
req.stream(stream);
res.handler(self);
res.stream(stream);
return stream;
}
void http2_handler::close_stream(int32_t stream_id) {
streams_.erase(stream_id);
}
std::shared_ptr<http2_stream> http2_handler::find_stream(int32_t stream_id) {
auto i = streams_.find(stream_id);
if (i == std::end(streams_)) {
return nullptr;
}
return (*i).second;
}
void http2_handler::call_on_request(http2_stream &stream) {
request_cb_(stream.get_request(), stream.get_response());
}
bool http2_handler::should_stop() const {
return !nghttp2_session_want_read(session_) &&
!nghttp2_session_want_write(session_);
}
int http2_handler::start_response(http2_stream &stream) {
int rv;
auto &res = stream.get_response()->impl();
auto &headers = res.headers();
auto nva = std::vector<nghttp2_nv>();
nva.reserve(2 + headers.size());
auto status = util::utos(res.status_code());
auto date = cached_date;
nva.push_back(nghttp2::http2::make_nv_ls(":status", status));
nva.push_back(nghttp2::http2::make_nv_ls("date", *date));
for (auto &hd : headers) {
nva.push_back(nghttp2::http2::make_nv(hd.name, hd.value));
}
nghttp2_data_provider prd;
prd.source.ptr = &stream;
prd.read_callback =
[](nghttp2_session *session, int32_t stream_id, uint8_t *buf,
size_t length, uint32_t *data_flags, nghttp2_data_source *source,
void *user_data) -> ssize_t {
auto &stream = *static_cast<http2_stream *>(source->ptr);
auto rv = stream.get_response()->impl().call_read(buf, length);
if (rv.first < 0) {
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
if (rv.second) {
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
} else if (rv.first == 0) {
return NGHTTP2_ERR_DEFERRED;
}
return rv.first;
};
rv = nghttp2_submit_response(session_, stream.get_stream_id(), nva.data(),
nva.size(), &prd);
if (rv != 0) {
return -1;
}
return 0;
}
void http2_handler::enter_callback() {
assert(!inside_callback_);
inside_callback_ = true;
}
void http2_handler::leave_callback() {
assert(inside_callback_);
inside_callback_ = false;
}
bool http2_handler::inside_callback() const { return inside_callback_; }
void http2_handler::stream_error(int32_t stream_id, uint32_t error_code) {
::nghttp2::asio_http2::server::stream_error(session_, stream_id, error_code);
}
void http2_handler::initiate_write() { writefun_(); }
void http2_handler::resume(http2_stream &stream) {
nghttp2_session_resume_data(session_, stream.get_stream_id());
}
int http2_handler::push_promise(http2_stream &stream, std::string method,
std::string path, std::vector<header> headers) {
int rv;
auto &req = stream.get_request()->impl();
auto nva = std::vector<nghttp2_nv>();
nva.reserve(5 + headers.size());
nva.push_back(nghttp2::http2::make_nv_ls(":method", method));
nva.push_back(nghttp2::http2::make_nv_ls(":scheme", req.scheme()));
if (!req.authority().empty()) {
nva.push_back(nghttp2::http2::make_nv_ls(":authority", req.authority()));
}
nva.push_back(nghttp2::http2::make_nv_ls(":path", path));
if (!req.host().empty()) {
nva.push_back(nghttp2::http2::make_nv_ls("host", req.host()));
}
for (auto &hd : headers) {
nva.push_back(nghttp2::http2::make_nv(hd.name, hd.value));
}
rv = nghttp2_submit_push_promise(session_, NGHTTP2_FLAG_NONE,
stream.get_stream_id(), nva.data(),
nva.size(), nullptr);
if (rv < 0) {
return -1;
}
auto promised_stream = create_stream(rv);
auto &promised_req = promised_stream->get_request()->impl();
promised_req.pushed(true);
promised_req.method(std::move(method));
promised_req.scheme(req.scheme());
promised_req.authority(req.authority());
promised_req.path(std::move(path));
promised_req.host(req.host());
promised_req.set_header(std::move(headers));
if (!req.host().empty()) {
promised_req.add_header("host", req.host());
}
return 0;
}
bool http2_handler::run_task(thread_cb start) {
auto strand = strand_;
try {
task_io_service_.post([start, strand]() {
channel chan;
chan.impl().strand(strand.get());
start(chan);
});
return true;
} catch (std::exception &ex) {
return false;
}
}
boost::asio::io_service &http2_handler::io_service() { return io_service_; }
callback_guard::callback_guard(http2_handler &h) : handler(h) {
handler.enter_callback();
}
callback_guard::~callback_guard() { handler.leave_callback(); }
} // namespace server
} // namespace asio_http2
} // namespace nghttp2

View File

@@ -1,265 +0,0 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2014 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef HTTP2_HANDLER_H
#define HTTP2_HANDLER_H
#include "nghttp2_config.h"
#include <map>
#include <vector>
#include <functional>
#include <string>
#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <nghttp2/nghttp2.h>
#include <nghttp2/asio_http2.h>
namespace nghttp2 {
namespace asio_http2 {
class channel_impl {
public:
channel_impl();
void post(void_cb cb);
void strand(boost::asio::io_service::strand *strand);
private:
boost::asio::io_service::strand *strand_;
};
namespace server {
class http2_handler;
class http2_stream;
class request_impl {
public:
request_impl();
const std::vector<header> &headers() const;
const std::string &method() const;
const std::string &scheme() const;
const std::string &authority() const;
const std::string &host() const;
const std::string &path() const;
bool push(std::string method, std::string path,
std::vector<header> headers = {});
bool pushed() const;
bool closed() const;
void on_data(data_cb cb);
void on_end(void_cb cb);
bool run_task(thread_cb start);
void set_header(std::vector<header> headers);
void add_header(std::string name, std::string value);
void method(std::string method);
void scheme(std::string scheme);
void authority(std::string authority);
void host(std::string host);
void path(std::string path);
void pushed(bool f);
void handler(std::weak_ptr<http2_handler> h);
void stream(std::weak_ptr<http2_stream> s);
void call_on_data(const uint8_t *data, std::size_t len);
void call_on_end();
private:
std::vector<header> headers_;
std::string method_;
std::string scheme_;
std::string authority_;
std::string host_;
std::string path_;
data_cb on_data_cb_;
void_cb on_end_cb_;
std::weak_ptr<http2_handler> handler_;
std::weak_ptr<http2_stream> stream_;
bool pushed_;
};
class response_impl {
public:
response_impl();
void write_head(unsigned int status_code, std::vector<header> headers = {});
void end(std::string data = "");
void end(read_cb cb);
void resume();
bool closed() const;
unsigned int status_code() const;
const std::vector<header> &headers() const;
bool started() const;
void handler(std::weak_ptr<http2_handler> h);
void stream(std::weak_ptr<http2_stream> s);
read_cb::result_type call_read(uint8_t *data, std::size_t len);
private:
std::vector<header> headers_;
read_cb read_cb_;
std::weak_ptr<http2_handler> handler_;
std::weak_ptr<http2_stream> stream_;
unsigned int status_code_;
bool started_;
};
class http2_stream {
public:
http2_stream(int32_t stream_id);
int32_t get_stream_id() const;
const std::shared_ptr<request> &get_request();
const std::shared_ptr<response> &get_response();
private:
std::shared_ptr<request> request_;
std::shared_ptr<response> response_;
int32_t stream_id_;
};
struct callback_guard {
callback_guard(http2_handler &h);
~callback_guard();
http2_handler &handler;
};
typedef std::function<void(void)> connection_write;
class http2_handler : public std::enable_shared_from_this<http2_handler> {
public:
http2_handler(boost::asio::io_service &io_service,
boost::asio::io_service &task_io_service,
connection_write writefun, request_cb cb);
~http2_handler();
int start();
std::shared_ptr<http2_stream> create_stream(int32_t stream_id);
void close_stream(int32_t stream_id);
std::shared_ptr<http2_stream> find_stream(int32_t stream_id);
void call_on_request(http2_stream &stream);
bool should_stop() const;
int start_response(http2_stream &stream);
void stream_error(int32_t stream_id, uint32_t error_code);
void initiate_write();
void enter_callback();
void leave_callback();
bool inside_callback() const;
void resume(http2_stream &stream);
int push_promise(http2_stream &stream, std::string method, std::string path,
std::vector<header> headers);
bool run_task(thread_cb start);
boost::asio::io_service &io_service();
template <size_t N>
int on_read(const boost::array<uint8_t, N> &buffer, std::size_t len) {
callback_guard cg(*this);
int rv;
rv = nghttp2_session_mem_recv(session_, buffer.data(), len);
if (rv < 0) {
return -1;
}
return 0;
}
template <size_t N>
int on_write(boost::array<uint8_t, N> &buffer, std::size_t &len) {
callback_guard cg(*this);
len = 0;
if (buf_) {
std::copy_n(buf_, buflen_, std::begin(buffer));
len += buflen_;
buf_ = nullptr;
buflen_ = 0;
}
for (;;) {
const uint8_t *data;
auto nread = nghttp2_session_mem_send(session_, &data);
if (nread < 0) {
return -1;
}
if (nread == 0) {
break;
}
if (len + nread > buffer.size()) {
buf_ = data;
buflen_ = nread;
break;
}
std::copy_n(data, nread, std::begin(buffer) + len);
len += nread;
}
return 0;
}
private:
std::map<int32_t, std::shared_ptr<http2_stream>> streams_;
connection_write writefun_;
request_cb request_cb_;
boost::asio::io_service &io_service_;
boost::asio::io_service &task_io_service_;
std::shared_ptr<boost::asio::io_service::strand> strand_;
nghttp2_session *session_;
const uint8_t *buf_;
std::size_t buflen_;
bool inside_callback_;
};
} // namespace server
} // namespace asio_http2
} // namespace nghttp
#endif // HTTP2_HANDLER_H

View File

@@ -1,183 +0,0 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2014 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "asio_http2_impl.h"
#include <boost/asio/ssl.hpp>
#include <openssl/ssl.h>
#include <nghttp2/nghttp2.h>
#include "asio_server.h"
#include "util.h"
#include "ssl.h"
#include "template.h"
namespace nghttp2 {
namespace asio_http2 {
namespace server {
http2::http2() : impl_(make_unique<http2_impl>()) {}
http2::~http2() {}
void http2::listen(const std::string &address, uint16_t port, request_cb cb) {
impl_->listen(address, port, std::move(cb));
}
void http2::num_threads(size_t num_threads) { impl_->num_threads(num_threads); }
void http2::tls(std::string private_key_file, std::string certificate_file) {
impl_->tls(std::move(private_key_file), std::move(certificate_file));
}
void http2::num_concurrent_tasks(size_t num_concurrent_tasks) {
impl_->num_concurrent_tasks(num_concurrent_tasks);
}
void http2::backlog(int backlog) { impl_->backlog(backlog); }
http2_impl::http2_impl()
: num_threads_(1), num_concurrent_tasks_(1), backlog_(-1) {}
namespace {
std::vector<unsigned char> &get_alpn_token() {
static auto alpn_token = util::get_default_alpn();
return alpn_token;
}
} // namespace
void http2_impl::listen(const std::string &address, uint16_t port,
request_cb cb) {
std::unique_ptr<boost::asio::ssl::context> ssl_ctx;
if (!private_key_file_.empty() && !certificate_file_.empty()) {
ssl_ctx = make_unique<boost::asio::ssl::context>(
boost::asio::ssl::context::sslv23);
ssl_ctx->use_private_key_file(private_key_file_,
boost::asio::ssl::context::pem);
ssl_ctx->use_certificate_chain_file(certificate_file_);
auto ctx = ssl_ctx->native_handle();
SSL_CTX_set_options(ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
SSL_OP_NO_COMPRESSION |
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION |
SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_TICKET |
SSL_OP_CIPHER_SERVER_PREFERENCE);
SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
SSL_CTX_set_mode(ctx, SSL_MODE_RELEASE_BUFFERS);
SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
SSL_CTX_set_cipher_list(ctx, ssl::DEFAULT_CIPHER_LIST);
auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
if (ecdh) {
SSL_CTX_set_tmp_ecdh(ctx, ecdh);
EC_KEY_free(ecdh);
}
SSL_CTX_set_next_protos_advertised_cb(
ctx,
[](SSL *s, const unsigned char **data, unsigned int *len, void *arg) {
auto &token = get_alpn_token();
*data = token.data();
*len = token.size();
return SSL_TLSEXT_ERR_OK;
},
nullptr);
}
server(address, port, num_threads_, num_concurrent_tasks_, std::move(cb),
std::move(ssl_ctx), backlog_).run();
}
void http2_impl::num_threads(size_t num_threads) { num_threads_ = num_threads; }
void http2_impl::tls(std::string private_key_file,
std::string certificate_file) {
private_key_file_ = std::move(private_key_file);
certificate_file_ = std::move(certificate_file);
}
void http2_impl::num_concurrent_tasks(size_t num_concurrent_tasks) {
num_concurrent_tasks_ = num_concurrent_tasks;
}
void http2_impl::backlog(int backlog) { backlog_ = backlog; }
} // namespace server
template <typename F, typename... T>
std::shared_ptr<Defer<F, T...>> defer_shared(F &&f, T &&... t) {
return std::make_shared<Defer<F, T...>>(std::forward<F>(f),
std::forward<T>(t)...);
}
read_cb file_reader(const std::string &path) {
auto fd = open(path.c_str(), O_RDONLY);
if (fd == -1) {
return read_cb();
}
return file_reader_from_fd(fd);
}
read_cb file_reader_from_fd(int fd) {
auto d = defer_shared(close, fd);
return [fd, d](uint8_t *buf, size_t len) -> read_cb::result_type {
int rv;
while ((rv = read(fd, buf, len)) == -1 && errno == EINTR)
;
if (rv == -1) {
return std::make_pair(-1, false);
}
if (rv == 0) {
return std::make_pair(rv, true);
}
return std::make_pair(rv, false);
};
}
bool check_path(const std::string &path) { return util::check_path(path); }
std::string percent_decode(const std::string &s) {
return util::percentDecode(std::begin(s), std::end(s));
}
std::string http_date(int64_t t) { return util::http_date(t); }
} // namespace asio_http2
} // namespace nghttp2

View File

@@ -33,22 +33,15 @@
// Distributed under the Boost Software License, Version 1.0. (See accompanying // Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
// //
#include "asio_io_service_pool.h"
#include "asio_server.h"
#include <stdexcept>
#include <future> #include <future>
#include <boost/thread/thread.hpp>
#include <boost/bind.hpp>
namespace nghttp2 { namespace nghttp2 {
namespace asio_http2 { namespace asio_http2 {
namespace server { io_service_pool::io_service_pool(std::size_t pool_size) : next_io_service_(0) {
io_service_pool::io_service_pool(std::size_t pool_size,
std::size_t thread_pool_size)
: next_io_service_(0), thread_pool_size_(thread_pool_size) {
if (pool_size == 0) { if (pool_size == 0) {
throw std::runtime_error("io_service_pool size is 0"); throw std::runtime_error("io_service_pool size is 0");
} }
@@ -61,16 +54,9 @@ io_service_pool::io_service_pool(std::size_t pool_size,
io_services_.push_back(io_service); io_services_.push_back(io_service);
work_.push_back(work); work_.push_back(work);
} }
auto work = std::make_shared<boost::asio::io_service::work>(task_io_service_);
work_.push_back(work);
} }
void io_service_pool::run() { void io_service_pool::run() {
for (std::size_t i = 0; i < thread_pool_size_; ++i) {
thread_pool_.create_thread([this]() { task_io_service_.run(); });
}
// Create a pool of threads to run all of the io_services. // Create a pool of threads to run all of the io_services.
auto futs = std::vector<std::future<std::size_t>>(); auto futs = std::vector<std::future<std::size_t>>();
@@ -85,8 +71,6 @@ void io_service_pool::run() {
for (auto &fut : futs) { for (auto &fut : futs) {
fut.get(); fut.get();
} }
thread_pool_.join_all();
} }
void io_service_pool::stop() { void io_service_pool::stop() {
@@ -94,8 +78,6 @@ void io_service_pool::stop() {
for (auto &iosv : io_services_) { for (auto &iosv : io_services_) {
iosv->stop(); iosv->stop();
} }
task_io_service_.stop();
} }
boost::asio::io_service &io_service_pool::get_io_service() { boost::asio::io_service &io_service_pool::get_io_service() {
@@ -108,12 +90,6 @@ boost::asio::io_service &io_service_pool::get_io_service() {
return io_service; return io_service;
} }
boost::asio::io_service &io_service_pool::get_task_io_service() {
return task_io_service_;
}
} // namespace server
} // namespace asio_http2 } // namespace asio_http2
} // namespace nghttp2 } // namespace nghttp2

View File

@@ -34,14 +34,14 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
// //
#ifndef HTTP_SERVER2_IO_SERVICE_POOL_HPP #ifndef ASIO_IO_SERVICE_POOL_H
#define HTTP_SERVER2_IO_SERVICE_POOL_HPP #define ASIO_IO_SERVICE_POOL_H
#include "nghttp2_config.h" #include "nghttp2_config.h"
#include <vector> #include <vector>
#include <memory> #include <memory>
#include <boost/asio.hpp>
#include <boost/noncopyable.hpp> #include <boost/noncopyable.hpp>
#include <boost/thread.hpp> #include <boost/thread.hpp>
@@ -51,13 +51,11 @@ namespace nghttp2 {
namespace asio_http2 { namespace asio_http2 {
namespace server {
/// A pool of io_service objects. /// A pool of io_service objects.
class io_service_pool : private boost::noncopyable { class io_service_pool : private boost::noncopyable {
public: public:
/// Construct the io_service pool. /// Construct the io_service pool.
explicit io_service_pool(std::size_t pool_size, std::size_t thread_pool_size); explicit io_service_pool(std::size_t pool_size);
/// Run all io_service objects in the pool. /// Run all io_service objects in the pool.
void run(); void run();
@@ -68,31 +66,19 @@ public:
/// Get an io_service to use. /// Get an io_service to use.
boost::asio::io_service &get_io_service(); boost::asio::io_service &get_io_service();
boost::asio::io_service &get_task_io_service();
private: private:
typedef std::shared_ptr<boost::asio::io_service> io_service_ptr;
typedef std::shared_ptr<boost::asio::io_service::work> work_ptr;
/// The pool of io_services. /// The pool of io_services.
std::vector<io_service_ptr> io_services_; std::vector<std::shared_ptr<boost::asio::io_service>> io_services_;
boost::asio::io_service task_io_service_;
boost::thread_group thread_pool_;
/// The work that keeps the io_services running. /// The work that keeps the io_services running.
std::vector<work_ptr> work_; std::vector<std::shared_ptr<boost::asio::io_service::work>> work_;
/// The next io_service to use for a connection. /// The next io_service to use for a connection.
std::size_t next_io_service_; std::size_t next_io_service_;
std::size_t thread_pool_size_;
}; };
} // namespace server
} // namespace asio_http2 } // namespace asio_http2
} // namespace nghttp2 } // namespace nghttp2
#endif // HTTP_SERVER2_IO_SERVICE_POOL_HPP #endif // ASIO_IO_SERVICE_POOL_H

View File

@@ -36,125 +36,124 @@
#include "asio_server.h" #include "asio_server.h"
#include <boost/date_time/posix_time/posix_time.hpp> #include "asio_server_connection.h"
#include "util.h"
namespace nghttp2 { namespace nghttp2 {
namespace asio_http2 { namespace asio_http2 {
namespace server { namespace server {
server::server(const std::string &address, uint16_t port, server::server(std::size_t io_service_pool_size)
std::size_t io_service_pool_size, std::size_t thread_pool_size, : io_service_pool_(io_service_pool_size) {}
request_cb cb,
std::unique_ptr<boost::asio::ssl::context> ssl_ctx, int backlog)
: io_service_pool_(io_service_pool_size, thread_pool_size),
signals_(io_service_pool_.get_io_service()),
tick_timer_(io_service_pool_.get_io_service(),
boost::posix_time::seconds(1)),
ssl_ctx_(std::move(ssl_ctx)), request_cb_(std::move(cb)) {
// Register to handle the signals that indicate when the server should exit.
// It is safe to register for the same signal multiple times in a program,
// provided all registration for the specified signal is made through Asio.
signals_.add(SIGINT);
signals_.add(SIGTERM);
#if defined(SIGQUIT)
signals_.add(SIGQUIT);
#endif // defined(SIGQUIT)
signals_.async_wait([this](const boost::system::error_code &error,
int signal_number) { io_service_pool_.stop(); });
// Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR). boost::system::error_code
boost::asio::ip::tcp::resolver resolver(io_service_pool_.get_io_service()); server::listen_and_serve(boost::system::error_code &ec,
boost::asio::ip::tcp::resolver::query query(address, std::to_string(port)); boost::asio::ssl::context *tls_context,
const std::string &address, const std::string &port,
int backlog, serve_mux &mux) {
ec.clear();
for (auto itr = resolver.resolve(query); if (bind_and_listen(ec, address, port, backlog)) {
itr != boost::asio::ip::tcp::resolver::iterator(); ++itr) { return ec;
boost::asio::ip::tcp::endpoint endpoint = *itr; }
auto acceptor =
boost::asio::ip::tcp::acceptor(io_service_pool_.get_io_service());
acceptor.open(endpoint.protocol()); for (auto &acceptor : acceptors_) {
acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); if (tls_context) {
acceptor.bind(endpoint); start_accept(*tls_context, acceptor, mux);
if (backlog == -1) {
acceptor.listen();
} else { } else {
acceptor.listen(backlog); start_accept(acceptor, mux);
} }
}
io_service_pool_.run();
return ec;
}
boost::system::error_code server::bind_and_listen(boost::system::error_code &ec,
const std::string &address,
const std::string &port,
int backlog) {
// Open the acceptor with the option to reuse the address (i.e.
// SO_REUSEADDR).
tcp::resolver resolver(io_service_pool_.get_io_service());
tcp::resolver::query query(address, port);
auto it = resolver.resolve(query, ec);
if (ec) {
return ec;
}
for (; it != tcp::resolver::iterator(); ++it) {
tcp::endpoint endpoint = *it;
auto acceptor = tcp::acceptor(io_service_pool_.get_io_service());
if (acceptor.open(endpoint.protocol(), ec)) {
continue;
}
acceptor.set_option(tcp::acceptor::reuse_address(true));
if (acceptor.bind(endpoint, ec)) {
continue;
}
if (acceptor.listen(
backlog == -1 ? boost::asio::socket_base::max_connections : backlog,
ec)) {
continue;
}
acceptors_.push_back(std::move(acceptor)); acceptors_.push_back(std::move(acceptor));
} }
start_accept(); if (acceptors_.empty()) {
return ec;
}
start_timer(); // ec could have some errors since we may have failed to bind some
// interfaces.
ec.clear();
return ec;
} }
void server::run() { io_service_pool_.run(); } void server::start_accept(boost::asio::ssl::context &tls_context,
tcp::acceptor &acceptor, serve_mux &mux) {
auto new_connection = std::make_shared<connection<ssl_socket>>(
mux, io_service_pool_.get_io_service(), tls_context);
std::shared_ptr<std::string> cached_date; acceptor.async_accept(new_connection->socket().lowest_layer(),
[this, &tls_context, &acceptor, &mux, new_connection](
const boost::system::error_code &e) {
if (!e) {
new_connection->socket().lowest_layer().set_option(tcp::no_delay(true));
new_connection->socket().async_handshake(
boost::asio::ssl::stream_base::server,
[new_connection](const boost::system::error_code &e) {
if (!e) {
new_connection->start();
}
});
}
namespace { start_accept(tls_context, acceptor, mux);
void update_date() {
cached_date = std::make_shared<std::string>(util::http_date(time(nullptr)));
}
} // namespace
void server::start_timer() {
update_date();
tick_timer_.async_wait([this](const boost::system::error_code &e) {
tick_timer_.expires_at(tick_timer_.expires_at() +
boost::posix_time::seconds(1));
start_timer();
}); });
} }
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ssl_socket; void server::start_accept(tcp::acceptor &acceptor, serve_mux &mux) {
auto new_connection = std::make_shared<connection<tcp::socket>>(
mux, io_service_pool_.get_io_service());
void server::start_accept() { acceptor.async_accept(new_connection->socket(),
if (ssl_ctx_) { [this, &acceptor, &mux, new_connection](
auto new_connection = std::make_shared<connection<ssl_socket>>( const boost::system::error_code &e) {
request_cb_, io_service_pool_.get_task_io_service(), if (!e) {
io_service_pool_.get_io_service(), *ssl_ctx_); new_connection->socket().set_option(tcp::no_delay(true));
new_connection->start();
for (auto &acceptor : acceptors_) {
acceptor.async_accept(
new_connection->socket().lowest_layer(),
[this, new_connection](const boost::system::error_code &e) {
if (!e) {
new_connection->socket().lowest_layer().set_option(
boost::asio::ip::tcp::no_delay(true));
new_connection->socket().async_handshake(
boost::asio::ssl::stream_base::server,
[new_connection](const boost::system::error_code &e) {
if (!e) {
new_connection->start();
}
});
}
start_accept();
});
} }
} else {
auto new_connection =
std::make_shared<connection<boost::asio::ip::tcp::socket>>(
request_cb_, io_service_pool_.get_task_io_service(),
io_service_pool_.get_io_service());
for (auto &acceptor : acceptors_) { start_accept(acceptor, mux);
acceptor.async_accept( });
new_connection->socket(),
[this, new_connection](const boost::system::error_code &e) {
if (!e) {
new_connection->socket().set_option(
boost::asio::ip::tcp::no_delay(true));
new_connection->start();
}
start_accept();
});
}
}
} }
} // namespace server } // namespace server

View File

@@ -34,21 +34,19 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
// //
#ifndef HTTP_SERVER2_SERVER_HPP #ifndef ASIO_SERVER_H
#define HTTP_SERVER2_SERVER_HPP #define ASIO_SERVER_H
#include "nghttp2_config.h" #include "nghttp2_config.h"
#include <string> #include <string>
#include <vector> #include <vector>
#include <memory> #include <memory>
#include <boost/noncopyable.hpp> #include <boost/noncopyable.hpp>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <nghttp2/asio_http2.h> #include <nghttp2/asio_http2_server.h>
#include "asio_connection.h"
#include "asio_io_service_pool.h" #include "asio_io_service_pool.h"
namespace nghttp2 { namespace nghttp2 {
@@ -57,40 +55,43 @@ namespace asio_http2 {
namespace server { namespace server {
/// The top-level class of the HTTP server. class serve_mux;
using boost::asio::ip::tcp;
using ssl_socket = boost::asio::ssl::stream<tcp::socket>;
class server : private boost::noncopyable { class server : private boost::noncopyable {
public: public:
/// Construct the server to listen on the specified TCP address and port, and explicit server(std::size_t io_service_pool_size);
/// serve up files from the given directory.
explicit server(const std::string &address, uint16_t port,
std::size_t io_service_pool_size,
std::size_t thread_pool_size, request_cb cb,
std::unique_ptr<boost::asio::ssl::context> ssl_ctx,
int backlog = -1);
/// Run the server's io_service loop. boost::system::error_code
void run(); 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);
private: private:
/// Initiate an asynchronous accept operation. /// Initiate an asynchronous accept operation.
void start_accept(); void start_accept(tcp::acceptor &acceptor, serve_mux &mux);
/// Same as above but with tls_context
void start_accept(boost::asio::ssl::context &tls_context,
tcp::acceptor &acceptor, serve_mux &mux);
void start_timer(); /// Resolves address and bind socket to the resolved addresses.
boost::system::error_code bind_and_listen(boost::system::error_code &ec,
const std::string &address,
const std::string &port,
int backlog);
/// The pool of io_service objects used to perform asynchronous operations. /// The pool of io_service objects used to perform asynchronous
/// operations.
io_service_pool io_service_pool_; io_service_pool io_service_pool_;
/// The signal_set is used to register for process termination notifications.
boost::asio::signal_set signals_;
boost::asio::deadline_timer tick_timer_;
/// Acceptor used to listen for incoming connections. /// Acceptor used to listen for incoming connections.
std::vector<boost::asio::ip::tcp::acceptor> acceptors_; std::vector<tcp::acceptor> acceptors_;
std::unique_ptr<boost::asio::ssl::context> ssl_ctx_; std::unique_ptr<boost::asio::ssl::context> ssl_ctx_;
request_cb request_cb_;
}; };
} // namespace server } // namespace server
@@ -99,4 +100,4 @@ private:
} // namespace nghttp2 } // namespace nghttp2
#endif // HTTP_SERVER2_SERVER_HPP #endif // ASIO_SERVER_H

View File

@@ -34,19 +34,20 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
// //
#ifndef HTTP_SERVER2_CONNECTION_HPP #ifndef ASIO_SERVER_CONNECTION_H
#define HTTP_SERVER2_CONNECTION_HPP #define ASIO_SERVER_CONNECTION_H
#include "nghttp2_config.h" #include "nghttp2_config.h"
#include <memory> #include <memory>
#include <boost/asio.hpp>
#include <boost/noncopyable.hpp> #include <boost/noncopyable.hpp>
#include <boost/array.hpp> #include <boost/array.hpp>
#include <nghttp2/asio_http2.h> #include <nghttp2/asio_http2_server.h>
#include "asio_http2_handler.h"
#include "asio_server_http2_handler.h"
#include "asio_server_serve_mux.h"
#include "util.h" #include "util.h"
namespace nghttp2 { namespace nghttp2 {
@@ -62,16 +63,14 @@ class connection : public std::enable_shared_from_this<connection<socket_type>>,
public: public:
/// Construct a connection with the given io_service. /// Construct a connection with the given io_service.
template <typename... SocketArgs> template <typename... SocketArgs>
explicit connection(request_cb cb, boost::asio::io_service &task_io_service, explicit connection(serve_mux &mux, SocketArgs &&... args)
SocketArgs &&... args) : socket_(std::forward<SocketArgs>(args)...), mux_(mux), writing_(false) {
: socket_(std::forward<SocketArgs>(args)...), request_cb_(std::move(cb)), }
task_io_service_(task_io_service), writing_(false) {}
/// Start the first asynchronous operation for the connection. /// Start the first asynchronous operation for the connection.
void start() { void start() {
handler_ = std::make_shared<http2_handler>( handler_ = std::make_shared<http2_handler>(socket_.get_io_service(),
socket_.get_io_service(), task_io_service_, [this]() { do_write(); }, [this]() { do_write(); }, mux_);
request_cb_);
if (handler_->start() != 0) { if (handler_->start() != 0) {
return; return;
} }
@@ -149,9 +148,7 @@ public:
private: private:
socket_type socket_; socket_type socket_;
request_cb request_cb_; serve_mux &mux_;
boost::asio::io_service &task_io_service_;
std::shared_ptr<http2_handler> handler_; std::shared_ptr<http2_handler> handler_;
@@ -169,4 +166,4 @@ private:
} // namespace nghttp2 } // namespace nghttp2
#endif // HTTP_SERVER2_CONNECTION_HPP #endif // ASIO_SERVER_CONNECTION_H

80
src/asio_server_http2.cc Normal file
View File

@@ -0,0 +1,80 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2014 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "nghttp2_config.h"
#include <nghttp2/asio_http2_server.h>
#include "asio_server_http2_impl.h"
#include "asio_server.h"
#include "template.h"
namespace nghttp2 {
namespace asio_http2 {
namespace server {
http2::http2() : impl_(make_unique<http2_impl>()) {}
http2::~http2() {}
http2::http2(http2 &&other) noexcept : impl_(std::move(other.impl_)) {}
http2 &http2::operator=(http2 &&other) noexcept {
if (this == &other) {
return *this;
}
impl_ = std::move(other.impl_);
return *this;
}
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);
}
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);
}
void http2::num_threads(size_t num_threads) { impl_->num_threads(num_threads); }
void http2::backlog(int backlog) { impl_->backlog(backlog); }
bool http2::handle(std::string pattern, request_cb cb) {
return impl_->handle(std::move(pattern), std::move(cb));
}
} // namespace server
} // namespace asio_http2
} // namespace nghttp2

View File

@@ -0,0 +1,472 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2014 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "asio_server_http2_handler.h"
#include <iostream>
#include "asio_common.h"
#include "asio_server_serve_mux.h"
#include "asio_server_stream.h"
#include "asio_server_request_impl.h"
#include "asio_server_response_impl.h"
#include "http2.h"
#include "util.h"
#include "template.h"
namespace nghttp2 {
namespace asio_http2 {
namespace server {
namespace {
int stream_error(nghttp2_session *session, int32_t stream_id,
uint32_t error_code) {
return nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id,
error_code);
}
} // namespace
namespace {
int on_begin_headers_callback(nghttp2_session *session,
const nghttp2_frame *frame, void *user_data) {
auto handler = static_cast<http2_handler *>(user_data);
if (frame->hd.type != NGHTTP2_HEADERS ||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
return 0;
}
handler->create_stream(frame->hd.stream_id);
return 0;
}
} // namespace
namespace {
int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen, uint8_t flags,
void *user_data) {
auto handler = static_cast<http2_handler *>(user_data);
auto stream_id = frame->hd.stream_id;
if (frame->hd.type != NGHTTP2_HEADERS ||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
return 0;
}
auto strm = handler->find_stream(stream_id);
if (!strm) {
return 0;
}
auto &req = strm->request().impl();
auto &uref = req.uri();
switch (nghttp2::http2::lookup_token(name, namelen)) {
case nghttp2::http2::HD__METHOD:
req.method(std::string(value, value + valuelen));
break;
case nghttp2::http2::HD__SCHEME:
uref.scheme.assign(value, value + valuelen);
break;
case nghttp2::http2::HD__AUTHORITY:
uref.host.assign(value, value + valuelen);
break;
case nghttp2::http2::HD__PATH:
split_path(uref, value, value + valuelen);
break;
case nghttp2::http2::HD_HOST:
if (uref.host.empty()) {
uref.host.assign(value, value + valuelen);
}
// fall through
default:
req.header().emplace(std::string(name, name + namelen),
header_value{std::string(value, value + valuelen),
(flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0});
}
return 0;
}
} // namespace
namespace {
int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
void *user_data) {
auto handler = static_cast<http2_handler *>(user_data);
auto strm = handler->find_stream(frame->hd.stream_id);
switch (frame->hd.type) {
case NGHTTP2_DATA:
if (!strm) {
break;
}
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
strm->request().impl().call_on_data(nullptr, 0);
}
break;
case NGHTTP2_HEADERS: {
if (!strm || frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
break;
}
handler->call_on_request(*strm);
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
strm->request().impl().call_on_data(nullptr, 0);
}
break;
}
}
return 0;
}
} // namespace
namespace {
int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
int32_t stream_id, const uint8_t *data,
size_t len, void *user_data) {
auto handler = static_cast<http2_handler *>(user_data);
auto strm = handler->find_stream(stream_id);
if (!strm) {
return 0;
}
strm->request().impl().call_on_data(data, len);
return 0;
}
} // namespace
namespace {
int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
uint32_t error_code, void *user_data) {
auto handler = static_cast<http2_handler *>(user_data);
auto strm = handler->find_stream(stream_id);
if (!strm) {
return 0;
}
strm->response().impl().call_on_close(error_code);
handler->close_stream(stream_id);
return 0;
}
} // namespace
namespace {
int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame,
void *user_data) {
auto handler = static_cast<http2_handler *>(user_data);
if (frame->hd.type != NGHTTP2_PUSH_PROMISE) {
return 0;
}
auto strm = handler->find_stream(frame->push_promise.promised_stream_id);
if (!strm) {
return 0;
}
auto &res = strm->response().impl();
res.push_promise_sent();
return 0;
}
} // namespace
namespace {
int on_frame_not_send_callback(nghttp2_session *session,
const nghttp2_frame *frame, int lib_error_code,
void *user_data) {
if (frame->hd.type != NGHTTP2_HEADERS) {
return 0;
}
// Issue RST_STREAM so that stream does not hang around.
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id,
NGHTTP2_INTERNAL_ERROR);
return 0;
}
} // namespace
http2_handler::http2_handler(boost::asio::io_service &io_service,
connection_write writefun, serve_mux &mux)
: writefun_(writefun), mux_(mux), io_service_(io_service),
session_(nullptr), buf_(nullptr), buflen_(0), inside_callback_(false),
tstamp_cached_(time(nullptr)),
formatted_date_(util::http_date(tstamp_cached_)) {}
http2_handler::~http2_handler() { nghttp2_session_del(session_); }
const std::string &http2_handler::http_date() {
auto t = time(nullptr);
if (t != tstamp_cached_) {
tstamp_cached_ = t;
formatted_date_ = util::http_date(t);
}
return formatted_date_;
}
int http2_handler::start() {
int rv;
nghttp2_session_callbacks *callbacks;
rv = nghttp2_session_callbacks_new(&callbacks);
if (rv != 0) {
return -1;
}
auto cb_del = defer(nghttp2_session_callbacks_del, callbacks);
nghttp2_session_callbacks_set_on_begin_headers_callback(
callbacks, on_begin_headers_callback);
nghttp2_session_callbacks_set_on_header_callback(callbacks,
on_header_callback);
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
on_frame_recv_callback);
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
callbacks, on_data_chunk_recv_callback);
nghttp2_session_callbacks_set_on_stream_close_callback(
callbacks, on_stream_close_callback);
nghttp2_session_callbacks_set_on_frame_send_callback(callbacks,
on_frame_send_callback);
nghttp2_session_callbacks_set_on_frame_not_send_callback(
callbacks, on_frame_not_send_callback);
nghttp2_option *option;
rv = nghttp2_option_new(&option);
if (rv != 0) {
return -1;
}
auto opt_del = defer(nghttp2_option_del, option);
nghttp2_option_set_recv_client_preface(option, 1);
rv = nghttp2_session_server_new2(&session_, callbacks, this, option);
if (rv != 0) {
return -1;
}
nghttp2_settings_entry ent{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100};
nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, &ent, 1);
return 0;
}
stream *http2_handler::create_stream(int32_t stream_id) {
auto p = streams_.emplace(stream_id, make_unique<stream>(this, stream_id));
assert(p.second);
return (*p.first).second.get();
}
void http2_handler::close_stream(int32_t stream_id) {
streams_.erase(stream_id);
}
stream *http2_handler::find_stream(int32_t stream_id) {
auto i = streams_.find(stream_id);
if (i == std::end(streams_)) {
return nullptr;
}
return (*i).second.get();
}
void http2_handler::call_on_request(stream &strm) {
auto cb = mux_.handler(strm.request().impl());
cb(strm.request(), strm.response());
}
bool http2_handler::should_stop() const {
return !nghttp2_session_want_read(session_) &&
!nghttp2_session_want_write(session_);
}
int http2_handler::start_response(stream &strm) {
int rv;
auto &res = strm.response().impl();
auto &header = res.header();
auto nva = std::vector<nghttp2_nv>();
nva.reserve(2 + header.size());
auto status = util::utos(res.status_code());
auto date = http_date();
nva.push_back(nghttp2::http2::make_nv_ls(":status", status));
nva.push_back(nghttp2::http2::make_nv_ls("date", date));
for (auto &hd : header) {
nva.push_back(nghttp2::http2::make_nv(hd.first, hd.second.value,
hd.second.sensitive));
}
nghttp2_data_provider *prd_ptr = nullptr, prd;
auto &req = strm.request().impl();
if (::nghttp2::http2::expect_response_body(req.method(), res.status_code())) {
prd.source.ptr = &strm;
prd.read_callback =
[](nghttp2_session *session, int32_t stream_id, uint8_t *buf,
size_t length, uint32_t *data_flags, nghttp2_data_source *source,
void *user_data) -> ssize_t {
auto &strm = *static_cast<stream *>(source->ptr);
return strm.response().impl().call_read(buf, length, data_flags);
};
prd_ptr = &prd;
}
rv = nghttp2_submit_response(session_, strm.get_stream_id(), nva.data(),
nva.size(), prd_ptr);
if (rv != 0) {
return -1;
}
signal_write();
return 0;
}
int http2_handler::submit_trailer(stream &strm, header_map h) {
int rv;
auto nva = std::vector<nghttp2_nv>();
nva.reserve(h.size());
for (auto &hd : h) {
nva.push_back(nghttp2::http2::make_nv(hd.first, hd.second.value,
hd.second.sensitive));
}
rv = nghttp2_submit_trailer(session_, strm.get_stream_id(), nva.data(),
nva.size());
if (rv != 0) {
return -1;
}
signal_write();
return 0;
}
void http2_handler::enter_callback() {
assert(!inside_callback_);
inside_callback_ = true;
}
void http2_handler::leave_callback() {
assert(inside_callback_);
inside_callback_ = false;
}
void http2_handler::stream_error(int32_t stream_id, uint32_t error_code) {
::nghttp2::asio_http2::server::stream_error(session_, stream_id, error_code);
signal_write();
}
void http2_handler::signal_write() {
if (!inside_callback_) {
initiate_write();
}
}
void http2_handler::initiate_write() { writefun_(); }
void http2_handler::resume(stream &strm) {
nghttp2_session_resume_data(session_, strm.get_stream_id());
signal_write();
}
response *http2_handler::push_promise(boost::system::error_code &ec,
stream &strm, std::string method,
std::string raw_path_query,
header_map h) {
int rv;
ec.clear();
auto &req = strm.request().impl();
auto nva = std::vector<nghttp2_nv>();
nva.reserve(4 + h.size());
nva.push_back(nghttp2::http2::make_nv_ls(":method", method));
nva.push_back(nghttp2::http2::make_nv_ls(":scheme", req.uri().scheme));
nva.push_back(nghttp2::http2::make_nv_ls(":authority", req.uri().host));
nva.push_back(nghttp2::http2::make_nv_ls(":path", raw_path_query));
for (auto &hd : h) {
nva.push_back(nghttp2::http2::make_nv(hd.first, hd.second.value,
hd.second.sensitive));
}
rv = nghttp2_submit_push_promise(session_, NGHTTP2_FLAG_NONE,
strm.get_stream_id(), nva.data(), nva.size(),
nullptr);
if (rv < 0) {
ec = make_error_code(static_cast<nghttp2_error>(rv));
return nullptr;
}
auto promised_strm = create_stream(rv);
auto &promised_req = promised_strm->request().impl();
promised_req.header(std::move(h));
promised_req.method(std::move(method));
auto &uref = promised_req.uri();
uref.scheme = req.uri().scheme;
uref.host = req.uri().host;
split_path(uref, std::begin(raw_path_query), std::end(raw_path_query));
auto &promised_res = promised_strm->response().impl();
promised_res.pushed(true);
signal_write();
return &promised_strm->response();
}
boost::asio::io_service &http2_handler::io_service() { return io_service_; }
callback_guard::callback_guard(http2_handler &h) : handler(h) {
handler.enter_callback();
}
callback_guard::~callback_guard() { handler.leave_callback(); }
} // namespace server
} // namespace asio_http2
} // namespace nghttp2

View File

@@ -0,0 +1,167 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2014 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef ASIO_SERVER_HTTP2_HANDLER_H
#define ASIO_SERVER_HTTP2_HANDLER_H
#include "nghttp2_config.h"
#include <map>
#include <functional>
#include <string>
#include <boost/array.hpp>
#include <nghttp2/asio_http2_server.h>
namespace nghttp2 {
namespace asio_http2 {
namespace server {
class http2_handler;
class stream;
class serve_mux;
struct callback_guard {
callback_guard(http2_handler &h);
~callback_guard();
http2_handler &handler;
};
using connection_write = std::function<void(void)>;
class http2_handler : public std::enable_shared_from_this<http2_handler> {
public:
http2_handler(boost::asio::io_service &io_service, connection_write writefun,
serve_mux &mux);
~http2_handler();
int start();
stream *create_stream(int32_t stream_id);
void close_stream(int32_t stream_id);
stream *find_stream(int32_t stream_id);
void call_on_request(stream &s);
bool should_stop() const;
int start_response(stream &s);
int submit_trailer(stream &s, header_map h);
void stream_error(int32_t stream_id, uint32_t error_code);
void initiate_write();
void enter_callback();
void leave_callback();
void resume(stream &s);
response *push_promise(boost::system::error_code &ec, stream &s,
std::string method, std::string raw_path_query,
header_map h);
void signal_write();
boost::asio::io_service &io_service();
const std::string &http_date();
template <size_t N>
int on_read(const boost::array<uint8_t, N> &buffer, std::size_t len) {
callback_guard cg(*this);
int rv;
rv = nghttp2_session_mem_recv(session_, buffer.data(), len);
if (rv < 0) {
return -1;
}
return 0;
}
template <size_t N>
int on_write(boost::array<uint8_t, N> &buffer, std::size_t &len) {
callback_guard cg(*this);
len = 0;
if (buf_) {
std::copy_n(buf_, buflen_, std::begin(buffer));
len += buflen_;
buf_ = nullptr;
buflen_ = 0;
}
for (;;) {
const uint8_t *data;
auto nread = nghttp2_session_mem_send(session_, &data);
if (nread < 0) {
return -1;
}
if (nread == 0) {
break;
}
if (len + nread > buffer.size()) {
buf_ = data;
buflen_ = nread;
break;
}
std::copy_n(data, nread, std::begin(buffer) + len);
len += nread;
}
return 0;
}
private:
std::map<int32_t, std::shared_ptr<stream>> streams_;
connection_write writefun_;
serve_mux &mux_;
boost::asio::io_service &io_service_;
nghttp2_session *session_;
const uint8_t *buf_;
std::size_t buflen_;
bool inside_callback_;
time_t tstamp_cached_;
std::string formatted_date_;
};
} // namespace server
} // namespace asio_http2
} // namespace nghttp
#endif // ASIO_SERVER_HTTP2_HANDLER_H

View File

@@ -0,0 +1,61 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2014 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "asio_server_http2_impl.h"
#include <openssl/ssl.h>
#include "asio_server.h"
#include "util.h"
#include "ssl.h"
#include "template.h"
namespace nghttp2 {
namespace asio_http2 {
namespace server {
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_);
}
void http2_impl::num_threads(size_t num_threads) { num_threads_ = num_threads; }
void http2_impl::backlog(int backlog) { backlog_ = backlog; }
bool http2_impl::handle(std::string pattern, request_cb cb) {
return mux_.handle(std::move(pattern), std::move(cb));
}
} // namespace server
} // namespace asio_http2
} // namespace nghttp2

View File

@@ -22,12 +22,14 @@
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
#ifndef ASIO_HTTP2_IMPL_H #ifndef ASIO_SERVER_HTTP2_IMPL_H
#define ASIO_HTTP2_IMPL_H #define ASIO_SERVER_HTTP2_IMPL_H
#include "nghttp2_config.h" #include "nghttp2_config.h"
#include <nghttp2/asio_http2.h> #include <nghttp2/asio_http2_server.h>
#include "asio_server_serve_mux.h"
namespace nghttp2 { namespace nghttp2 {
@@ -40,19 +42,19 @@ class server;
class http2_impl { class http2_impl {
public: public:
http2_impl(); http2_impl();
void listen(const std::string &address, uint16_t port, request_cb cb); 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);
void num_threads(size_t num_threads); void num_threads(size_t num_threads);
void tls(std::string private_key_file, std::string certificate_file);
void num_concurrent_tasks(size_t num_concurrent_tasks);
void backlog(int backlog); void backlog(int backlog);
bool handle(std::string pattern, request_cb cb);
private: private:
std::string private_key_file_;
std::string certificate_file_;
std::unique_ptr<server> server_; std::unique_ptr<server> server_;
std::size_t num_threads_; std::size_t num_threads_;
std::size_t num_concurrent_tasks_;
int backlog_; int backlog_;
serve_mux mux_;
}; };
} // namespace server } // namespace server
@@ -61,4 +63,4 @@ private:
} // namespace nghttp2 } // namespace nghttp2
#endif // ASIO_HTTP2_IMPL_H #endif // ASIO_SERVER_HTTP2_IMPL_H

View File

@@ -0,0 +1,55 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "nghttp2_config.h"
#include <nghttp2/asio_http2_server.h>
#include "asio_server_request_impl.h"
#include "template.h"
namespace nghttp2 {
namespace asio_http2 {
namespace server {
request::request() : impl_(make_unique<request_impl>()) {}
request::~request() {}
const header_map &request::header() const { return impl_->header(); }
const std::string &request::method() const { return impl_->method(); }
const uri_ref &request::uri() const { return impl_->uri(); }
void request::on_data(data_cb cb) const {
return impl_->on_data(std::move(cb));
}
request_impl &request::impl() const { return *impl_; }
} // namespace server
} // namespace asio_http2
} // namespace nghttp2

View File

@@ -0,0 +1,84 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "asio_server_request_handler.h"
#include "util.h"
#include "http2.h"
namespace nghttp2 {
namespace asio_http2 {
namespace server {
namespace {
std::string create_html(int status_code) {
std::string res;
res.reserve(512);
auto status = ::nghttp2::http2::get_status_string(status_code);
res += R"(<!DOCTYPE html><html lang="en"><title>)";
res += status;
res += "</title><body><h1>";
res += status;
res += "</h1></body></html>";
return res;
}
} // namespace
request_cb redirect_handler(int status_code, std::string uri) {
return [status_code, uri](const request &req, const response &res) {
header_map h;
h.emplace("location", header_value{std::move(uri)});
std::string html;
if (req.method() == "GET") {
html = create_html(status_code);
}
h.emplace("content-length", header_value{util::utos(html.size())});
res.write_head(status_code, std::move(h));
res.end(std::move(html));
};
}
request_cb status_handler(int status_code) {
return [status_code](const request &req, const response &res) {
if (!::nghttp2::http2::expect_response_body(status_code)) {
res.write_head(status_code);
res.end();
return;
}
// we supply content-length for HEAD request, but body will not be
// sent.
auto html = create_html(status_code);
header_map h;
h.emplace("content-length", header_value{util::utos(html.size())});
h.emplace("content-type", header_value{"text/html; charset=utf-8"});
res.write_head(status_code, std::move(h));
res.end(std::move(html));
};
}
} // namespace server
} // namespace asio_http2
} // namespace nghttp2

View File

@@ -0,0 +1,32 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef ASIO_SERVER_REQUEST_HANDLER_H
#define ASIO_SERVER_REQUEST_HANDLER_H
#include "nghttp2_config.h"
#include <nghttp2/asio_http2_server.h>
#endif // ASIO_SERVER_REQUEST_HANDLER_H

View File

@@ -0,0 +1,59 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "asio_server_request_impl.h"
namespace nghttp2 {
namespace asio_http2 {
namespace server {
request_impl::request_impl() : strm_(nullptr) {}
const header_map &request_impl::header() const { return header_; }
const std::string &request_impl::method() const { return method_; }
const uri_ref &request_impl::uri() const { return uri_; }
uri_ref &request_impl::uri() { return uri_; }
void request_impl::header(header_map h) { header_ = std::move(h); }
header_map &request_impl::header() { return header_; }
void request_impl::method(std::string arg) { method_ = std::move(arg); }
void request_impl::on_data(data_cb cb) { on_data_cb_ = std::move(cb); }
void request_impl::stream(class stream *s) { strm_ = s; }
void request_impl::call_on_data(const uint8_t *data, std::size_t len) {
if (on_data_cb_) {
on_data_cb_(data, len);
}
}
} // namespace server
} // namespace asio_http2
} // namespace nghttp2

View File

@@ -0,0 +1,69 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef ASIO_SERVER_REQUEST_IMPL_H
#define ASIO_SERVER_REQUEST_IMPL_H
#include "nghttp2_config.h"
#include <nghttp2/asio_http2_server.h>
namespace nghttp2 {
namespace asio_http2 {
namespace server {
class stream;
class request_impl {
public:
request_impl();
void header(header_map h);
const header_map &header() const;
header_map &header();
void method(std::string method);
const std::string &method() const;
const uri_ref &uri() const;
uri_ref &uri();
void on_data(data_cb cb);
void stream(class stream *s);
void call_on_data(const uint8_t *data, std::size_t len);
private:
class stream *strm_;
header_map header_;
std::string method_;
uri_ref uri_;
data_cb on_data_cb_;
};
} // namespace server
} // namespace asio_http2
} // namespace nghttp2
#endif // ASIO_SERVER_REQUEST_IMPL_H

View File

@@ -0,0 +1,75 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "nghttp2_config.h"
#include <nghttp2/asio_http2_server.h>
#include "asio_server_response_impl.h"
#include "template.h"
namespace nghttp2 {
namespace asio_http2 {
namespace server {
response::response() : impl_(make_unique<response_impl>()) {}
response::~response() {}
void response::write_head(unsigned int status_code, header_map h) const {
impl_->write_head(status_code, std::move(h));
}
void response::end(std::string data) const { impl_->end(std::move(data)); }
void response::end(generator_cb cb) const { impl_->end(std::move(cb)); }
void response::write_trailer(header_map h) const {
impl_->write_trailer(std::move(h));
}
void response::on_close(close_cb cb) const { impl_->on_close(std::move(cb)); }
void response::cancel(uint32_t error_code) const { impl_->cancel(error_code); }
const response *response::push(boost::system::error_code &ec,
std::string method, std::string path,
header_map h) const {
return impl_->push(ec, std::move(method), std::move(path), std::move(h));
}
void response::resume() const { impl_->resume(); }
unsigned int response::status_code() const { return impl_->status_code(); }
boost::asio::io_service &response::io_service() const {
return impl_->io_service();
}
response_impl &response::impl() const { return *impl_; }
} // namespace server
} // namespace asio_http2
} // namespace nghttp2

View File

@@ -0,0 +1,163 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "asio_server_response_impl.h"
#include "asio_server_stream.h"
#include "asio_server_request_impl.h"
#include "asio_server_http2_handler.h"
#include "asio_common.h"
#include "http2.h"
namespace nghttp2 {
namespace asio_http2 {
namespace server {
response_impl::response_impl()
: strm_(nullptr), generator_cb_(deferred_generator()), status_code_(200),
state_(response_state::INITIAL), pushed_(false),
push_promise_sent_(false) {}
unsigned int response_impl::status_code() const { return status_code_; }
void response_impl::write_head(unsigned int status_code, header_map h) {
if (state_ != response_state::INITIAL) {
return;
}
status_code_ = status_code;
header_ = std::move(h);
state_ = response_state::HEADER_DONE;
if (pushed_ && !push_promise_sent_) {
return;
}
start_response();
}
void response_impl::end(std::string data) {
end(string_generator(std::move(data)));
}
void response_impl::end(generator_cb cb) {
if (state_ == response_state::BODY_STARTED) {
return;
}
generator_cb_ = std::move(cb);
if (state_ == response_state::INITIAL) {
write_head(status_code_);
} else {
// generator_cb is changed, start writing in case it is deferred.
auto handler = strm_->handler();
handler->resume(*strm_);
}
state_ = response_state::BODY_STARTED;
}
void response_impl::write_trailer(header_map h) {
auto handler = strm_->handler();
handler->submit_trailer(*strm_, std::move(h));
}
void response_impl::start_response() {
auto handler = strm_->handler();
auto &req = strm_->request().impl();
if (!::nghttp2::http2::expect_response_body(req.method(), status_code_)) {
state_ = response_state::BODY_STARTED;
}
if (handler->start_response(*strm_) != 0) {
handler->stream_error(strm_->get_stream_id(), NGHTTP2_INTERNAL_ERROR);
return;
}
}
void response_impl::on_close(close_cb cb) { close_cb_ = std::move(cb); }
void response_impl::call_on_close(uint32_t error_code) {
if (close_cb_) {
close_cb_(error_code);
}
}
void response_impl::cancel(uint32_t error_code) {
auto handler = strm_->handler();
handler->stream_error(strm_->get_stream_id(), error_code);
}
response *response_impl::push(boost::system::error_code &ec, std::string method,
std::string raw_path_query, header_map h) const {
auto handler = strm_->handler();
return handler->push_promise(ec, *strm_, std::move(method),
std::move(raw_path_query), std::move(h));
}
void response_impl::resume() {
auto handler = strm_->handler();
handler->resume(*strm_);
}
boost::asio::io_service &response_impl::io_service() {
return strm_->handler()->io_service();
}
void response_impl::pushed(bool f) { pushed_ = f; }
void response_impl::push_promise_sent() {
if (push_promise_sent_) {
return;
}
push_promise_sent_ = true;
if (state_ == response_state::INITIAL) {
return;
}
start_response();
}
const header_map &response_impl::header() const { return header_; }
void response_impl::stream(class stream *s) { strm_ = s; }
generator_cb::result_type
response_impl::call_read(uint8_t *data, std::size_t len, uint32_t *data_flags) {
if (generator_cb_) {
return generator_cb_(data, len, data_flags);
}
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
return 0;
}
} // namespace server
} // namespace asio_http2
} // namespace nghttp2

View File

@@ -0,0 +1,92 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef ASIO_SERVER_RESPONSE_IMPL_H
#define ASIO_SERVER_RESPONSE_IMPL_H
#include "nghttp2_config.h"
#include <nghttp2/asio_http2_server.h>
namespace nghttp2 {
namespace asio_http2 {
namespace server {
class stream;
enum class response_state {
INITIAL,
// response_impl::write_head() was called
HEADER_DONE,
// response_impl::end() was called
BODY_STARTED,
};
class response_impl {
public:
response_impl();
void write_head(unsigned int status_code, header_map h = header_map{});
void end(std::string data = "");
void end(generator_cb cb);
void write_trailer(header_map h);
void on_close(close_cb cb);
void resume();
void cancel(uint32_t error_code);
response *push(boost::system::error_code &ec, std::string method,
std::string raw_path_query, header_map) const;
boost::asio::io_service &io_service();
void start_response();
unsigned int status_code() const;
const header_map &header() const;
void pushed(bool f);
void push_promise_sent();
void stream(class stream *s);
generator_cb::result_type call_read(uint8_t *data, std::size_t len,
uint32_t *data_flags);
void call_on_close(uint32_t error_code);
private:
class stream *strm_;
header_map header_;
generator_cb generator_cb_;
close_cb close_cb_;
unsigned int status_code_;
response_state state_;
// true if this is pushed stream's response
bool pushed_;
// true if PUSH_PROMISE is sent if this is response of a pushed
// stream
bool push_promise_sent_;
};
} // namespace server
} // namespace asio_http2
} // namespace nghttp2
#endif // ASIO_SERVER_RESPONSE_IMPL_H

View File

@@ -0,0 +1,138 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "asio_server_serve_mux.h"
#include "asio_server_request_impl.h"
#include "asio_server_request_handler.h"
#include "util.h"
#include "http2.h"
namespace nghttp2 {
namespace asio_http2 {
namespace server {
bool serve_mux::handle(std::string pattern, request_cb cb) {
if (pattern.empty() || !cb) {
return false;
}
auto it = mux_.find(pattern);
if (it != std::end(mux_) && (*it).second.user_defined) {
return false;
}
// if pattern ends with '/' (e.g., /foo/), add implicit permanent
// redirect for '/foo'.
if (pattern.size() >= 2 && pattern.back() == '/') {
auto redirect_pattern = pattern.substr(0, pattern.size() - 1);
auto it = mux_.find(redirect_pattern);
if (it == std::end(mux_) || !(*it).second.user_defined) {
std::string path;
if (pattern[0] == '/') {
path = pattern;
} else {
// skip host part
path = pattern.substr(pattern.find('/'));
}
if (it == std::end(mux_)) {
mux_.emplace(std::move(redirect_pattern),
handler_entry{false,
redirect_handler(301, std::move(path)),
pattern});
} else {
(*it).second = handler_entry{
false, redirect_handler(301, std::move(path)), pattern};
}
}
}
mux_.emplace(pattern, handler_entry{true, std::move(cb), pattern});
return true;
}
request_cb serve_mux::handler(request_impl &req) const {
auto &path = req.uri().path;
if (req.method() != "CONNECT") {
auto clean_path = ::nghttp2::http2::path_join(
nullptr, 0, nullptr, 0, path.c_str(), path.size(), nullptr, 0);
if (clean_path != path) {
auto new_uri = util::percent_encode_path(clean_path);
auto &uref = req.uri();
if (!uref.raw_query.empty()) {
new_uri += "?";
new_uri += uref.raw_query;
}
return redirect_handler(301, std::move(new_uri));
}
}
auto &host = req.uri().host;
auto cb = match(host + path);
if (cb) {
return cb;
}
cb = match(path);
if (cb) {
return cb;
}
return status_handler(404);
}
namespace {
bool path_match(const std::string &pattern, const std::string &path) {
if (pattern.back() != '/') {
return pattern == path;
}
return util::startsWith(path, pattern);
}
} // namespace
request_cb serve_mux::match(const std::string &path) const {
const handler_entry *ent = nullptr;
size_t best = 0;
for (auto &kv : mux_) {
auto &pattern = kv.first;
if (!path_match(pattern, path)) {
continue;
}
if (!ent || best < pattern.size()) {
best = pattern.size();
ent = &kv.second;
}
}
if (ent) {
return ent->cb;
}
return request_cb();
}
} // namespace server
} // namespace asio_http2
} // namespace nghttp2

View File

@@ -0,0 +1,64 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef ASIO_SERVER_SERVE_MUX_H
#define ASIO_SERVER_SERVE_MUX_H
#include "nghttp2_config.h"
#include <nghttp2/asio_http2_server.h>
namespace nghttp2 {
namespace asio_http2 {
namespace server {
class request_impl;
// port from go's ServeMux
struct handler_entry {
bool user_defined;
request_cb cb;
std::string pattern;
};
class serve_mux {
public:
bool handle(std::string pattern, request_cb cb);
request_cb handler(request_impl &req) const;
request_cb match(const std::string &path) const;
private:
std::map<std::string, handler_entry> mux_;
};
} // namespace server
} // namespace asio_http2
} // namespace nghttp2
#endif // ASIO_SERVER_SERVE_MUX_H

55
src/asio_server_stream.cc Normal file
View File

@@ -0,0 +1,55 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "asio_server_stream.h"
#include "asio_server_http2_handler.h"
#include "asio_server_request_impl.h"
#include "asio_server_response_impl.h"
namespace nghttp2 {
namespace asio_http2 {
namespace server {
stream::stream(http2_handler *h, int32_t stream_id)
: handler_(h), stream_id_(stream_id) {
request_.impl().stream(this);
response_.impl().stream(this);
}
int32_t stream::get_stream_id() const { return stream_id_; }
class request &stream::request() {
return request_;
}
class response &stream::response() {
return response_;
}
http2_handler *stream::handler() const { return handler_; }
} // namespace server
} // namespace asio_http2
} // namespace nghttp2

59
src/asio_server_stream.h Normal file
View File

@@ -0,0 +1,59 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef ASIO_SERVER_STREAM_H
#define ASIO_SERVER_STREAM_H
#include "nghttp2_config.h"
#include <nghttp2/asio_http2_server.h>
namespace nghttp2 {
namespace asio_http2 {
namespace server {
class http2_handler;
class stream {
public:
stream(http2_handler *h, int32_t stream_id);
int32_t get_stream_id() const;
class request &request();
class response &response();
http2_handler *handler() const;
private:
http2_handler *handler_;
class request request_;
class response response_;
int32_t stream_id_;
};
} // namespace server
} // namespace asio_http2
} // namespace nghttp2
#endif // ASIO_SERVER_STREAM_H

View File

@@ -0,0 +1,85 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "asio_server_tls_context.h"
#include <openssl/ssl.h>
#include <boost/asio/ssl.hpp>
#include "ssl.h"
#include "util.h"
namespace nghttp2 {
namespace asio_http2 {
namespace server {
namespace {
std::vector<unsigned char> &get_alpn_token() {
static auto alpn_token = util::get_default_alpn();
return alpn_token;
}
} // namespace
boost::system::error_code
configure_tls_context_easy(boost::system::error_code &ec,
boost::asio::ssl::context &tls_context) {
ec.clear();
auto ctx = tls_context.native_handle();
SSL_CTX_set_options(ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
SSL_OP_NO_COMPRESSION |
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION |
SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_TICKET |
SSL_OP_CIPHER_SERVER_PREFERENCE);
SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
SSL_CTX_set_mode(ctx, SSL_MODE_RELEASE_BUFFERS);
SSL_CTX_set_cipher_list(ctx, ssl::DEFAULT_CIPHER_LIST);
auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
if (ecdh) {
SSL_CTX_set_tmp_ecdh(ctx, ecdh);
EC_KEY_free(ecdh);
}
SSL_CTX_set_next_protos_advertised_cb(
ctx,
[](SSL *s, const unsigned char **data, unsigned int *len, void *arg) {
auto &token = get_alpn_token();
*data = token.data();
*len = token.size();
return SSL_TLSEXT_ERR_OK;
},
nullptr);
return ec;
}
} // namespace server
} // namespace asio_http2
} // namespace nghttp2

View File

@@ -0,0 +1,32 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef ASIO_SERVER_TLS_CONTEXT_H
#define ASIO_SERVER_TLS_CONTEXT_H
#include "nghttp2_config.h"
#include <nghttp2/asio_http2_server.h>
#endif // ASIO_SERVER_TLS_CONTEXT_H

View File

@@ -28,6 +28,8 @@
#include <signal.h> #include <signal.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <netinet/tcp.h> #include <netinet/tcp.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <cstdio> #include <cstdio>
#include <cassert> #include <cassert>
@@ -57,16 +59,27 @@
#include "util.h" #include "util.h"
#include "template.h" #include "template.h"
#ifndef O_BINARY
#define O_BINARY (0)
#endif // O_BINARY
using namespace nghttp2; using namespace nghttp2;
namespace h2load { namespace h2load {
Config::Config() Config::Config()
: addrs(nullptr), nreqs(1), nclients(1), nthreads(1), : 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(16), connection_window_bits(16),
no_tls_proto(PROTO_HTTP2), port(0), default_port(0), verbose(false) {} no_tls_proto(PROTO_HTTP2), data_fd(-1), port(0), default_port(0),
verbose(false) {}
Config::~Config() { freeaddrinfo(addrs); } Config::~Config() {
freeaddrinfo(addrs);
if (data_fd != -1) {
close(data_fd);
}
}
Config config; Config config;
@@ -95,7 +108,7 @@ void debug_nextproto_error() {
} }
} // namespace } // namespace
RequestStat::RequestStat() : completed(false) {} RequestStat::RequestStat() : data_offset(0), completed(false) {}
Stats::Stats(size_t req_todo) Stats::Stats(size_t req_todo)
: req_todo(0), req_started(0), req_done(0), req_success(0), : req_todo(0), req_started(0), req_done(0), req_success(0),
@@ -987,6 +1000,9 @@ Options:
#endif // !HAVE_SPDYLAY #endif // !HAVE_SPDYLAY
out << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"( out << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"(
Default: )" << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"( Default: )" << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"(
-d, --data=<FILE>
Post FILE to server. The request method is changed to
POST.
-v, --verbose -v, --verbose
Output debug information. Output debug information.
--version Display version information and exit. --version Display version information and exit.
@@ -995,11 +1011,13 @@ Options:
} // namespace } // namespace
int main(int argc, char **argv) { int main(int argc, char **argv) {
std::string datafile;
while (1) { while (1) {
static int flag = 0; static int flag = 0;
static option long_options[] = { static option long_options[] = {
{"requests", required_argument, nullptr, 'n'}, {"requests", required_argument, nullptr, 'n'},
{"clients", required_argument, nullptr, 'c'}, {"clients", required_argument, nullptr, 'c'},
{"data", required_argument, nullptr, 'd'},
{"threads", required_argument, nullptr, 't'}, {"threads", required_argument, nullptr, 't'},
{"max-concurrent-streams", required_argument, nullptr, 'm'}, {"max-concurrent-streams", required_argument, nullptr, 'm'},
{"window-bits", required_argument, nullptr, 'w'}, {"window-bits", required_argument, nullptr, 'w'},
@@ -1012,7 +1030,7 @@ int main(int argc, char **argv) {
{"version", no_argument, &flag, 1}, {"version", no_argument, &flag, 1},
{nullptr, 0, nullptr, 0}}; {nullptr, 0, nullptr, 0}};
int option_index = 0; int option_index = 0;
auto c = getopt_long(argc, argv, "hvW:c:m:n:p:t:w:H:i:", long_options, auto c = getopt_long(argc, argv, "hvW:c:d:m:n:p:t:w:H:i:", long_options,
&option_index); &option_index);
if (c == -1) { if (c == -1) {
break; break;
@@ -1024,6 +1042,9 @@ int main(int argc, char **argv) {
case 'c': case 'c':
config.nclients = strtoul(optarg, nullptr, 10); config.nclients = strtoul(optarg, nullptr, 10);
break; break;
case 'd':
datafile = optarg;
break;
case 't': case 't':
#ifdef NOTHREADS #ifdef NOTHREADS
std::cerr << "-t: WARNING: Threading disabled at build time, " std::cerr << "-t: WARNING: Threading disabled at build time, "
@@ -1157,11 +1178,31 @@ int main(int argc, char **argv) {
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (config.nclients < config.nthreads) {
std::cerr << "-c, -t: the number of client must be greater than or equal "
"to the number of threads." << std::endl;
exit(EXIT_FAILURE);
}
if (config.nthreads > std::thread::hardware_concurrency()) { if (config.nthreads > std::thread::hardware_concurrency()) {
std::cerr << "-t: warning: the number of threads is greater than hardware " std::cerr << "-t: warning: the number of threads is greater than hardware "
<< "cores." << std::endl; << "cores." << std::endl;
} }
if (!datafile.empty()) {
config.data_fd = open(datafile.c_str(), O_RDONLY | O_BINARY);
if (config.data_fd == -1) {
std::cerr << "-d: Could not open file " << datafile << std::endl;
exit(EXIT_FAILURE);
}
struct stat data_stat;
if (fstat(config.data_fd, &data_stat) == -1) {
std::cerr << "-d: Could not stat file " << datafile << std::endl;
exit(EXIT_FAILURE);
}
config.data_length = data_stat.st_size;
}
struct sigaction act; struct sigaction act;
memset(&act, 0, sizeof(struct sigaction)); memset(&act, 0, sizeof(struct sigaction));
act.sa_handler = SIG_IGN; act.sa_handler = SIG_IGN;
@@ -1243,7 +1284,7 @@ int main(int argc, char **argv) {
} else { } else {
shared_nva.emplace_back(":authority", config.host); shared_nva.emplace_back(":authority", config.host);
} }
shared_nva.emplace_back(":method", "GET"); shared_nva.emplace_back(":method", config.data_fd == -1 ? "GET" : "POST");
// list overridalbe headers // list overridalbe headers
auto override_hdrs = auto override_hdrs =
@@ -1315,7 +1356,7 @@ int main(int argc, char **argv) {
auto start = std::chrono::steady_clock::now(); auto start = std::chrono::steady_clock::now();
std::vector<std::unique_ptr<Worker>> workers; std::vector<std::unique_ptr<Worker>> workers;
workers.reserve(config.nthreads - 1); workers.reserve(config.nthreads);
#ifndef NOTHREADS #ifndef NOTHREADS
std::vector<std::future<void>> futures; std::vector<std::future<void>> futures;

View File

@@ -60,6 +60,8 @@ struct Config {
std::string scheme; std::string scheme;
std::string host; std::string host;
std::string ifile; std::string ifile;
// length of upload data
int64_t data_length;
addrinfo *addrs; addrinfo *addrs;
size_t nreqs; size_t nreqs;
size_t nclients; size_t nclients;
@@ -69,6 +71,8 @@ struct Config {
size_t window_bits; size_t window_bits;
size_t connection_window_bits; size_t connection_window_bits;
enum { PROTO_HTTP2, PROTO_SPDY2, PROTO_SPDY3, PROTO_SPDY3_1 } no_tls_proto; enum { PROTO_HTTP2, PROTO_SPDY2, PROTO_SPDY3, PROTO_SPDY3_1 } no_tls_proto;
// file descriptor for upload data
int data_fd;
uint16_t port; uint16_t port;
uint16_t default_port; uint16_t default_port;
bool verbose; bool verbose;
@@ -83,6 +87,8 @@ struct RequestStat {
std::chrono::steady_clock::time_point request_time; std::chrono::steady_clock::time_point request_time;
// time point when stream was closed // time point when stream was closed
std::chrono::steady_clock::time_point stream_close_time; std::chrono::steady_clock::time_point stream_close_time;
// upload data length sent so far
int64_t data_offset;
// true if stream was successfully closed. This means stream was // true if stream was successfully closed. This means stream was
// not reset, but it does not mean HTTP level error (e.g., 404). // not reset, but it does not mean HTTP level error (e.g., 404).
bool completed; bool completed;

View File

@@ -110,6 +110,36 @@ int before_frame_send_callback(nghttp2_session *session,
} }
} // namespace } // namespace
namespace {
ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
uint8_t *buf, size_t length, uint32_t *data_flags,
nghttp2_data_source *source, void *user_data) {
auto client = static_cast<Client *>(user_data);
auto config = client->worker->config;
auto req_stat = static_cast<RequestStat *>(
nghttp2_session_get_stream_user_data(session, stream_id));
ssize_t nread;
while ((nread = pread(config->data_fd, buf, length, req_stat->data_offset)) ==
-1 &&
errno == EINTR)
;
if (nread == -1) {
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
req_stat->data_offset += nread;
if (nread == 0 || req_stat->data_offset == config->data_length) {
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
}
return nread;
}
} // namespace
namespace { namespace {
ssize_t send_callback(nghttp2_session *session, const uint8_t *data, ssize_t send_callback(nghttp2_session *session, const uint8_t *data,
size_t length, int flags, void *user_data) { size_t length, int flags, void *user_data) {
@@ -188,8 +218,11 @@ void Http2Session::submit_request(RequestStat *req_stat) {
client_->reqidx = 0; client_->reqidx = 0;
} }
auto stream_id = nghttp2_submit_request(session_, nullptr, nva.data(), nghttp2_data_provider prd{{0}, file_read_callback};
nva.size(), nullptr, req_stat);
auto stream_id =
nghttp2_submit_request(session_, nullptr, nva.data(), nva.size(),
config->data_fd == -1 ? nullptr : &prd, req_stat);
assert(stream_id > 0); assert(stream_id > 0);
} }

View File

@@ -110,6 +110,35 @@ ssize_t send_callback(spdylay_session *session, const uint8_t *data,
} }
} // namespace } // namespace
namespace {
ssize_t file_read_callback(spdylay_session *session, int32_t stream_id,
uint8_t *buf, size_t length, int *eof,
spdylay_data_source *source, void *user_data) {
auto client = static_cast<Client *>(user_data);
auto config = client->worker->config;
auto req_stat = static_cast<RequestStat *>(
spdylay_session_get_stream_user_data(session, stream_id));
ssize_t nread;
while ((nread = pread(config->data_fd, buf, length, req_stat->data_offset)) ==
-1 &&
errno == EINTR)
;
if (nread == -1) {
return SPDYLAY_ERR_TEMPORAL_CALLBACK_FAILURE;
}
req_stat->data_offset += nread;
if (nread == 0 || req_stat->data_offset == config->data_length) {
*eof = 1;
}
return nread;
}
} // namespace
void SpdySession::on_connect() { void SpdySession::on_connect() {
spdylay_session_callbacks callbacks = {0}; spdylay_session_callbacks callbacks = {0};
callbacks.send_callback = send_callback; callbacks.send_callback = send_callback;
@@ -150,7 +179,10 @@ void SpdySession::submit_request(RequestStat *req_stat) {
client_->reqidx = 0; client_->reqidx = 0;
} }
spdylay_submit_request(session_, 0, nv.data(), nullptr, req_stat); spdylay_data_provider prd{{0}, file_read_callback};
spdylay_submit_request(session_, 0, nv.data(),
config->data_fd == -1 ? nullptr : &prd, req_stat);
} }
int SpdySession::on_read(const uint8_t *data, size_t len) { int SpdySession::on_read(const uint8_t *data, size_t len) {

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